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