github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/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  	coreUtil "github.com/hyperledger/fabric/common/util"
    25  	"github.com/hyperledger/fabric/core/chaincode/shim"
    26  	"github.com/hyperledger/fabric/core/common/ccprovider"
    27  	"github.com/hyperledger/fabric/core/common/validation"
    28  	"github.com/hyperledger/fabric/core/ledger"
    29  	ledgerUtil "github.com/hyperledger/fabric/core/ledger/util"
    30  	"github.com/hyperledger/fabric/msp"
    31  
    32  	"github.com/hyperledger/fabric/protos/common"
    33  	"github.com/hyperledger/fabric/protos/peer"
    34  	"github.com/hyperledger/fabric/protos/utils"
    35  	"github.com/op/go-logging"
    36  )
    37  
    38  // Support provides all of the needed to evaluate the VSCC
    39  type Support interface {
    40  	// Ledger returns the ledger associated with this validator
    41  	Ledger() ledger.PeerLedger
    42  
    43  	// MSPManager returns the MSP manager for this chain
    44  	MSPManager() msp.MSPManager
    45  
    46  	// Apply attempts to apply a configtx to become the new config
    47  	Apply(configtx *common.ConfigEnvelope) error
    48  }
    49  
    50  //Validator interface which defines API to validate block transactions
    51  // and return the bit array mask indicating invalid transactions which
    52  // didn't pass validation.
    53  type Validator interface {
    54  	Validate(block *common.Block) error
    55  }
    56  
    57  // private interface to decouple tx validator
    58  // and vscc execution, in order to increase
    59  // testability of txValidator
    60  type vsccValidator interface {
    61  	VSCCValidateTx(payload *common.Payload, envBytes []byte) error
    62  }
    63  
    64  // vsccValidator implementation which used to call
    65  // vscc chaincode and validate block transactions
    66  type vsccValidatorImpl struct {
    67  	support    Support
    68  	ccprovider ccprovider.ChaincodeProvider
    69  }
    70  
    71  // implementation of Validator interface, keeps
    72  // reference to the ledger to enable tx simulation
    73  // and execution of vscc
    74  type txValidator struct {
    75  	support Support
    76  	vscc    vsccValidator
    77  }
    78  
    79  var logger *logging.Logger // package-level logger
    80  
    81  func init() {
    82  	// Init logger with module name
    83  	logger = logging.MustGetLogger("txvalidator")
    84  }
    85  
    86  // NewTxValidator creates new transactions validator
    87  func NewTxValidator(support Support) Validator {
    88  	// Encapsulates interface implementation
    89  	return &txValidator{support, &vsccValidatorImpl{support: support, ccprovider: ccprovider.GetChaincodeProvider()}}
    90  }
    91  
    92  func (v *txValidator) chainExists(chain string) bool {
    93  	// TODO: implement this function!
    94  	return true
    95  }
    96  
    97  func (v *txValidator) Validate(block *common.Block) error {
    98  	logger.Debug("START Block Validation")
    99  	defer logger.Debug("END Block Validation")
   100  	// Initialize trans as valid here, then set invalidation reason code upon invalidation below
   101  	txsfltr := ledgerUtil.NewTxValidationFlags(len(block.Data.Data))
   102  	for tIdx, d := range block.Data.Data {
   103  		if d != nil {
   104  			if env, err := utils.GetEnvelopeFromBlock(d); err != nil {
   105  				logger.Warningf("Error getting tx from block(%s)", err)
   106  				txsfltr.SetFlag(tIdx, peer.TxValidationCode_INVALID_OTHER_REASON)
   107  			} else if env != nil {
   108  				// validate the transaction: here we check that the transaction
   109  				// is properly formed, properly signed and that the security
   110  				// chain binding proposal to endorsements to tx holds. We do
   111  				// NOT check the validity of endorsements, though. That's a
   112  				// job for VSCC below
   113  				logger.Debug("Validating transaction peer.ValidateTransaction()")
   114  				var payload *common.Payload
   115  				var err error
   116  				var txResult peer.TxValidationCode
   117  
   118  				if payload, txResult = validation.ValidateTransaction(env); txResult != peer.TxValidationCode_VALID {
   119  					logger.Errorf("Invalid transaction with index %d, error %s", tIdx, err)
   120  					txsfltr.SetFlag(tIdx, txResult)
   121  					continue
   122  				}
   123  
   124  				chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
   125  				if err != nil {
   126  					logger.Warning("Could not unmarshal channel header, err %s, skipping", err)
   127  					txsfltr.SetFlag(tIdx, peer.TxValidationCode_INVALID_OTHER_REASON)
   128  					continue
   129  				}
   130  
   131  				channel := chdr.ChannelId
   132  				logger.Debug("Transaction is for chain %s", channel)
   133  
   134  				if !v.chainExists(channel) {
   135  					logger.Errorf("Dropping transaction for non-existent chain %s", channel)
   136  					txsfltr.SetFlag(tIdx, peer.TxValidationCode_TARGET_CHAIN_NOT_FOUND)
   137  					continue
   138  				}
   139  
   140  				if common.HeaderType(chdr.Type) == common.HeaderType_ENDORSER_TRANSACTION {
   141  					// Check duplicate transactions
   142  					txID := chdr.TxId
   143  					if _, err := v.support.Ledger().GetTransactionByID(txID); err == nil {
   144  						logger.Error("Duplicate transaction found, ", txID, ", skipping")
   145  						txsfltr.SetFlag(tIdx, peer.TxValidationCode_DUPLICATE_TXID)
   146  						continue
   147  					}
   148  
   149  					//the payload is used to get headers
   150  					logger.Debug("Validating transaction vscc tx validate")
   151  					if err = v.vscc.VSCCValidateTx(payload, d); err != nil {
   152  						txID := txID
   153  						logger.Errorf("VSCCValidateTx for transaction txId = %s returned error %s", txID, err)
   154  						txsfltr.SetFlag(tIdx, peer.TxValidationCode_ENDORSEMENT_POLICY_FAILURE)
   155  						continue
   156  					}
   157  				} else if common.HeaderType(chdr.Type) == common.HeaderType_CONFIG {
   158  					configEnvelope, err := configtx.UnmarshalConfigEnvelope(payload.Data)
   159  					if err != nil {
   160  						err := fmt.Errorf("Error unmarshaling config which passed initial validity checks: %s", err)
   161  						logger.Critical(err)
   162  						return err
   163  					}
   164  
   165  					if err := v.support.Apply(configEnvelope); err != nil {
   166  						err := fmt.Errorf("Error validating config which passed initial validity checks: %s", err)
   167  						logger.Critical(err)
   168  						return err
   169  					}
   170  					logger.Debugf("config transaction received for chain %s", channel)
   171  				}
   172  
   173  				if _, err := proto.Marshal(env); err != nil {
   174  					logger.Warningf("Cannot marshal transaction due to %s", err)
   175  					txsfltr.SetFlag(tIdx, peer.TxValidationCode_MARSHAL_TX_ERROR)
   176  					continue
   177  				}
   178  				// Succeeded to pass down here, transaction is valid
   179  				txsfltr.SetFlag(tIdx, peer.TxValidationCode_VALID)
   180  			} else {
   181  				logger.Warning("Nil tx from block")
   182  				txsfltr.SetFlag(tIdx, peer.TxValidationCode_NIL_ENVELOPE)
   183  			}
   184  		}
   185  	}
   186  	// Initialize metadata structure
   187  	utils.InitBlockMetadata(block)
   188  
   189  	block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txsfltr
   190  
   191  	return nil
   192  }
   193  
   194  func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []byte) error {
   195  	chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
   196  	if err != nil {
   197  		return err
   198  	}
   199  
   200  	// Chain ID
   201  	chainID := chdr.ChannelId
   202  	if chainID == "" {
   203  		err := fmt.Errorf("transaction header does not contain an chain ID")
   204  		logger.Errorf("%s", err)
   205  		return err
   206  	}
   207  
   208  	// Get transaction id
   209  	txid := chdr.TxId
   210  	if txid == "" {
   211  		err := fmt.Errorf("transaction header does not contain transaction ID")
   212  		logger.Errorf("%s", err)
   213  		return err
   214  	}
   215  
   216  	ctxt, err := v.ccprovider.GetContext(v.support.Ledger())
   217  	if err != nil {
   218  		logger.Errorf("Cannot obtain context for txid=%s, err %s", txid, err)
   219  		return err
   220  	}
   221  	defer v.ccprovider.ReleaseContext()
   222  
   223  	// get header extensions so we have the visibility field
   224  	hdrExt, err := utils.GetChaincodeHeaderExtension(payload.Header)
   225  	if err != nil {
   226  		return err
   227  	}
   228  
   229  	// LCCC should not undergo standard VSCC type of
   230  	// validation. It should instead go through system
   231  	// policy validation to determine whether the issuer
   232  	// is entitled to deploy a chaincode on our chain
   233  	// VSCCValidateTx should
   234  	if hdrExt.ChaincodeId.Name == "lccc" {
   235  		// TODO: until FAB-1934 is in, we need to stop here
   236  		logger.Debugf("Invocation of LCCC detected, no further VSCC validation necessary")
   237  		return nil
   238  	}
   239  
   240  	// obtain name of the VSCC and the policy from LCCC
   241  	vscc, policy, err := v.ccprovider.GetCCValidationInfoFromLCCC(ctxt, txid, nil, nil, chainID, hdrExt.ChaincodeId.Name)
   242  	if err != nil {
   243  		logger.Errorf("Unable to get chaincode data from LCCC for txid %s, due to %s", txid, err)
   244  		return err
   245  	}
   246  
   247  	// build arguments for VSCC invocation
   248  	// args[0] - function name (not used now)
   249  	// args[1] - serialized Envelope
   250  	// args[2] - serialized policy
   251  	args := [][]byte{[]byte(""), envBytes, policy}
   252  
   253  	vscctxid := coreUtil.GenerateUUID()
   254  
   255  	// Get chaincode version
   256  	version := coreUtil.GetSysCCVersion()
   257  	cccid := v.ccprovider.GetCCContext(chainID, vscc, version, vscctxid, true, nil, nil)
   258  
   259  	// invoke VSCC
   260  	logger.Debug("Invoking VSCC txid", txid, "chaindID", chainID)
   261  	res, _, err := v.ccprovider.ExecuteChaincode(ctxt, cccid, args)
   262  	if err != nil {
   263  		logger.Errorf("Invoke VSCC failed for transaction txid=%s, error %s", txid, err)
   264  		return err
   265  	}
   266  	if res.Status != shim.OK {
   267  		logger.Errorf("VSCC check failed for transaction txid=%s, error %s", txid, res.Message)
   268  		return fmt.Errorf("%s", res.Message)
   269  	}
   270  
   271  	return nil
   272  }