github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/integration/gossip/gossip_test.go (about)

     1  /*
     2   * Copyright IBM Corp. All Rights Reserved.
     3   *
     4   * SPDX-License-Identifier: Apache-2.0
     5   */
     6  
     7  package gossip
     8  
     9  import (
    10  	"fmt"
    11  	"io/ioutil"
    12  	"os"
    13  	"syscall"
    14  	"time"
    15  
    16  	docker "github.com/fsouza/go-dockerclient"
    17  	"github.com/osdi23p228/fabric/integration/nwo"
    18  	"github.com/osdi23p228/fabric/integration/nwo/commands"
    19  	. "github.com/onsi/ginkgo"
    20  	. "github.com/onsi/gomega"
    21  	"github.com/onsi/gomega/gbytes"
    22  	"github.com/onsi/gomega/gexec"
    23  	"github.com/tedsuo/ifrit"
    24  	"github.com/tedsuo/ifrit/ginkgomon"
    25  )
    26  
    27  var _ = Describe("Gossip State Transfer and Membership", func() {
    28  	var (
    29  		testDir     string
    30  		network     *nwo.Network
    31  		nwprocs     *networkProcesses
    32  		chaincode   nwo.Chaincode
    33  		channelName string
    34  	)
    35  
    36  	BeforeEach(func() {
    37  		var err error
    38  		testDir, err = ioutil.TempDir("", "gossip-statexfer")
    39  		Expect(err).NotTo(HaveOccurred())
    40  
    41  		dockerClient, err := docker.NewClientFromEnv()
    42  		Expect(err).NotTo(HaveOccurred())
    43  
    44  		channelName = "testchannel"
    45  		network = nwo.New(nwo.FullSolo(), testDir, dockerClient, StartPort(), components)
    46  		network.GenerateConfigTree()
    47  
    48  		nwprocs = &networkProcesses{
    49  			network:       network,
    50  			peerRunners:   map[string]*ginkgomon.Runner{},
    51  			peerProcesses: map[string]ifrit.Process{},
    52  		}
    53  
    54  		chaincode = nwo.Chaincode{
    55  			Name:    "mycc",
    56  			Version: "0.0",
    57  			Path:    "github.com/osdi23p228/fabric/integration/chaincode/simple/cmd",
    58  			Ctor:    `{"Args":["init","a","100","b","200"]}`,
    59  			Policy:  `OR ('Org1MSP.member','Org2MSP.member')`,
    60  		}
    61  	})
    62  
    63  	AfterEach(func() {
    64  		if nwprocs != nil {
    65  			nwprocs.terminateAll()
    66  		}
    67  		if network != nil {
    68  			network.Cleanup()
    69  		}
    70  		os.RemoveAll(testDir)
    71  	})
    72  
    73  	It("syncs blocks from the peer via state transfer when no orderer is available", func() {
    74  		//  modify peer config to enable state transfer on all peers, and configure leaders as follows:
    75  		//  Org1: leader election
    76  		//  Org2: no leader election
    77  		//      peer0: follower
    78  		//      peer1: leader
    79  		for _, peer := range network.Peers {
    80  			if peer.Organization == "Org1" {
    81  				if peer.Name == "peer0" {
    82  					core := network.ReadPeerConfig(peer)
    83  					core.Peer.Gossip.State.Enabled = true
    84  					core.Peer.Gossip.UseLeaderElection = true
    85  					core.Peer.Gossip.OrgLeader = false
    86  					network.WritePeerConfig(peer, core)
    87  				}
    88  				if peer.Name == "peer1" {
    89  					core := network.ReadPeerConfig(peer)
    90  					core.Peer.Gossip.State.Enabled = true
    91  					core.Peer.Gossip.UseLeaderElection = true
    92  					core.Peer.Gossip.OrgLeader = false
    93  					core.Peer.Gossip.Bootstrap = fmt.Sprintf("127.0.0.1:%d", network.ReservePort())
    94  					network.WritePeerConfig(peer, core)
    95  				}
    96  			}
    97  			if peer.Organization == "Org2" {
    98  				core := network.ReadPeerConfig(peer)
    99  				core.Peer.Gossip.State.Enabled = true
   100  				core.Peer.Gossip.UseLeaderElection = false
   101  				core.Peer.Gossip.OrgLeader = peer.Name == "peer1"
   102  				network.WritePeerConfig(peer, core)
   103  			}
   104  		}
   105  
   106  		network.Bootstrap()
   107  		orderer := network.Orderer("orderer")
   108  		nwprocs.ordererRunner = network.OrdererRunner(orderer)
   109  		nwprocs.ordererProcess = ifrit.Invoke(nwprocs.ordererRunner)
   110  		Eventually(nwprocs.ordererProcess.Ready(), network.EventuallyTimeout).Should(BeClosed())
   111  
   112  		peer0Org1, peer1Org1 := network.Peer("Org1", "peer0"), network.Peer("Org1", "peer1")
   113  		peer0Org2, peer1Org2 := network.Peer("Org2", "peer0"), network.Peer("Org2", "peer1")
   114  
   115  		By("bringing up all four peers")
   116  		startPeers(nwprocs, false, peer0Org1, peer1Org1, peer0Org2, peer1Org2)
   117  
   118  		network.CreateChannel(channelName, orderer, peer0Org1)
   119  		By("joining all peers to channel")
   120  		network.JoinChannel(channelName, orderer, peer0Org1, peer1Org1, peer0Org2, peer1Org2)
   121  
   122  		network.UpdateChannelAnchors(orderer, channelName)
   123  
   124  		// base peer will be used for chaincode interactions
   125  		basePeerForTransactions := peer0Org1
   126  		nwo.DeployChaincodeLegacy(network, channelName, orderer, chaincode, basePeerForTransactions)
   127  
   128  		By("verifying peer0Org1 discovers all the peers and the legacy chaincode before starting the tests")
   129  		Eventually(nwo.DiscoverPeers(network, peer0Org1, "User1", "testchannel"), network.EventuallyTimeout).Should(ConsistOf(
   130  			network.DiscoveredPeer(peer0Org1, "_lifecycle", "mycc"),
   131  			network.DiscoveredPeer(peer1Org1, "_lifecycle"),
   132  			network.DiscoveredPeer(peer0Org2, "_lifecycle"),
   133  			network.DiscoveredPeer(peer1Org2, "_lifecycle"),
   134  		))
   135  
   136  		By("STATE TRANSFER TEST 1: newly joined peers should receive blocks from the peers that are already up")
   137  
   138  		// Note, a better test would be to bring orderer down before joining the two peers.
   139  		// However, network.JoinChannel() requires orderer to be up so that genesis block can be fetched from orderer before joining peers.
   140  		// Therefore, for now we've joined all four peers and stop the two peers that should be synced up.
   141  		stopPeers(nwprocs, peer1Org1, peer1Org2)
   142  
   143  		By("confirming peer0Org1 was elected to be a leader")
   144  		expectedMsg := "Elected as a leader, starting delivery service for channel testchannel"
   145  		Eventually(nwprocs.peerRunners[peer0Org1.ID()].Err(), network.EventuallyTimeout).Should(gbytes.Say(expectedMsg))
   146  
   147  		sendTransactionsAndSyncUpPeers(nwprocs, orderer, basePeerForTransactions, channelName, peer1Org1, peer1Org2)
   148  
   149  		By("STATE TRANSFER TEST 2: restarted peers should receive blocks from the peers that are already up")
   150  		basePeerForTransactions = peer1Org1
   151  		nwo.InstallChaincodeLegacy(network, chaincode, basePeerForTransactions)
   152  
   153  		By("verifying peer0Org1 discovers all the peers and the additional legacy chaincode installed on peer1Org1")
   154  		Eventually(nwo.DiscoverPeers(network, peer0Org1, "User1", "testchannel"), network.EventuallyTimeout).Should(ConsistOf(
   155  			network.DiscoveredPeer(peer0Org1, "_lifecycle", "mycc"),
   156  			network.DiscoveredPeer(peer1Org1, "_lifecycle", "mycc"),
   157  			network.DiscoveredPeer(peer0Org2, "_lifecycle"),
   158  			network.DiscoveredPeer(peer1Org2, "_lifecycle"),
   159  		))
   160  
   161  		By("stopping peer0Org1 (currently elected leader in Org1) and peer1Org2 (static leader in Org2)")
   162  		stopPeers(nwprocs, peer0Org1, peer1Org2)
   163  
   164  		By("confirming peer1Org1 was elected to be a leader")
   165  		Eventually(nwprocs.peerRunners[peer1Org1.ID()].Err(), network.EventuallyTimeout).Should(gbytes.Say(expectedMsg))
   166  
   167  		// Note that with the static leader in Org2 down, the static follower peer0Org2 will also get blocks via state transfer
   168  		// This effectively tests leader election as well, since the newly elected leader in Org1 (peer1Org1) will be the only peer
   169  		// that receives blocks from orderer and will therefore serve as the provider of blocks to all other peers.
   170  		sendTransactionsAndSyncUpPeers(nwprocs, orderer, basePeerForTransactions, channelName, peer0Org1, peer1Org2)
   171  
   172  		By("verifying peer0Org1 can still discover all the peers and the legacy chaincode after it has been restarted")
   173  		Eventually(nwo.DiscoverPeers(network, peer0Org1, "User1", "testchannel"), network.EventuallyTimeout).Should(ConsistOf(
   174  			network.DiscoveredPeer(peer0Org1, "_lifecycle", "mycc"),
   175  			network.DiscoveredPeer(peer1Org1, "_lifecycle", "mycc"),
   176  			network.DiscoveredPeer(peer0Org2, "_lifecycle"),
   177  			network.DiscoveredPeer(peer1Org2, "_lifecycle"),
   178  		))
   179  
   180  	})
   181  
   182  	When("gossip connection is lost and restored", func() {
   183  		var (
   184  			orderer       *nwo.Orderer
   185  			peerEndpoints map[string]string = map[string]string{}
   186  		)
   187  
   188  		BeforeEach(func() {
   189  			//  modify peer config
   190  			for _, peer := range network.Peers {
   191  				core := network.ReadPeerConfig(peer)
   192  				core.Peer.Gossip.AliveTimeInterval = 1 * time.Second
   193  				core.Peer.Gossip.AliveExpirationTimeout = 2 * core.Peer.Gossip.AliveTimeInterval
   194  				core.Peer.Gossip.ReconnectInterval = 2 * time.Second
   195  				core.Peer.Gossip.MsgExpirationFactor = 2
   196  				core.Peer.Gossip.MaxConnectionAttempts = 10
   197  				network.WritePeerConfig(peer, core)
   198  				peerEndpoints[peer.ID()] = core.Peer.Address
   199  			}
   200  
   201  			network.Bootstrap()
   202  			orderer = network.Orderer("orderer")
   203  			nwprocs.ordererRunner = network.OrdererRunner(orderer)
   204  			nwprocs.ordererProcess = ifrit.Invoke(nwprocs.ordererRunner)
   205  			Eventually(nwprocs.ordererProcess.Ready(), network.EventuallyTimeout).Should(BeClosed())
   206  		})
   207  
   208  		It("updates membership when peers in the same org are stopped and restarted", func() {
   209  			peer0Org1 := network.Peer("Org1", "peer0")
   210  			peer1Org1 := network.Peer("Org1", "peer1")
   211  
   212  			By("bringing up all peers")
   213  			startPeers(nwprocs, false, peer0Org1, peer1Org1)
   214  
   215  			By("creating and joining a channel")
   216  			network.CreateChannel(channelName, orderer, peer0Org1)
   217  			network.JoinChannel(channelName, orderer, peer0Org1, peer1Org1)
   218  			network.UpdateChannelAnchors(orderer, channelName)
   219  
   220  			By("verifying peer1Org1 discovers all the peers before testing membership change on it")
   221  			Eventually(nwo.DiscoverPeers(network, peer1Org1, "User1", "testchannel"), network.EventuallyTimeout).Should(ConsistOf(
   222  				network.DiscoveredPeer(peer0Org1, "_lifecycle"),
   223  				network.DiscoveredPeer(peer1Org1, "_lifecycle"),
   224  			))
   225  
   226  			By("verifying membership change on peer1Org1 when an anchor peer in the same org is stopped and restarted")
   227  			expectedMsgFromExpirationCallback := fmt.Sprintf("Do not remove bootstrap or anchor peer endpoint %s from membership", peerEndpoints[peer0Org1.ID()])
   228  			assertPeerMembershipUpdate(network, peer1Org1, []*nwo.Peer{peer0Org1}, nwprocs, expectedMsgFromExpirationCallback)
   229  
   230  			By("verifying peer0Org1 discovers all the peers before testing membership change on it")
   231  			Eventually(nwo.DiscoverPeers(network, peer0Org1, "User1", "testchannel"), network.EventuallyTimeout).Should(ConsistOf(
   232  				network.DiscoveredPeer(peer0Org1, "_lifecycle"),
   233  				network.DiscoveredPeer(peer1Org1, "_lifecycle"),
   234  			))
   235  
   236  			By("verifying membership change on peer0Org1 when a non-anchor peer in the same org is stopped and restarted")
   237  			expectedMsgFromExpirationCallback = fmt.Sprintf("Removing member: Endpoint: %s", peerEndpoints[peer1Org1.ID()])
   238  			assertPeerMembershipUpdate(network, peer0Org1, []*nwo.Peer{peer1Org1}, nwprocs, expectedMsgFromExpirationCallback)
   239  		})
   240  
   241  		It("updates peer membership when peers in another org are stopped and restarted", func() {
   242  			peer0Org1, peer1Org1 := network.Peer("Org1", "peer0"), network.Peer("Org1", "peer1")
   243  			peer0Org2, peer1Org2 := network.Peer("Org2", "peer0"), network.Peer("Org2", "peer1")
   244  
   245  			By("bringing up all peers")
   246  			startPeers(nwprocs, false, peer0Org1, peer1Org1, peer0Org2, peer1Org2)
   247  
   248  			By("creating and joining a channel")
   249  			network.CreateChannel(channelName, orderer, peer0Org1)
   250  			network.JoinChannel(channelName, orderer, peer0Org1, peer1Org1, peer0Org2, peer1Org2)
   251  			network.UpdateChannelAnchors(orderer, channelName)
   252  
   253  			By("verifying membership on peer1Org1")
   254  			Eventually(nwo.DiscoverPeers(network, peer1Org1, "User1", "testchannel"), network.EventuallyTimeout).Should(ConsistOf(
   255  				network.DiscoveredPeer(peer0Org1, "_lifecycle"),
   256  				network.DiscoveredPeer(peer1Org1, "_lifecycle"),
   257  				network.DiscoveredPeer(peer0Org2, "_lifecycle"),
   258  				network.DiscoveredPeer(peer1Org2, "_lifecycle"),
   259  			))
   260  
   261  			By("stopping anchor peer peer0Org1 to have only one peer in org1")
   262  			stopPeers(nwprocs, peer0Org1)
   263  
   264  			By("verifying peer membership update when peers in another org are stopped and restarted")
   265  			expectedMsgFromExpirationCallback := fmt.Sprintf("Do not remove bootstrap or anchor peer endpoint %s from membership", peerEndpoints[peer0Org2.ID()])
   266  			assertPeerMembershipUpdate(network, peer1Org1, []*nwo.Peer{peer0Org2, peer1Org2}, nwprocs, expectedMsgFromExpirationCallback)
   267  		})
   268  	})
   269  })
   270  
   271  func runTransactions(n *nwo.Network, orderer *nwo.Orderer, peer *nwo.Peer, chaincodeName string, channelID string) {
   272  	for i := 0; i < 5; i++ {
   273  		sess, err := n.PeerUserSession(peer, "User1", commands.ChaincodeInvoke{
   274  			ChannelID: channelID,
   275  			Orderer:   n.OrdererAddress(orderer, nwo.ListenPort),
   276  			Name:      chaincodeName,
   277  			Ctor:      `{"Args":["invoke","a","b","10"]}`,
   278  			PeerAddresses: []string{
   279  				n.PeerAddress(peer, nwo.ListenPort),
   280  			},
   281  			WaitForEvent: true,
   282  		})
   283  		Expect(err).NotTo(HaveOccurred())
   284  		Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
   285  		Expect(sess.Err).To(gbytes.Say("Chaincode invoke successful. result: status:200"))
   286  	}
   287  }
   288  
   289  // networkProcesses holds references to the network, its runners, and processes.
   290  type networkProcesses struct {
   291  	network *nwo.Network
   292  
   293  	ordererRunner  *ginkgomon.Runner
   294  	ordererProcess ifrit.Process
   295  
   296  	peerRunners   map[string]*ginkgomon.Runner
   297  	peerProcesses map[string]ifrit.Process
   298  }
   299  
   300  func (n *networkProcesses) terminateAll() {
   301  	if n.ordererProcess != nil {
   302  		n.ordererProcess.Signal(syscall.SIGTERM)
   303  		Eventually(n.ordererProcess.Wait(), n.network.EventuallyTimeout).Should(Receive())
   304  	}
   305  	for _, process := range n.peerProcesses {
   306  		process.Signal(syscall.SIGTERM)
   307  		Eventually(process.Wait(), n.network.EventuallyTimeout).Should(Receive())
   308  	}
   309  }
   310  
   311  func startPeers(n *networkProcesses, forceStateTransfer bool, peersToStart ...*nwo.Peer) {
   312  	env := []string{"FABRIC_LOGGING_SPEC=info:gossip.state=debug:gossip.discovery=debug"}
   313  
   314  	// Setting CORE_PEER_GOSSIP_STATE_CHECKINTERVAL to 200ms (from default of 10s) will ensure that state transfer happens quickly,
   315  	// before blocks are gossipped through normal mechanisms
   316  	if forceStateTransfer {
   317  		env = append(env, "CORE_PEER_GOSSIP_STATE_CHECKINTERVAL=200ms")
   318  	}
   319  
   320  	for _, peer := range peersToStart {
   321  		runner := n.network.PeerRunner(peer, env...)
   322  		process := ifrit.Invoke(runner)
   323  		Eventually(process.Ready(), n.network.EventuallyTimeout).Should(BeClosed())
   324  
   325  		n.peerProcesses[peer.ID()] = process
   326  		n.peerRunners[peer.ID()] = runner
   327  	}
   328  }
   329  
   330  func stopPeers(n *networkProcesses, peersToStop ...*nwo.Peer) {
   331  	for _, peer := range peersToStop {
   332  		id := peer.ID()
   333  		proc := n.peerProcesses[id]
   334  		proc.Signal(syscall.SIGTERM)
   335  		Eventually(proc.Wait(), n.network.EventuallyTimeout).Should(Receive())
   336  		delete(n.peerProcesses, id)
   337  	}
   338  }
   339  
   340  func assertPeersLedgerHeight(n *nwo.Network, peersToSyncUp []*nwo.Peer, expectedVal int, channelID string) {
   341  	for _, peer := range peersToSyncUp {
   342  		Eventually(func() int {
   343  			return nwo.GetLedgerHeight(n, peer, channelID)
   344  		}, n.EventuallyTimeout).Should(Equal(expectedVal))
   345  	}
   346  }
   347  
   348  // send transactions, stop orderering server, then start peers to ensure they received blcoks via state transfer
   349  func sendTransactionsAndSyncUpPeers(n *networkProcesses, orderer *nwo.Orderer, basePeer *nwo.Peer, channelName string, peersToSyncUp ...*nwo.Peer) {
   350  	By("creating transactions")
   351  	runTransactions(n.network, orderer, basePeer, "mycc", channelName)
   352  	basePeerLedgerHeight := nwo.GetLedgerHeight(n.network, basePeer, channelName)
   353  
   354  	By("stopping orderer")
   355  	n.ordererProcess.Signal(syscall.SIGTERM)
   356  	Eventually(n.ordererProcess.Wait(), n.network.EventuallyTimeout).Should(Receive())
   357  	n.ordererProcess = nil
   358  
   359  	By("starting the peers contained in the peersToSyncUp list")
   360  	startPeers(n, true, peersToSyncUp...)
   361  
   362  	By("ensuring the peers are synced up")
   363  	assertPeersLedgerHeight(n.network, peersToSyncUp, basePeerLedgerHeight, channelName)
   364  
   365  	By("restarting orderer")
   366  	n.ordererRunner = n.network.OrdererRunner(orderer)
   367  	n.ordererProcess = ifrit.Invoke(n.ordererRunner)
   368  	Eventually(n.ordererProcess.Ready(), n.network.EventuallyTimeout).Should(BeClosed())
   369  }
   370  
   371  // assertPeerMembershipUpdate stops and restart peersToRestart and verify peer membership
   372  func assertPeerMembershipUpdate(network *nwo.Network, peer *nwo.Peer, peersToRestart []*nwo.Peer, nwprocs *networkProcesses, expectedMsgFromExpirationCallback string) {
   373  	stopPeers(nwprocs, peersToRestart...)
   374  
   375  	// timeout is the same amount of time as it takes to remove a message from the aliveMsgStore, and add a second as buffer
   376  	core := network.ReadPeerConfig(peer)
   377  	timeout := core.Peer.Gossip.AliveExpirationTimeout*time.Duration(core.Peer.Gossip.MsgExpirationFactor) + time.Second
   378  	By("verifying peer membership after all other peers are stopped")
   379  	Eventually(nwo.DiscoverPeers(network, peer, "User1", "testchannel"), timeout, 100*time.Millisecond).Should(ConsistOf(
   380  		network.DiscoveredPeer(peer, "_lifecycle"),
   381  	))
   382  
   383  	By("verifying expected log message from expiration callback")
   384  	runner := nwprocs.peerRunners[peer.ID()]
   385  	Eventually(runner.Err(), network.EventuallyTimeout).Should(gbytes.Say(expectedMsgFromExpirationCallback))
   386  
   387  	By("restarting peers")
   388  	startPeers(nwprocs, false, peersToRestart...)
   389  
   390  	By("verifying peer membership, expected to discover restarted peers")
   391  	expectedPeers := make([]nwo.DiscoveredPeer, len(peersToRestart)+1)
   392  	expectedPeers[0] = network.DiscoveredPeer(peer, "_lifecycle")
   393  	for i, p := range peersToRestart {
   394  		expectedPeers[i+1] = network.DiscoveredPeer(p, "_lifecycle")
   395  	}
   396  	timeout = 3 * core.Peer.Gossip.ReconnectInterval
   397  	Eventually(nwo.DiscoverPeers(network, peer, "User1", "testchannel"), timeout, 100*time.Millisecond).Should(ConsistOf(expectedPeers))
   398  }