github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+incompatible/core/common/validation/statebased/validator_keylevel.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  	"github.com/hyperledger/fabric-protos-go/common"
    13  	"github.com/hyperledger/fabric-protos-go/peer"
    14  	commonerrors "github.com/hyperledger/fabric/common/errors"
    15  	validation "github.com/hyperledger/fabric/core/handlers/validation/api/policies"
    16  	"github.com/hyperledger/fabric/core/ledger"
    17  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil"
    18  	"github.com/hyperledger/fabric/protoutil"
    19  	"github.com/pkg/errors"
    20  )
    21  
    22  type epEvaluator interface {
    23  	CheckCCEPIfNotChecked(cc, coll string, blockNum, txNum uint64, sd []*protoutil.SignedData) commonerrors.TxValidationError
    24  	CheckCCEPIfNoEPChecked(cc string, blockNum, txNum uint64, sd []*protoutil.SignedData) commonerrors.TxValidationError
    25  	SBEPChecked()
    26  }
    27  
    28  /**********************************************************************************************************/
    29  /**********************************************************************************************************/
    30  
    31  type baseEvaluator struct {
    32  	epEvaluator
    33  	vpmgr         KeyLevelValidationParameterManager
    34  	policySupport validation.PolicyEvaluator
    35  }
    36  
    37  func (p *baseEvaluator) checkSBAndCCEP(cc, coll, key string, blockNum, txNum uint64, signatureSet []*protoutil.SignedData) commonerrors.TxValidationError {
    38  	// see if there is a key-level validation parameter for this key
    39  	vp, err := p.vpmgr.GetValidationParameterForKey(cc, coll, key, blockNum, txNum)
    40  	if err != nil {
    41  		// error handling for GetValidationParameterForKey follows this rationale:
    42  		switch err := errors.Cause(err).(type) {
    43  		// 1) if there is a conflict because validation params have been updated
    44  		//    by another transaction in this block, we will get ValidationParameterUpdatedError.
    45  		//    This should lead to invalidating the transaction by calling policyErr
    46  		case *ValidationParameterUpdatedError:
    47  			return policyErr(err)
    48  		// 2) if the ledger returns "determinstic" errors, that is, errors that
    49  		//    every peer in the channel will also return (such as errors linked to
    50  		//    an attempt to retrieve metadata from a non-defined collection) should be
    51  		//    logged and ignored. The ledger will take the most appropriate action
    52  		//    when performing its side of the validation.
    53  		case *ledger.CollConfigNotDefinedError, *ledger.InvalidCollNameError:
    54  			logger.Warningf(errors.WithMessage(err, "skipping key-level validation").Error())
    55  			err = nil
    56  		// 3) any other type of error should return an execution failure which will
    57  		//    lead to halting the processing on this channel. Note that any non-categorized
    58  		//    deterministic error would be caught by the default and would lead to
    59  		//    a processing halt. This would certainly be a bug, but - in the absence of a
    60  		//    single, well-defined deterministic error returned by the ledger, it is
    61  		//    best to err on the side of caution and rather halt processing (because a
    62  		//    deterministic error is treated like an I/O one) rather than risking a fork
    63  		//    (in case an I/O error is treated as a deterministic one).
    64  		default:
    65  			return &commonerrors.VSCCExecutionFailureError{
    66  				Err: err,
    67  			}
    68  		}
    69  	}
    70  
    71  	// if no key-level validation parameter has been specified, the regular cc endorsement policy needs to hold
    72  	if len(vp) == 0 {
    73  		return p.CheckCCEPIfNotChecked(cc, coll, blockNum, txNum, signatureSet)
    74  	}
    75  
    76  	// validate against key-level vp
    77  	err = p.policySupport.Evaluate(vp, signatureSet)
    78  	if err != nil {
    79  		return policyErr(errors.Wrapf(err, "validation of key %s (coll'%s':ns'%s') in tx %d:%d failed", key, coll, cc, blockNum, txNum))
    80  	}
    81  
    82  	p.SBEPChecked()
    83  
    84  	return nil
    85  }
    86  
    87  func (p *baseEvaluator) Evaluate(blockNum, txNum uint64, NsRwSets []*rwsetutil.NsRwSet, ns string, sd []*protoutil.SignedData) commonerrors.TxValidationError {
    88  	// iterate over all writes in the rwset
    89  	for _, nsRWSet := range NsRwSets {
    90  		// skip other namespaces
    91  		if nsRWSet.NameSpace != ns {
    92  			continue
    93  		}
    94  
    95  		// public writes
    96  		// we validate writes against key-level validation parameters
    97  		// if any are present or the chaincode-wide endorsement policy
    98  		for _, pubWrite := range nsRWSet.KvRwSet.Writes {
    99  			err := p.checkSBAndCCEP(ns, "", pubWrite.Key, blockNum, txNum, sd)
   100  			if err != nil {
   101  				return err
   102  			}
   103  		}
   104  		// public metadata writes
   105  		// we validate writes against key-level validation parameters
   106  		// if any are present or the chaincode-wide endorsement policy
   107  		for _, pubMdWrite := range nsRWSet.KvRwSet.MetadataWrites {
   108  			err := p.checkSBAndCCEP(ns, "", pubMdWrite.Key, blockNum, txNum, sd)
   109  			if err != nil {
   110  				return err
   111  			}
   112  		}
   113  		// writes in collections
   114  		// we validate writes against key-level validation parameters
   115  		// if any are present or the chaincode-wide endorsement policy
   116  		for _, collRWSet := range nsRWSet.CollHashedRwSets {
   117  			coll := collRWSet.CollectionName
   118  			for _, hashedWrite := range collRWSet.HashedRwSet.HashedWrites {
   119  				key := string(hashedWrite.KeyHash)
   120  				err := p.checkSBAndCCEP(ns, coll, key, blockNum, txNum, sd)
   121  				if err != nil {
   122  					return err
   123  				}
   124  			}
   125  		}
   126  		// metadata writes in collections
   127  		// we validate writes against key-level validation parameters
   128  		// if any are present or the chaincode-wide endorsement policy
   129  		for _, collRWSet := range nsRWSet.CollHashedRwSets {
   130  			coll := collRWSet.CollectionName
   131  			for _, hashedMdWrite := range collRWSet.HashedRwSet.MetadataWrites {
   132  				key := string(hashedMdWrite.KeyHash)
   133  				err := p.checkSBAndCCEP(ns, coll, key, blockNum, txNum, sd)
   134  				if err != nil {
   135  					return err
   136  				}
   137  			}
   138  		}
   139  	}
   140  
   141  	// we make sure that we check at least the CCEP to honour FAB-9473
   142  	return p.CheckCCEPIfNoEPChecked(ns, blockNum, txNum, sd)
   143  }
   144  
   145  /**********************************************************************************************************/
   146  /**********************************************************************************************************/
   147  
   148  // RWSetPolicyEvaluatorFactory is a factory for policy evaluators
   149  type RWSetPolicyEvaluatorFactory interface {
   150  	// Evaluator returns a new policy evaluator
   151  	// given the supplied chaincode endorsement policy
   152  	Evaluator(ccEP []byte) RWSetPolicyEvaluator
   153  }
   154  
   155  // RWSetPolicyEvaluator provides means to evaluate transaction artefacts
   156  type RWSetPolicyEvaluator interface {
   157  	Evaluate(blockNum, txNum uint64, NsRwSets []*rwsetutil.NsRwSet, ns string, sd []*protoutil.SignedData) commonerrors.TxValidationError
   158  }
   159  
   160  /**********************************************************************************************************/
   161  /**********************************************************************************************************/
   162  
   163  type blockDependency struct {
   164  	mutex     sync.Mutex
   165  	blockNum  uint64
   166  	txDepOnce []sync.Once
   167  }
   168  
   169  // KeyLevelValidator implements per-key level ep validation
   170  type KeyLevelValidator struct {
   171  	vpmgr    KeyLevelValidationParameterManager
   172  	blockDep blockDependency
   173  	pef      RWSetPolicyEvaluatorFactory
   174  }
   175  
   176  func NewKeyLevelValidator(evaluator RWSetPolicyEvaluatorFactory, vpmgr KeyLevelValidationParameterManager) *KeyLevelValidator {
   177  	return &KeyLevelValidator{
   178  		vpmgr:    vpmgr,
   179  		blockDep: blockDependency{},
   180  		pef:      evaluator,
   181  	}
   182  }
   183  
   184  func (klv *KeyLevelValidator) invokeOnce(block *common.Block, txnum uint64) *sync.Once {
   185  	klv.blockDep.mutex.Lock()
   186  	defer klv.blockDep.mutex.Unlock()
   187  
   188  	if klv.blockDep.blockNum != block.Header.Number {
   189  		klv.blockDep.blockNum = block.Header.Number
   190  		klv.blockDep.txDepOnce = make([]sync.Once, len(block.Data.Data))
   191  	}
   192  
   193  	return &klv.blockDep.txDepOnce[txnum]
   194  }
   195  
   196  func (klv *KeyLevelValidator) extractDependenciesForTx(blockNum, txNum uint64, envelopeBytes []byte) {
   197  	env, err := protoutil.GetEnvelopeFromBlock(envelopeBytes)
   198  	if err != nil {
   199  		logger.Warningf("while executing GetEnvelopeFromBlock got error '%s', skipping tx at height (%d,%d)", err, blockNum, txNum)
   200  		return
   201  	}
   202  
   203  	payl, err := protoutil.UnmarshalPayload(env.Payload)
   204  	if err != nil {
   205  		logger.Warningf("while executing GetPayload got error '%s', skipping tx at height (%d,%d)", err, blockNum, txNum)
   206  		return
   207  	}
   208  
   209  	tx, err := protoutil.UnmarshalTransaction(payl.Data)
   210  	if err != nil {
   211  		logger.Warningf("while executing GetTransaction got error '%s', skipping tx at height (%d,%d)", err, blockNum, txNum)
   212  		return
   213  	}
   214  
   215  	cap, err := protoutil.UnmarshalChaincodeActionPayload(tx.Actions[0].Payload)
   216  	if err != nil {
   217  		logger.Warningf("while executing GetChaincodeActionPayload got error '%s', skipping tx at height (%d,%d)", err, blockNum, txNum)
   218  		return
   219  	}
   220  
   221  	pRespPayload, err := protoutil.UnmarshalProposalResponsePayload(cap.Action.ProposalResponsePayload)
   222  	if err != nil {
   223  		logger.Warningf("while executing GetProposalResponsePayload got error '%s', skipping tx at height (%d,%d)", err, blockNum, txNum)
   224  		return
   225  	}
   226  
   227  	respPayload, err := protoutil.UnmarshalChaincodeAction(pRespPayload.Extension)
   228  	if err != nil {
   229  		logger.Warningf("while executing GetChaincodeAction got error '%s', skipping tx at height (%d,%d)", err, blockNum, txNum)
   230  		return
   231  	}
   232  
   233  	klv.vpmgr.ExtractValidationParameterDependency(blockNum, txNum, respPayload.Results)
   234  }
   235  
   236  // PreValidate implements the function of the StateBasedValidator interface
   237  func (klv *KeyLevelValidator) PreValidate(txNum uint64, block *common.Block) {
   238  	for i := int64(txNum); i >= 0; i-- {
   239  		txPosition := uint64(i)
   240  
   241  		klv.invokeOnce(block, txPosition).Do(
   242  			func() {
   243  				klv.extractDependenciesForTx(block.Header.Number, txPosition, block.Data.Data[txPosition])
   244  			})
   245  	}
   246  }
   247  
   248  // Validate implements the function of the StateBasedValidator interface
   249  func (klv *KeyLevelValidator) Validate(cc string, blockNum, txNum uint64, rwsetBytes, prp, ccEP []byte, endorsements []*peer.Endorsement) commonerrors.TxValidationError {
   250  	// construct signature set
   251  	signatureSet := []*protoutil.SignedData{}
   252  	for _, endorsement := range endorsements {
   253  		data := make([]byte, len(prp)+len(endorsement.Endorser))
   254  		copy(data, prp)
   255  		copy(data[len(prp):], endorsement.Endorser)
   256  
   257  		signatureSet = append(signatureSet, &protoutil.SignedData{
   258  			// set the data that is signed; concatenation of proposal response bytes and endorser ID
   259  			Data: data,
   260  			// set the identity that signs the message: it's the endorser
   261  			Identity: endorsement.Endorser,
   262  			// set the signature
   263  			Signature: endorsement.Signature})
   264  	}
   265  
   266  	// construct the policy checker object
   267  	policyEvaluator := klv.pef.Evaluator(ccEP)
   268  
   269  	// unpack the rwset
   270  	rwset := &rwsetutil.TxRwSet{}
   271  	if err := rwset.FromProtoBytes(rwsetBytes); err != nil {
   272  		return policyErr(errors.WithMessagef(err, "txRWSet.FromProtoBytes failed on tx (%d,%d)", blockNum, txNum))
   273  	}
   274  
   275  	// return the decision of the policy evaluator
   276  	return policyEvaluator.Evaluate(blockNum, txNum, rwset.NsRwSets, cc, signatureSet)
   277  }
   278  
   279  // PostValidate implements the function of the StateBasedValidator interface
   280  func (klv *KeyLevelValidator) PostValidate(cc string, blockNum, txNum uint64, err error) {
   281  	klv.vpmgr.SetTxValidationResult(cc, blockNum, txNum, err)
   282  }
   283  
   284  func policyErr(err error) *commonerrors.VSCCEndorsementPolicyError {
   285  	return &commonerrors.VSCCEndorsementPolicyError{
   286  		Err: err,
   287  	}
   288  }