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