github.com/ewagmig/fabric@v2.1.1+incompatible/core/ledger/kvledger/txmgmt/validator/internal/tx_ops_preparation.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package internal
     8  
     9  import (
    10  	"github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset"
    11  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/privacyenabledstate"
    12  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil"
    13  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb"
    14  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/storageutil"
    15  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version"
    16  )
    17  
    18  func prepareTxOps(rwset *rwsetutil.TxRwSet, txht *version.Height,
    19  	precedingUpdates *PubAndHashUpdates, db privacyenabledstate.DB) (txOps, error) {
    20  	txops := txOps{}
    21  	txops.applyTxRwset(rwset)
    22  	//logger.Debugf("prepareTxOps() txops after applying raw rwset=%#v", spew.Sdump(txops))
    23  	for ck, keyop := range txops {
    24  		// check if the final state of the key, value and metadata, is already present in the transaction, then skip
    25  		// otherwise we need to retrieve latest state and merge in the current value or metadata update
    26  		if keyop.isDelete() || keyop.isUpsertAndMetadataUpdate() {
    27  			continue
    28  		}
    29  
    30  		// check if only value is updated in the current transaction then merge the metadata from last committed state
    31  		if keyop.isOnlyUpsert() {
    32  			latestMetadata, err := retrieveLatestMetadata(ck.ns, ck.coll, ck.key, precedingUpdates, db)
    33  			if err != nil {
    34  				return nil, err
    35  			}
    36  			keyop.metadata = latestMetadata
    37  			continue
    38  		}
    39  
    40  		// only metadata is updated in the current transaction. Merge the value from the last committed state
    41  		// If the key does not exist in the last state, make this key as noop in current transaction
    42  		latestVal, err := retrieveLatestState(ck.ns, ck.coll, ck.key, precedingUpdates, db)
    43  		if err != nil {
    44  			return nil, err
    45  		}
    46  		if latestVal != nil {
    47  			keyop.value = latestVal.Value
    48  		} else {
    49  			delete(txops, ck)
    50  		}
    51  	}
    52  	//logger.Debugf("prepareTxOps() txops after final processing=%#v", spew.Sdump(txops))
    53  	return txops, nil
    54  }
    55  
    56  // applyTxRwset records the upsertion/deletion of a kv and updatation/deletion
    57  // of associated metadata present in a txrwset
    58  func (txops txOps) applyTxRwset(rwset *rwsetutil.TxRwSet) error {
    59  	for _, nsRWSet := range rwset.NsRwSets {
    60  		ns := nsRWSet.NameSpace
    61  		for _, kvWrite := range nsRWSet.KvRwSet.Writes {
    62  			txops.applyKVWrite(ns, "", kvWrite)
    63  		}
    64  		for _, kvMetadataWrite := range nsRWSet.KvRwSet.MetadataWrites {
    65  			txops.applyMetadata(ns, "", kvMetadataWrite)
    66  		}
    67  
    68  		// apply collection level kvwrite and kvMetadataWrite
    69  		for _, collHashRWset := range nsRWSet.CollHashedRwSets {
    70  			coll := collHashRWset.CollectionName
    71  			for _, hashedWrite := range collHashRWset.HashedRwSet.HashedWrites {
    72  				txops.applyKVWrite(ns, coll,
    73  					&kvrwset.KVWrite{
    74  						Key:      string(hashedWrite.KeyHash),
    75  						Value:    hashedWrite.ValueHash,
    76  						IsDelete: hashedWrite.IsDelete,
    77  					},
    78  				)
    79  			}
    80  
    81  			for _, metadataWrite := range collHashRWset.HashedRwSet.MetadataWrites {
    82  				txops.applyMetadata(ns, coll,
    83  					&kvrwset.KVMetadataWrite{
    84  						Key:     string(metadataWrite.KeyHash),
    85  						Entries: metadataWrite.Entries,
    86  					},
    87  				)
    88  			}
    89  		}
    90  	}
    91  	return nil
    92  }
    93  
    94  // applyKVWrite records upsertion/deletion of a kvwrite
    95  func (txops txOps) applyKVWrite(ns, coll string, kvWrite *kvrwset.KVWrite) {
    96  	if kvWrite.IsDelete {
    97  		txops.delete(compositeKey{ns, coll, kvWrite.Key})
    98  	} else {
    99  		txops.upsert(compositeKey{ns, coll, kvWrite.Key}, kvWrite.Value)
   100  	}
   101  }
   102  
   103  // applyMetadata records updatation/deletion of a metadataWrite
   104  func (txops txOps) applyMetadata(ns, coll string, metadataWrite *kvrwset.KVMetadataWrite) error {
   105  	if metadataWrite.Entries == nil {
   106  		txops.metadataDelete(compositeKey{ns, coll, metadataWrite.Key})
   107  	} else {
   108  		metadataBytes, err := storageutil.SerializeMetadata(metadataWrite.Entries)
   109  		if err != nil {
   110  			return err
   111  		}
   112  		txops.metadataUpdate(compositeKey{ns, coll, metadataWrite.Key}, metadataBytes)
   113  	}
   114  	return nil
   115  }
   116  
   117  // retrieveLatestState returns the value of the key from the precedingUpdates (if the key was operated upon by a previous tran in the block).
   118  // If the key not present in the precedingUpdates, then this function, pulls the latest value from statedb
   119  // TODO FAB-11328, pulling from state for (especially for couchdb) will pay significant performance penalty so a bulkload would be helpful.
   120  // Further, all the keys that gets written will be required to pull from statedb by vscc for endorsement policy check (in the case of key level
   121  // endorsement) and hence, the bulkload should be combined
   122  func retrieveLatestState(ns, coll, key string,
   123  	precedingUpdates *PubAndHashUpdates, db privacyenabledstate.DB) (*statedb.VersionedValue, error) {
   124  	var vv *statedb.VersionedValue
   125  	var err error
   126  	if coll == "" {
   127  		vv := precedingUpdates.PubUpdates.Get(ns, key)
   128  		if vv == nil {
   129  			vv, err = db.GetState(ns, key)
   130  		}
   131  		return vv, err
   132  	}
   133  
   134  	vv = precedingUpdates.HashUpdates.Get(ns, coll, key)
   135  	if vv == nil {
   136  		vv, err = db.GetValueHash(ns, coll, []byte(key))
   137  	}
   138  	return vv, err
   139  }
   140  
   141  func retrieveLatestMetadata(ns, coll, key string,
   142  	precedingUpdates *PubAndHashUpdates, db privacyenabledstate.DB) ([]byte, error) {
   143  	if coll == "" {
   144  		vv := precedingUpdates.PubUpdates.Get(ns, key)
   145  		if vv != nil {
   146  			return vv.Metadata, nil
   147  		}
   148  		return db.GetStateMetadata(ns, key)
   149  	}
   150  	vv := precedingUpdates.HashUpdates.Get(ns, coll, key)
   151  	if vv != nil {
   152  		return vv.Metadata, nil
   153  	}
   154  	return db.GetPrivateDataMetadataByHash(ns, coll, []byte(key))
   155  }
   156  
   157  type keyOpsFlag uint8
   158  
   159  const (
   160  	upsertVal      keyOpsFlag = 1 // 1 << 0
   161  	metadataUpdate            = 2 // 1 << 1
   162  	metadataDelete            = 4 // 1 << 2
   163  	keyDelete                 = 8 // 1 << 3
   164  )
   165  
   166  type compositeKey struct {
   167  	ns, coll, key string
   168  }
   169  
   170  type txOps map[compositeKey]*keyOps
   171  
   172  type keyOps struct {
   173  	flag     keyOpsFlag
   174  	value    []byte
   175  	metadata []byte
   176  }
   177  
   178  ////////////////// txOps functions
   179  
   180  func (txops txOps) upsert(k compositeKey, val []byte) {
   181  	keyops := txops.getOrCreateKeyEntry(k)
   182  	keyops.flag += upsertVal
   183  	keyops.value = val
   184  }
   185  
   186  func (txops txOps) delete(k compositeKey) {
   187  	keyops := txops.getOrCreateKeyEntry(k)
   188  	keyops.flag += keyDelete
   189  }
   190  
   191  func (txops txOps) metadataUpdate(k compositeKey, metadata []byte) {
   192  	keyops := txops.getOrCreateKeyEntry(k)
   193  	keyops.flag += metadataUpdate
   194  	keyops.metadata = metadata
   195  }
   196  
   197  func (txops txOps) metadataDelete(k compositeKey) {
   198  	keyops := txops.getOrCreateKeyEntry(k)
   199  	keyops.flag += metadataDelete
   200  }
   201  
   202  func (txops txOps) getOrCreateKeyEntry(k compositeKey) *keyOps {
   203  	keyops, ok := txops[k]
   204  	if !ok {
   205  		keyops = &keyOps{}
   206  		txops[k] = keyops
   207  	}
   208  	return keyops
   209  }
   210  
   211  ////////////////// keyOps functions
   212  
   213  func (keyops keyOps) isDelete() bool {
   214  	return keyops.flag&(keyDelete) == keyDelete
   215  }
   216  
   217  func (keyops keyOps) isUpsertAndMetadataUpdate() bool {
   218  	if keyops.flag&upsertVal == upsertVal {
   219  		return keyops.flag&metadataUpdate == metadataUpdate ||
   220  			keyops.flag&metadataDelete == metadataDelete
   221  	}
   222  	return false
   223  }
   224  
   225  func (keyops keyOps) isOnlyUpsert() bool {
   226  	return keyops.flag|upsertVal == upsertVal
   227  }