github.com/myafeier/fabric@v1.0.1-0.20170722181825-3a4b1f2bce86/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  	//the inner dep spec will contain the type
   189  	cds, err := putils.GetChaincodeDeploymentSpec(cis.ChaincodeSpec.Input.Args[argNo])
   190  	if err != nil {
   191  		return err
   192  	}
   193  
   194  	//finally, if JAVA error out
   195  	if cds.ChaincodeSpec.Type == pb.ChaincodeSpec_JAVA {
   196  		return fmt.Errorf("Java chaincode is work-in-progress and disabled")
   197  	}
   198  
   199  	//not a java install, instantiate or upgrade op
   200  	return nil
   201  }
   202  
   203  //simulate the proposal by calling the chaincode
   204  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) {
   205  	endorserLogger.Debugf("Entry - txid: %s channel id: %s", txid, chainID)
   206  	defer endorserLogger.Debugf("Exit")
   207  	//we do expect the payload to be a ChaincodeInvocationSpec
   208  	//if we are supporting other payloads in future, this be glaringly point
   209  	//as something that should change
   210  	cis, err := putils.GetChaincodeInvocationSpec(prop)
   211  	if err != nil {
   212  		return nil, nil, nil, nil, err
   213  	}
   214  
   215  	//disable Java install,instantiate,upgrade for now
   216  	if err = e.disableJavaCCInst(cid, cis); err != nil {
   217  		return nil, nil, nil, nil, err
   218  	}
   219  
   220  	//---1. check ESCC and VSCC for the chaincode
   221  	if err = e.checkEsccAndVscc(prop); err != nil {
   222  		return nil, nil, nil, nil, err
   223  	}
   224  
   225  	var cdLedger *ccprovider.ChaincodeData
   226  	var version string
   227  
   228  	if !syscc.IsSysCC(cid.Name) {
   229  		cdLedger, err = e.getCDSFromLSCC(ctx, chainID, txid, signedProp, prop, cid.Name, txsim)
   230  		if err != nil {
   231  			return nil, nil, nil, nil, fmt.Errorf("%s - make sure the chaincode %s has been successfully instantiated and try again", err, cid.Name)
   232  		}
   233  		version = cdLedger.Version
   234  
   235  		err = ccprovider.CheckInsantiationPolicy(cid.Name, version, cdLedger)
   236  		if err != nil {
   237  			return nil, nil, nil, nil, err
   238  		}
   239  	} else {
   240  		version = util.GetSysCCVersion()
   241  	}
   242  
   243  	//---3. execute the proposal and get simulation results
   244  	var simResult []byte
   245  	var res *pb.Response
   246  	var ccevent *pb.ChaincodeEvent
   247  	res, ccevent, err = e.callChaincode(ctx, chainID, version, txid, signedProp, prop, cis, cid, txsim)
   248  	if err != nil {
   249  		endorserLogger.Errorf("failed to invoke chaincode %s on transaction %s, error: %s", cid, txid, err)
   250  		return nil, nil, nil, nil, err
   251  	}
   252  
   253  	if txsim != nil {
   254  		if simResult, err = txsim.GetTxSimulationResults(); err != nil {
   255  			return nil, nil, nil, nil, err
   256  		}
   257  	}
   258  
   259  	return cdLedger, res, simResult, ccevent, nil
   260  }
   261  
   262  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) {
   263  	ctxt := ctx
   264  	if txsim != nil {
   265  		ctxt = context.WithValue(ctx, chaincode.TXSimulatorKey, txsim)
   266  	}
   267  
   268  	return chaincode.GetChaincodeDataFromLSCC(ctxt, txid, signedProp, prop, chainID, chaincodeID)
   269  }
   270  
   271  //endorse the proposal by calling the ESCC
   272  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) {
   273  	endorserLogger.Debugf("Entry - txid: %s channel id: %s chaincode id: %s", txid, chainID, ccid)
   274  	defer endorserLogger.Debugf("Exit")
   275  
   276  	isSysCC := cd == nil
   277  	// 1) extract the name of the escc that is requested to endorse this chaincode
   278  	var escc string
   279  	//ie, not "lscc" or system chaincodes
   280  	if isSysCC {
   281  		// FIXME: getCDSFromLSCC seems to fail for lscc - not sure this is expected?
   282  		// TODO: who should endorse a call to LSCC?
   283  		escc = "escc"
   284  	} else {
   285  		escc = cd.Escc
   286  		if escc == "" { // this should never happen, LSCC always fills this field
   287  			panic("No ESCC specified in ChaincodeData")
   288  		}
   289  	}
   290  
   291  	endorserLogger.Debugf("info: escc for chaincode id %s is %s", ccid, escc)
   292  
   293  	// marshalling event bytes
   294  	var err error
   295  	var eventBytes []byte
   296  	if event != nil {
   297  		eventBytes, err = putils.GetBytesChaincodeEvent(event)
   298  		if err != nil {
   299  			return nil, fmt.Errorf("failed to marshal event bytes - %s", err)
   300  		}
   301  	}
   302  
   303  	resBytes, err := putils.GetBytesResponse(response)
   304  	if err != nil {
   305  		return nil, fmt.Errorf("failed to marshal response bytes - %s", err)
   306  	}
   307  
   308  	// set version of executing chaincode
   309  	if isSysCC {
   310  		// if we want to allow mixed fabric levels we should
   311  		// set syscc version to ""
   312  		ccid.Version = util.GetSysCCVersion()
   313  	} else {
   314  		ccid.Version = cd.Version
   315  	}
   316  
   317  	ccidBytes, err := putils.Marshal(ccid)
   318  	if err != nil {
   319  		return nil, fmt.Errorf("failed to marshal ChaincodeID - %s", err)
   320  	}
   321  
   322  	// 3) call the ESCC we've identified
   323  	// arguments:
   324  	// args[0] - function name (not used now)
   325  	// args[1] - serialized Header object
   326  	// args[2] - serialized ChaincodeProposalPayload object
   327  	// args[3] - ChaincodeID of executing chaincode
   328  	// args[4] - result of executing chaincode
   329  	// args[5] - binary blob of simulation results
   330  	// args[6] - serialized events
   331  	// args[7] - payloadVisibility
   332  	args := [][]byte{[]byte(""), proposal.Header, proposal.Payload, ccidBytes, resBytes, simRes, eventBytes, visibility}
   333  	version := util.GetSysCCVersion()
   334  	ecccis := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, ChaincodeId: &pb.ChaincodeID{Name: escc}, Input: &pb.ChaincodeInput{Args: args}}}
   335  	res, _, err := e.callChaincode(ctx, chainID, version, txid, signedProp, proposal, ecccis, &pb.ChaincodeID{Name: escc}, txsim)
   336  	if err != nil {
   337  		return nil, err
   338  	}
   339  
   340  	if res.Status >= shim.ERRORTHRESHOLD {
   341  		return &pb.ProposalResponse{Response: res}, nil
   342  	}
   343  
   344  	prBytes := res.Payload
   345  	// Note that we do not extract any simulation results from
   346  	// the call to ESCC. This is intentional becuse ESCC is meant
   347  	// to endorse (i.e. sign) the simulation results of a chaincode,
   348  	// but it can't obviously sign its own. Furthermore, ESCC runs
   349  	// on private input (its own signing key) and so if it were to
   350  	// produce simulationr results, they are likely to be different
   351  	// from other ESCCs, which would stand in the way of the
   352  	// endorsement process.
   353  
   354  	//3 -- respond
   355  	pResp, err := putils.GetProposalResponse(prBytes)
   356  	if err != nil {
   357  		return nil, err
   358  	}
   359  
   360  	return pResp, nil
   361  }
   362  
   363  // ProcessProposal process the Proposal
   364  func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedProposal) (*pb.ProposalResponse, error) {
   365  	endorserLogger.Debugf("Entry")
   366  	defer endorserLogger.Debugf("Exit")
   367  	// at first, we check whether the message is valid
   368  	prop, hdr, hdrExt, err := validation.ValidateProposalMessage(signedProp)
   369  	if err != nil {
   370  		return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   371  	}
   372  
   373  	chdr, err := putils.UnmarshalChannelHeader(hdr.ChannelHeader)
   374  	if err != nil {
   375  		return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   376  	}
   377  
   378  	shdr, err := putils.GetSignatureHeader(hdr.SignatureHeader)
   379  	if err != nil {
   380  		return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   381  	}
   382  
   383  	// block invocations to security-sensitive system chaincodes
   384  	if syscc.IsSysCCAndNotInvokableExternal(hdrExt.ChaincodeId.Name) {
   385  		endorserLogger.Errorf("Error: an attempt was made by %#v to invoke system chaincode %s",
   386  			shdr.Creator, hdrExt.ChaincodeId.Name)
   387  		err = fmt.Errorf("Chaincode %s cannot be invoked through a proposal", hdrExt.ChaincodeId.Name)
   388  		return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   389  	}
   390  
   391  	chainID := chdr.ChannelId
   392  
   393  	// Check for uniqueness of prop.TxID with ledger
   394  	// Notice that ValidateProposalMessage has already verified
   395  	// that TxID is computed properly
   396  	txid := chdr.TxId
   397  	if txid == "" {
   398  		err = errors.New("Invalid txID. It must be different from the empty string.")
   399  		return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   400  	}
   401  	endorserLogger.Debugf("processing txid: %s", txid)
   402  	if chainID != "" {
   403  		// here we handle uniqueness check and ACLs for proposals targeting a chain
   404  		lgr := peer.GetLedger(chainID)
   405  		if lgr == nil {
   406  			return nil, fmt.Errorf("failure while looking up the ledger %s", chainID)
   407  		}
   408  		if _, err := lgr.GetTransactionByID(txid); err == nil {
   409  			return nil, fmt.Errorf("Duplicate transaction found [%s]. Creator [%x]. [%s]", txid, shdr.Creator, err)
   410  		}
   411  
   412  		// check ACL only for application chaincodes; ACLs
   413  		// for system chaincodes are checked elsewhere
   414  		if !syscc.IsSysCC(hdrExt.ChaincodeId.Name) {
   415  			// check that the proposal complies with the channel's writers
   416  			if err = e.checkACL(signedProp, chdr, shdr, hdrExt); err != nil {
   417  				return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   418  			}
   419  		}
   420  	} else {
   421  		// chainless proposals do not/cannot affect ledger and cannot be submitted as transactions
   422  		// ignore uniqueness checks; also, chainless proposals are not validated using the policies
   423  		// of the chain since by definition there is no chain; they are validated against the local
   424  		// MSP of the peer instead by the call to ValidateProposalMessage above
   425  	}
   426  
   427  	// obtaining once the tx simulator for this proposal. This will be nil
   428  	// for chainless proposals
   429  	// Also obtain a history query executor for history queries, since tx simulator does not cover history
   430  	var txsim ledger.TxSimulator
   431  	var historyQueryExecutor ledger.HistoryQueryExecutor
   432  	if chainID != "" {
   433  		if txsim, err = e.getTxSimulator(chainID); err != nil {
   434  			return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   435  		}
   436  		if historyQueryExecutor, err = e.getHistoryQueryExecutor(chainID); err != nil {
   437  			return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   438  		}
   439  		// Add the historyQueryExecutor to context
   440  		// TODO shouldn't we also add txsim to context here as well? Rather than passing txsim parameter
   441  		// around separately, since eventually it gets added to context anyways
   442  		ctx = context.WithValue(ctx, chaincode.HistoryQueryExecutorKey, historyQueryExecutor)
   443  
   444  		defer txsim.Done()
   445  	}
   446  	//this could be a request to a chainless SysCC
   447  
   448  	// TODO: if the proposal has an extension, it will be of type ChaincodeAction;
   449  	//       if it's present it means that no simulation is to be performed because
   450  	//       we're trying to emulate a submitting peer. On the other hand, we need
   451  	//       to validate the supplied action before endorsing it
   452  
   453  	//1 -- simulate
   454  	cd, res, simulationResult, ccevent, err := e.simulateProposal(ctx, chainID, txid, signedProp, prop, hdrExt.ChaincodeId, txsim)
   455  	if err != nil {
   456  		return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   457  	}
   458  	if res != nil {
   459  		if res.Status >= shim.ERROR {
   460  			endorserLogger.Errorf("simulateProposal() resulted in chaincode response status %d for txid: %s", res.Status, txid)
   461  			var cceventBytes []byte
   462  			if ccevent != nil {
   463  				cceventBytes, err = putils.GetBytesChaincodeEvent(ccevent)
   464  				if err != nil {
   465  					return nil, fmt.Errorf("failed to marshal event bytes - %s", err)
   466  				}
   467  			}
   468  			pResp, err := putils.CreateProposalResponseFailure(prop.Header, prop.Payload, res, simulationResult, cceventBytes, hdrExt.ChaincodeId, hdrExt.PayloadVisibility)
   469  			if err != nil {
   470  				return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   471  			}
   472  
   473  			return pResp, &chaincodeError{res.Status, res.Message}
   474  		}
   475  	}
   476  
   477  	//2 -- endorse and get a marshalled ProposalResponse message
   478  	var pResp *pb.ProposalResponse
   479  
   480  	//TODO till we implement global ESCC, CSCC for system chaincodes
   481  	//chainless proposals (such as CSCC) don't have to be endorsed
   482  	if chainID == "" {
   483  		pResp = &pb.ProposalResponse{Response: res}
   484  	} else {
   485  		pResp, err = e.endorseProposal(ctx, chainID, txid, signedProp, prop, res, simulationResult, ccevent, hdrExt.PayloadVisibility, hdrExt.ChaincodeId, txsim, cd)
   486  		if err != nil {
   487  			return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
   488  		}
   489  		if pResp != nil {
   490  			if res.Status >= shim.ERRORTHRESHOLD {
   491  				endorserLogger.Debugf("endorseProposal() resulted in chaincode error for txid: %s", txid)
   492  				return pResp, &chaincodeError{res.Status, res.Message}
   493  			}
   494  		}
   495  	}
   496  
   497  	// Set the proposal response payload - it
   498  	// contains the "return value" from the
   499  	// chaincode invocation
   500  	pResp.Response.Payload = res.Payload
   501  
   502  	return pResp, nil
   503  }
   504  
   505  // Only exposed for testing purposes - commit the tx simulation so that
   506  // a deploy transaction is persisted and that chaincode can be invoked.
   507  // This makes the endorser test self-sufficient
   508  func (e *Endorser) commitTxSimulation(proposal *pb.Proposal, chainID string, signer msp.SigningIdentity, pResp *pb.ProposalResponse, blockNumber uint64) error {
   509  	tx, err := putils.CreateSignedTx(proposal, signer, pResp)
   510  	if err != nil {
   511  		return err
   512  	}
   513  
   514  	lgr := peer.GetLedger(chainID)
   515  	if lgr == nil {
   516  		return fmt.Errorf("failure while looking up the ledger")
   517  	}
   518  
   519  	txBytes, err := proto.Marshal(tx)
   520  	if err != nil {
   521  		return err
   522  	}
   523  	block := common.NewBlock(blockNumber, []byte{})
   524  	block.Data.Data = [][]byte{txBytes}
   525  	block.Header.DataHash = block.Data.Hash()
   526  	if err = lgr.Commit(block); err != nil {
   527  		return err
   528  	}
   529  
   530  	return nil
   531  }