github.com/anjalikarhana/fabric@v2.1.1+incompatible/internal/peer/chaincode/common.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package chaincode
     8  
     9  import (
    10  	"context"
    11  	"crypto/tls"
    12  	"encoding/json"
    13  	"fmt"
    14  	"io/ioutil"
    15  	"math"
    16  	"strings"
    17  	"sync"
    18  
    19  	"github.com/golang/protobuf/proto"
    20  	"github.com/hyperledger/fabric-chaincode-go/shim"
    21  	pcommon "github.com/hyperledger/fabric-protos-go/common"
    22  	ab "github.com/hyperledger/fabric-protos-go/orderer"
    23  	pb "github.com/hyperledger/fabric-protos-go/peer"
    24  	"github.com/hyperledger/fabric/bccsp"
    25  	"github.com/hyperledger/fabric/common/policydsl"
    26  	"github.com/hyperledger/fabric/common/util"
    27  	"github.com/hyperledger/fabric/internal/peer/common"
    28  	"github.com/hyperledger/fabric/internal/pkg/identity"
    29  	"github.com/hyperledger/fabric/protoutil"
    30  	"github.com/pkg/errors"
    31  	"github.com/spf13/cobra"
    32  	"github.com/spf13/viper"
    33  )
    34  
    35  // checkSpec to see if chaincode resides within current package capture for language.
    36  func checkSpec(spec *pb.ChaincodeSpec) error {
    37  	// Don't allow nil value
    38  	if spec == nil {
    39  		return errors.New("expected chaincode specification, nil received")
    40  	}
    41  	if spec.ChaincodeId == nil {
    42  		return errors.New("expected chaincode ID, nil received")
    43  	}
    44  
    45  	return platformRegistry.ValidateSpec(spec.Type.String(), spec.ChaincodeId.Path)
    46  }
    47  
    48  // getChaincodeDeploymentSpec get chaincode deployment spec given the chaincode spec
    49  func getChaincodeDeploymentSpec(spec *pb.ChaincodeSpec, crtPkg bool) (*pb.ChaincodeDeploymentSpec, error) {
    50  	var codePackageBytes []byte
    51  	if crtPkg {
    52  		var err error
    53  		if err = checkSpec(spec); err != nil {
    54  			return nil, err
    55  		}
    56  
    57  		codePackageBytes, err = platformRegistry.GetDeploymentPayload(spec.Type.String(), spec.ChaincodeId.Path)
    58  		if err != nil {
    59  			return nil, errors.WithMessage(err, "error getting chaincode package bytes")
    60  		}
    61  		chaincodePath, err := platformRegistry.NormalizePath(spec.Type.String(), spec.ChaincodeId.Path)
    62  		if err != nil {
    63  			return nil, errors.WithMessage(err, "failed to normalize chaincode path")
    64  		}
    65  		spec.ChaincodeId.Path = chaincodePath
    66  	}
    67  
    68  	return &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec, CodePackage: codePackageBytes}, nil
    69  }
    70  
    71  // getChaincodeSpec get chaincode spec from the cli cmd parameters
    72  func getChaincodeSpec(cmd *cobra.Command) (*pb.ChaincodeSpec, error) {
    73  	spec := &pb.ChaincodeSpec{}
    74  	if err := checkChaincodeCmdParams(cmd); err != nil {
    75  		// unset usage silence because it's a command line usage error
    76  		cmd.SilenceUsage = false
    77  		return spec, err
    78  	}
    79  
    80  	// Build the spec
    81  	input := chaincodeInput{}
    82  	if err := json.Unmarshal([]byte(chaincodeCtorJSON), &input); err != nil {
    83  		return spec, errors.Wrap(err, "chaincode argument error")
    84  	}
    85  	input.IsInit = isInit
    86  
    87  	chaincodeLang = strings.ToUpper(chaincodeLang)
    88  	spec = &pb.ChaincodeSpec{
    89  		Type:        pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value[chaincodeLang]),
    90  		ChaincodeId: &pb.ChaincodeID{Path: chaincodePath, Name: chaincodeName, Version: chaincodeVersion},
    91  		Input:       &input.ChaincodeInput,
    92  	}
    93  	return spec, nil
    94  }
    95  
    96  // chaincodeInput is wrapper around the proto defined ChaincodeInput message that
    97  // is decorated with a custom JSON unmarshaller.
    98  type chaincodeInput struct {
    99  	pb.ChaincodeInput
   100  }
   101  
   102  // UnmarshalJSON converts the string-based REST/JSON input to
   103  // the []byte-based current ChaincodeInput structure.
   104  func (c *chaincodeInput) UnmarshalJSON(b []byte) error {
   105  	sa := struct {
   106  		Function string
   107  		Args     []string
   108  	}{}
   109  	err := json.Unmarshal(b, &sa)
   110  	if err != nil {
   111  		return err
   112  	}
   113  	allArgs := sa.Args
   114  	if sa.Function != "" {
   115  		allArgs = append([]string{sa.Function}, sa.Args...)
   116  	}
   117  	c.Args = util.ToChaincodeArgs(allArgs...)
   118  	return nil
   119  }
   120  
   121  func chaincodeInvokeOrQuery(cmd *cobra.Command, invoke bool, cf *ChaincodeCmdFactory) (err error) {
   122  	spec, err := getChaincodeSpec(cmd)
   123  	if err != nil {
   124  		return err
   125  	}
   126  
   127  	// call with empty txid to ensure production code generates a txid.
   128  	// otherwise, tests can explicitly set their own txid
   129  	txID := ""
   130  
   131  	proposalResp, err := ChaincodeInvokeOrQuery(
   132  		spec,
   133  		channelID,
   134  		txID,
   135  		invoke,
   136  		cf.Signer,
   137  		cf.Certificate,
   138  		cf.EndorserClients,
   139  		cf.DeliverClients,
   140  		cf.BroadcastClient,
   141  	)
   142  
   143  	if err != nil {
   144  		return errors.Errorf("%s - proposal response: %v", err, proposalResp)
   145  	}
   146  
   147  	if invoke {
   148  		logger.Debugf("ESCC invoke result: %v", proposalResp)
   149  		pRespPayload, err := protoutil.UnmarshalProposalResponsePayload(proposalResp.Payload)
   150  		if err != nil {
   151  			return errors.WithMessage(err, "error while unmarshaling proposal response payload")
   152  		}
   153  		ca, err := protoutil.UnmarshalChaincodeAction(pRespPayload.Extension)
   154  		if err != nil {
   155  			return errors.WithMessage(err, "error while unmarshaling chaincode action")
   156  		}
   157  		if proposalResp.Endorsement == nil {
   158  			return errors.Errorf("endorsement failure during invoke. response: %v", proposalResp.Response)
   159  		}
   160  		logger.Infof("Chaincode invoke successful. result: %v", ca.Response)
   161  	} else {
   162  		if proposalResp == nil {
   163  			return errors.New("error during query: received nil proposal response")
   164  		}
   165  		if proposalResp.Endorsement == nil {
   166  			return errors.Errorf("endorsement failure during query. response: %v", proposalResp.Response)
   167  		}
   168  
   169  		if chaincodeQueryRaw && chaincodeQueryHex {
   170  			return fmt.Errorf("options --raw (-r) and --hex (-x) are not compatible")
   171  		}
   172  		if chaincodeQueryRaw {
   173  			fmt.Println(proposalResp.Response.Payload)
   174  			return nil
   175  		}
   176  		if chaincodeQueryHex {
   177  			fmt.Printf("%x\n", proposalResp.Response.Payload)
   178  			return nil
   179  		}
   180  		fmt.Println(string(proposalResp.Response.Payload))
   181  	}
   182  	return nil
   183  }
   184  
   185  type endorsementPolicy struct {
   186  	ChannelConfigPolicy string `json:"channelConfigPolicy,omitempty"`
   187  	SignaturePolicy     string `json:"signaturePolicy,omitempty"`
   188  }
   189  
   190  type collectionConfigJson struct {
   191  	Name              string             `json:"name"`
   192  	Policy            string             `json:"policy"`
   193  	RequiredPeerCount *int32             `json:"requiredPeerCount"`
   194  	MaxPeerCount      *int32             `json:"maxPeerCount"`
   195  	BlockToLive       uint64             `json:"blockToLive"`
   196  	MemberOnlyRead    bool               `json:"memberOnlyRead"`
   197  	MemberOnlyWrite   bool               `json:"memberOnlyWrite"`
   198  	EndorsementPolicy *endorsementPolicy `json:"endorsementPolicy,omitempty"`
   199  }
   200  
   201  // GetCollectionConfigFromFile retrieves the collection configuration
   202  // from the supplied file; the supplied file must contain a
   203  // json-formatted array of collectionConfigJson elements
   204  func GetCollectionConfigFromFile(ccFile string) (*pb.CollectionConfigPackage, []byte, error) {
   205  	fileBytes, err := ioutil.ReadFile(ccFile)
   206  	if err != nil {
   207  		return nil, nil, errors.Wrapf(err, "could not read file '%s'", ccFile)
   208  	}
   209  
   210  	return getCollectionConfigFromBytes(fileBytes)
   211  }
   212  
   213  // getCollectionConfig retrieves the collection configuration
   214  // from the supplied byte array; the byte array must contain a
   215  // json-formatted array of collectionConfigJson elements
   216  func getCollectionConfigFromBytes(cconfBytes []byte) (*pb.CollectionConfigPackage, []byte, error) {
   217  	cconf := &[]collectionConfigJson{}
   218  	err := json.Unmarshal(cconfBytes, cconf)
   219  	if err != nil {
   220  		return nil, nil, errors.Wrap(err, "could not parse the collection configuration")
   221  	}
   222  
   223  	ccarray := make([]*pb.CollectionConfig, 0, len(*cconf))
   224  	for _, cconfitem := range *cconf {
   225  		p, err := policydsl.FromString(cconfitem.Policy)
   226  		if err != nil {
   227  			return nil, nil, errors.WithMessagef(err, "invalid policy %s", cconfitem.Policy)
   228  		}
   229  
   230  		cpc := &pb.CollectionPolicyConfig{
   231  			Payload: &pb.CollectionPolicyConfig_SignaturePolicy{
   232  				SignaturePolicy: p,
   233  			},
   234  		}
   235  
   236  		var ep *pb.ApplicationPolicy
   237  		if cconfitem.EndorsementPolicy != nil {
   238  			signaturePolicy := cconfitem.EndorsementPolicy.SignaturePolicy
   239  			channelConfigPolicy := cconfitem.EndorsementPolicy.ChannelConfigPolicy
   240  			ep, err = getApplicationPolicy(signaturePolicy, channelConfigPolicy)
   241  			if err != nil {
   242  				return nil, nil, errors.WithMessagef(err, "invalid endorsement policy [%#v]", cconfitem.EndorsementPolicy)
   243  			}
   244  		}
   245  
   246  		// Set default requiredPeerCount and MaxPeerCount if not specified in json
   247  		requiredPeerCount := int32(0)
   248  		maxPeerCount := int32(1)
   249  		if cconfitem.RequiredPeerCount != nil {
   250  			requiredPeerCount = *cconfitem.RequiredPeerCount
   251  		}
   252  		if cconfitem.MaxPeerCount != nil {
   253  			maxPeerCount = *cconfitem.MaxPeerCount
   254  		}
   255  
   256  		cc := &pb.CollectionConfig{
   257  			Payload: &pb.CollectionConfig_StaticCollectionConfig{
   258  				StaticCollectionConfig: &pb.StaticCollectionConfig{
   259  					Name:              cconfitem.Name,
   260  					MemberOrgsPolicy:  cpc,
   261  					RequiredPeerCount: requiredPeerCount,
   262  					MaximumPeerCount:  maxPeerCount,
   263  					BlockToLive:       cconfitem.BlockToLive,
   264  					MemberOnlyRead:    cconfitem.MemberOnlyRead,
   265  					MemberOnlyWrite:   cconfitem.MemberOnlyWrite,
   266  					EndorsementPolicy: ep,
   267  				},
   268  			},
   269  		}
   270  
   271  		ccarray = append(ccarray, cc)
   272  	}
   273  
   274  	ccp := &pb.CollectionConfigPackage{Config: ccarray}
   275  	ccpBytes, err := proto.Marshal(ccp)
   276  	return ccp, ccpBytes, err
   277  }
   278  
   279  func getApplicationPolicy(signaturePolicy, channelConfigPolicy string) (*pb.ApplicationPolicy, error) {
   280  	if signaturePolicy == "" && channelConfigPolicy == "" {
   281  		// no policy, no problem
   282  		return nil, nil
   283  	}
   284  
   285  	if signaturePolicy != "" && channelConfigPolicy != "" {
   286  		// mo policies, mo problems
   287  		return nil, errors.New(`cannot specify both "--signature-policy" and "--channel-config-policy"`)
   288  	}
   289  
   290  	var applicationPolicy *pb.ApplicationPolicy
   291  	if signaturePolicy != "" {
   292  		signaturePolicyEnvelope, err := policydsl.FromString(signaturePolicy)
   293  		if err != nil {
   294  			return nil, errors.Errorf("invalid signature policy: %s", signaturePolicy)
   295  		}
   296  
   297  		applicationPolicy = &pb.ApplicationPolicy{
   298  			Type: &pb.ApplicationPolicy_SignaturePolicy{
   299  				SignaturePolicy: signaturePolicyEnvelope,
   300  			},
   301  		}
   302  	}
   303  
   304  	if channelConfigPolicy != "" {
   305  		applicationPolicy = &pb.ApplicationPolicy{
   306  			Type: &pb.ApplicationPolicy_ChannelConfigPolicyReference{
   307  				ChannelConfigPolicyReference: channelConfigPolicy,
   308  			},
   309  		}
   310  	}
   311  
   312  	return applicationPolicy, nil
   313  }
   314  
   315  func checkChaincodeCmdParams(cmd *cobra.Command) error {
   316  	// we need chaincode name for everything, including deploy
   317  	if chaincodeName == common.UndefinedParamValue {
   318  		return errors.Errorf("must supply value for %s name parameter", chainFuncName)
   319  	}
   320  
   321  	if cmd.Name() == instantiateCmdName || cmd.Name() == installCmdName ||
   322  		cmd.Name() == upgradeCmdName || cmd.Name() == packageCmdName {
   323  		if chaincodeVersion == common.UndefinedParamValue {
   324  			return errors.Errorf("chaincode version is not provided for %s", cmd.Name())
   325  		}
   326  
   327  		if escc != common.UndefinedParamValue {
   328  			logger.Infof("Using escc %s", escc)
   329  		} else {
   330  			logger.Info("Using default escc")
   331  			escc = "escc"
   332  		}
   333  
   334  		if vscc != common.UndefinedParamValue {
   335  			logger.Infof("Using vscc %s", vscc)
   336  		} else {
   337  			logger.Info("Using default vscc")
   338  			vscc = "vscc"
   339  		}
   340  
   341  		if policy != common.UndefinedParamValue {
   342  			p, err := policydsl.FromString(policy)
   343  			if err != nil {
   344  				return errors.Errorf("invalid policy %s", policy)
   345  			}
   346  			policyMarshalled = protoutil.MarshalOrPanic(p)
   347  		}
   348  
   349  		if collectionsConfigFile != common.UndefinedParamValue {
   350  			var err error
   351  			_, collectionConfigBytes, err = GetCollectionConfigFromFile(collectionsConfigFile)
   352  			if err != nil {
   353  				return errors.WithMessagef(err, "invalid collection configuration in file %s", collectionsConfigFile)
   354  			}
   355  		}
   356  	}
   357  
   358  	// Check that non-empty chaincode parameters contain only Args as a key.
   359  	// Type checking is done later when the JSON is actually unmarshaled
   360  	// into a pb.ChaincodeInput. To better understand what's going
   361  	// on here with JSON parsing see http://blog.golang.org/json-and-go -
   362  	// Generic JSON with interface{}
   363  	if chaincodeCtorJSON != "{}" {
   364  		var f interface{}
   365  		err := json.Unmarshal([]byte(chaincodeCtorJSON), &f)
   366  		if err != nil {
   367  			return errors.Wrap(err, "chaincode argument error")
   368  		}
   369  		m := f.(map[string]interface{})
   370  		sm := make(map[string]interface{})
   371  		for k := range m {
   372  			sm[strings.ToLower(k)] = m[k]
   373  		}
   374  		_, argsPresent := sm["args"]
   375  		_, funcPresent := sm["function"]
   376  		if !argsPresent || (len(m) == 2 && !funcPresent) || len(m) > 2 {
   377  			return errors.New("non-empty JSON chaincode parameters must contain the following keys: 'Args' or 'Function' and 'Args'")
   378  		}
   379  	} else {
   380  		if cmd == nil || (cmd != chaincodeInstallCmd && cmd != chaincodePackageCmd) {
   381  			return errors.New("empty JSON chaincode parameters must contain the following keys: 'Args' or 'Function' and 'Args'")
   382  		}
   383  	}
   384  
   385  	return nil
   386  }
   387  
   388  func validatePeerConnectionParameters(cmdName string) error {
   389  	if connectionProfile != common.UndefinedParamValue {
   390  		networkConfig, err := common.GetConfig(connectionProfile)
   391  		if err != nil {
   392  			return err
   393  		}
   394  		if len(networkConfig.Channels[channelID].Peers) != 0 {
   395  			peerAddresses = []string{}
   396  			tlsRootCertFiles = []string{}
   397  			for peer, peerChannelConfig := range networkConfig.Channels[channelID].Peers {
   398  				if peerChannelConfig.EndorsingPeer {
   399  					peerConfig, ok := networkConfig.Peers[peer]
   400  					if !ok {
   401  						return errors.Errorf("peer '%s' is defined in the channel config but doesn't have associated peer config", peer)
   402  					}
   403  					peerAddresses = append(peerAddresses, peerConfig.URL)
   404  					tlsRootCertFiles = append(tlsRootCertFiles, peerConfig.TLSCACerts.Path)
   405  				}
   406  			}
   407  		}
   408  	}
   409  
   410  	// currently only support multiple peer addresses for invoke
   411  	multiplePeersAllowed := map[string]bool{
   412  		"invoke": true,
   413  	}
   414  	_, ok := multiplePeersAllowed[cmdName]
   415  	if !ok && len(peerAddresses) > 1 {
   416  		return errors.Errorf("'%s' command can only be executed against one peer. received %d", cmdName, len(peerAddresses))
   417  	}
   418  
   419  	if len(tlsRootCertFiles) > len(peerAddresses) {
   420  		logger.Warningf("received more TLS root cert files (%d) than peer addresses (%d)", len(tlsRootCertFiles), len(peerAddresses))
   421  	}
   422  
   423  	if viper.GetBool("peer.tls.enabled") {
   424  		if len(tlsRootCertFiles) != len(peerAddresses) {
   425  			return errors.Errorf("number of peer addresses (%d) does not match the number of TLS root cert files (%d)", len(peerAddresses), len(tlsRootCertFiles))
   426  		}
   427  	} else {
   428  		tlsRootCertFiles = nil
   429  	}
   430  
   431  	return nil
   432  }
   433  
   434  // ChaincodeCmdFactory holds the clients used by ChaincodeCmd
   435  type ChaincodeCmdFactory struct {
   436  	EndorserClients []pb.EndorserClient
   437  	DeliverClients  []pb.DeliverClient
   438  	Certificate     tls.Certificate
   439  	Signer          identity.SignerSerializer
   440  	BroadcastClient common.BroadcastClient
   441  }
   442  
   443  // InitCmdFactory init the ChaincodeCmdFactory with default clients
   444  func InitCmdFactory(cmdName string, isEndorserRequired, isOrdererRequired bool, cryptoProvider bccsp.BCCSP) (*ChaincodeCmdFactory, error) {
   445  	var err error
   446  	var endorserClients []pb.EndorserClient
   447  	var deliverClients []pb.DeliverClient
   448  	if isEndorserRequired {
   449  		if err = validatePeerConnectionParameters(cmdName); err != nil {
   450  			return nil, errors.WithMessage(err, "error validating peer connection parameters")
   451  		}
   452  		for i, address := range peerAddresses {
   453  			var tlsRootCertFile string
   454  			if tlsRootCertFiles != nil {
   455  				tlsRootCertFile = tlsRootCertFiles[i]
   456  			}
   457  			endorserClient, err := common.GetEndorserClientFnc(address, tlsRootCertFile)
   458  			if err != nil {
   459  				return nil, errors.WithMessagef(err, "error getting endorser client for %s", cmdName)
   460  			}
   461  			endorserClients = append(endorserClients, endorserClient)
   462  			deliverClient, err := common.GetPeerDeliverClientFnc(address, tlsRootCertFile)
   463  			if err != nil {
   464  				return nil, errors.WithMessagef(err, "error getting deliver client for %s", cmdName)
   465  			}
   466  			deliverClients = append(deliverClients, deliverClient)
   467  		}
   468  		if len(endorserClients) == 0 {
   469  			return nil, errors.New("no endorser clients retrieved - this might indicate a bug")
   470  		}
   471  	}
   472  	certificate, err := common.GetCertificateFnc()
   473  	if err != nil {
   474  		return nil, errors.WithMessage(err, "error getting client certificate")
   475  	}
   476  
   477  	signer, err := common.GetDefaultSignerFnc()
   478  	if err != nil {
   479  		return nil, errors.WithMessage(err, "error getting default signer")
   480  	}
   481  
   482  	var broadcastClient common.BroadcastClient
   483  	if isOrdererRequired {
   484  		if len(common.OrderingEndpoint) == 0 {
   485  			if len(endorserClients) == 0 {
   486  				return nil, errors.New("orderer is required, but no ordering endpoint or endorser client supplied")
   487  			}
   488  			endorserClient := endorserClients[0]
   489  
   490  			orderingEndpoints, err := common.GetOrdererEndpointOfChainFnc(channelID, signer, endorserClient, cryptoProvider)
   491  			if err != nil {
   492  				return nil, errors.WithMessagef(err, "error getting channel (%s) orderer endpoint", channelID)
   493  			}
   494  			if len(orderingEndpoints) == 0 {
   495  				return nil, errors.Errorf("no orderer endpoints retrieved for channel %s", channelID)
   496  			}
   497  			logger.Infof("Retrieved channel (%s) orderer endpoint: %s", channelID, orderingEndpoints[0])
   498  			// override viper env
   499  			viper.Set("orderer.address", orderingEndpoints[0])
   500  		}
   501  
   502  		broadcastClient, err = common.GetBroadcastClientFnc()
   503  
   504  		if err != nil {
   505  			return nil, errors.WithMessage(err, "error getting broadcast client")
   506  		}
   507  	}
   508  	return &ChaincodeCmdFactory{
   509  		EndorserClients: endorserClients,
   510  		DeliverClients:  deliverClients,
   511  		Signer:          signer,
   512  		BroadcastClient: broadcastClient,
   513  		Certificate:     certificate,
   514  	}, nil
   515  }
   516  
   517  // processProposals sends a signed proposal to a set of peers, and gathers all the responses.
   518  func processProposals(endorserClients []pb.EndorserClient, signedProposal *pb.SignedProposal) ([]*pb.ProposalResponse, error) {
   519  	responsesCh := make(chan *pb.ProposalResponse, len(endorserClients))
   520  	errorCh := make(chan error, len(endorserClients))
   521  	wg := sync.WaitGroup{}
   522  	for _, endorser := range endorserClients {
   523  		wg.Add(1)
   524  		go func(endorser pb.EndorserClient) {
   525  			defer wg.Done()
   526  			proposalResp, err := endorser.ProcessProposal(context.Background(), signedProposal)
   527  			if err != nil {
   528  				errorCh <- err
   529  				return
   530  			}
   531  			responsesCh <- proposalResp
   532  		}(endorser)
   533  	}
   534  	wg.Wait()
   535  	close(responsesCh)
   536  	close(errorCh)
   537  	for err := range errorCh {
   538  		return nil, err
   539  	}
   540  	var responses []*pb.ProposalResponse
   541  	for response := range responsesCh {
   542  		responses = append(responses, response)
   543  	}
   544  	return responses, nil
   545  }
   546  
   547  // ChaincodeInvokeOrQuery invokes or queries the chaincode. If successful, the
   548  // INVOKE form prints the ProposalResponse to STDOUT, and the QUERY form prints
   549  // the query result on STDOUT. A command-line flag (-r, --raw) determines
   550  // whether the query result is output as raw bytes, or as a printable string.
   551  // The printable form is optionally (-x, --hex) a hexadecimal representation
   552  // of the query response. If the query response is NIL, nothing is output.
   553  //
   554  // NOTE - Query will likely go away as all interactions with the endorser are
   555  // Proposal and ProposalResponses
   556  func ChaincodeInvokeOrQuery(
   557  	spec *pb.ChaincodeSpec,
   558  	cID string,
   559  	txID string,
   560  	invoke bool,
   561  	signer identity.SignerSerializer,
   562  	certificate tls.Certificate,
   563  	endorserClients []pb.EndorserClient,
   564  	deliverClients []pb.DeliverClient,
   565  	bc common.BroadcastClient,
   566  ) (*pb.ProposalResponse, error) {
   567  	// Build the ChaincodeInvocationSpec message
   568  	invocation := &pb.ChaincodeInvocationSpec{ChaincodeSpec: spec}
   569  
   570  	creator, err := signer.Serialize()
   571  	if err != nil {
   572  		return nil, errors.WithMessage(err, "error serializing identity")
   573  	}
   574  
   575  	funcName := "invoke"
   576  	if !invoke {
   577  		funcName = "query"
   578  	}
   579  
   580  	// extract the transient field if it exists
   581  	var tMap map[string][]byte
   582  	if transient != "" {
   583  		if err := json.Unmarshal([]byte(transient), &tMap); err != nil {
   584  			return nil, errors.Wrap(err, "error parsing transient string")
   585  		}
   586  	}
   587  
   588  	prop, txid, err := protoutil.CreateChaincodeProposalWithTxIDAndTransient(pcommon.HeaderType_ENDORSER_TRANSACTION, cID, invocation, creator, txID, tMap)
   589  	if err != nil {
   590  		return nil, errors.WithMessagef(err, "error creating proposal for %s", funcName)
   591  	}
   592  
   593  	signedProp, err := protoutil.GetSignedProposal(prop, signer)
   594  	if err != nil {
   595  		return nil, errors.WithMessagef(err, "error creating signed proposal for %s", funcName)
   596  	}
   597  
   598  	responses, err := processProposals(endorserClients, signedProp)
   599  	if err != nil {
   600  		return nil, errors.WithMessagef(err, "error endorsing %s", funcName)
   601  	}
   602  
   603  	if len(responses) == 0 {
   604  		// this should only happen if some new code has introduced a bug
   605  		return nil, errors.New("no proposal responses received - this might indicate a bug")
   606  	}
   607  	// all responses will be checked when the signed transaction is created.
   608  	// for now, just set this so we check the first response's status
   609  	proposalResp := responses[0]
   610  
   611  	if invoke {
   612  		if proposalResp != nil {
   613  			if proposalResp.Response.Status >= shim.ERRORTHRESHOLD {
   614  				return proposalResp, nil
   615  			}
   616  			// assemble a signed transaction (it's an Envelope message)
   617  			env, err := protoutil.CreateSignedTx(prop, signer, responses...)
   618  			if err != nil {
   619  				return proposalResp, errors.WithMessage(err, "could not assemble transaction")
   620  			}
   621  			var dg *DeliverGroup
   622  			var ctx context.Context
   623  			if waitForEvent {
   624  				var cancelFunc context.CancelFunc
   625  				ctx, cancelFunc = context.WithTimeout(context.Background(), waitForEventTimeout)
   626  				defer cancelFunc()
   627  
   628  				dg = NewDeliverGroup(
   629  					deliverClients,
   630  					peerAddresses,
   631  					signer,
   632  					certificate,
   633  					channelID,
   634  					txid,
   635  				)
   636  				// connect to deliver service on all peers
   637  				err := dg.Connect(ctx)
   638  				if err != nil {
   639  					return nil, err
   640  				}
   641  			}
   642  
   643  			// send the envelope for ordering
   644  			if err = bc.Send(env); err != nil {
   645  				return proposalResp, errors.WithMessagef(err, "error sending transaction for %s", funcName)
   646  			}
   647  
   648  			if dg != nil && ctx != nil {
   649  				// wait for event that contains the txid from all peers
   650  				err = dg.Wait(ctx)
   651  				if err != nil {
   652  					return nil, err
   653  				}
   654  			}
   655  		}
   656  	}
   657  
   658  	return proposalResp, nil
   659  }
   660  
   661  // DeliverGroup holds all of the information needed to connect
   662  // to a set of peers to wait for the interested txid to be
   663  // committed to the ledgers of all peers. This functionality
   664  // is currently implemented via the peer's DeliverFiltered service.
   665  // An error from any of the peers/deliver clients will result in
   666  // the invoke command returning an error. Only the first error that
   667  // occurs will be set
   668  type DeliverGroup struct {
   669  	Clients     []*DeliverClient
   670  	Certificate tls.Certificate
   671  	ChannelID   string
   672  	TxID        string
   673  	Signer      identity.SignerSerializer
   674  	mutex       sync.Mutex
   675  	Error       error
   676  	wg          sync.WaitGroup
   677  }
   678  
   679  // DeliverClient holds the client/connection related to a specific
   680  // peer. The address is included for logging purposes
   681  type DeliverClient struct {
   682  	Client     pb.DeliverClient
   683  	Connection pb.Deliver_DeliverClient
   684  	Address    string
   685  }
   686  
   687  func NewDeliverGroup(
   688  	deliverClients []pb.DeliverClient,
   689  	peerAddresses []string,
   690  	signer identity.SignerSerializer,
   691  	certificate tls.Certificate,
   692  	channelID string,
   693  	txid string,
   694  ) *DeliverGroup {
   695  	clients := make([]*DeliverClient, len(deliverClients))
   696  	for i, client := range deliverClients {
   697  		dc := &DeliverClient{
   698  			Client:  client,
   699  			Address: peerAddresses[i],
   700  		}
   701  		clients[i] = dc
   702  	}
   703  
   704  	dg := &DeliverGroup{
   705  		Clients:     clients,
   706  		Certificate: certificate,
   707  		ChannelID:   channelID,
   708  		TxID:        txid,
   709  		Signer:      signer,
   710  	}
   711  
   712  	return dg
   713  }
   714  
   715  // Connect waits for all deliver clients in the group to connect to
   716  // the peer's deliver service, receive an error, or for the context
   717  // to timeout. An error will be returned whenever even a single
   718  // deliver client fails to connect to its peer
   719  func (dg *DeliverGroup) Connect(ctx context.Context) error {
   720  	dg.wg.Add(len(dg.Clients))
   721  	for _, client := range dg.Clients {
   722  		go dg.ClientConnect(ctx, client)
   723  	}
   724  	readyCh := make(chan struct{})
   725  	go dg.WaitForWG(readyCh)
   726  
   727  	select {
   728  	case <-readyCh:
   729  		if dg.Error != nil {
   730  			err := errors.WithMessage(dg.Error, "failed to connect to deliver on all peers")
   731  			return err
   732  		}
   733  	case <-ctx.Done():
   734  		err := errors.New("timed out waiting for connection to deliver on all peers")
   735  		return err
   736  	}
   737  
   738  	return nil
   739  }
   740  
   741  // ClientConnect sends a deliver seek info envelope using the
   742  // provided deliver client, setting the deliverGroup's Error
   743  // field upon any error
   744  func (dg *DeliverGroup) ClientConnect(ctx context.Context, dc *DeliverClient) {
   745  	defer dg.wg.Done()
   746  	df, err := dc.Client.DeliverFiltered(ctx)
   747  	if err != nil {
   748  		err = errors.WithMessagef(err, "error connecting to deliver filtered at %s", dc.Address)
   749  		dg.setError(err)
   750  		return
   751  	}
   752  	defer df.CloseSend()
   753  	dc.Connection = df
   754  
   755  	envelope := createDeliverEnvelope(dg.ChannelID, dg.Certificate, dg.Signer)
   756  	err = df.Send(envelope)
   757  	if err != nil {
   758  		err = errors.WithMessagef(err, "error sending deliver seek info envelope to %s", dc.Address)
   759  		dg.setError(err)
   760  		return
   761  	}
   762  }
   763  
   764  // Wait waits for all deliver client connections in the group to
   765  // either receive a block with the txid, an error, or for the
   766  // context to timeout
   767  func (dg *DeliverGroup) Wait(ctx context.Context) error {
   768  	if len(dg.Clients) == 0 {
   769  		return nil
   770  	}
   771  
   772  	dg.wg.Add(len(dg.Clients))
   773  	for _, client := range dg.Clients {
   774  		go dg.ClientWait(client)
   775  	}
   776  	readyCh := make(chan struct{})
   777  	go dg.WaitForWG(readyCh)
   778  
   779  	select {
   780  	case <-readyCh:
   781  		if dg.Error != nil {
   782  			return dg.Error
   783  		}
   784  	case <-ctx.Done():
   785  		err := errors.New("timed out waiting for txid on all peers")
   786  		return err
   787  	}
   788  
   789  	return nil
   790  }
   791  
   792  // ClientWait waits for the specified deliver client to receive
   793  // a block event with the requested txid
   794  func (dg *DeliverGroup) ClientWait(dc *DeliverClient) {
   795  	defer dg.wg.Done()
   796  	for {
   797  		resp, err := dc.Connection.Recv()
   798  		if err != nil {
   799  			err = errors.WithMessagef(err, "error receiving from deliver filtered at %s", dc.Address)
   800  			dg.setError(err)
   801  			return
   802  		}
   803  		switch r := resp.Type.(type) {
   804  		case *pb.DeliverResponse_FilteredBlock:
   805  			filteredTransactions := r.FilteredBlock.FilteredTransactions
   806  			for _, tx := range filteredTransactions {
   807  				if tx.Txid == dg.TxID {
   808  					logger.Infof("txid [%s] committed with status (%s) at %s", dg.TxID, tx.TxValidationCode, dc.Address)
   809  					if tx.TxValidationCode != pb.TxValidationCode_VALID {
   810  						err = errors.Errorf("transaction invalidated with status (%s)", tx.TxValidationCode)
   811  						dg.setError(err)
   812  					}
   813  					return
   814  				}
   815  			}
   816  		case *pb.DeliverResponse_Status:
   817  			err = errors.Errorf("deliver completed with status (%s) before txid received", r.Status)
   818  			dg.setError(err)
   819  			return
   820  		default:
   821  			err = errors.Errorf("received unexpected response type (%T) from %s", r, dc.Address)
   822  			dg.setError(err)
   823  			return
   824  		}
   825  	}
   826  }
   827  
   828  // WaitForWG waits for the deliverGroup's wait group and closes
   829  // the channel when ready
   830  func (dg *DeliverGroup) WaitForWG(readyCh chan struct{}) {
   831  	dg.wg.Wait()
   832  	close(readyCh)
   833  }
   834  
   835  // setError serializes an error for the deliverGroup
   836  func (dg *DeliverGroup) setError(err error) {
   837  	dg.mutex.Lock()
   838  	dg.Error = err
   839  	dg.mutex.Unlock()
   840  }
   841  
   842  func createDeliverEnvelope(
   843  	channelID string,
   844  	certificate tls.Certificate,
   845  	signer identity.SignerSerializer,
   846  ) *pcommon.Envelope {
   847  	var tlsCertHash []byte
   848  	// check for client certificate and create hash if present
   849  	if len(certificate.Certificate) > 0 {
   850  		tlsCertHash = util.ComputeSHA256(certificate.Certificate[0])
   851  	}
   852  
   853  	start := &ab.SeekPosition{
   854  		Type: &ab.SeekPosition_Newest{
   855  			Newest: &ab.SeekNewest{},
   856  		},
   857  	}
   858  
   859  	stop := &ab.SeekPosition{
   860  		Type: &ab.SeekPosition_Specified{
   861  			Specified: &ab.SeekSpecified{
   862  				Number: math.MaxUint64,
   863  			},
   864  		},
   865  	}
   866  
   867  	seekInfo := &ab.SeekInfo{
   868  		Start:    start,
   869  		Stop:     stop,
   870  		Behavior: ab.SeekInfo_BLOCK_UNTIL_READY,
   871  	}
   872  
   873  	env, err := protoutil.CreateSignedEnvelopeWithTLSBinding(
   874  		pcommon.HeaderType_DELIVER_SEEK_INFO,
   875  		channelID,
   876  		signer,
   877  		seekInfo,
   878  		int32(0),
   879  		uint64(0),
   880  		tlsCertHash,
   881  	)
   882  	if err != nil {
   883  		logger.Errorf("Error signing envelope: %s", err)
   884  		return nil
   885  	}
   886  
   887  	return env
   888  }