Skip to content

Commit

Permalink
Handle missing endpoints from discovery
Browse files Browse the repository at this point in the history
For some reason the discovery PeersOfChannel function does not
return an endpoint for the local peer, which the gateway was
relying on

Rather than updating PeersOfChannel to return the endpoint when
there is one, to match the Peers function, this change updates
how the gateway identifies the local peer

Signed-off-by: James Taylor <jamest@uk.ibm.com>
  • Loading branch information
jt-nti authored and denyeart committed Jun 18, 2021
1 parent dc09b6e commit 00910ba
Show file tree
Hide file tree
Showing 10 changed files with 280 additions and 15 deletions.
30 changes: 30 additions & 0 deletions integration/chaincode/marbles_private/chaincode.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ func (t *MarblesPrivateChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Re
case "getMarblePrivateDetailsHash":
// get private data hash for collectionMarblePrivateDetails
return t.getMarblePrivateDetailsHash(stub, args)
case "checkEndorsingOrg":
// check mspid of the current peer
return t.checkEndorsingOrg(stub)
default:
// error
fmt.Println("invoke did not find func: " + function)
Expand Down Expand Up @@ -494,3 +497,30 @@ func (t *MarblesPrivateChaincode) getMarblesByRange(stub shim.ChaincodeStubInter

return shim.Success(buffer.Bytes())
}

// CheckEndorsingOrg checks that the peer org is present in the given transient data
func (t *MarblesPrivateChaincode) checkEndorsingOrg(stub shim.ChaincodeStubInterface) pb.Response {
transient, err := stub.GetTransient()
if err != nil {
return shim.Error(fmt.Sprintf("failed to get transient data: %v", err))
}

peerOrgMSP, err := shim.GetMSPID()
if err != nil {
return shim.Error(fmt.Sprintf("failed getting client's orgID: %v", err))
}

var result string
if _, ok := transient[peerOrgMSP]; ok {
result = "Peer mspid OK"
} else {
expectedMSPs := make([]string, 0, len(transient))
for k := range transient {
expectedMSPs = append(expectedMSPs, k)
}

result = fmt.Sprintf("Unexpected peer mspid! Expected MSP IDs: %s Actual MSP ID: %s", expectedMSPs, peerOrgMSP)
}

return shim.Success([]byte(result))
}
151 changes: 151 additions & 0 deletions integration/gateway/endorsing_orgs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
Copyright IBM Corp All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package gateway

import (
"context"
"io/ioutil"
"os"
"path/filepath"
"syscall"

docker "github.com/fsouza/go-dockerclient"
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric-protos-go/gateway"
"github.com/hyperledger/fabric-protos-go/peer"
"github.com/hyperledger/fabric/integration/nwo"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/tedsuo/ifrit"
)

var _ = Describe("GatewayService with endorsing orgs", func() {
var (
testDir string
network *nwo.Network
orderer *nwo.Orderer
org1Peer0 *nwo.Peer
org2Peer0 *nwo.Peer
org3Peer0 *nwo.Peer
process ifrit.Process
)

BeforeEach(func() {
var err error
testDir, err = ioutil.TempDir("", "gateway")
Expect(err).NotTo(HaveOccurred())

client, err := docker.NewClientFromEnv()
Expect(err).NotTo(HaveOccurred())

config := nwo.ThreeOrgRaft()
network = nwo.New(config, testDir, client, StartPort(), components)

network.GatewayEnabled = true

network.GenerateConfigTree()
network.Bootstrap()

networkRunner := network.NetworkGroupRunner()
process = ifrit.Invoke(networkRunner)
Eventually(process.Ready(), network.EventuallyTimeout).Should(BeClosed())

orderer = network.Orderer("orderer")
network.CreateAndJoinChannel(orderer, "testchannel")
network.UpdateChannelAnchors(orderer, "testchannel")
network.VerifyMembership(
network.PeersWithChannel("testchannel"),
"testchannel",
)
nwo.EnableCapabilities(
network,
"testchannel",
"Application", "V2_0",
orderer,
network.PeersWithChannel("testchannel")...,
)

org1Peer0 = network.Peer("Org1", "peer0")
org2Peer0 = network.Peer("Org2", "peer0")
org3Peer0 = network.Peer("Org3", "peer0")

chaincode := nwo.Chaincode{
Name: "pvtmarblescc",
Version: "0.0",
Path: components.Build("github.com/hyperledger/fabric/integration/chaincode/marbles_private/cmd"),
Lang: "binary",
PackageFile: filepath.Join(testDir, "pvtmarblescc.tar.gz"),
Policy: `OR ('Org1MSP.member', 'Org2MSP.member', 'Org3MSP.member')`,
SignaturePolicy: `OR ('Org1MSP.member', 'Org2MSP.member', 'Org3MSP.member')`,
Sequence: "1",
InitRequired: false,
Label: "pvtmarblescc_label",
CollectionsConfig: filepath.Join("testdata", "collections_config_anyorg.json"),
}

nwo.DeployChaincode(network, "testchannel", orderer, chaincode)
})

AfterEach(func() {
if process != nil {
process.Signal(syscall.SIGTERM)
Eventually(process.Wait(), network.EventuallyTimeout).Should(Receive())
}
if network != nil {
network.Cleanup()
}
os.RemoveAll(testDir)
})

It("should execute chaincode on a peer in the specified org", func() {
peers := [3]*nwo.Peer{org1Peer0, org2Peer0, org3Peer0}

for _, p := range peers {
conn := network.PeerClientConn(p)
defer conn.Close()
gatewayClient := gateway.NewGatewayClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), network.EventuallyTimeout)
defer cancel()

signingIdentity := network.PeerUserSigner(p, "User1")
for _, o := range peers {
mspid := network.Organization(o.Organization).MSPID

submitCheckEndorsingOrgTransaction(ctx, gatewayClient, signingIdentity, mspid)
}
}
})
})

