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