github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/p2p/broadcaster_test.go (about) 1 package p2p 2 3 import ( 4 "context" 5 "fmt" 6 "net" 7 "reflect" 8 "sync" 9 "testing" 10 "time" 11 12 "github.com/ethereum/go-ethereum/p2p/discover" 13 "github.com/libp2p/go-libp2p-core/host" 14 pubsub "github.com/libp2p/go-libp2p-pubsub" 15 "github.com/prysmaticlabs/go-bitfield" 16 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 17 "github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers" 18 "github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers/scorers" 19 p2ptest "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing" 20 pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" 21 eth "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 22 testpb "github.com/prysmaticlabs/prysm/proto/testing" 23 "github.com/prysmaticlabs/prysm/shared/bytesutil" 24 "github.com/prysmaticlabs/prysm/shared/interfaces" 25 "github.com/prysmaticlabs/prysm/shared/testutil" 26 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 27 "github.com/prysmaticlabs/prysm/shared/testutil/require" 28 "google.golang.org/protobuf/proto" 29 ) 30 31 func TestService_Broadcast(t *testing.T) { 32 p1 := p2ptest.NewTestP2P(t) 33 p2 := p2ptest.NewTestP2P(t) 34 p1.Connect(p2) 35 if len(p1.BHost.Network().Peers()) == 0 { 36 t.Fatal("No peers") 37 } 38 39 p := &Service{ 40 host: p1.BHost, 41 pubsub: p1.PubSub(), 42 joinedTopics: map[string]*pubsub.Topic{}, 43 cfg: &Config{}, 44 genesisTime: time.Now(), 45 genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32), 46 } 47 48 msg := &pb.Fork{ 49 Epoch: 55, 50 CurrentVersion: []byte("fooo"), 51 PreviousVersion: []byte("barr"), 52 } 53 54 topic := "/eth2/%x/testing" 55 // Set a test gossip mapping for testpb.TestSimpleMessage. 56 GossipTypeMapping[reflect.TypeOf(msg)] = topic 57 digest, err := p.forkDigest() 58 require.NoError(t, err) 59 topic = fmt.Sprintf(topic, digest) 60 61 // External peer subscribes to the topic. 62 topic += p.Encoding().ProtocolSuffix() 63 sub, err := p2.SubscribeToTopic(topic) 64 require.NoError(t, err) 65 66 time.Sleep(50 * time.Millisecond) // libp2p fails without this delay... 67 68 // Async listen for the pubsub, must be before the broadcast. 69 var wg sync.WaitGroup 70 wg.Add(1) 71 go func(tt *testing.T) { 72 defer wg.Done() 73 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) 74 defer cancel() 75 76 incomingMessage, err := sub.Next(ctx) 77 require.NoError(t, err) 78 79 result := &pb.Fork{} 80 require.NoError(t, p.Encoding().DecodeGossip(incomingMessage.Data, result)) 81 if !proto.Equal(result, msg) { 82 tt.Errorf("Did not receive expected message, got %+v, wanted %+v", result, msg) 83 } 84 }(t) 85 86 // Broadcast to peers and wait. 87 require.NoError(t, p.Broadcast(context.Background(), msg)) 88 if testutil.WaitTimeout(&wg, 1*time.Second) { 89 t.Error("Failed to receive pubsub within 1s") 90 } 91 } 92 93 func TestService_Broadcast_ReturnsErr_TopicNotMapped(t *testing.T) { 94 p := Service{ 95 genesisTime: time.Now(), 96 genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32), 97 } 98 assert.ErrorContains(t, ErrMessageNotMapped.Error(), p.Broadcast(context.Background(), &testpb.AddressBook{})) 99 } 100 101 func TestService_Attestation_Subnet(t *testing.T) { 102 if gtm := GossipTypeMapping[reflect.TypeOf(ð.Attestation{})]; gtm != AttestationSubnetTopicFormat { 103 t.Errorf("Constant is out of date. Wanted %s, got %s", AttestationSubnetTopicFormat, gtm) 104 } 105 106 tests := []struct { 107 att *eth.Attestation 108 topic string 109 }{ 110 { 111 att: ð.Attestation{ 112 Data: ð.AttestationData{ 113 CommitteeIndex: 0, 114 Slot: 2, 115 }, 116 }, 117 topic: "/eth2/00000000/beacon_attestation_2", 118 }, 119 { 120 att: ð.Attestation{ 121 Data: ð.AttestationData{ 122 CommitteeIndex: 11, 123 Slot: 10, 124 }, 125 }, 126 topic: "/eth2/00000000/beacon_attestation_21", 127 }, 128 { 129 att: ð.Attestation{ 130 Data: ð.AttestationData{ 131 CommitteeIndex: 55, 132 Slot: 529, 133 }, 134 }, 135 topic: "/eth2/00000000/beacon_attestation_8", 136 }, 137 } 138 for _, tt := range tests { 139 subnet := helpers.ComputeSubnetFromCommitteeAndSlot(100, tt.att.Data.CommitteeIndex, tt.att.Data.Slot) 140 assert.Equal(t, tt.topic, attestationToTopic(subnet, [4]byte{} /* fork digest */), "Wrong topic") 141 } 142 } 143 144 func TestService_BroadcastAttestation(t *testing.T) { 145 p1 := p2ptest.NewTestP2P(t) 146 p2 := p2ptest.NewTestP2P(t) 147 p1.Connect(p2) 148 if len(p1.BHost.Network().Peers()) == 0 { 149 t.Fatal("No peers") 150 } 151 152 p := &Service{ 153 host: p1.BHost, 154 pubsub: p1.PubSub(), 155 joinedTopics: map[string]*pubsub.Topic{}, 156 cfg: &Config{}, 157 genesisTime: time.Now(), 158 genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32), 159 subnetsLock: make(map[uint64]*sync.RWMutex), 160 subnetsLockLock: sync.Mutex{}, 161 peers: peers.NewStatus(context.Background(), &peers.StatusConfig{ 162 ScorerParams: &scorers.Config{}, 163 }), 164 } 165 166 msg := testutil.HydrateAttestation(ð.Attestation{AggregationBits: bitfield.NewBitlist(7)}) 167 subnet := uint64(5) 168 169 topic := AttestationSubnetTopicFormat 170 GossipTypeMapping[reflect.TypeOf(msg)] = topic 171 digest, err := p.forkDigest() 172 require.NoError(t, err) 173 topic = fmt.Sprintf(topic, digest, subnet) 174 175 // External peer subscribes to the topic. 176 topic += p.Encoding().ProtocolSuffix() 177 sub, err := p2.SubscribeToTopic(topic) 178 require.NoError(t, err) 179 180 time.Sleep(50 * time.Millisecond) // libp2p fails without this delay... 181 182 // Async listen for the pubsub, must be before the broadcast. 183 var wg sync.WaitGroup 184 wg.Add(1) 185 go func(tt *testing.T) { 186 defer wg.Done() 187 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) 188 defer cancel() 189 190 incomingMessage, err := sub.Next(ctx) 191 require.NoError(t, err) 192 193 result := ð.Attestation{} 194 require.NoError(t, p.Encoding().DecodeGossip(incomingMessage.Data, result)) 195 if !proto.Equal(result, msg) { 196 tt.Errorf("Did not receive expected message, got %+v, wanted %+v", result, msg) 197 } 198 }(t) 199 200 // Broadcast to peers and wait. 201 require.NoError(t, p.BroadcastAttestation(context.Background(), subnet, msg)) 202 if testutil.WaitTimeout(&wg, 1*time.Second) { 203 t.Error("Failed to receive pubsub within 1s") 204 } 205 } 206 207 func TestService_BroadcastAttestationWithDiscoveryAttempts(t *testing.T) { 208 // Setup bootnode. 209 cfg := &Config{} 210 port := 2000 211 cfg.UDPPort = uint(port) 212 _, pkey := createAddrAndPrivKey(t) 213 ipAddr := net.ParseIP("127.0.0.1") 214 genesisTime := time.Now() 215 genesisValidatorsRoot := make([]byte, 32) 216 s := &Service{ 217 cfg: cfg, 218 genesisTime: genesisTime, 219 genesisValidatorsRoot: genesisValidatorsRoot, 220 } 221 bootListener, err := s.createListener(ipAddr, pkey) 222 require.NoError(t, err) 223 defer bootListener.Close() 224 225 // Use shorter period for testing. 226 currentPeriod := pollingPeriod 227 pollingPeriod = 1 * time.Second 228 defer func() { 229 pollingPeriod = currentPeriod 230 }() 231 232 bootNode := bootListener.Self() 233 subnet := uint64(5) 234 235 var listeners []*discover.UDPv5 236 var hosts []host.Host 237 // setup other nodes. 238 cfg = &Config{ 239 BootstrapNodeAddr: []string{bootNode.String()}, 240 Discv5BootStrapAddr: []string{bootNode.String()}, 241 MaxPeers: 30, 242 } 243 // Setup 2 different hosts 244 for i := 1; i <= 2; i++ { 245 h, pkey, ipAddr := createHost(t, port+i) 246 cfg.UDPPort = uint(port + i) 247 cfg.TCPPort = uint(port + i) 248 s := &Service{ 249 cfg: cfg, 250 genesisTime: genesisTime, 251 genesisValidatorsRoot: genesisValidatorsRoot, 252 } 253 listener, err := s.startDiscoveryV5(ipAddr, pkey) 254 // Set for 2nd peer 255 if i == 2 { 256 s.dv5Listener = listener 257 s.metaData = interfaces.WrappedMetadataV0(new(pb.MetaDataV0)) 258 bitV := bitfield.NewBitvector64() 259 bitV.SetBitAt(subnet, true) 260 s.updateSubnetRecordWithMetadata(bitV) 261 } 262 assert.NoError(t, err, "Could not start discovery for node") 263 listeners = append(listeners, listener) 264 hosts = append(hosts, h) 265 } 266 defer func() { 267 // Close down all peers. 268 for _, listener := range listeners { 269 listener.Close() 270 } 271 }() 272 273 // close peers upon exit of test 274 defer func() { 275 for _, h := range hosts { 276 if err := h.Close(); err != nil { 277 t.Log(err) 278 } 279 } 280 }() 281 282 ps1, err := pubsub.NewFloodSub(context.Background(), hosts[0], 283 pubsub.WithMessageSigning(false), 284 pubsub.WithStrictSignatureVerification(false), 285 ) 286 require.NoError(t, err) 287 288 ps2, err := pubsub.NewFloodSub(context.Background(), hosts[1], 289 pubsub.WithMessageSigning(false), 290 pubsub.WithStrictSignatureVerification(false), 291 ) 292 require.NoError(t, err) 293 p := &Service{ 294 host: hosts[0], 295 ctx: context.Background(), 296 pubsub: ps1, 297 dv5Listener: listeners[0], 298 joinedTopics: map[string]*pubsub.Topic{}, 299 cfg: &Config{}, 300 genesisTime: time.Now(), 301 genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32), 302 subnetsLock: make(map[uint64]*sync.RWMutex), 303 subnetsLockLock: sync.Mutex{}, 304 peers: peers.NewStatus(context.Background(), &peers.StatusConfig{ 305 ScorerParams: &scorers.Config{}, 306 }), 307 } 308 309 p2 := &Service{ 310 host: hosts[1], 311 ctx: context.Background(), 312 pubsub: ps2, 313 dv5Listener: listeners[1], 314 joinedTopics: map[string]*pubsub.Topic{}, 315 cfg: &Config{}, 316 genesisTime: time.Now(), 317 genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32), 318 subnetsLock: make(map[uint64]*sync.RWMutex), 319 subnetsLockLock: sync.Mutex{}, 320 peers: peers.NewStatus(context.Background(), &peers.StatusConfig{ 321 ScorerParams: &scorers.Config{}, 322 }), 323 } 324 325 msg := testutil.HydrateAttestation(ð.Attestation{AggregationBits: bitfield.NewBitlist(7)}) 326 topic := AttestationSubnetTopicFormat 327 GossipTypeMapping[reflect.TypeOf(msg)] = topic 328 digest, err := p.forkDigest() 329 require.NoError(t, err) 330 topic = fmt.Sprintf(topic, digest, subnet) 331 332 // External peer subscribes to the topic. 333 topic += p.Encoding().ProtocolSuffix() 334 // We dont use our internal subscribe method 335 // due to using floodsub over here. 336 tpHandle, err := p2.JoinTopic(topic) 337 require.NoError(t, err) 338 sub, err := tpHandle.Subscribe() 339 require.NoError(t, err) 340 341 time.Sleep(50 * time.Millisecond) // libp2p fails without this delay... 342 343 // Async listen for the pubsub, must be before the broadcast. 344 var wg sync.WaitGroup 345 wg.Add(1) 346 go func(tt *testing.T) { 347 defer wg.Done() 348 ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) 349 defer cancel() 350 351 incomingMessage, err := sub.Next(ctx) 352 require.NoError(t, err) 353 354 result := ð.Attestation{} 355 require.NoError(t, p.Encoding().DecodeGossip(incomingMessage.Data, result)) 356 if !proto.Equal(result, msg) { 357 tt.Errorf("Did not receive expected message, got %+v, wanted %+v", result, msg) 358 } 359 }(t) 360 361 // Broadcast to peers and wait. 362 require.NoError(t, p.BroadcastAttestation(context.Background(), subnet, msg)) 363 if testutil.WaitTimeout(&wg, 4*time.Second) { 364 t.Error("Failed to receive pubsub within 4s") 365 } 366 }