github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/common/validation/statebased/validator_keylevel.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/golang/protobuf/proto"
    13  	commonerrors "github.com/hechain20/hechain/common/errors"
    14  	validation "github.com/hechain20/hechain/core/handlers/validation/api/policies"
    15  	"github.com/hechain20/hechain/core/ledger"
    16  	"github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/rwsetutil"
    17  	"github.com/hechain20/hechain/protoutil"
    18  	"github.com/hyperledger/fabric-protos-go/common"
    19  	"github.com/hyperledger/fabric-protos-go/peer"
    20  	"github.com/pkg/errors"
    21  )
    22  
    23  type epEvaluator interface {
    24  	CheckCCEPIfNotChecked(cc, coll string, blockNum, txNum uint64, sd []*protoutil.SignedData) commonerrors.TxValidationError
    25  	CheckCCEPIfNoEPChecked(cc string, blockNum, txNum uint64, sd []*protoutil.SignedData) commonerrors.TxValidationError
    26  	SBEPChecked()
    27  }
    28  
    29  /**********************************************************************************************************/
    30  /**********************************************************************************************************/
    31  
    32  type baseEvaluator struct {
    33  	epEvaluator
    34  	vpmgr         KeyLevelValidationParameterManager
    35  	policySupport validation.PolicyEvaluator
    36  }
    37  
    38  func (p *baseEvaluator) checkSBAndCCEP(cc, coll, key string, blockNum, txNum uint64, signatureSet []*protoutil.SignedData) commonerrors.TxValidationError {
    39  	// see if there is a key-level validation parameter for this key
    40  	vp, err := p.vpmgr.GetValidationParameterForKey(cc, coll, key, blockNum, txNum)
    41  	if err != nil {
    42  		// error handling for GetValidationParameterForKey follows this rationale:
    43  		switch err := errors.Cause(err).(type) {
    44  		// 1) if there is a conflict because validation params have been updated
    45  		//    by another transaction in this block, we will get ValidationParameterUpdatedError.
    46  		//    This should lead to invalidating the transaction by calling policyErr
    47  		case *ValidationParameterUpdatedError:
    48  			return policyErr(err)
    49  		// 2) if the ledger returns "determinstic" errors, that is, errors that
    50  		//    every peer in the channel will also return (such as errors linked to
    51  		//    an attempt to retrieve metadata from a non-defined collection) should be
    52  		//    logged and ignored. The ledger will take the most appropriate action
    53  		//    when performing its side of the validation.
    54  		case *ledger.CollConfigNotDefinedError, *ledger.InvalidCollNameError:
    55  			logger.Warningf(errors.WithMessage(err, "skipping key-level validation").Error())
    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) extractDependenciesForTx(blockNum, txNum uint64, envelopeBytes []byte) {
   185  	env, err := protoutil.GetEnvelopeFromBlock(envelopeBytes)
   186  	if err != nil {
   187  		logger.Warningf("while executing GetEnvelopeFromBlock got error '%s', skipping tx at height (%d,%d)", err, blockNum, txNum)
   188  		return
   189  	}
   190  
   191  	payl, err := protoutil.UnmarshalPayload(env.Payload)
   192  	if err != nil {
   193  		logger.Warningf("while executing GetPayload got error '%s', skipping tx at height (%d,%d)", err, blockNum, txNum)
   194  		return
   195  	}
   196  
   197  	tx, err := protoutil.UnmarshalTransaction(payl.Data)
   198  	if err != nil {
   199  		logger.Warningf("while executing GetTransaction got error '%s', skipping tx at height (%d,%d)", err, blockNum, txNum)
   200  		return
   201  	}
   202  
   203  	cap, err := protoutil.UnmarshalChaincodeActionPayload(tx.Actions[0].Payload)
   204  	if err != nil {
   205  		logger.Warningf("while executing GetChaincodeActionPayload got error '%s', skipping tx at height (%d,%d)", err, blockNum, txNum)
   206  		return
   207  	}
   208  
   209  	pRespPayload, err := protoutil.UnmarshalProposalResponsePayload(cap.Action.ProposalResponsePayload)
   210  	if err != nil {
   211  		logger.Warningf("while executing GetProposalResponsePayload got error '%s', skipping tx at height (%d,%d)", err, blockNum, txNum)
   212  		return
   213  	}
   214  
   215  	respPayload, err := protoutil.UnmarshalChaincodeAction(pRespPayload.Extension)
   216  	if err != nil {
   217  		logger.Warningf("while executing GetChaincodeAction got error '%s', skipping tx at height (%d,%d)", err, blockNum, txNum)
   218  		return
   219  	}
   220  
   221  	klv.vpmgr.ExtractValidationParameterDependency(blockNum, txNum, respPayload.Results)
   222  }
   223  
   224  // PreValidate implements the function of the StateBasedValidator interface
   225  func (klv *KeyLevelValidator) PreValidate(txNum uint64, block *common.Block) {
   226  	klv.blockDep.mutex.Lock()
   227  	if klv.blockDep.blockNum != block.Header.Number {
   228  		klv.blockDep.blockNum = block.Header.Number
   229  		klv.blockDep.txDepOnce = make([]sync.Once, len(block.Data.Data))
   230  	}
   231  	klv.blockDep.mutex.Unlock()
   232  
   233  	for i := int64(txNum); i >= 0; i-- {
   234  		txPosition := uint64(i)
   235  
   236  		klv.blockDep.txDepOnce[i].Do(
   237  			func() {
   238  				klv.extractDependenciesForTx(block.Header.Number, txPosition, block.Data.Data[txPosition])
   239  			})
   240  	}
   241  }
   242  
   243  // Validate implements the function of the StateBasedValidator interface
   244  func (klv *KeyLevelValidator) Validate(cc string, blockNum, txNum uint64, rwsetBytes, prp, ccEP []byte, endorsements []*peer.Endorsement) commonerrors.TxValidationError {
   245  	// construct signature set
   246  	signatureSet := []*protoutil.SignedData{}
   247  	for _, endorsement := range endorsements {
   248  		data := make([]byte, len(prp)+len(endorsement.Endorser))
   249  		copy(data, prp)
   250  		copy(data[len(prp):], endorsement.Endorser)
   251  
   252  		signatureSet = append(signatureSet, &protoutil.SignedData{
   253  			// set the data that is signed; concatenation of proposal response bytes and endorser ID
   254  			Data: data,
   255  			// set the identity that signs the message: it's the endorser
   256  			Identity: endorsement.Endorser,
   257  			// set the signature
   258  			Signature: endorsement.Signature,
   259  		})
   260  	}
   261  
   262  	// construct the policy checker object
   263  	policyEvaluator := klv.pef.Evaluator(ccEP)
   264  
   265  	// unpack the rwset
   266  	rwset := &rwsetutil.TxRwSet{}
   267  	if err := rwset.FromProtoBytes(rwsetBytes); err != nil {
   268  		return policyErr(errors.WithMessagef(err, "txRWSet.FromProtoBytes failed on tx (%d,%d)", blockNum, txNum))
   269  	}
   270  
   271  	// return the decision of the policy evaluator
   272  	err := policyEvaluator.Evaluate(blockNum, txNum, rwset.NsRwSets, cc, signatureSet)
   273  	if err != nil {
   274  		// If endorsement policy check fails, log the endorsement policy and endorser identities.
   275  		// No need to handle Unmarshal() errors since it will simply result in endorsementPolicy being empty in the log message.
   276  		endorsementPolicy := &peer.ApplicationPolicy{}
   277  		proto.Unmarshal(ccEP, endorsementPolicy)
   278  		logger.Warnw("Endorsment policy failure", "error", err, "chaincode", cc, "endorsementPolicy", endorsementPolicy, "endorsingIdentities", protoutil.LogMessageForSerializedIdentities(signatureSet))
   279  
   280  	}
   281  	return err
   282  }
   283  
   284  // PostValidate implements the function of the StateBasedValidator interface
   285  func (klv *KeyLevelValidator) PostValidate(cc string, blockNum, txNum uint64, err error) {
   286  	klv.vpmgr.SetTxValidationResult(cc, blockNum, txNum, err)
   287  }
   288  
   289  func policyErr(err error) *commonerrors.VSCCEndorsementPolicyError {
   290  	return &commonerrors.VSCCEndorsementPolicyError{
   291  		Err: err,
   292  	}
   293  }