github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/txmgmt/privacyenabledstate/snapshot.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package privacyenabledstate 8 9 import ( 10 "encoding/base64" 11 "hash" 12 "path/filepath" 13 14 "github.com/hechain20/hechain/common/ledger/snapshot" 15 "github.com/hechain20/hechain/core/ledger/internal/version" 16 "github.com/hechain20/hechain/core/ledger/kvledger/bookkeeping" 17 "github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/statedb" 18 "github.com/hechain20/hechain/internal/fileutil" 19 "github.com/pkg/errors" 20 ) 21 22 const ( 23 snapshotFileFormat = byte(1) 24 PubStateDataFileName = "public_state.data" 25 PubStateMetadataFileName = "public_state.metadata" 26 PvtStateHashesFileName = "private_state_hashes.data" 27 PvtStateHashesMetadataFileName = "private_state_hashes.metadata" 28 ) 29 30 // ExportPubStateAndPvtStateHashes generates four files in the specified dir. The files, public_state.data and public_state.metadata 31 // contains the exported public state and the files private_state_hashes.data and private_state_hashes.metadata contain the exported private state hashes. 32 // The file format for public state and the private state hashes are the same. The data files contains a series serialized proto message SnapshotRecord 33 // and the metadata files contains a series of tuple <namespace, num entries for the namespace in the data file>. 34 func (s *DB) ExportPubStateAndPvtStateHashes(dir string, newHashFunc snapshot.NewHashFunc) (map[string][]byte, error) { 35 itr, err := s.GetFullScanIterator(isPvtdataNs) 36 if err != nil { 37 return nil, err 38 } 39 defer itr.Close() 40 41 var pubStateWriter *SnapshotWriter 42 var pvtStateHashesWriter *SnapshotWriter 43 for { 44 kv, err := itr.Next() 45 if err != nil { 46 return nil, err 47 } 48 if kv == nil { 49 break 50 } 51 52 namespace := kv.Namespace 53 snapshotRecord := &SnapshotRecord{ 54 Key: []byte(kv.Key), 55 Value: kv.Value, 56 Metadata: kv.Metadata, 57 Version: kv.Version.ToBytes(), 58 } 59 60 switch { 61 case isHashedDataNs(namespace): 62 if !s.BytesKeySupported() { 63 key, err := base64.StdEncoding.DecodeString(kv.Key) 64 if err != nil { 65 return nil, err 66 } 67 snapshotRecord.Key = key 68 } 69 70 if pvtStateHashesWriter == nil { // encountered first time the pvt state hash element 71 pvtStateHashesWriter, err = NewSnapshotWriter( 72 dir, 73 PvtStateHashesFileName, 74 PvtStateHashesMetadataFileName, 75 newHashFunc, 76 ) 77 if err != nil { 78 return nil, err 79 } 80 defer pvtStateHashesWriter.Close() 81 } 82 if err := pvtStateHashesWriter.AddData(namespace, snapshotRecord); err != nil { 83 return nil, err 84 } 85 default: 86 if pubStateWriter == nil { // encountered first time the pub state element 87 pubStateWriter, err = NewSnapshotWriter( 88 dir, 89 PubStateDataFileName, 90 PubStateMetadataFileName, 91 newHashFunc, 92 ) 93 if err != nil { 94 return nil, err 95 } 96 defer pubStateWriter.Close() 97 } 98 if err := pubStateWriter.AddData(namespace, snapshotRecord); err != nil { 99 return nil, err 100 } 101 } 102 } 103 104 snapshotFilesInfo := map[string][]byte{} 105 106 if pubStateWriter != nil { 107 pubStateDataHash, pubStateMetadataHash, err := pubStateWriter.Done() 108 if err != nil { 109 return nil, err 110 } 111 snapshotFilesInfo[PubStateDataFileName] = pubStateDataHash 112 snapshotFilesInfo[PubStateMetadataFileName] = pubStateMetadataHash 113 } 114 115 if pvtStateHashesWriter != nil { 116 pvtStateHahshesDataHash, pvtStateHashesMetadataHash, err := pvtStateHashesWriter.Done() 117 if err != nil { 118 return nil, err 119 } 120 snapshotFilesInfo[PvtStateHashesFileName] = pvtStateHahshesDataHash 121 snapshotFilesInfo[PvtStateHashesMetadataFileName] = pvtStateHashesMetadataHash 122 } 123 124 return snapshotFilesInfo, nil 125 } 126 127 // SnapshotWriter generates two files, a data file and a metadata file. The datafile contains a series of tuples <key, dbValue> 128 // and the metadata file contains a series of tuples <namesapce, number-of-tuples-in-the-data-file-that-belong-to-this-namespace> 129 type SnapshotWriter struct { 130 dataFile *snapshot.FileWriter 131 metadataFile *snapshot.FileWriter 132 metadata []*metadataRow 133 } 134 135 // NewSnapshotWriter creates a new SnapshotWriter 136 func NewSnapshotWriter( 137 dir, dataFileName, metadataFileName string, 138 newHash func() (hash.Hash, error), 139 ) (*SnapshotWriter, error) { 140 dataFilePath := filepath.Join(dir, dataFileName) 141 metadataFilePath := filepath.Join(dir, metadataFileName) 142 143 var dataFile, metadataFile *snapshot.FileWriter 144 var err error 145 defer func() { 146 if err != nil { 147 dataFile.Close() 148 metadataFile.Close() 149 } 150 }() 151 152 dataFile, err = snapshot.CreateFile(dataFilePath, snapshotFileFormat, newHash) 153 if err != nil { 154 return nil, err 155 } 156 157 metadataFile, err = snapshot.CreateFile(metadataFilePath, snapshotFileFormat, newHash) 158 if err != nil { 159 return nil, err 160 } 161 return &SnapshotWriter{ 162 dataFile: dataFile, 163 metadataFile: metadataFile, 164 }, 165 nil 166 } 167 168 func (w *SnapshotWriter) AddData(namespace string, snapshotRecord *SnapshotRecord) error { 169 if len(w.metadata) == 0 || w.metadata[len(w.metadata)-1].namespace != namespace { 170 // new namespace begins 171 w.metadata = append(w.metadata, 172 &metadataRow{ 173 namespace: namespace, 174 kvCounts: 1, 175 }, 176 ) 177 } else { 178 w.metadata[len(w.metadata)-1].kvCounts++ 179 } 180 181 return w.dataFile.EncodeProtoMessage(snapshotRecord) 182 } 183 184 func (w *SnapshotWriter) Done() ([]byte, []byte, error) { 185 dataHash, err := w.dataFile.Done() 186 if err != nil { 187 return nil, nil, err 188 } 189 if err := writeMetadata(w.metadata, w.metadataFile); err != nil { 190 return nil, nil, err 191 } 192 metadataHash, err := w.metadataFile.Done() 193 if err != nil { 194 return nil, nil, err 195 } 196 return dataHash, metadataHash, nil 197 } 198 199 func writeMetadata(metadata []*metadataRow, metadataFile *snapshot.FileWriter) error { 200 if err := metadataFile.EncodeUVarint(uint64(len(metadata))); err != nil { 201 return err 202 } 203 for _, m := range metadata { 204 if err := metadataFile.EncodeString(m.namespace); err != nil { 205 return err 206 } 207 if err := metadataFile.EncodeUVarint(m.kvCounts); err != nil { 208 return err 209 } 210 } 211 return nil 212 } 213 214 func (w *SnapshotWriter) Close() { 215 if w == nil { 216 return 217 } 218 w.dataFile.Close() 219 w.metadataFile.Close() 220 } 221 222 // ImportFromSnapshot imports the public state and private state hashes from the corresponding 223 // files in the snapshotDir 224 func (p *DBProvider) ImportFromSnapshot( 225 dbname string, 226 savepoint *version.Height, 227 snapshotDir string, 228 pvtdataHashesConsumers ...SnapshotPvtdataHashesConsumer, 229 ) error { 230 worldStateSnapshotReader, err := newWorldStateSnapshotReader( 231 snapshotDir, 232 pvtdataHashesConsumers, 233 !p.VersionedDBProvider.BytesKeySupported(), 234 ) 235 if err != nil { 236 return err 237 } 238 defer worldStateSnapshotReader.Close() 239 240 if worldStateSnapshotReader.pubState == nil && worldStateSnapshotReader.pvtStateHashes == nil { 241 return p.VersionedDBProvider.ImportFromSnapshot(dbname, savepoint, nil) 242 } 243 244 if err := p.VersionedDBProvider.ImportFromSnapshot(dbname, savepoint, worldStateSnapshotReader); err != nil { 245 return err 246 } 247 248 metadataHinter := &metadataHint{ 249 bookkeeper: p.bookkeepingProvider.GetDBHandle( 250 dbname, 251 bookkeeping.MetadataPresenceIndicator, 252 ), 253 } 254 255 err = metadataHinter.importNamespacesThatUseMetadata( 256 worldStateSnapshotReader.namespacesThatUseMetadata, 257 ) 258 259 if err != nil { 260 return errors.WithMessage(err, "error while writing to metadata-hint db") 261 } 262 return nil 263 } 264 265 // worldStateSnapshotReader encapsulates the two SnapshotReaders - one for the public state and another for the 266 // pvtstate hashes. worldStateSnapshotReader also implements the interface statedb.FullScanIterator. In the Next() 267 // function, it returns the public state data and then the pvtstate hashes 268 type worldStateSnapshotReader struct { 269 pubState *SnapshotReader 270 pvtStateHashes *SnapshotReader 271 272 pvtdataHashesConsumers []SnapshotPvtdataHashesConsumer 273 encodeKeyHashesWithBase64 bool 274 namespacesThatUseMetadata map[string]struct{} 275 } 276 277 func newWorldStateSnapshotReader( 278 dir string, 279 pvtdataHashesConsumers []SnapshotPvtdataHashesConsumer, 280 encodeKeyHashesWithBase64 bool, 281 ) (*worldStateSnapshotReader, error) { 282 var pubState *SnapshotReader 283 var pvtStateHashes *SnapshotReader 284 var err error 285 286 pubState, err = NewSnapshotReader( 287 dir, PubStateDataFileName, PubStateMetadataFileName, 288 ) 289 if err != nil { 290 return nil, err 291 } 292 293 pvtStateHashes, err = NewSnapshotReader( 294 dir, PvtStateHashesFileName, PvtStateHashesMetadataFileName, 295 ) 296 if err != nil { 297 if pubState != nil { 298 pubState.Close() 299 } 300 return nil, err 301 } 302 303 return &worldStateSnapshotReader{ 304 pubState: pubState, 305 pvtStateHashes: pvtStateHashes, 306 pvtdataHashesConsumers: pvtdataHashesConsumers, 307 encodeKeyHashesWithBase64: encodeKeyHashesWithBase64, 308 namespacesThatUseMetadata: map[string]struct{}{}, 309 }, nil 310 } 311 312 func (r *worldStateSnapshotReader) Next() (*statedb.VersionedKV, error) { 313 if r.pubState != nil && r.pubState.hasMore() { 314 namespace, snapshotRecord, err := r.pubState.Next() 315 if err != nil { 316 return nil, err 317 } 318 319 version, _, err := version.NewHeightFromBytes(snapshotRecord.Version) 320 if err != nil { 321 return nil, errors.WithMessage(err, "error while decoding version") 322 } 323 324 if len(snapshotRecord.Metadata) != 0 { 325 r.namespacesThatUseMetadata[namespace] = struct{}{} 326 } 327 328 return &statedb.VersionedKV{ 329 CompositeKey: &statedb.CompositeKey{ 330 Namespace: namespace, 331 Key: string(snapshotRecord.Key), 332 }, 333 VersionedValue: &statedb.VersionedValue{ 334 Value: snapshotRecord.Value, 335 Metadata: snapshotRecord.Metadata, 336 Version: version, 337 }, 338 }, nil 339 } 340 341 if r.pvtStateHashes != nil && r.pvtStateHashes.hasMore() { 342 namespace, snapshotRecord, err := r.pvtStateHashes.Next() 343 if err != nil { 344 return nil, err 345 } 346 347 version, _, err := version.NewHeightFromBytes(snapshotRecord.Version) 348 if err != nil { 349 return nil, errors.WithMessage(err, "error while decoding version") 350 } 351 352 ns, coll, err := decodeHashedDataNsColl(namespace) 353 if err != nil { 354 return nil, err 355 } 356 357 if len(snapshotRecord.Metadata) != 0 { 358 r.namespacesThatUseMetadata[ns] = struct{}{} 359 } 360 361 if err := r.invokePvtdataHashesConsumers( 362 ns, coll, snapshotRecord.Key, snapshotRecord.Value, version, 363 ); err != nil { 364 return nil, err 365 } 366 367 keyHash := snapshotRecord.Key 368 if r.encodeKeyHashesWithBase64 { 369 keyHash = []byte(base64.StdEncoding.EncodeToString(keyHash)) 370 } 371 372 return &statedb.VersionedKV{ 373 CompositeKey: &statedb.CompositeKey{ 374 Namespace: namespace, 375 Key: string(keyHash), 376 }, 377 VersionedValue: &statedb.VersionedValue{ 378 Value: snapshotRecord.Value, 379 Metadata: snapshotRecord.Metadata, 380 Version: version, 381 }, 382 }, nil 383 } 384 385 if r.pvtStateHashes != nil { 386 if err := r.invokeDoneOnPvtdataHashesConsumers(); err != nil { 387 return nil, err 388 } 389 } 390 return nil, nil 391 } 392 393 func (r *worldStateSnapshotReader) invokePvtdataHashesConsumers( 394 ns string, 395 coll string, 396 keyHash []byte, 397 valueHash []byte, 398 version *version.Height, 399 ) error { 400 if len(r.pvtdataHashesConsumers) == 0 { 401 return nil 402 } 403 404 for _, l := range r.pvtdataHashesConsumers { 405 if err := l.ConsumeSnapshotData( 406 ns, coll, keyHash, valueHash, version, 407 ); err != nil { 408 return err 409 } 410 } 411 return nil 412 } 413 414 func (r *worldStateSnapshotReader) invokeDoneOnPvtdataHashesConsumers() error { 415 if len(r.pvtdataHashesConsumers) == 0 { 416 return nil 417 } 418 var err error 419 for _, c := range r.pvtdataHashesConsumers { 420 if cErr := c.Done(); cErr != nil && err == nil { 421 err = cErr 422 } 423 } 424 return err 425 } 426 427 func (r *worldStateSnapshotReader) Close() { 428 if r == nil { 429 return 430 } 431 r.pubState.Close() 432 r.pvtStateHashes.Close() 433 } 434 435 // SnapshotReader reads data from a pair of files (a data file and the corresponding metadata file) 436 type SnapshotReader struct { 437 dataFile *snapshot.FileReader 438 cursor *cursor 439 } 440 441 func NewSnapshotReader(dir, dataFileName, metadataFileName string) (*SnapshotReader, error) { 442 dataFilePath := filepath.Join(dir, dataFileName) 443 metadataFilePath := filepath.Join(dir, metadataFileName) 444 exist, _, err := fileutil.FileExists(dataFilePath) 445 if err != nil { 446 return nil, errors.WithMessage(err, "error while checking if data file exists") 447 } 448 if !exist { 449 logger.Infow("Data file does not exist. Nothing to be done.", "filepath", dataFilePath) 450 return nil, nil 451 } 452 453 var dataFile, metadataFile *snapshot.FileReader 454 455 defer func() { 456 if err != nil { 457 dataFile.Close() 458 metadataFile.Close() 459 } 460 }() 461 462 if dataFile, err = snapshot.OpenFile(dataFilePath, snapshotFileFormat); err != nil { 463 return nil, errors.WithMessage(err, "error while opening data file") 464 } 465 if metadataFile, err = snapshot.OpenFile(metadataFilePath, snapshotFileFormat); err != nil { 466 return nil, errors.WithMessage(err, "error while opening metadata file") 467 } 468 469 metadata, err := readMetadata(metadataFile) 470 if err != nil { 471 return nil, err 472 } 473 return &SnapshotReader{ 474 dataFile: dataFile, 475 cursor: &cursor{ 476 metadata: metadata, 477 }, 478 }, nil 479 } 480 481 func readMetadata(metadataFile *snapshot.FileReader) ([]*metadataRow, error) { 482 numMetadata, err := metadataFile.DecodeUVarInt() 483 if err != nil { 484 return nil, errors.WithMessage(err, "error while reading num-rows in metadata") 485 } 486 metadata := make([]*metadataRow, numMetadata) 487 for i := uint64(0); i < numMetadata; i++ { 488 ns, err := metadataFile.DecodeString() 489 if err != nil { 490 return nil, errors.WithMessage(err, "error while reading namespace name") 491 } 492 numKVs, err := metadataFile.DecodeUVarInt() 493 if err != nil { 494 return nil, errors.WithMessagef(err, "error while reading num entries for the namespace [%s]", ns) 495 } 496 metadata[i] = &metadataRow{ 497 namespace: ns, 498 kvCounts: numKVs, 499 } 500 } 501 return metadata, nil 502 } 503 504 func (r *SnapshotReader) Next() (string, *SnapshotRecord, error) { 505 if !r.cursor.move() { 506 return "", nil, nil 507 } 508 509 snapshotRecord := &SnapshotRecord{} 510 if err := r.dataFile.DecodeProtoMessage(snapshotRecord); err != nil { 511 return "", nil, errors.WithMessage(err, "error while retrieving record from snapshot file") 512 } 513 return r.cursor.currentNamespace(), snapshotRecord, nil 514 } 515 516 func (r *SnapshotReader) Close() { 517 if r == nil { 518 return 519 } 520 r.dataFile.Close() 521 } 522 523 func (r *SnapshotReader) hasMore() bool { 524 return r.cursor.canMove() 525 } 526 527 // metadataRow captures one tuple <namespace, number-of-KVs> in the metadata file 528 type metadataRow struct { 529 namespace string 530 kvCounts uint64 531 } 532 533 type cursor struct { 534 metadata []*metadataRow 535 currentRow int 536 movesInRow uint64 537 } 538 539 func (c *cursor) move() bool { 540 if c.movesInRow < c.metadata[c.currentRow].kvCounts { 541 c.movesInRow++ 542 return true 543 } 544 if c.currentRow < len(c.metadata)-1 { 545 c.currentRow++ 546 c.movesInRow = 1 547 return true 548 } 549 return false 550 } 551 552 func (c *cursor) canMove() bool { 553 return c.movesInRow < c.metadata[c.currentRow].kvCounts || 554 c.currentRow < len(c.metadata)-1 555 } 556 557 func (c *cursor) currentNamespace() string { 558 return c.metadata[c.currentRow].namespace 559 } 560 561 type SnapshotPvtdataHashesConsumer interface { 562 ConsumeSnapshotData(namespace, coll string, keyHash []byte, valueHash []byte, version *version.Height) error 563 Done() error 564 }