/* Copyright IBM Corp All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ package discovery import ( "encoding/json" "io/ioutil" "os" "path/filepath" "strconv" "syscall" "time" docker "github.com/fsouza/go-dockerclient" "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric-protos-go/common" "github.com/hyperledger/fabric-protos-go/discovery" pm "github.com/hyperledger/fabric-protos-go/msp" "github.com/hyperledger/fabric/common/policydsl" "github.com/hyperledger/fabric/integration/channelparticipation" "github.com/hyperledger/fabric/integration/nwo" "github.com/hyperledger/fabric/integration/nwo/commands" "github.com/hyperledger/fabric/msp" "github.com/hyperledger/fabric/protoutil" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/onsi/gomega/gbytes" "github.com/onsi/gomega/gexec" "github.com/tedsuo/ifrit" ginkgomon "github.com/tedsuo/ifrit/ginkgomon_v2" ) var _ = Describe("DiscoveryService", func() { var ( testDir string client *docker.Client config *nwo.Config network *nwo.Network ordererRunner *ginkgomon.Runner ordererProcess ifrit.Process peerProcesses []ifrit.Process orderer *nwo.Orderer org1Peer0 *nwo.Peer org2Peer0 *nwo.Peer org3Peer0 *nwo.Peer ) BeforeEach(func() { var err error testDir, err = ioutil.TempDir("", "e2e-sd") Expect(err).NotTo(HaveOccurred()) client, err = docker.NewClientFromEnv() Expect(err).NotTo(HaveOccurred()) config = nwo.BasicEtcdRaft() Expect(config.Peers).To(HaveLen(2)) }) AfterEach(func() { if ordererProcess != nil { ordererProcess.Signal(syscall.SIGTERM) Eventually(ordererProcess.Wait(), network.EventuallyTimeout).Should(Receive()) } for _, process := range peerProcesses { process.Signal(syscall.SIGTERM) Eventually(process.Wait(), network.EventuallyTimeout).Should(Receive()) } if network != nil { network.Cleanup() } os.RemoveAll(testDir) }) Describe("basic etcdraft network without anchor peers", func() { BeforeEach(func() { By("generating a network with no anchor peers defined") // disable all anchor peers for _, p := range config.Peers { for _, pc := range p.Channels { pc.Anchor = false } } network = nwo.New(config, testDir, client, StartPort(), components) network.GenerateConfigTree() network.Bootstrap() By("asserting there are no anchor peers in the genesis block") assertAnchorPeers(network, false, "testchannel", "Org1", "Org2") // Start all the fabric processes var peerProcess ifrit.Process ordererRunner, ordererProcess, peerProcess = network.StartSingleOrdererNetwork("orderer") peerProcesses = append(peerProcesses, peerProcess) orderer = network.Orderer("orderer") channelparticipation.JoinOrdererJoinPeersAppChannel(network, "testchannel", orderer, ordererRunner) org1Peer0 = network.Peer("Org1", "peer0") org2Peer0 = network.Peer("Org2", "peer0") }) It("discovers network configuration even without anchor peers present", func() { chaincodeWhenNoAnchorPeers := nwo.Chaincode{ Name: "noanchorpeersjustyet", Version: "1.0", Path: "github.com/hyperledger/fabric/integration/chaincode/simple/cmd", Ctor: `{"Args":["init","a","100","b","200"]}`, Policy: `OR ('Org1MSP.member')`, } By("Deploying chaincode before anchor peers are defined in the channel") nwo.DeployChaincodeLegacy(network, "testchannel", orderer, chaincodeWhenNoAnchorPeers, org1Peer0) endorsersForChaincodeBeforeAnchorPeersExist := commands.Endorsers{ UserCert: network.PeerUserCert(org1Peer0, "User1"), UserKey: network.PeerUserKey(org1Peer0, "User1"), MSPID: network.Organization(org1Peer0.Organization).MSPID, Server: network.PeerAddress(org1Peer0, nwo.ListenPort), Channel: "testchannel", Chaincode: chaincodeWhenNoAnchorPeers.Name, } discoveryQuery := discoverEndorsers(network, endorsersForChaincodeBeforeAnchorPeersExist) Eventually(discoveryQuery, network.EventuallyTimeout).Should(BeEquivalentTo( []ChaincodeEndorsers{ { Chaincode: chaincodeWhenNoAnchorPeers.Name, EndorsersByGroups: map[string][]nwo.DiscoveredPeer{ "G0": {network.DiscoveredPeer(org1Peer0)}, }, Layouts: []*discovery.Layout{ { QuantitiesByGroup: map[string]uint32{"G0": 1}, }, }, }, }, )) }) It("discovers all peers after explicitly updating anchor peers", func() { By("discovering peers without anchors yields partitioned information") Eventually(nwo.DiscoverPeers(network, org1Peer0, "User1", "testchannel"), network.EventuallyTimeout).Should(ConsistOf( network.DiscoveredPeer(org1Peer0, "_lifecycle"), )) Eventually(nwo.DiscoverPeers(network, org2Peer0, "User1", "testchannel"), network.EventuallyTimeout).Should(ConsistOf( network.DiscoveredPeer(org2Peer0, "_lifecycle"), )) By("explicitly setting anchor peers on both organizations") network.UpdateOrgAnchorPeers(orderer, "testchannel", "Org1", network.PeersInOrg("Org1")) network.UpdateOrgAnchorPeers(orderer, "testchannel", "Org2", network.PeersInOrg("Org2")) config := nwo.GetConfig(network, org1Peer0, orderer, "testchannel") configGroups := config.GetChannelGroup().GetGroups()["Application"] Expect(configGroups.GetGroups()["Org1"].GetValues()["AnchorPeers"].GetValue()).ToNot(BeEmpty()) Expect(configGroups.GetGroups()["Org2"].GetValues()["AnchorPeers"].GetValue()).ToNot(BeEmpty()) By("discovering peers with anchors yields complete information") Eventually(nwo.DiscoverPeers(network, org1Peer0, "User1", "testchannel"), network.EventuallyTimeout).Should(ConsistOf( network.DiscoveredPeer(org1Peer0, "_lifecycle"), network.DiscoveredPeer(org2Peer0, "_lifecycle"), )) Eventually(nwo.DiscoverPeers(network, org2Peer0, "User1", "testchannel"), network.EventuallyTimeout).Should(ConsistOf( network.DiscoveredPeer(org1Peer0, "_lifecycle"), network.DiscoveredPeer(org2Peer0, "_lifecycle"), )) }) }) Describe("basic etcdraft network with anchor peers", func() { BeforeEach(func() { By("generating a network with anchor peers defined and adding a 3rd org") // add org3 with one peer (to generate cryptogen and configtx files) config.Organizations = append(config.Organizations, &nwo.Organization{ Name: "Org3", MSPID: "Org3MSP", Domain: "org3.example.com", EnableNodeOUs: true, Users: 2, CA: &nwo.CA{Hostname: "ca"}, }) config.Profiles[0].Organizations = append(config.Profiles[0].Organizations, "Org3") config.Peers = append(config.Peers, &nwo.Peer{ Name: "peer0", Organization: "Org3", Channels: []*nwo.PeerChannel{ {Name: "testchannel", Anchor: true}, }, }, ) network = nwo.New(config, testDir, client, StartPort(), components) network.GenerateConfigTree() network.Bootstrap() // initially remove Org3 peer0 from the network and later add it back to test joinbysnapshot peers := []*nwo.Peer{} for _, p := range network.Peers { if p.Organization != "Org3" { peers = append(peers, p) } } network.Peers = peers By("asserting the genesis block has anchor peers defined") assertAnchorPeers(network, true, "testchannel", "Org1", "Org2", "Org3") // Start all the fabric processes var peerProcess ifrit.Process ordererRunner, ordererProcess, peerProcess = network.StartSingleOrdererNetwork("orderer") peerProcesses = append(peerProcesses, peerProcess) orderer = network.Orderer("orderer") channelparticipation.JoinOrdererJoinPeersAppChannel(network, "testchannel", orderer, ordererRunner) org1Peer0 = network.Peer("Org1", "peer0") org2Peer0 = network.Peer("Org2", "peer0") }) It("discovers network configuration, endorsers, and peer membership", func() { // // bootstrapping a peer from snapshot // By("generating a snapshot at current block number on org1Peer0") blockNum := nwo.GetLedgerHeight(network, org1Peer0, "testchannel") - 1 submitSnapshotRequest(network, "testchannel", 0, org1Peer0, "Snapshot request submitted successfully") By("verifying snapshot completed on org1Peer0") verifyNoPendingSnapshotRequest(network, org1Peer0, "testchannel") snapshotDir := filepath.Join(network.PeerDir(org1Peer0), "filesystem", "snapshots", "completed", "testchannel", strconv.Itoa(blockNum)) By("adding peer org3Peer0 to the network") org3Peer0 = &nwo.Peer{ Name: "peer0", Organization: "Org3", Channels: []*nwo.PeerChannel{ {Name: "testchannel", Anchor: true}, }, } network.Peers = append(network.Peers, org3Peer0) By("starting peer org3Peer0") peerRunner := network.PeerRunner(org3Peer0) process := ifrit.Invoke(peerRunner) Eventually(process.Ready(), network.EventuallyTimeout).Should(BeClosed()) peerProcesses = append(peerProcesses, process) By("joining peer org3Peer0 to channel by a snapshot") joinBySnapshot(network, orderer, org3Peer0, "testchannel", snapshotDir) // // retrieving configuration and validating membership // By("retrieving the configuration from org1Peer0") discoveredConfig := discoverConfiguration(network, org1Peer0) By("retrieving the configuration from org3Peer0") discoveredConfig2 := discoverConfiguration(network, org3Peer0) By("comparing configuration from org1Peer0 and org3Peer0") Expect(proto.Equal(discoveredConfig, discoveredConfig2)).To(BeTrue()) By("validating the membership data") Expect(discoveredConfig.Msps).To(HaveLen(len(network.Organizations))) for _, o := range network.Orderers { org := network.Organization(o.Organization) mspConfig, err := msp.GetVerifyingMspConfig(network.OrdererOrgMSPDir(org), org.MSPID, "bccsp") Expect(err).NotTo(HaveOccurred()) Expect(discoveredConfig.Msps[org.MSPID]).To(Equal(unmarshalFabricMSPConfig(mspConfig))) } for _, p := range network.Peers { org := network.Organization(p.Organization) mspConfig, err := msp.GetVerifyingMspConfig(network.PeerOrgMSPDir(org), org.MSPID, "bccsp") Expect(err).NotTo(HaveOccurred()) Expect(discoveredConfig.Msps[org.MSPID]).To(Equal(unmarshalFabricMSPConfig(mspConfig))) } By("validating the orderers") Expect(discoveredConfig.Orderers).To(HaveLen(len(network.Orderers))) for _, orderer := range network.Orderers { ordererMSPID := network.Organization(orderer.Organization).MSPID Expect(discoveredConfig.Orderers[ordererMSPID].Endpoint).To(ConsistOf( &discovery.Endpoint{Host: "127.0.0.1", Port: uint32(network.OrdererPort(orderer, nwo.ListenPort))}, )) } // // discovering peers and endorsers // By("discovering peers before deploying any user chaincodes") Eventually(nwo.DiscoverPeers(network, org1Peer0, "User1", "testchannel"), network.EventuallyTimeout).Should(ConsistOf( network.DiscoveredPeer(org1Peer0, "_lifecycle"), network.DiscoveredPeer(org2Peer0, "_lifecycle"), network.DiscoveredPeer(org3Peer0, "_lifecycle"), )) By("discovering endorsers when missing chaincode") endorsers := commands.Endorsers{ UserCert: network.PeerUserCert(org1Peer0, "User1"), UserKey: network.PeerUserKey(org1Peer0, "User1"), MSPID: network.Organization(org1Peer0.Organization).MSPID, Server: network.PeerAddress(org1Peer0, nwo.ListenPort), Channel: "testchannel", Chaincode: "mycc", } sess, err := network.Discover(endorsers) Expect(err).NotTo(HaveOccurred()) Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(1)) Expect(sess.Err).To(gbytes.Say(`failed constructing descriptor for chaincodes: