code: func (cc *Client) InvokeHandler(handler invoke.Handler, request Request, options ...RequestOption) (Response, error) {
  	//Read execute tx options
  	txnOpts, err := cc.prepareOptsFromOptions(cc.context, options...)
  	if err != nil {
  		return Response{}, err
  	}
  
  	reqCtx, cancel := cc.createReqContext(&txnOpts)
  	defer cancel()
  
  	//Prepare context objects for handler
  	requestContext, clientContext, err := cc.prepareHandlerContexts(reqCtx, request, txnOpts)
  	if err != nil {
  		return Response{}, err
  	}
  
  	invoker := retry.NewInvoker(
  		requestContext.RetryHandler,
  		retry.WithBeforeRetry(
  			func(err error) {
  				if requestContext.Opts.BeforeRetry != nil {
  					requestContext.Opts.BeforeRetry(err)
  				}
  
  				cc.greylist.Greylist(err)
  
  				// Reset context parameters
  				requestContext.Opts.Targets = txnOpts.Targets
  				requestContext.Error = nil
  				requestContext.Response = invoke.Response{}
  			},
  		),
  	)
  
  	complete := make(chan bool, 1)
  	go func() {
  		_, _ = invoker.Invoke( // nolint: gas
  			func() (interface{}, error) {
  				handler.Handle(requestContext, clientContext)
  				return nil, requestContext.Error
  			})
  		complete <- true
  	}()
  	select {
  	case <-complete:
  		return Response(requestContext.Response), requestContext.Error
  	case <-reqCtx.Done():
  		return Response{}, status.New(status.ClientStatus, status.Timeout.ToInt32(),
  			"request timed out or been cancelled", nil)
  	}
  }
docs: // InvokeHandler invokes handler using request and optional request options provided
  //  Parameters:
  //  handler to be invoked
  //  request holds info about mandatory chaincode ID and function
  //  options holds optional request options
  //
  //  Returns:
  //  the proposal responses from peer(s)