Skip to content

Commit

Permalink
Use any peer to evaluate system chaincode transactions (#3446)
Browse files Browse the repository at this point in the history
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 <mark_lewis@uk.ibm.com>
  • Loading branch information
bestbeforetoday authored May 25, 2022
1 parent 4923b26 commit d0e2093
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 13 deletions.
43 changes: 43 additions & 0 deletions integration/gateway/gateway_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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() {
Expand All @@ -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() {
Expand Down
1 change: 1 addition & 0 deletions internal/peer/node/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,7 @@ func serve(args []string) error {
aclProvider,
coreConfig.LocalMSPID,
coreConfig.GatewayOptions,
builtinSCCs,
)
gatewayprotos.RegisterGatewayServer(peerServer.Server(), gatewayServer)
} else {
Expand Down
3 changes: 2 additions & 1 deletion internal/pkg/gateway/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1930,6 +1930,7 @@ func TestNilArgs(t *testing.T) {
"msp1",
&comm.SecureOptions{},
config.GetOptions(viper.New()),
nil,
)
ctx := context.Background()

Expand Down Expand Up @@ -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)
Expand Down
26 changes: 24 additions & 2 deletions internal/pkg/gateway/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
}
Expand All @@ -66,14 +76,25 @@ func CreateServer(localEndorser peerproto.EndorserServer, discovery Discovery, p
localMSPID,
secureOptions,
options,
systemChaincodes,
)

peerInstance.AddConfigCallbacks(server.registry.configUpdate)

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: &registry{
localEndorser: &endorser{client: localEndorser, endpointConfig: &endpointConfig{pkiid: localInfo.PKIid, address: localInfo.Endpoint, mspid: localMSPID}},
Expand All @@ -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,
Expand Down
32 changes: 22 additions & 10 deletions internal/pkg/gateway/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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:
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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)
}

0 comments on commit d0e2093

Please sign in to comment.