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 }