github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/endorser/endorser_test.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package endorser_test
     8  
     9  import (
    10  	"context"
    11  	"fmt"
    12  	"sort"
    13  
    14  	"github.com/golang/protobuf/proto"
    15  	"github.com/hechain20/hechain/common/metrics/metricsfakes"
    16  	"github.com/hechain20/hechain/core/chaincode/lifecycle"
    17  	"github.com/hechain20/hechain/core/endorser"
    18  	"github.com/hechain20/hechain/core/endorser/fake"
    19  	"github.com/hechain20/hechain/core/ledger"
    20  	ledgermock "github.com/hechain20/hechain/core/ledger/mock"
    21  	"github.com/hechain20/hechain/protoutil"
    22  	cb "github.com/hyperledger/fabric-protos-go/common"
    23  	"github.com/hyperledger/fabric-protos-go/ledger/rwset"
    24  	"github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset"
    25  	mspproto "github.com/hyperledger/fabric-protos-go/msp"
    26  	pb "github.com/hyperledger/fabric-protos-go/peer"
    27  	. "github.com/onsi/ginkgo"
    28  	. "github.com/onsi/gomega"
    29  	"github.com/pkg/errors"
    30  )
    31  
    32  type CcInterest pb.ChaincodeInterest
    33  
    34  func (a CcInterest) Len() int { return len(a.Chaincodes) }
    35  func (a CcInterest) Swap(i, j int) {
    36  	a.Chaincodes[i], a.Chaincodes[j] = a.Chaincodes[j], a.Chaincodes[i]
    37  }
    38  
    39  func (a CcInterest) Less(i, j int) bool {
    40  	ai := a.Chaincodes[i]
    41  	aj := a.Chaincodes[j]
    42  
    43  	if ai.Name != aj.Name {
    44  		return ai.Name < aj.Name
    45  	}
    46  
    47  	if len(ai.CollectionNames) != len(aj.CollectionNames) {
    48  		return len(ai.CollectionNames) < len(aj.CollectionNames)
    49  	}
    50  
    51  	for ii := range ai.CollectionNames {
    52  		if ai.CollectionNames[ii] != aj.CollectionNames[ii] {
    53  			return ai.CollectionNames[ii] < aj.CollectionNames[ii]
    54  		}
    55  	}
    56  
    57  	return false
    58  }
    59  
    60  var _ = Describe("Endorser", func() {
    61  	var (
    62  		fakeProposalDuration         *metricsfakes.Histogram
    63  		fakeProposalsReceived        *metricsfakes.Counter
    64  		fakeSuccessfulProposals      *metricsfakes.Counter
    65  		fakeProposalValidationFailed *metricsfakes.Counter
    66  		fakeProposalACLCheckFailed   *metricsfakes.Counter
    67  		fakeInitFailed               *metricsfakes.Counter
    68  		fakeEndorsementsFailed       *metricsfakes.Counter
    69  		fakeDuplicateTxsFailure      *metricsfakes.Counter
    70  		fakeSimulateFailure          *metricsfakes.Counter
    71  
    72  		fakeLocalIdentity                *fake.Identity
    73  		fakeLocalMSPIdentityDeserializer *fake.IdentityDeserializer
    74  
    75  		fakeChannelIdentity                *fake.Identity
    76  		fakeChannelMSPIdentityDeserializer *fake.IdentityDeserializer
    77  
    78  		fakeChannelFetcher *fake.ChannelFetcher
    79  
    80  		fakePrivateDataDistributor *fake.PrivateDataDistributor
    81  
    82  		fakeSupport              *fake.Support
    83  		fakeTxSimulator          *fake.TxSimulator
    84  		fakeHistoryQueryExecutor *fake.HistoryQueryExecutor
    85  
    86  		signedProposal *pb.SignedProposal
    87  		channelID      string
    88  		chaincodeName  string
    89  
    90  		chaincodeResponse *pb.Response
    91  		chaincodeEvent    *pb.ChaincodeEvent
    92  		chaincodeInput    *pb.ChaincodeInput
    93  
    94  		e *endorser.Endorser
    95  	)
    96  
    97  	BeforeEach(func() {
    98  		fakeProposalDuration = &metricsfakes.Histogram{}
    99  		fakeProposalDuration.WithReturns(fakeProposalDuration)
   100  
   101  		fakeProposalACLCheckFailed = &metricsfakes.Counter{}
   102  		fakeProposalACLCheckFailed.WithReturns(fakeProposalACLCheckFailed)
   103  
   104  		fakeInitFailed = &metricsfakes.Counter{}
   105  		fakeInitFailed.WithReturns(fakeInitFailed)
   106  
   107  		fakeEndorsementsFailed = &metricsfakes.Counter{}
   108  		fakeEndorsementsFailed.WithReturns(fakeEndorsementsFailed)
   109  
   110  		fakeDuplicateTxsFailure = &metricsfakes.Counter{}
   111  		fakeDuplicateTxsFailure.WithReturns(fakeDuplicateTxsFailure)
   112  
   113  		fakeProposalsReceived = &metricsfakes.Counter{}
   114  		fakeSuccessfulProposals = &metricsfakes.Counter{}
   115  		fakeProposalValidationFailed = &metricsfakes.Counter{}
   116  
   117  		fakeSimulateFailure = &metricsfakes.Counter{}
   118  		fakeSimulateFailure.WithReturns(fakeSimulateFailure)
   119  
   120  		fakeLocalIdentity = &fake.Identity{}
   121  		fakeLocalMSPIdentityDeserializer = &fake.IdentityDeserializer{}
   122  		fakeLocalMSPIdentityDeserializer.DeserializeIdentityReturns(fakeLocalIdentity, nil)
   123  
   124  		fakeChannelIdentity = &fake.Identity{}
   125  		fakeChannelMSPIdentityDeserializer = &fake.IdentityDeserializer{}
   126  		fakeChannelMSPIdentityDeserializer.DeserializeIdentityReturns(fakeChannelIdentity, nil)
   127  
   128  		fakeChannelFetcher = &fake.ChannelFetcher{}
   129  		fakeChannelFetcher.ChannelReturns(&endorser.Channel{
   130  			IdentityDeserializer: fakeChannelMSPIdentityDeserializer,
   131  		})
   132  
   133  		fakePrivateDataDistributor = &fake.PrivateDataDistributor{}
   134  
   135  		channelID = "channel-id"
   136  		chaincodeName = "chaincode-name"
   137  		chaincodeInput = &pb.ChaincodeInput{
   138  			Args: [][]byte{[]byte("arg1"), []byte("arg2"), []byte("arg3")},
   139  		}
   140  
   141  		chaincodeResponse = &pb.Response{
   142  			Status:  200,
   143  			Payload: []byte("response-payload"),
   144  		}
   145  		chaincodeEvent = &pb.ChaincodeEvent{
   146  			ChaincodeId: "chaincode-id",
   147  			TxId:        "event-txid",
   148  			EventName:   "event-name",
   149  			Payload:     []byte("event-payload"),
   150  		}
   151  
   152  		fakeSupport = &fake.Support{}
   153  		fakeSupport.ExecuteReturns(
   154  			chaincodeResponse,
   155  			chaincodeEvent,
   156  			nil,
   157  		)
   158  
   159  		fakeSupport.ChaincodeEndorsementInfoReturns(&lifecycle.ChaincodeEndorsementInfo{
   160  			Version:           "chaincode-definition-version",
   161  			EndorsementPlugin: "plugin-name",
   162  		}, nil)
   163  
   164  		fakeSupport.GetLedgerHeightReturns(7, nil)
   165  
   166  		fakeSupport.EndorseWithPluginReturns(
   167  			&pb.Endorsement{
   168  				Endorser:  []byte("endorser-identity"),
   169  				Signature: []byte("endorser-signature"),
   170  			},
   171  			[]byte("endorser-modified-payload"),
   172  			nil,
   173  		)
   174  
   175  		fakeTxSimulator = &fake.TxSimulator{}
   176  		fakeTxSimulator.GetTxSimulationResultsReturns(
   177  			&ledger.TxSimulationResults{
   178  				PubSimulationResults: &rwset.TxReadWriteSet{},
   179  				PvtSimulationResults: &rwset.TxPvtReadWriteSet{},
   180  			},
   181  			nil,
   182  		)
   183  
   184  		fakeHistoryQueryExecutor = &fake.HistoryQueryExecutor{}
   185  		fakeSupport.GetHistoryQueryExecutorReturns(fakeHistoryQueryExecutor, nil)
   186  
   187  		fakeSupport.GetTxSimulatorReturns(fakeTxSimulator, nil)
   188  
   189  		fakeSupport.GetTransactionByIDReturns(nil, fmt.Errorf("txid-error"))
   190  
   191  		e = &endorser.Endorser{
   192  			LocalMSP:               fakeLocalMSPIdentityDeserializer,
   193  			PrivateDataDistributor: fakePrivateDataDistributor,
   194  			Metrics: &endorser.Metrics{
   195  				ProposalDuration:         fakeProposalDuration,
   196  				ProposalsReceived:        fakeProposalsReceived,
   197  				SuccessfulProposals:      fakeSuccessfulProposals,
   198  				ProposalValidationFailed: fakeProposalValidationFailed,
   199  				ProposalACLCheckFailed:   fakeProposalACLCheckFailed,
   200  				InitFailed:               fakeInitFailed,
   201  				EndorsementsFailed:       fakeEndorsementsFailed,
   202  				DuplicateTxsFailure:      fakeDuplicateTxsFailure,
   203  				SimulationFailure:        fakeSimulateFailure,
   204  			},
   205  			Support:        fakeSupport,
   206  			ChannelFetcher: fakeChannelFetcher,
   207  		}
   208  	})
   209  
   210  	JustBeforeEach(func() {
   211  		signedProposal = &pb.SignedProposal{
   212  			ProposalBytes: protoutil.MarshalOrPanic(&pb.Proposal{
   213  				Header: protoutil.MarshalOrPanic(&cb.Header{
   214  					ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{
   215  						Type:      int32(cb.HeaderType_ENDORSER_TRANSACTION),
   216  						ChannelId: channelID,
   217  						Extension: protoutil.MarshalOrPanic(&pb.ChaincodeHeaderExtension{
   218  							ChaincodeId: &pb.ChaincodeID{
   219  								Name: chaincodeName,
   220  							},
   221  						}),
   222  						TxId: "6f142589e4ef6a1e62c9c816e2074f70baa9f7cf67c2f0c287d4ef907d6d2015",
   223  					}),
   224  					SignatureHeader: protoutil.MarshalOrPanic(&cb.SignatureHeader{
   225  						Creator: protoutil.MarshalOrPanic(&mspproto.SerializedIdentity{
   226  							Mspid: "msp-id",
   227  						}),
   228  						Nonce: []byte("nonce"),
   229  					}),
   230  				}),
   231  				Payload: protoutil.MarshalOrPanic(&pb.ChaincodeProposalPayload{
   232  					Input: protoutil.MarshalOrPanic(&pb.ChaincodeInvocationSpec{
   233  						ChaincodeSpec: &pb.ChaincodeSpec{
   234  							Input: chaincodeInput,
   235  						},
   236  					}),
   237  				}),
   238  			}),
   239  			Signature: []byte("signature"),
   240  		}
   241  	})
   242  
   243  	It("successfully endorses the proposal", func() {
   244  		proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal)
   245  		Expect(err).NotTo(HaveOccurred())
   246  		Expect(proposalResponse.Endorsement).To(Equal(&pb.Endorsement{
   247  			Endorser:  []byte("endorser-identity"),
   248  			Signature: []byte("endorser-signature"),
   249  		}))
   250  		Expect(proposalResponse.Timestamp).To(BeNil())
   251  		Expect(proposalResponse.Version).To(Equal(int32(1)))
   252  		Expect(proposalResponse.Payload).To(Equal([]byte("endorser-modified-payload")))
   253  		Expect(proto.Equal(proposalResponse.Response, &pb.Response{
   254  			Status:  200,
   255  			Payload: []byte("response-payload"),
   256  		})).To(BeTrue())
   257  
   258  		Expect(fakeSupport.EndorseWithPluginCallCount()).To(Equal(1))
   259  		pluginName, cid, propRespPayloadBytes, sp := fakeSupport.EndorseWithPluginArgsForCall(0)
   260  		Expect(sp).To(Equal(signedProposal))
   261  		Expect(pluginName).To(Equal("plugin-name"))
   262  		Expect(cid).To(Equal("channel-id"))
   263  
   264  		prp := &pb.ProposalResponsePayload{}
   265  		err = proto.Unmarshal(propRespPayloadBytes, prp)
   266  		Expect(err).NotTo(HaveOccurred())
   267  		Expect(fmt.Sprintf("%x", prp.ProposalHash)).To(Equal("6fa450b00ebef6c7de9f3479148f6d6ff2c645762e17fcaae989ff7b668be001"))
   268  
   269  		ccAct := &pb.ChaincodeAction{}
   270  		err = proto.Unmarshal(prp.Extension, ccAct)
   271  		Expect(err).NotTo(HaveOccurred())
   272  		Expect(ccAct.Events).To(Equal(protoutil.MarshalOrPanic(chaincodeEvent)))
   273  		Expect(proto.Equal(ccAct.Response, &pb.Response{
   274  			Status:  200,
   275  			Payload: []byte("response-payload"),
   276  		})).To(BeTrue())
   277  		Expect(fakeSupport.GetHistoryQueryExecutorCallCount()).To(Equal(1))
   278  		ledgerName := fakeSupport.GetHistoryQueryExecutorArgsForCall(0)
   279  		Expect(ledgerName).To(Equal("channel-id"))
   280  	})
   281  
   282  	Context("when the chaincode endorsement fails", func() {
   283  		BeforeEach(func() {
   284  			fakeSupport.EndorseWithPluginReturns(nil, nil, fmt.Errorf("fake-endorserment-error"))
   285  		})
   286  
   287  		It("returns the error, but with no payload encoded", func() {
   288  			proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal)
   289  			Expect(err).NotTo(HaveOccurred())
   290  			Expect(proposalResponse.Payload).To(BeNil())
   291  			Expect(proposalResponse.Response).To(Equal(&pb.Response{
   292  				Status:  500,
   293  				Message: "endorsing with plugin failed: fake-endorserment-error",
   294  			}))
   295  		})
   296  	})
   297  
   298  	It("checks for duplicate transactions", func() {
   299  		_, err := e.ProcessProposal(context.Background(), signedProposal)
   300  		Expect(err).NotTo(HaveOccurred())
   301  		Expect(fakeSupport.GetTransactionByIDCallCount()).To(Equal(1))
   302  		channelID, txid := fakeSupport.GetTransactionByIDArgsForCall(0)
   303  		Expect(channelID).To(Equal("channel-id"))
   304  		Expect(txid).To(Equal("6f142589e4ef6a1e62c9c816e2074f70baa9f7cf67c2f0c287d4ef907d6d2015"))
   305  	})
   306  
   307  	Context("when the txid is duplicated", func() {
   308  		BeforeEach(func() {
   309  			fakeSupport.GetTransactionByIDReturns(nil, nil)
   310  		})
   311  
   312  		It("wraps and returns an error and responds to the client", func() {
   313  			proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal)
   314  			Expect(err).To(MatchError("duplicate transaction found [6f142589e4ef6a1e62c9c816e2074f70baa9f7cf67c2f0c287d4ef907d6d2015]. Creator [0a066d73702d6964]"))
   315  			Expect(proposalResponse).To(Equal(&pb.ProposalResponse{
   316  				Response: &pb.Response{
   317  					Status:  500,
   318  					Message: "duplicate transaction found [6f142589e4ef6a1e62c9c816e2074f70baa9f7cf67c2f0c287d4ef907d6d2015]. Creator [0a066d73702d6964]",
   319  				},
   320  			}))
   321  		})
   322  	})
   323  
   324  	It("gets a transaction simulator", func() {
   325  		_, err := e.ProcessProposal(context.Background(), signedProposal)
   326  		Expect(err).NotTo(HaveOccurred())
   327  		Expect(fakeSupport.GetTxSimulatorCallCount()).To(Equal(1))
   328  		ledgerName, txid := fakeSupport.GetTxSimulatorArgsForCall(0)
   329  		Expect(ledgerName).To(Equal("channel-id"))
   330  		Expect(txid).To(Equal("6f142589e4ef6a1e62c9c816e2074f70baa9f7cf67c2f0c287d4ef907d6d2015"))
   331  	})
   332  
   333  	Context("when getting the tx simulator fails", func() {
   334  		BeforeEach(func() {
   335  			fakeSupport.GetTxSimulatorReturns(nil, fmt.Errorf("fake-simulator-error"))
   336  		})
   337  
   338  		It("returns a response with the error", func() {
   339  			proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal)
   340  			Expect(err).NotTo(HaveOccurred())
   341  			Expect(proposalResponse.Payload).To(BeNil())
   342  			Expect(proposalResponse.Response).To(Equal(&pb.Response{
   343  				Status:  500,
   344  				Message: "fake-simulator-error",
   345  			}))
   346  		})
   347  	})
   348  
   349  	It("gets a history query executor", func() {
   350  		_, err := e.ProcessProposal(context.Background(), signedProposal)
   351  		Expect(err).NotTo(HaveOccurred())
   352  		Expect(fakeSupport.GetHistoryQueryExecutorCallCount()).To(Equal(1))
   353  		ledgerName := fakeSupport.GetHistoryQueryExecutorArgsForCall(0)
   354  		Expect(ledgerName).To(Equal("channel-id"))
   355  	})
   356  
   357  	Context("when getting the history query executor fails", func() {
   358  		BeforeEach(func() {
   359  			fakeSupport.GetHistoryQueryExecutorReturns(nil, fmt.Errorf("fake-history-error"))
   360  		})
   361  
   362  		It("returns a response with the error", func() {
   363  			proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal)
   364  			Expect(err).NotTo(HaveOccurred())
   365  			Expect(proposalResponse.Payload).To(BeNil())
   366  			Expect(proposalResponse.Response).To(Equal(&pb.Response{
   367  				Status:  500,
   368  				Message: "fake-history-error",
   369  			}))
   370  		})
   371  	})
   372  
   373  	It("gets the channel context", func() {
   374  		_, err := e.ProcessProposal(context.Background(), signedProposal)
   375  		Expect(err).NotTo(HaveOccurred())
   376  		Expect(fakeChannelFetcher.ChannelCallCount()).To(Equal(1))
   377  		channelID := fakeChannelFetcher.ChannelArgsForCall(0)
   378  		Expect(channelID).To(Equal("channel-id"))
   379  	})
   380  
   381  	Context("when the channel context cannot be retrieved", func() {
   382  		BeforeEach(func() {
   383  			fakeChannelFetcher.ChannelReturns(nil)
   384  		})
   385  
   386  		It("returns a response with the error", func() {
   387  			proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal)
   388  			Expect(err).NotTo(HaveOccurred())
   389  			Expect(proposalResponse.Payload).To(BeNil())
   390  			Expect(proposalResponse.Response).To(Equal(&pb.Response{
   391  				Status:  500,
   392  				Message: "channel 'channel-id' not found",
   393  			}))
   394  		})
   395  	})
   396  
   397  	It("checks the submitter's identity", func() {
   398  		_, err := e.ProcessProposal(context.Background(), signedProposal)
   399  		Expect(err).NotTo(HaveOccurred())
   400  		Expect(fakeChannelMSPIdentityDeserializer.DeserializeIdentityCallCount()).To(Equal(1))
   401  		identity := fakeChannelMSPIdentityDeserializer.DeserializeIdentityArgsForCall(0)
   402  		Expect(identity).To(Equal(protoutil.MarshalOrPanic(&mspproto.SerializedIdentity{
   403  			Mspid: "msp-id",
   404  		})))
   405  
   406  		Expect(fakeLocalMSPIdentityDeserializer.DeserializeIdentityCallCount()).To(Equal(0))
   407  	})
   408  
   409  	Context("when the proposal is not validly signed", func() {
   410  		BeforeEach(func() {
   411  			fakeChannelMSPIdentityDeserializer.DeserializeIdentityReturns(nil, fmt.Errorf("fake-deserialize-error"))
   412  		})
   413  
   414  		It("wraps and returns an error and responds to the client", func() {
   415  			proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal)
   416  			Expect(err).To(MatchError("error validating proposal: access denied: channel [channel-id] creator org unknown, creator is malformed"))
   417  			Expect(proposalResponse).To(Equal(&pb.ProposalResponse{
   418  				Response: &pb.Response{
   419  					Status:  500,
   420  					Message: "error validating proposal: access denied: channel [channel-id] creator org unknown, creator is malformed",
   421  				},
   422  			}))
   423  		})
   424  	})
   425  
   426  	It("checks the ACLs for the identity", func() {
   427  		_, err := e.ProcessProposal(context.Background(), signedProposal)
   428  		Expect(err).NotTo(HaveOccurred())
   429  		Expect(fakeProposalACLCheckFailed.WithCallCount()).To(Equal(0))
   430  		Expect(fakeInitFailed.WithCallCount()).To(Equal(0))
   431  		Expect(fakeEndorsementsFailed.WithCallCount()).To(Equal(0))
   432  		Expect(fakeDuplicateTxsFailure.WithCallCount()).To(Equal(0))
   433  	})
   434  
   435  	Context("when the acl check fails", func() {
   436  		BeforeEach(func() {
   437  			fakeSupport.CheckACLReturns(fmt.Errorf("fake-acl-error"))
   438  		})
   439  
   440  		It("wraps and returns an error and responds to the client", func() {
   441  			proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal)
   442  			Expect(err).To(MatchError("fake-acl-error"))
   443  			Expect(proposalResponse).To(Equal(&pb.ProposalResponse{
   444  				Response: &pb.Response{
   445  					Status:  500,
   446  					Message: "fake-acl-error",
   447  				},
   448  			}))
   449  		})
   450  
   451  		Context("when it's for a system chaincode", func() {
   452  			BeforeEach(func() {
   453  				fakeSupport.IsSysCCReturns(true)
   454  			})
   455  
   456  			It("skips the acl check", func() {
   457  				proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal)
   458  				Expect(err).NotTo(HaveOccurred())
   459  				Expect(proposalResponse.Response.Status).To(Equal(int32(200)))
   460  			})
   461  		})
   462  	})
   463  
   464  	It("gets the chaincode definition", func() {
   465  		_, err := e.ProcessProposal(context.Background(), signedProposal)
   466  		Expect(err).NotTo(HaveOccurred())
   467  		Expect(fakeSupport.ChaincodeEndorsementInfoCallCount()).To(Equal(1))
   468  		channelID, chaincodeName, txSim := fakeSupport.ChaincodeEndorsementInfoArgsForCall(0)
   469  		Expect(channelID).To(Equal("channel-id"))
   470  		Expect(chaincodeName).To(Equal("chaincode-name"))
   471  		Expect(txSim).To(Equal(fakeTxSimulator))
   472  	})
   473  
   474  	Context("when the chaincode definition is not found", func() {
   475  		BeforeEach(func() {
   476  			fakeSupport.ChaincodeEndorsementInfoReturns(nil, fmt.Errorf("fake-definition-error"))
   477  		})
   478  
   479  		It("returns an error in the response", func() {
   480  			proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal)
   481  			Expect(err).NotTo(HaveOccurred())
   482  			Expect(proposalResponse.Response).To(Equal(&pb.Response{
   483  				Status:  500,
   484  				Message: "make sure the chaincode chaincode-name has been successfully defined on channel channel-id and try again: fake-definition-error",
   485  			}))
   486  		})
   487  	})
   488  
   489  	It("calls the chaincode", func() {
   490  		_, err := e.ProcessProposal(context.Background(), signedProposal)
   491  		Expect(err).NotTo(HaveOccurred())
   492  		Expect(fakeSupport.ExecuteCallCount()).To(Equal(1))
   493  		txParams, chaincodeName, input := fakeSupport.ExecuteArgsForCall(0)
   494  		Expect(txParams.ChannelID).To(Equal("channel-id"))
   495  		Expect(txParams.SignedProp).To(Equal(signedProposal))
   496  		Expect(txParams.TXSimulator).To(Equal(fakeTxSimulator))
   497  		Expect(txParams.HistoryQueryExecutor).To(Equal(fakeHistoryQueryExecutor))
   498  		Expect(chaincodeName).To(Equal("chaincode-name"))
   499  		Expect(proto.Equal(input, &pb.ChaincodeInput{
   500  			Args: [][]byte{[]byte("arg1"), []byte("arg2"), []byte("arg3")},
   501  		})).To(BeTrue())
   502  	})
   503  
   504  	Context("when calling the chaincode returns an error", func() {
   505  		BeforeEach(func() {
   506  			fakeSupport.ExecuteReturns(nil, nil, fmt.Errorf("fake-chaincode-execution-error"))
   507  		})
   508  
   509  		It("returns a response with the error and no payload", func() {
   510  			proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal)
   511  			Expect(err).NotTo(HaveOccurred())
   512  			Expect(proposalResponse.Payload).To(BeNil())
   513  			Expect(proposalResponse.Response).To(Equal(&pb.Response{
   514  				Status:  500,
   515  				Message: "error in simulation: fake-chaincode-execution-error",
   516  			}))
   517  			Expect(fakeSimulateFailure.AddCallCount()).To(Equal(1))
   518  		})
   519  	})
   520  
   521  	It("distributes private data", func() {
   522  		_, err := e.ProcessProposal(context.Background(), signedProposal)
   523  		Expect(err).NotTo(HaveOccurred())
   524  		Expect(fakePrivateDataDistributor.DistributePrivateDataCallCount()).To(Equal(1))
   525  		cid, txid, privateData, blkHt := fakePrivateDataDistributor.DistributePrivateDataArgsForCall(0)
   526  		Expect(cid).To(Equal("channel-id"))
   527  		Expect(txid).To(Equal("6f142589e4ef6a1e62c9c816e2074f70baa9f7cf67c2f0c287d4ef907d6d2015"))
   528  		Expect(blkHt).To(Equal(uint64(7)))
   529  
   530  		// TODO, this deserves a better test, but there was none before and this logic,
   531  		// really seems far too jumbled to be in the endorser package.  There are separate
   532  		// tests of the private data assembly functions in their test file.
   533  		Expect(privateData).NotTo(BeNil())
   534  		Expect(privateData.EndorsedAt).To(Equal(uint64(7)))
   535  	})
   536  
   537  	Context("when the private data cannot be distributed", func() {
   538  		BeforeEach(func() {
   539  			fakePrivateDataDistributor.DistributePrivateDataReturns(fmt.Errorf("fake-private-data-error"))
   540  		})
   541  
   542  		It("returns a response with the error and no payload", func() {
   543  			proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal)
   544  			Expect(err).NotTo(HaveOccurred())
   545  			Expect(proposalResponse.Payload).To(BeNil())
   546  			Expect(proposalResponse.Response).To(Equal(&pb.Response{
   547  				Status:  500,
   548  				Message: "error in simulation: fake-private-data-error",
   549  			}))
   550  			Expect(fakeSimulateFailure.AddCallCount()).To(Equal(1))
   551  		})
   552  	})
   553  
   554  	It("checks the block height", func() {
   555  		_, err := e.ProcessProposal(context.Background(), signedProposal)
   556  		Expect(err).NotTo(HaveOccurred())
   557  		Expect(fakeSupport.GetLedgerHeightCallCount()).To(Equal(1))
   558  	})
   559  
   560  	Context("when the block height cannot be determined", func() {
   561  		BeforeEach(func() {
   562  			fakeSupport.GetLedgerHeightReturns(0, fmt.Errorf("fake-block-height-error"))
   563  		})
   564  
   565  		It("returns a response with the error and no payload", func() {
   566  			proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal)
   567  			Expect(err).NotTo(HaveOccurred())
   568  			Expect(proposalResponse.Payload).To(BeNil())
   569  			Expect(proposalResponse.Response).To(Equal(&pb.Response{
   570  				Status:  500,
   571  				Message: "error in simulation: failed to obtain ledger height for channel 'channel-id': fake-block-height-error",
   572  			}))
   573  
   574  			Expect(fakeSimulateFailure.AddCallCount()).To(Equal(1))
   575  		})
   576  	})
   577  
   578  	It("records metrics about the proposal processing", func() {
   579  		_, err := e.ProcessProposal(context.Background(), signedProposal)
   580  		Expect(err).NotTo(HaveOccurred())
   581  
   582  		Expect(fakeProposalsReceived.AddCallCount()).To(Equal(1))
   583  		Expect(fakeSuccessfulProposals.AddCallCount()).To(Equal(1))
   584  		Expect(fakeProposalValidationFailed.AddCallCount()).To(Equal(0))
   585  
   586  		Expect(fakeProposalDuration.WithCallCount()).To(Equal(1))
   587  		Expect(fakeProposalDuration.WithArgsForCall(0)).To(Equal([]string{
   588  			"channel", "channel-id",
   589  			"chaincode", "chaincode-name",
   590  			"success", "true",
   591  		}))
   592  	})
   593  
   594  	Context("when the channel id is empty", func() {
   595  		BeforeEach(func() {
   596  			channelID = ""
   597  		})
   598  
   599  		It("returns a successful proposal response with no endorsement", func() {
   600  			proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal)
   601  			Expect(err).NotTo(HaveOccurred())
   602  			Expect(proposalResponse.Endorsement).To(BeNil())
   603  			Expect(proposalResponse.Timestamp).To(BeNil())
   604  			Expect(proposalResponse.Version).To(Equal(int32(0)))
   605  			Expect(proposalResponse.Payload).To(BeNil())
   606  			Expect(proto.Equal(proposalResponse.Response, &pb.Response{
   607  				Status:  200,
   608  				Payload: []byte("response-payload"),
   609  			})).To(BeTrue())
   610  		})
   611  
   612  		It("does not attempt to get a history query executor", func() {
   613  			_, err := e.ProcessProposal(context.Background(), signedProposal)
   614  			Expect(err).NotTo(HaveOccurred())
   615  			Expect(fakeSupport.GetHistoryQueryExecutorCallCount()).To(Equal(0))
   616  		})
   617  
   618  		It("does not attempt to deduplicate the txid", func() {
   619  			_, err := e.ProcessProposal(context.Background(), signedProposal)
   620  			Expect(err).NotTo(HaveOccurred())
   621  			Expect(fakeSupport.GetTransactionByIDCallCount()).To(Equal(0))
   622  		})
   623  
   624  		It("does not attempt to get a tx simulator", func() {
   625  			_, err := e.ProcessProposal(context.Background(), signedProposal)
   626  			Expect(err).NotTo(HaveOccurred())
   627  			Expect(fakeSupport.GetTxSimulatorCallCount()).To(Equal(0))
   628  		})
   629  
   630  		It("uses the local MSP to authorize the creator", func() {
   631  			_, err := e.ProcessProposal(context.Background(), signedProposal)
   632  			Expect(err).NotTo(HaveOccurred())
   633  			Expect(fakeChannelMSPIdentityDeserializer.DeserializeIdentityCallCount()).To(Equal(0))
   634  
   635  			Expect(fakeLocalMSPIdentityDeserializer.DeserializeIdentityCallCount()).To(Equal(1))
   636  			identity := fakeLocalMSPIdentityDeserializer.DeserializeIdentityArgsForCall(0)
   637  			Expect(identity).To(Equal(protoutil.MarshalOrPanic(&mspproto.SerializedIdentity{
   638  				Mspid: "msp-id",
   639  			})))
   640  		})
   641  
   642  		Context("when the proposal is not validly signed", func() {
   643  			BeforeEach(func() {
   644  				fakeLocalMSPIdentityDeserializer.DeserializeIdentityReturns(nil, fmt.Errorf("fake-deserialize-error"))
   645  			})
   646  
   647  			It("wraps and returns an error and responds to the client", func() {
   648  				proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal)
   649  				Expect(err).To(MatchError("error validating proposal: access denied: channel [] creator org unknown, creator is malformed"))
   650  				Expect(proposalResponse).To(Equal(&pb.ProposalResponse{
   651  					Response: &pb.Response{
   652  						Status:  500,
   653  						Message: "error validating proposal: access denied: channel [] creator org unknown, creator is malformed",
   654  					},
   655  				}))
   656  			})
   657  		})
   658  
   659  		It("records metrics but without a channel ID set", func() {
   660  			_, err := e.ProcessProposal(context.Background(), signedProposal)
   661  			Expect(err).NotTo(HaveOccurred())
   662  			Expect(fakeProposalsReceived.AddCallCount()).To(Equal(1))
   663  			Expect(fakeSuccessfulProposals.AddCallCount()).To(Equal(1))
   664  			Expect(fakeProposalValidationFailed.AddCallCount()).To(Equal(0))
   665  
   666  			Expect(fakeProposalDuration.WithCallCount()).To(Equal(1))
   667  			Expect(fakeProposalDuration.WithArgsForCall(0)).To(Equal([]string{
   668  				"channel", "",
   669  				"chaincode", "chaincode-name",
   670  				"success", "true",
   671  			}))
   672  			Expect(fakeProposalACLCheckFailed.WithCallCount()).To(Equal(0))
   673  			Expect(fakeInitFailed.WithCallCount()).To(Equal(0))
   674  			Expect(fakeEndorsementsFailed.WithCallCount()).To(Equal(0))
   675  			Expect(fakeDuplicateTxsFailure.WithCallCount()).To(Equal(0))
   676  		})
   677  
   678  		Context("when the chaincode response is >= 500", func() {
   679  			BeforeEach(func() {
   680  				chaincodeResponse.Status = 500
   681  			})
   682  
   683  			It("returns the result, but with the proposal encoded, and no endorsements", func() {
   684  				proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal)
   685  				Expect(err).NotTo(HaveOccurred())
   686  				Expect(proposalResponse.Endorsement).To(BeNil())
   687  				Expect(proposalResponse.Timestamp).To(BeNil())
   688  				Expect(proposalResponse.Version).To(Equal(int32(0)))
   689  				Expect(proto.Equal(proposalResponse.Response, &pb.Response{
   690  					Status:  500,
   691  					Payload: []byte("response-payload"),
   692  				})).To(BeTrue())
   693  
   694  				// This is almost definitely a bug, but, adding a test in case someone is relying on this behavior.
   695  				// When the response is >= 500, we return a payload, but not on success.  A payload is only meaningful
   696  				// if it is endorsed, so it's unclear why we're returning it here.
   697  				prp := &pb.ProposalResponsePayload{}
   698  				err = proto.Unmarshal(proposalResponse.Payload, prp)
   699  				Expect(err).NotTo(HaveOccurred())
   700  				Expect(fmt.Sprintf("%x", prp.ProposalHash)).To(Equal("f2c27f04f897dc28fd1b2983e7b22ebc8fbbb3d0617c140d913b33e463886788"))
   701  
   702  				ccAct := &pb.ChaincodeAction{}
   703  				err = proto.Unmarshal(prp.Extension, ccAct)
   704  				Expect(err).NotTo(HaveOccurred())
   705  				Expect(proto.Equal(ccAct.Response, &pb.Response{
   706  					Status:  500,
   707  					Payload: []byte("response-payload"),
   708  				})).To(BeTrue())
   709  
   710  				// This is an especially weird bit of the behavior, the chaincode event is nil-ed before creating
   711  				// the proposal response. (That probably shouldn't be created)
   712  				Expect(ccAct.Events).To(BeNil())
   713  			})
   714  		})
   715  
   716  		Context("when the 200 < chaincode response < 500", func() {
   717  			BeforeEach(func() {
   718  				chaincodeResponse.Status = 499
   719  			})
   720  
   721  			It("returns the result, but with the proposal encoded, and no endorsements", func() {
   722  				proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal)
   723  				Expect(err).NotTo(HaveOccurred())
   724  				Expect(proposalResponse.Endorsement).To(BeNil())
   725  				Expect(proposalResponse.Timestamp).To(BeNil())
   726  				Expect(proposalResponse.Version).To(Equal(int32(0)))
   727  				Expect(proto.Equal(proposalResponse.Response, &pb.Response{
   728  					Status:  499,
   729  					Payload: []byte("response-payload"),
   730  				})).To(BeTrue())
   731  				Expect(proposalResponse.Payload).To(BeNil())
   732  			})
   733  		})
   734  	})
   735  
   736  	Context("when the proposal is malformed", func() {
   737  		JustBeforeEach(func() {
   738  			signedProposal = &pb.SignedProposal{
   739  				ProposalBytes: []byte("garbage"),
   740  			}
   741  		})
   742  
   743  		It("wraps and returns an error and responds to the client", func() {
   744  			proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal)
   745  			Expect(err).To(MatchError("error unmarshalling Proposal: proto: can't skip unknown wire type 7"))
   746  			Expect(proposalResponse).To(Equal(&pb.ProposalResponse{
   747  				Response: &pb.Response{
   748  					Status:  500,
   749  					Message: "error unmarshalling Proposal: proto: can't skip unknown wire type 7",
   750  				},
   751  			}))
   752  		})
   753  	})
   754  
   755  	Context("when the chaincode response is >= 500", func() {
   756  		BeforeEach(func() {
   757  			chaincodeResponse.Status = 500
   758  		})
   759  
   760  		It("returns the result, but with the proposal encoded, and no endorsements", func() {
   761  			proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal)
   762  			Expect(err).NotTo(HaveOccurred())
   763  			Expect(proposalResponse.Endorsement).To(BeNil())
   764  			Expect(proposalResponse.Timestamp).To(BeNil())
   765  			Expect(proposalResponse.Version).To(Equal(int32(0)))
   766  			Expect(proto.Equal(proposalResponse.Response, &pb.Response{
   767  				Status:  500,
   768  				Payload: []byte("response-payload"),
   769  			})).To(BeTrue())
   770  			Expect(proposalResponse.Payload).NotTo(BeNil())
   771  		})
   772  	})
   773  
   774  	Context("when the chaincode name is qscc", func() {
   775  		BeforeEach(func() {
   776  			chaincodeName = "qscc"
   777  		})
   778  
   779  		It("skips fetching the tx simulator and history query exucutor", func() {
   780  			_, err := e.ProcessProposal(context.Background(), signedProposal)
   781  			Expect(err).NotTo(HaveOccurred())
   782  			Expect(fakeSupport.GetTxSimulatorCallCount()).To(Equal(0))
   783  			Expect(fakeSupport.GetHistoryQueryExecutorCallCount()).To(Equal(0))
   784  		})
   785  	})
   786  
   787  	Context("when the chaincode name is cscc", func() {
   788  		BeforeEach(func() {
   789  			chaincodeName = "cscc"
   790  		})
   791  
   792  		It("skips fetching the tx simulator and history query exucutor", func() {
   793  			_, err := e.ProcessProposal(context.Background(), signedProposal)
   794  			Expect(err).NotTo(HaveOccurred())
   795  			Expect(fakeSupport.GetTxSimulatorCallCount()).To(Equal(0))
   796  			Expect(fakeSupport.GetHistoryQueryExecutorCallCount()).To(Equal(0))
   797  		})
   798  	})
   799  
   800  	Context("when the chaincode response is >= 400 but < 500", func() {
   801  		BeforeEach(func() {
   802  			chaincodeResponse.Status = 400
   803  		})
   804  
   805  		It("returns the response with no payload", func() {
   806  			proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal)
   807  			Expect(err).NotTo(HaveOccurred())
   808  			Expect(proposalResponse.Payload).To(BeNil())
   809  			Expect(proto.Equal(proposalResponse.Response, &pb.Response{
   810  				Status:  400,
   811  				Payload: []byte("response-payload"),
   812  			})).To(BeTrue())
   813  		})
   814  	})
   815  
   816  	Context("when we're in the degenerate legacy lifecycle case", func() {
   817  		BeforeEach(func() {
   818  			chaincodeName = "lscc"
   819  			chaincodeInput.Args = [][]byte{
   820  				[]byte("deploy"),
   821  				nil,
   822  				protoutil.MarshalOrPanic(&pb.ChaincodeDeploymentSpec{
   823  					ChaincodeSpec: &pb.ChaincodeSpec{
   824  						ChaincodeId: &pb.ChaincodeID{
   825  							Name:    "deploy-name",
   826  							Version: "deploy-version",
   827  						},
   828  						Input: &pb.ChaincodeInput{
   829  							Args: [][]byte{[]byte("target-arg")},
   830  						},
   831  					},
   832  				}),
   833  			}
   834  
   835  			fakeTxSimulator.GetTxSimulationResultsReturns(
   836  				&ledger.TxSimulationResults{
   837  					PubSimulationResults: &rwset.TxReadWriteSet{},
   838  					// We don't return private data in this case because lscc forbids it
   839  				},
   840  				nil,
   841  			)
   842  		})
   843  
   844  		It("triggers the legacy init, and returns the response from lscc", func() {
   845  			proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal)
   846  			Expect(err).NotTo(HaveOccurred())
   847  			Expect(proto.Equal(proposalResponse.Response, &pb.Response{
   848  				Status:  200,
   849  				Payload: []byte("response-payload"),
   850  			})).To(BeTrue())
   851  
   852  			Expect(fakeSupport.ExecuteLegacyInitCallCount()).To(Equal(1))
   853  			_, name, version, input := fakeSupport.ExecuteLegacyInitArgsForCall(0)
   854  			Expect(name).To(Equal("deploy-name"))
   855  			Expect(version).To(Equal("deploy-version"))
   856  			Expect(input.Args).To(Equal([][]byte{[]byte("target-arg")}))
   857  		})
   858  
   859  		Context("when the chaincode spec contains a code package", func() {
   860  			BeforeEach(func() {
   861  				chaincodeInput.Args = [][]byte{
   862  					[]byte("deploy"),
   863  					nil,
   864  					protoutil.MarshalOrPanic(&pb.ChaincodeDeploymentSpec{
   865  						ChaincodeSpec: &pb.ChaincodeSpec{
   866  							ChaincodeId: &pb.ChaincodeID{
   867  								Name:    "deploy-name",
   868  								Version: "deploy-version",
   869  							},
   870  							Input: &pb.ChaincodeInput{
   871  								Args: [][]byte{[]byte("target-arg")},
   872  							},
   873  						},
   874  						CodePackage: []byte("some-code"),
   875  					}),
   876  				}
   877  			})
   878  
   879  			It("returns an error to the client", func() {
   880  				proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal)
   881  				Expect(err).NotTo(HaveOccurred())
   882  				Expect(proposalResponse.Response).To(Equal(&pb.Response{
   883  					Status:  500,
   884  					Message: "error in simulation: lscc upgrade/deploy should not include a code packages",
   885  				}))
   886  				Expect(fakeSimulateFailure.AddCallCount()).To(Equal(1))
   887  			})
   888  		})
   889  
   890  		Context("when the simulation uses private data", func() {
   891  			BeforeEach(func() {
   892  				fakeTxSimulator.GetTxSimulationResultsReturns(
   893  					&ledger.TxSimulationResults{
   894  						PubSimulationResults: &rwset.TxReadWriteSet{},
   895  						PvtSimulationResults: &rwset.TxPvtReadWriteSet{},
   896  					},
   897  					nil,
   898  				)
   899  			})
   900  
   901  			It("returns an error to the client", func() {
   902  				proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal)
   903  				Expect(err).NotTo(HaveOccurred())
   904  				Expect(proposalResponse.Response).To(Equal(&pb.Response{
   905  					Status:  500,
   906  					Message: "error in simulation: Private data is forbidden to be used in instantiate",
   907  				}))
   908  				Expect(fakeSimulateFailure.AddCallCount()).To(Equal(1))
   909  			})
   910  		})
   911  
   912  		Context("when retrieving simulation results", func() {
   913  			BeforeEach(func() {
   914  				fakeTxSimulator.GetTxSimulationResultsReturns(
   915  					&ledger.TxSimulationResults{
   916  						PubSimulationResults: &rwset.TxReadWriteSet{},
   917  						PvtSimulationResults: &rwset.TxPvtReadWriteSet{},
   918  					},
   919  					errors.New("bad simulation"),
   920  				)
   921  			})
   922  
   923  			It("returns an error to the client", func() {
   924  				proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal)
   925  				Expect(err).NotTo(HaveOccurred())
   926  				Expect(proposalResponse.Response).To(Equal(&pb.Response{
   927  					Status:  500,
   928  					Message: "error in simulation: bad simulation",
   929  				}))
   930  				Expect(fakeSimulateFailure.AddCallCount()).To(Equal(1))
   931  			})
   932  		})
   933  
   934  		Context("when retrieving public simulation results fails", func() {
   935  			BeforeEach(func() {
   936  				fakeTxSimulator.GetTxSimulationResultsReturns(
   937  					&ledger.TxSimulationResults{},
   938  					nil,
   939  				)
   940  			})
   941  
   942  			It("returns an error to the client", func() {
   943  				proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal)
   944  				Expect(err).NotTo(HaveOccurred())
   945  				Expect(proposalResponse.Response).To(Equal(&pb.Response{
   946  					Status:  500,
   947  					Message: "error in simulation: proto: Marshal called with nil",
   948  				}))
   949  				Expect(fakeSimulateFailure.AddCallCount()).To(Equal(1))
   950  			})
   951  		})
   952  
   953  		Context("when the init fails", func() {
   954  			BeforeEach(func() {
   955  				fakeSupport.ExecuteLegacyInitReturns(nil, nil, fmt.Errorf("fake-legacy-init-error"))
   956  			})
   957  
   958  			It("returns an error and increments the metric", func() {
   959  				proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal)
   960  				Expect(err).NotTo(HaveOccurred())
   961  				Expect(proposalResponse.Response).To(Equal(&pb.Response{
   962  					Status:  500,
   963  					Message: "error in simulation: fake-legacy-init-error",
   964  				}))
   965  
   966  				Expect(fakeInitFailed.WithCallCount()).To(Equal(1))
   967  				Expect(fakeInitFailed.WithArgsForCall(0)).To(Equal([]string{
   968  					"channel", "channel-id",
   969  					"chaincode", "deploy-name",
   970  				}))
   971  				Expect(fakeInitFailed.AddCallCount()).To(Equal(1))
   972  			})
   973  		})
   974  
   975  		Context("when the deploying chaincode is the name of a builtin system chaincode", func() {
   976  			BeforeEach(func() {
   977  				fakeSupport.IsSysCCStub = func(name string) bool {
   978  					return name == "deploy-name"
   979  				}
   980  			})
   981  
   982  			It("triggers the legacy init, and returns the response from lscc", func() {
   983  				proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal)
   984  				Expect(err).NotTo(HaveOccurred())
   985  				Expect(proposalResponse.Response).To(Equal(&pb.Response{
   986  					Status:  500,
   987  					Message: "error in simulation: attempting to deploy a system chaincode deploy-name/channel-id",
   988  				}))
   989  				Expect(fakeSimulateFailure.AddCallCount()).To(Equal(1))
   990  			})
   991  		})
   992  
   993  		Context("when unmarshalling UnmarshalChaincodeDeploymentSpec fails", func() {
   994  			BeforeEach(func() {
   995  				chaincodeInput = &pb.ChaincodeInput{
   996  					Args: [][]byte{[]byte("deploy"), nil, []byte("arg3")},
   997  				}
   998  			})
   999  
  1000  			It("returns an error to the client", func() {
  1001  				proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal)
  1002  				Expect(err).NotTo(HaveOccurred())
  1003  				Expect(proposalResponse.Response).To(Equal(&pb.Response{
  1004  					Status:  500,
  1005  					Message: "error in simulation: error unmarshalling ChaincodeDeploymentSpec: unexpected EOF",
  1006  				}))
  1007  				Expect(fakeSimulateFailure.AddCallCount()).To(Equal(1))
  1008  			})
  1009  		})
  1010  	})
  1011  
  1012  	Context("when retrieving simulation results", func() {
  1013  		BeforeEach(func() {
  1014  			mockDeployedCCInfoProvider := &ledgermock.DeployedChaincodeInfoProvider{}
  1015  			fakeSupport.GetDeployedCCInfoProviderReturns(mockDeployedCCInfoProvider)
  1016  
  1017  			pvtSimResults := &rwset.TxPvtReadWriteSet{
  1018  				DataModel: rwset.TxReadWriteSet_KV,
  1019  				NsPvtRwset: []*rwset.NsPvtReadWriteSet{
  1020  					{
  1021  						Namespace: "myCC",
  1022  						CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{
  1023  							{
  1024  								CollectionName: "mycollection-1",
  1025  								Rwset:          []byte{1, 2, 3, 4, 5, 6, 7, 8},
  1026  							},
  1027  						},
  1028  					},
  1029  				},
  1030  			}
  1031  
  1032  			fakeTxSimulator.GetTxSimulationResultsReturns(
  1033  				&ledger.TxSimulationResults{
  1034  					PvtSimulationResults: pvtSimResults,
  1035  				},
  1036  				nil,
  1037  			)
  1038  		})
  1039  
  1040  		It("returns an error to the client", func() {
  1041  			proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal)
  1042  			Expect(err).NotTo(HaveOccurred())
  1043  			Expect(proposalResponse.Response).To(Equal(&pb.Response{
  1044  				Status:  500,
  1045  				Message: "error in simulation: failed to obtain collections config: no collection config for chaincode \"myCC\"",
  1046  			}))
  1047  			Expect(fakeSimulateFailure.AddCallCount()).To(Equal(1))
  1048  		})
  1049  	})
  1050  
  1051  	Context("when building the ChaincodeInterest", func() {
  1052  		var pvtSimResults *rwset.TxPvtReadWriteSet
  1053  		var pubSimResults *rwset.TxReadWriteSet
  1054  		var readWrites []byte
  1055  		var readSet []byte
  1056  
  1057  		BeforeEach(func() {
  1058  			ccPkg := &pb.CollectionConfigPackage{
  1059  				Config: []*pb.CollectionConfig{
  1060  					{
  1061  						Payload: &pb.CollectionConfig_StaticCollectionConfig{
  1062  							StaticCollectionConfig: &pb.StaticCollectionConfig{
  1063  								Name: "myCC",
  1064  								MemberOrgsPolicy: &pb.CollectionPolicyConfig{
  1065  									Payload: &pb.CollectionPolicyConfig_SignaturePolicy{
  1066  										SignaturePolicy: &cb.SignaturePolicyEnvelope{},
  1067  									},
  1068  								},
  1069  							},
  1070  						},
  1071  					},
  1072  				},
  1073  			}
  1074  			mockDeployedCCInfoProvider := &ledgermock.DeployedChaincodeInfoProvider{}
  1075  			mockDeployedCCInfoProvider.AllCollectionsConfigPkgReturns(ccPkg, nil)
  1076  			fakeSupport.GetDeployedCCInfoProviderReturns(mockDeployedCCInfoProvider)
  1077  
  1078  			var err error
  1079  			readWrites, err = proto.Marshal(
  1080  				&kvrwset.KVRWSet{
  1081  					Writes: []*kvrwset.KVWrite{
  1082  						{Key: "myKey", Value: []byte("myValue")},
  1083  					},
  1084  				},
  1085  			)
  1086  			Expect(err).NotTo(HaveOccurred())
  1087  			readSet, err = proto.Marshal(
  1088  				&kvrwset.KVRWSet{
  1089  					Reads: []*kvrwset.KVRead{
  1090  						{Key: "myKey"},
  1091  					},
  1092  				},
  1093  			)
  1094  			Expect(err).NotTo(HaveOccurred())
  1095  
  1096  			pubSimResults = &rwset.TxReadWriteSet{
  1097  				DataModel: rwset.TxReadWriteSet_KV,
  1098  				NsRwset: []*rwset.NsReadWriteSet{
  1099  					{
  1100  						Namespace: "myCC",
  1101  						Rwset:     readWrites,
  1102  					},
  1103  				},
  1104  			}
  1105  
  1106  			pvtSimResults = &rwset.TxPvtReadWriteSet{
  1107  				DataModel: rwset.TxReadWriteSet_KV,
  1108  				NsPvtRwset: []*rwset.NsPvtReadWriteSet{
  1109  					{
  1110  						Namespace: "myCC",
  1111  						CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{
  1112  							{
  1113  								CollectionName: "mycollection-1",
  1114  								Rwset:          []byte("private RW set"),
  1115  							},
  1116  						},
  1117  					},
  1118  				},
  1119  			}
  1120  		})
  1121  
  1122  		It("add private collection which gets read", func() {
  1123  			privateReads := ledger.PrivateReads{}
  1124  			privateReads.Add("myCC", "mycollection-1")
  1125  			writesetMetadata := ledger.WritesetMetadata{}
  1126  			writesetMetadata.Add("myCC", "mycollection-1", "mykey", nil)
  1127  
  1128  			fakeTxSimulator.GetTxSimulationResultsReturns(
  1129  				&ledger.TxSimulationResults{
  1130  					PubSimulationResults: &rwset.TxReadWriteSet{},
  1131  					PvtSimulationResults: pvtSimResults,
  1132  					PrivateReads:         privateReads,
  1133  					WritesetMetadata:     writesetMetadata,
  1134  				},
  1135  				nil,
  1136  			)
  1137  
  1138  			proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal)
  1139  			Expect(err).NotTo(HaveOccurred())
  1140  			sort.Sort(CcInterest(*proposalResponse.Interest))
  1141  			Expect(proposalResponse.Interest).To(Equal(&pb.ChaincodeInterest{
  1142  				Chaincodes: []*pb.ChaincodeCall{{
  1143  					Name:            "myCC",
  1144  					CollectionNames: []string{"mycollection-1"},
  1145  				}},
  1146  			}))
  1147  		})
  1148  
  1149  		It("add private collection which gets read, but not written", func() {
  1150  			privateReads := ledger.PrivateReads{}
  1151  			privateReads.Add("myCC", "mycollection-1")
  1152  			writesetMetadata := ledger.WritesetMetadata{}
  1153  
  1154  			// a private read will also have an entry in public hashed RWset
  1155  			pubSimResults = &rwset.TxReadWriteSet{
  1156  				DataModel: rwset.TxReadWriteSet_KV,
  1157  				NsRwset: []*rwset.NsReadWriteSet{
  1158  					{
  1159  						Namespace:             "myCC",
  1160  						CollectionHashedRwset: []*rwset.CollectionHashedReadWriteSet{{CollectionName: "mycollection-1"}},
  1161  					},
  1162  				},
  1163  			}
  1164  
  1165  			fakeTxSimulator.GetTxSimulationResultsReturns(
  1166  				&ledger.TxSimulationResults{
  1167  					PubSimulationResults: pubSimResults,
  1168  					PvtSimulationResults: &rwset.TxPvtReadWriteSet{},
  1169  					PrivateReads:         privateReads,
  1170  					WritesetMetadata:     writesetMetadata,
  1171  				},
  1172  				nil,
  1173  			)
  1174  
  1175  			proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal)
  1176  			Expect(err).NotTo(HaveOccurred())
  1177  			sort.Sort(CcInterest(*proposalResponse.Interest))
  1178  			Expect(proposalResponse.Interest).To(Equal(&pb.ChaincodeInterest{
  1179  				Chaincodes: []*pb.ChaincodeCall{
  1180  					{
  1181  						Name: "myCC",
  1182  					},
  1183  					{
  1184  						Name:            "myCC",
  1185  						CollectionNames: []string{"mycollection-1"},
  1186  					},
  1187  				},
  1188  			}))
  1189  		})
  1190  
  1191  		It("add private collection which is not read", func() {
  1192  			privateReads := ledger.PrivateReads{}
  1193  			writesetMetadata := ledger.WritesetMetadata{}
  1194  			writesetMetadata.Add("myCC", "mycollection-1", "mykey", nil)
  1195  			pubSimResults = &rwset.TxReadWriteSet{
  1196  				DataModel: rwset.TxReadWriteSet_KV,
  1197  				NsRwset: []*rwset.NsReadWriteSet{
  1198  					{
  1199  						Namespace: "myCC",
  1200  						Rwset:     readSet,
  1201  					},
  1202  				},
  1203  			}
  1204  
  1205  			fakeTxSimulator.GetTxSimulationResultsReturns(
  1206  				&ledger.TxSimulationResults{
  1207  					PubSimulationResults: pubSimResults,
  1208  					PvtSimulationResults: pvtSimResults,
  1209  					PrivateReads:         privateReads,
  1210  					WritesetMetadata:     writesetMetadata,
  1211  				},
  1212  				nil,
  1213  			)
  1214  
  1215  			proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal)
  1216  			Expect(err).NotTo(HaveOccurred())
  1217  			sort.Sort(CcInterest(*proposalResponse.Interest))
  1218  			Expect(proposalResponse.Interest).To(Equal(&pb.ChaincodeInterest{
  1219  				Chaincodes: []*pb.ChaincodeCall{{
  1220  					Name:            "myCC",
  1221  					CollectionNames: []string{"mycollection-1"},
  1222  					NoPrivateReads:  true,
  1223  				}},
  1224  			}))
  1225  		})
  1226  
  1227  		It("add private collection and SBE", func() {
  1228  			privateReads := ledger.PrivateReads{}
  1229  
  1230  			sbe := &cb.SignaturePolicyEnvelope{
  1231  				Rule: &cb.SignaturePolicy{
  1232  					Type: &cb.SignaturePolicy_SignedBy{SignedBy: 0},
  1233  				},
  1234  			}
  1235  			sbeBytes, err := proto.Marshal(sbe)
  1236  			Expect(err).NotTo(HaveOccurred())
  1237  			metadata := map[string][]byte{pb.MetaDataKeys_VALIDATION_PARAMETER.String(): sbeBytes}
  1238  
  1239  			writesetMetadata := ledger.WritesetMetadata{}
  1240  			writesetMetadata.Add("myCC", "mycollection-1", "mykey1", nil)
  1241  			writesetMetadata.Add("myCC", "mycollection-1", "mykey2", metadata)
  1242  
  1243  			fakeTxSimulator.GetTxSimulationResultsReturns(
  1244  				&ledger.TxSimulationResults{
  1245  					PubSimulationResults: &rwset.TxReadWriteSet{},
  1246  					PvtSimulationResults: pvtSimResults,
  1247  					PrivateReads:         privateReads,
  1248  					WritesetMetadata:     writesetMetadata,
  1249  				},
  1250  				nil,
  1251  			)
  1252  
  1253  			proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal)
  1254  			Expect(err).NotTo(HaveOccurred())
  1255  
  1256  			sort.Sort(CcInterest(*proposalResponse.Interest))
  1257  			Expect(proto.Equal(
  1258  				proposalResponse.Interest,
  1259  				&pb.ChaincodeInterest{
  1260  					Chaincodes: []*pb.ChaincodeCall{
  1261  						{
  1262  							Name:                     "myCC",
  1263  							KeyPolicies:              []*cb.SignaturePolicyEnvelope{sbe},
  1264  							DisregardNamespacePolicy: true,
  1265  						},
  1266  						{
  1267  							Name:            "myCC",
  1268  							CollectionNames: []string{"mycollection-1"},
  1269  							NoPrivateReads:  true,
  1270  						},
  1271  					},
  1272  				},
  1273  			)).To(BeTrue())
  1274  		})
  1275  
  1276  		It("SBE only, no chaincode policy updates", func() {
  1277  			sbe := &cb.SignaturePolicyEnvelope{
  1278  				Rule: &cb.SignaturePolicy{
  1279  					Type: &cb.SignaturePolicy_SignedBy{SignedBy: 0},
  1280  				},
  1281  			}
  1282  			sbeBytes, err := proto.Marshal(sbe)
  1283  			Expect(err).NotTo(HaveOccurred())
  1284  			metadata := map[string][]byte{pb.MetaDataKeys_VALIDATION_PARAMETER.String(): sbeBytes}
  1285  
  1286  			writesetMetadata := ledger.WritesetMetadata{}
  1287  			writesetMetadata.Add("myCC", "", "myKey", metadata)
  1288  
  1289  			fakeTxSimulator.GetTxSimulationResultsReturns(
  1290  				&ledger.TxSimulationResults{
  1291  					PubSimulationResults: pubSimResults,
  1292  					PvtSimulationResults: pvtSimResults,
  1293  					WritesetMetadata:     writesetMetadata,
  1294  				},
  1295  				nil,
  1296  			)
  1297  
  1298  			proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal)
  1299  			Expect(err).NotTo(HaveOccurred())
  1300  
  1301  			sort.Sort(CcInterest(*proposalResponse.Interest))
  1302  			Expect(proto.Equal(
  1303  				proposalResponse.Interest,
  1304  				&pb.ChaincodeInterest{
  1305  					Chaincodes: []*pb.ChaincodeCall{{
  1306  						Name:                     "myCC",
  1307  						KeyPolicies:              []*cb.SignaturePolicyEnvelope{sbe},
  1308  						DisregardNamespacePolicy: true,
  1309  					}},
  1310  				},
  1311  			)).To(BeTrue())
  1312  		})
  1313  
  1314  		It("chaincode to chaincode calls", func() {
  1315  			cc3ccSimResults := &rwset.TxReadWriteSet{
  1316  				DataModel: rwset.TxReadWriteSet_KV,
  1317  				NsRwset: []*rwset.NsReadWriteSet{
  1318  					{
  1319  						Namespace: "myCC",
  1320  						Rwset:     readWrites,
  1321  					},
  1322  					{
  1323  						Namespace: "otherCC",
  1324  						Rwset:     readWrites,
  1325  					},
  1326  				},
  1327  			}
  1328  
  1329  			writesetMetadata := ledger.WritesetMetadata{}
  1330  			writesetMetadata.Add("myCC", "", "mykey", nil)
  1331  			writesetMetadata.Add("otherCC", "", "mykey", nil)
  1332  
  1333  			fakeTxSimulator.GetTxSimulationResultsReturns(
  1334  				&ledger.TxSimulationResults{
  1335  					PubSimulationResults: cc3ccSimResults,
  1336  					PvtSimulationResults: &rwset.TxPvtReadWriteSet{},
  1337  					WritesetMetadata:     writesetMetadata,
  1338  				},
  1339  				nil,
  1340  			)
  1341  
  1342  			proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal)
  1343  			Expect(err).NotTo(HaveOccurred())
  1344  
  1345  			Expect(proposalResponse.Interest.Chaincodes).To(ContainElements([]*pb.ChaincodeCall{
  1346  				{Name: "myCC"},
  1347  				{Name: "otherCC"},
  1348  			}))
  1349  		})
  1350  
  1351  		It("ignores system chaincodes", func() {
  1352  			fakeSupport.IsSysCCStub = func(cc string) bool {
  1353  				return cc == "_lifecycle"
  1354  			}
  1355  			pubSimResults = &rwset.TxReadWriteSet{
  1356  				DataModel: rwset.TxReadWriteSet_KV,
  1357  				NsRwset: []*rwset.NsReadWriteSet{
  1358  					{
  1359  						Namespace: "myCC",
  1360  						Rwset:     readWrites,
  1361  					},
  1362  					{
  1363  						Namespace: "_lifecycle",
  1364  						Rwset:     readWrites,
  1365  					},
  1366  				},
  1367  			}
  1368  
  1369  			pvtSimResults = &rwset.TxPvtReadWriteSet{
  1370  				DataModel: rwset.TxReadWriteSet_KV,
  1371  				NsPvtRwset: []*rwset.NsPvtReadWriteSet{
  1372  					{
  1373  						Namespace: "myCC",
  1374  						CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{
  1375  							{
  1376  								CollectionName: "mycollection-1",
  1377  								Rwset:          []byte("private RW set"),
  1378  							},
  1379  						},
  1380  					},
  1381  					{
  1382  						Namespace: "_lifecycle",
  1383  						CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{
  1384  							{
  1385  								CollectionName: "mycollection-2",
  1386  								Rwset:          []byte("should be ignored 2"),
  1387  							},
  1388  						},
  1389  					},
  1390  				},
  1391  			}
  1392  			privateReads := ledger.PrivateReads{}
  1393  			privateReads.Add("myCC", "mycollection-1")
  1394  			privateReads.Add("_lifecycle", "mycollection-1")
  1395  			writesetMetadata := ledger.WritesetMetadata{}
  1396  			writesetMetadata.Add("myCC", "mycollection-1", "mykey", nil)
  1397  			writesetMetadata.Add("_lifecycle", "mycollection-2", "mykey", nil)
  1398  
  1399  			fakeTxSimulator.GetTxSimulationResultsReturns(
  1400  				&ledger.TxSimulationResults{
  1401  					PubSimulationResults: pubSimResults,
  1402  					PvtSimulationResults: pvtSimResults,
  1403  					PrivateReads:         privateReads,
  1404  					WritesetMetadata:     writesetMetadata,
  1405  				},
  1406  				nil,
  1407  			)
  1408  
  1409  			proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal)
  1410  			Expect(err).NotTo(HaveOccurred())
  1411  
  1412  			Expect(proposalResponse.Interest.Chaincodes).To(ContainElements([]*pb.ChaincodeCall{
  1413  				{Name: "myCC", CollectionNames: []string{"mycollection-1"}},
  1414  			}))
  1415  		})
  1416  	})
  1417  })