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