github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/core/ledger/kvledger/txmgmt/privacyenabledstate/snapshot_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package privacyenabledstate
     8  
     9  import (
    10  	"crypto/sha256"
    11  	"fmt"
    12  	"hash"
    13  	"io/ioutil"
    14  	"os"
    15  	"path/filepath"
    16  	"strings"
    17  	"testing"
    18  
    19  	"github.com/osdi23p228/fabric/common/ledger/snapshot"
    20  	"github.com/osdi23p228/fabric/core/ledger/internal/version"
    21  	"github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/statedb"
    22  	"github.com/stretchr/testify/require"
    23  )
    24  
    25  var (
    26  	testNewHashFunc = func() (hash.Hash, error) {
    27  		return sha256.New(), nil
    28  	}
    29  )
    30  
    31  func TestSnapshot(t *testing.T) {
    32  	for _, env := range testEnvs {
    33  		if _, ok := env.(*LevelDBTestEnv); !ok {
    34  			continue
    35  		}
    36  		t.Run(env.GetName(), func(t *testing.T) {
    37  			testSanpshot(t, env)
    38  		})
    39  	}
    40  }
    41  
    42  func testSanpshot(t *testing.T, env TestEnv) {
    43  	// generateSampleData returns a slice of KVs. The returned value contains five KVs for each of the namespaces
    44  	generateSampleData := func(namespaces ...string) []*statedb.VersionedKV {
    45  		sampleData := []*statedb.VersionedKV{}
    46  		for _, ns := range namespaces {
    47  			for i := 0; i < 5; i++ {
    48  				sampleKV := &statedb.VersionedKV{
    49  					CompositeKey: statedb.CompositeKey{
    50  						Namespace: ns,
    51  						Key:       fmt.Sprintf("key-%d", i),
    52  					},
    53  					VersionedValue: statedb.VersionedValue{
    54  						Value:    []byte(fmt.Sprintf("value-for-key-%d-for-%s", i, ns)),
    55  						Version:  version.NewHeight(1, 1),
    56  						Metadata: []byte(fmt.Sprintf("metadata-for-key-%d-for-%s", i, ns)),
    57  					},
    58  				}
    59  				sampleData = append(sampleData, sampleKV)
    60  			}
    61  		}
    62  		return sampleData
    63  	}
    64  	samplePublicState := generateSampleData(
    65  		"",
    66  		"ns1",
    67  		"ns2",
    68  		"ns4",
    69  	)
    70  
    71  	samplePvtStateHashes := generateSampleData(
    72  		deriveHashedDataNs("", "coll1"),
    73  		deriveHashedDataNs("ns1", "coll1"),
    74  		deriveHashedDataNs("ns1", "coll2"),
    75  		deriveHashedDataNs("ns2", "coll3"),
    76  		deriveHashedDataNs("ns3", "coll1"),
    77  	)
    78  
    79  	samplePvtState := generateSampleData(
    80  		derivePvtDataNs("", "coll1"),
    81  		derivePvtDataNs("ns1", "coll1"),
    82  		derivePvtDataNs("ns1", "coll2"),
    83  		derivePvtDataNs("ns2", "coll3"),
    84  		derivePvtDataNs("ns3", "coll1"),
    85  	)
    86  
    87  	testSnapshotWithSampleData(t, env, nil, nil, nil)                                           // no data
    88  	testSnapshotWithSampleData(t, env, samplePublicState, nil, nil)                             // test with only public data
    89  	testSnapshotWithSampleData(t, env, nil, samplePvtStateHashes, nil)                          // test with only pvtdata hashes
    90  	testSnapshotWithSampleData(t, env, samplePublicState, samplePvtStateHashes, nil)            // test with public data and pvtdata hashes
    91  	testSnapshotWithSampleData(t, env, samplePublicState, samplePvtStateHashes, samplePvtState) // test with public data, pvtdata hashes, and pvt data
    92  }
    93  
    94  func testSnapshotWithSampleData(t *testing.T, env TestEnv,
    95  	publicState []*statedb.VersionedKV,
    96  	pvtStateHashes []*statedb.VersionedKV,
    97  	pvtState []*statedb.VersionedKV,
    98  ) {
    99  	env.Init(t)
   100  	defer env.Cleanup()
   101  	db := env.GetDBHandle(generateLedgerID(t))
   102  
   103  	// load data into statedb
   104  	updateBatch := NewUpdateBatch()
   105  	for _, s := range publicState {
   106  		updateBatch.PubUpdates.PutValAndMetadata(s.Namespace, s.Key, s.Value, s.Metadata, s.Version)
   107  	}
   108  	for _, s := range pvtStateHashes {
   109  		nsColl := strings.Split(s.Namespace, nsJoiner+hashDataPrefix)
   110  		ns := nsColl[0]
   111  		coll := nsColl[1]
   112  		updateBatch.HashUpdates.PutValHashAndMetadata(ns, coll, []byte(s.Key), s.Value, s.Metadata, s.Version)
   113  	}
   114  	for _, s := range pvtState {
   115  		nsColl := strings.Split(s.Namespace, nsJoiner+pvtDataPrefix)
   116  		ns := nsColl[0]
   117  		coll := nsColl[1]
   118  		updateBatch.PvtUpdates.Put(ns, coll, s.Key, s.Value, s.Version)
   119  	}
   120  	err := db.ApplyPrivacyAwareUpdates(updateBatch, version.NewHeight(2, 2))
   121  	require.NoError(t, err)
   122  
   123  	// export snapshot files for statedb
   124  	snapshotDir, err := ioutil.TempDir("", "testsnapshot")
   125  	require.NoError(t, err)
   126  	defer func() {
   127  		os.RemoveAll(snapshotDir)
   128  	}()
   129  
   130  	filesAndHashes, err := db.ExportPubStateAndPvtStateHashes(snapshotDir, testNewHashFunc)
   131  	require.NoError(t, err)
   132  
   133  	for f, h := range filesAndHashes {
   134  		expectedFile := filepath.Join(snapshotDir, f)
   135  		require.FileExists(t, expectedFile)
   136  		require.Equal(t, sha256ForFileForTest(t, expectedFile), h)
   137  	}
   138  
   139  	numFilesExpected := 0
   140  	if len(publicState) != 0 {
   141  		numFilesExpected += 2
   142  		require.Contains(t, filesAndHashes, pubStateDataFileName)
   143  		require.Contains(t, filesAndHashes, pubStateMetadataFileName)
   144  		// verify snapshot files contents
   145  		pubStateFromSnapshot := loadSnapshotDataForTest(t,
   146  			env,
   147  			filepath.Join(snapshotDir, pubStateDataFileName),
   148  			filepath.Join(snapshotDir, pubStateMetadataFileName),
   149  		)
   150  		require.Equal(t, publicState, pubStateFromSnapshot)
   151  	}
   152  
   153  	if len(pvtStateHashes) != 0 {
   154  		numFilesExpected += 2
   155  		require.Contains(t, filesAndHashes, pvtStateHashesFileName)
   156  		require.Contains(t, filesAndHashes, pvtStateHashesMetadataFileName)
   157  		// verify snapshot files contents
   158  		pvtStateHashesFromSnapshot := loadSnapshotDataForTest(t,
   159  			env,
   160  			filepath.Join(snapshotDir, pvtStateHashesFileName),
   161  			filepath.Join(snapshotDir, pvtStateHashesMetadataFileName),
   162  		)
   163  
   164  		require.Equal(t, pvtStateHashes, pvtStateHashesFromSnapshot)
   165  	}
   166  	require.Len(t, filesAndHashes, numFilesExpected)
   167  }
   168  
   169  func sha256ForFileForTest(t *testing.T, file string) []byte {
   170  	data, err := ioutil.ReadFile(file)
   171  	require.NoError(t, err)
   172  	sha := sha256.Sum256(data)
   173  	return sha[:]
   174  }
   175  
   176  func loadSnapshotDataForTest(
   177  	t *testing.T,
   178  	testenv TestEnv,
   179  	dataFilePath, metadataFilePath string) []*statedb.VersionedKV {
   180  	dataFile, err := snapshot.OpenFile(dataFilePath, snapshotFileFormat)
   181  	require.NoError(t, err)
   182  	defer dataFile.Close()
   183  	dbValueFormat, err := dataFile.DecodeBytes()
   184  	require.NoError(t, err)
   185  	require.Equal(t, []byte{testenv.DBValueFormat()}, dbValueFormat)
   186  
   187  	metadataFile, err := snapshot.OpenFile(metadataFilePath, snapshotFileFormat)
   188  	require.NoError(t, err)
   189  	defer metadataFile.Close()
   190  	numMetadataEntries, err := metadataFile.DecodeUVarInt()
   191  	require.NoError(t, err)
   192  	if numMetadataEntries == 0 {
   193  		return nil
   194  	}
   195  	data := []*statedb.VersionedKV{}
   196  	for i := uint64(0); i < numMetadataEntries; i++ {
   197  		ns, err := metadataFile.DecodeString()
   198  		require.NoError(t, err)
   199  		numKVs, err := metadataFile.DecodeUVarInt()
   200  		require.NoError(t, err)
   201  		for j := uint64(0); j < numKVs; j++ {
   202  			key, err := dataFile.DecodeString()
   203  			require.NoError(t, err)
   204  			dbValue, err := dataFile.DecodeBytes()
   205  			require.NoError(t, err)
   206  			ck := statedb.CompositeKey{
   207  				Namespace: ns,
   208  				Key:       key,
   209  			}
   210  			data = append(data, &statedb.VersionedKV{
   211  				CompositeKey:   ck,
   212  				VersionedValue: testenv.DecodeDBValue(dbValue),
   213  			})
   214  		}
   215  	}
   216  	return data
   217  }
   218  
   219  func TestSnapshotErrorPropagation(t *testing.T) {
   220  	var dbEnv *LevelDBTestEnv
   221  	var snapshotDir string
   222  	var db *DB
   223  	var cleanup func()
   224  	var err error
   225  	init := func() {
   226  		dbEnv = &LevelDBTestEnv{}
   227  		dbEnv.Init(t)
   228  		db = dbEnv.GetDBHandle(generateLedgerID(t))
   229  		updateBatch := NewUpdateBatch()
   230  		updateBatch.PubUpdates.Put("ns1", "key1", []byte("value1"), version.NewHeight(1, 1))
   231  		updateBatch.HashUpdates.Put("ns1", "coll1", []byte("key1"), []byte("value1"), version.NewHeight(1, 1))
   232  		db.ApplyPrivacyAwareUpdates(updateBatch, version.NewHeight(1, 1))
   233  		snapshotDir, err = ioutil.TempDir("", "testsnapshot")
   234  		require.NoError(t, err)
   235  		cleanup = func() {
   236  			dbEnv.Cleanup()
   237  			os.RemoveAll(snapshotDir)
   238  		}
   239  	}
   240  
   241  	reinit := func() {
   242  		cleanup()
   243  		init()
   244  	}
   245  
   246  	// pubStateDataFile already exists
   247  	init()
   248  	defer cleanup()
   249  	pubStateDataFilePath := filepath.Join(snapshotDir, pubStateDataFileName)
   250  	_, err = os.Create(pubStateDataFilePath)
   251  	require.NoError(t, err)
   252  	_, err = db.ExportPubStateAndPvtStateHashes(snapshotDir, testNewHashFunc)
   253  	require.Contains(t, err.Error(), "error while creating the snapshot file: "+pubStateDataFilePath)
   254  
   255  	// pubStateMetadataFile already exists
   256  	reinit()
   257  	pubStateMetadataFilePath := filepath.Join(snapshotDir, pubStateMetadataFileName)
   258  	_, err = os.Create(pubStateMetadataFilePath)
   259  	require.NoError(t, err)
   260  	_, err = db.ExportPubStateAndPvtStateHashes(snapshotDir, testNewHashFunc)
   261  	require.Contains(t, err.Error(), "error while creating the snapshot file: "+pubStateMetadataFilePath)
   262  
   263  	// pvtStateHashesDataFile already exists
   264  	reinit()
   265  	pvtStateHashesDataFilePath := filepath.Join(snapshotDir, pvtStateHashesFileName)
   266  	_, err = os.Create(pvtStateHashesDataFilePath)
   267  	require.NoError(t, err)
   268  	_, err = db.ExportPubStateAndPvtStateHashes(snapshotDir, testNewHashFunc)
   269  	require.Contains(t, err.Error(), "error while creating the snapshot file: "+pvtStateHashesDataFilePath)
   270  
   271  	// pvtStateHashesMetadataFile already exists
   272  	reinit()
   273  	pvtStateHashesMetadataFilePath := filepath.Join(snapshotDir, pvtStateHashesMetadataFileName)
   274  	_, err = os.Create(pvtStateHashesMetadataFilePath)
   275  	require.NoError(t, err)
   276  	_, err = db.ExportPubStateAndPvtStateHashes(snapshotDir, testNewHashFunc)
   277  	require.Contains(t, err.Error(), "error while creating the snapshot file: "+pvtStateHashesMetadataFilePath)
   278  
   279  	reinit()
   280  	dbEnv.provider.Close()
   281  	_, err = db.ExportPubStateAndPvtStateHashes(snapshotDir, testNewHashFunc)
   282  	require.Contains(t, err.Error(), "internal leveldb error while obtaining db iterator:")
   283  }