github.com/MetalBlockchain/metalgo@v1.11.9/tests/e2e/faultinjection/duplicate_node_id.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package faultinjection 5 6 import ( 7 "context" 8 "fmt" 9 10 "github.com/stretchr/testify/require" 11 12 "github.com/MetalBlockchain/metalgo/api/info" 13 "github.com/MetalBlockchain/metalgo/config" 14 "github.com/MetalBlockchain/metalgo/ids" 15 "github.com/MetalBlockchain/metalgo/tests/fixture/e2e" 16 "github.com/MetalBlockchain/metalgo/tests/fixture/tmpnet" 17 "github.com/MetalBlockchain/metalgo/utils/set" 18 19 ginkgo "github.com/onsi/ginkgo/v2" 20 ) 21 22 var _ = ginkgo.Describe("Duplicate node handling", func() { 23 require := require.New(ginkgo.GinkgoT()) 24 25 ginkgo.It("should ensure that a given Node ID (i.e. staking keypair) can be used at most once on a network", func() { 26 network := e2e.Env.GetNetwork() 27 28 ginkgo.By("creating new node") 29 node1 := e2e.AddEphemeralNode(network, tmpnet.FlagsMap{}) 30 e2e.WaitForHealthy(node1) 31 32 ginkgo.By("checking that the new node is connected to its peers") 33 checkConnectedPeers(network.Nodes, node1) 34 35 ginkgo.By("creating a second new node with the same staking keypair as the first new node") 36 node1Flags := node1.Flags 37 node2Flags := tmpnet.FlagsMap{ 38 config.StakingTLSKeyContentKey: node1Flags[config.StakingTLSKeyContentKey], 39 config.StakingCertContentKey: node1Flags[config.StakingCertContentKey], 40 // Construct a unique data dir to ensure the two nodes' data will be stored 41 // separately. Usually the dir name is the node ID but in this one case the nodes have 42 // the same node ID. 43 config.DataDirKey: fmt.Sprintf("%s-second", node1Flags[config.DataDirKey]), 44 } 45 node2 := e2e.AddEphemeralNode(network, node2Flags) 46 47 ginkgo.By("checking that the second new node fails to become healthy before timeout") 48 err := tmpnet.WaitForHealthy(e2e.DefaultContext(), node2) 49 require.ErrorIs(err, context.DeadlineExceeded) 50 51 ginkgo.By("stopping the first new node") 52 require.NoError(node1.Stop(e2e.DefaultContext())) 53 54 ginkgo.By("checking that the second new node becomes healthy within timeout") 55 e2e.WaitForHealthy(node2) 56 57 ginkgo.By("checking that the second new node is connected to its peers") 58 checkConnectedPeers(network.Nodes, node2) 59 60 // A bootstrap check was already performed by the second node. 61 }) 62 }) 63 64 // Check that a new node is connected to existing nodes and vice versa 65 func checkConnectedPeers(existingNodes []*tmpnet.Node, newNode *tmpnet.Node) { 66 require := require.New(ginkgo.GinkgoT()) 67 68 // Collect the node ids of the new node's peers 69 infoClient := info.NewClient(newNode.URI) 70 peers, err := infoClient.Peers(e2e.DefaultContext()) 71 require.NoError(err) 72 peerIDs := set.NewSet[ids.NodeID](len(existingNodes)) 73 for _, peer := range peers { 74 peerIDs.Add(peer.ID) 75 } 76 77 for _, existingNode := range existingNodes { 78 // Check that the existing node is a peer of the new node 79 require.True(peerIDs.Contains(existingNode.NodeID)) 80 81 // Check that the new node is a peer 82 infoClient := info.NewClient(existingNode.URI) 83 peers, err := infoClient.Peers(e2e.DefaultContext()) 84 require.NoError(err) 85 isPeer := false 86 for _, peer := range peers { 87 if peer.ID == newNode.NodeID { 88 isPeer = true 89 break 90 } 91 } 92 require.True(isPeer) 93 } 94 }