github.com/kaituanwang/hyperledger@v2.0.1+incompatible/core/committer/txvalidator/v20/validator.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package txvalidator
     8  
     9  import (
    10  	"context"
    11  	"fmt"
    12  	"time"
    13  
    14  	"github.com/golang/protobuf/proto"
    15  	"github.com/hyperledger/fabric-protos-go/common"
    16  	mspprotos "github.com/hyperledger/fabric-protos-go/msp"
    17  	"github.com/hyperledger/fabric-protos-go/peer"
    18  	"github.com/hyperledger/fabric/bccsp"
    19  	"github.com/hyperledger/fabric/common/channelconfig"
    20  	"github.com/hyperledger/fabric/common/configtx"
    21  	commonerrors "github.com/hyperledger/fabric/common/errors"
    22  	"github.com/hyperledger/fabric/common/flogging"
    23  	"github.com/hyperledger/fabric/common/policies"
    24  	"github.com/hyperledger/fabric/core/committer/txvalidator/plugin"
    25  	"github.com/hyperledger/fabric/core/committer/txvalidator/v20/plugindispatcher"
    26  	"github.com/hyperledger/fabric/core/common/validation"
    27  	"github.com/hyperledger/fabric/core/ledger"
    28  	ledgerUtil "github.com/hyperledger/fabric/core/ledger/util"
    29  	"github.com/hyperledger/fabric/msp"
    30  	"github.com/hyperledger/fabric/protoutil"
    31  	"github.com/pkg/errors"
    32  )
    33  
    34  // Semaphore provides to the validator means for synchronisation
    35  type Semaphore interface {
    36  	// Acquire implements semaphore-like acquire semantics
    37  	Acquire(ctx context.Context) error
    38  
    39  	// Release implements semaphore-like release semantics
    40  	Release()
    41  }
    42  
    43  // ChannelResources provides access to channel artefacts or
    44  // functions to interact with them
    45  type ChannelResources interface {
    46  	// MSPManager returns the MSP manager for this channel
    47  	MSPManager() msp.MSPManager
    48  
    49  	// Apply attempts to apply a configtx to become the new config
    50  	Apply(configtx *common.ConfigEnvelope) error
    51  
    52  	// GetMSPIDs returns the IDs for the application MSPs
    53  	// that have been defined in the channel
    54  	GetMSPIDs() []string
    55  
    56  	// Capabilities defines the capabilities for the application portion of this channel
    57  	Capabilities() channelconfig.ApplicationCapabilities
    58  }
    59  
    60  // LedgerResources provides access to ledger artefacts or
    61  // functions to interact with them
    62  type LedgerResources interface {
    63  	// GetTransactionByID retrieves a transaction by id
    64  	GetTransactionByID(txID string) (*peer.ProcessedTransaction, error)
    65  
    66  	// NewQueryExecutor gives handle to a query executor.
    67  	// A client can obtain more than one 'QueryExecutor's for parallel execution.
    68  	// Any synchronization should be performed at the implementation level if required
    69  	NewQueryExecutor() (ledger.QueryExecutor, error)
    70  }
    71  
    72  // Dispatcher is an interface to decouple tx validator
    73  // and plugin dispatcher
    74  type Dispatcher interface {
    75  	// Dispatch invokes the appropriate validation plugin for the supplied transaction in the block
    76  	Dispatch(seq int, payload *common.Payload, envBytes []byte, block *common.Block) (error, peer.TxValidationCode)
    77  }
    78  
    79  //go:generate mockery -dir . -name ChannelResources -case underscore -output mocks/
    80  //go:generate mockery -dir . -name LedgerResources -case underscore -output mocks/
    81  //go:generate mockery -dir . -name Dispatcher -case underscore -output mocks/
    82  
    83  //go:generate mockery -dir . -name QueryExecutor -case underscore -output mocks/
    84  
    85  // QueryExecutor is the local interface that used to generate mocks for foreign interface.
    86  type QueryExecutor interface {
    87  	ledger.QueryExecutor
    88  }
    89  
    90  //go:generate mockery -dir . -name ChannelPolicyManagerGetter -case underscore -output mocks/
    91  
    92  // ChannelPolicyManagerGetter is the local interface that used to generate mocks for foreign interface.
    93  type ChannelPolicyManagerGetter interface {
    94  	policies.ChannelPolicyManagerGetter
    95  }
    96  
    97  //go:generate mockery -dir . -name PolicyManager -case underscore -output mocks/
    98  
    99  type PolicyManager interface {
   100  	policies.Manager
   101  }
   102  
   103  //go:generate mockery -dir plugindispatcher/ -name CollectionResources -case underscore -output mocks/
   104  
   105  // TxValidator is the implementation of Validator interface, keeps
   106  // reference to the ledger to enable tx simulation
   107  // and execution of plugins
   108  type TxValidator struct {
   109  	ChannelID        string
   110  	Semaphore        Semaphore
   111  	ChannelResources ChannelResources
   112  	LedgerResources  LedgerResources
   113  	Dispatcher       Dispatcher
   114  	CryptoProvider   bccsp.BCCSP
   115  }
   116  
   117  var logger = flogging.MustGetLogger("committer.txvalidator")
   118  
   119  type blockValidationRequest struct {
   120  	block *common.Block
   121  	d     []byte
   122  	tIdx  int
   123  }
   124  
   125  type blockValidationResult struct {
   126  	tIdx           int
   127  	validationCode peer.TxValidationCode
   128  	err            error
   129  	txid           string
   130  }
   131  
   132  // NewTxValidator creates new transactions validator
   133  func NewTxValidator(
   134  	channelID string,
   135  	sem Semaphore,
   136  	cr ChannelResources,
   137  	ler LedgerResources,
   138  	lcr plugindispatcher.LifecycleResources,
   139  	cor plugindispatcher.CollectionResources,
   140  	pm plugin.Mapper,
   141  	channelPolicyManagerGetter policies.ChannelPolicyManagerGetter,
   142  	cryptoProvider bccsp.BCCSP,
   143  ) *TxValidator {
   144  	// Encapsulates interface implementation
   145  	pluginValidator := plugindispatcher.NewPluginValidator(pm, ler, &dynamicDeserializer{cr: cr}, &dynamicCapabilities{cr: cr}, channelPolicyManagerGetter, cor)
   146  	return &TxValidator{
   147  		ChannelID:        channelID,
   148  		Semaphore:        sem,
   149  		ChannelResources: cr,
   150  		LedgerResources:  ler,
   151  		Dispatcher:       plugindispatcher.New(channelID, cr, ler, lcr, pluginValidator),
   152  		CryptoProvider:   cryptoProvider,
   153  	}
   154  }
   155  
   156  func (v *TxValidator) chainExists(chain string) bool {
   157  	// TODO: implement this function!
   158  	return true
   159  }
   160  
   161  // Validate performs the validation of a block. The validation
   162  // of each transaction in the block is performed in parallel.
   163  // The approach is as follows: the committer thread starts the
   164  // tx validation function in a goroutine (using a semaphore to cap
   165  // the number of concurrent validating goroutines). The committer
   166  // thread then reads results of validation (in orderer of completion
   167  // of the goroutines) from the results channel. The goroutines
   168  // perform the validation of the txs in the block and enqueue the
   169  // validation result in the results channel. A few note-worthy facts:
   170  // 1) to keep the approach simple, the committer thread enqueues
   171  //    all transactions in the block and then moves on to reading the
   172  //    results.
   173  // 2) for parallel validation to work, it is important that the
   174  //    validation function does not change the state of the system.
   175  //    Otherwise the order in which validation is perform matters
   176  //    and we have to resort to sequential validation (or some locking).
   177  //    This is currently true, because the only function that affects
   178  //    state is when a config transaction is received, but they are
   179  //    guaranteed to be alone in the block. If/when this assumption
   180  //    is violated, this code must be changed.
   181  func (v *TxValidator) Validate(block *common.Block) error {
   182  	var err error
   183  	var errPos int
   184  
   185  	startValidation := time.Now() // timer to log Validate block duration
   186  	logger.Debugf("[%s] START Block Validation for block [%d]", v.ChannelID, block.Header.Number)
   187  
   188  	// Initialize trans as valid here, then set invalidation reason code upon invalidation below
   189  	txsfltr := ledgerUtil.NewTxValidationFlags(len(block.Data.Data))
   190  	// array of txids
   191  	txidArray := make([]string, len(block.Data.Data))
   192  
   193  	results := make(chan *blockValidationResult)
   194  	go func() {
   195  		for tIdx, d := range block.Data.Data {
   196  			// ensure that we don't have too many concurrent validation workers
   197  			v.Semaphore.Acquire(context.Background())
   198  
   199  			go func(index int, data []byte) {
   200  				defer v.Semaphore.Release()
   201  
   202  				v.validateTx(&blockValidationRequest{
   203  					d:     data,
   204  					block: block,
   205  					tIdx:  index,
   206  				}, results)
   207  			}(tIdx, d)
   208  		}
   209  	}()
   210  
   211  	logger.Debugf("expecting %d block validation responses", len(block.Data.Data))
   212  
   213  	// now we read responses in the order in which they come back
   214  	for i := 0; i < len(block.Data.Data); i++ {
   215  		res := <-results
   216  
   217  		if res.err != nil {
   218  			// if there is an error, we buffer its value, wait for
   219  			// all workers to complete validation and then return
   220  			// the error from the first tx in this block that returned an error
   221  			logger.Debugf("got terminal error %s for idx %d", res.err, res.tIdx)
   222  
   223  			if err == nil || res.tIdx < errPos {
   224  				err = res.err
   225  				errPos = res.tIdx
   226  			}
   227  		} else {
   228  			// if there was no error, we set the txsfltr and we set the
   229  			// txsChaincodeNames and txsUpgradedChaincodes maps
   230  			logger.Debugf("got result for idx %d, code %d", res.tIdx, res.validationCode)
   231  
   232  			txsfltr.SetFlag(res.tIdx, res.validationCode)
   233  
   234  			if res.validationCode == peer.TxValidationCode_VALID {
   235  				txidArray[res.tIdx] = res.txid
   236  			}
   237  		}
   238  	}
   239  
   240  	// if we're here, all workers have completed the validation.
   241  	// If there was an error we return the error from the first
   242  	// tx in this block that returned an error
   243  	if err != nil {
   244  		return err
   245  	}
   246  
   247  	// we mark invalid any transaction that has a txid
   248  	// which is equal to that of a previous tx in this block
   249  	markTXIdDuplicates(txidArray, txsfltr)
   250  
   251  	// make sure no transaction has skipped validation
   252  	err = v.allValidated(txsfltr, block)
   253  	if err != nil {
   254  		return err
   255  	}
   256  
   257  	// Initialize metadata structure
   258  	protoutil.InitBlockMetadata(block)
   259  
   260  	block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txsfltr
   261  
   262  	elapsedValidation := time.Since(startValidation) / time.Millisecond // duration in ms
   263  	logger.Infof("[%s] Validated block [%d] in %dms", v.ChannelID, block.Header.Number, elapsedValidation)
   264  
   265  	return nil
   266  }
   267  
   268  // allValidated returns error if some of the validation flags have not been set
   269  // during validation
   270  func (v *TxValidator) allValidated(txsfltr ledgerUtil.TxValidationFlags, block *common.Block) error {
   271  	for id, f := range txsfltr {
   272  		if peer.TxValidationCode(f) == peer.TxValidationCode_NOT_VALIDATED {
   273  			return errors.Errorf("transaction %d in block %d has skipped validation", id, block.Header.Number)
   274  		}
   275  	}
   276  
   277  	return nil
   278  }
   279  
   280  func markTXIdDuplicates(txids []string, txsfltr ledgerUtil.TxValidationFlags) {
   281  	txidMap := make(map[string]struct{})
   282  
   283  	for id, txid := range txids {
   284  		if txid == "" {
   285  			continue
   286  		}
   287  
   288  		_, in := txidMap[txid]
   289  		if in {
   290  			logger.Error("Duplicate txid", txid, "found, skipping")
   291  			txsfltr.SetFlag(id, peer.TxValidationCode_DUPLICATE_TXID)
   292  		} else {
   293  			txidMap[txid] = struct{}{}
   294  		}
   295  	}
   296  }
   297  
   298  func (v *TxValidator) validateTx(req *blockValidationRequest, results chan<- *blockValidationResult) {
   299  	block := req.block
   300  	d := req.d
   301  	tIdx := req.tIdx
   302  	txID := ""
   303  
   304  	if d == nil {
   305  		results <- &blockValidationResult{
   306  			tIdx: tIdx,
   307  		}
   308  		return
   309  	}
   310  
   311  	if env, err := protoutil.GetEnvelopeFromBlock(d); err != nil {
   312  		logger.Warningf("Error getting tx from block: %+v", err)
   313  		results <- &blockValidationResult{
   314  			tIdx:           tIdx,
   315  			validationCode: peer.TxValidationCode_INVALID_OTHER_REASON,
   316  		}
   317  		return
   318  	} else if env != nil {
   319  		// validate the transaction: here we check that the transaction
   320  		// is properly formed, properly signed and that the security
   321  		// chain binding proposal to endorsements to tx holds. We do
   322  		// NOT check the validity of endorsements, though. That's a
   323  		// job for the validation plugins
   324  		logger.Debugf("[%s] validateTx starts for block %p env %p txn %d", v.ChannelID, block, env, tIdx)
   325  		defer logger.Debugf("[%s] validateTx completes for block %p env %p txn %d", v.ChannelID, block, env, tIdx)
   326  		var payload *common.Payload
   327  		var err error
   328  		var txResult peer.TxValidationCode
   329  
   330  		if payload, txResult = validation.ValidateTransaction(env, v.CryptoProvider); txResult != peer.TxValidationCode_VALID {
   331  			logger.Errorf("Invalid transaction with index %d", tIdx)
   332  			results <- &blockValidationResult{
   333  				tIdx:           tIdx,
   334  				validationCode: txResult,
   335  			}
   336  			return
   337  		}
   338  
   339  		chdr, err := protoutil.UnmarshalChannelHeader(payload.Header.ChannelHeader)
   340  		if err != nil {
   341  			logger.Warningf("Could not unmarshal channel header, err %s, skipping", err)
   342  			results <- &blockValidationResult{
   343  				tIdx:           tIdx,
   344  				validationCode: peer.TxValidationCode_INVALID_OTHER_REASON,
   345  			}
   346  			return
   347  		}
   348  
   349  		channel := chdr.ChannelId
   350  		logger.Debugf("Transaction is for channel %s", channel)
   351  
   352  		if !v.chainExists(channel) {
   353  			logger.Errorf("Dropping transaction for non-existent channel %s", channel)
   354  			results <- &blockValidationResult{
   355  				tIdx:           tIdx,
   356  				validationCode: peer.TxValidationCode_TARGET_CHAIN_NOT_FOUND,
   357  			}
   358  			return
   359  		}
   360  
   361  		if common.HeaderType(chdr.Type) == common.HeaderType_ENDORSER_TRANSACTION {
   362  
   363  			txID = chdr.TxId
   364  
   365  			// Check duplicate transactions
   366  			erroneousResultEntry := v.checkTxIdDupsLedger(tIdx, chdr, v.LedgerResources)
   367  			if erroneousResultEntry != nil {
   368  				results <- erroneousResultEntry
   369  				return
   370  			}
   371  
   372  			// Validate tx with plugins
   373  			logger.Debug("Validating transaction with plugins")
   374  			err, cde := v.Dispatcher.Dispatch(tIdx, payload, d, block)
   375  			if err != nil {
   376  				logger.Errorf("Dispatch for transaction txId = %s returned error: %s", txID, err)
   377  				switch err.(type) {
   378  				case *commonerrors.VSCCExecutionFailureError:
   379  					results <- &blockValidationResult{
   380  						tIdx: tIdx,
   381  						err:  err,
   382  					}
   383  					return
   384  				case *commonerrors.VSCCInfoLookupFailureError:
   385  					results <- &blockValidationResult{
   386  						tIdx: tIdx,
   387  						err:  err,
   388  					}
   389  					return
   390  				default:
   391  					results <- &blockValidationResult{
   392  						tIdx:           tIdx,
   393  						validationCode: cde,
   394  					}
   395  					return
   396  				}
   397  			}
   398  		} else if common.HeaderType(chdr.Type) == common.HeaderType_CONFIG {
   399  			configEnvelope, err := configtx.UnmarshalConfigEnvelope(payload.Data)
   400  			if err != nil {
   401  				err = errors.WithMessage(err, "error unmarshalling config which passed initial validity checks")
   402  				logger.Criticalf("%+v", err)
   403  				results <- &blockValidationResult{
   404  					tIdx: tIdx,
   405  					err:  err,
   406  				}
   407  				return
   408  			}
   409  
   410  			if err := v.ChannelResources.Apply(configEnvelope); err != nil {
   411  				err = errors.WithMessage(err, "error validating config which passed initial validity checks")
   412  				logger.Criticalf("%+v", err)
   413  				results <- &blockValidationResult{
   414  					tIdx: tIdx,
   415  					err:  err,
   416  				}
   417  				return
   418  			}
   419  			logger.Debugf("config transaction received for chain %s", channel)
   420  		} else {
   421  			logger.Warningf("Unknown transaction type [%s] in block number [%d] transaction index [%d]",
   422  				common.HeaderType(chdr.Type), block.Header.Number, tIdx)
   423  			results <- &blockValidationResult{
   424  				tIdx:           tIdx,
   425  				validationCode: peer.TxValidationCode_UNKNOWN_TX_TYPE,
   426  			}
   427  			return
   428  		}
   429  
   430  		if _, err := proto.Marshal(env); err != nil {
   431  			logger.Warningf("Cannot marshal transaction: %s", err)
   432  			results <- &blockValidationResult{
   433  				tIdx:           tIdx,
   434  				validationCode: peer.TxValidationCode_MARSHAL_TX_ERROR,
   435  			}
   436  			return
   437  		}
   438  		// Succeeded to pass down here, transaction is valid
   439  		results <- &blockValidationResult{
   440  			tIdx:           tIdx,
   441  			validationCode: peer.TxValidationCode_VALID,
   442  			txid:           txID,
   443  		}
   444  		return
   445  	} else {
   446  		logger.Warning("Nil tx from block")
   447  		results <- &blockValidationResult{
   448  			tIdx:           tIdx,
   449  			validationCode: peer.TxValidationCode_NIL_ENVELOPE,
   450  		}
   451  		return
   452  	}
   453  }
   454  
   455  // CheckTxIdDupsLedger returns a vlockValidationResult enhanced with the respective
   456  // error codes if and only if there is transaction with the same transaction identifier
   457  // in the ledger or no decision can be made for whether such transaction exists;
   458  // the function returns nil if it has ensured that there is no such duplicate, such
   459  // that its consumer can proceed with the transaction processing
   460  func (v *TxValidator) checkTxIdDupsLedger(tIdx int, chdr *common.ChannelHeader, ldgr LedgerResources) *blockValidationResult {
   461  
   462  	// Retrieve the transaction identifier of the input header
   463  	txID := chdr.TxId
   464  
   465  	// Look for a transaction with the same identifier inside the ledger
   466  	_, err := ldgr.GetTransactionByID(txID)
   467  
   468  	switch err.(type) {
   469  	case nil:
   470  		// invalid case, returned error is nil. It means that there is already a tx in the ledger with the same id
   471  		logger.Error("Duplicate transaction found, ", txID, ", skipping")
   472  		return &blockValidationResult{
   473  			tIdx:           tIdx,
   474  			validationCode: peer.TxValidationCode_DUPLICATE_TXID,
   475  		}
   476  	case ledger.NotFoundInIndexErr:
   477  		// valid case, returned error is of type NotFoundInIndexErr.
   478  		// It means that no tx with the same id is found in the ledger
   479  		return nil
   480  	default:
   481  		// invalid case, returned error is not of type NotFoundInIndexErr.
   482  		// It means that we could not verify whether a tx with the supplied id is in the ledger
   483  		logger.Errorf("Ledger failure while attempting to detect duplicate status for txid %s: %s", txID, err)
   484  		return &blockValidationResult{
   485  			tIdx: tIdx,
   486  			err:  err,
   487  		}
   488  	}
   489  }
   490  
   491  // generateCCKey generates a unique identifier for chaincode in specific channel
   492  func (v *TxValidator) generateCCKey(ccName, chainID string) string {
   493  	return fmt.Sprintf("%s/%s", ccName, chainID)
   494  }
   495  
   496  type dynamicDeserializer struct {
   497  	cr ChannelResources
   498  }
   499  
   500  func (ds *dynamicDeserializer) DeserializeIdentity(serializedIdentity []byte) (msp.Identity, error) {
   501  	return ds.cr.MSPManager().DeserializeIdentity(serializedIdentity)
   502  }
   503  
   504  func (ds *dynamicDeserializer) IsWellFormed(identity *mspprotos.SerializedIdentity) error {
   505  	return ds.cr.MSPManager().IsWellFormed(identity)
   506  }
   507  
   508  type dynamicCapabilities struct {
   509  	cr ChannelResources
   510  }
   511  
   512  func (ds *dynamicCapabilities) ACLs() bool {
   513  	return ds.cr.Capabilities().ACLs()
   514  }
   515  
   516  func (ds *dynamicCapabilities) CollectionUpgrade() bool {
   517  	return ds.cr.Capabilities().CollectionUpgrade()
   518  }
   519  
   520  func (ds *dynamicCapabilities) ForbidDuplicateTXIdInBlock() bool {
   521  	return ds.cr.Capabilities().ForbidDuplicateTXIdInBlock()
   522  }
   523  
   524  func (ds *dynamicCapabilities) KeyLevelEndorsement() bool {
   525  	return ds.cr.Capabilities().KeyLevelEndorsement()
   526  }
   527  
   528  func (ds *dynamicCapabilities) MetadataLifecycle() bool {
   529  	// This capability no longer exists and should not be referenced in validation anyway
   530  	return false
   531  }
   532  
   533  func (ds *dynamicCapabilities) PrivateChannelData() bool {
   534  	return ds.cr.Capabilities().PrivateChannelData()
   535  }
   536  
   537  func (ds *dynamicCapabilities) StorePvtDataOfInvalidTx() bool {
   538  	return ds.cr.Capabilities().StorePvtDataOfInvalidTx()
   539  }
   540  
   541  func (ds *dynamicCapabilities) Supported() error {
   542  	return ds.cr.Capabilities().Supported()
   543  }
   544  
   545  func (ds *dynamicCapabilities) V1_1Validation() bool {
   546  	return ds.cr.Capabilities().V1_1Validation()
   547  }
   548  
   549  func (ds *dynamicCapabilities) V1_2Validation() bool {
   550  	return ds.cr.Capabilities().V1_2Validation()
   551  }
   552  
   553  func (ds *dynamicCapabilities) V1_3Validation() bool {
   554  	return ds.cr.Capabilities().V1_3Validation()
   555  }
   556  
   557  func (ds *dynamicCapabilities) V2_0Validation() bool {
   558  	return ds.cr.Capabilities().V2_0Validation()
   559  }