github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/core/ledger/pvtdatastorage/reconcile_missing_pvtdata_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package pvtdatastorage
     8  
     9  import (
    10  	"fmt"
    11  	"testing"
    12  
    13  	"github.com/osdi23p228/fabric/core/ledger"
    14  	btltestutil "github.com/osdi23p228/fabric/core/ledger/pvtdatapolicy/testutil"
    15  	"github.com/stretchr/testify/require"
    16  	"github.com/willf/bitset"
    17  )
    18  
    19  type blockTxPvtDataInfoForTest struct {
    20  	blkNum         uint64
    21  	txNum          uint64
    22  	pvtDataPresent map[string][]string
    23  	pvtDataMissing map[string][]string
    24  }
    25  
    26  type pvtDataForTest struct {
    27  	blockNum        uint64
    28  	pvtData         []*ledger.TxPvtData
    29  	dataKeys        []*dataKey
    30  	missingDataInfo ledger.TxMissingPvtDataMap
    31  }
    32  
    33  func TestCommitPvtDataOfOldBlocks(t *testing.T) {
    34  	btlPolicy := btltestutil.SampleBTLPolicy(
    35  		map[[2]string]uint64{
    36  			{"ns-1", "coll-1"}: 0,
    37  			{"ns-1", "coll-2"}: 0,
    38  			{"ns-2", "coll-1"}: 0,
    39  			{"ns-2", "coll-2"}: 0,
    40  		},
    41  	)
    42  	env := NewTestStoreEnv(t, "TestCommitPvtDataOfOldBlocks", btlPolicy, pvtDataConf())
    43  	defer env.Cleanup()
    44  	store := env.TestStore
    45  
    46  	blockTxPvtDataInfo := []*blockTxPvtDataInfoForTest{
    47  		{
    48  			blkNum: 1,
    49  			txNum:  1,
    50  			pvtDataMissing: map[string][]string{
    51  				"ns-1": {"coll-1", "coll-2"},
    52  				"ns-2": {"coll-1", "coll-2"},
    53  			},
    54  		},
    55  		{
    56  			blkNum: 1,
    57  			txNum:  2,
    58  			pvtDataPresent: map[string][]string{
    59  				"ns-2": {"coll-1", "coll-2"},
    60  			},
    61  			pvtDataMissing: map[string][]string{
    62  				"ns-1": {"coll-1", "coll-2"},
    63  			},
    64  		},
    65  		{
    66  			blkNum: 1,
    67  			txNum:  4,
    68  			pvtDataPresent: map[string][]string{
    69  				"ns-1": {"coll-1", "coll-2"},
    70  				"ns-2": {"coll-1", "coll-2"},
    71  			},
    72  		},
    73  		{
    74  			blkNum: 2,
    75  			txNum:  1,
    76  			pvtDataMissing: map[string][]string{
    77  				"ns-1": {"coll-1", "coll-2"},
    78  			},
    79  		},
    80  		{
    81  			blkNum: 2,
    82  			txNum:  3,
    83  			pvtDataMissing: map[string][]string{
    84  				"ns-1": {"coll-1"},
    85  			},
    86  		},
    87  	}
    88  
    89  	blocksPvtData, missingDataSummary := constructPvtDataForTest(t, blockTxPvtDataInfo)
    90  
    91  	require.NoError(t, store.Commit(0, nil, nil))
    92  	require.NoError(t, store.Commit(1, blocksPvtData[1].pvtData, blocksPvtData[1].missingDataInfo))
    93  	require.NoError(t, store.Commit(2, blocksPvtData[2].pvtData, blocksPvtData[2].missingDataInfo))
    94  
    95  	assertMissingDataInfo(t, store, missingDataSummary, 2)
    96  
    97  	// COMMIT some of the missing data in the block 1 and block 2
    98  	oldBlockTxPvtDataInfo := []*blockTxPvtDataInfoForTest{
    99  		{
   100  			blkNum: 1,
   101  			txNum:  1,
   102  			pvtDataPresent: map[string][]string{
   103  				"ns-1": {"coll-1"},
   104  				"ns-2": {"coll-1"},
   105  			},
   106  			pvtDataMissing: map[string][]string{
   107  				"ns-1": {"coll-2"},
   108  				"ns-2": {"coll-2"},
   109  			},
   110  		},
   111  		{
   112  			blkNum: 1,
   113  			txNum:  2,
   114  			pvtDataPresent: map[string][]string{
   115  				"ns-1": {"coll-1"},
   116  			},
   117  			pvtDataMissing: map[string][]string{
   118  				"ns-1": {"coll-2"},
   119  			},
   120  		},
   121  		{
   122  			blkNum: 2,
   123  			txNum:  1,
   124  			pvtDataMissing: map[string][]string{
   125  				"ns-1": {"coll-1", "coll-2"},
   126  			},
   127  		},
   128  		{
   129  			blkNum: 2,
   130  			txNum:  3,
   131  			pvtDataPresent: map[string][]string{
   132  				"ns-1": {"coll-1"},
   133  			},
   134  		},
   135  	}
   136  
   137  	blocksPvtData, missingDataSummary = constructPvtDataForTest(t, oldBlockTxPvtDataInfo)
   138  	oldBlocksPvtData := map[uint64][]*ledger.TxPvtData{
   139  		1: blocksPvtData[1].pvtData,
   140  		2: blocksPvtData[2].pvtData,
   141  	}
   142  	require.NoError(t, store.CommitPvtDataOfOldBlocks(oldBlocksPvtData, nil))
   143  
   144  	for _, b := range blocksPvtData {
   145  		for _, dkey := range b.dataKeys {
   146  			require.True(t, testDataKeyExists(t, store, dkey))
   147  		}
   148  	}
   149  	assertMissingDataInfo(t, store, missingDataSummary, 2)
   150  }
   151  
   152  func TestCommitPvtDataOfOldBlocksWithBTL(t *testing.T) {
   153  	setup := func(store *Store) {
   154  		blockTxPvtDataInfo := []*blockTxPvtDataInfoForTest{
   155  			{
   156  				blkNum: 1,
   157  				txNum:  1,
   158  				pvtDataMissing: map[string][]string{
   159  					"ns-1": {"coll-1"},
   160  					"ns-2": {"coll-1"},
   161  				},
   162  			},
   163  			{
   164  				blkNum: 1,
   165  				txNum:  2,
   166  				pvtDataMissing: map[string][]string{
   167  					"ns-1": {"coll-1"},
   168  					"ns-2": {"coll-1"},
   169  				},
   170  			},
   171  			{
   172  				blkNum: 1,
   173  				txNum:  3,
   174  				pvtDataMissing: map[string][]string{
   175  					"ns-1": {"coll-1"},
   176  					"ns-2": {"coll-1"},
   177  				},
   178  			},
   179  		}
   180  
   181  		blocksPvtData, missingDataSummary := constructPvtDataForTest(t, blockTxPvtDataInfo)
   182  
   183  		require.NoError(t, store.Commit(0, nil, nil))
   184  		require.NoError(t, store.Commit(1, blocksPvtData[1].pvtData, blocksPvtData[1].missingDataInfo))
   185  
   186  		assertMissingDataInfo(t, store, missingDataSummary, 1)
   187  
   188  		// COMMIT BLOCK 2 & 3 WITH NO PVTDATA
   189  		require.NoError(t, store.Commit(2, nil, nil))
   190  		require.NoError(t, store.Commit(3, nil, nil))
   191  	}
   192  
   193  	t.Run("expired but not purged", func(t *testing.T) {
   194  		btlPolicy := btltestutil.SampleBTLPolicy(
   195  			map[[2]string]uint64{
   196  				{"ns-1", "coll-1"}: 1,
   197  				{"ns-2", "coll-1"}: 1,
   198  			},
   199  		)
   200  		env := NewTestStoreEnv(t, "TestCommitPvtDataOfOldBlocksWithBTL", btlPolicy, pvtDataConf())
   201  		defer env.Cleanup()
   202  		store := env.TestStore
   203  
   204  		setup(store)
   205  		// in block 1, ns-1:coll-1 and ns-2:coll-2 should have expired but not purged.
   206  		// hence, the commit of pvtdata of block 1 transaction 1 should create entries
   207  		// in the store
   208  		oldBlockTxPvtDataInfo := []*blockTxPvtDataInfoForTest{
   209  			{
   210  				blkNum: 1,
   211  				txNum:  1,
   212  				pvtDataPresent: map[string][]string{
   213  					"ns-1": {"coll-1"},
   214  					"ns-2": {"coll-1"},
   215  				},
   216  			},
   217  		}
   218  
   219  		blocksPvtData, _ := constructPvtDataForTest(t, oldBlockTxPvtDataInfo)
   220  		oldBlocksPvtData := map[uint64][]*ledger.TxPvtData{
   221  			1: blocksPvtData[1].pvtData,
   222  		}
   223  		deprioritizedList := ledger.MissingPvtDataInfo{
   224  			1: ledger.MissingBlockPvtdataInfo{
   225  				2: {
   226  					{
   227  						Namespace:  "ns-1",
   228  						Collection: "coll-1",
   229  					},
   230  					{
   231  						Namespace:  "ns-2",
   232  						Collection: "coll-1",
   233  					},
   234  				},
   235  			},
   236  		}
   237  		require.NoError(t, store.CommitPvtDataOfOldBlocks(oldBlocksPvtData, deprioritizedList))
   238  
   239  		for _, b := range blocksPvtData {
   240  			for _, dkey := range b.dataKeys {
   241  				require.True(t, testDataKeyExists(t, store, dkey))
   242  			}
   243  		}
   244  		// as all missing data are expired, get missing info would return nil though
   245  		// it is not purged yet
   246  		assertMissingDataInfo(t, store, make(ledger.MissingPvtDataInfo), 1)
   247  
   248  		// deprioritized list should be present
   249  		tests := []struct {
   250  			key            nsCollBlk
   251  			expectedBitmap *bitset.BitSet
   252  		}{
   253  			{
   254  				key: nsCollBlk{
   255  					ns:     "ns-1",
   256  					coll:   "coll-1",
   257  					blkNum: 1,
   258  				},
   259  				expectedBitmap: constructBitSetForTest(2),
   260  			},
   261  			{
   262  				key: nsCollBlk{
   263  					ns:     "ns-2",
   264  					coll:   "coll-1",
   265  					blkNum: 1,
   266  				},
   267  				expectedBitmap: constructBitSetForTest(2),
   268  			},
   269  		}
   270  
   271  		for _, tt := range tests {
   272  			encKey := encodeElgDeprioMissingDataKey(&missingDataKey{tt.key})
   273  			missingData, err := store.db.Get(encKey)
   274  			require.NoError(t, err)
   275  
   276  			expectedMissingData, err := encodeMissingDataValue(tt.expectedBitmap)
   277  			require.NoError(t, err)
   278  			require.Equal(t, expectedMissingData, missingData)
   279  		}
   280  	})
   281  
   282  	t.Run("expired and purged", func(t *testing.T) {
   283  		btlPolicy := btltestutil.SampleBTLPolicy(
   284  			map[[2]string]uint64{
   285  				{"ns-1", "coll-1"}: 1,
   286  				{"ns-2", "coll-1"}: 1,
   287  			},
   288  		)
   289  		env := NewTestStoreEnv(t, "TestCommitPvtDataOfOldBlocksWithBTL", btlPolicy, pvtDataConf())
   290  		defer env.Cleanup()
   291  		store := env.TestStore
   292  
   293  		setup(store)
   294  		require.NoError(t, store.Commit(4, nil, nil))
   295  
   296  		testWaitForPurgerRoutineToFinish(store)
   297  
   298  		// in block 1, ns-1:coll-1 and ns-2:coll-2 should have expired and purged.
   299  		// hence, the commit of pvtdata of block 1 transaction 2 should not create
   300  		// entries in the store
   301  		oldBlockTxPvtDataInfo := []*blockTxPvtDataInfoForTest{
   302  			{
   303  				blkNum: 1,
   304  				txNum:  2,
   305  				pvtDataPresent: map[string][]string{
   306  					"ns-1": {"coll-1"},
   307  					"ns-2": {"coll-1"},
   308  				},
   309  			},
   310  		}
   311  		blocksPvtData, _ := constructPvtDataForTest(t, oldBlockTxPvtDataInfo)
   312  		oldBlocksPvtData := map[uint64][]*ledger.TxPvtData{
   313  			1: blocksPvtData[1].pvtData,
   314  		}
   315  		deprioritizedList := ledger.MissingPvtDataInfo{
   316  			1: ledger.MissingBlockPvtdataInfo{
   317  				3: {
   318  					{
   319  						Namespace:  "ns-1",
   320  						Collection: "coll-1",
   321  					},
   322  					{
   323  						Namespace:  "ns-2",
   324  						Collection: "coll-1",
   325  					},
   326  				},
   327  			},
   328  		}
   329  		require.NoError(t, store.CommitPvtDataOfOldBlocks(oldBlocksPvtData, deprioritizedList))
   330  
   331  		for _, b := range blocksPvtData {
   332  			for _, dkey := range b.dataKeys {
   333  				require.False(t, testDataKeyExists(t, store, dkey))
   334  			}
   335  		}
   336  
   337  		// deprioritized list should not be present
   338  		keys := []nsCollBlk{
   339  			{
   340  				ns:     "ns-1",
   341  				coll:   "coll-1",
   342  				blkNum: 1,
   343  			},
   344  			{
   345  				ns:     "ns-2",
   346  				coll:   "coll-1",
   347  				blkNum: 1,
   348  			},
   349  		}
   350  
   351  		for _, k := range keys {
   352  			encKey := encodeElgDeprioMissingDataKey(&missingDataKey{k})
   353  			missingData, err := store.db.Get(encKey)
   354  			require.NoError(t, err)
   355  			require.Nil(t, missingData)
   356  		}
   357  	})
   358  }
   359  
   360  func TestCommitPvtDataOfOldBlocksWithDeprioritization(t *testing.T) {
   361  	blockTxPvtDataInfo := []*blockTxPvtDataInfoForTest{
   362  		{
   363  			blkNum: 1,
   364  			txNum:  1,
   365  			pvtDataMissing: map[string][]string{
   366  				"ns-1": {"coll-1"},
   367  				"ns-2": {"coll-1"},
   368  			},
   369  		},
   370  		{
   371  			blkNum: 1,
   372  			txNum:  2,
   373  			pvtDataMissing: map[string][]string{
   374  				"ns-1": {"coll-1"},
   375  				"ns-2": {"coll-1"},
   376  			},
   377  		},
   378  		{
   379  			blkNum: 2,
   380  			txNum:  1,
   381  			pvtDataMissing: map[string][]string{
   382  				"ns-1": {"coll-1"},
   383  				"ns-2": {"coll-1"},
   384  			},
   385  		},
   386  		{
   387  			blkNum: 2,
   388  			txNum:  2,
   389  			pvtDataMissing: map[string][]string{
   390  				"ns-1": {"coll-1"},
   391  				"ns-2": {"coll-1"},
   392  			},
   393  		},
   394  	}
   395  
   396  	blocksPvtData, missingDataSummary := constructPvtDataForTest(t, blockTxPvtDataInfo)
   397  
   398  	tests := []struct {
   399  		name                        string
   400  		deprioritizedList           ledger.MissingPvtDataInfo
   401  		expectedPrioMissingDataKeys ledger.MissingPvtDataInfo
   402  	}{
   403  		{
   404  			name:                        "all keys deprioritized",
   405  			deprioritizedList:           missingDataSummary,
   406  			expectedPrioMissingDataKeys: make(ledger.MissingPvtDataInfo),
   407  		},
   408  		{
   409  			name: "some keys deprioritized",
   410  			deprioritizedList: ledger.MissingPvtDataInfo{
   411  				1: ledger.MissingBlockPvtdataInfo{
   412  					2: {
   413  						{
   414  							Namespace:  "ns-1",
   415  							Collection: "coll-1",
   416  						},
   417  					},
   418  				},
   419  				2: ledger.MissingBlockPvtdataInfo{
   420  					2: {
   421  						{
   422  							Namespace:  "ns-1",
   423  							Collection: "coll-1",
   424  						},
   425  					},
   426  				},
   427  			},
   428  			expectedPrioMissingDataKeys: ledger.MissingPvtDataInfo{
   429  				1: ledger.MissingBlockPvtdataInfo{
   430  					1: {
   431  						{
   432  							Namespace:  "ns-1",
   433  							Collection: "coll-1",
   434  						},
   435  						{
   436  							Namespace:  "ns-2",
   437  							Collection: "coll-1",
   438  						},
   439  					},
   440  					2: {
   441  						{
   442  							Namespace:  "ns-2",
   443  							Collection: "coll-1",
   444  						},
   445  					},
   446  				},
   447  				2: ledger.MissingBlockPvtdataInfo{
   448  					1: {
   449  						{
   450  							Namespace:  "ns-1",
   451  							Collection: "coll-1",
   452  						},
   453  						{
   454  							Namespace:  "ns-2",
   455  							Collection: "coll-1",
   456  						},
   457  					},
   458  					2: {
   459  						{
   460  							Namespace:  "ns-2",
   461  							Collection: "coll-1",
   462  						},
   463  					},
   464  				},
   465  			},
   466  		},
   467  	}
   468  
   469  	for _, tt := range tests {
   470  		t.Run(tt.name, func(t *testing.T) {
   471  			btlPolicy := btltestutil.SampleBTLPolicy(
   472  				map[[2]string]uint64{
   473  					{"ns-1", "coll-1"}: 0,
   474  					{"ns-2", "coll-1"}: 0,
   475  				},
   476  			)
   477  			env := NewTestStoreEnv(t, "TestCommitPvtDataOfOldBlocksWithDeprio", btlPolicy, pvtDataConf())
   478  			defer env.Cleanup()
   479  			store := env.TestStore
   480  
   481  			// COMMIT BLOCK 0 WITH NO DATA
   482  			require.NoError(t, store.Commit(0, nil, nil))
   483  			require.NoError(t, store.Commit(1, blocksPvtData[1].pvtData, blocksPvtData[1].missingDataInfo))
   484  			require.NoError(t, store.Commit(2, blocksPvtData[2].pvtData, blocksPvtData[2].missingDataInfo))
   485  
   486  			assertMissingDataInfo(t, store, missingDataSummary, 2)
   487  
   488  			require.NoError(t, store.CommitPvtDataOfOldBlocks(nil, tt.deprioritizedList))
   489  
   490  			prioMissingData, err := store.getMissingData(elgPrioritizedMissingDataGroup, 3)
   491  			require.NoError(t, err)
   492  			require.Equal(t, len(tt.expectedPrioMissingDataKeys), len(prioMissingData))
   493  			for blkNum, txsMissingData := range tt.expectedPrioMissingDataKeys {
   494  				for txNum, expectedMissingData := range txsMissingData {
   495  					require.ElementsMatch(t, expectedMissingData, prioMissingData[blkNum][txNum])
   496  				}
   497  			}
   498  
   499  			deprioMissingData, err := store.getMissingData(elgDeprioritizedMissingDataGroup, 3)
   500  			require.NoError(t, err)
   501  			require.Equal(t, len(tt.deprioritizedList), len(deprioMissingData))
   502  			for blkNum, txsMissingData := range tt.deprioritizedList {
   503  				for txNum, expectedMissingData := range txsMissingData {
   504  					require.ElementsMatch(t, expectedMissingData, deprioMissingData[blkNum][txNum])
   505  				}
   506  			}
   507  
   508  			oldBlockTxPvtDataInfo := []*blockTxPvtDataInfoForTest{
   509  				{
   510  					blkNum: 1,
   511  					txNum:  1,
   512  					pvtDataPresent: map[string][]string{
   513  						"ns-1": {"coll-1"},
   514  						"ns-2": {"coll-1"},
   515  					},
   516  				},
   517  				{
   518  					blkNum: 1,
   519  					txNum:  2,
   520  					pvtDataPresent: map[string][]string{
   521  						"ns-1": {"coll-1"},
   522  						"ns-2": {"coll-1"},
   523  					},
   524  				},
   525  				{
   526  					blkNum: 2,
   527  					txNum:  1,
   528  					pvtDataPresent: map[string][]string{
   529  						"ns-1": {"coll-1"},
   530  						"ns-2": {"coll-1"},
   531  					},
   532  				},
   533  				{
   534  					blkNum: 2,
   535  					txNum:  2,
   536  					pvtDataPresent: map[string][]string{
   537  						"ns-1": {"coll-1"},
   538  						"ns-2": {"coll-1"},
   539  					},
   540  				},
   541  			}
   542  
   543  			pvtDataOfOldBlocks, _ := constructPvtDataForTest(t, oldBlockTxPvtDataInfo)
   544  			oldBlocksPvtData := map[uint64][]*ledger.TxPvtData{
   545  				1: pvtDataOfOldBlocks[1].pvtData,
   546  				2: pvtDataOfOldBlocks[2].pvtData,
   547  			}
   548  			require.NoError(t, store.CommitPvtDataOfOldBlocks(oldBlocksPvtData, nil))
   549  
   550  			prioMissingData, err = store.getMissingData(elgPrioritizedMissingDataGroup, 3)
   551  			require.NoError(t, err)
   552  			require.Equal(t, make(ledger.MissingPvtDataInfo), prioMissingData)
   553  
   554  			deprioMissingData, err = store.getMissingData(elgDeprioritizedMissingDataGroup, 3)
   555  			require.NoError(t, err)
   556  			require.Equal(t, make(ledger.MissingPvtDataInfo), deprioMissingData)
   557  		})
   558  	}
   559  }
   560  
   561  func constructPvtDataForTest(t *testing.T, blockInfo []*blockTxPvtDataInfoForTest) (map[uint64]*pvtDataForTest, ledger.MissingPvtDataInfo) {
   562  	blocksPvtData := make(map[uint64]*pvtDataForTest)
   563  	missingPvtDataInfoSummary := make(ledger.MissingPvtDataInfo)
   564  
   565  	for _, b := range blockInfo {
   566  		p, ok := blocksPvtData[b.blkNum]
   567  		if !ok {
   568  			p = &pvtDataForTest{
   569  				missingDataInfo: make(ledger.TxMissingPvtDataMap),
   570  			}
   571  			blocksPvtData[b.blkNum] = p
   572  		}
   573  
   574  		for ns, colls := range b.pvtDataMissing {
   575  			for _, coll := range colls {
   576  				p.missingDataInfo.Add(b.txNum, ns, coll, true)
   577  				missingPvtDataInfoSummary.Add(b.blkNum, b.txNum, ns, coll)
   578  			}
   579  		}
   580  
   581  		var nsColls []string
   582  		for ns, colls := range b.pvtDataPresent {
   583  			for _, coll := range colls {
   584  				nsColls = append(nsColls, fmt.Sprintf("%s:%s", ns, coll))
   585  				p.dataKeys = append(p.dataKeys, &dataKey{
   586  					nsCollBlk: nsCollBlk{
   587  						ns:     ns,
   588  						coll:   coll,
   589  						blkNum: b.blkNum,
   590  					},
   591  					txNum: b.txNum,
   592  				})
   593  			}
   594  		}
   595  
   596  		if len(nsColls) == 0 {
   597  			continue
   598  		}
   599  		p.pvtData = append(
   600  			p.pvtData,
   601  			produceSamplePvtdata(t, b.txNum, nsColls),
   602  		)
   603  	}
   604  
   605  	return blocksPvtData, missingPvtDataInfoSummary
   606  }
   607  
   608  func assertMissingDataInfo(t *testing.T, store *Store, expected ledger.MissingPvtDataInfo, numRecentBlocks int) {
   609  	missingPvtDataInfo, err := store.GetMissingPvtDataInfoForMostRecentBlocks(numRecentBlocks)
   610  	require.NoError(t, err)
   611  	require.Equal(t, len(expected), len(missingPvtDataInfo))
   612  	for blkNum, txsMissingData := range expected {
   613  		for txNum, expectedMissingData := range txsMissingData {
   614  			require.ElementsMatch(t, expectedMissingData, missingPvtDataInfo[blkNum][txNum])
   615  		}
   616  	}
   617  }
   618  
   619  func constructBitSetForTest(txNums ...uint) *bitset.BitSet {
   620  	bitmap := &bitset.BitSet{}
   621  	for _, txNum := range txNums {
   622  		bitmap.Set(txNum)
   623  	}
   624  	return bitmap
   625  }