github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/p2p/fork_test.go (about) 1 package p2p 2 3 import ( 4 "context" 5 "math/rand" 6 "os" 7 "path" 8 "strconv" 9 "testing" 10 "time" 11 12 "github.com/ethereum/go-ethereum/p2p/discover" 13 "github.com/ethereum/go-ethereum/p2p/enode" 14 "github.com/ethereum/go-ethereum/p2p/enr" 15 ma "github.com/multiformats/go-multiaddr" 16 types "github.com/prysmaticlabs/eth2-types" 17 mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" 18 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 19 pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" 20 "github.com/prysmaticlabs/prysm/shared/bytesutil" 21 "github.com/prysmaticlabs/prysm/shared/p2putils" 22 "github.com/prysmaticlabs/prysm/shared/params" 23 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 24 "github.com/prysmaticlabs/prysm/shared/testutil/require" 25 "github.com/sirupsen/logrus" 26 logTest "github.com/sirupsen/logrus/hooks/test" 27 ) 28 29 func TestStartDiscv5_DifferentForkDigests(t *testing.T) { 30 port := 2000 31 ipAddr, pkey := createAddrAndPrivKey(t) 32 genesisTime := time.Now() 33 genesisValidatorsRoot := make([]byte, 32) 34 s := &Service{ 35 cfg: &Config{ 36 UDPPort: uint(port), 37 StateNotifier: &mock.MockStateNotifier{}, 38 }, 39 genesisTime: genesisTime, 40 genesisValidatorsRoot: genesisValidatorsRoot, 41 } 42 bootListener, err := s.createListener(ipAddr, pkey) 43 require.NoError(t, err) 44 defer bootListener.Close() 45 46 bootNode := bootListener.Self() 47 cfg := &Config{ 48 Discv5BootStrapAddr: []string{bootNode.String()}, 49 UDPPort: uint(port), 50 StateNotifier: &mock.MockStateNotifier{}, 51 } 52 53 var listeners []*discover.UDPv5 54 for i := 1; i <= 5; i++ { 55 port = 3000 + i 56 cfg.UDPPort = uint(port) 57 ipAddr, pkey := createAddrAndPrivKey(t) 58 59 // We give every peer a different genesis validators root, which 60 // will cause each peer to have a different ForkDigest, preventing 61 // them from connecting according to our discovery rules for Ethereum consensus. 62 root := make([]byte, 32) 63 copy(root, strconv.Itoa(port)) 64 s = &Service{ 65 cfg: cfg, 66 genesisTime: genesisTime, 67 genesisValidatorsRoot: root, 68 } 69 listener, err := s.startDiscoveryV5(ipAddr, pkey) 70 assert.NoError(t, err, "Could not start discovery for node") 71 listeners = append(listeners, listener) 72 } 73 defer func() { 74 // Close down all peers. 75 for _, listener := range listeners { 76 listener.Close() 77 } 78 }() 79 80 // Wait for the nodes to have their local routing tables to be populated with the other nodes 81 time.Sleep(discoveryWaitTime) 82 83 lastListener := listeners[len(listeners)-1] 84 nodes := lastListener.Lookup(bootNode.ID()) 85 if len(nodes) < 4 { 86 t.Errorf("The node's local table doesn't have the expected number of nodes. "+ 87 "Expected more than or equal to %d but got %d", 4, len(nodes)) 88 } 89 90 // Now, we start a new p2p service. It should have no peers aside from the 91 // bootnode given all nodes provided by discv5 will have different fork digests. 92 cfg.UDPPort = 14000 93 cfg.TCPPort = 14001 94 cfg.MaxPeers = 30 95 s, err = NewService(context.Background(), cfg) 96 require.NoError(t, err) 97 s.genesisTime = genesisTime 98 s.genesisValidatorsRoot = make([]byte, 32) 99 s.dv5Listener = lastListener 100 var addrs []ma.Multiaddr 101 102 for _, n := range nodes { 103 if s.filterPeer(n) { 104 addr, err := convertToSingleMultiAddr(n) 105 require.NoError(t, err) 106 addrs = append(addrs, addr) 107 } 108 } 109 110 // We should not have valid peers if the fork digest mismatched. 111 assert.Equal(t, 0, len(addrs), "Expected 0 valid peers") 112 require.NoError(t, s.Stop()) 113 } 114 115 func TestStartDiscv5_SameForkDigests_DifferentNextForkData(t *testing.T) { 116 hook := logTest.NewGlobal() 117 logrus.SetLevel(logrus.TraceLevel) 118 port := 2000 119 ipAddr, pkey := createAddrAndPrivKey(t) 120 genesisTime := time.Now() 121 genesisValidatorsRoot := make([]byte, 32) 122 s := &Service{ 123 cfg: &Config{UDPPort: uint(port)}, 124 genesisTime: genesisTime, 125 genesisValidatorsRoot: genesisValidatorsRoot, 126 stateNotifier: &mock.MockStateNotifier{}, 127 } 128 bootListener, err := s.createListener(ipAddr, pkey) 129 require.NoError(t, err) 130 defer bootListener.Close() 131 132 bootNode := bootListener.Self() 133 cfg := &Config{ 134 Discv5BootStrapAddr: []string{bootNode.String()}, 135 UDPPort: uint(port), 136 } 137 138 params.SetupTestConfigCleanup(t) 139 var listeners []*discover.UDPv5 140 for i := 1; i <= 5; i++ { 141 port = 3000 + i 142 cfg.UDPPort = uint(port) 143 ipAddr, pkey := createAddrAndPrivKey(t) 144 145 c := params.BeaconConfig() 146 nextForkEpoch := types.Epoch(i) 147 c.NextForkEpoch = nextForkEpoch 148 params.OverrideBeaconConfig(c) 149 150 // We give every peer a different genesis validators root, which 151 // will cause each peer to have a different ForkDigest, preventing 152 // them from connecting according to our discovery rules for Ethereum consensus. 153 s = &Service{ 154 cfg: cfg, 155 genesisTime: genesisTime, 156 genesisValidatorsRoot: genesisValidatorsRoot, 157 stateNotifier: &mock.MockStateNotifier{}, 158 } 159 listener, err := s.startDiscoveryV5(ipAddr, pkey) 160 assert.NoError(t, err, "Could not start discovery for node") 161 listeners = append(listeners, listener) 162 } 163 defer func() { 164 // Close down all peers. 165 for _, listener := range listeners { 166 listener.Close() 167 } 168 }() 169 170 // Wait for the nodes to have their local routing tables to be populated with the other nodes 171 time.Sleep(discoveryWaitTime) 172 173 lastListener := listeners[len(listeners)-1] 174 nodes := lastListener.Lookup(bootNode.ID()) 175 if len(nodes) < 4 { 176 t.Errorf("The node's local table doesn't have the expected number of nodes. "+ 177 "Expected more than or equal to %d but got %d", 4, len(nodes)) 178 } 179 180 // Now, we start a new p2p service. It should have no peers aside from the 181 // bootnode given all nodes provided by discv5 will have different fork digests. 182 cfg.UDPPort = 14000 183 cfg.TCPPort = 14001 184 cfg.MaxPeers = 30 185 cfg.StateNotifier = &mock.MockStateNotifier{} 186 s, err = NewService(context.Background(), cfg) 187 require.NoError(t, err) 188 189 s.genesisTime = genesisTime 190 s.genesisValidatorsRoot = make([]byte, 32) 191 s.dv5Listener = lastListener 192 var addrs []ma.Multiaddr 193 194 for _, n := range nodes { 195 if s.filterPeer(n) { 196 addr, err := convertToSingleMultiAddr(n) 197 require.NoError(t, err) 198 addrs = append(addrs, addr) 199 } 200 } 201 if len(addrs) == 0 { 202 t.Error("Expected to have valid peers, got 0") 203 } 204 205 require.LogsContain(t, hook, "Peer matches fork digest but has different next fork epoch") 206 require.NoError(t, s.Stop()) 207 } 208 209 func TestDiscv5_AddRetrieveForkEntryENR(t *testing.T) { 210 params.SetupTestConfigCleanup(t) 211 c := params.BeaconConfig() 212 c.ForkVersionSchedule = map[types.Epoch][]byte{ 213 0: params.BeaconConfig().GenesisForkVersion, 214 1: {0, 0, 0, 1}, 215 } 216 nextForkEpoch := types.Epoch(1) 217 nextForkVersion := []byte{0, 0, 0, 1} 218 c.NextForkEpoch = nextForkEpoch 219 c.NextForkVersion = nextForkVersion 220 params.OverrideBeaconConfig(c) 221 222 genesisTime := time.Now() 223 genesisValidatorsRoot := make([]byte, 32) 224 digest, err := p2putils.CreateForkDigest(genesisTime, make([]byte, 32)) 225 require.NoError(t, err) 226 enrForkID := &pb.ENRForkID{ 227 CurrentForkDigest: digest[:], 228 NextForkVersion: nextForkVersion, 229 NextForkEpoch: nextForkEpoch, 230 } 231 enc, err := enrForkID.MarshalSSZ() 232 require.NoError(t, err) 233 entry := enr.WithEntry(eth2ENRKey, enc) 234 // In epoch 1 of current time, the fork version should be 235 // {0, 0, 0, 1} according to the configuration override above. 236 temp := t.TempDir() 237 randNum := rand.Int() 238 tempPath := path.Join(temp, strconv.Itoa(randNum)) 239 require.NoError(t, os.Mkdir(tempPath, 0700)) 240 pkey, err := privKey(&Config{DataDir: tempPath}) 241 require.NoError(t, err, "Could not get private key") 242 db, err := enode.OpenDB("") 243 require.NoError(t, err) 244 localNode := enode.NewLocalNode(db, pkey) 245 localNode.Set(entry) 246 247 want, err := helpers.ComputeForkDigest([]byte{0, 0, 0, 0}, genesisValidatorsRoot) 248 require.NoError(t, err) 249 250 resp, err := forkEntry(localNode.Node().Record()) 251 require.NoError(t, err) 252 assert.DeepEqual(t, want[:], resp.CurrentForkDigest) 253 assert.DeepEqual(t, nextForkVersion, resp.NextForkVersion) 254 assert.Equal(t, nextForkEpoch, resp.NextForkEpoch, "Unexpected next fork epoch") 255 } 256 257 func TestAddForkEntry_Genesis(t *testing.T) { 258 temp := t.TempDir() 259 randNum := rand.Int() 260 tempPath := path.Join(temp, strconv.Itoa(randNum)) 261 require.NoError(t, os.Mkdir(tempPath, 0700)) 262 pkey, err := privKey(&Config{DataDir: tempPath}) 263 require.NoError(t, err, "Could not get private key") 264 db, err := enode.OpenDB("") 265 require.NoError(t, err) 266 267 localNode := enode.NewLocalNode(db, pkey) 268 localNode, err = addForkEntry(localNode, time.Now().Add(10*time.Second), bytesutil.PadTo([]byte{'A', 'B', 'C', 'D'}, 32)) 269 require.NoError(t, err) 270 forkEntry, err := forkEntry(localNode.Node().Record()) 271 require.NoError(t, err) 272 assert.DeepEqual(t, 273 params.BeaconConfig().GenesisForkVersion, forkEntry.NextForkVersion, 274 "Wanted Next Fork Version to be equal to genesis fork version") 275 }