github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/core/ledger/kvledger/txmgmt/validation/tx_ops.go (about)

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