github.com/yimialmonte/fabric@v2.1.1+incompatible/core/peer/deliverevents.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package peer
     8  
     9  import (
    10  	"runtime/debug"
    11  
    12  	"github.com/hyperledger/fabric-protos-go/common"
    13  	"github.com/hyperledger/fabric-protos-go/ledger/rwset"
    14  	"github.com/hyperledger/fabric-protos-go/peer"
    15  	"github.com/hyperledger/fabric/common/deliver"
    16  	"github.com/hyperledger/fabric/common/flogging"
    17  	"github.com/hyperledger/fabric/core/aclmgmt/resources"
    18  	"github.com/hyperledger/fabric/core/common/privdata"
    19  	"github.com/hyperledger/fabric/core/ledger"
    20  	"github.com/hyperledger/fabric/core/ledger/util"
    21  	"github.com/hyperledger/fabric/msp"
    22  	"github.com/hyperledger/fabric/msp/mgmt"
    23  	"github.com/hyperledger/fabric/protoutil"
    24  	"github.com/pkg/errors"
    25  )
    26  
    27  var logger = flogging.MustGetLogger("common.deliverevents")
    28  
    29  // PolicyCheckerProvider provides the corresponding policy checker for a
    30  // given resource name
    31  type PolicyCheckerProvider func(resourceName string) deliver.PolicyCheckerFunc
    32  
    33  // DeliverServer holds the dependencies necessary to create a deliver server
    34  type DeliverServer struct {
    35  	DeliverHandler          *deliver.Handler
    36  	PolicyCheckerProvider   PolicyCheckerProvider
    37  	CollectionPolicyChecker CollectionPolicyChecker
    38  	IdentityDeserializerMgr IdentityDeserializerManager
    39  }
    40  
    41  // Chain adds Ledger() to deliver.Chain
    42  type Chain interface {
    43  	deliver.Chain
    44  	Ledger() ledger.PeerLedger
    45  }
    46  
    47  // CollectionPolicyChecker is an interface that encapsulates the CheckCollectionPolicy method
    48  type CollectionPolicyChecker interface {
    49  	CheckCollectionPolicy(blockNum uint64, ccName string, collName string, cfgHistoryRetriever ledger.ConfigHistoryRetriever, deserializer msp.IdentityDeserializer, signedData *protoutil.SignedData) (bool, error)
    50  }
    51  
    52  // IdentityDeserializerManager returns instances of Deserializer
    53  type IdentityDeserializerManager interface {
    54  	// Deserializer returns an instance of transaction.Deserializer for the passed channel
    55  	// if the channel exists
    56  	Deserializer(channel string) (msp.IdentityDeserializer, error)
    57  }
    58  
    59  // blockResponseSender structure used to send block responses
    60  type blockResponseSender struct {
    61  	peer.Deliver_DeliverServer
    62  }
    63  
    64  // SendStatusResponse generates status reply proto message
    65  func (brs *blockResponseSender) SendStatusResponse(status common.Status) error {
    66  	reply := &peer.DeliverResponse{
    67  		Type: &peer.DeliverResponse_Status{Status: status},
    68  	}
    69  	return brs.Send(reply)
    70  }
    71  
    72  // SendBlockResponse generates deliver response with block message.
    73  func (brs *blockResponseSender) SendBlockResponse(
    74  	block *common.Block,
    75  	channelID string,
    76  	chain deliver.Chain,
    77  	signedData *protoutil.SignedData,
    78  ) error {
    79  	// Generates filtered block response
    80  	response := &peer.DeliverResponse{
    81  		Type: &peer.DeliverResponse_Block{Block: block},
    82  	}
    83  	return brs.Send(response)
    84  }
    85  
    86  func (brs *blockResponseSender) DataType() string {
    87  	return "block"
    88  }
    89  
    90  // filteredBlockResponseSender structure used to send filtered block responses
    91  type filteredBlockResponseSender struct {
    92  	peer.Deliver_DeliverFilteredServer
    93  }
    94  
    95  // SendStatusResponse generates status reply proto message
    96  func (fbrs *filteredBlockResponseSender) SendStatusResponse(status common.Status) error {
    97  	response := &peer.DeliverResponse{
    98  		Type: &peer.DeliverResponse_Status{Status: status},
    99  	}
   100  	return fbrs.Send(response)
   101  }
   102  
   103  // IsFiltered is a marker method which indicates that this response sender
   104  // sends filtered blocks.
   105  func (fbrs *filteredBlockResponseSender) IsFiltered() bool {
   106  	return true
   107  }
   108  
   109  // SendBlockResponse generates deliver response with filtered block message
   110  func (fbrs *filteredBlockResponseSender) SendBlockResponse(
   111  	block *common.Block,
   112  	channelID string,
   113  	chain deliver.Chain,
   114  	signedData *protoutil.SignedData,
   115  ) error {
   116  	// Generates filtered block response
   117  	b := blockEvent(*block)
   118  	filteredBlock, err := b.toFilteredBlock()
   119  	if err != nil {
   120  		logger.Warningf("Failed to generate filtered block due to: %s", err)
   121  		return fbrs.SendStatusResponse(common.Status_BAD_REQUEST)
   122  	}
   123  	response := &peer.DeliverResponse{
   124  		Type: &peer.DeliverResponse_FilteredBlock{FilteredBlock: filteredBlock},
   125  	}
   126  	return fbrs.Send(response)
   127  }
   128  
   129  func (fbrs *filteredBlockResponseSender) DataType() string {
   130  	return "filtered_block"
   131  }
   132  
   133  // blockResponseSender structure used to send block responses
   134  type blockAndPrivateDataResponseSender struct {
   135  	peer.Deliver_DeliverWithPrivateDataServer
   136  	CollectionPolicyChecker
   137  	IdentityDeserializerManager
   138  }
   139  
   140  // SendStatusResponse generates status reply proto message
   141  func (bprs *blockAndPrivateDataResponseSender) SendStatusResponse(status common.Status) error {
   142  	reply := &peer.DeliverResponse{
   143  		Type: &peer.DeliverResponse_Status{Status: status},
   144  	}
   145  	return bprs.Send(reply)
   146  }
   147  
   148  // SendBlockResponse gets private data and generates deliver response with both block and private data
   149  func (bprs *blockAndPrivateDataResponseSender) SendBlockResponse(
   150  	block *common.Block,
   151  	channelID string,
   152  	chain deliver.Chain,
   153  	signedData *protoutil.SignedData,
   154  ) error {
   155  	pvtData, err := bprs.getPrivateData(block, chain, channelID, signedData)
   156  	if err != nil {
   157  		return err
   158  	}
   159  
   160  	blockAndPvtData := &peer.BlockAndPrivateData{
   161  		Block:          block,
   162  		PrivateDataMap: pvtData,
   163  	}
   164  	response := &peer.DeliverResponse{
   165  		Type: &peer.DeliverResponse_BlockAndPrivateData{BlockAndPrivateData: blockAndPvtData},
   166  	}
   167  	return bprs.Send(response)
   168  }
   169  
   170  func (bprs *blockAndPrivateDataResponseSender) DataType() string {
   171  	return "block_and_pvtdata"
   172  }
   173  
   174  // getPrivateData returns private data for the block
   175  func (bprs *blockAndPrivateDataResponseSender) getPrivateData(
   176  	block *common.Block,
   177  	chain deliver.Chain,
   178  	channelID string,
   179  	signedData *protoutil.SignedData,
   180  ) (map[uint64]*rwset.TxPvtReadWriteSet, error) {
   181  
   182  	channel, ok := chain.(Chain)
   183  	if !ok {
   184  		return nil, errors.New("wrong chain type")
   185  	}
   186  
   187  	pvtData, err := channel.Ledger().GetPvtDataByNum(block.Header.Number, nil)
   188  	if err != nil {
   189  		logger.Errorf("Error getting private data by block number %d on channel %s", block.Header.Number, channelID)
   190  		return nil, errors.Wrapf(err, "error getting private data by block number %d", block.Header.Number)
   191  	}
   192  
   193  	seqs2Namespaces := aggregatedCollections(make(map[seqAndDataModel]map[string][]*rwset.CollectionPvtReadWriteSet))
   194  
   195  	configHistoryRetriever, err := channel.Ledger().GetConfigHistoryRetriever()
   196  	if err != nil {
   197  		return nil, err
   198  	}
   199  
   200  	identityDeserializer, err := bprs.IdentityDeserializerManager.Deserializer(channelID)
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  
   205  	// check policy for each collection and add the collection if passing the policy requirement
   206  	for _, item := range pvtData {
   207  		logger.Debugf("Got private data for block number %d, tx sequence %d", block.Header.Number, item.SeqInBlock)
   208  		if item.WriteSet == nil {
   209  			continue
   210  		}
   211  		for _, ns := range item.WriteSet.NsPvtRwset {
   212  			for _, col := range ns.CollectionPvtRwset {
   213  				logger.Debugf("Checking policy for namespace %s, collection %s", ns.Namespace, col.CollectionName)
   214  
   215  				eligible, err := bprs.CollectionPolicyChecker.CheckCollectionPolicy(block.Header.Number,
   216  					ns.Namespace, col.CollectionName, configHistoryRetriever, identityDeserializer, signedData)
   217  				if err != nil {
   218  					return nil, err
   219  				}
   220  
   221  				if eligible {
   222  					logger.Debugf("Adding private data for namespace %s, collection %s", ns.Namespace, col.CollectionName)
   223  					seqs2Namespaces.addCollection(item.SeqInBlock, item.WriteSet.DataModel, ns.Namespace, col)
   224  				}
   225  			}
   226  		}
   227  	}
   228  
   229  	return seqs2Namespaces.asPrivateDataMap(), nil
   230  }
   231  
   232  // transactionActions aliasing for peer.TransactionAction pointers slice
   233  type transactionActions []*peer.TransactionAction
   234  
   235  // blockEvent an alias for common.Block structure, used to
   236  // extend with auxiliary functionality
   237  type blockEvent common.Block
   238  
   239  // DeliverFiltered sends a stream of blocks to a client after commitment
   240  func (s *DeliverServer) DeliverFiltered(srv peer.Deliver_DeliverFilteredServer) error {
   241  	logger.Debugf("Starting new DeliverFiltered handler")
   242  	defer dumpStacktraceOnPanic()
   243  	// getting policy checker based on resources.Event_FilteredBlock resource name
   244  	deliverServer := &deliver.Server{
   245  		Receiver:      srv,
   246  		PolicyChecker: s.PolicyCheckerProvider(resources.Event_FilteredBlock),
   247  		ResponseSender: &filteredBlockResponseSender{
   248  			Deliver_DeliverFilteredServer: srv,
   249  		},
   250  	}
   251  	return s.DeliverHandler.Handle(srv.Context(), deliverServer)
   252  }
   253  
   254  // Deliver sends a stream of blocks to a client after commitment
   255  func (s *DeliverServer) Deliver(srv peer.Deliver_DeliverServer) (err error) {
   256  	logger.Debugf("Starting new Deliver handler")
   257  	defer dumpStacktraceOnPanic()
   258  	// getting policy checker based on resources.Event_Block resource name
   259  	deliverServer := &deliver.Server{
   260  		PolicyChecker: s.PolicyCheckerProvider(resources.Event_Block),
   261  		Receiver:      srv,
   262  		ResponseSender: &blockResponseSender{
   263  			Deliver_DeliverServer: srv,
   264  		},
   265  	}
   266  	return s.DeliverHandler.Handle(srv.Context(), deliverServer)
   267  }
   268  
   269  // DeliverWithPrivateData sends a stream of blocks and pvtdata to a client after commitment
   270  func (s *DeliverServer) DeliverWithPrivateData(srv peer.Deliver_DeliverWithPrivateDataServer) (err error) {
   271  	logger.Debug("Starting new DeliverWithPrivateData handler")
   272  	defer dumpStacktraceOnPanic()
   273  	if s.CollectionPolicyChecker == nil {
   274  		s.CollectionPolicyChecker = &collPolicyChecker{}
   275  	}
   276  	if s.IdentityDeserializerMgr == nil {
   277  		s.IdentityDeserializerMgr = &identityDeserializerMgr{}
   278  	}
   279  	// getting policy checker based on resources.Event_Block resource name
   280  	deliverServer := &deliver.Server{
   281  		PolicyChecker: s.PolicyCheckerProvider(resources.Event_Block),
   282  		Receiver:      srv,
   283  		ResponseSender: &blockAndPrivateDataResponseSender{
   284  			Deliver_DeliverWithPrivateDataServer: srv,
   285  			CollectionPolicyChecker:              s.CollectionPolicyChecker,
   286  			IdentityDeserializerManager:          s.IdentityDeserializerMgr,
   287  		},
   288  	}
   289  	err = s.DeliverHandler.Handle(srv.Context(), deliverServer)
   290  	return err
   291  }
   292  
   293  func (block *blockEvent) toFilteredBlock() (*peer.FilteredBlock, error) {
   294  	filteredBlock := &peer.FilteredBlock{
   295  		Number: block.Header.Number,
   296  	}
   297  
   298  	txsFltr := util.TxValidationFlags(block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER])
   299  	for txIndex, ebytes := range block.Data.Data {
   300  		var env *common.Envelope
   301  		var err error
   302  
   303  		if ebytes == nil {
   304  			logger.Debugf("got nil data bytes for tx index %d, block num %d", txIndex, block.Header.Number)
   305  			continue
   306  		}
   307  
   308  		env, err = protoutil.GetEnvelopeFromBlock(ebytes)
   309  		if err != nil {
   310  			logger.Errorf("error getting tx from block, %s", err)
   311  			continue
   312  		}
   313  
   314  		// get the payload from the envelope
   315  		payload, err := protoutil.UnmarshalPayload(env.Payload)
   316  		if err != nil {
   317  			return nil, errors.WithMessage(err, "could not extract payload from envelope")
   318  		}
   319  
   320  		if payload.Header == nil {
   321  			logger.Debugf("transaction payload header is nil, %d, block num %d", txIndex, block.Header.Number)
   322  			continue
   323  		}
   324  		chdr, err := protoutil.UnmarshalChannelHeader(payload.Header.ChannelHeader)
   325  		if err != nil {
   326  			return nil, err
   327  		}
   328  
   329  		filteredBlock.ChannelId = chdr.ChannelId
   330  
   331  		filteredTransaction := &peer.FilteredTransaction{
   332  			Txid:             chdr.TxId,
   333  			Type:             common.HeaderType(chdr.Type),
   334  			TxValidationCode: txsFltr.Flag(txIndex),
   335  		}
   336  
   337  		if filteredTransaction.Type == common.HeaderType_ENDORSER_TRANSACTION {
   338  			tx, err := protoutil.UnmarshalTransaction(payload.Data)
   339  			if err != nil {
   340  				return nil, errors.WithMessage(err, "error unmarshal transaction payload for block event")
   341  			}
   342  
   343  			filteredTransaction.Data, err = transactionActions(tx.Actions).toFilteredActions()
   344  			if err != nil {
   345  				logger.Errorf(err.Error())
   346  				return nil, err
   347  			}
   348  		}
   349  
   350  		filteredBlock.FilteredTransactions = append(filteredBlock.FilteredTransactions, filteredTransaction)
   351  	}
   352  
   353  	return filteredBlock, nil
   354  }
   355  
   356  func (ta transactionActions) toFilteredActions() (*peer.FilteredTransaction_TransactionActions, error) {
   357  	transactionActions := &peer.FilteredTransactionActions{}
   358  	for _, action := range ta {
   359  		chaincodeActionPayload, err := protoutil.UnmarshalChaincodeActionPayload(action.Payload)
   360  		if err != nil {
   361  			return nil, errors.WithMessage(err, "error unmarshal transaction action payload for block event")
   362  		}
   363  
   364  		if chaincodeActionPayload.Action == nil {
   365  			logger.Debugf("chaincode action, the payload action is nil, skipping")
   366  			continue
   367  		}
   368  		propRespPayload, err := protoutil.UnmarshalProposalResponsePayload(chaincodeActionPayload.Action.ProposalResponsePayload)
   369  		if err != nil {
   370  			return nil, errors.WithMessage(err, "error unmarshal proposal response payload for block event")
   371  		}
   372  
   373  		caPayload, err := protoutil.UnmarshalChaincodeAction(propRespPayload.Extension)
   374  		if err != nil {
   375  			return nil, errors.WithMessage(err, "error unmarshal chaincode action for block event")
   376  		}
   377  
   378  		ccEvent, err := protoutil.UnmarshalChaincodeEvents(caPayload.Events)
   379  		if err != nil {
   380  			return nil, errors.WithMessage(err, "error unmarshal chaincode event for block event")
   381  		}
   382  
   383  		if ccEvent.GetChaincodeId() != "" {
   384  			filteredAction := &peer.FilteredChaincodeAction{
   385  				ChaincodeEvent: &peer.ChaincodeEvent{
   386  					TxId:        ccEvent.TxId,
   387  					ChaincodeId: ccEvent.ChaincodeId,
   388  					EventName:   ccEvent.EventName,
   389  				},
   390  			}
   391  			transactionActions.ChaincodeActions = append(transactionActions.ChaincodeActions, filteredAction)
   392  		}
   393  	}
   394  	return &peer.FilteredTransaction_TransactionActions{
   395  		TransactionActions: transactionActions,
   396  	}, nil
   397  }
   398  
   399  func dumpStacktraceOnPanic() {
   400  	func() {
   401  		if r := recover(); r != nil {
   402  			logger.Criticalf("Deliver client triggered panic: %s\n%s", r, debug.Stack())
   403  		}
   404  		logger.Debugf("Closing Deliver stream")
   405  	}()
   406  }
   407  
   408  type seqAndDataModel struct {
   409  	seq       uint64
   410  	dataModel rwset.TxReadWriteSet_DataModel
   411  }
   412  
   413  // Below map temporarily stores the private data that have passed the corresponding collection policy.
   414  // outer map is from seqAndDataModel to inner map,
   415  // and innner map is from namespace to []*rwset.CollectionPvtReadWriteSet
   416  type aggregatedCollections map[seqAndDataModel]map[string][]*rwset.CollectionPvtReadWriteSet
   417  
   418  // addCollection adds private data based on seq, namespace, and collection.
   419  func (ac aggregatedCollections) addCollection(seqInBlock uint64, dm rwset.TxReadWriteSet_DataModel, namespace string, col *rwset.CollectionPvtReadWriteSet) {
   420  	seq := seqAndDataModel{
   421  		dataModel: dm,
   422  		seq:       seqInBlock,
   423  	}
   424  	if _, exists := ac[seq]; !exists {
   425  		ac[seq] = make(map[string][]*rwset.CollectionPvtReadWriteSet)
   426  	}
   427  	ac[seq][namespace] = append(ac[seq][namespace], col)
   428  }
   429  
   430  // asPrivateDataMap converts aggregatedCollections to map[uint64]*rwset.TxPvtReadWriteSet
   431  // as defined in BlockAndPrivateData protobuf message.
   432  func (ac aggregatedCollections) asPrivateDataMap() map[uint64]*rwset.TxPvtReadWriteSet {
   433  	var pvtDataMap = make(map[uint64]*rwset.TxPvtReadWriteSet)
   434  	for seq, ns := range ac {
   435  		// create a txPvtReadWriteSet and add collection data to it
   436  		txPvtRWSet := &rwset.TxPvtReadWriteSet{
   437  			DataModel: seq.dataModel,
   438  		}
   439  
   440  		for namespaceName, cols := range ns {
   441  			txPvtRWSet.NsPvtRwset = append(txPvtRWSet.NsPvtRwset, &rwset.NsPvtReadWriteSet{
   442  				Namespace:          namespaceName,
   443  				CollectionPvtRwset: cols,
   444  			})
   445  		}
   446  
   447  		pvtDataMap[seq.seq] = txPvtRWSet
   448  	}
   449  	return pvtDataMap
   450  }
   451  
   452  // identityDeserializerMgr implements an IdentityDeserializerManager
   453  // by routing the call to the msp/mgmt package
   454  type identityDeserializerMgr struct {
   455  }
   456  
   457  func (*identityDeserializerMgr) Deserializer(channelID string) (msp.IdentityDeserializer, error) {
   458  	id, ok := mgmt.GetDeserializers()[channelID]
   459  	if !ok {
   460  		return nil, errors.Errorf("channel %s not found", channelID)
   461  	}
   462  	return id, nil
   463  }
   464  
   465  // collPolicyChecker is the default implementation for CollectionPolicyChecker interface
   466  type collPolicyChecker struct {
   467  }
   468  
   469  // CheckCollectionPolicy checks if the CollectionCriteria meets the policy requirement
   470  func (cs *collPolicyChecker) CheckCollectionPolicy(
   471  	blockNum uint64,
   472  	ccName string,
   473  	collName string,
   474  	cfgHistoryRetriever ledger.ConfigHistoryRetriever,
   475  	deserializer msp.IdentityDeserializer,
   476  	signedData *protoutil.SignedData,
   477  ) (bool, error) {
   478  	configInfo, err := cfgHistoryRetriever.MostRecentCollectionConfigBelow(blockNum, ccName)
   479  	if err != nil {
   480  		return false, errors.WithMessagef(err, "error getting most recent collection config below block sequence = %d for chaincode %s", blockNum, ccName)
   481  	}
   482  
   483  	staticCollConfig := extractStaticCollectionConfig(configInfo.CollectionConfig, collName)
   484  	if staticCollConfig == nil {
   485  		return false, errors.Errorf("no collection config was found for collection %s for chaincode %s", collName, ccName)
   486  	}
   487  
   488  	if !staticCollConfig.MemberOnlyRead {
   489  		return true, nil
   490  	}
   491  
   492  	// get collection access policy and access filter to check eligibility
   493  	collAP := &privdata.SimpleCollection{}
   494  	err = collAP.Setup(staticCollConfig, deserializer)
   495  	if err != nil {
   496  		return false, errors.WithMessagef(err, "error setting up collection  %s", staticCollConfig.Name)
   497  	}
   498  	logger.Debugf("got collection access policy")
   499  
   500  	collFilter := collAP.AccessFilter()
   501  	if collFilter == nil {
   502  		logger.Debugf("collection %s has no access filter, skipping...", collName)
   503  		return false, nil
   504  	}
   505  
   506  	eligible := collFilter(*signedData)
   507  	return eligible, nil
   508  }
   509  
   510  func extractStaticCollectionConfig(configPackage *peer.CollectionConfigPackage, collectionName string) *peer.StaticCollectionConfig {
   511  	for _, config := range configPackage.Config {
   512  		switch cconf := config.Payload.(type) {
   513  		case *peer.CollectionConfig_StaticCollectionConfig:
   514  			if cconf.StaticCollectionConfig.Name == collectionName {
   515  				return cconf.StaticCollectionConfig
   516  			}
   517  		default:
   518  			return nil
   519  		}
   520  	}
   521  	return nil
   522  }