github.com/ethersphere/bee/v2@v2.2.0/pkg/hive/hive_test.go (about) 1 // Copyright 2020 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package hive_test 6 7 import ( 8 "bytes" 9 "context" 10 "errors" 11 "fmt" 12 "strconv" 13 "testing" 14 "time" 15 16 "github.com/ethereum/go-ethereum/common" 17 ma "github.com/multiformats/go-multiaddr" 18 19 ab "github.com/ethersphere/bee/v2/pkg/addressbook" 20 "github.com/ethersphere/bee/v2/pkg/bzz" 21 "github.com/ethersphere/bee/v2/pkg/crypto" 22 "github.com/ethersphere/bee/v2/pkg/hive" 23 "github.com/ethersphere/bee/v2/pkg/hive/pb" 24 "github.com/ethersphere/bee/v2/pkg/log" 25 "github.com/ethersphere/bee/v2/pkg/p2p/protobuf" 26 "github.com/ethersphere/bee/v2/pkg/p2p/streamtest" 27 "github.com/ethersphere/bee/v2/pkg/spinlock" 28 "github.com/ethersphere/bee/v2/pkg/statestore/mock" 29 "github.com/ethersphere/bee/v2/pkg/swarm" 30 "github.com/ethersphere/bee/v2/pkg/util/testutil" 31 ) 32 33 var ( 34 nonce = common.HexToHash("0x2").Bytes() 35 block = common.HexToHash("0x1").Bytes() 36 ) 37 38 const spinTimeout = time.Second * 5 39 40 func TestHandlerRateLimit(t *testing.T) { 41 t.Parallel() 42 43 logger := log.Noop 44 statestore := mock.NewStateStore() 45 addressbook := ab.New(statestore) 46 networkID := uint64(1) 47 48 addressbookclean := ab.New(mock.NewStateStore()) 49 50 // new recorder for handling Ping 51 streamer := streamtest.New() 52 // create a hive server that handles the incoming stream 53 server := hive.New(streamer, addressbookclean, networkID, false, true, logger) 54 testutil.CleanupCloser(t, server) 55 56 serverAddress := swarm.RandAddress(t) 57 58 // setup the stream recorder to record stream data 59 serverRecorder := streamtest.New( 60 streamtest.WithProtocols(server.Protocol()), 61 streamtest.WithBaseAddr(serverAddress), 62 ) 63 64 peers := make([]swarm.Address, hive.LimitBurst+1) 65 for i := range peers { 66 67 underlay, err := ma.NewMultiaddr("/ip4/127.0.0.1/udp/" + strconv.Itoa(i)) 68 if err != nil { 69 t.Fatal(err) 70 } 71 pk, err := crypto.GenerateSecp256k1Key() 72 if err != nil { 73 t.Fatal(err) 74 } 75 signer := crypto.NewDefaultSigner(pk) 76 overlay, err := crypto.NewOverlayAddress(pk.PublicKey, networkID, block) 77 if err != nil { 78 t.Fatal(err) 79 } 80 bzzAddr, err := bzz.NewAddress(signer, underlay, overlay, networkID, nonce) 81 if err != nil { 82 t.Fatal(err) 83 } 84 85 err = addressbook.Put(bzzAddr.Overlay, *bzzAddr) 86 if err != nil { 87 t.Fatal(err) 88 } 89 peers[i] = bzzAddr.Overlay 90 } 91 92 // create a hive client that will do broadcast 93 client := hive.New(serverRecorder, addressbook, networkID, false, true, logger) 94 err := client.BroadcastPeers(context.Background(), serverAddress, peers...) 95 if err != nil { 96 t.Fatal(err) 97 } 98 testutil.CleanupCloser(t, client) 99 100 rec, err := serverRecorder.Records(serverAddress, "hive", "1.1.0", "peers") 101 if err != nil { 102 t.Fatal(err) 103 } 104 105 lastRec := rec[len(rec)-1] 106 107 if lastRec.Err() != nil { 108 t.Fatal("want nil error") 109 } 110 } 111 112 func TestBroadcastPeers(t *testing.T) { 113 t.Parallel() 114 115 logger := log.Noop 116 statestore := mock.NewStateStore() 117 addressbook := ab.New(statestore) 118 networkID := uint64(1) 119 120 // populate all expected and needed random resources for 2 full batches 121 // tests cases that uses fewer resources can use sub-slices of this data 122 var bzzAddresses []bzz.Address 123 var overlays []swarm.Address 124 var wantMsgs []pb.Peers 125 126 for i := 0; i < 2; i++ { 127 wantMsgs = append(wantMsgs, pb.Peers{Peers: []*pb.BzzAddress{}}) 128 } 129 130 for i := 0; i < 2*hive.MaxBatchSize; i++ { 131 base := "/ip4/127.0.0.1/udp/" 132 if i == 2*hive.MaxBatchSize-1 { 133 base = "/ip4/1.1.1.1/udp/" // The last underlay has public address. 134 } 135 underlay, err := ma.NewMultiaddr(base + strconv.Itoa(i)) 136 if err != nil { 137 t.Fatal(err) 138 } 139 pk, err := crypto.GenerateSecp256k1Key() 140 if err != nil { 141 t.Fatal(err) 142 } 143 signer := crypto.NewDefaultSigner(pk) 144 overlay, err := crypto.NewOverlayAddress(pk.PublicKey, networkID, block) 145 if err != nil { 146 t.Fatal(err) 147 } 148 bzzAddr, err := bzz.NewAddress(signer, underlay, overlay, networkID, nonce) 149 if err != nil { 150 t.Fatal(err) 151 } 152 153 bzzAddresses = append(bzzAddresses, *bzzAddr) 154 overlays = append(overlays, bzzAddr.Overlay) 155 err = addressbook.Put(bzzAddr.Overlay, *bzzAddr) 156 if err != nil { 157 t.Fatal(err) 158 } 159 160 wantMsgs[i/hive.MaxBatchSize].Peers = append(wantMsgs[i/hive.MaxBatchSize].Peers, &pb.BzzAddress{ 161 Overlay: bzzAddresses[i].Overlay.Bytes(), 162 Underlay: bzzAddresses[i].Underlay.Bytes(), 163 Signature: bzzAddresses[i].Signature, 164 Nonce: nonce, 165 }) 166 } 167 168 testCases := map[string]struct { 169 addresee swarm.Address 170 peers []swarm.Address 171 wantMsgs []pb.Peers 172 wantOverlays []swarm.Address 173 wantBzzAddresses []bzz.Address 174 allowPrivateCIDRs bool 175 pingErr func(addr ma.Multiaddr) (time.Duration, error) 176 }{ 177 "OK - single record": { 178 addresee: swarm.MustParseHexAddress("ca1e9f3938cc1425c6061b96ad9eb93e134dfe8734ad490164ef20af9d1cf59c"), 179 peers: []swarm.Address{overlays[0]}, 180 wantMsgs: []pb.Peers{{Peers: wantMsgs[0].Peers[:1]}}, 181 wantOverlays: []swarm.Address{overlays[0]}, 182 wantBzzAddresses: []bzz.Address{bzzAddresses[0]}, 183 allowPrivateCIDRs: true, 184 }, 185 "OK - single batch - multiple records": { 186 addresee: swarm.MustParseHexAddress("ca1e9f3938cc1425c6061b96ad9eb93e134dfe8734ad490164ef20af9d1cf59c"), 187 peers: overlays[:15], 188 wantMsgs: []pb.Peers{{Peers: wantMsgs[0].Peers[:15]}}, 189 wantOverlays: overlays[:15], 190 wantBzzAddresses: bzzAddresses[:15], 191 allowPrivateCIDRs: true, 192 }, 193 "OK - single batch - max number of records": { 194 addresee: swarm.MustParseHexAddress("ca1e9f3938cc1425c6061b96ad9eb93e134dfe8734ad490164ef20af9d1cf59c"), 195 peers: overlays[:hive.MaxBatchSize], 196 wantMsgs: []pb.Peers{{Peers: wantMsgs[0].Peers[:hive.MaxBatchSize]}}, 197 wantOverlays: overlays[:hive.MaxBatchSize], 198 wantBzzAddresses: bzzAddresses[:hive.MaxBatchSize], 199 allowPrivateCIDRs: true, 200 }, 201 "OK - multiple batches": { 202 addresee: swarm.MustParseHexAddress("ca1e9f3938cc1425c6061b96ad9eb93e134dfe8734ad490164ef20af9d1cf59c"), 203 peers: overlays[:hive.MaxBatchSize+10], 204 wantMsgs: []pb.Peers{{Peers: wantMsgs[0].Peers}, {Peers: wantMsgs[1].Peers[:10]}}, 205 wantOverlays: overlays[:hive.MaxBatchSize+10], 206 wantBzzAddresses: bzzAddresses[:hive.MaxBatchSize+10], 207 allowPrivateCIDRs: true, 208 }, 209 "OK - multiple batches - max number of records": { 210 addresee: swarm.MustParseHexAddress("ca1e9f3938cc1425c6061b96ad9eb93e134dfe8734ad490164ef20af9d1cf59c"), 211 peers: overlays[:2*hive.MaxBatchSize], 212 wantMsgs: []pb.Peers{{Peers: wantMsgs[0].Peers}, {Peers: wantMsgs[1].Peers}}, 213 wantOverlays: overlays[:2*hive.MaxBatchSize], 214 wantBzzAddresses: bzzAddresses[:2*hive.MaxBatchSize], 215 allowPrivateCIDRs: true, 216 }, 217 "OK - single batch - skip ping failures": { 218 addresee: swarm.MustParseHexAddress("ca1e9f3938cc1425c6061b96ad9eb93e134dfe8734ad490164ef20af9d1cf59c"), 219 peers: overlays[:15], 220 wantMsgs: []pb.Peers{{Peers: wantMsgs[0].Peers[:15]}}, 221 wantOverlays: overlays[:10], 222 wantBzzAddresses: bzzAddresses[:10], 223 allowPrivateCIDRs: true, 224 pingErr: func(addr ma.Multiaddr) (rtt time.Duration, err error) { 225 for _, v := range bzzAddresses[10:15] { 226 if v.Underlay.Equal(addr) { 227 return rtt, errors.New("ping failure") 228 } 229 } 230 return rtt, nil 231 }, 232 }, 233 "Ok - don't advertise private CIDRs": { 234 addresee: overlays[len(overlays)-1], 235 peers: overlays[:15], 236 wantMsgs: []pb.Peers{{}}, 237 wantOverlays: nil, 238 wantBzzAddresses: nil, 239 allowPrivateCIDRs: false, 240 }, 241 } 242 243 for name, tc := range testCases { 244 tc := tc 245 t.Run(name, func(t *testing.T) { 246 t.Parallel() 247 248 addressbookclean := ab.New(mock.NewStateStore()) 249 250 // new recorder for handling Ping 251 var streamer *streamtest.Recorder 252 if tc.pingErr != nil { 253 streamer = streamtest.New(streamtest.WithPingErr(tc.pingErr)) 254 } else { 255 streamer = streamtest.New() 256 } 257 // create a hive server that handles the incoming stream 258 server := hive.New(streamer, addressbookclean, networkID, false, true, logger) 259 testutil.CleanupCloser(t, server) 260 261 // setup the stream recorder to record stream data 262 recorder := streamtest.New( 263 streamtest.WithProtocols(server.Protocol()), 264 ) 265 266 // create a hive client that will do broadcast 267 client := hive.New(recorder, addressbook, networkID, false, tc.allowPrivateCIDRs, logger) 268 if err := client.BroadcastPeers(context.Background(), tc.addresee, tc.peers...); err != nil { 269 t.Fatal(err) 270 } 271 testutil.CleanupCloser(t, client) 272 273 // get a record for this stream 274 records, err := recorder.Records(tc.addresee, "hive", "1.1.0", "peers") 275 if err != nil { 276 t.Fatal(err) 277 } 278 if l := len(records); l != len(tc.wantMsgs) { 279 t.Fatalf("got %v records, want %v", l, len(tc.wantMsgs)) 280 } 281 282 // there is a one record per batch (wantMsg) 283 for i, record := range records { 284 messages, err := readAndAssertPeersMsgs(record.In(), 1) 285 if err != nil { 286 t.Fatal(err) 287 } 288 289 if fmt.Sprint(messages[0]) != fmt.Sprint(tc.wantMsgs[i]) { 290 t.Errorf("Messages got %v, want %v", messages, tc.wantMsgs) 291 } 292 } 293 294 expectOverlaysEventually(t, addressbookclean, tc.wantOverlays) 295 expectBzzAddresessEventually(t, addressbookclean, tc.wantBzzAddresses) 296 }) 297 } 298 } 299 300 func expectOverlaysEventually(t *testing.T, exporter ab.Interface, wantOverlays []swarm.Address) { 301 t.Helper() 302 303 var ( 304 overlays []swarm.Address 305 err error 306 ) 307 308 err = spinlock.Wait(spinTimeout, func() bool { 309 overlays, err = exporter.Overlays() 310 if err != nil { 311 t.Fatal(err) 312 } 313 314 return len(overlays) == len(wantOverlays) 315 }) 316 if err != nil { 317 t.Fatal("timed out waiting for overlays") 318 } 319 320 for _, v := range wantOverlays { 321 if !swarm.ContainsAddress(overlays, v) { 322 t.Errorf("overlay %s expected but not found", v.String()) 323 } 324 } 325 326 if t.Failed() { 327 t.Errorf("overlays got %v, want %v", overlays, wantOverlays) 328 } 329 } 330 331 func expectBzzAddresessEventually(t *testing.T, exporter ab.Interface, wantBzzAddresses []bzz.Address) { 332 t.Helper() 333 334 var ( 335 addresses []bzz.Address 336 err error 337 ) 338 339 err = spinlock.Wait(spinTimeout, func() bool { 340 addresses, err = exporter.Addresses() 341 if err != nil { 342 t.Fatal(err) 343 } 344 345 return len(addresses) == len(wantBzzAddresses) 346 }) 347 if err != nil { 348 t.Fatal("timed out waiting for bzz addresses") 349 } 350 351 for _, v := range wantBzzAddresses { 352 if !bzz.ContainsAddress(addresses, &v) { 353 t.Errorf("address %s expected but not found", v.Overlay.String()) 354 } 355 } 356 357 if t.Failed() { 358 t.Errorf("bzz addresses got %v, want %v", addresses, wantBzzAddresses) 359 } 360 } 361 362 func readAndAssertPeersMsgs(in []byte, expectedLen int) ([]pb.Peers, error) { 363 messages, err := protobuf.ReadMessages( 364 bytes.NewReader(in), 365 func() protobuf.Message { 366 return new(pb.Peers) 367 }, 368 ) 369 370 if err != nil { 371 return nil, err 372 } 373 374 if len(messages) != expectedLen { 375 return nil, fmt.Errorf("got %v messages, want %v", len(messages), expectedLen) 376 } 377 378 peers := make([]pb.Peers, len(messages)) 379 for i := range messages { 380 peers[i] = *messages[i].(*pb.Peers) 381 } 382 383 return peers, nil 384 }