github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/committer/txvalidator/v14/vscc_validator.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package txvalidator
     8  
     9  import (
    10  	"fmt"
    11  
    12  	"github.com/golang/protobuf/proto"
    13  	commonerrors "github.com/hechain20/hechain/common/errors"
    14  	"github.com/hechain20/hechain/common/policydsl"
    15  	"github.com/hechain20/hechain/core/common/ccprovider"
    16  	"github.com/hechain20/hechain/core/common/sysccprovider"
    17  	validation "github.com/hechain20/hechain/core/handlers/validation/api"
    18  	"github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/rwsetutil"
    19  	"github.com/hechain20/hechain/protoutil"
    20  	"github.com/hyperledger/fabric-protos-go/common"
    21  	"github.com/hyperledger/fabric-protos-go/peer"
    22  	"github.com/pkg/errors"
    23  )
    24  
    25  // VsccValidatorImpl is the implementation used to call
    26  // the vscc chaincode and validate block transactions
    27  type VsccValidatorImpl struct {
    28  	channelID       string
    29  	cr              ChannelResources
    30  	pluginValidator *PluginValidator
    31  }
    32  
    33  // newVSCCValidator creates new vscc validator
    34  func newVSCCValidator(channelID string, cr ChannelResources, pluginValidator *PluginValidator) *VsccValidatorImpl {
    35  	return &VsccValidatorImpl{
    36  		channelID:       channelID,
    37  		cr:              cr,
    38  		pluginValidator: pluginValidator,
    39  	}
    40  }
    41  
    42  func getChaincodeHeaderExtension(hdr *common.Header) (*peer.ChaincodeHeaderExtension, error) {
    43  	chdr, err := protoutil.UnmarshalChannelHeader(hdr.ChannelHeader)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	chaincodeHdrExt := &peer.ChaincodeHeaderExtension{}
    49  	err = proto.Unmarshal(chdr.Extension, chaincodeHdrExt)
    50  	return chaincodeHdrExt, errors.Wrap(err, "error unmarshalling ChaincodeHeaderExtension")
    51  }
    52  
    53  // VSCCValidateTx executes vscc validation for transaction
    54  func (v *VsccValidatorImpl) VSCCValidateTx(seq int, payload *common.Payload, envBytes []byte, block *common.Block) (peer.TxValidationCode, error) {
    55  	chainID := v.channelID
    56  	logger.Debugf("[%s] VSCCValidateTx starts for bytes %p", chainID, envBytes)
    57  
    58  	// get header extensions so we have the chaincode ID
    59  	hdrExt, err := getChaincodeHeaderExtension(payload.Header)
    60  	if err != nil {
    61  		return peer.TxValidationCode_BAD_HEADER_EXTENSION, err
    62  	}
    63  
    64  	// get channel header
    65  	chdr, err := protoutil.UnmarshalChannelHeader(payload.Header.ChannelHeader)
    66  	if err != nil {
    67  		return peer.TxValidationCode_BAD_CHANNEL_HEADER, err
    68  	}
    69  
    70  	/* obtain the list of namespaces we're writing stuff to;
    71  	   at first, we establish a few facts about this invocation:
    72  	   1) which namespaces does it write to?
    73  	   2) does it write to LSCC's namespace?
    74  	   3) does it write to any cc that cannot be invoked? */
    75  	writesToLSCC := false
    76  	writesToNonInvokableSCC := false
    77  	respPayload, err := protoutil.GetActionFromEnvelope(envBytes)
    78  	if err != nil {
    79  		return peer.TxValidationCode_BAD_RESPONSE_PAYLOAD, errors.WithMessage(err, "GetActionFromEnvelope failed")
    80  	}
    81  	txRWSet := &rwsetutil.TxRwSet{}
    82  	if err = txRWSet.FromProtoBytes(respPayload.Results); err != nil {
    83  		return peer.TxValidationCode_BAD_RWSET, errors.WithMessage(err, "txRWSet.FromProtoBytes failed")
    84  	}
    85  
    86  	// Verify the header extension and response payload contain the ChaincodeId
    87  	if hdrExt.ChaincodeId == nil {
    88  		return peer.TxValidationCode_INVALID_OTHER_REASON, errors.New("nil ChaincodeId in header extension")
    89  	}
    90  
    91  	if respPayload.ChaincodeId == nil {
    92  		return peer.TxValidationCode_INVALID_OTHER_REASON, errors.New("nil ChaincodeId in ChaincodeAction")
    93  	}
    94  
    95  	// get name and version of the cc we invoked
    96  	ccID := hdrExt.ChaincodeId.Name
    97  	ccVer := respPayload.ChaincodeId.Version
    98  
    99  	// sanity check on ccID
   100  	if ccID == "" {
   101  		err = errors.New("invalid chaincode ID")
   102  		logger.Errorf("%+v", err)
   103  		return peer.TxValidationCode_INVALID_OTHER_REASON, err
   104  	}
   105  	if ccID != respPayload.ChaincodeId.Name {
   106  		err = errors.Errorf("inconsistent ccid info (%s/%s)", ccID, respPayload.ChaincodeId.Name)
   107  		logger.Errorf("%+v", err)
   108  		return peer.TxValidationCode_INVALID_OTHER_REASON, err
   109  	}
   110  	// sanity check on ccver
   111  	if ccVer == "" {
   112  		err = errors.New("invalid chaincode version")
   113  		logger.Errorf("%+v", err)
   114  		return peer.TxValidationCode_INVALID_OTHER_REASON, err
   115  	}
   116  
   117  	var wrNamespace []string
   118  	alwaysEnforceOriginalNamespace := v.cr.Capabilities().V1_2Validation()
   119  	if alwaysEnforceOriginalNamespace {
   120  		wrNamespace = append(wrNamespace, ccID)
   121  		if respPayload.Events != nil {
   122  			ccEvent := &peer.ChaincodeEvent{}
   123  			if err = proto.Unmarshal(respPayload.Events, ccEvent); err != nil {
   124  				return peer.TxValidationCode_INVALID_OTHER_REASON, errors.Wrapf(err, "invalid chaincode event")
   125  			}
   126  			if ccEvent.ChaincodeId != ccID {
   127  				return peer.TxValidationCode_INVALID_OTHER_REASON, errors.Errorf("chaincode event chaincode id does not match chaincode action chaincode id")
   128  			}
   129  		}
   130  	}
   131  
   132  	namespaces := make(map[string]struct{})
   133  	for _, ns := range txRWSet.NsRwSets {
   134  		// check to make sure there is no duplicate namespace in txRWSet
   135  		if _, ok := namespaces[ns.NameSpace]; ok {
   136  			return peer.TxValidationCode_ILLEGAL_WRITESET, errors.Errorf("duplicate namespace '%s' in txRWSet", ns.NameSpace)
   137  		}
   138  		namespaces[ns.NameSpace] = struct{}{}
   139  
   140  		if !v.txWritesToNamespace(ns) {
   141  			continue
   142  		}
   143  
   144  		// Check to make sure we did not already populate this chaincode
   145  		// name to avoid checking the same namespace twice
   146  		if ns.NameSpace != ccID || !alwaysEnforceOriginalNamespace {
   147  			wrNamespace = append(wrNamespace, ns.NameSpace)
   148  		}
   149  
   150  		if !writesToLSCC && ns.NameSpace == "lscc" {
   151  			writesToLSCC = true
   152  		}
   153  
   154  		if !writesToNonInvokableSCC && IsSysCCAndNotInvokableCC2CC(ns.NameSpace) {
   155  			writesToNonInvokableSCC = true
   156  		}
   157  
   158  		if !writesToNonInvokableSCC && IsSysCCAndNotInvokableExternal(ns.NameSpace) {
   159  			writesToNonInvokableSCC = true
   160  		}
   161  	}
   162  
   163  	// we've gathered all the info required to proceed to validation;
   164  	// validation will behave differently depending on the type of
   165  	// chaincode (system vs. application)
   166  
   167  	if !IsSysCC(ccID) {
   168  		// if we're here, we know this is an invocation of an application chaincode;
   169  		// first of all, we make sure that:
   170  		// 1) we don't write to LSCC - an application chaincode is free to invoke LSCC
   171  		//    for instance to get information about itself or another chaincode; however
   172  		//    these legitimate invocations only ready from LSCC's namespace; currently
   173  		//    only two functions of LSCC write to its namespace: deploy and upgrade and
   174  		//    neither should be used by an application chaincode
   175  		if writesToLSCC {
   176  			return peer.TxValidationCode_ILLEGAL_WRITESET,
   177  				errors.Errorf("chaincode %s attempted to write to the namespace of LSCC", ccID)
   178  		}
   179  		// 2) we don't write to the namespace of a chaincode that we cannot invoke - if
   180  		//    the chaincode cannot be invoked in the first place, there's no legitimate
   181  		//    way in which a transaction has a write set that writes to it; additionally
   182  		//    we don't have any means of verifying whether the transaction had the rights
   183  		//    to perform that write operation because in v1, system chaincodes do not have
   184  		//    any endorsement policies to speak of. So if the chaincode can't be invoked
   185  		//    it can't be written to by an invocation of an application chaincode
   186  		if writesToNonInvokableSCC {
   187  			return peer.TxValidationCode_ILLEGAL_WRITESET,
   188  				errors.Errorf("chaincode %s attempted to write to the namespace of a system chaincode that cannot be invoked", ccID)
   189  		}
   190  
   191  		// validate *EACH* read write set according to its chaincode's endorsement policy
   192  		for _, ns := range wrNamespace {
   193  			// Get latest chaincode version, vscc and validate policy
   194  			txcc, vscc, policy, err := v.GetInfoForValidate(chdr, ns)
   195  			if err != nil {
   196  				logger.Errorf("GetInfoForValidate for txId = %s returned error: %+v", chdr.TxId, err)
   197  				return peer.TxValidationCode_INVALID_OTHER_REASON, err
   198  			}
   199  
   200  			// if the namespace corresponds to the cc that was originally
   201  			// invoked, we check that the version of the cc that was
   202  			// invoked corresponds to the version that lscc has returned
   203  			if ns == ccID && txcc.ChaincodeVersion != ccVer {
   204  				err = errors.Errorf("chaincode %s:%s/%s didn't match %s:%s/%s in lscc", ccID, ccVer, chdr.ChannelId, txcc.ChaincodeName, txcc.ChaincodeVersion, chdr.ChannelId)
   205  				logger.Errorf("%+v", err)
   206  				return peer.TxValidationCode_EXPIRED_CHAINCODE, err
   207  			}
   208  
   209  			// do VSCC validation
   210  			ctx := &Context{
   211  				Seq:       seq,
   212  				Envelope:  envBytes,
   213  				Block:     block,
   214  				TxID:      chdr.TxId,
   215  				Channel:   chdr.ChannelId,
   216  				Namespace: ns,
   217  				Policy:    policy,
   218  				VSCCName:  vscc.ChaincodeName,
   219  			}
   220  			if err = v.VSCCValidateTxForCC(ctx); err != nil {
   221  				switch err.(type) {
   222  				case *commonerrors.VSCCEndorsementPolicyError:
   223  					return peer.TxValidationCode_ENDORSEMENT_POLICY_FAILURE, err
   224  				default:
   225  					return peer.TxValidationCode_INVALID_OTHER_REASON, err
   226  				}
   227  			}
   228  		}
   229  	} else {
   230  		// make sure that we can invoke this system chaincode - if the chaincode
   231  		// cannot be invoked through a proposal to this peer, we have to drop the
   232  		// transaction; if we didn't, we wouldn't know how to decide whether it's
   233  		// valid or not because in v1, system chaincodes have no endorsement policy
   234  		if IsSysCCAndNotInvokableExternal(ccID) {
   235  			return peer.TxValidationCode_ILLEGAL_WRITESET,
   236  				errors.Errorf("committing an invocation of cc %s is illegal", ccID)
   237  		}
   238  
   239  		// Get latest chaincode version, vscc and validate policy
   240  		_, vscc, policy, err := v.GetInfoForValidate(chdr, ccID)
   241  		if err != nil {
   242  			logger.Errorf("GetInfoForValidate for txId = %s returned error: %+v", chdr.TxId, err)
   243  			return peer.TxValidationCode_INVALID_OTHER_REASON, err
   244  		}
   245  
   246  		// validate the transaction as an invocation of this system chaincode;
   247  		// vscc will have to do custom validation for this system chaincode
   248  		// currently, VSCC does custom validation for LSCC only; if an hlf
   249  		// user creates a new system chaincode which is invokable from the outside
   250  		// they have to modify VSCC to provide appropriate validation
   251  		ctx := &Context{
   252  			Seq:       seq,
   253  			Envelope:  envBytes,
   254  			Block:     block,
   255  			TxID:      chdr.TxId,
   256  			Channel:   chdr.ChannelId,
   257  			Namespace: ccID,
   258  			Policy:    policy,
   259  			VSCCName:  vscc.ChaincodeName,
   260  		}
   261  		if err = v.VSCCValidateTxForCC(ctx); err != nil {
   262  			switch err.(type) {
   263  			case *commonerrors.VSCCEndorsementPolicyError:
   264  				return peer.TxValidationCode_ENDORSEMENT_POLICY_FAILURE, err
   265  			default:
   266  				return peer.TxValidationCode_INVALID_OTHER_REASON, err
   267  			}
   268  		}
   269  	}
   270  	logger.Debugf("[%s] VSCCValidateTx completes env bytes %p", chainID, envBytes)
   271  	return peer.TxValidationCode_VALID, nil
   272  }
   273  
   274  func (v *VsccValidatorImpl) VSCCValidateTxForCC(ctx *Context) error {
   275  	logger.Debug("Validating", ctx, "with plugin")
   276  	err := v.pluginValidator.ValidateWithPlugin(ctx)
   277  	if err == nil {
   278  		return nil
   279  	}
   280  	// If the error is a pluggable validation execution error, cast it to the common errors ExecutionFailureError.
   281  	if e, isExecutionError := err.(*validation.ExecutionFailureError); isExecutionError {
   282  		return &commonerrors.VSCCExecutionFailureError{Err: e}
   283  	}
   284  	// Else, treat it as an endorsement error.
   285  	return &commonerrors.VSCCEndorsementPolicyError{Err: err}
   286  }
   287  
   288  func (v *VsccValidatorImpl) getCDataForCC(chid, ccid string) (*ccprovider.ChaincodeData, error) {
   289  	l := v.cr.Ledger()
   290  	if l == nil {
   291  		return nil, errors.New("nil ledger instance")
   292  	}
   293  
   294  	qe, err := l.NewQueryExecutor()
   295  	if err != nil {
   296  		return nil, errors.WithMessage(err, "could not retrieve QueryExecutor")
   297  	}
   298  	defer qe.Done()
   299  
   300  	bytes, err := qe.GetState("lscc", ccid)
   301  	if err != nil {
   302  		return nil, &commonerrors.VSCCInfoLookupFailureError{
   303  			Reason: fmt.Sprintf("Could not retrieve state for chaincode %s, error %s", ccid, err),
   304  		}
   305  	}
   306  
   307  	if bytes == nil {
   308  		return nil, errors.Errorf("lscc's state for [%s] not found.", ccid)
   309  	}
   310  
   311  	cd := &ccprovider.ChaincodeData{}
   312  	err = proto.Unmarshal(bytes, cd)
   313  	if err != nil {
   314  		return nil, errors.Wrap(err, "unmarshalling ChaincodeQueryResponse failed")
   315  	}
   316  
   317  	if cd.Vscc == "" {
   318  		return nil, errors.Errorf("lscc's state for [%s] is invalid, vscc field must be set", ccid)
   319  	}
   320  
   321  	if len(cd.Policy) == 0 {
   322  		return nil, errors.Errorf("lscc's state for [%s] is invalid, policy field must be set", ccid)
   323  	}
   324  
   325  	return cd, err
   326  }
   327  
   328  // GetInfoForValidate gets the ChaincodeInstance(with latest version) of tx, vscc and policy from lscc
   329  func (v *VsccValidatorImpl) GetInfoForValidate(chdr *common.ChannelHeader, ccID string) (*sysccprovider.ChaincodeInstance, *sysccprovider.ChaincodeInstance, []byte, error) {
   330  	cc := &sysccprovider.ChaincodeInstance{
   331  		ChannelID:     chdr.ChannelId,
   332  		ChaincodeName: ccID,
   333  	}
   334  	vscc := &sysccprovider.ChaincodeInstance{
   335  		ChannelID:     chdr.ChannelId,
   336  		ChaincodeName: "vscc", // default vscc for system chaincodes
   337  	}
   338  	var policy []byte
   339  	var err error
   340  	if !IsSysCC(ccID) {
   341  		// when we are validating a chaincode that is not a
   342  		// system CC, we need to ask the CC to give us the name
   343  		// of VSCC and of the policy that should be used
   344  
   345  		// obtain name of the VSCC and the policy
   346  		cd, err := v.getCDataForCC(chdr.ChannelId, ccID)
   347  		if err != nil {
   348  			msg := fmt.Sprintf("Unable to get chaincode data from ledger for txid %s, due to %s", chdr.TxId, err)
   349  			logger.Errorf(msg)
   350  			return nil, nil, nil, err
   351  		}
   352  		cc.ChaincodeName = cd.Name
   353  		cc.ChaincodeVersion = cd.Version
   354  		vscc.ChaincodeName, policy = cd.Vscc, cd.Policy
   355  	} else {
   356  		// when we are validating a system CC, we use the default
   357  		// VSCC and a default policy that requires one signature
   358  		// from any of the members of the channel
   359  		p := policydsl.SignedByAnyMember(v.cr.GetMSPIDs())
   360  		policy, err = protoutil.Marshal(p)
   361  		if err != nil {
   362  			return nil, nil, nil, err
   363  		}
   364  	}
   365  
   366  	return cc, vscc, policy, nil
   367  }
   368  
   369  // txWritesToNamespace returns true if the supplied NsRwSet
   370  // performs a ledger write
   371  func (v *VsccValidatorImpl) txWritesToNamespace(ns *rwsetutil.NsRwSet) bool {
   372  	// check for public writes first
   373  	if ns.KvRwSet != nil && len(ns.KvRwSet.Writes) > 0 {
   374  		return true
   375  	}
   376  
   377  	// only look at collection data if we support that capability
   378  	if v.cr.Capabilities().PrivateChannelData() {
   379  		// check for private writes for all collections
   380  		for _, c := range ns.CollHashedRwSets {
   381  			if c.HashedRwSet != nil && len(c.HashedRwSet.HashedWrites) > 0 {
   382  				return true
   383  			}
   384  
   385  			// only look at private metadata writes if we support that capability
   386  			if v.cr.Capabilities().KeyLevelEndorsement() {
   387  				// private metadata updates
   388  				if c.HashedRwSet != nil && len(c.HashedRwSet.MetadataWrites) > 0 {
   389  					return true
   390  				}
   391  			}
   392  		}
   393  	}
   394  
   395  	// only look at metadata writes if we support that capability
   396  	if v.cr.Capabilities().KeyLevelEndorsement() {
   397  		// public metadata updates
   398  		if ns.KvRwSet != nil && len(ns.KvRwSet.MetadataWrites) > 0 {
   399  			return true
   400  		}
   401  	}
   402  
   403  	return false
   404  }
   405  
   406  func IsSysCCAndNotInvokableExternal(name string) bool {
   407  	return name == "vscc" || name == "escc"
   408  }
   409  
   410  func IsSysCC(name string) bool {
   411  	return name == "vscc" || name == "escc" || name == "lscc" || name == "qscc" || name == "cscc"
   412  }
   413  
   414  func IsSysCCAndNotInvokableCC2CC(name string) bool {
   415  	return name == "vscc" || name == "escc" || name == "cscc"
   416  }