github.com/lzy4123/fabric@v2.1.1+incompatible/core/common/validation/statebased/vpmanagerimpl.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package statebased 8 9 import ( 10 "sync" 11 12 pb "github.com/hyperledger/fabric-protos-go/peer" 13 "github.com/hyperledger/fabric/common/flogging" 14 validation "github.com/hyperledger/fabric/core/handlers/validation/api/state" 15 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil" 16 "github.com/pkg/errors" 17 ) 18 19 var logger = flogging.MustGetLogger("vscc") 20 21 /**********************************************************************************************************/ 22 /**********************************************************************************************************/ 23 24 type ledgerKeyID struct { 25 cc string 26 coll string 27 key string 28 } 29 30 func newLedgerKeyID(cc, coll, key string) *ledgerKeyID { 31 return &ledgerKeyID{cc, coll, key} 32 } 33 34 /**********************************************************************************************************/ 35 /**********************************************************************************************************/ 36 37 // txDependency provides synchronization mechanisms for a transaction's 38 // dependencies at a vscc scope, where transactions are validated on a per- 39 // namespace basis: 40 // -) the pair waitForDepInserted() / signalDepInserted() is used to sync on 41 // insertion of dependencies 42 // -) the pair waitForAndRetrieveValidationResult() / signalValidationResult() 43 // is used to sync on the validation results for a given namespace 44 type txDependency struct { 45 mutex sync.Mutex 46 cond *sync.Cond 47 validationResultMap map[string]error 48 depInserted chan struct{} 49 } 50 51 func newTxDependency() *txDependency { 52 txd := &txDependency{ 53 depInserted: make(chan struct{}), 54 validationResultMap: make(map[string]error), 55 } 56 txd.cond = sync.NewCond(&txd.mutex) 57 return txd 58 } 59 60 // waitForDepInserted waits until dependencies introduced by a transaction 61 // have been inserted. The function returns as soon as 62 // d.depInserted has been closed by signalDepInserted 63 func (d *txDependency) waitForDepInserted() { 64 <-d.depInserted 65 } 66 67 // signalDepInserted signals that transactions dependencies introduced 68 // by transaction d.txNum have been inserted. The function 69 // closes d.depInserted, causing all callers of waitForDepInserted to 70 // return. This function can only be called once on this object 71 func (d *txDependency) signalDepInserted() { 72 close(d.depInserted) 73 } 74 75 // waitForAndRetrieveValidationResult returns the validation results 76 // for namespace `ns` - possibly waiting for the corresponding call 77 // to signalValidationResult to finish first. 78 func (d *txDependency) waitForAndRetrieveValidationResult(ns string) error { 79 d.mutex.Lock() 80 defer d.mutex.Unlock() 81 82 err, ok := d.validationResultMap[ns] 83 if ok { 84 return err 85 } 86 87 for !ok { 88 d.cond.Wait() 89 err, ok = d.validationResultMap[ns] 90 } 91 92 return err 93 } 94 95 // signalValidationResult signals that validation of namespace `ns` 96 // for transaction `d.txnum` completed with error `err`. Results 97 // are cached into a map. We also broadcast a conditional variable 98 // to wake up possible callers of waitForAndRetrieveValidationResult 99 func (d *txDependency) signalValidationResult(ns string, err error) { 100 d.mutex.Lock() 101 defer d.mutex.Unlock() 102 103 d.validationResultMap[ns] = err 104 d.cond.Broadcast() 105 } 106 107 /**********************************************************************************************************/ 108 /**********************************************************************************************************/ 109 110 // validationContext captures the all dependencies within a single block 111 type validationContext struct { 112 // mutex ensures that only one goroutine at a 113 // time will modify blockHeight, depsByTxnumMap 114 // or depsByLedgerKeyIDMap 115 mutex sync.RWMutex 116 blockHeight uint64 117 depsByTxnumMap map[uint64]*txDependency 118 depsByLedgerKeyIDMap map[ledgerKeyID]map[uint64]*txDependency 119 } 120 121 func (c *validationContext) forBlock(newHeight uint64) *validationContext { 122 c.mutex.RLock() 123 curHeight := c.blockHeight 124 c.mutex.RUnlock() 125 126 if curHeight > newHeight { 127 logger.Panicf("programming error: block with number %d validated after block with number %d", newHeight, curHeight) 128 } 129 130 // block 0 is the genesis block, and so the first block that comes here 131 // will actually be block 1, forcing a reset 132 if curHeight < newHeight { 133 c.mutex.Lock() 134 defer c.mutex.Unlock() 135 136 if c.blockHeight < newHeight { 137 c.blockHeight = newHeight 138 c.depsByLedgerKeyIDMap = map[ledgerKeyID]map[uint64]*txDependency{} 139 c.depsByTxnumMap = map[uint64]*txDependency{} 140 } 141 } 142 143 return c 144 } 145 146 func (c *validationContext) addDependency(kid *ledgerKeyID, txnum uint64, dep *txDependency) { 147 c.mutex.Lock() 148 defer c.mutex.Unlock() 149 150 // create map if necessary 151 _, ok := c.depsByLedgerKeyIDMap[*kid] 152 if !ok { 153 c.depsByLedgerKeyIDMap[*kid] = map[uint64]*txDependency{} 154 } 155 156 c.depsByLedgerKeyIDMap[*kid][txnum] = dep 157 } 158 159 func (c *validationContext) dependenciesForTxnum(kid *ledgerKeyID, txnum uint64) []*txDependency { 160 c.mutex.RLock() 161 defer c.mutex.RUnlock() 162 163 var deps []*txDependency 164 165 dl, in := c.depsByLedgerKeyIDMap[*kid] 166 if in { 167 deps = make([]*txDependency, 0, len(dl)) 168 for depTxnum, dep := range dl { 169 if depTxnum < txnum { 170 deps = append(deps, dep) 171 } 172 } 173 } 174 175 return deps 176 } 177 178 func (c *validationContext) getOrCreateDependencyByTxnum(txnum uint64) *txDependency { 179 c.mutex.RLock() 180 dep, ok := c.depsByTxnumMap[txnum] 181 c.mutex.RUnlock() 182 183 if !ok { 184 c.mutex.Lock() 185 defer c.mutex.Unlock() 186 dep, ok = c.depsByTxnumMap[txnum] 187 if !ok { 188 dep = newTxDependency() 189 c.depsByTxnumMap[txnum] = dep 190 } 191 } 192 193 return dep 194 } 195 196 func (c *validationContext) waitForValidationResults(kid *ledgerKeyID, blockNum uint64, txnum uint64) error { 197 // in the code below we see whether any transaction in this block 198 // that precedes txnum introduces a dependency. We do so by 199 // extracting from the map all txDependency instances for txnum 200 // strictly lower than ours and retrieving their validation 201 // result. If the validation result of *any* of them is a nil 202 // error, we have a dependency. Otherwise we have no dependency. 203 // Note that depsMap is iterated in non-predictable order. 204 // This does not violate correctness, since the hasDependencies 205 // should return true if *any* dependency has been introduced 206 207 // we proceed in two steps: 208 // 1) while holding the mutex, we get a snapshot of all dependencies 209 // that affect us and put them in a local slice; we then release 210 // the mutex 211 // 2) we traverse the slice of dependencies and for each, retrieve 212 // the validation result 213 // The two step approach is required to avoid a deadlock where the 214 // consumer (the caller of this function) holds the mutex and thus 215 // prevents the producer (the caller of signalValidationResult) to 216 // produce the result. 217 218 for _, dep := range c.dependenciesForTxnum(kid, txnum) { 219 if valErr := dep.waitForAndRetrieveValidationResult(kid.cc); valErr == nil { 220 return &ValidationParameterUpdatedError{ 221 CC: kid.cc, 222 Coll: kid.coll, 223 Key: kid.key, 224 Height: blockNum, 225 Txnum: txnum, 226 } 227 } 228 } 229 return nil 230 } 231 232 /**********************************************************************************************************/ 233 /**********************************************************************************************************/ 234 235 // PolicyTranslator translates marshaled policies 236 // into different protobuf representations 237 type PolicyTranslator interface { 238 // Translate performs the translation of the 239 // supplied bytes, returning the translated 240 // version or an error if one occurred 241 Translate([]byte) ([]byte, error) 242 } 243 244 type KeyLevelValidationParameterManagerImpl struct { 245 StateFetcher validation.StateFetcher 246 validationCtx validationContext 247 PolicyTranslator PolicyTranslator 248 } 249 250 // ExtractValidationParameterDependency implements the method of 251 // the same name of the KeyLevelValidationParameterManager interface 252 // Note that this function doesn't take any namespace argument. This is 253 // because we want to inspect all namespaces for which this transaction 254 // modifies metadata. 255 func (m *KeyLevelValidationParameterManagerImpl) ExtractValidationParameterDependency(blockNum, txNum uint64, rwsetBytes []byte) { 256 vCtx := m.validationCtx.forBlock(blockNum) 257 258 // this object represents the dependency that transaction (blockNum, txNum) introduces 259 dep := vCtx.getOrCreateDependencyByTxnum(txNum) 260 261 rwset := &rwsetutil.TxRwSet{} 262 err := rwset.FromProtoBytes(rwsetBytes) 263 // note that we silently discard broken read-write 264 // sets - ledger will invalidate them anyway 265 if err == nil { 266 // here we cycle through all metadata updates generated by this transaction 267 // and signal that transaction (blockNum, txNum) modifies them so that 268 // all subsequent transaction know they have to wait for validation of 269 // transaction (blockNum, txNum) before they can continue 270 for _, rws := range rwset.NsRwSets { 271 for _, mw := range rws.KvRwSet.MetadataWrites { 272 // record the fact that this key has a dependency on our tx 273 vCtx.addDependency(newLedgerKeyID(rws.NameSpace, "", mw.Key), txNum, dep) 274 } 275 276 for _, cw := range rws.CollHashedRwSets { 277 for _, mw := range cw.HashedRwSet.MetadataWrites { 278 // record the fact that this (pvt) key has a dependency on our tx 279 vCtx.addDependency(newLedgerKeyID(rws.NameSpace, cw.CollectionName, string(mw.KeyHash)), txNum, dep) 280 } 281 } 282 } 283 } else { 284 logger.Warningf("unmarshalling the read write set returned error '%s', skipping", err) 285 } 286 287 // signal that we have introduced all dependencies for this transaction 288 dep.signalDepInserted() 289 } 290 291 // GetValidationParameterForKey implements the method of 292 // the same name of the KeyLevelValidationParameterManager interface 293 func (m *KeyLevelValidationParameterManagerImpl) GetValidationParameterForKey(cc, coll, key string, blockNum, txNum uint64) ([]byte, error) { 294 vCtx := m.validationCtx.forBlock(blockNum) 295 296 // wait until all txes before us have introduced dependencies 297 for i := int64(txNum) - 1; i >= 0; i-- { 298 txdep := vCtx.getOrCreateDependencyByTxnum(uint64(i)) 299 txdep.waitForDepInserted() 300 } 301 302 // wait until the validation results for all dependencies in the cc namespace are available 303 // bail, if the validation parameter has been updated in the meantime 304 err := vCtx.waitForValidationResults(newLedgerKeyID(cc, coll, key), blockNum, txNum) 305 if err != nil { 306 logger.Errorf(err.Error()) 307 return nil, err 308 } 309 310 // if we're here, it means that it is safe to retrieve validation 311 // parameters for the requested key from the ledger 312 313 state, err := m.StateFetcher.FetchState() 314 if err != nil { 315 err = errors.WithMessage(err, "could not retrieve ledger") 316 logger.Errorf(err.Error()) 317 return nil, err 318 } 319 defer state.Done() 320 321 var mdMap map[string][]byte 322 if coll == "" { 323 mdMap, err = state.GetStateMetadata(cc, key) 324 if err != nil { 325 err = errors.WithMessagef(err, "could not retrieve metadata for %s:%s", cc, key) 326 logger.Errorf(err.Error()) 327 return nil, err 328 } 329 } else { 330 mdMap, err = state.GetPrivateDataMetadataByHash(cc, coll, []byte(key)) 331 if err != nil { 332 err = errors.WithMessagef(err, "could not retrieve metadata for %s:%s:%x", cc, coll, []byte(key)) 333 logger.Errorf(err.Error()) 334 return nil, err 335 } 336 } 337 338 policy, err := m.PolicyTranslator.Translate(mdMap[pb.MetaDataKeys_VALIDATION_PARAMETER.String()]) 339 if err != nil { 340 if coll == "" { 341 return nil, errors.WithMessagef(err, "could not translate policy for %s:%s", cc, key) 342 } else { 343 return nil, errors.WithMessagef(err, "could not translate policy for %s:%s:%x", cc, coll, []byte(key)) 344 } 345 } 346 347 return policy, nil 348 } 349 350 // SetTxValidationCode implements the method of the same name of 351 // the KeyLevelValidationParameterManager interface. Note that 352 // this function receives a namespace argument so that it records 353 // the validation result for this transaction and for this chaincode. 354 func (m *KeyLevelValidationParameterManagerImpl) SetTxValidationResult(ns string, blockNum, txNum uint64, err error) { 355 vCtx := m.validationCtx.forBlock(blockNum) 356 357 // this object represents the dependency that the transaction of our caller introduces 358 dep := vCtx.getOrCreateDependencyByTxnum(txNum) 359 360 // signal the validation status of this tx 361 dep.signalValidationResult(ns, err) 362 }