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 }