From 05e8599e892797574500f2ae729f79934353969d Mon Sep 17 00:00:00 2001 From: "Mark S. Lewis" Date: Wed, 25 May 2022 09:20:36 +0100 Subject: [PATCH] Use any peer to evaluate system chaincode transactions System chaincodes are not included in the installed chaincodes returned by service discovery. The Gateway service was relying on discovery results to find peers on which to evaluate transactions and so failed to evaluate system chaincode transaction functions. Now the Gateway service uses any network peer to evaluate transactions invoked on known built-in system chaincodes. Also added a system test to confirm that the Gateway service can endorse system chaincode transaction functions. Signed-off-by: Mark S. Lewis --- integration/gateway/gateway_test.go | 43 +++++++++++++++++++++++++++++ internal/peer/node/start.go | 1 + internal/pkg/gateway/api_test.go | 3 +- internal/pkg/gateway/gateway.go | 26 +++++++++++++++-- internal/pkg/gateway/registry.go | 32 ++++++++++++++------- 5 files changed, 92 insertions(+), 13 deletions(-) diff --git a/integration/gateway/gateway_test.go b/integration/gateway/gateway_test.go index 8b80432e88a..a3bf7474d76 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" @@ -262,6 +263,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() { @@ -275,6 +295,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 5f519e6bbdc..0ec3f681c4c 100644 --- a/internal/pkg/gateway/api_test.go +++ b/internal/pkg/gateway/api_test.go @@ -1837,6 +1837,7 @@ func TestNilArgs(t *testing.T) { "msp1", &comm.SecureOptions{}, config.GetOptions(viper.New()), + nil, ) ctx := context.Background() @@ -1979,7 +1980,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 63ec47dbd85..53ed3ab60de 100644 --- a/internal/pkg/gateway/gateway.go +++ b/internal/pkg/gateway/gateway.go @@ -12,6 +12,7 @@ import ( "github.com/hyperledger/fabric/common/flogging" "github.com/hyperledger/fabric/common/ledger" "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" @@ -52,7 +53,16 @@ type LedgerProvider 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 := &peerAdapter{ Peer: peerInstance, } @@ -70,6 +80,7 @@ func CreateServer(localEndorser peerproto.EndorserServer, discovery Discovery, p localMSPID, secureOptions, options, + systemChaincodes, ) peerInstance.AddConfigCallbacks(server.registry.configUpdate) @@ -77,7 +88,17 @@ func CreateServer(localEndorser peerproto.EndorserServer, discovery Discovery, p return server } -func newServer(localEndorser peerproto.EndorserClient, discovery Discovery, finder CommitFinder, policy ACLChecker, ledgerProvider LedgerProvider, localInfo gdiscovery.NetworkMember, localMSPID string, secureOptions *comm.SecureOptions, options config.Options) *Server { +func newServer(localEndorser peerproto.EndorserClient, + discovery Discovery, + finder CommitFinder, + policy ACLChecker, + ledgerProvider LedgerProvider, + 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}}, @@ -86,6 +107,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) +}