github.com/defanghe/fabric@v2.1.1+incompatible/core/ledger/ledgerstorage/store_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package ledgerstorage
     8  
     9  import (
    10  	"io/ioutil"
    11  	"os"
    12  	"path/filepath"
    13  	"testing"
    14  
    15  	"github.com/golang/protobuf/proto"
    16  	"github.com/hyperledger/fabric-protos-go/common"
    17  	"github.com/hyperledger/fabric-protos-go/ledger/rwset"
    18  	pb "github.com/hyperledger/fabric-protos-go/peer"
    19  	"github.com/hyperledger/fabric/common/flogging"
    20  	"github.com/hyperledger/fabric/common/ledger/testutil"
    21  	"github.com/hyperledger/fabric/common/metrics/disabled"
    22  	"github.com/hyperledger/fabric/core/ledger"
    23  	"github.com/hyperledger/fabric/core/ledger/pvtdatapolicy"
    24  	btltestutil "github.com/hyperledger/fabric/core/ledger/pvtdatapolicy/testutil"
    25  	"github.com/hyperledger/fabric/core/ledger/pvtdatastorage"
    26  	lutil "github.com/hyperledger/fabric/core/ledger/util"
    27  	"github.com/stretchr/testify/assert"
    28  )
    29  
    30  var metricsProvider = &disabled.Provider{}
    31  
    32  func TestMain(m *testing.M) {
    33  	flogging.ActivateSpec("ledgerstorage,pvtdatastorage=debug")
    34  	os.Exit(m.Run())
    35  }
    36  
    37  func TestStore(t *testing.T) {
    38  	storeDir, err := ioutil.TempDir("", "lstore")
    39  	if err != nil {
    40  		t.Fatalf("Failed to create ledger storage directory: %s", err)
    41  	}
    42  	defer os.RemoveAll(storeDir)
    43  	conf := buildPrivateDataConfig(storeDir)
    44  	blockStoreDir := filepath.Join(storeDir, "chains")
    45  	provider, err := NewProvider(blockStoreDir, conf, metricsProvider)
    46  	assert.NoError(t, err)
    47  	defer provider.Close()
    48  	store, err := provider.Open("testLedger")
    49  	store.Init(btlPolicyForSampleData())
    50  	defer store.Shutdown()
    51  
    52  	assert.NoError(t, err)
    53  	sampleData := sampleDataWithPvtdataForSelectiveTx(t)
    54  	for _, sampleDatum := range sampleData {
    55  		assert.NoError(t, store.CommitWithPvtData(sampleDatum))
    56  	}
    57  
    58  	// block 1 has no pvt data
    59  	pvtdata, err := store.GetPvtDataByNum(1, nil)
    60  	assert.NoError(t, err)
    61  	assert.Nil(t, pvtdata)
    62  
    63  	// block 4 has no pvt data
    64  	pvtdata, err = store.GetPvtDataByNum(4, nil)
    65  	assert.NoError(t, err)
    66  	assert.Nil(t, pvtdata)
    67  
    68  	// block 2 has pvt data for tx 3, 5 and 6. Though the tx 6
    69  	// is marked as invalid in the block, the pvtData should
    70  	// have been stored
    71  	pvtdata, err = store.GetPvtDataByNum(2, nil)
    72  	assert.NoError(t, err)
    73  	assert.Equal(t, 3, len(pvtdata))
    74  	assert.Equal(t, uint64(3), pvtdata[0].SeqInBlock)
    75  	assert.Equal(t, uint64(5), pvtdata[1].SeqInBlock)
    76  	assert.Equal(t, uint64(6), pvtdata[2].SeqInBlock)
    77  
    78  	// block 3 has pvt data for tx 4 and 6 only
    79  	pvtdata, err = store.GetPvtDataByNum(3, nil)
    80  	assert.NoError(t, err)
    81  	assert.Equal(t, 2, len(pvtdata))
    82  	assert.Equal(t, uint64(4), pvtdata[0].SeqInBlock)
    83  	assert.Equal(t, uint64(6), pvtdata[1].SeqInBlock)
    84  
    85  	blockAndPvtdata, err := store.GetPvtDataAndBlockByNum(2, nil)
    86  	assert.NoError(t, err)
    87  	assert.True(t, proto.Equal(sampleData[2].Block, blockAndPvtdata.Block))
    88  
    89  	blockAndPvtdata, err = store.GetPvtDataAndBlockByNum(3, nil)
    90  	assert.NoError(t, err)
    91  	assert.True(t, proto.Equal(sampleData[3].Block, blockAndPvtdata.Block))
    92  
    93  	// pvt data retrieval for block 3 with filter should return filtered pvtdata
    94  	filter := ledger.NewPvtNsCollFilter()
    95  	filter.Add("ns-1", "coll-1")
    96  	blockAndPvtdata, err = store.GetPvtDataAndBlockByNum(3, filter)
    97  	assert.NoError(t, err)
    98  	assert.Equal(t, sampleData[3].Block, blockAndPvtdata.Block)
    99  	// two transactions should be present
   100  	assert.Equal(t, 2, len(blockAndPvtdata.PvtData))
   101  	// both tran number 4 and 6 should have only one collection because of filter
   102  	assert.Equal(t, 1, len(blockAndPvtdata.PvtData[4].WriteSet.NsPvtRwset))
   103  	assert.Equal(t, 1, len(blockAndPvtdata.PvtData[6].WriteSet.NsPvtRwset))
   104  	// any other transaction entry should be nil
   105  	assert.Nil(t, blockAndPvtdata.PvtData[2])
   106  
   107  	// test missing data retrieval in the presence of invalid tx. Block 5 had
   108  	// missing data (for tx4 and tx5). Though tx5 was marked as invalid tx,
   109  	// both tx4 and tx5 missing data should be returned
   110  	expectedMissingDataInfo := make(ledger.MissingPvtDataInfo)
   111  	expectedMissingDataInfo.Add(5, 4, "ns-4", "coll-4")
   112  	expectedMissingDataInfo.Add(5, 5, "ns-5", "coll-5")
   113  	missingDataInfo, err := store.GetMissingPvtDataInfoForMostRecentBlocks(1)
   114  	assert.NoError(t, err)
   115  	assert.Equal(t, expectedMissingDataInfo, missingDataInfo)
   116  }
   117  
   118  func TestCrashAfterPvtdataStoreCommit(t *testing.T) {
   119  	storeDir, err := ioutil.TempDir("", "lstore")
   120  	if err != nil {
   121  		t.Fatalf("Failed to create ledger storage directory: %s", err)
   122  	}
   123  	defer os.RemoveAll(storeDir)
   124  	conf := buildPrivateDataConfig(storeDir)
   125  	blockStoreDir := filepath.Join(storeDir, "chains")
   126  	provider, err := NewProvider(blockStoreDir, conf, metricsProvider)
   127  	assert.NoError(t, err)
   128  	defer provider.Close()
   129  	store, err := provider.Open("testLedger")
   130  	store.Init(btlPolicyForSampleData())
   131  	defer store.Shutdown()
   132  	assert.NoError(t, err)
   133  
   134  	sampleData := sampleDataWithPvtdataForAllTxs(t)
   135  	dataBeforeCrash := sampleData[0:3]
   136  	dataAtCrash := sampleData[3]
   137  
   138  	for _, sampleDatum := range dataBeforeCrash {
   139  		assert.NoError(t, store.CommitWithPvtData(sampleDatum))
   140  	}
   141  	blockNumAtCrash := dataAtCrash.Block.Header.Number
   142  	var pvtdataAtCrash []*ledger.TxPvtData
   143  	for _, p := range dataAtCrash.PvtData {
   144  		pvtdataAtCrash = append(pvtdataAtCrash, p)
   145  	}
   146  	// call Commit on pvt data store and mimic a crash before committing the block to block store
   147  	store.pvtdataStore.Commit(blockNumAtCrash, pvtdataAtCrash, nil)
   148  	store.Shutdown()
   149  	provider.Close()
   150  	provider, err = NewProvider(blockStoreDir, conf, metricsProvider)
   151  	assert.NoError(t, err)
   152  	store, err = provider.Open("testLedger")
   153  	assert.NoError(t, err)
   154  	store.Init(btlPolicyForSampleData())
   155  
   156  	// When starting the storage after a crash, we should be able to fetch the pvtData from pvtStore
   157  	testVerifyPvtData(t, store, blockNumAtCrash, dataAtCrash.PvtData)
   158  	bcInfo, err := store.GetBlockchainInfo()
   159  	assert.NoError(t, err)
   160  	assert.Equal(t, blockNumAtCrash, bcInfo.Height)
   161  
   162  	// we should be able to write the last block again
   163  	// to ensure that the pvtdataStore is not updated, we send a different pvtData for
   164  	// the same block such that we can retrieve the pvtData and compare.
   165  	expectedPvtData := dataAtCrash.PvtData
   166  	dataAtCrash.PvtData = make(ledger.TxPvtDataMap)
   167  	dataAtCrash.PvtData[0] = &ledger.TxPvtData{
   168  		SeqInBlock: 0,
   169  		WriteSet: &rwset.TxPvtReadWriteSet{
   170  			NsPvtRwset: []*rwset.NsPvtReadWriteSet{
   171  				{
   172  					Namespace: "ns-1",
   173  					CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{
   174  						{
   175  							CollectionName: "coll-1",
   176  							Rwset:          []byte("pvtdata"),
   177  						},
   178  					},
   179  				},
   180  			},
   181  		},
   182  	}
   183  	assert.NoError(t, store.CommitWithPvtData(dataAtCrash))
   184  	testVerifyPvtData(t, store, blockNumAtCrash, expectedPvtData)
   185  	bcInfo, err = store.GetBlockchainInfo()
   186  	assert.NoError(t, err)
   187  	assert.Equal(t, blockNumAtCrash+1, bcInfo.Height)
   188  
   189  }
   190  
   191  func testVerifyPvtData(t *testing.T, store *Store, blockNum uint64, expectedPvtData ledger.TxPvtDataMap) {
   192  	pvtdata, err := store.GetPvtDataByNum(blockNum, nil)
   193  	assert.NoError(t, err)
   194  	constructed := constructPvtdataMap(pvtdata)
   195  	assert.Equal(t, len(expectedPvtData), len(constructed))
   196  	for k, v := range expectedPvtData {
   197  		ov, ok := constructed[k]
   198  		assert.True(t, ok)
   199  		assert.Equal(t, v.SeqInBlock, ov.SeqInBlock)
   200  		assert.True(t, proto.Equal(v.WriteSet, ov.WriteSet))
   201  	}
   202  }
   203  
   204  func TestAddAfterPvtdataStoreError(t *testing.T) {
   205  	storeDir, err := ioutil.TempDir("", "lstore")
   206  	if err != nil {
   207  		t.Fatalf("Failed to create ledger storage directory: %s", err)
   208  	}
   209  	defer os.RemoveAll(storeDir)
   210  	conf := buildPrivateDataConfig(storeDir)
   211  	blockStoreDir := filepath.Join(storeDir, "chains")
   212  	provider, err := NewProvider(blockStoreDir, conf, metricsProvider)
   213  	assert.NoError(t, err)
   214  	defer provider.Close()
   215  	store, err := provider.Open("testLedger")
   216  	store.Init(btlPolicyForSampleData())
   217  	defer store.Shutdown()
   218  	assert.NoError(t, err)
   219  
   220  	sampleData := sampleDataWithPvtdataForAllTxs(t)
   221  	for _, d := range sampleData[0:9] {
   222  		assert.NoError(t, store.CommitWithPvtData(d))
   223  	}
   224  	// try to write the last block again. The function should skip adding block to the private store
   225  	// as the pvt store but the block storage should return error
   226  	assert.Error(t, store.CommitWithPvtData(sampleData[8]))
   227  
   228  	// At the end, the pvt store status should not have changed
   229  	pvtStoreCommitHt, err := store.pvtdataStore.LastCommittedBlockHeight()
   230  	assert.NoError(t, err)
   231  	assert.Equal(t, uint64(9), pvtStoreCommitHt)
   232  
   233  	// commit the rightful next block
   234  	assert.NoError(t, store.CommitWithPvtData(sampleData[9]))
   235  	pvtStoreCommitHt, err = store.pvtdataStore.LastCommittedBlockHeight()
   236  	assert.NoError(t, err)
   237  	assert.Equal(t, uint64(10), pvtStoreCommitHt)
   238  }
   239  
   240  func TestAddAfterBlkStoreError(t *testing.T) {
   241  	storeDir, err := ioutil.TempDir("", "lstore")
   242  	if err != nil {
   243  		t.Fatalf("Failed to create ledger storage directory: %s", err)
   244  	}
   245  	defer os.RemoveAll(storeDir)
   246  	conf := buildPrivateDataConfig(storeDir)
   247  	blockStoreDir := filepath.Join(storeDir, "chains")
   248  	provider, err := NewProvider(blockStoreDir, conf, metricsProvider)
   249  	assert.NoError(t, err)
   250  	defer provider.Close()
   251  	store, err := provider.Open("testLedger")
   252  	store.Init(btlPolicyForSampleData())
   253  	defer store.Shutdown()
   254  	assert.NoError(t, err)
   255  
   256  	sampleData := sampleDataWithPvtdataForAllTxs(t)
   257  	for _, d := range sampleData[0:9] {
   258  		assert.NoError(t, store.CommitWithPvtData(d))
   259  	}
   260  	lastBlkAndPvtData := sampleData[9]
   261  	// Add the block directly to blockstore
   262  	store.BlockStore.AddBlock(lastBlkAndPvtData.Block)
   263  	// Adding the same block should cause passing on the error caused by the block storgae
   264  	assert.Error(t, store.CommitWithPvtData(lastBlkAndPvtData))
   265  	// At the end, the pvt store status should be changed
   266  	pvtStoreCommitHt, err := store.pvtdataStore.LastCommittedBlockHeight()
   267  	assert.NoError(t, err)
   268  	assert.Equal(t, uint64(10), pvtStoreCommitHt)
   269  }
   270  
   271  func TestPvtStoreAheadOfBlockStore(t *testing.T) {
   272  	storeDir, err := ioutil.TempDir("", "lstore")
   273  	if err != nil {
   274  		t.Fatalf("Failed to create ledger storage directory: %s", err)
   275  	}
   276  	defer os.RemoveAll(storeDir)
   277  	conf := buildPrivateDataConfig(storeDir)
   278  	blockStoreDir := filepath.Join(storeDir, "chains")
   279  	provider, err := NewProvider(blockStoreDir, conf, metricsProvider)
   280  	assert.NoError(t, err)
   281  	defer provider.Close()
   282  	store, err := provider.Open("testLedger")
   283  	store.Init(btlPolicyForSampleData())
   284  	defer store.Shutdown()
   285  	assert.NoError(t, err)
   286  
   287  	// when both stores are empty, isPvtstoreAheadOfBlockstore should be false
   288  	assert.False(t, store.IsPvtStoreAheadOfBlockStore())
   289  
   290  	sampleData := sampleDataWithPvtdataForSelectiveTx(t)
   291  	for _, d := range sampleData[0:9] { // commit block number 0 to 8
   292  		assert.NoError(t, store.CommitWithPvtData(d))
   293  	}
   294  	assert.False(t, store.IsPvtStoreAheadOfBlockStore())
   295  
   296  	// close and reopen
   297  	store.Shutdown()
   298  	provider.Close()
   299  	provider, err = NewProvider(blockStoreDir, conf, metricsProvider)
   300  	assert.NoError(t, err)
   301  	store, err = provider.Open("testLedger")
   302  	assert.NoError(t, err)
   303  	store.Init(btlPolicyForSampleData())
   304  
   305  	// as both stores are at the same block height, isPvtstoreAheadOfBlockstore should be false
   306  	info, err := store.GetBlockchainInfo()
   307  	assert.NoError(t, err)
   308  	assert.Equal(t, uint64(9), info.Height)
   309  	pvtStoreHt, err := store.pvtdataStore.LastCommittedBlockHeight()
   310  	assert.NoError(t, err)
   311  	assert.Equal(t, uint64(9), pvtStoreHt)
   312  	assert.False(t, store.IsPvtStoreAheadOfBlockStore())
   313  
   314  	lastBlkAndPvtData := sampleData[9]
   315  	// Add the last block directly to the pvtdataStore but not to blockstore. This would make
   316  	// the pvtdatastore height greater than the block store height.
   317  	validTxPvtData, validTxMissingPvtData := constructPvtDataAndMissingData(lastBlkAndPvtData)
   318  	err = store.pvtdataStore.Commit(lastBlkAndPvtData.Block.Header.Number, validTxPvtData, validTxMissingPvtData)
   319  	assert.NoError(t, err)
   320  
   321  	// close and reopen
   322  	store.Shutdown()
   323  	provider.Close()
   324  	provider, err = NewProvider(blockStoreDir, conf, metricsProvider)
   325  	assert.NoError(t, err)
   326  	store, err = provider.Open("testLedger")
   327  	assert.NoError(t, err)
   328  	store.Init(btlPolicyForSampleData())
   329  
   330  	// pvtdataStore should be ahead of blockstore
   331  	info, err = store.GetBlockchainInfo()
   332  	assert.NoError(t, err)
   333  	assert.Equal(t, uint64(9), info.Height)
   334  	pvtStoreHt, err = store.pvtdataStore.LastCommittedBlockHeight()
   335  	assert.NoError(t, err)
   336  	assert.Equal(t, uint64(10), pvtStoreHt)
   337  	assert.True(t, store.IsPvtStoreAheadOfBlockStore())
   338  
   339  	// bring the height of BlockStore equal to pvtdataStore
   340  	assert.NoError(t, store.CommitWithPvtData(lastBlkAndPvtData))
   341  	info, err = store.GetBlockchainInfo()
   342  	assert.NoError(t, err)
   343  	assert.Equal(t, uint64(10), info.Height)
   344  	pvtStoreHt, err = store.pvtdataStore.LastCommittedBlockHeight()
   345  	assert.NoError(t, err)
   346  	assert.Equal(t, uint64(10), pvtStoreHt)
   347  	assert.False(t, store.IsPvtStoreAheadOfBlockStore())
   348  }
   349  
   350  func TestConstructPvtdataMap(t *testing.T) {
   351  	assert.Nil(t, constructPvtdataMap(nil))
   352  }
   353  
   354  func sampleDataWithPvtdataForSelectiveTx(t *testing.T) []*ledger.BlockAndPvtData {
   355  	var blockAndpvtdata []*ledger.BlockAndPvtData
   356  	blocks := testutil.ConstructTestBlocks(t, 10)
   357  	for i := 0; i < 10; i++ {
   358  		blockAndpvtdata = append(blockAndpvtdata, &ledger.BlockAndPvtData{Block: blocks[i]})
   359  	}
   360  
   361  	// txNum 3, 5, 6 in block 2 has pvtdata but txNum 6 is invalid
   362  	blockAndpvtdata[2].PvtData = samplePvtData(t, []uint64{3, 5, 6})
   363  	txFilter := lutil.TxValidationFlags(blockAndpvtdata[2].Block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER])
   364  	txFilter.SetFlag(6, pb.TxValidationCode_INVALID_WRITESET)
   365  	blockAndpvtdata[2].Block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txFilter
   366  
   367  	// txNum 4, 6 in block 3 has pvtdata
   368  	blockAndpvtdata[3].PvtData = samplePvtData(t, []uint64{4, 6})
   369  
   370  	// txNum 4, 5 in block 5 has missing pvt data but txNum 5 is invalid
   371  	missingData := make(ledger.TxMissingPvtDataMap)
   372  	missingData.Add(4, "ns-4", "coll-4", true)
   373  	missingData.Add(5, "ns-5", "coll-5", true)
   374  	blockAndpvtdata[5].MissingPvtData = missingData
   375  	txFilter = lutil.TxValidationFlags(blockAndpvtdata[5].Block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER])
   376  	txFilter.SetFlag(5, pb.TxValidationCode_INVALID_WRITESET)
   377  	blockAndpvtdata[5].Block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txFilter
   378  
   379  	return blockAndpvtdata
   380  }
   381  
   382  func sampleDataWithPvtdataForAllTxs(t *testing.T) []*ledger.BlockAndPvtData {
   383  	var blockAndpvtdata []*ledger.BlockAndPvtData
   384  	blocks := testutil.ConstructTestBlocks(t, 10)
   385  	for i := 0; i < 10; i++ {
   386  		blockAndpvtdata = append(blockAndpvtdata,
   387  			&ledger.BlockAndPvtData{
   388  				Block:   blocks[i],
   389  				PvtData: samplePvtData(t, []uint64{uint64(i), uint64(i + 1)}),
   390  			},
   391  		)
   392  	}
   393  	return blockAndpvtdata
   394  }
   395  
   396  func samplePvtData(t *testing.T, txNums []uint64) map[uint64]*ledger.TxPvtData {
   397  	pvtWriteSet := &rwset.TxPvtReadWriteSet{DataModel: rwset.TxReadWriteSet_KV}
   398  	pvtWriteSet.NsPvtRwset = []*rwset.NsPvtReadWriteSet{
   399  		{
   400  			Namespace: "ns-1",
   401  			CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{
   402  				{
   403  					CollectionName: "coll-1",
   404  					Rwset:          []byte("RandomBytes-PvtRWSet-ns1-coll1"),
   405  				},
   406  				{
   407  					CollectionName: "coll-2",
   408  					Rwset:          []byte("RandomBytes-PvtRWSet-ns1-coll2"),
   409  				},
   410  			},
   411  		},
   412  	}
   413  	var pvtData []*ledger.TxPvtData
   414  	for _, txNum := range txNums {
   415  		pvtData = append(pvtData, &ledger.TxPvtData{SeqInBlock: txNum, WriteSet: pvtWriteSet})
   416  	}
   417  	return constructPvtdataMap(pvtData)
   418  }
   419  
   420  func btlPolicyForSampleData() pvtdatapolicy.BTLPolicy {
   421  	return btltestutil.SampleBTLPolicy(
   422  		map[[2]string]uint64{
   423  			{"ns-1", "coll-1"}: 0,
   424  			{"ns-1", "coll-2"}: 0,
   425  		},
   426  	)
   427  }
   428  
   429  func buildPrivateDataConfig(rootFSPath string) *pvtdatastorage.PrivateDataConfig {
   430  	return &pvtdatastorage.PrivateDataConfig{
   431  		PrivateDataConfig: &ledger.PrivateDataConfig{
   432  			PurgeInterval: 1,
   433  		},
   434  		StorePath: filepath.Join(rootFSPath, "pvtdataStore"),
   435  	}
   436  }