github.com/luckypickle/go-ethereum-vet@v1.14.2/p2p/peer_test.go (about) 1 // Copyright 2014 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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-ethereum 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-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package p2p 18 19 import ( 20 "errors" 21 "fmt" 22 "math/rand" 23 "net" 24 "reflect" 25 "testing" 26 "time" 27 ) 28 29 var discard = Protocol{ 30 Name: "discard", 31 Length: 1, 32 Run: func(p *Peer, rw MsgReadWriter) error { 33 for { 34 msg, err := rw.ReadMsg() 35 if err != nil { 36 return err 37 } 38 fmt.Printf("discarding %d\n", msg.Code) 39 if err = msg.Discard(); err != nil { 40 return err 41 } 42 } 43 }, 44 } 45 46 func testPeer(protos []Protocol) (func(), *conn, *Peer, <-chan error) { 47 fd1, fd2 := net.Pipe() 48 c1 := &conn{fd: fd1, transport: newTestTransport(randomID(), fd1)} 49 c2 := &conn{fd: fd2, transport: newTestTransport(randomID(), fd2)} 50 for _, p := range protos { 51 c1.caps = append(c1.caps, p.cap()) 52 c2.caps = append(c2.caps, p.cap()) 53 } 54 55 peer := newPeer(c1, protos) 56 errc := make(chan error, 1) 57 go func() { 58 _, err := peer.run() 59 errc <- err 60 }() 61 62 closer := func() { c2.close(errors.New("close func called")) } 63 return closer, c2, peer, errc 64 } 65 66 func TestPeerProtoReadMsg(t *testing.T) { 67 proto := Protocol{ 68 Name: "a", 69 Length: 5, 70 Run: func(peer *Peer, rw MsgReadWriter) error { 71 if err := ExpectMsg(rw, 2, []uint{1}); err != nil { 72 t.Error(err) 73 } 74 if err := ExpectMsg(rw, 3, []uint{2}); err != nil { 75 t.Error(err) 76 } 77 if err := ExpectMsg(rw, 4, []uint{3}); err != nil { 78 t.Error(err) 79 } 80 return nil 81 }, 82 } 83 84 closer, rw, _, errc := testPeer([]Protocol{proto}) 85 defer closer() 86 87 Send(rw, baseProtocolLength+2, []uint{1}) 88 Send(rw, baseProtocolLength+3, []uint{2}) 89 Send(rw, baseProtocolLength+4, []uint{3}) 90 91 select { 92 case err := <-errc: 93 if err != errProtocolReturned { 94 t.Errorf("peer returned error: %v", err) 95 } 96 case <-time.After(2 * time.Second): 97 t.Errorf("receive timeout") 98 } 99 } 100 101 func TestPeerProtoEncodeMsg(t *testing.T) { 102 proto := Protocol{ 103 Name: "a", 104 Length: 2, 105 Run: func(peer *Peer, rw MsgReadWriter) error { 106 if err := SendItems(rw, 2); err == nil { 107 t.Error("expected error for out-of-range msg code, got nil") 108 } 109 if err := SendItems(rw, 1, "foo", "bar"); err != nil { 110 t.Errorf("write error: %v", err) 111 } 112 return nil 113 }, 114 } 115 closer, rw, _, _ := testPeer([]Protocol{proto}) 116 defer closer() 117 118 if err := ExpectMsg(rw, 17, []string{"foo", "bar"}); err != nil { 119 t.Error(err) 120 } 121 } 122 123 func TestPeerPing(t *testing.T) { 124 closer, rw, _, _ := testPeer(nil) 125 defer closer() 126 if err := SendItems(rw, pingMsg); err != nil { 127 t.Fatal(err) 128 } 129 if err := ExpectMsg(rw, pongMsg, nil); err != nil { 130 t.Error(err) 131 } 132 } 133 134 func TestPeerDisconnect(t *testing.T) { 135 closer, rw, _, disc := testPeer(nil) 136 defer closer() 137 if err := SendItems(rw, discMsg, DiscQuitting); err != nil { 138 t.Fatal(err) 139 } 140 select { 141 case reason := <-disc: 142 if reason != DiscQuitting { 143 t.Errorf("run returned wrong reason: got %v, want %v", reason, DiscQuitting) 144 } 145 case <-time.After(500 * time.Millisecond): 146 t.Error("peer did not return") 147 } 148 } 149 150 // This test is supposed to verify that Peer can reliably handle 151 // multiple causes of disconnection occurring at the same time. 152 func TestPeerDisconnectRace(t *testing.T) { 153 maybe := func() bool { return rand.Intn(1) == 1 } 154 155 for i := 0; i < 1000; i++ { 156 protoclose := make(chan error) 157 protodisc := make(chan DiscReason) 158 closer, rw, p, disc := testPeer([]Protocol{ 159 { 160 Name: "closereq", 161 Run: func(p *Peer, rw MsgReadWriter) error { return <-protoclose }, 162 Length: 1, 163 }, 164 { 165 Name: "disconnect", 166 Run: func(p *Peer, rw MsgReadWriter) error { p.Disconnect(<-protodisc); return nil }, 167 Length: 1, 168 }, 169 }) 170 171 // Simulate incoming messages. 172 go SendItems(rw, baseProtocolLength+1) 173 go SendItems(rw, baseProtocolLength+2) 174 // Close the network connection. 175 go closer() 176 // Make protocol "closereq" return. 177 protoclose <- errors.New("protocol closed") 178 // Make protocol "disconnect" call peer.Disconnect 179 protodisc <- DiscAlreadyConnected 180 // In some cases, simulate something else calling peer.Disconnect. 181 if maybe() { 182 go p.Disconnect(DiscInvalidIdentity) 183 } 184 // In some cases, simulate remote requesting a disconnect. 185 if maybe() { 186 go SendItems(rw, discMsg, DiscQuitting) 187 } 188 189 select { 190 case <-disc: 191 case <-time.After(2 * time.Second): 192 // Peer.run should return quickly. If it doesn't the Peer 193 // goroutines are probably deadlocked. Call panic in order to 194 // show the stacks. 195 panic("Peer.run took to long to return.") 196 } 197 } 198 } 199 200 func TestNewPeer(t *testing.T) { 201 name := "nodename" 202 caps := []Cap{{"foo", 2}, {"bar", 3}} 203 id := randomID() 204 p := NewPeer(id, name, caps) 205 if p.ID() != id { 206 t.Errorf("ID mismatch: got %v, expected %v", p.ID(), id) 207 } 208 if p.Name() != name { 209 t.Errorf("Name mismatch: got %v, expected %v", p.Name(), name) 210 } 211 if !reflect.DeepEqual(p.Caps(), caps) { 212 t.Errorf("Caps mismatch: got %v, expected %v", p.Caps(), caps) 213 } 214 215 p.Disconnect(DiscAlreadyConnected) // Should not hang 216 } 217 218 func TestMatchProtocols(t *testing.T) { 219 tests := []struct { 220 Remote []Cap 221 Local []Protocol 222 Match map[string]protoRW 223 }{ 224 { 225 // No remote capabilities 226 Local: []Protocol{{Name: "a"}}, 227 }, 228 { 229 // No local protocols 230 Remote: []Cap{{Name: "a"}}, 231 }, 232 { 233 // No mutual protocols 234 Remote: []Cap{{Name: "a"}}, 235 Local: []Protocol{{Name: "b"}}, 236 }, 237 { 238 // Some matches, some differences 239 Remote: []Cap{{Name: "local"}, {Name: "match1"}, {Name: "match2"}}, 240 Local: []Protocol{{Name: "match1"}, {Name: "match2"}, {Name: "remote"}}, 241 Match: map[string]protoRW{"match1": {Protocol: Protocol{Name: "match1"}}, "match2": {Protocol: Protocol{Name: "match2"}}}, 242 }, 243 { 244 // Various alphabetical ordering 245 Remote: []Cap{{Name: "aa"}, {Name: "ab"}, {Name: "bb"}, {Name: "ba"}}, 246 Local: []Protocol{{Name: "ba"}, {Name: "bb"}, {Name: "ab"}, {Name: "aa"}}, 247 Match: map[string]protoRW{"aa": {Protocol: Protocol{Name: "aa"}}, "ab": {Protocol: Protocol{Name: "ab"}}, "ba": {Protocol: Protocol{Name: "ba"}}, "bb": {Protocol: Protocol{Name: "bb"}}}, 248 }, 249 { 250 // No mutual versions 251 Remote: []Cap{{Version: 1}}, 252 Local: []Protocol{{Version: 2}}, 253 }, 254 { 255 // Multiple versions, single common 256 Remote: []Cap{{Version: 1}, {Version: 2}}, 257 Local: []Protocol{{Version: 2}, {Version: 3}}, 258 Match: map[string]protoRW{"": {Protocol: Protocol{Version: 2}}}, 259 }, 260 { 261 // Multiple versions, multiple common 262 Remote: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Version: 4}}, 263 Local: []Protocol{{Version: 2}, {Version: 3}}, 264 Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}}, 265 }, 266 { 267 // Various version orderings 268 Remote: []Cap{{Version: 4}, {Version: 1}, {Version: 3}, {Version: 2}}, 269 Local: []Protocol{{Version: 2}, {Version: 3}, {Version: 1}}, 270 Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}}, 271 }, 272 { 273 // Versions overriding sub-protocol lengths 274 Remote: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Name: "a"}}, 275 Local: []Protocol{{Version: 1, Length: 1}, {Version: 2, Length: 2}, {Version: 3, Length: 3}, {Name: "a"}}, 276 Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}, "a": {Protocol: Protocol{Name: "a"}, offset: 3}}, 277 }, 278 } 279 280 for i, tt := range tests { 281 result := matchProtocols(tt.Local, tt.Remote, nil) 282 if len(result) != len(tt.Match) { 283 t.Errorf("test %d: negotiation mismatch: have %v, want %v", i, len(result), len(tt.Match)) 284 continue 285 } 286 // Make sure all negotiated protocols are needed and correct 287 for name, proto := range result { 288 match, ok := tt.Match[name] 289 if !ok { 290 t.Errorf("test %d, proto '%s': negotiated but shouldn't have", i, name) 291 continue 292 } 293 if proto.Name != match.Name { 294 t.Errorf("test %d, proto '%s': name mismatch: have %v, want %v", i, name, proto.Name, match.Name) 295 } 296 if proto.Version != match.Version { 297 t.Errorf("test %d, proto '%s': version mismatch: have %v, want %v", i, name, proto.Version, match.Version) 298 } 299 if proto.offset-baseProtocolLength != match.offset { 300 t.Errorf("test %d, proto '%s': offset mismatch: have %v, want %v", i, name, proto.offset-baseProtocolLength, match.offset) 301 } 302 } 303 // Make sure no protocols missed negotiation 304 for name := range tt.Match { 305 if _, ok := result[name]; !ok { 306 t.Errorf("test %d, proto '%s': not negotiated, should have", i, name) 307 continue 308 } 309 } 310 } 311 }