github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/core/endorser/endorser.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package endorser
     8  
     9  import (
    10  	"fmt"
    11  
    12  	"github.com/golang/protobuf/proto"
    13  	"github.com/hyperledger/fabric/common/flogging"
    14  	"golang.org/x/net/context"
    15  
    16  	"errors"
    17  
    18  	"github.com/hyperledger/fabric/common/policies"
    19  	"github.com/hyperledger/fabric/common/util"
    20  	"github.com/hyperledger/fabric/core/chaincode"
    21  	"github.com/hyperledger/fabric/core/chaincode/shim"
    22  	"github.com/hyperledger/fabric/core/common/ccprovider"
    23  	"github.com/hyperledger/fabric/core/common/validation"
    24  	"github.com/hyperledger/fabric/core/ledger"
    25  	"github.com/hyperledger/fabric/core/peer"
    26  	"github.com/hyperledger/fabric/core/policy"
    27  	syscc "github.com/hyperledger/fabric/core/scc"
    28  	"github.com/hyperledger/fabric/msp"
    29  	"github.com/hyperledger/fabric/msp/mgmt"
    30  	"github.com/hyperledger/fabric/protos/common"
    31  	pb "github.com/hyperledger/fabric/protos/peer"
    32  	putils "github.com/hyperledger/fabric/protos/utils"
    33  )
    34  
    35  // >>>>> begin errors section >>>>>
    36  //chaincodeError is a fabric error signifying error from chaincode
    37  type chaincodeError struct {
    38  	status int32
    39  	msg    string
    40  }
    41  
    42  func (ce chaincodeError) Error() string {
    43  	return fmt.Sprintf("chaincode error (status: %d, message: %s)", ce.status, ce.msg)
    44  }
    45  
    46  // <<<<< end errors section <<<<<<
    47  
    48  var endorserLogger = flogging.MustGetLogger("endorser")
    49  
    50  // The Jira issue that documents Endorser flow along with its relationship to
    51  // the lifecycle chaincode - https://jira.hyperledger.org/browse/FAB-181
    52  
    53  // Endorser provides the Endorser service ProcessProposal
    54  type Endorser struct {
    55  	policyChecker policy.PolicyChecker
    56  }
    57  
    58  // NewEndorserServer creates and returns a new Endorser server instance.
    59  func NewEndorserServer() pb.EndorserServer {
    60  	e := new(Endorser)
    61  	e.policyChecker = policy.NewPolicyChecker(
    62  		peer.NewChannelPolicyManagerGetter(),
    63  		mgmt.GetLocalMSP(),
    64  		mgmt.NewLocalMSPPrincipalGetter(),
    65  	)
    66  
    67  	return e
    68  }
    69  
    70  // checkACL checks that the supplied proposal complies
    71  // with the writers policy of the chain
    72  func (e *Endorser) checkACL(signedProp *pb.SignedProposal, chdr *common.ChannelHeader, shdr *common.SignatureHeader, hdrext *pb.ChaincodeHeaderExtension) error {
    73  	return e.policyChecker.CheckPolicy(chdr.ChannelId, policies.ChannelApplicationWriters, signedProp)
    74  }
    75  
    76  //TODO - check for escc and vscc
    77  func (*Endorser) checkEsccAndVscc(prop *pb.Proposal) error {
    78  	return nil
    79  }
    80  
    81  func (*Endorser) getTxSimulator(ledgername string) (ledger.TxSimulator, error) {
    82  	lgr := peer.GetLedger(ledgername)
    83  	if lgr == nil {
    84  		return nil, fmt.Errorf("channel does not exist: %s", ledgername)
    85  	}
    86  	return lgr.NewTxSimulator()
    87  }
    88  
    89  func (*Endorser) getHistoryQueryExecutor(ledgername string) (ledger.HistoryQueryExecutor, error) {
    90  	lgr := peer.GetLedger(ledgername)
    91  	if lgr == nil {
    92  		return nil, fmt.Errorf("channel does not exist: %s", ledgername)
    93  	}
    94  	return lgr.NewHistoryQueryExecutor()
    95  }
    96  
    97  //call specified chaincode (system or user)
    98  func (e *Endorser) callChaincode(ctxt context.Context, chainID string, version string, txid string, signedProp *pb.SignedProposal, prop *pb.Proposal, cis *pb.ChaincodeInvocationSpec, cid *pb.ChaincodeID, txsim ledger.TxSimulator) (*pb.Response, *pb.ChaincodeEvent, error) {
    99  	endorserLogger.Debugf("Entry - txid: %s channel id: %s version: %s", txid, chainID, version)
   100  	defer endorserLogger.Debugf("Exit")
   101  	var err error
   102  	var res *pb.Response
   103  	var ccevent *pb.ChaincodeEvent
   104  
   105  	if txsim != nil {
   106  		ctxt = context.WithValue(ctxt, chaincode.TXSimulatorKey, txsim)
   107  	}
   108  
   109  	//is this a system chaincode
   110  	scc := syscc.IsSysCC(cid.Name)
   111  
   112  	cccid := ccprovider.NewCCContext(chainID, cid.Name, version, txid, scc, signedProp, prop)
   113  
   114  	res, ccevent, err = chaincode.ExecuteChaincode(ctxt, cccid, cis.ChaincodeSpec.Input.Args)
   115  
   116  	if err != nil {
   117  		return nil, nil, err
   118  	}
   119  
   120  	//per doc anything < 400 can be sent as TX.
   121  	//fabric errors will always be >= 400 (ie, unambiguous errors )
   122  	//"lscc" will respond with status 200 or 500 (ie, unambiguous OK or ERROR)
   123  	if res.Status >= shim.ERRORTHRESHOLD {
   124  		return res, nil, nil
   125  	}
   126  
   127  	//----- BEGIN -  SECTION THAT MAY NEED TO BE DONE IN LSCC ------
   128  	//if this a call to deploy a chaincode, We need a mechanism
   129  	//to pass TxSimulator into LSCC. Till that is worked out this
   130  	//special code does the actual deploy, upgrade here so as to collect
   131  	//all state under one TxSimulator
   132  	//
   133  	//NOTE that if there's an error all simulation, including the chaincode
   134  	//table changes in lscc will be thrown away
   135  	if cid.Name == "lscc" && len(cis.ChaincodeSpec.Input.Args) >= 3 && (string(cis.ChaincodeSpec.Input.Args[0]) == "deploy" || string(cis.ChaincodeSpec.Input.Args[0]) == "upgrade") {
   136  		var cds *pb.ChaincodeDeploymentSpec
   137  		cds, err = putils.GetChaincodeDeploymentSpec(cis.ChaincodeSpec.Input.Args[2])
   138  		if err != nil {
   139  			return nil, nil, err
   140  		}
   141  
   142  		//this should not be a system chaincode
   143  		if syscc.IsSysCC(cds.ChaincodeSpec.ChaincodeId.Name) {
   144  			return nil, nil, fmt.Errorf("attempting to deploy a system chaincode %s/%s", cds.ChaincodeSpec.ChaincodeId.Name, chainID)
   145  		}
   146  
   147  		cccid = ccprovider.NewCCContext(chainID, cds.ChaincodeSpec.ChaincodeId.Name, cds.ChaincodeSpec.ChaincodeId.Version, txid, false, signedProp, prop)
   148  
   149  		_, _, err = chaincode.Execute(ctxt, cccid, cds)
   150  		if err != nil {
   151  			return nil, nil, fmt.Errorf("%s", err)
   152  		}
   153  	}
   154  	//----- END -------
   155  
   156  	return res, ccevent, err
   157  }
   158  
   159  //TO BE REMOVED WHEN JAVA CC IS ENABLED
   160  //disableJavaCCInst if trying to install, instantiate or upgrade Java CC
   161  func (e *Endorser) disableJavaCCInst(cid *pb.ChaincodeID, cis *pb.ChaincodeInvocationSpec) error {
   162  	//if not lscc we don't care
   163  	if cid.Name != "lscc" {
   164  		return nil
   165  	}
   166  
   167  	//non-nil spec ? leave it to callers to handle error if this is an error
   168  	if cis.ChaincodeSpec == nil || cis.ChaincodeSpec.Input == nil {
   169  		return nil
   170  	}
   171  
   172  	//should at least have a command arg, leave it to callers if this is an error
   173  	if len(cis.ChaincodeSpec.Input.Args) < 1 {
   174  		return nil
   175  	}
   176  
   177  	var argNo int
   178  	switch string(cis.ChaincodeSpec.Input.Args[0]) {
   179  	case "install":
   180  		argNo = 1
   181  	case "deploy", "upgrade":
   182  		argNo = 2
   183  	default:
   184  		//what else can it be ? leave it caller to handle it if error
   185  		return nil
   186  	}
   187  
   188  	if argNo >= len(cis.ChaincodeSpec.Input.Args) {
   189  		return errors.New("Too few arguments passed")
   190  	}
   191  
   192  	//the inner dep spec will contain the type
   193  	ccpack, err := ccprovider.GetCCPackage(cis.ChaincodeSpec.Input.Args[argNo])
   194  	if err != nil {
   195  		return err
   196  	}
   197  
   198  	cds := ccpack.GetDepSpec()
   199  
   200  	//finally, if JAVA error out
   201  	if cds.ChaincodeSpec.Type == pb.ChaincodeSpec_JAVA {
   202  		return fmt.Errorf("Java chaincode is work-in-progress and disabled")
   203  	}
   204  
   205  	//not a java install, instantiate or upgrade op
   206  	return nil
   207  }
   208  
   209  //simulate the proposal by calling the chaincode
   210  func (e *Endorser) simulateProposal(ctx context.Context, chainID string, txid string, signedProp *pb.SignedProposal, prop *pb.Proposal, cid *pb.ChaincodeID, txsim ledger.TxSimulator) (*ccprovider.ChaincodeData, *pb.Response, []byte, *pb.ChaincodeEvent, error) {
   211  	endorserLogger.Debugf("Entry - txid: %s channel id: %s", txid, chainID)
   212  	defer endorserLogger.Debugf("Exit")
   213  	//we do expect the payload to be a ChaincodeInvocationSpec
   214  	//if we are supporting other payloads in future, this be glaringly point
   215  	//as something that should change
   216  	cis, err := putils.GetChaincodeInvocationSpec(prop)
   217  	if err != nil {
   218  		return nil, nil, nil, nil, err
   219  	}
   220  
   221  	//disable Java install,instantiate,upgrade for now
   222  	if err = e.disableJavaCCInst(cid, cis); err != nil {
   223  		return nil, nil, nil, nil, err
   224  	}
   225  
   226  	//---1. check ESCC and VSCC for the chaincode
   227  	if err = e.checkEsccAndVscc(prop); err != nil {
   228  		return nil, nil, nil, nil, err
   229  	}
   230  
   231  	var cdLedger *ccprovider.ChaincodeData
   232  	var version string
   233  
   234  	if !syscc.IsSysCC(cid.Name) {
   235  		cdLedger, err = e.getCDSFromLSCC(ctx, chainID, txid, signedProp, prop, cid.Name, txsim)
   236  		if err != nil {
   237  			return nil, nil, nil, nil, fmt.Errorf("%s - make sure the chaincode %s has been successfully instantiated and try again", err, cid.Name)
   238  		}
   239  		version = cdLedger.Version
   240  
   241  		err = ccprovider.CheckInsantiationPolicy(cid.Name, version, cdLedger)
   242  		if err != nil {
   243  			return nil, nil, nil, nil, err
   244  		}
   245  	} else {
   246  		version = util.GetSysCCVersion()
   247  	}
   248  
   249  	//---3. execute the proposal and get simulation results
   250  	var simResult []byte
   251  	var res *pb.Response
   252  	var ccevent *pb.ChaincodeEvent
   253  	res, ccevent, err = e.callChaincode(ctx, chainID, version, txid, signedProp, prop, cis, cid, txsim)
   254  	if err != nil {
   255  		endorserLogger.Errorf("failed to invoke chaincode %s on transaction %s, error: %s", cid, txid, err)
   256  		return nil, nil, nil, nil, err
   257  	}
   258  
   259  	if txsim != nil {
   260  		if simResult, err = txsim.GetTxSimulationResults(); err != nil {
   261  			return nil, nil, nil, nil, err
   262  		}
   263  	}
   264  
   265  	return cdLedger, res, simResult, ccevent, nil
   266  }
   267  
   268  func (e *Endorser) getCDSFromLSCC(ctx context.Context, chainID string, txid string, signedProp *pb.SignedProposal, prop *pb.Proposal, chaincodeID string, txsim ledger.TxSimulator) (*ccprovider.ChaincodeData, error) {
   269  	ctxt := ctx
   270  	if txsim != nil {
   271  		ctxt = context.WithValue(ctx, chaincode.TXSimulatorKey, txsim)
   272  	}
   273  
   274  	return chaincode.GetChaincodeDataFromLSCC(ctxt, txid, signedProp, prop, chainID, chaincodeID)
   275  }
   276  
   277  //endorse the proposal by calling the ESCC
   278  func (e *Endorser) endorseProposal(ctx context.Context, chainID string, txid string, signedProp *pb.SignedProposal, proposal *pb.Proposal, response *pb.Response, simRes []byte, event *pb.ChaincodeEvent, visibility []byte, ccid *pb.ChaincodeID, txsim ledger.TxSimulator, cd *ccprovider.ChaincodeData) (*pb.ProposalResponse, error) {
   279  	endorserLogger.Debugf("Entry - txid: %s channel id: %s chaincode id: %s", txid, chainID, ccid)
   280  	defer endorserLogger.Debugf("Exit")
   281  
   282  	isSysCC := cd == nil
   283  	// 1) extract the name of the escc that is requested to endorse this chaincode
   284  	var escc string
   285  	//ie, not "lscc" or system chaincodes
   286  	if isSysCC {
   287  		// FIXME: getCDSFromLSCC seems to fail for lscc - not sure this is expected?
   288  		// TODO: who should endorse a call to LSCC?
   289  		escc = "escc"
   290  	} else {
   291  		escc = cd.Escc
   292  		if escc == "" { // this should never happen, LSCC always fills this field
   293  			panic("No ESCC specified in ChaincodeData")
   294  		}
   295  	}
   296  
   297  	endorserLogger.Debugf("info: escc for chaincode id %s is %s", ccid, escc)
   298  
   299  	// marshalling event bytes
   300  	var err error
   301  	var eventBytes []byte
   302  	if event != nil {
   303  		eventBytes, err = putils.GetBytesChaincodeEvent(event)
   304  		if err != nil {
   305  			return nil, fmt.Errorf("failed to marshal event bytes - %s", err)
   306  		}
   307  	}
   308  
   309  	resBytes, err := putils.GetBytesResponse(response)
   310  	if err != nil {
   311  		return nil, fmt.Errorf("failed to marshal response bytes - %s", err)
   312  	}
   313  
   314  	// set version of executing chaincode
   315  	if isSysCC {
   316  		// if we want to allow mixed fabric levels we should
   317  		// set syscc version to ""
   318  		ccid.Version = util.GetSysCCVersion()
   319  	} else {
   320  		ccid.Version = cd.Version
   321  	}
   322  
   323  	ccidBytes, err := putils.Marshal(ccid)
   324  	if err != nil {
   325  		return nil, fmt.Errorf("failed to marshal ChaincodeID - %s", err)
   326  	}
   327  
   328  	// 3) call the ESCC we've identified
   329  	// arguments:
   330  	// args[0] - function name (not used now)
   331  	// args[1] - serialized Header object
   332  	// args[2] - serialized ChaincodeProposalPayload object
   333  	// args[3] - ChaincodeID of executing chaincode
   334  	// args[4] - result of executing chaincode
   335  	// args[5] - binary blob of simulation results
   336  	// args[6] - serialized events
   337  	// args[7] - payloadVisibility
   338  	args := [][]byte{[]byte(""), proposal.Header, proposal.Payload, ccidBytes, resBytes, simRes, eventBytes, visibility}
   339  	version := util.GetSysCCVersion()
   340  	ecccis := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, ChaincodeId: &pb.ChaincodeID{Name: escc}, Input: &pb.ChaincodeInput{Args: args}}}
   341  	res, _, err := e.callChaincode(ctx, chainID, version, txid, signedProp, proposal, ecccis, &pb.ChaincodeID{Name: escc}, txsim)
   342  	if err != nil {
   343  		return nil, err
   344  	}
   345  
   346  	if res.Status >= shim.ERRORTHRESHOLD {
   347  		return &pb.ProposalResponse{Response: res}, nil
   348  	}
   349  
   350  	prBytes := res.Payload
   351  	// Note that we do not extract any simulation results from
   352  	// the call to ESCC. This is intentional becuse ESCC is meant
   353  	// to endorse (i.e. sign) the simulation results of a chaincode,
   354  	// but it can't obviously sign its own. Furthermore, ESCC runs
   355  	// on private input (its own signing key) and so if it were to
   356  	// produce simulationr results, they are likely to be different
   357  	// from other ESCCs, which would stand in the way of the
   358  	// endorsement process.
   359  
   360  	//3 -- respond
   361  	pResp, err := putils.GetProposalResponse(prBytes)
   362  	if err != nil {
   363  		return nil, err
   364  	}
   365  
   366  	return pResp, nil
   367  }
   368  
   369  // ProcessProposal process the Proposal
   370  func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedProposal) (*pb.ProposalResponse, error) {
   371  	endorserLogger.Debugf("Entry")
   372  	defer endorserLogger.Debugf("Exit")
   373  	// at first, we check whether the message is valid
   374  	prop, hdr, hdrExt, err := validation.ValidateProposalMessage(signedProp)
   375  	if err != nil {
   376  		return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   377  	}
   378  
   379  	chdr, err := putils.UnmarshalChannelHeader(hdr.ChannelHeader)
   380  	if err != nil {
   381  		return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   382  	}
   383  
   384  	shdr, err := putils.GetSignatureHeader(hdr.SignatureHeader)
   385  	if err != nil {
   386  		return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   387  	}
   388  
   389  	// block invocations to security-sensitive system chaincodes
   390  	if syscc.IsSysCCAndNotInvokableExternal(hdrExt.ChaincodeId.Name) {
   391  		endorserLogger.Errorf("Error: an attempt was made by %#v to invoke system chaincode %s",
   392  			shdr.Creator, hdrExt.ChaincodeId.Name)
   393  		err = fmt.Errorf("Chaincode %s cannot be invoked through a proposal", hdrExt.ChaincodeId.Name)
   394  		return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   395  	}
   396  
   397  	chainID := chdr.ChannelId
   398  
   399  	// Check for uniqueness of prop.TxID with ledger
   400  	// Notice that ValidateProposalMessage has already verified
   401  	// that TxID is computed properly
   402  	txid := chdr.TxId
   403  	if txid == "" {
   404  		err = errors.New("Invalid txID. It must be different from the empty string.")
   405  		return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   406  	}
   407  	endorserLogger.Debugf("processing txid: %s", txid)
   408  	if chainID != "" {
   409  		// here we handle uniqueness check and ACLs for proposals targeting a chain
   410  		lgr := peer.GetLedger(chainID)
   411  		if lgr == nil {
   412  			return nil, fmt.Errorf("failure while looking up the ledger %s", chainID)
   413  		}
   414  		if _, err := lgr.GetTransactionByID(txid); err == nil {
   415  			return nil, fmt.Errorf("Duplicate transaction found [%s]. Creator [%x]. [%s]", txid, shdr.Creator, err)
   416  		}
   417  
   418  		// check ACL only for application chaincodes; ACLs
   419  		// for system chaincodes are checked elsewhere
   420  		if !syscc.IsSysCC(hdrExt.ChaincodeId.Name) {
   421  			// check that the proposal complies with the channel's writers
   422  			if err = e.checkACL(signedProp, chdr, shdr, hdrExt); err != nil {
   423  				return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   424  			}
   425  		}
   426  	} else {
   427  		// chainless proposals do not/cannot affect ledger and cannot be submitted as transactions
   428  		// ignore uniqueness checks; also, chainless proposals are not validated using the policies
   429  		// of the chain since by definition there is no chain; they are validated against the local
   430  		// MSP of the peer instead by the call to ValidateProposalMessage above
   431  	}
   432  
   433  	// obtaining once the tx simulator for this proposal. This will be nil
   434  	// for chainless proposals
   435  	// Also obtain a history query executor for history queries, since tx simulator does not cover history
   436  	var txsim ledger.TxSimulator
   437  	var historyQueryExecutor ledger.HistoryQueryExecutor
   438  	if chainID != "" {
   439  		if txsim, err = e.getTxSimulator(chainID); err != nil {
   440  			return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   441  		}
   442  		if historyQueryExecutor, err = e.getHistoryQueryExecutor(chainID); err != nil {
   443  			return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   444  		}
   445  		// Add the historyQueryExecutor to context
   446  		// TODO shouldn't we also add txsim to context here as well? Rather than passing txsim parameter
   447  		// around separately, since eventually it gets added to context anyways
   448  		ctx = context.WithValue(ctx, chaincode.HistoryQueryExecutorKey, historyQueryExecutor)
   449  
   450  		defer txsim.Done()
   451  	}
   452  	//this could be a request to a chainless SysCC
   453  
   454  	// TODO: if the proposal has an extension, it will be of type ChaincodeAction;
   455  	//       if it's present it means that no simulation is to be performed because
   456  	//       we're trying to emulate a submitting peer. On the other hand, we need
   457  	//       to validate the supplied action before endorsing it
   458  
   459  	//1 -- simulate
   460  	cd, res, simulationResult, ccevent, err := e.simulateProposal(ctx, chainID, txid, signedProp, prop, hdrExt.ChaincodeId, txsim)
   461  	if err != nil {
   462  		return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   463  	}
   464  	if res != nil {
   465  		if res.Status >= shim.ERROR {
   466  			endorserLogger.Errorf("simulateProposal() resulted in chaincode response status %d for txid: %s", res.Status, txid)
   467  			var cceventBytes []byte
   468  			if ccevent != nil {
   469  				cceventBytes, err = putils.GetBytesChaincodeEvent(ccevent)
   470  				if err != nil {
   471  					return nil, fmt.Errorf("failed to marshal event bytes - %s", err)
   472  				}
   473  			}
   474  			pResp, err := putils.CreateProposalResponseFailure(prop.Header, prop.Payload, res, simulationResult, cceventBytes, hdrExt.ChaincodeId, hdrExt.PayloadVisibility)
   475  			if err != nil {
   476  				return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   477  			}
   478  
   479  			return pResp, &chaincodeError{res.Status, res.Message}
   480  		}
   481  	}
   482  
   483  	//2 -- endorse and get a marshalled ProposalResponse message
   484  	var pResp *pb.ProposalResponse
   485  
   486  	//TODO till we implement global ESCC, CSCC for system chaincodes
   487  	//chainless proposals (such as CSCC) don't have to be endorsed
   488  	if chainID == "" {
   489  		pResp = &pb.ProposalResponse{Response: res}
   490  	} else {
   491  		pResp, err = e.endorseProposal(ctx, chainID, txid, signedProp, prop, res, simulationResult, ccevent, hdrExt.PayloadVisibility, hdrExt.ChaincodeId, txsim, cd)
   492  		if err != nil {
   493  			return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   494  		}
   495  		if pResp != nil {
   496  			if res.Status >= shim.ERRORTHRESHOLD {
   497  				endorserLogger.Debugf("endorseProposal() resulted in chaincode error for txid: %s", txid)
   498  				return pResp, &chaincodeError{res.Status, res.Message}
   499  			}
   500  		}
   501  	}
   502  
   503  	// Set the proposal response payload - it
   504  	// contains the "return value" from the
   505  	// chaincode invocation
   506  	pResp.Response.Payload = res.Payload
   507  
   508  	return pResp, nil
   509  }
   510  
   511  // Only exposed for testing purposes - commit the tx simulation so that
   512  // a deploy transaction is persisted and that chaincode can be invoked.
   513  // This makes the endorser test self-sufficient
   514  func (e *Endorser) commitTxSimulation(proposal *pb.Proposal, chainID string, signer msp.SigningIdentity, pResp *pb.ProposalResponse, blockNumber uint64) error {
   515  	tx, err := putils.CreateSignedTx(proposal, signer, pResp)
   516  	if err != nil {
   517  		return err
   518  	}
   519  
   520  	lgr := peer.GetLedger(chainID)
   521  	if lgr == nil {
   522  		return fmt.Errorf("failure while looking up the ledger")
   523  	}
   524  
   525  	txBytes, err := proto.Marshal(tx)
   526  	if err != nil {
   527  		return err
   528  	}
   529  	block := common.NewBlock(blockNumber, []byte{})
   530  	block.Data.Data = [][]byte{txBytes}
   531  	block.Header.DataHash = block.Data.Hash()
   532  	if err = lgr.Commit(block); err != nil {
   533  		return err
   534  	}
   535  
   536  	return nil
   537  }