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