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 }