github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/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/validation"
    29  	"github.com/hyperledger/fabric/core/ledger"
    30  	ledgerUtil "github.com/hyperledger/fabric/core/ledger/util"
    31  	"github.com/hyperledger/fabric/msp"
    32  
    33  	"github.com/hyperledger/fabric/common/policies"
    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  	"github.com/hyperledger/fabric/common/cauthdsl"
    40  )
    41  
    42  // Support provides all of the needed to evaluate the VSCC
    43  type Support interface {
    44  	// Ledger returns the ledger associated with this validator
    45  	Ledger() ledger.PeerLedger
    46  
    47  	// MSPManager returns the MSP manager for this chain
    48  	MSPManager() msp.MSPManager
    49  
    50  	// Apply attempts to apply a configtx to become the new config
    51  	Apply(configtx *common.ConfigEnvelope) error
    52  
    53  	// PolicyManager returns the policies.Manager for the channel
    54  	PolicyManager() policies.Manager
    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
    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  }
    81  
    82  // implementation of Validator interface, keeps
    83  // reference to the ledger to enable tx simulation
    84  // and execution of vscc
    85  type txValidator struct {
    86  	support Support
    87  	vscc    vsccValidator
    88  }
    89  
    90  var logger *logging.Logger // package-level logger
    91  
    92  func init() {
    93  	// Init logger with module name
    94  	logger = flogging.MustGetLogger("txvalidator")
    95  }
    96  
    97  // NewTxValidator creates new transactions validator
    98  func NewTxValidator(support Support) Validator {
    99  	// Encapsulates interface implementation
   100  	return &txValidator{support, &vsccValidatorImpl{support: support, ccprovider: ccprovider.GetChaincodeProvider()}}
   101  }
   102  
   103  func (v *txValidator) chainExists(chain string) bool {
   104  	// TODO: implement this function!
   105  	return true
   106  }
   107  
   108  // ChaincodeInstance is unique identifier of chaincode instance
   109  type ChaincodeInstance struct {
   110  	ChainID          string
   111  	ChaincodeName    string
   112  	ChaincodeVersion string
   113  }
   114  
   115  func (v *txValidator) Validate(block *common.Block) error {
   116  	logger.Debug("START Block Validation")
   117  	defer logger.Debug("END Block Validation")
   118  	// Initialize trans as valid here, then set invalidation reason code upon invalidation below
   119  	txsfltr := ledgerUtil.NewTxValidationFlags(len(block.Data.Data))
   120  	// txsChaincodeNames records all the invoked chaincodes by tx in a block
   121  	txsChaincodeNames := make(map[int]*ChaincodeInstance)
   122  	// upgradedChaincodes records all the chaincodes that are upgrded in a block
   123  	txsUpgradedChaincodes := make(map[int]*ChaincodeInstance)
   124  	for tIdx, d := range block.Data.Data {
   125  		if d != nil {
   126  			if env, err := utils.GetEnvelopeFromBlock(d); err != nil {
   127  				logger.Warningf("Error getting tx from block(%s)", err)
   128  				txsfltr.SetFlag(tIdx, peer.TxValidationCode_INVALID_OTHER_REASON)
   129  			} else if env != nil {
   130  				// validate the transaction: here we check that the transaction
   131  				// is properly formed, properly signed and that the security
   132  				// chain binding proposal to endorsements to tx holds. We do
   133  				// NOT check the validity of endorsements, though. That's a
   134  				// job for VSCC below
   135  				logger.Debug("Validating transaction peer.ValidateTransaction()")
   136  				var payload *common.Payload
   137  				var err error
   138  				var txResult peer.TxValidationCode
   139  
   140  				if payload, txResult = validation.ValidateTransaction(env); txResult != peer.TxValidationCode_VALID {
   141  					logger.Errorf("Invalid transaction with index %d, error %s", tIdx, err)
   142  					txsfltr.SetFlag(tIdx, txResult)
   143  					continue
   144  				}
   145  
   146  				chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
   147  				if err != nil {
   148  					logger.Warning("Could not unmarshal channel header, err %s, skipping", err)
   149  					txsfltr.SetFlag(tIdx, peer.TxValidationCode_INVALID_OTHER_REASON)
   150  					continue
   151  				}
   152  
   153  				channel := chdr.ChannelId
   154  				logger.Debug("Transaction is for chain %s", channel)
   155  
   156  				if !v.chainExists(channel) {
   157  					logger.Errorf("Dropping transaction for non-existent chain %s", channel)
   158  					txsfltr.SetFlag(tIdx, peer.TxValidationCode_TARGET_CHAIN_NOT_FOUND)
   159  					continue
   160  				}
   161  
   162  				if common.HeaderType(chdr.Type) == common.HeaderType_ENDORSER_TRANSACTION {
   163  					// Check duplicate transactions
   164  					txID := chdr.TxId
   165  					if _, err := v.support.Ledger().GetTransactionByID(txID); err == nil {
   166  						logger.Error("Duplicate transaction found, ", txID, ", skipping")
   167  						txsfltr.SetFlag(tIdx, peer.TxValidationCode_DUPLICATE_TXID)
   168  						continue
   169  					}
   170  
   171  					//the payload is used to get headers
   172  					logger.Debug("Validating transaction vscc tx validate")
   173  					if err = v.vscc.VSCCValidateTx(payload, d, env); err != nil {
   174  						txID := txID
   175  						logger.Errorf("VSCCValidateTx for transaction txId = %s returned error %s", txID, err)
   176  						txsfltr.SetFlag(tIdx, peer.TxValidationCode_ENDORSEMENT_POLICY_FAILURE)
   177  						continue
   178  					}
   179  
   180  					invokeCC, upgradeCC, err := v.getTxCCInstance(payload)
   181  					if err != nil {
   182  						logger.Errorf("VSCCValidateTx for transaction txId = %s returned error %s", txID, err)
   183  						txsfltr.SetFlag(tIdx, peer.TxValidationCode_ENDORSEMENT_POLICY_FAILURE)
   184  						continue
   185  					}
   186  					txsChaincodeNames[tIdx] = invokeCC
   187  					if upgradeCC != nil {
   188  						logger.Infof("Find chaincode upgrade transaction for chaincode %s on chain %s with new version %s", upgradeCC.ChaincodeName, upgradeCC.ChainID, upgradeCC.ChaincodeVersion)
   189  						txsUpgradedChaincodes[tIdx] = upgradeCC
   190  					}
   191  				} else if common.HeaderType(chdr.Type) == common.HeaderType_CONFIG {
   192  					configEnvelope, err := configtx.UnmarshalConfigEnvelope(payload.Data)
   193  					if err != nil {
   194  						err := fmt.Errorf("Error unmarshaling config which passed initial validity checks: %s", err)
   195  						logger.Critical(err)
   196  						return err
   197  					}
   198  
   199  					if err := v.support.Apply(configEnvelope); err != nil {
   200  						err := fmt.Errorf("Error validating config which passed initial validity checks: %s", err)
   201  						logger.Critical(err)
   202  						return err
   203  					}
   204  					logger.Debugf("config transaction received for chain %s", channel)
   205  				}
   206  
   207  				if _, err := proto.Marshal(env); err != nil {
   208  					logger.Warningf("Cannot marshal transaction due to %s", err)
   209  					txsfltr.SetFlag(tIdx, peer.TxValidationCode_MARSHAL_TX_ERROR)
   210  					continue
   211  				}
   212  				// Succeeded to pass down here, transaction is valid
   213  				txsfltr.SetFlag(tIdx, peer.TxValidationCode_VALID)
   214  			} else {
   215  				logger.Warning("Nil tx from block")
   216  				txsfltr.SetFlag(tIdx, peer.TxValidationCode_NIL_ENVELOPE)
   217  			}
   218  		}
   219  	}
   220  
   221  	txsfltr = v.invalidTXsForUpgradeCC(txsChaincodeNames, txsUpgradedChaincodes, txsfltr)
   222  
   223  	// Initialize metadata structure
   224  	utils.InitBlockMetadata(block)
   225  
   226  	block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txsfltr
   227  
   228  	return nil
   229  }
   230  
   231  // generateCCKey generates a unique identifier for chaincode in specific chain
   232  func (v *txValidator) generateCCKey(ccName, chainID string) string {
   233  	return fmt.Sprintf("%s/%s", ccName, chainID)
   234  }
   235  
   236  // invalidTXsForUpgradeCC invalid all txs that should be invalided because of chaincode upgrade txs
   237  func (v *txValidator) invalidTXsForUpgradeCC(txsChaincodeNames map[int]*ChaincodeInstance, txsUpgradedChaincodes map[int]*ChaincodeInstance, txsfltr ledgerUtil.TxValidationFlags) ledgerUtil.TxValidationFlags {
   238  	if len(txsUpgradedChaincodes) == 0 {
   239  		return txsfltr
   240  	}
   241  
   242  	// Invalid former cc upgrade txs if there're two or more txs upgrade the same cc
   243  	finalValidUpgradeTXs := make(map[string]int)
   244  	upgradedChaincodes := make(map[string]*ChaincodeInstance)
   245  	for tIdx, cc := range txsUpgradedChaincodes {
   246  		if cc == nil {
   247  			continue
   248  		}
   249  		upgradedCCKey := v.generateCCKey(cc.ChaincodeName, cc.ChainID)
   250  
   251  		if finalIdx, exist := finalValidUpgradeTXs[upgradedCCKey]; !exist {
   252  			finalValidUpgradeTXs[upgradedCCKey] = tIdx
   253  			upgradedChaincodes[upgradedCCKey] = cc
   254  		} else if finalIdx < tIdx {
   255  			logger.Infof("Invalid transaction with index %d: chaincode was upgraded by latter tx", finalIdx)
   256  			txsfltr.SetFlag(finalIdx, peer.TxValidationCode_EXPIRED_CHAINCODE)
   257  
   258  			// record latter cc upgrade tx info
   259  			finalValidUpgradeTXs[upgradedCCKey] = tIdx
   260  			upgradedChaincodes[upgradedCCKey] = cc
   261  		} else {
   262  			logger.Infof("Invalid transaction with index %d: chaincode was upgraded by latter tx", tIdx)
   263  			txsfltr.SetFlag(tIdx, peer.TxValidationCode_EXPIRED_CHAINCODE)
   264  		}
   265  	}
   266  
   267  	// invalid txs which invoke the upgraded chaincodes
   268  	for tIdx, cc := range txsChaincodeNames {
   269  		if cc == nil {
   270  			continue
   271  		}
   272  		ccKey := v.generateCCKey(cc.ChaincodeName, cc.ChainID)
   273  		if _, exist := upgradedChaincodes[ccKey]; exist {
   274  			if txsfltr.IsValid(tIdx) {
   275  				logger.Infof("Invalid transaction with index %d: chaincode was upgraded in the same block", tIdx)
   276  				txsfltr.SetFlag(tIdx, peer.TxValidationCode_EXPIRED_CHAINCODE)
   277  			}
   278  		}
   279  	}
   280  
   281  	return txsfltr
   282  }
   283  
   284  func (v *txValidator) getTxCCInstance(payload *common.Payload) (invokeCCIns, upgradeCCIns *ChaincodeInstance, err error) {
   285  	// This is duplicated unpacking work, but make test easier.
   286  	chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
   287  	if err != nil {
   288  		return nil, nil, err
   289  	}
   290  
   291  	// Chain ID
   292  	chainID := chdr.ChannelId
   293  	if chainID == "" {
   294  		err := fmt.Errorf("transaction header does not contain an chain ID")
   295  		logger.Errorf("%s", err)
   296  		return nil, nil, err
   297  	}
   298  
   299  	// ChaincodeID
   300  	hdrExt, err := utils.GetChaincodeHeaderExtension(payload.Header)
   301  	if err != nil {
   302  		return nil, nil, err
   303  	}
   304  	invokeCC := hdrExt.ChaincodeId
   305  	invokeIns := &ChaincodeInstance{ChainID: chainID, ChaincodeName: invokeCC.Name, ChaincodeVersion: invokeCC.Version}
   306  
   307  	// Transaction
   308  	tx, err := utils.GetTransaction(payload.Data)
   309  	if err != nil {
   310  		logger.Errorf("GetTransaction failed: %s", err)
   311  		return invokeIns, nil, nil
   312  	}
   313  
   314  	// ChaincodeActionPayload
   315  	cap, err := utils.GetChaincodeActionPayload(tx.Actions[0].Payload)
   316  	if err != nil {
   317  		logger.Errorf("GetChaincodeActionPayload failed: %s", err)
   318  		return invokeIns, nil, nil
   319  	}
   320  
   321  	// ChaincodeProposalPayload
   322  	cpp, err := utils.GetChaincodeProposalPayload(cap.ChaincodeProposalPayload)
   323  	if err != nil {
   324  		logger.Errorf("GetChaincodeProposalPayload failed: %s", err)
   325  		return invokeIns, nil, nil
   326  	}
   327  
   328  	// ChaincodeInvocationSpec
   329  	cis := &peer.ChaincodeInvocationSpec{}
   330  	err = proto.Unmarshal(cpp.Input, cis)
   331  	if err != nil {
   332  		logger.Errorf("GetChaincodeInvokeSpec failed: %s", err)
   333  		return invokeIns, nil, nil
   334  	}
   335  
   336  	if invokeCC.Name == "lscc" {
   337  		if string(cis.ChaincodeSpec.Input.Args[0]) == "upgrade" {
   338  			upgradeIns, err := v.getUpgradeTxInstance(chainID, cis.ChaincodeSpec.Input.Args[2])
   339  			if err != nil {
   340  				return invokeIns, nil, nil
   341  			}
   342  			return invokeIns, upgradeIns, nil
   343  		}
   344  	}
   345  
   346  	return invokeIns, nil, nil
   347  }
   348  
   349  func (v *txValidator) getUpgradeTxInstance(chainID string, cdsBytes []byte) (*ChaincodeInstance, error) {
   350  	cds, err := utils.GetChaincodeDeploymentSpec(cdsBytes)
   351  	if err != nil {
   352  		return nil, err
   353  	}
   354  
   355  	return &ChaincodeInstance{
   356  		ChainID:          chainID,
   357  		ChaincodeName:    cds.ChaincodeSpec.ChaincodeId.Name,
   358  		ChaincodeVersion: cds.ChaincodeSpec.ChaincodeId.Version,
   359  	}, nil
   360  }
   361  
   362  func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []byte, env *common.Envelope) error {
   363  	// get channel header
   364  	chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
   365  	if err != nil {
   366  		return err
   367  	}
   368  
   369  	// Chain ID
   370  	chainID := chdr.ChannelId
   371  	if chainID == "" {
   372  		err := fmt.Errorf("transaction header does not contain an chain ID")
   373  		logger.Errorf("%s", err)
   374  		return err
   375  	}
   376  
   377  	// Get transaction id
   378  	txid := chdr.TxId
   379  	if txid == "" {
   380  		err := fmt.Errorf("transaction header does not contain transaction ID")
   381  		logger.Errorf("%s", err)
   382  		return err
   383  	}
   384  
   385  	ctxt, err := v.ccprovider.GetContext(v.support.Ledger())
   386  	if err != nil {
   387  		logger.Errorf("Cannot obtain context for txid=%s, err %s", txid, err)
   388  		return err
   389  	}
   390  	defer v.ccprovider.ReleaseContext()
   391  
   392  	// get header extensions so we have the visibility field
   393  	hdrExt, err := utils.GetChaincodeHeaderExtension(payload.Header)
   394  	if err != nil {
   395  		return err
   396  	}
   397  
   398  	var vscc string
   399  	var policy []byte
   400  	if hdrExt.ChaincodeId.Name != "lscc" {
   401  		// when we are validating any chaincode other than
   402  		// LSCC, we need to ask LSCC to give us the name
   403  		// of VSCC and of the policy that should be used
   404  
   405  		// obtain name of the VSCC and the policy from LSCC
   406  		cd, err := v.getCDataForCC(hdrExt.ChaincodeId.Name)
   407  		if err != nil {
   408  			logger.Errorf("Unable to get chaincode data from ledger for txid %s, due to %s", txid, err)
   409  			return err
   410  		}
   411  		vscc = cd.Vscc
   412  		policy = cd.Policy
   413  	} else {
   414  		// when we are validating LSCC, we use the default
   415  		// VSCC and a default policy that requires one signature
   416  		// from any of the members of the channel
   417  		vscc = "vscc"
   418  		policy = cauthdsl.SignedByAnyMember(v.support.GetMSPIDs(chainID))
   419  	}
   420  
   421  	// build arguments for VSCC invocation
   422  	// args[0] - function name (not used now)
   423  	// args[1] - serialized Envelope
   424  	// args[2] - serialized policy
   425  	args := [][]byte{[]byte(""), envBytes, policy}
   426  
   427  	vscctxid := coreUtil.GenerateUUID()
   428  
   429  	// Get chaincode version
   430  	version := coreUtil.GetSysCCVersion()
   431  	cccid := v.ccprovider.GetCCContext(chainID, vscc, version, vscctxid, true, nil, nil)
   432  
   433  	// invoke VSCC
   434  	logger.Debug("Invoking VSCC txid", txid, "chaindID", chainID)
   435  	res, _, err := v.ccprovider.ExecuteChaincode(ctxt, cccid, args)
   436  	if err != nil {
   437  		logger.Errorf("Invoke VSCC failed for transaction txid=%s, error %s", txid, err)
   438  		return err
   439  	}
   440  	if res.Status != shim.OK {
   441  		logger.Errorf("VSCC check failed for transaction txid=%s, error %s", txid, res.Message)
   442  		return fmt.Errorf("%s", res.Message)
   443  	}
   444  
   445  	return nil
   446  }
   447  
   448  func (v *vsccValidatorImpl) getCDataForCC(ccid string) (*ccprovider.ChaincodeData, error) {
   449  	l := v.support.Ledger()
   450  	if l == nil {
   451  		return nil, fmt.Errorf("nil ledger instance")
   452  	}
   453  
   454  	qe, err := l.NewQueryExecutor()
   455  	if err != nil {
   456  		return nil, fmt.Errorf("Could not retrieve QueryExecutor, error %s", err)
   457  	}
   458  	defer qe.Done()
   459  
   460  	bytes, err := qe.GetState("lscc", ccid)
   461  	if err != nil {
   462  		return nil, fmt.Errorf("Could not retrieve state for chaincode %s, error %s", ccid, err)
   463  	}
   464  
   465  	if bytes == nil {
   466  		return nil, fmt.Errorf("lscc's state for [%s] not found.", ccid)
   467  	}
   468  
   469  	cd := &ccprovider.ChaincodeData{}
   470  	err = proto.Unmarshal(bytes, cd)
   471  	if err != nil {
   472  		return nil, fmt.Errorf("Unmarshalling ChaincodeQueryResponse failed, error %s", err)
   473  	}
   474  
   475  	if cd.Vscc == "" {
   476  		return nil, fmt.Errorf("lscc's state for [%s] is invalid, vscc field must be set.", ccid)
   477  	}
   478  
   479  	if len(cd.Policy) == 0 {
   480  		return nil, fmt.Errorf("lscc's state for [%s] is invalid, policy field must be set.", ccid)
   481  	}
   482  
   483  	return cd, err
   484  }