github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/common/validation/statebased/vpmanagerimpl.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package statebased
     8  
     9  import (
    10  	"sync"
    11  
    12  	"github.com/hechain20/hechain/common/flogging"
    13  	validation "github.com/hechain20/hechain/core/handlers/validation/api/state"
    14  	"github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/rwsetutil"
    15  	pb "github.com/hyperledger/fabric-protos-go/peer"
    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  }