github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/txmgmt/validation/tx_ops.go (about)

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