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 }