func submitCheckEndorsingOrgTransaction(ctx context.Context, client gateway.GatewayClient, signingIdentity *nwo.SigningIdentity, mspids ...string) {
transientData := make(map[string][]byte)
for _, m := range mspids {
transientData[m] = []byte(`true`)
}

proposedTransaction, transactionID := NewProposedTransaction(signingIdentity, "testchannel", "pvtmarblescc", "checkEndorsingOrg", transientData)

endorseRequest := &gateway.EndorseRequest{
TransactionId: transactionID,
ChannelId: "testchannel",
ProposedTransaction: proposedTransaction,
EndorsingOrganizations: mspids,
}

endorseResponse, err := client.Endorse(ctx, endorseRequest)
Expect(err).NotTo(HaveOccurred())

result := endorseResponse.GetResult()
expectedPayload := "Peer mspid OK"
Expect(string(result.Payload)).To(Equal(expectedPayload))
expectedResult := &peer.Response{
Status: 200,
Message: "",
Payload: []uint8(expectedPayload),
}
Expect(proto.Equal(result, expectedResult)).To(BeTrue(), "Expected\n\t%#v\nto proto.Equal\n\t%#v", result, expectedResult)
}
9 changes: 5 additions & 4 deletions integration/gateway/gateway_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ func StartPort() int {
return integration.GatewayBasePort.StartPortForNode()
}

