github.com/Inphi/go-ethereum@v1.9.7/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  }