github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/core/committer/txvalidator/validator.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8                   http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package txvalidator
    18  
    19  import (
    20  	"fmt"
    21  
    22  	"github.com/golang/protobuf/proto"
    23  	"github.com/hyperledger/fabric/common/configtx"
    24  	"github.com/hyperledger/fabric/common/flogging"
    25  	coreUtil "github.com/hyperledger/fabric/common/util"
    26  	"github.com/hyperledger/fabric/core/chaincode/shim"
    27  	"github.com/hyperledger/fabric/core/common/ccprovider"
    28  	"github.com/hyperledger/fabric/core/common/sysccprovider"
    29  	"github.com/hyperledger/fabric/core/common/validation"
    30  	"github.com/hyperledger/fabric/core/ledger"
    31  	ledgerUtil "github.com/hyperledger/fabric/core/ledger/util"
    32  	"github.com/hyperledger/fabric/msp"
    33  
    34  	"github.com/hyperledger/fabric/protos/common"
    35  	"github.com/hyperledger/fabric/protos/peer"
    36  	"github.com/hyperledger/fabric/protos/utils"
    37  	"github.com/op/go-logging"
    38  
    39  	"errors"
    40  
    41  	"github.com/hyperledger/fabric/common/cauthdsl"
    42  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil"
    43  )
    44  
    45  // Support provides all of the needed to evaluate the VSCC
    46  type Support interface {
    47  	// Ledger returns the ledger associated with this validator
    48  	Ledger() ledger.PeerLedger
    49  
    50  	// MSPManager returns the MSP manager for this chain
    51  	MSPManager() msp.MSPManager
    52  
    53  	// Apply attempts to apply a configtx to become the new config
    54  	Apply(configtx *common.ConfigEnvelope) error
    55  
    56  	// GetMSPIDs returns the IDs for the application MSPs
    57  	// that have been defined in the channel
    58  	GetMSPIDs(cid string) []string
    59  }
    60  
    61  //Validator interface which defines API to validate block transactions
    62  // and return the bit array mask indicating invalid transactions which
    63  // didn't pass validation.
    64  type Validator interface {
    65  	Validate(block *common.Block) error
    66  }
    67  
    68  // private interface to decouple tx validator
    69  // and vscc execution, in order to increase
    70  // testability of txValidator
    71  type vsccValidator interface {
    72  	VSCCValidateTx(payload *common.Payload, envBytes []byte, env *common.Envelope) (error, peer.TxValidationCode)
    73  }
    74  
    75  // vsccValidator implementation which used to call
    76  // vscc chaincode and validate block transactions
    77  type vsccValidatorImpl struct {
    78  	support     Support
    79  	ccprovider  ccprovider.ChaincodeProvider
    80  	sccprovider sysccprovider.SystemChaincodeProvider
    81  }
    82  
    83  // implementation of Validator interface, keeps
    84  // reference to the ledger to enable tx simulation
    85  // and execution of vscc
    86  type txValidator struct {
    87  	support Support
    88  	vscc    vsccValidator
    89  }
    90  
    91  // VSCCInfoLookupFailureError error to indicate inability
    92  // to obtain VSCC information from LCCC
    93  type VSCCInfoLookupFailureError struct {
    94  	reason string
    95  }
    96  
    97  // Error returns reasons which lead to the failure
    98  func (e VSCCInfoLookupFailureError) Error() string {
    99  	return e.reason
   100  }
   101  
   102  // VSCCEndorsementPolicyError error to mark transaction
   103  // failed endrosement policy check
   104  type VSCCEndorsementPolicyError struct {
   105  	reason string
   106  }
   107  
   108  // Error returns reasons which lead to the failure
   109  func (e VSCCEndorsementPolicyError) Error() string {
   110  	return e.reason
   111  }
   112  
   113  // VSCCExecutionFailureError error to indicate
   114  // failure during attempt of executing VSCC
   115  // endorsement policy check
   116  type VSCCExecutionFailureError struct {
   117  	reason string
   118  }
   119  
   120  // Error returns reasons which lead to the failure
   121  func (e VSCCExecutionFailureError) Error() string {
   122  	return e.reason
   123  }
   124  
   125  var logger *logging.Logger // package-level logger
   126  
   127  func init() {
   128  	// Init logger with module name
   129  	logger = flogging.MustGetLogger("txvalidator")
   130  }
   131  
   132  // NewTxValidator creates new transactions validator
   133  func NewTxValidator(support Support) Validator {
   134  	// Encapsulates interface implementation
   135  	return &txValidator{support,
   136  		&vsccValidatorImpl{
   137  			support:     support,
   138  			ccprovider:  ccprovider.GetChaincodeProvider(),
   139  			sccprovider: sysccprovider.GetSystemChaincodeProvider()}}
   140  }
   141  
   142  func (v *txValidator) chainExists(chain string) bool {
   143  	// TODO: implement this function!
   144  	return true
   145  }
   146  
   147  func (v *txValidator) Validate(block *common.Block) error {
   148  	logger.Debug("START Block Validation")
   149  	defer logger.Debug("END Block Validation")
   150  	// Initialize trans as valid here, then set invalidation reason code upon invalidation below
   151  	txsfltr := ledgerUtil.NewTxValidationFlags(len(block.Data.Data))
   152  	// txsChaincodeNames records all the invoked chaincodes by tx in a block
   153  	txsChaincodeNames := make(map[int]*sysccprovider.ChaincodeInstance)
   154  	// upgradedChaincodes records all the chaincodes that are upgrded in a block
   155  	txsUpgradedChaincodes := make(map[int]*sysccprovider.ChaincodeInstance)
   156  	for tIdx, d := range block.Data.Data {
   157  		if d != nil {
   158  			if env, err := utils.GetEnvelopeFromBlock(d); err != nil {
   159  				logger.Warningf("Error getting tx from block(%s)", err)
   160  				txsfltr.SetFlag(tIdx, peer.TxValidationCode_INVALID_OTHER_REASON)
   161  			} else if env != nil {
   162  				// validate the transaction: here we check that the transaction
   163  				// is properly formed, properly signed and that the security
   164  				// chain binding proposal to endorsements to tx holds. We do
   165  				// NOT check the validity of endorsements, though. That's a
   166  				// job for VSCC below
   167  				logger.Debug("Validating transaction peer.ValidateTransaction()")
   168  				var payload *common.Payload
   169  				var err error
   170  				var txResult peer.TxValidationCode
   171  
   172  				if payload, txResult = validation.ValidateTransaction(env); txResult != peer.TxValidationCode_VALID {
   173  					logger.Errorf("Invalid transaction with index %d", tIdx)
   174  					txsfltr.SetFlag(tIdx, txResult)
   175  					continue
   176  				}
   177  
   178  				chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
   179  				if err != nil {
   180  					logger.Warningf("Could not unmarshal channel header, err %s, skipping", err)
   181  					txsfltr.SetFlag(tIdx, peer.TxValidationCode_INVALID_OTHER_REASON)
   182  					continue
   183  				}
   184  
   185  				channel := chdr.ChannelId
   186  				logger.Debugf("Transaction is for chain %s", channel)
   187  
   188  				if !v.chainExists(channel) {
   189  					logger.Errorf("Dropping transaction for non-existent chain %s", channel)
   190  					txsfltr.SetFlag(tIdx, peer.TxValidationCode_TARGET_CHAIN_NOT_FOUND)
   191  					continue
   192  				}
   193  
   194  				if common.HeaderType(chdr.Type) == common.HeaderType_ENDORSER_TRANSACTION {
   195  					// Check duplicate transactions
   196  					txID := chdr.TxId
   197  					if _, err := v.support.Ledger().GetTransactionByID(txID); err == nil {
   198  						logger.Error("Duplicate transaction found, ", txID, ", skipping")
   199  						txsfltr.SetFlag(tIdx, peer.TxValidationCode_DUPLICATE_TXID)
   200  						continue
   201  					}
   202  
   203  					// Validate tx with vscc and policy
   204  					logger.Debug("Validating transaction vscc tx validate")
   205  					err, cde := v.vscc.VSCCValidateTx(payload, d, env)
   206  					if err != nil {
   207  						txID := txID
   208  						logger.Errorf("VSCCValidateTx for transaction txId = %s returned error %s", txID, err)
   209  						switch err.(type) {
   210  						case *VSCCExecutionFailureError:
   211  							return err
   212  						case *VSCCInfoLookupFailureError:
   213  							return err
   214  						default:
   215  							txsfltr.SetFlag(tIdx, cde)
   216  							continue
   217  						}
   218  					}
   219  
   220  					invokeCC, upgradeCC, err := v.getTxCCInstance(payload)
   221  					if err != nil {
   222  						logger.Errorf("Get chaincode instance from transaction txId = %s returned error %s", txID, err)
   223  						txsfltr.SetFlag(tIdx, peer.TxValidationCode_INVALID_OTHER_REASON)
   224  						continue
   225  					}
   226  					txsChaincodeNames[tIdx] = invokeCC
   227  					if upgradeCC != nil {
   228  						logger.Infof("Find chaincode upgrade transaction for chaincode %s on chain %s with new version %s", upgradeCC.ChaincodeName, upgradeCC.ChainID, upgradeCC.ChaincodeVersion)
   229  						txsUpgradedChaincodes[tIdx] = upgradeCC
   230  					}
   231  				} else if common.HeaderType(chdr.Type) == common.HeaderType_CONFIG {
   232  					configEnvelope, err := configtx.UnmarshalConfigEnvelope(payload.Data)
   233  					if err != nil {
   234  						err := fmt.Errorf("Error unmarshaling config which passed initial validity checks: %s", err)
   235  						logger.Critical(err)
   236  						return err
   237  					}
   238  
   239  					if err := v.support.Apply(configEnvelope); err != nil {
   240  						err := fmt.Errorf("Error validating config which passed initial validity checks: %s", err)
   241  						logger.Critical(err)
   242  						return err
   243  					}
   244  					logger.Debugf("config transaction received for chain %s", channel)
   245  				} else {
   246  					logger.Warningf("Unknown transaction type [%s] in block number [%d] transaction index [%d]",
   247  						common.HeaderType(chdr.Type), block.Header.Number, tIdx)
   248  					txsfltr.SetFlag(tIdx, peer.TxValidationCode_UNKNOWN_TX_TYPE)
   249  					continue
   250  				}
   251  
   252  				if _, err := proto.Marshal(env); err != nil {
   253  					logger.Warningf("Cannot marshal transaction due to %s", err)
   254  					txsfltr.SetFlag(tIdx, peer.TxValidationCode_MARSHAL_TX_ERROR)
   255  					continue
   256  				}
   257  				// Succeeded to pass down here, transaction is valid
   258  				txsfltr.SetFlag(tIdx, peer.TxValidationCode_VALID)
   259  			} else {
   260  				logger.Warning("Nil tx from block")
   261  				txsfltr.SetFlag(tIdx, peer.TxValidationCode_NIL_ENVELOPE)
   262  			}
   263  		}
   264  	}
   265  
   266  	txsfltr = v.invalidTXsForUpgradeCC(txsChaincodeNames, txsUpgradedChaincodes, txsfltr)
   267  
   268  	// Initialize metadata structure
   269  	utils.InitBlockMetadata(block)
   270  
   271  	block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txsfltr
   272  
   273  	return nil
   274  }
   275  
   276  // generateCCKey generates a unique identifier for chaincode in specific chain
   277  func (v *txValidator) generateCCKey(ccName, chainID string) string {
   278  	return fmt.Sprintf("%s/%s", ccName, chainID)
   279  }
   280  
   281  // invalidTXsForUpgradeCC invalid all txs that should be invalided because of chaincode upgrade txs
   282  func (v *txValidator) invalidTXsForUpgradeCC(txsChaincodeNames map[int]*sysccprovider.ChaincodeInstance, txsUpgradedChaincodes map[int]*sysccprovider.ChaincodeInstance, txsfltr ledgerUtil.TxValidationFlags) ledgerUtil.TxValidationFlags {
   283  	if len(txsUpgradedChaincodes) == 0 {
   284  		return txsfltr
   285  	}
   286  
   287  	// Invalid former cc upgrade txs if there're two or more txs upgrade the same cc
   288  	finalValidUpgradeTXs := make(map[string]int)
   289  	upgradedChaincodes := make(map[string]*sysccprovider.ChaincodeInstance)
   290  	for tIdx, cc := range txsUpgradedChaincodes {
   291  		if cc == nil {
   292  			continue
   293  		}
   294  		upgradedCCKey := v.generateCCKey(cc.ChaincodeName, cc.ChainID)
   295  
   296  		if finalIdx, exist := finalValidUpgradeTXs[upgradedCCKey]; !exist {
   297  			finalValidUpgradeTXs[upgradedCCKey] = tIdx
   298  			upgradedChaincodes[upgradedCCKey] = cc
   299  		} else if finalIdx < tIdx {
   300  			logger.Infof("Invalid transaction with index %d: chaincode was upgraded by latter tx", finalIdx)
   301  			txsfltr.SetFlag(finalIdx, peer.TxValidationCode_CHAINCODE_VERSION_CONFLICT)
   302  
   303  			// record latter cc upgrade tx info
   304  			finalValidUpgradeTXs[upgradedCCKey] = tIdx
   305  			upgradedChaincodes[upgradedCCKey] = cc
   306  		} else {
   307  			logger.Infof("Invalid transaction with index %d: chaincode was upgraded by latter tx", tIdx)
   308  			txsfltr.SetFlag(tIdx, peer.TxValidationCode_CHAINCODE_VERSION_CONFLICT)
   309  		}
   310  	}
   311  
   312  	// invalid txs which invoke the upgraded chaincodes
   313  	for tIdx, cc := range txsChaincodeNames {
   314  		if cc == nil {
   315  			continue
   316  		}
   317  		ccKey := v.generateCCKey(cc.ChaincodeName, cc.ChainID)
   318  		if _, exist := upgradedChaincodes[ccKey]; exist {
   319  			if txsfltr.IsValid(tIdx) {
   320  				logger.Infof("Invalid transaction with index %d: chaincode was upgraded in the same block", tIdx)
   321  				txsfltr.SetFlag(tIdx, peer.TxValidationCode_CHAINCODE_VERSION_CONFLICT)
   322  			}
   323  		}
   324  	}
   325  
   326  	return txsfltr
   327  }
   328  
   329  func (v *txValidator) getTxCCInstance(payload *common.Payload) (invokeCCIns, upgradeCCIns *sysccprovider.ChaincodeInstance, err error) {
   330  	// This is duplicated unpacking work, but make test easier.
   331  	chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
   332  	if err != nil {
   333  		return nil, nil, err
   334  	}
   335  
   336  	// Chain ID
   337  	chainID := chdr.ChannelId // it is guaranteed to be an existing channel by now
   338  
   339  	// ChaincodeID
   340  	hdrExt, err := utils.GetChaincodeHeaderExtension(payload.Header)
   341  	if err != nil {
   342  		return nil, nil, err
   343  	}
   344  	invokeCC := hdrExt.ChaincodeId
   345  	invokeIns := &sysccprovider.ChaincodeInstance{ChainID: chainID, ChaincodeName: invokeCC.Name, ChaincodeVersion: invokeCC.Version}
   346  
   347  	// Transaction
   348  	tx, err := utils.GetTransaction(payload.Data)
   349  	if err != nil {
   350  		logger.Errorf("GetTransaction failed: %s", err)
   351  		return invokeIns, nil, nil
   352  	}
   353  
   354  	// ChaincodeActionPayload
   355  	cap, err := utils.GetChaincodeActionPayload(tx.Actions[0].Payload)
   356  	if err != nil {
   357  		logger.Errorf("GetChaincodeActionPayload failed: %s", err)
   358  		return invokeIns, nil, nil
   359  	}
   360  
   361  	// ChaincodeProposalPayload
   362  	cpp, err := utils.GetChaincodeProposalPayload(cap.ChaincodeProposalPayload)
   363  	if err != nil {
   364  		logger.Errorf("GetChaincodeProposalPayload failed: %s", err)
   365  		return invokeIns, nil, nil
   366  	}
   367  
   368  	// ChaincodeInvocationSpec
   369  	cis := &peer.ChaincodeInvocationSpec{}
   370  	err = proto.Unmarshal(cpp.Input, cis)
   371  	if err != nil {
   372  		logger.Errorf("GetChaincodeInvokeSpec failed: %s", err)
   373  		return invokeIns, nil, nil
   374  	}
   375  
   376  	if invokeCC.Name == "lscc" {
   377  		if string(cis.ChaincodeSpec.Input.Args[0]) == "upgrade" {
   378  			upgradeIns, err := v.getUpgradeTxInstance(chainID, cis.ChaincodeSpec.Input.Args[2])
   379  			if err != nil {
   380  				return invokeIns, nil, nil
   381  			}
   382  			return invokeIns, upgradeIns, nil
   383  		}
   384  	}
   385  
   386  	return invokeIns, nil, nil
   387  }
   388  
   389  func (v *txValidator) getUpgradeTxInstance(chainID string, cdsBytes []byte) (*sysccprovider.ChaincodeInstance, error) {
   390  	cds, err := utils.GetChaincodeDeploymentSpec(cdsBytes)
   391  	if err != nil {
   392  		return nil, err
   393  	}
   394  
   395  	return &sysccprovider.ChaincodeInstance{
   396  		ChainID:          chainID,
   397  		ChaincodeName:    cds.ChaincodeSpec.ChaincodeId.Name,
   398  		ChaincodeVersion: cds.ChaincodeSpec.ChaincodeId.Version,
   399  	}, nil
   400  }
   401  
   402  // GetInfoForValidate gets the ChaincodeInstance(with latest version) of tx, vscc and policy from lscc
   403  func (v *vsccValidatorImpl) GetInfoForValidate(txid, chID, ccID string) (*sysccprovider.ChaincodeInstance, *sysccprovider.ChaincodeInstance, []byte, error) {
   404  	cc := &sysccprovider.ChaincodeInstance{ChainID: chID}
   405  	vscc := &sysccprovider.ChaincodeInstance{ChainID: chID}
   406  	var policy []byte
   407  	var err error
   408  	if ccID != "lscc" {
   409  		// when we are validating any chaincode other than
   410  		// LSCC, we need to ask LSCC to give us the name
   411  		// of VSCC and of the policy that should be used
   412  
   413  		// obtain name of the VSCC and the policy from LSCC
   414  		cd, err := v.getCDataForCC(ccID)
   415  		if err != nil {
   416  			msg := fmt.Sprintf("Unable to get chaincode data from ledger for txid %s, due to %s", txid, err)
   417  			logger.Errorf(msg)
   418  			return nil, nil, nil, err
   419  		}
   420  		cc.ChaincodeName = cd.Name
   421  		cc.ChaincodeVersion = cd.Version
   422  		vscc.ChaincodeName = cd.Vscc
   423  		policy = cd.Policy
   424  	} else {
   425  		// when we are validating LSCC, we use the default
   426  		// VSCC and a default policy that requires one signature
   427  		// from any of the members of the channel
   428  		cc.ChaincodeName = "lscc"
   429  		cc.ChaincodeVersion = coreUtil.GetSysCCVersion()
   430  		vscc.ChaincodeName = "vscc"
   431  		p := cauthdsl.SignedByAnyMember(v.support.GetMSPIDs(chID))
   432  		policy, err = utils.Marshal(p)
   433  		if err != nil {
   434  			return nil, nil, nil, err
   435  		}
   436  	}
   437  
   438  	// Get vscc version
   439  	vscc.ChaincodeVersion = coreUtil.GetSysCCVersion()
   440  
   441  	return cc, vscc, policy, nil
   442  }
   443  
   444  func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []byte, env *common.Envelope) (error, peer.TxValidationCode) {
   445  	// get header extensions so we have the chaincode ID
   446  	hdrExt, err := utils.GetChaincodeHeaderExtension(payload.Header)
   447  	if err != nil {
   448  		return err, peer.TxValidationCode_BAD_HEADER_EXTENSION
   449  	}
   450  
   451  	// get channel header
   452  	chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
   453  	if err != nil {
   454  		return err, peer.TxValidationCode_BAD_CHANNEL_HEADER
   455  	}
   456  
   457  	/* obtain the list of namespaces we're writing stuff to;
   458  	   at first, we establish a few facts about this invocation:
   459  	   1) which namespaces does it write to?
   460  	   2) does it write to LSCC's namespace?
   461  	   3) does it write to any cc that cannot be invoked? */
   462  	wrNamespace := []string{}
   463  	writesToLSCC := false
   464  	writesToNonInvokableSCC := false
   465  	respPayload, err := utils.GetActionFromEnvelope(envBytes)
   466  	if err != nil {
   467  		return fmt.Errorf("GetActionFromEnvelope failed, error %s", err), peer.TxValidationCode_BAD_RESPONSE_PAYLOAD
   468  	}
   469  	txRWSet := &rwsetutil.TxRwSet{}
   470  	if err = txRWSet.FromProtoBytes(respPayload.Results); err != nil {
   471  		return fmt.Errorf("txRWSet.FromProtoBytes failed, error %s", err), peer.TxValidationCode_BAD_RWSET
   472  	}
   473  	for _, ns := range txRWSet.NsRwSets {
   474  		if len(ns.KvRwSet.Writes) > 0 {
   475  			wrNamespace = append(wrNamespace, ns.NameSpace)
   476  
   477  			if !writesToLSCC && ns.NameSpace == "lscc" {
   478  				writesToLSCC = true
   479  			}
   480  
   481  			if !writesToNonInvokableSCC && v.sccprovider.IsSysCCAndNotInvokableCC2CC(ns.NameSpace) {
   482  				writesToNonInvokableSCC = true
   483  			}
   484  
   485  			if !writesToNonInvokableSCC && v.sccprovider.IsSysCCAndNotInvokableExternal(ns.NameSpace) {
   486  				writesToNonInvokableSCC = true
   487  			}
   488  		}
   489  	}
   490  
   491  	// Verify the header extension and response payload contain the ChaincodeId
   492  	if hdrExt.ChaincodeId == nil {
   493  		return errors.New("nil ChaincodeId in header extension"), peer.TxValidationCode_INVALID_OTHER_REASON
   494  	}
   495  
   496  	if respPayload.ChaincodeId == nil {
   497  		return errors.New("nil ChaincodeId in ChaincodeAction"), peer.TxValidationCode_INVALID_OTHER_REASON
   498  	}
   499  
   500  	// get name and version of the cc we invoked
   501  	ccID := hdrExt.ChaincodeId.Name
   502  	ccVer := respPayload.ChaincodeId.Version
   503  
   504  	// sanity check on ccID
   505  	if ccID == "" {
   506  		err := fmt.Errorf("invalid chaincode ID")
   507  		logger.Errorf("%s", err)
   508  		return err, peer.TxValidationCode_INVALID_OTHER_REASON
   509  	}
   510  	if ccID != respPayload.ChaincodeId.Name {
   511  		err := fmt.Errorf("inconsistent ccid info (%s/%s)", ccID, respPayload.ChaincodeId.Name)
   512  		logger.Errorf("%s", err)
   513  		return err, peer.TxValidationCode_INVALID_OTHER_REASON
   514  	}
   515  	// sanity check on ccver
   516  	if ccVer == "" {
   517  		err := fmt.Errorf("invalid chaincode version")
   518  		logger.Errorf("%s", err)
   519  		return err, peer.TxValidationCode_INVALID_OTHER_REASON
   520  	}
   521  
   522  	// we've gathered all the info required to proceed to validation;
   523  	// validation will behave differently depending on the type of
   524  	// chaincode (system vs. application)
   525  
   526  	if !v.sccprovider.IsSysCC(ccID) {
   527  		// if we're here, we know this is an invocation of an application chaincode;
   528  		// first of all, we make sure that:
   529  		// 1) we don't write to LSCC - an application chaincode is free to invoke LSCC
   530  		//    for instance to get information about itself or another chaincode; however
   531  		//    these legitimate invocations only ready from LSCC's namespace; currently
   532  		//    only two functions of LSCC write to its namespace: deploy and upgrade and
   533  		//    neither should be used by an application chaincode
   534  		if writesToLSCC {
   535  			return fmt.Errorf("Chaincode %s attempted to write to the namespace of LSCC", ccID),
   536  				peer.TxValidationCode_ILLEGAL_WRITESET
   537  		}
   538  		// 2) we don't write to the namespace of a chaincode that we cannot invoke - if
   539  		//    the chaincode cannot be invoked in the first place, there's no legitimate
   540  		//    way in which a transaction has a write set that writes to it; additionally
   541  		//    we don't have any means of verifying whether the transaction had the rights
   542  		//    to perform that write operation because in v1, system chaincodes do not have
   543  		//    any endorsement policies to speak of. So if the chaincode can't be invoked
   544  		//    it can't be written to by an invocation of an application chaincode
   545  		if writesToNonInvokableSCC {
   546  			return fmt.Errorf("Chaincode %s attempted to write to the namespace of a system chaincode that cannot be invoked", ccID),
   547  				peer.TxValidationCode_ILLEGAL_WRITESET
   548  		}
   549  
   550  		// validate *EACH* read write set according to its chaincode's endorsement policy
   551  		for _, ns := range wrNamespace {
   552  			// Get latest chaincode version, vscc and validate policy
   553  			txcc, vscc, policy, err := v.GetInfoForValidate(chdr.TxId, chdr.ChannelId, ns)
   554  			if err != nil {
   555  				logger.Errorf("GetInfoForValidate for txId = %s returned error %s", chdr.TxId, err)
   556  				return err, peer.TxValidationCode_INVALID_OTHER_REASON
   557  			}
   558  
   559  			// if the namespace corresponds to the cc that was originally
   560  			// invoked, we check that the version of the cc that was
   561  			// invoked corresponds to the version that lscc has returned
   562  			if ns == ccID && txcc.ChaincodeVersion != ccVer {
   563  				err := fmt.Errorf("Chaincode %s:%s/%s didn't match %s:%s/%s in lscc", ccID, ccVer, chdr.ChannelId, txcc.ChaincodeName, txcc.ChaincodeVersion, chdr.ChannelId)
   564  				logger.Errorf(err.Error())
   565  				return err, peer.TxValidationCode_EXPIRED_CHAINCODE
   566  			}
   567  
   568  			// do VSCC validation
   569  			if err = v.VSCCValidateTxForCC(envBytes, chdr.TxId, chdr.ChannelId, vscc.ChaincodeName, vscc.ChaincodeVersion, policy); err != nil {
   570  				switch err.(type) {
   571  				case *VSCCEndorsementPolicyError:
   572  					return err, peer.TxValidationCode_ENDORSEMENT_POLICY_FAILURE
   573  				default:
   574  					return err, peer.TxValidationCode_INVALID_OTHER_REASON
   575  				}
   576  			}
   577  		}
   578  	} else {
   579  		// make sure that we can invoke this system chaincode - if the chaincode
   580  		// cannot be invoked through a proposal to this peer, we have to drop the
   581  		// transaction; if we didn't, we wouldn't know how to decide whether it's
   582  		// valid or not because in v1, system chaincodes have no endorsement policy
   583  		if v.sccprovider.IsSysCCAndNotInvokableExternal(ccID) {
   584  			return fmt.Errorf("Committing an invocation of cc %s is illegal", ccID),
   585  				peer.TxValidationCode_ILLEGAL_WRITESET
   586  		}
   587  
   588  		// Get latest chaincode version, vscc and validate policy
   589  		_, vscc, policy, err := v.GetInfoForValidate(chdr.TxId, chdr.ChannelId, ccID)
   590  		if err != nil {
   591  			logger.Errorf("GetInfoForValidate for txId = %s returned error %s", chdr.TxId, err)
   592  			return err, peer.TxValidationCode_INVALID_OTHER_REASON
   593  		}
   594  
   595  		// validate the transaction as an invocation of this system chaincode;
   596  		// vscc will have to do custom validation for this system chaincode
   597  		// currently, VSCC does custom validation for LSCC only; if an hlf
   598  		// user creates a new system chaincode which is invokable from the outside
   599  		// they have to modify VSCC to provide appropriate validation
   600  		if err = v.VSCCValidateTxForCC(envBytes, chdr.TxId, vscc.ChainID, vscc.ChaincodeName, vscc.ChaincodeVersion, policy); err != nil {
   601  			switch err.(type) {
   602  			case *VSCCEndorsementPolicyError:
   603  				return err, peer.TxValidationCode_ENDORSEMENT_POLICY_FAILURE
   604  			default:
   605  				return err, peer.TxValidationCode_INVALID_OTHER_REASON
   606  			}
   607  		}
   608  	}
   609  
   610  	return nil, peer.TxValidationCode_VALID
   611  }
   612  
   613  func (v *vsccValidatorImpl) VSCCValidateTxForCC(envBytes []byte, txid, chid, vsccName, vsccVer string, policy []byte) error {
   614  	ctxt, err := v.ccprovider.GetContext(v.support.Ledger())
   615  	if err != nil {
   616  		msg := fmt.Sprintf("Cannot obtain context for txid=%s, err %s", txid, err)
   617  		logger.Errorf(msg)
   618  		return &VSCCExecutionFailureError{msg}
   619  	}
   620  	defer v.ccprovider.ReleaseContext()
   621  
   622  	// build arguments for VSCC invocation
   623  	// args[0] - function name (not used now)
   624  	// args[1] - serialized Envelope
   625  	// args[2] - serialized policy
   626  	args := [][]byte{[]byte(""), envBytes, policy}
   627  
   628  	// get context to invoke VSCC
   629  	vscctxid := coreUtil.GenerateUUID()
   630  	cccid := v.ccprovider.GetCCContext(chid, vsccName, vsccVer, vscctxid, true, nil, nil)
   631  
   632  	// invoke VSCC
   633  	logger.Debug("Invoking VSCC txid", txid, "chaindID", chid)
   634  	res, _, err := v.ccprovider.ExecuteChaincode(ctxt, cccid, args)
   635  	if err != nil {
   636  		msg := fmt.Sprintf("Invoke VSCC failed for transaction txid=%s, error %s", txid, err)
   637  		logger.Errorf(msg)
   638  		return &VSCCExecutionFailureError{msg}
   639  	}
   640  	if res.Status != shim.OK {
   641  		logger.Errorf("VSCC check failed for transaction txid=%s, error %s", txid, res.Message)
   642  		return &VSCCEndorsementPolicyError{fmt.Sprintf("%s", res.Message)}
   643  	}
   644  
   645  	return nil
   646  }
   647  
   648  func (v *vsccValidatorImpl) getCDataForCC(ccid string) (*ccprovider.ChaincodeData, error) {
   649  	l := v.support.Ledger()
   650  	if l == nil {
   651  		return nil, fmt.Errorf("nil ledger instance")
   652  	}
   653  
   654  	qe, err := l.NewQueryExecutor()
   655  	if err != nil {
   656  		return nil, fmt.Errorf("Could not retrieve QueryExecutor, error %s", err)
   657  	}
   658  	defer qe.Done()
   659  
   660  	bytes, err := qe.GetState("lscc", ccid)
   661  	if err != nil {
   662  		return nil, &VSCCInfoLookupFailureError{fmt.Sprintf("Could not retrieve state for chaincode %s, error %s", ccid, err)}
   663  	}
   664  
   665  	if bytes == nil {
   666  		return nil, fmt.Errorf("lscc's state for [%s] not found.", ccid)
   667  	}
   668  
   669  	cd := &ccprovider.ChaincodeData{}
   670  	err = proto.Unmarshal(bytes, cd)
   671  	if err != nil {
   672  		return nil, fmt.Errorf("Unmarshalling ChaincodeQueryResponse failed, error %s", err)
   673  	}
   674  
   675  	if cd.Vscc == "" {
   676  		return nil, fmt.Errorf("lscc's state for [%s] is invalid, vscc field must be set.", ccid)
   677  	}
   678  
   679  	if len(cd.Policy) == 0 {
   680  		return nil, fmt.Errorf("lscc's state for [%s] is invalid, policy field must be set.", ccid)
   681  	}
   682  
   683  	return cd, err
   684  }