github.com/yimialmonte/fabric@v2.1.1+incompatible/core/transientstore/store.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package transientstore
     8  
     9  import (
    10  	"errors"
    11  
    12  	"github.com/golang/protobuf/proto"
    13  	"github.com/hyperledger/fabric-protos-go/ledger/rwset"
    14  	"github.com/hyperledger/fabric-protos-go/transientstore"
    15  	"github.com/hyperledger/fabric/common/flogging"
    16  	"github.com/hyperledger/fabric/common/ledger/util/leveldbhelper"
    17  	"github.com/hyperledger/fabric/common/util"
    18  	"github.com/hyperledger/fabric/core/ledger"
    19  	"github.com/syndtr/goleveldb/leveldb/iterator"
    20  )
    21  
    22  var logger = flogging.MustGetLogger("transientstore")
    23  
    24  var (
    25  	emptyValue = []byte{}
    26  	nilByte    = byte('\x00')
    27  	// ErrStoreEmpty is used to indicate that there are no entries in transient store
    28  	ErrStoreEmpty = errors.New("Transient store is empty")
    29  )
    30  
    31  //////////////////////////////////////////////
    32  // Interfaces and data types
    33  /////////////////////////////////////////////
    34  
    35  // StoreProvider provides an instance of a TransientStore
    36  type StoreProvider interface {
    37  	OpenStore(ledgerID string) (*Store, error)
    38  	Close()
    39  }
    40  
    41  // RWSetScanner provides an iterator for EndorserPvtSimulationResults
    42  type RWSetScanner interface {
    43  	// Next returns the next EndorserPvtSimulationResults from the RWSetScanner.
    44  	// It may return nil, nil when it has no further data, and also may return an error
    45  	// on failure
    46  	Next() (*EndorserPvtSimulationResults, error)
    47  	// Close frees the resources associated with this RWSetScanner
    48  	Close()
    49  }
    50  
    51  // EndorserPvtSimulationResults captures the details of the simulation results specific to an endorser
    52  type EndorserPvtSimulationResults struct {
    53  	ReceivedAtBlockHeight          uint64
    54  	PvtSimulationResultsWithConfig *transientstore.TxPvtReadWriteSetWithConfigInfo
    55  }
    56  
    57  //////////////////////////////////////////////
    58  // Implementation
    59  /////////////////////////////////////////////
    60  
    61  // storeProvider encapsulates a leveldb provider which is used to store
    62  // private write sets of simulated transactions, and implements TransientStoreProvider
    63  // interface.
    64  type storeProvider struct {
    65  	dbProvider *leveldbhelper.Provider
    66  }
    67  
    68  // store holds an instance of a levelDB.
    69  type Store struct {
    70  	db       *leveldbhelper.DBHandle
    71  	ledgerID string
    72  }
    73  
    74  // RwsetScanner helps iterating over results
    75  type RwsetScanner struct {
    76  	txid   string
    77  	dbItr  iterator.Iterator
    78  	filter ledger.PvtNsCollFilter
    79  }
    80  
    81  // NewStoreProvider instantiates TransientStoreProvider
    82  func NewStoreProvider(path string) (StoreProvider, error) {
    83  	dbProvider, err := leveldbhelper.NewProvider(&leveldbhelper.Conf{DBPath: path})
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	return &storeProvider{dbProvider: dbProvider}, nil
    88  }
    89  
    90  // OpenStore returns a handle to a ledgerId in Store
    91  func (provider *storeProvider) OpenStore(ledgerID string) (*Store, error) {
    92  	dbHandle := provider.dbProvider.GetDBHandle(ledgerID)
    93  	return &Store{db: dbHandle, ledgerID: ledgerID}, nil
    94  }
    95  
    96  // Close closes the TransientStoreProvider
    97  func (provider *storeProvider) Close() {
    98  	provider.dbProvider.Close()
    99  }
   100  
   101  // Persist stores the private write set of a transaction along with the collection config
   102  // in the transient store based on txid and the block height the private data was received at
   103  func (s *Store) Persist(txid string, blockHeight uint64,
   104  	privateSimulationResultsWithConfig *transientstore.TxPvtReadWriteSetWithConfigInfo) error {
   105  
   106  	logger.Debugf("Persisting private data to transient store for txid [%s] at block height [%d]", txid, blockHeight)
   107  
   108  	dbBatch := leveldbhelper.NewUpdateBatch()
   109  
   110  	// Create compositeKey with appropriate prefix, txid, uuid and blockHeight
   111  	// Due to the fact that the txid may have multiple private write sets persisted from different
   112  	// endorsers (via Gossip), we postfix an uuid with the txid to avoid collision.
   113  	uuid := util.GenerateUUID()
   114  	compositeKeyPvtRWSet := createCompositeKeyForPvtRWSet(txid, uuid, blockHeight)
   115  	privateSimulationResultsWithConfigBytes, err := proto.Marshal(privateSimulationResultsWithConfig)
   116  	if err != nil {
   117  		return err
   118  	}
   119  
   120  	// Note that some rwset.TxPvtReadWriteSet may exist in the transient store immediately after
   121  	// upgrading the peer to v1.2. In order to differentiate between new proto and old proto while
   122  	// retrieving, a nil byte is prepended to the new proto, i.e., privateSimulationResultsWithConfigBytes,
   123  	// as a marshaled message can never start with a nil byte. In v1.3, we can avoid prepending the
   124  	// nil byte.
   125  	value := append([]byte{nilByte}, privateSimulationResultsWithConfigBytes...)
   126  	dbBatch.Put(compositeKeyPvtRWSet, value)
   127  
   128  	// Create two index: (i) by txid, and (ii) by height
   129  
   130  	// Create compositeKey for purge index by height with appropriate prefix, blockHeight,
   131  	// txid, uuid and store the compositeKey (purge index) with a nil byte as value. Note that
   132  	// the purge index is used to remove orphan entries in the transient store (which are not removed
   133  	// by PurgeTxids()) using BTL policy by PurgeBelowHeight(). Note that orphan entries are due to transaction
   134  	// that gets endorsed but not submitted by the client for commit)
   135  	compositeKeyPurgeIndexByHeight := createCompositeKeyForPurgeIndexByHeight(blockHeight, txid, uuid)
   136  	dbBatch.Put(compositeKeyPurgeIndexByHeight, emptyValue)
   137  
   138  	// Create compositeKey for purge index by txid with appropriate prefix, txid, uuid,
   139  	// blockHeight and store the compositeKey (purge index) with a nil byte as value.
   140  	// Though compositeKeyPvtRWSet itself can be used to purge private write set by txid,
   141  	// we create a separate composite key with a nil byte as value. The reason is that
   142  	// if we use compositeKeyPvtRWSet, we unnecessarily read (potentially large) private write
   143  	// set associated with the key from db. Note that this purge index is used to remove non-orphan
   144  	// entries in the transient store and is used by PurgeTxids()
   145  	// Note: We can create compositeKeyPurgeIndexByTxid by just replacing the prefix of compositeKeyPvtRWSet
   146  	// with purgeIndexByTxidPrefix. For code readability and to be expressive, we use a
   147  	// createCompositeKeyForPurgeIndexByTxid() instead.
   148  	compositeKeyPurgeIndexByTxid := createCompositeKeyForPurgeIndexByTxid(txid, uuid, blockHeight)
   149  	dbBatch.Put(compositeKeyPurgeIndexByTxid, emptyValue)
   150  
   151  	return s.db.WriteBatch(dbBatch, true)
   152  }
   153  
   154  // GetTxPvtRWSetByTxid returns an iterator due to the fact that the txid may have multiple private
   155  // write sets persisted from different endorsers.
   156  func (s *Store) GetTxPvtRWSetByTxid(txid string, filter ledger.PvtNsCollFilter) (RWSetScanner, error) {
   157  
   158  	logger.Debugf("Getting private data from transient store for transaction %s", txid)
   159  
   160  	// Construct startKey and endKey to do an range query
   161  	startKey := createTxidRangeStartKey(txid)
   162  	endKey := createTxidRangeEndKey(txid)
   163  
   164  	iter := s.db.GetIterator(startKey, endKey)
   165  	return &RwsetScanner{txid, iter, filter}, nil
   166  }
   167  
   168  // PurgeByTxids removes private write sets of a given set of transactions from the
   169  // transient store. PurgeByTxids() is expected to be called by coordinator after
   170  // committing a block to ledger.
   171  func (s *Store) PurgeByTxids(txids []string) error {
   172  
   173  	logger.Debug("Purging private data from transient store for committed txids")
   174  
   175  	dbBatch := leveldbhelper.NewUpdateBatch()
   176  
   177  	for _, txid := range txids {
   178  		// Construct startKey and endKey to do an range query
   179  		startKey := createPurgeIndexByTxidRangeStartKey(txid)
   180  		endKey := createPurgeIndexByTxidRangeEndKey(txid)
   181  
   182  		iter := s.db.GetIterator(startKey, endKey)
   183  
   184  		// Get all txid and uuid from above result and remove it from transient store (both
   185  		// write set and the corresponding indexes.
   186  		for iter.Next() {
   187  			// For each entry, remove the private read-write set and corresponding indexes
   188  
   189  			// Remove private write set
   190  			compositeKeyPurgeIndexByTxid := iter.Key()
   191  			// Note: We can create compositeKeyPvtRWSet by just replacing the prefix of compositeKeyPurgeIndexByTxid
   192  			// with  prwsetPrefix. For code readability and to be expressive, we split and create again.
   193  			uuid, blockHeight, err := splitCompositeKeyOfPurgeIndexByTxid(compositeKeyPurgeIndexByTxid)
   194  			if err != nil {
   195  				return err
   196  			}
   197  			compositeKeyPvtRWSet := createCompositeKeyForPvtRWSet(txid, uuid, blockHeight)
   198  			dbBatch.Delete(compositeKeyPvtRWSet)
   199  
   200  			// Remove purge index -- purgeIndexByHeight
   201  			compositeKeyPurgeIndexByHeight := createCompositeKeyForPurgeIndexByHeight(blockHeight, txid, uuid)
   202  			dbBatch.Delete(compositeKeyPurgeIndexByHeight)
   203  
   204  			// Remove purge index -- purgeIndexByTxid
   205  			dbBatch.Delete(compositeKeyPurgeIndexByTxid)
   206  		}
   207  		iter.Release()
   208  	}
   209  	// If peer fails before/while writing the batch to golevelDB, these entries will be
   210  	// removed as per BTL policy later by PurgeBelowHeight()
   211  	return s.db.WriteBatch(dbBatch, true)
   212  }
   213  
   214  // PurgeBelowHeight removes private write sets at block height lesser than
   215  // a given maxBlockNumToRetain. In other words, Purge only retains private write sets
   216  // that were persisted at block height of maxBlockNumToRetain or higher. Though the private
   217  // write sets stored in transient store is removed by coordinator using PurgebyTxids()
   218  // after successful block commit, PurgeBelowHeight() is still required to remove orphan entries (as
   219  // transaction that gets endorsed may not be submitted by the client for commit)
   220  func (s *Store) PurgeBelowHeight(maxBlockNumToRetain uint64) error {
   221  
   222  	logger.Debugf("Purging orphaned private data from transient store received prior to block [%d]", maxBlockNumToRetain)
   223  
   224  	// Do a range query with 0 as startKey and maxBlockNumToRetain-1 as endKey
   225  	startKey := createPurgeIndexByHeightRangeStartKey(0)
   226  	endKey := createPurgeIndexByHeightRangeEndKey(maxBlockNumToRetain - 1)
   227  	iter := s.db.GetIterator(startKey, endKey)
   228  
   229  	dbBatch := leveldbhelper.NewUpdateBatch()
   230  
   231  	// Get all txid and uuid from above result and remove it from transient store (both
   232  	// write set and the corresponding index.
   233  	for iter.Next() {
   234  		// For each entry, remove the private read-write set and corresponding indexes
   235  
   236  		// Remove private write set
   237  		compositeKeyPurgeIndexByHeight := iter.Key()
   238  		txid, uuid, blockHeight, err := splitCompositeKeyOfPurgeIndexByHeight(compositeKeyPurgeIndexByHeight)
   239  		if err != nil {
   240  			return err
   241  		}
   242  		logger.Debugf("Purging from transient store private data simulated at block [%d]: txid [%s] uuid [%s]", blockHeight, txid, uuid)
   243  
   244  		compositeKeyPvtRWSet := createCompositeKeyForPvtRWSet(txid, uuid, blockHeight)
   245  		dbBatch.Delete(compositeKeyPvtRWSet)
   246  
   247  		// Remove purge index -- purgeIndexByTxid
   248  		compositeKeyPurgeIndexByTxid := createCompositeKeyForPurgeIndexByTxid(txid, uuid, blockHeight)
   249  		dbBatch.Delete(compositeKeyPurgeIndexByTxid)
   250  
   251  		// Remove purge index -- purgeIndexByHeight
   252  		dbBatch.Delete(compositeKeyPurgeIndexByHeight)
   253  	}
   254  	iter.Release()
   255  
   256  	return s.db.WriteBatch(dbBatch, true)
   257  }
   258  
   259  // GetMinTransientBlkHt returns the lowest block height remaining in transient store
   260  func (s *Store) GetMinTransientBlkHt() (uint64, error) {
   261  	// Current approach performs a range query on purgeIndex with startKey
   262  	// as 0 (i.e., blockHeight) and returns the first key which denotes
   263  	// the lowest block height remaining in transient store. An alternative approach
   264  	// is to explicitly store the minBlockHeight in the transientStore.
   265  	startKey := createPurgeIndexByHeightRangeStartKey(0)
   266  	iter := s.db.GetIterator(startKey, nil)
   267  	defer iter.Release()
   268  	// Fetch the minimum transient block height
   269  	if iter.Next() {
   270  		dbKey := iter.Key()
   271  		_, _, blockHeight, err := splitCompositeKeyOfPurgeIndexByHeight(dbKey)
   272  		return blockHeight, err
   273  	}
   274  	// Returning an error may not be the right thing to do here. May be
   275  	// return a bool. -1 is not possible due to unsigned int as first
   276  	// return value
   277  	return 0, ErrStoreEmpty
   278  }
   279  
   280  func (s *Store) Shutdown() {
   281  	// do nothing because shared db is used
   282  }
   283  
   284  // Next moves the iterator to the next key/value pair.
   285  // It returns <nil, nil> when the iterator is exhausted.
   286  func (scanner *RwsetScanner) Next() (*EndorserPvtSimulationResults, error) {
   287  	if !scanner.dbItr.Next() {
   288  		return nil, nil
   289  	}
   290  	dbKey := scanner.dbItr.Key()
   291  	dbVal := scanner.dbItr.Value()
   292  	_, blockHeight, err := splitCompositeKeyOfPvtRWSet(dbKey)
   293  	if err != nil {
   294  		return nil, err
   295  	}
   296  
   297  	txPvtRWSet := &rwset.TxPvtReadWriteSet{}
   298  	var filteredTxPvtRWSet *rwset.TxPvtReadWriteSet = nil
   299  	txPvtRWSetWithConfig := &transientstore.TxPvtReadWriteSetWithConfigInfo{}
   300  
   301  	if dbVal[0] == nilByte {
   302  		// new proto, i.e., TxPvtReadWriteSetWithConfigInfo
   303  		if err := proto.Unmarshal(dbVal[1:], txPvtRWSetWithConfig); err != nil {
   304  			return nil, err
   305  		}
   306  
   307  		filteredTxPvtRWSet = trimPvtWSet(txPvtRWSetWithConfig.GetPvtRwset(), scanner.filter)
   308  		configs, err := trimPvtCollectionConfigs(txPvtRWSetWithConfig.CollectionConfigs, scanner.filter)
   309  		if err != nil {
   310  			return nil, err
   311  		}
   312  		txPvtRWSetWithConfig.CollectionConfigs = configs
   313  	} else {
   314  		// old proto, i.e., TxPvtReadWriteSet
   315  		if err := proto.Unmarshal(dbVal, txPvtRWSet); err != nil {
   316  			return nil, err
   317  		}
   318  		filteredTxPvtRWSet = trimPvtWSet(txPvtRWSet, scanner.filter)
   319  	}
   320  
   321  	txPvtRWSetWithConfig.PvtRwset = filteredTxPvtRWSet
   322  
   323  	return &EndorserPvtSimulationResults{
   324  		ReceivedAtBlockHeight:          blockHeight,
   325  		PvtSimulationResultsWithConfig: txPvtRWSetWithConfig,
   326  	}, nil
   327  }
   328  
   329  // Close releases resource held by the iterator
   330  func (scanner *RwsetScanner) Close() {
   331  	scanner.dbItr.Release()
   332  }