func NewProposedTransaction(signingIdentity *nwo.SigningIdentity, channelName, chaincodeName, transactionName string, args ...[]byte) (*peer.SignedProposal, string) {
proposal, transactionID := newProposalProto(signingIdentity, channelName, chaincodeName, transactionName, args...)
func NewProposedTransaction(signingIdentity *nwo.SigningIdentity, channelName, chaincodeName, transactionName string, transientData map[string][]byte, args ...[]byte) (*peer.SignedProposal, string) {
proposal, transactionID := newProposalProto(signingIdentity, channelName, chaincodeName, transactionName, transientData, args...)
signedProposal, err := protoutil.GetSignedProposal(proposal, signingIdentity)
Expect(err).NotTo(HaveOccurred())

return signedProposal, transactionID
}

func newProposalProto(signingIdentity *nwo.SigningIdentity, channelName, chaincodeName, transactionName string, args ...[]byte) (*peer.Proposal, string) {
func newProposalProto(signingIdentity *nwo.SigningIdentity, channelName, chaincodeName, transactionName string, transientData map[string][]byte, args ...[]byte) (*peer.Proposal, string) {
creator, err := signingIdentity.Serialize()
Expect(err).NotTo(HaveOccurred())

Expand All @@ -75,11 +75,12 @@ func newProposalProto(signingIdentity *nwo.SigningIdentity, channelName, chainco
},
}

result, transactionID, err := protoutil.CreateChaincodeProposal(
result, transactionID, err := protoutil.CreateChaincodeProposalWithTransient(
common.HeaderType_ENDORSER_TRANSACTION,
channelName,
invocationSpec,
creator,
transientData,
)
Expect(err).NotTo(HaveOccurred())

Expand Down
4 changes: 2 additions & 2 deletions integration/gateway/gateway_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ var _ = Describe("GatewayService", func() {
})

submitTransaction := func(transactionName string, args ...[]byte) (*peer.Response, string) {
proposedTransaction, transactionID := NewProposedTransaction(signingIdentity, "testchannel", "gatewaycc", transactionName, args...)
proposedTransaction, transactionID := NewProposedTransaction(signingIdentity, "testchannel", "gatewaycc", transactionName, nil, args...)

endorseRequest := &gateway.EndorseRequest{
TransactionId: transactionID,
Expand Down Expand Up @@ -165,7 +165,7 @@ var _ = Describe("GatewayService", func() {

Describe("Evaluate", func() {
It("should respond with the expected result", func() {
proposedTransaction, transactionID := NewProposedTransaction(signingIdentity, "testchannel", "gatewaycc", "respond", []byte("200"), []byte("conga message"), []byte("conga payload"))
proposedTransaction, transactionID := NewProposedTransaction(signingIdentity, "testchannel", "gatewaycc", "respond", nil, []byte("200"), []byte("conga message"), []byte("conga payload"))

request := &gateway.EvaluateRequest{
TransactionId: transactionID,
Expand Down
18 changes: 18 additions & 0 deletions integration/gateway/testdata/collections_config_anyorg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
{
"name": "collectionMarbles",
"policy": "OR('Org1MSP.member', 'Org2MSP.member', 'Org3MSP.member')",
"requiredPeerCount": 1,
"maxPeerCount": 2,
"blockToLive": 1000000,
"memberOnlyRead": false
},
{
"name": "collectionMarblePrivateDetails",
"policy": "OR('Org1MSP.member', 'Org2MSP.member', 'Org3MSP.member')",
"requiredPeerCount": 1,
"maxPeerCount": 2,
"blockToLive": 1000000,
"memberOnlyRead": false
}
]
41 changes: 41 additions & 0 deletions integration/nwo/standard_networks.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,47 @@ func MinimalRaft() *Config {
return config
}

func ThreeOrgRaft() *Config {
config := BasicEtcdRaft()

config.Organizations = append(
config.Organizations,
&Organization{
Name: "Org3",
MSPID: "Org3MSP",
Domain: "org3.example.com",
Users: 2,
CA: &CA{Hostname: "ca"},
},
)
config.Consortiums[0].Organizations = append(
config.Consortiums[0].Organizations,
"Org3",
)
config.SystemChannel.Profile = "ThreeOrgsOrdererGenesis"
config.Channels[0].Profile = "ThreeOrgsChannel"
config.Peers = append(
config.Peers,
&Peer{
Name: "peer0",
Organization: "Org3",
Channels: []*PeerChannel{
{Name: "testchannel", Anchor: true},
},
},
)
config.Profiles = []*Profile{{
Name: "ThreeOrgsOrdererGenesis",
Orderers: []string{"orderer"},
}, {
Name: "ThreeOrgsChannel",
Consortium: "SampleConsortium",
Organizations: []string{"Org1", "Org2", "Org3"},
}}

return config
}

func MultiChannelEtcdRaft() *Config {
config := MultiChannelBasicSolo()

Expand Down
14 changes: 13 additions & 1 deletion internal/pkg/gateway/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,17 @@ func TestEvaluate(t *testing.T) {
},
expectedEndorsers: []string{"localhost:7051"},
},
{
name: "five endorsers, prefer host peer despite no endpoint",
members: []networkMember{
{"id1", "", "msp1", 5},
{"id2", "peer1:8051", "msp1", 5},
{"id3", "peer2:9051", "msp2", 6},
{"id4", "peer3:10051", "msp2", 5},
{"id5", "peer4:11051", "msp3", 6},
},
expectedEndorsers: []string{"localhost:7051"},
},
{
name: "evaluate with targetOrganizations, prefer local org despite block height",
members: []networkMember{
Expand Down Expand Up @@ -974,6 +985,7 @@ func TestNilArgs(t *testing.T) {
&mocks.CommitFinder{},
&mocks.Eventer{},
&mocks.ACLChecker{},
common.PKIidType("id1"),
"localhost:7051",
"msp1",
config.GetOptions(viper.New()),
Expand Down Expand Up @@ -1080,7 +1092,7 @@ func prepareTest(t *testing.T, tt *testDef) *preparedTest {
EndorsementTimeout: endorsementTimeout,
}

server := newServer(localEndorser, disc, mockFinder, mockEventer, mockPolicy, "localhost:7051", "msp1", options)
server := newServer(localEndorser, disc, mockFinder, mockEventer, mockPolicy, common.PKIidType("id1"), "localhost:7051", "msp1", options)

dialer := &mocks.Dialer{}
dialer.Returns(nil, nil)
Expand Down
6 changes: 4 additions & 2 deletions internal/pkg/gateway/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

ab "github.com/hyperledger/fabric-protos-go/orderer"
"github.com/hyperledger/fabric-protos-go/peer"
"github.com/hyperledger/fabric/gossip/common"
"github.com/hyperledger/fabric/internal/pkg/comm"
"google.golang.org/grpc"
)
Expand All @@ -28,6 +29,7 @@ type orderer struct {
}

type endpointConfig struct {
pkiid common.PKIidType
address string
mspid string
}
Expand All @@ -47,7 +49,7 @@ type endpointFactory struct {
dialer dialer
}

func (ef *endpointFactory) newEndorser(address, mspid string, tlsRootCerts [][]byte) (*endorser, error) {
func (ef *endpointFactory) newEndorser(pkiid common.PKIidType, address, mspid string, tlsRootCerts [][]byte) (*endorser, error) {
conn, err := ef.newConnection(address, tlsRootCerts)
if err != nil {
return nil, err
Expand All @@ -58,7 +60,7 @@ func (ef *endpointFactory) newEndorser(address, mspid string, tlsRootCerts [][]b
}
return &endorser{
client: connectEndorser(conn),
endpointConfig: &endpointConfig{address: address, mspid: mspid},
endpointConfig: &endpointConfig{pkiid: pkiid, address: address, mspid: mspid},
}, nil
}

Expand Down
Loading

0 comments on commit 00910ba

Please sign in to comment.