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  }