diff --git a/integration/gateway/gateway_test.go b/integration/gateway/gateway_test.go index 00f2c1af4f4..f543c014434 100644 --- a/integration/gateway/gateway_test.go +++ b/integration/gateway/gateway_test.go @@ -20,6 +20,7 @@ import ( "github.com/hyperledger/fabric-protos-go/gateway" "github.com/hyperledger/fabric-protos-go/orderer" "github.com/hyperledger/fabric-protos-go/peer" + "github.com/hyperledger/fabric-protos-go/peer/lifecycle" "github.com/hyperledger/fabric/integration/nwo" "github.com/hyperledger/fabric/protoutil" . "github.com/onsi/ginkgo" @@ -266,6 +267,25 @@ var _ = Describe("GatewayService", func() { Expect(response.Result.Payload).To(Equal(expectedResponse.Result.Payload)) Expect(proto.Equal(response, expectedResponse)).To(BeTrue(), "Expected\n\t%#v\nto proto.Equal\n\t%#v", response, expectedResponse) }) + + It("should responsd with system chaincode result", func() { + proposedTransaction, transactionID := NewProposedTransaction(signingIdentity, "testchannel", "qscc", "GetChainInfo", nil, []byte("testchannel")) + + request := &gateway.EvaluateRequest{ + TransactionId: transactionID, + ChannelId: "testchannel", + ProposedTransaction: proposedTransaction, + } + + response, err := gatewayClient.Evaluate(ctx, request) + Expect(err).NotTo(HaveOccurred()) + + status := common.Status(response.GetResult().GetStatus()) + Expect(status).To(Equal(common.Status_SUCCESS)) + + blockchainInfo := new(common.BlockchainInfo) + Expect(proto.Unmarshal(response.GetResult().GetPayload(), blockchainInfo)).NotTo(HaveOccurred()) + }) }) Describe("Submit", func() { @@ -279,6 +299,29 @@ var _ = Describe("GatewayService", func() { Expect(result.Payload).To(Equal(expectedResult.Payload)) Expect(proto.Equal(result, expectedResult)).To(BeTrue(), "Expected\n\t%#v\nto proto.Equal\n\t%#v", result, expectedResult) }) + + It("should endorse a system chaincode transaction", func() { + arg, err := proto.Marshal(&lifecycle.QueryInstalledChaincodesArgs{}) + Expect(err).NotTo(HaveOccurred()) + adminSigner := network.PeerUserSigner(org1Peer0, "Admin") + proposedTransaction, transactionID := NewProposedTransaction(adminSigner, "testchannel", "_lifecycle", "QueryInstalledChaincodes", nil, arg) + + request := &gateway.EndorseRequest{ + TransactionId: transactionID, + ChannelId: "testchannel", + ProposedTransaction: proposedTransaction, + EndorsingOrganizations: []string{adminSigner.MSPID}, // Only use peers for our admin ID org + } + + response, err := gatewayClient.Endorse(ctx, request) + Expect(err).NotTo(HaveOccurred()) + + chaincodeAction, err := protoutil.GetActionFromEnvelopeMsg(response.GetPreparedTransaction()) + Expect(err).NotTo(HaveOccurred()) + + queryResult := new(lifecycle.QueryInstalledChaincodesResult) + Expect(proto.Unmarshal(chaincodeAction.GetResponse().GetPayload(), queryResult)).NotTo(HaveOccurred()) + }) }) Describe("CommitStatus", func() { diff --git a/internal/peer/node/start.go b/internal/peer/node/start.go index 2a1f38b93ff..354db7702e6 100644 --- a/internal/peer/node/start.go +++ b/internal/peer/node/start.go @@ -829,6 +829,7 @@ func serve(args []string) error { aclProvider, coreConfig.LocalMSPID, coreConfig.GatewayOptions, + builtinSCCs, ) gatewayprotos.RegisterGatewayServer(peerServer.Server(), gatewayServer) } else { diff --git a/internal/pkg/gateway/api_test.go b/internal/pkg/gateway/api_test.go index 356a59a0b49..972ec49c08f 100644 --- a/internal/pkg/gateway/api_test.go +++ b/internal/pkg/gateway/api_test.go @@ -1930,6 +1930,7 @@ func TestNilArgs(t *testing.T) { "msp1", &comm.SecureOptions{}, config.GetOptions(viper.New()), + nil, ) ctx := context.Background() @@ -2072,7 +2073,7 @@ func prepareTest(t *testing.T, tt *testDef) *preparedTest { Endpoint: "localhost:7051", } - server := newServer(localEndorser, disc, mockFinder, mockPolicy, mockLedgerProvider, member, "msp1", &comm.SecureOptions{}, options) + server := newServer(localEndorser, disc, mockFinder, mockPolicy, mockLedgerProvider, member, "msp1", &comm.SecureOptions{}, options, nil) dialer := &mocks.Dialer{} dialer.Returns(nil, nil) diff --git a/internal/pkg/gateway/gateway.go b/internal/pkg/gateway/gateway.go index b0ccdb557ec..9fadd7546b8 100644 --- a/internal/pkg/gateway/gateway.go +++ b/internal/pkg/gateway/gateway.go @@ -11,6 +11,7 @@ import ( peerproto "github.com/hyperledger/fabric-protos-go/peer" "github.com/hyperledger/fabric/common/flogging" "github.com/hyperledger/fabric/core/peer" + "github.com/hyperledger/fabric/core/scc" gdiscovery "github.com/hyperledger/fabric/gossip/discovery" "github.com/hyperledger/fabric/internal/pkg/comm" "github.com/hyperledger/fabric/internal/pkg/gateway/commit" @@ -48,7 +49,16 @@ type ACLChecker interface { } // CreateServer creates an embedded instance of the Gateway. -func CreateServer(localEndorser peerproto.EndorserServer, discovery Discovery, peerInstance *peer.Peer, secureOptions *comm.SecureOptions, policy ACLChecker, localMSPID string, options config.Options) *Server { +func CreateServer( + localEndorser peerproto.EndorserServer, + discovery Discovery, + peerInstance *peer.Peer, + secureOptions *comm.SecureOptions, + policy ACLChecker, + localMSPID string, + options config.Options, + systemChaincodes scc.BuiltinSCCs, +) *Server { adapter := &ledger.PeerAdapter{ Peer: peerInstance, } @@ -66,6 +76,7 @@ func CreateServer(localEndorser peerproto.EndorserServer, discovery Discovery, p localMSPID, secureOptions, options, + systemChaincodes, ) peerInstance.AddConfigCallbacks(server.registry.configUpdate) @@ -73,7 +84,17 @@ func CreateServer(localEndorser peerproto.EndorserServer, discovery Discovery, p return server } -func newServer(localEndorser peerproto.EndorserClient, discovery Discovery, finder CommitFinder, policy ACLChecker, ledgerProvider ledger.Provider, localInfo gdiscovery.NetworkMember, localMSPID string, secureOptions *comm.SecureOptions, options config.Options) *Server { +func newServer(localEndorser peerproto.EndorserClient, + discovery Discovery, + finder CommitFinder, + policy ACLChecker, + ledgerProvider ledger.Provider, + localInfo gdiscovery.NetworkMember, + localMSPID string, + secureOptions *comm.SecureOptions, + options config.Options, + systemChaincodes scc.BuiltinSCCs, +) *Server { return &Server{ registry: ®istry{ localEndorser: &endorser{client: localEndorser, endpointConfig: &endpointConfig{pkiid: localInfo.PKIid, address: localInfo.Endpoint, mspid: localMSPID}}, @@ -82,6 +103,7 @@ func newServer(localEndorser peerproto.EndorserClient, discovery Discovery, find endpointFactory: &endpointFactory{timeout: options.DialTimeout, clientCert: secureOptions.Certificate, clientKey: secureOptions.Key}, remoteEndorsers: map[string]*endorser{}, channelInitialized: map[string]bool{}, + systemChaincodes: systemChaincodes, }, commitFinder: finder, policy: policy, diff --git a/internal/pkg/gateway/registry.go b/internal/pkg/gateway/registry.go index 4544d2bc1ca..0fcaf6f1a6b 100644 --- a/internal/pkg/gateway/registry.go +++ b/internal/pkg/gateway/registry.go @@ -20,9 +20,11 @@ import ( "github.com/hyperledger/fabric-protos-go/peer" "github.com/hyperledger/fabric/common/channelconfig" "github.com/hyperledger/fabric/common/flogging" + "github.com/hyperledger/fabric/core/scc" gossipapi "github.com/hyperledger/fabric/gossip/api" gossipcommon "github.com/hyperledger/fabric/gossip/common" gossipdiscovery "github.com/hyperledger/fabric/gossip/discovery" + "github.com/pkg/errors" ) type Discovery interface { @@ -42,6 +44,7 @@ type registry struct { channelInitialized map[string]bool configLock sync.RWMutex channelOrderers sync.Map // channel (string) -> orderer addresses (endpointConfig) + systemChaincodes scc.BuiltinSCCs } type endorserState struct { @@ -55,7 +58,7 @@ func (reg *registry) endorsementPlan(channel string, interest *peer.ChaincodeInt descriptor, err := reg.discovery.PeersForEndorsement(gossipcommon.ChannelID(channel), interest) if err != nil { logger.Errorw("PeersForEndorsement failed.", "error", err, "channel", channel, "ChaincodeInterest", proto.MarshalTextString(interest)) - return nil, fmt.Errorf("no combination of peers can be derived which satisfy the endorsement policy: %s", err) + return nil, errors.Wrap(err, "no combination of peers can be derived which satisfy the endorsement policy") } // There are two parts to an endorsement plan: @@ -186,17 +189,16 @@ func (reg *registry) endorsersByOrg(channel string, chaincode string) map[string if endorser == nil { continue } - for _, installedChaincode := range member.Properties.GetChaincodes() { - // only consider the peers that have our chaincode installed - if installedChaincode.GetName() == chaincode { - endorsersByOrg[endorser.mspid] = append(endorsersByOrg[endorser.mspid], &endorserState{endorser: endorser, height: member.Properties.GetLedgerHeight()}) - } - } - for _, es := range endorsersByOrg { - // sort by decreasing height in each org - sort.Slice(es, sorter(es, reg.localEndorser.address)) + if reg.hasChaincode(member, chaincode) { + endorsersByOrg[endorser.mspid] = append(endorsersByOrg[endorser.mspid], &endorserState{endorser: endorser, height: member.Properties.GetLedgerHeight()}) } } + + // sort by decreasing height in each org + for _, es := range endorsersByOrg { + sort.Slice(es, sorter(es, reg.localEndorser.address)) + } + return endorsersByOrg } @@ -465,3 +467,13 @@ func (reg *registry) closeStaleOrdererConnections(channel string, channelOrderer } } } + +func (reg *registry) hasChaincode(member gossipdiscovery.NetworkMember, chaincodeName string) bool { + for _, installedChaincode := range member.Properties.GetChaincodes() { + if installedChaincode.GetName() == chaincodeName { + return true + } + } + + return reg.systemChaincodes.IsSysCC(chaincodeName) +}