github.com/core-coin/go-core/v2@v2.1.9/p2p/peer_test.go (about) 1 // Copyright 2014 by the Authors 2 // This file is part of the go-core library. 3 // 4 // The go-core library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-core library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-core library. If not, see <http://www.gnu.org/licenses/>. 16 17 package p2p 18 19 import ( 20 "encoding/binary" 21 "errors" 22 "fmt" 23 "math/rand" 24 "net" 25 "reflect" 26 "strconv" 27 "strings" 28 "testing" 29 "time" 30 31 "github.com/core-coin/go-core/v2/log" 32 "github.com/core-coin/go-core/v2/p2p/enode" 33 "github.com/core-coin/go-core/v2/p2p/enr" 34 ) 35 36 var discard = Protocol{ 37 Name: "discard", 38 Length: 1, 39 Run: func(p *Peer, rw MsgReadWriter) error { 40 for { 41 msg, err := rw.ReadMsg() 42 if err != nil { 43 return err 44 } 45 fmt.Printf("discarding %d\n", msg.Code) 46 if err = msg.Discard(); err != nil { 47 return err 48 } 49 } 50 }, 51 } 52 53 // uintID encodes i into a node ID. 54 func uintID(i uint16) enode.ID { 55 var id enode.ID 56 binary.BigEndian.PutUint16(id[:], i) 57 return id 58 } 59 60 // newNode creates a node record with the given address. 61 func newNode(id enode.ID, addr string) *enode.Node { 62 var r enr.Record 63 if addr != "" { 64 // Set the port if present. 65 if strings.Contains(addr, ":") { 66 hs, ps, err := net.SplitHostPort(addr) 67 if err != nil { 68 panic(fmt.Errorf("invalid address %q", addr)) 69 } 70 port, err := strconv.Atoi(ps) 71 if err != nil { 72 panic(fmt.Errorf("invalid port in %q", addr)) 73 } 74 r.Set(enr.TCP(port)) 75 r.Set(enr.UDP(port)) 76 addr = hs 77 } 78 // Set the IP. 79 ip := net.ParseIP(addr) 80 if ip == nil { 81 panic(fmt.Errorf("invalid IP %q", addr)) 82 } 83 r.Set(enr.IP(ip)) 84 } 85 return enode.SignNull(&r, id) 86 } 87 88 func testPeer(protos []Protocol) (func(), *conn, *Peer, <-chan error) { 89 var ( 90 fd1, fd2 = net.Pipe() 91 key1, key2 = newkey(), newkey() 92 t1 = newTestTransport(key2.PublicKey(), fd1, nil) 93 t2 = newTestTransport(key1.PublicKey(), fd2, key1.PublicKey()) 94 ) 95 96 c1 := &conn{fd: fd1, node: newNode(uintID(1), ""), transport: t1} 97 c2 := &conn{fd: fd2, node: newNode(uintID(2), ""), transport: t2} 98 for _, p := range protos { 99 c1.caps = append(c1.caps, p.cap()) 100 c2.caps = append(c2.caps, p.cap()) 101 } 102 103 peer := newPeer(log.Root(), c1, protos) 104 errc := make(chan error, 1) 105 go func() { 106 _, err := peer.run() 107 errc <- err 108 }() 109 110 closer := func() { c2.close(errors.New("close func called")) } 111 return closer, c2, peer, errc 112 } 113 114 func TestPeerProtoReadMsg(t *testing.T) { 115 proto := Protocol{ 116 Name: "a", 117 Length: 5, 118 Run: func(peer *Peer, rw MsgReadWriter) error { 119 if err := ExpectMsg(rw, 2, []uint{1}); err != nil { 120 t.Error(err) 121 } 122 if err := ExpectMsg(rw, 3, []uint{2}); err != nil { 123 t.Error(err) 124 } 125 if err := ExpectMsg(rw, 4, []uint{3}); err != nil { 126 t.Error(err) 127 } 128 return nil 129 }, 130 } 131 132 closer, rw, _, errc := testPeer([]Protocol{proto}) 133 defer closer() 134 135 Send(rw, baseProtocolLength+2, []uint{1}) 136 Send(rw, baseProtocolLength+3, []uint{2}) 137 Send(rw, baseProtocolLength+4, []uint{3}) 138 139 select { 140 case err := <-errc: 141 if err != errProtocolReturned { 142 t.Errorf("peer returned error: %v", err) 143 } 144 case <-time.After(2 * time.Second): 145 t.Errorf("receive timeout") 146 } 147 } 148 149 func TestPeerProtoEncodeMsg(t *testing.T) { 150 proto := Protocol{ 151 Name: "a", 152 Length: 2, 153 Run: func(peer *Peer, rw MsgReadWriter) error { 154 if err := SendItems(rw, 2); err == nil { 155 t.Error("expected error for out-of-range msg code, got nil") 156 } 157 if err := SendItems(rw, 1, "foo", "bar"); err != nil { 158 t.Errorf("write error: %v", err) 159 } 160 return nil 161 }, 162 } 163 closer, rw, _, _ := testPeer([]Protocol{proto}) 164 defer closer() 165 166 if err := ExpectMsg(rw, 17, []string{"foo", "bar"}); err != nil { 167 t.Error(err) 168 } 169 } 170 171 func TestPeerPing(t *testing.T) { 172 closer, rw, _, _ := testPeer(nil) 173 defer closer() 174 if err := SendItems(rw, pingMsg); err != nil { 175 t.Fatal(err) 176 } 177 if err := ExpectMsg(rw, pongMsg, nil); err != nil { 178 t.Error(err) 179 } 180 } 181 182 // This test checks that a disconnect message sent by a peer is returned 183 // as the error from Peer.run. 184 func TestPeerDisconnect(t *testing.T) { 185 closer, rw, _, disc := testPeer(nil) 186 defer closer() 187 188 if err := SendItems(rw, discMsg, DiscQuitting); err != nil { 189 t.Fatal(err) 190 } 191 select { 192 case reason := <-disc: 193 if reason != DiscQuitting { 194 t.Errorf("run returned wrong reason: got %v, want %v", reason, DiscQuitting) 195 } 196 case <-time.After(500 * time.Millisecond): 197 t.Error("peer did not return") 198 } 199 } 200 201 // This test is supposed to verify that Peer can reliably handle 202 // multiple causes of disconnection occurring at the same time. 203 func TestPeerDisconnectRace(t *testing.T) { 204 maybe := func() bool { return rand.Intn(2) == 1 } 205 206 for i := 0; i < 1000; i++ { 207 protoclose := make(chan error) 208 protodisc := make(chan DiscReason) 209 closer, rw, p, disc := testPeer([]Protocol{ 210 { 211 Name: "closereq", 212 Run: func(p *Peer, rw MsgReadWriter) error { return <-protoclose }, 213 Length: 1, 214 }, 215 { 216 Name: "disconnect", 217 Run: func(p *Peer, rw MsgReadWriter) error { p.Disconnect(<-protodisc); return nil }, 218 Length: 1, 219 }, 220 }) 221 222 // Simulate incoming messages. 223 go SendItems(rw, baseProtocolLength+1) 224 go SendItems(rw, baseProtocolLength+2) 225 // Close the network connection. 226 go closer() 227 // Make protocol "closereq" return. 228 protoclose <- errors.New("protocol closed") 229 // Make protocol "disconnect" call peer.Disconnect 230 protodisc <- DiscAlreadyConnected 231 // In some cases, simulate something else calling peer.Disconnect. 232 if maybe() { 233 go p.Disconnect(DiscInvalidIdentity) 234 } 235 // In some cases, simulate remote requesting a disconnect. 236 if maybe() { 237 go SendItems(rw, discMsg, DiscQuitting) 238 } 239 240 select { 241 case <-disc: 242 case <-time.After(2 * time.Second): 243 // Peer.run should return quickly. If it doesn't the Peer 244 // goroutines are probably deadlocked. Call panic in order to 245 // show the stacks. 246 panic("Peer.run took to long to return.") 247 } 248 } 249 } 250 251 func TestNewPeer(t *testing.T) { 252 name := "nodename" 253 caps := []Cap{{"foo", 2}, {"bar", 3}} 254 id := randomID() 255 p := NewPeer(id, name, caps) 256 if p.ID() != id { 257 t.Errorf("ID mismatch: got %v, expected %v", p.ID(), id) 258 } 259 if p.Name() != name { 260 t.Errorf("Name mismatch: got %v, expected %v", p.Name(), name) 261 } 262 if !reflect.DeepEqual(p.Caps(), caps) { 263 t.Errorf("Caps mismatch: got %v, expected %v", p.Caps(), caps) 264 } 265 266 p.Disconnect(DiscAlreadyConnected) // Should not hang 267 } 268 269 func TestMatchProtocols(t *testing.T) { 270 tests := []struct { 271 Remote []Cap 272 Local []Protocol 273 Match map[string]protoRW 274 }{ 275 { 276 // No remote capabilities 277 Local: []Protocol{{Name: "a"}}, 278 }, 279 { 280 // No local protocols 281 Remote: []Cap{{Name: "a"}}, 282 }, 283 { 284 // No mutual protocols 285 Remote: []Cap{{Name: "a"}}, 286 Local: []Protocol{{Name: "b"}}, 287 }, 288 { 289 // Some matches, some differences 290 Remote: []Cap{{Name: "local"}, {Name: "match1"}, {Name: "match2"}}, 291 Local: []Protocol{{Name: "match1"}, {Name: "match2"}, {Name: "remote"}}, 292 Match: map[string]protoRW{"match1": {Protocol: Protocol{Name: "match1"}}, "match2": {Protocol: Protocol{Name: "match2"}}}, 293 }, 294 { 295 // Various alphabetical ordering 296 Remote: []Cap{{Name: "aa"}, {Name: "ab"}, {Name: "bb"}, {Name: "ba"}}, 297 Local: []Protocol{{Name: "ba"}, {Name: "bb"}, {Name: "ab"}, {Name: "aa"}}, 298 Match: map[string]protoRW{"aa": {Protocol: Protocol{Name: "aa"}}, "ab": {Protocol: Protocol{Name: "ab"}}, "ba": {Protocol: Protocol{Name: "ba"}}, "bb": {Protocol: Protocol{Name: "bb"}}}, 299 }, 300 { 301 // No mutual versions 302 Remote: []Cap{{Version: 1}}, 303 Local: []Protocol{{Version: 2}}, 304 }, 305 { 306 // Multiple versions, single common 307 Remote: []Cap{{Version: 1}, {Version: 2}}, 308 Local: []Protocol{{Version: 2}, {Version: 3}}, 309 Match: map[string]protoRW{"": {Protocol: Protocol{Version: 2}}}, 310 }, 311 { 312 // Multiple versions, multiple common 313 Remote: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Version: 4}}, 314 Local: []Protocol{{Version: 2}, {Version: 3}}, 315 Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}}, 316 }, 317 { 318 // Various version orderings 319 Remote: []Cap{{Version: 4}, {Version: 1}, {Version: 3}, {Version: 2}}, 320 Local: []Protocol{{Version: 2}, {Version: 3}, {Version: 1}}, 321 Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}}, 322 }, 323 { 324 // Versions overriding sub-protocol lengths 325 Remote: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Name: "a"}}, 326 Local: []Protocol{{Version: 1, Length: 1}, {Version: 2, Length: 2}, {Version: 3, Length: 3}, {Name: "a"}}, 327 Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}, "a": {Protocol: Protocol{Name: "a"}, offset: 3}}, 328 }, 329 } 330 331 for i, tt := range tests { 332 result := matchProtocols(tt.Local, tt.Remote, nil) 333 if len(result) != len(tt.Match) { 334 t.Errorf("test %d: negotiation mismatch: have %v, want %v", i, len(result), len(tt.Match)) 335 continue 336 } 337 // Make sure all negotiated protocols are needed and correct 338 for name, proto := range result { 339 match, ok := tt.Match[name] 340 if !ok { 341 t.Errorf("test %d, proto '%s': negotiated but shouldn't have", i, name) 342 continue 343 } 344 if proto.Name != match.Name { 345 t.Errorf("test %d, proto '%s': name mismatch: have %v, want %v", i, name, proto.Name, match.Name) 346 } 347 if proto.Version != match.Version { 348 t.Errorf("test %d, proto '%s': version mismatch: have %v, want %v", i, name, proto.Version, match.Version) 349 } 350 if proto.offset-baseProtocolLength != match.offset { 351 t.Errorf("test %d, proto '%s': offset mismatch: have %v, want %v", i, name, proto.offset-baseProtocolLength, match.offset) 352 } 353 } 354 // Make sure no protocols missed negotiation 355 for name := range tt.Match { 356 if _, ok := result[name]; !ok { 357 t.Errorf("test %d, proto '%s': not negotiated, should have", i, name) 358 continue 359 } 360 } 361 } 362 }