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

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