github.com/anacrolix/torrent@v1.61.0/pex_test.go (about) 1 package torrent 2 3 import ( 4 "net" 5 "testing" 6 7 "github.com/anacrolix/dht/v2/krpc" 8 "github.com/stretchr/testify/assert" 9 "github.com/stretchr/testify/require" 10 11 pp "github.com/anacrolix/torrent/peer_protocol" 12 ) 13 14 var ( 15 addrs6 = []net.Addr{ 16 &net.TCPAddr{IP: net.IPv6loopback, Port: 4747}, 17 &net.TCPAddr{IP: net.IPv6loopback, Port: 4748}, 18 &net.TCPAddr{IP: net.IPv6loopback, Port: 4749}, 19 &net.TCPAddr{IP: net.IPv6loopback, Port: 4750}, 20 } 21 addrs4 = []net.Addr{ 22 &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 4747}, 23 &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 4748}, 24 &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 4749}, 25 &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 4750}, 26 } 27 addrs = []net.Addr{ 28 addrs6[0], 29 addrs6[1], 30 addrs4[0], 31 addrs4[1], 32 } 33 ) 34 35 func TestPexReset(t *testing.T) { 36 s := &pexState{} 37 conns := []PeerConn{ 38 {Peer: Peer{RemoteAddr: addrs[0]}}, 39 {Peer: Peer{RemoteAddr: addrs[1]}}, 40 {Peer: Peer{RemoteAddr: addrs[2]}}, 41 } 42 s.Add(&conns[0]) 43 s.Add(&conns[1]) 44 s.Drop(&conns[0]) 45 s.Reset() 46 targ := new(pexState) 47 require.EqualValues(t, targ, s) 48 } 49 50 func krpcNodeAddrFromNetAddr(addr net.Addr) krpc.NodeAddr { 51 addrPort, err := addrPortFromPeerRemoteAddr(addr) 52 if err != nil { 53 panic(err) 54 } 55 return krpcNodeAddrFromAddrPort(addrPort) 56 } 57 58 var testcases = []struct { 59 name string 60 in *pexState 61 targ pp.PexMsg 62 update func(*pexState) 63 targ1 pp.PexMsg 64 }{ 65 { 66 name: "empty", 67 in: &pexState{}, 68 targ: pp.PexMsg{}, 69 }, 70 { 71 name: "add0", 72 in: func() *pexState { 73 s := new(pexState) 74 nullAddr := &net.TCPAddr{} 75 s.Add(&PeerConn{Peer: Peer{RemoteAddr: nullAddr}}) 76 return s 77 }(), 78 targ: pp.PexMsg{}, 79 }, 80 { 81 name: "drop0", 82 in: func() *pexState { 83 nullAddr := &net.TCPAddr{} 84 s := new(pexState) 85 s.Drop(&PeerConn{Peer: Peer{RemoteAddr: nullAddr}, pex: pexConnState{Listed: true}}) 86 return s 87 }(), 88 targ: pp.PexMsg{}, 89 }, 90 { 91 name: "add4", 92 in: func() *pexState { 93 s := new(pexState) 94 s.Add(&PeerConn{Peer: Peer{RemoteAddr: addrs[0]}}) 95 s.Add(&PeerConn{Peer: Peer{RemoteAddr: addrs[1], outgoing: true}}) 96 s.Add(&PeerConn{Peer: Peer{RemoteAddr: addrs[2], outgoing: true}}) 97 s.Add(&PeerConn{Peer: Peer{RemoteAddr: addrs[3]}}) 98 return s 99 }(), 100 targ: pp.PexMsg{ 101 Added: krpc.CompactIPv4NodeAddrs{ 102 krpcNodeAddrFromNetAddr(addrs[2]), 103 krpcNodeAddrFromNetAddr(addrs[3]), 104 }, 105 AddedFlags: []pp.PexPeerFlags{pp.PexOutgoingConn, 0}, 106 Added6: krpc.CompactIPv6NodeAddrs{ 107 krpcNodeAddrFromNetAddr(addrs[0]), 108 krpcNodeAddrFromNetAddr(addrs[1]), 109 }, 110 Added6Flags: []pp.PexPeerFlags{0, pp.PexOutgoingConn}, 111 }, 112 }, 113 { 114 name: "drop2", 115 in: func() *pexState { 116 s := &pexState{nc: pexTargAdded + 2} 117 s.Drop(&PeerConn{Peer: Peer{RemoteAddr: addrs[0]}, pex: pexConnState{Listed: true}}) 118 s.Drop(&PeerConn{Peer: Peer{RemoteAddr: addrs[2]}, pex: pexConnState{Listed: true}}) 119 return s 120 }(), 121 targ: pp.PexMsg{ 122 Dropped: krpc.CompactIPv4NodeAddrs{ 123 krpcNodeAddrFromNetAddr(addrs[2]), 124 }, 125 Dropped6: krpc.CompactIPv6NodeAddrs{ 126 krpcNodeAddrFromNetAddr(addrs[0]), 127 }, 128 }, 129 }, 130 { 131 name: "add2drop1", 132 in: func() *pexState { 133 conns := []PeerConn{ 134 {Peer: Peer{RemoteAddr: addrs[0]}}, 135 {Peer: Peer{RemoteAddr: addrs[1]}}, 136 {Peer: Peer{RemoteAddr: addrs[2]}}, 137 } 138 s := &pexState{nc: pexTargAdded} 139 s.Add(&conns[0]) 140 s.Add(&conns[1]) 141 s.Drop(&conns[0]) 142 s.Drop(&conns[2]) // to be ignored: it wasn't added 143 return s 144 }(), 145 targ: pp.PexMsg{ 146 Added6: krpc.CompactIPv6NodeAddrs{ 147 krpcNodeAddrFromNetAddr(addrs[1]), 148 }, 149 Added6Flags: []pp.PexPeerFlags{0}, 150 }, 151 }, 152 { 153 name: "delayed", 154 in: func() *pexState { 155 conns := []PeerConn{ 156 {Peer: Peer{RemoteAddr: addrs[0]}}, 157 {Peer: Peer{RemoteAddr: addrs[1]}}, 158 {Peer: Peer{RemoteAddr: addrs[2]}}, 159 } 160 s := new(pexState) 161 s.Add(&conns[0]) 162 s.Add(&conns[1]) 163 s.Add(&conns[2]) 164 s.Drop(&conns[0]) // on hold: s.nc < pexTargAdded 165 s.Drop(&conns[2]) 166 s.Drop(&conns[1]) 167 return s 168 }(), 169 targ: pp.PexMsg{ 170 Added: krpc.CompactIPv4NodeAddrs{ 171 krpcNodeAddrFromNetAddr(addrs[2]), 172 }, 173 AddedFlags: []pp.PexPeerFlags{0}, 174 Added6: krpc.CompactIPv6NodeAddrs{ 175 krpcNodeAddrFromNetAddr(addrs[0]), 176 krpcNodeAddrFromNetAddr(addrs[1]), 177 }, 178 Added6Flags: []pp.PexPeerFlags{0, 0}, 179 }, 180 }, 181 { 182 name: "unheld", 183 in: func() *pexState { 184 conns := []PeerConn{ 185 {Peer: Peer{RemoteAddr: addrs[0]}}, 186 {Peer: Peer{RemoteAddr: addrs[1]}}, 187 } 188 s := &pexState{nc: pexTargAdded - 1} 189 s.Add(&conns[0]) 190 s.Drop(&conns[0]) // on hold: s.nc < pexTargAdded 191 s.Add(&conns[1]) // unholds the above 192 return s 193 }(), 194 targ: pp.PexMsg{ 195 Added6: krpc.CompactIPv6NodeAddrs{ 196 krpcNodeAddrFromNetAddr(addrs[1]), 197 }, 198 Added6Flags: []pp.PexPeerFlags{0}, 199 }, 200 }, 201 { 202 name: "followup", 203 in: func() *pexState { 204 s := new(pexState) 205 s.Add(&PeerConn{Peer: Peer{RemoteAddr: addrs[0]}}) 206 return s 207 }(), 208 targ: pp.PexMsg{ 209 Added6: krpc.CompactIPv6NodeAddrs{ 210 krpcNodeAddrFromNetAddr(addrs[0]), 211 }, 212 Added6Flags: []pp.PexPeerFlags{0}, 213 }, 214 update: func(s *pexState) { 215 s.Add(&PeerConn{Peer: Peer{RemoteAddr: addrs[1]}}) 216 }, 217 targ1: pp.PexMsg{ 218 Added6: krpc.CompactIPv6NodeAddrs{ 219 krpcNodeAddrFromNetAddr(addrs[1]), 220 }, 221 Added6Flags: []pp.PexPeerFlags{0}, 222 }, 223 }, 224 } 225 226 // Represents the contents of a PexMsg in a way that supports equivalence checking in tests. This is 227 // necessary because pexMsgFactory uses maps and so ordering of the resultant PexMsg isn't 228 // deterministic. Because the flags are in a different array, we can't just use testify's 229 // ElementsMatch because the ordering *does* still matter between an added addr and its flags. 230 type comparablePexMsg struct { 231 added, added6 []krpc.NodeAddr 232 addedFlags, added6Flags []pp.PexPeerFlags 233 dropped, dropped6 []krpc.NodeAddr 234 } 235 236 // Such Rust-inspired. 237 func (me *comparablePexMsg) From(f pp.PexMsg) { 238 me.added = f.Added 239 me.addedFlags = f.AddedFlags 240 me.added6 = f.Added6 241 me.added6Flags = f.Added6Flags 242 me.dropped = f.Dropped 243 me.dropped6 = f.Dropped6 244 } 245 246 // For PexMsg created by pexMsgFactory, this is as good as it can get without using data structures 247 // in pexMsgFactory that preserve insert ordering. 248 func (actual comparablePexMsg) AssertEqual(t *testing.T, expected comparablePexMsg) { 249 assert.ElementsMatch(t, expected.added, actual.added) 250 assert.ElementsMatch(t, expected.addedFlags, actual.addedFlags) 251 assert.ElementsMatch(t, expected.added6, actual.added6) 252 assert.ElementsMatch(t, expected.added6Flags, actual.added6Flags) 253 assert.ElementsMatch(t, expected.dropped, actual.dropped) 254 assert.ElementsMatch(t, expected.dropped6, actual.dropped6) 255 } 256 257 func assertPexMsgsEqual(t *testing.T, expected, actual pp.PexMsg) { 258 var ec, ac comparablePexMsg 259 ec.From(expected) 260 ac.From(actual) 261 ac.AssertEqual(t, ec) 262 } 263 264 func TestPexGenmsg0(t *testing.T) { 265 for _, tc := range testcases { 266 t.Run(tc.name, func(t *testing.T) { 267 s := *tc.in 268 m, last := s.Genmsg(nil) 269 assertPexMsgsEqual(t, tc.targ, m) 270 if tc.update != nil { 271 tc.update(&s) 272 m1, last := s.Genmsg(last) 273 assertPexMsgsEqual(t, tc.targ1, m1) 274 assert.NotNil(t, last) 275 } 276 }) 277 } 278 } 279 280 // generate 𝑛 distinct values of net.Addr 281 func addrgen(n int) chan net.Addr { 282 c := make(chan net.Addr) 283 go func() { 284 defer close(c) 285 for i := 4747; i < 65535 && n > 0; i++ { 286 c <- &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: i} 287 n-- 288 } 289 }() 290 return c 291 } 292 293 func TestPexInitialNoCutoff(t *testing.T) { 294 const n = 2 * pexMaxDelta 295 var s pexState 296 297 c := addrgen(n) 298 for addr := range c { 299 s.Add(&PeerConn{Peer: Peer{RemoteAddr: addr}}) 300 } 301 m, _ := s.Genmsg(nil) 302 303 require.EqualValues(t, n, len(m.Added)) 304 require.EqualValues(t, n, len(m.AddedFlags)) 305 require.EqualValues(t, 0, len(m.Added6)) 306 require.EqualValues(t, 0, len(m.Added6Flags)) 307 require.EqualValues(t, 0, len(m.Dropped)) 308 require.EqualValues(t, 0, len(m.Dropped6)) 309 } 310 311 func benchmarkPexInitialN(b *testing.B, npeers int) { 312 for i := 0; i < b.N; i++ { 313 var s pexState 314 c := addrgen(npeers) 315 for addr := range c { 316 s.Add(&PeerConn{Peer: Peer{RemoteAddr: addr}}) 317 s.Genmsg(nil) 318 } 319 } 320 } 321 322 // obtain at least 5 points, e.g. to plot a graph 323 func BenchmarkPexInitial4(b *testing.B) { benchmarkPexInitialN(b, 4) } 324 func BenchmarkPexInitial50(b *testing.B) { benchmarkPexInitialN(b, 50) } 325 func BenchmarkPexInitial100(b *testing.B) { benchmarkPexInitialN(b, 100) } 326 func BenchmarkPexInitial200(b *testing.B) { benchmarkPexInitialN(b, 200) } 327 func BenchmarkPexInitial400(b *testing.B) { benchmarkPexInitialN(b, 400) }