github.com/lzy4123/fabric@v2.1.1+incompatible/gossip/privdata/dataretriever.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package privdata 8 9 import ( 10 protosgossip "github.com/hyperledger/fabric-protos-go/gossip" 11 "github.com/hyperledger/fabric-protos-go/ledger/rwset" 12 "github.com/hyperledger/fabric/core/committer" 13 "github.com/hyperledger/fabric/core/ledger" 14 "github.com/hyperledger/fabric/core/transientstore" 15 "github.com/hyperledger/fabric/gossip/privdata/common" 16 "github.com/hyperledger/fabric/gossip/util" 17 "github.com/pkg/errors" 18 ) 19 20 //go:generate mockery -dir . -name RWSetScanner -case underscore -output mocks/ 21 22 // RWSetScanner is the local interface used to generate mocks for foreign interface. 23 type RWSetScanner interface { 24 transientstore.RWSetScanner 25 } 26 27 // StorageDataRetriever defines an API to retrieve private date from the storage. 28 type StorageDataRetriever interface { 29 // CollectionRWSet retrieves for give digest relevant private data if 30 // available otherwise returns nil, bool which is true if data fetched from ledger and false if was fetched from transient store, and an error 31 CollectionRWSet(dig []*protosgossip.PvtDataDigest, blockNum uint64) (Dig2PvtRWSetWithConfig, bool, error) 32 } 33 34 type dataRetriever struct { 35 store *transientstore.Store 36 committer committer.Committer 37 } 38 39 // NewDataRetriever constructing function for implementation of the 40 // StorageDataRetriever interface 41 func NewDataRetriever(store *transientstore.Store, committer committer.Committer) StorageDataRetriever { 42 return &dataRetriever{ 43 store: store, 44 committer: committer, 45 } 46 } 47 48 // CollectionRWSet retrieves for give digest relevant private data if 49 // available otherwise returns nil, bool which is true if data fetched from ledger and false if was fetched from transient store, and an error 50 func (dr *dataRetriever) CollectionRWSet(digests []*protosgossip.PvtDataDigest, blockNum uint64) (Dig2PvtRWSetWithConfig, bool, error) { 51 height, err := dr.committer.LedgerHeight() 52 if err != nil { 53 // if there is an error getting info from the ledger, we need to try to read from transient store 54 return nil, false, errors.Wrap(err, "wasn't able to read ledger height") 55 } 56 if height <= blockNum { 57 logger.Debug("Current ledger height ", height, "is below requested block sequence number", 58 blockNum, "retrieving private data from transient store") 59 } 60 61 if height <= blockNum { // Check whenever current ledger height is equal or below block sequence num. 62 results := make(Dig2PvtRWSetWithConfig) 63 for _, dig := range digests { 64 filter := map[string]ledger.PvtCollFilter{ 65 dig.Namespace: map[string]bool{ 66 dig.Collection: true, 67 }, 68 } 69 pvtRWSet, err := dr.fromTransientStore(dig, filter) 70 if err != nil { 71 logger.Errorf("couldn't read from transient store private read-write set, "+ 72 "digest %+v, because of %s", dig, err) 73 continue 74 } 75 results[common.DigKey{ 76 Namespace: dig.Namespace, 77 Collection: dig.Collection, 78 TxId: dig.TxId, 79 BlockSeq: dig.BlockSeq, 80 SeqInBlock: dig.SeqInBlock, 81 }] = pvtRWSet 82 } 83 84 return results, false, nil 85 } 86 // Since ledger height is above block sequence number private data is might be available in the ledger 87 results, err := dr.fromLedger(digests, blockNum) 88 return results, true, err 89 } 90 91 func (dr *dataRetriever) fromLedger(digests []*protosgossip.PvtDataDigest, blockNum uint64) (Dig2PvtRWSetWithConfig, error) { 92 filter := make(map[string]ledger.PvtCollFilter) 93 for _, dig := range digests { 94 if _, ok := filter[dig.Namespace]; !ok { 95 filter[dig.Namespace] = make(ledger.PvtCollFilter) 96 } 97 filter[dig.Namespace][dig.Collection] = true 98 } 99 100 pvtData, err := dr.committer.GetPvtDataByNum(blockNum, filter) 101 if err != nil { 102 return nil, errors.Errorf("wasn't able to obtain private data, block sequence number %d, due to %s", blockNum, err) 103 } 104 105 results := make(Dig2PvtRWSetWithConfig) 106 for _, dig := range digests { 107 dig := dig 108 pvtRWSetWithConfig := &util.PrivateRWSetWithConfig{} 109 for _, data := range pvtData { 110 if data.WriteSet == nil { 111 logger.Warning("Received nil write set for collection tx in block", data.SeqInBlock, "block number", blockNum) 112 continue 113 } 114 115 // private data doesn't hold rwsets for namespace and collection or 116 // belongs to different transaction 117 if !data.Has(dig.Namespace, dig.Collection) || data.SeqInBlock != dig.SeqInBlock { 118 continue 119 } 120 121 pvtRWSet := dr.extractPvtRWsets(data.WriteSet.NsPvtRwset, dig.Namespace, dig.Collection) 122 pvtRWSetWithConfig.RWSet = append(pvtRWSetWithConfig.RWSet, pvtRWSet...) 123 } 124 125 confHistoryRetriever, err := dr.committer.GetConfigHistoryRetriever() 126 if err != nil { 127 return nil, errors.Errorf("cannot obtain configuration history retriever, for collection <%s>"+ 128 " txID <%s> block sequence number <%d> due to <%s>", dig.Collection, dig.TxId, dig.BlockSeq, err) 129 } 130 131 configInfo, err := confHistoryRetriever.MostRecentCollectionConfigBelow(dig.BlockSeq, dig.Namespace) 132 if err != nil { 133 return nil, errors.Errorf("cannot find recent collection config update below block sequence = %d,"+ 134 " collection name = <%s> for chaincode <%s>", dig.BlockSeq, dig.Collection, dig.Namespace) 135 } 136 137 if configInfo == nil { 138 return nil, errors.Errorf("no collection config update below block sequence = <%d>"+ 139 " collection name = <%s> for chaincode <%s> is available ", dig.BlockSeq, dig.Collection, dig.Namespace) 140 } 141 configs := extractCollectionConfig(configInfo.CollectionConfig, dig.Collection) 142 if configs == nil { 143 return nil, errors.Errorf("no collection config was found for collection <%s>"+ 144 " namespace <%s> txID <%s>", dig.Collection, dig.Namespace, dig.TxId) 145 } 146 pvtRWSetWithConfig.CollectionConfig = configs 147 results[common.DigKey{ 148 Namespace: dig.Namespace, 149 Collection: dig.Collection, 150 TxId: dig.TxId, 151 BlockSeq: dig.BlockSeq, 152 SeqInBlock: dig.SeqInBlock, 153 }] = pvtRWSetWithConfig 154 } 155 156 return results, nil 157 } 158 159 func (dr *dataRetriever) fromTransientStore(dig *protosgossip.PvtDataDigest, filter map[string]ledger.PvtCollFilter) (*util.PrivateRWSetWithConfig, error) { 160 results := &util.PrivateRWSetWithConfig{} 161 it, err := dr.store.GetTxPvtRWSetByTxid(dig.TxId, filter) 162 if err != nil { 163 return nil, errors.Errorf("was not able to retrieve private data from transient store, namespace <%s>"+ 164 ", collection name %s, txID <%s>, due to <%s>", dig.Namespace, dig.Collection, dig.TxId, err) 165 } 166 defer it.Close() 167 168 maxEndorsedAt := uint64(0) 169 for { 170 res, err := it.Next() 171 if err != nil { 172 return nil, errors.Errorf("error getting next element out of private data iterator, namespace <%s>"+ 173 ", collection name <%s>, txID <%s>, due to <%s>", dig.Namespace, dig.Collection, dig.TxId, err) 174 } 175 if res == nil { 176 return results, nil 177 } 178 rws := res.PvtSimulationResultsWithConfig 179 if rws == nil { 180 logger.Debug("Skipping nil PvtSimulationResultsWithConfig received at block height", res.ReceivedAtBlockHeight) 181 continue 182 } 183 txPvtRWSet := rws.PvtRwset 184 if txPvtRWSet == nil { 185 logger.Debug("Skipping empty PvtRwset of PvtSimulationResultsWithConfig received at block height", res.ReceivedAtBlockHeight) 186 continue 187 } 188 189 colConfigs, found := rws.CollectionConfigs[dig.Namespace] 190 if !found { 191 logger.Error("No collection config was found for chaincode", dig.Namespace, "collection name", 192 dig.Namespace, "txID", dig.TxId) 193 continue 194 } 195 196 configs := extractCollectionConfig(colConfigs, dig.Collection) 197 if configs == nil { 198 logger.Error("No collection config was found for collection", dig.Collection, 199 "namespace", dig.Namespace, "txID", dig.TxId) 200 continue 201 } 202 203 pvtRWSet := dr.extractPvtRWsets(txPvtRWSet.NsPvtRwset, dig.Namespace, dig.Collection) 204 if rws.EndorsedAt >= maxEndorsedAt { 205 maxEndorsedAt = rws.EndorsedAt 206 results.CollectionConfig = configs 207 } 208 results.RWSet = append(results.RWSet, pvtRWSet...) 209 } 210 } 211 212 func (dr *dataRetriever) extractPvtRWsets(pvtRWSets []*rwset.NsPvtReadWriteSet, namespace string, collectionName string) []util.PrivateRWSet { 213 pRWsets := []util.PrivateRWSet{} 214 215 // Iterate over all namespaces 216 for _, nsws := range pvtRWSets { 217 // and in each namespace - iterate over all collections 218 if nsws.Namespace != namespace { 219 logger.Debug("Received private data namespace ", nsws.Namespace, " instead of ", namespace, " skipping...") 220 continue 221 } 222 for _, col := range nsws.CollectionPvtRwset { 223 // This isn't the collection we're looking for 224 if col.CollectionName != collectionName { 225 logger.Debug("Received private data collection ", col.CollectionName, " instead of ", collectionName, " skipping...") 226 continue 227 } 228 // Add the collection pRWset to the accumulated set 229 pRWsets = append(pRWsets, col.Rwset) 230 } 231 } 232 233 return pRWsets 234 }