github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/hashcheck_pvtdata.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package kvledger 8 9 import ( 10 "bytes" 11 12 "github.com/hechain20/hechain/common/ledger/blkstorage" 13 "github.com/hechain20/hechain/common/util" 14 "github.com/hechain20/hechain/core/ledger" 15 "github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/rwsetutil" 16 "github.com/hechain20/hechain/core/ledger/pvtdatastorage" 17 "github.com/hechain20/hechain/protoutil" 18 "github.com/hyperledger/fabric-protos-go/ledger/rwset" 19 ) 20 21 // constructValidAndInvalidPvtData computes the valid pvt data and hash mismatch list 22 // from a received pvt data list of old blocks. The reconciled data elements that belong 23 // to a block which is not available in the blockstore (i.e., the block is prior to or equal 24 // to the lastBlockInBootSnapshot), the boot KV hashes in the private data store are used for 25 // verifying the hashes. Further, in this case, the write-set of a collection is trimmed in order 26 // to remove the key-values that were not present in the snapshot (most likely because, they 27 // were over-written in a later version) 28 func constructValidAndInvalidPvtData( 29 reconciledPvtdata []*ledger.ReconciledPvtdata, 30 blockStore *blkstorage.BlockStore, 31 pvtdataStore *pvtdatastorage.Store, 32 lastBlockInBootSnapshot uint64, 33 ) ( 34 map[uint64][]*ledger.TxPvtData, []*ledger.PvtdataHashMismatch, error, 35 ) { 36 // for each block, for each transaction, verify the hash of pvtRwSet 37 // present in the received data txPvtData. 38 // On a mismatch, add an entry to hashMismatch list. 39 // On a match, add the pvtData to the validPvtData list 40 validPvtData := make(map[uint64][]*ledger.TxPvtData) 41 var invalidPvtData []*ledger.PvtdataHashMismatch 42 43 for _, pvtdata := range reconciledPvtdata { 44 var validData []*ledger.TxPvtData 45 var invalidData []*ledger.PvtdataHashMismatch 46 var err error 47 48 if pvtdata.BlockNum <= lastBlockInBootSnapshot { 49 validData, invalidData, err = verifyHashesViaBootKVHashes(pvtdata, pvtdataStore) 50 } else { 51 validData, invalidData, err = verifyHashesFromBlockStore(pvtdata, blockStore) 52 } 53 if err != nil { 54 return nil, nil, err 55 } 56 if len(validData) > 0 { 57 validPvtData[pvtdata.BlockNum] = validData 58 } 59 invalidPvtData = append(invalidPvtData, invalidData...) 60 } // for each block's pvtData 61 return validPvtData, invalidPvtData, nil 62 } 63 64 func verifyHashesFromBlockStore(reconciledPvtdata *ledger.ReconciledPvtdata, blockStore *blkstorage.BlockStore) ( 65 []*ledger.TxPvtData, []*ledger.PvtdataHashMismatch, error, 66 ) { 67 var validPvtData []*ledger.TxPvtData 68 var invalidPvtData []*ledger.PvtdataHashMismatch 69 for _, txPvtData := range reconciledPvtdata.WriteSets { 70 // (1) retrieve the txrwset from the blockstore 71 logger.Debugf("Retrieving rwset of blockNum:[%d], txNum:[%d]", reconciledPvtdata.BlockNum, txPvtData.SeqInBlock) 72 txRWSet, err := retrieveRwsetForTx(reconciledPvtdata.BlockNum, txPvtData.SeqInBlock, blockStore) 73 if err != nil { 74 return nil, nil, err 75 } 76 77 // (2) validate passed pvtData against the pvtData hash in the tx rwset. 78 logger.Debugf("Constructing valid and invalid pvtData using rwset of blockNum:[%d], txNum:[%d]", 79 reconciledPvtdata.BlockNum, txPvtData.SeqInBlock) 80 validData, invalidData := findValidAndInvalidTxPvtData(txPvtData, txRWSet, reconciledPvtdata.BlockNum) 81 82 // (3) append validData to validPvtDataPvt list of this block and 83 // invalidData to invalidPvtData list 84 if validData != nil { 85 validPvtData = append(validPvtData, validData) 86 } 87 invalidPvtData = append(invalidPvtData, invalidData...) 88 } // for each tx's pvtData 89 return validPvtData, invalidPvtData, nil 90 } 91 92 func retrieveRwsetForTx(blkNum uint64, txNum uint64, blockStore *blkstorage.BlockStore) (*rwsetutil.TxRwSet, error) { 93 // retrieve the txEnvelope from the block store so that the hash of 94 // the pvtData can be retrieved for comparison 95 txEnvelope, err := blockStore.RetrieveTxByBlockNumTranNum(blkNum, txNum) 96 if err != nil { 97 return nil, err 98 } 99 // retrieve pvtRWset hash from the txEnvelope 100 responsePayload, err := protoutil.GetActionFromEnvelopeMsg(txEnvelope) 101 if err != nil { 102 return nil, err 103 } 104 txRWSet := &rwsetutil.TxRwSet{} 105 if err := txRWSet.FromProtoBytes(responsePayload.Results); err != nil { 106 return nil, err 107 } 108 return txRWSet, nil 109 } 110 111 func findValidAndInvalidTxPvtData(txPvtData *ledger.TxPvtData, txRWSet *rwsetutil.TxRwSet, blkNum uint64) ( 112 *ledger.TxPvtData, []*ledger.PvtdataHashMismatch, 113 ) { 114 var invalidPvtData []*ledger.PvtdataHashMismatch 115 var toDeleteNsColl []*nsColl 116 // Compare the hash of pvtData with the hash present in the rwset to 117 // find valid and invalid pvt data 118 for _, nsRwset := range txPvtData.WriteSet.NsPvtRwset { 119 txNum := txPvtData.SeqInBlock 120 invalidData, invalidNsColl := findInvalidNsPvtData(nsRwset, txRWSet, blkNum, txNum) 121 invalidPvtData = append(invalidPvtData, invalidData...) 122 toDeleteNsColl = append(toDeleteNsColl, invalidNsColl...) 123 } 124 for _, nsColl := range toDeleteNsColl { 125 removeCollFromTxPvtReadWriteSet(txPvtData.WriteSet, nsColl.ns, nsColl.coll) 126 } 127 if len(txPvtData.WriteSet.NsPvtRwset) == 0 { 128 // denotes that all namespaces had 129 // invalid pvt data 130 return nil, invalidPvtData 131 } 132 return txPvtData, invalidPvtData 133 } 134 135 // Remove removes the rwset for the given <ns, coll> tuple. If after this removal, 136 // there are no more collection in the namespace <ns>, the whole namespace entry is removed 137 func removeCollFromTxPvtReadWriteSet(p *rwset.TxPvtReadWriteSet, ns, coll string) { 138 for i := 0; i < len(p.NsPvtRwset); i++ { 139 n := p.NsPvtRwset[i] 140 if n.Namespace != ns { 141 continue 142 } 143 removeCollFromNsPvtWriteSet(n, coll) 144 if len(n.CollectionPvtRwset) == 0 { 145 p.NsPvtRwset = append(p.NsPvtRwset[:i], p.NsPvtRwset[i+1:]...) 146 } 147 return 148 } 149 } 150 151 func removeCollFromNsPvtWriteSet(n *rwset.NsPvtReadWriteSet, collName string) { 152 for i := 0; i < len(n.CollectionPvtRwset); i++ { 153 c := n.CollectionPvtRwset[i] 154 if c.CollectionName != collName { 155 continue 156 } 157 n.CollectionPvtRwset = append(n.CollectionPvtRwset[:i], n.CollectionPvtRwset[i+1:]...) 158 return 159 } 160 } 161 162 type nsColl struct { 163 ns, coll string 164 } 165 166 func findInvalidNsPvtData(nsRwset *rwset.NsPvtReadWriteSet, txRWSet *rwsetutil.TxRwSet, blkNum, txNum uint64) ( 167 []*ledger.PvtdataHashMismatch, []*nsColl, 168 ) { 169 var invalidPvtData []*ledger.PvtdataHashMismatch 170 var invalidNsColl []*nsColl 171 172 ns := nsRwset.Namespace 173 for _, collPvtRwset := range nsRwset.CollectionPvtRwset { 174 coll := collPvtRwset.CollectionName 175 rwsetHash := txRWSet.GetPvtDataHash(ns, coll) 176 if rwsetHash == nil { 177 logger.Warningf("namespace: %s collection: %s was not accessed by txNum %d in BlkNum %d. "+ 178 "Unnecessary pvtdata has been passed", ns, coll, txNum, blkNum) 179 invalidNsColl = append(invalidNsColl, &nsColl{ns, coll}) 180 continue 181 } 182 183 if !bytes.Equal(util.ComputeSHA256(collPvtRwset.Rwset), rwsetHash) { 184 invalidPvtData = append(invalidPvtData, &ledger.PvtdataHashMismatch{ 185 BlockNum: blkNum, 186 TxNum: txNum, 187 Namespace: ns, 188 Collection: coll, 189 }) 190 invalidNsColl = append(invalidNsColl, &nsColl{ns, coll}) 191 } 192 } 193 return invalidPvtData, invalidNsColl 194 } 195 196 func verifyHashesViaBootKVHashes(reconciledPvtdata *ledger.ReconciledPvtdata, pvtdataStore *pvtdatastorage.Store) ( 197 []*ledger.TxPvtData, []*ledger.PvtdataHashMismatch, error, 198 ) { 199 var validPvtData []*ledger.TxPvtData 200 var invalidPvtData []*ledger.PvtdataHashMismatch 201 202 blkNum := reconciledPvtdata.BlockNum 203 204 for txNum, txData := range reconciledPvtdata.WriteSets { // Tx loop 205 var toDeleteNsColl []*nsColl 206 207 reconTx, err := rwsetutil.TxPvtRwSetFromProtoMsg(txData.WriteSet) 208 if err != nil { 209 continue 210 } 211 212 for _, reconNS := range reconTx.NsPvtRwSet { // Ns Loop 213 for _, reconColl := range reconNS.CollPvtRwSets { // coll loop 214 if reconColl.KvRwSet == nil || len(reconColl.KvRwSet.Writes) == 0 { 215 toDeleteNsColl = append(toDeleteNsColl, 216 &nsColl{ 217 ns: reconNS.NameSpace, 218 coll: reconColl.CollectionName, 219 }, 220 ) 221 continue 222 } 223 224 expectedKVHashes, err := pvtdataStore.FetchBootKVHashes(blkNum, txNum, reconNS.NameSpace, reconColl.CollectionName) 225 if err != nil { 226 return nil, nil, err 227 } 228 if len(expectedKVHashes) == 0 { 229 toDeleteNsColl = append(toDeleteNsColl, 230 &nsColl{ 231 ns: reconNS.NameSpace, 232 coll: reconColl.CollectionName, 233 }, 234 ) 235 continue 236 } 237 238 anyKVMismatch := false 239 numKVsRecieved := 0 240 keysVisited := map[string]struct{}{} 241 242 for _, reconKV := range reconColl.KvRwSet.Writes { 243 _, ok := keysVisited[reconKV.Key] 244 if ok { 245 anyKVMismatch = true 246 break 247 } 248 keysVisited[reconKV.Key] = struct{}{} 249 250 reconKeyHash := util.ComputeSHA256([]byte(reconKV.Key)) 251 reconValHash := util.ComputeSHA256(reconKV.Value) 252 253 expectedValHash, ok := expectedKVHashes[string(reconKeyHash)] 254 if ok { 255 numKVsRecieved++ 256 if !bytes.Equal(expectedValHash, reconValHash) { 257 anyKVMismatch = true 258 break 259 } 260 } 261 } 262 263 if anyKVMismatch || numKVsRecieved < len(expectedKVHashes) { 264 invalidPvtData = append(invalidPvtData, 265 &ledger.PvtdataHashMismatch{ 266 BlockNum: blkNum, 267 TxNum: txNum, 268 Namespace: reconNS.NameSpace, 269 Collection: reconColl.CollectionName, 270 }, 271 ) 272 toDeleteNsColl = append(toDeleteNsColl, 273 &nsColl{ 274 ns: reconNS.NameSpace, 275 coll: reconColl.CollectionName, 276 }, 277 ) 278 continue 279 } 280 } // end coll loop 281 } // end Ns loop 282 283 for _, nsColl := range toDeleteNsColl { 284 removeCollFromTxPvtReadWriteSet(txData.WriteSet, nsColl.ns, nsColl.coll) 285 } 286 287 if len(txData.WriteSet.NsPvtRwset) > 0 { 288 validPvtData = append(validPvtData, txData) 289 } 290 } // end Tx loop 291 return validPvtData, invalidPvtData, nil 292 }