github.com/jincm/wesharechain@v0.0.0-20210122032815-1537409ce26a/chain/p2p/protocols/protocol_test.go (about)

     1  // Copyright 2017 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 protocols
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"errors"
    23  	"fmt"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/ethereum/go-ethereum/rlp"
    28  
    29  	"github.com/ethereum/go-ethereum/p2p"
    30  	"github.com/ethereum/go-ethereum/p2p/enode"
    31  	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
    32  	p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
    33  )
    34  
    35  // handshake message type
    36  type hs0 struct {
    37  	C uint
    38  }
    39  
    40  // message to kill/drop the peer with nodeID
    41  type kill struct {
    42  	C enode.ID
    43  }
    44  
    45  // message to drop connection
    46  type drop struct {
    47  }
    48  
    49  /// protoHandshake represents module-independent aspects of the protocol and is
    50  // the first message peers send and receive as part the initial exchange
    51  type protoHandshake struct {
    52  	Version   uint   // local and remote peer should have identical version
    53  	NetworkID string // local and remote peer should have identical network id
    54  }
    55  
    56  // checkProtoHandshake verifies local and remote protoHandshakes match
    57  func checkProtoHandshake(testVersion uint, testNetworkID string) func(interface{}) error {
    58  	return func(rhs interface{}) error {
    59  		remote := rhs.(*protoHandshake)
    60  		if remote.NetworkID != testNetworkID {
    61  			return fmt.Errorf("%s (!= %s)", remote.NetworkID, testNetworkID)
    62  		}
    63  
    64  		if remote.Version != testVersion {
    65  			return fmt.Errorf("%d (!= %d)", remote.Version, testVersion)
    66  		}
    67  		return nil
    68  	}
    69  }
    70  
    71  // newProtocol sets up a protocol
    72  // the run function here demonstrates a typical protocol using peerPool, handshake
    73  // and messages registered to handlers
    74  func newProtocol(pp *p2ptest.TestPeerPool) func(*p2p.Peer, p2p.MsgReadWriter) error {
    75  	spec := &Spec{
    76  		Name:       "test",
    77  		Version:    42,
    78  		MaxMsgSize: 10 * 1024,
    79  		Messages: []interface{}{
    80  			protoHandshake{},
    81  			hs0{},
    82  			kill{},
    83  			drop{},
    84  		},
    85  	}
    86  	return func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
    87  		peer := NewPeer(p, rw, spec)
    88  
    89  		// initiate one-off protohandshake and check validity
    90  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    91  		defer cancel()
    92  		phs := &protoHandshake{42, "420"}
    93  		hsCheck := checkProtoHandshake(phs.Version, phs.NetworkID)
    94  		_, err := peer.Handshake(ctx, phs, hsCheck)
    95  		if err != nil {
    96  			return err
    97  		}
    98  
    99  		lhs := &hs0{42}
   100  		// module handshake demonstrating a simple repeatable exchange of same-type message
   101  		hs, err := peer.Handshake(ctx, lhs, nil)
   102  		if err != nil {
   103  			return err
   104  		}
   105  
   106  		if rmhs := hs.(*hs0); rmhs.C > lhs.C {
   107  			return fmt.Errorf("handshake mismatch remote %v > local %v", rmhs.C, lhs.C)
   108  		}
   109  
   110  		handle := func(ctx context.Context, msg interface{}) error {
   111  			switch msg := msg.(type) {
   112  
   113  			case *protoHandshake:
   114  				return errors.New("duplicate handshake")
   115  
   116  			case *hs0:
   117  				rhs := msg
   118  				if rhs.C > lhs.C {
   119  					return fmt.Errorf("handshake mismatch remote %v > local %v", rhs.C, lhs.C)
   120  				}
   121  				lhs.C += rhs.C
   122  				return peer.Send(ctx, lhs)
   123  
   124  			case *kill:
   125  				// demonstrates use of peerPool, killing another peer connection as a response to a message
   126  				id := msg.C
   127  				pp.Get(id).Drop(errors.New("killed"))
   128  				return nil
   129  
   130  			case *drop:
   131  				// for testing we can trigger self induced disconnect upon receiving drop message
   132  				return errors.New("dropped")
   133  
   134  			default:
   135  				return fmt.Errorf("unknown message type: %T", msg)
   136  			}
   137  		}
   138  
   139  		pp.Add(peer)
   140  		defer pp.Remove(peer)
   141  		return peer.Run(handle)
   142  	}
   143  }
   144  
   145  func protocolTester(pp *p2ptest.TestPeerPool) *p2ptest.ProtocolTester {
   146  	conf := adapters.RandomNodeConfig()
   147  	return p2ptest.NewProtocolTester(conf.ID, 2, newProtocol(pp))
   148  }
   149  
   150  func protoHandshakeExchange(id enode.ID, proto *protoHandshake) []p2ptest.Exchange {
   151  
   152  	return []p2ptest.Exchange{
   153  		{
   154  			Expects: []p2ptest.Expect{
   155  				{
   156  					Code: 0,
   157  					Msg:  &protoHandshake{42, "420"},
   158  					Peer: id,
   159  				},
   160  			},
   161  		},
   162  		{
   163  			Triggers: []p2ptest.Trigger{
   164  				{
   165  					Code: 0,
   166  					Msg:  proto,
   167  					Peer: id,
   168  				},
   169  			},
   170  		},
   171  	}
   172  }
   173  
   174  func runProtoHandshake(t *testing.T, proto *protoHandshake, errs ...error) {
   175  	pp := p2ptest.NewTestPeerPool()
   176  	s := protocolTester(pp)
   177  	// TODO: make this more than one handshake
   178  	node := s.Nodes[0]
   179  	if err := s.TestExchanges(protoHandshakeExchange(node.ID(), proto)...); err != nil {
   180  		t.Fatal(err)
   181  	}
   182  	var disconnects []*p2ptest.Disconnect
   183  	for i, err := range errs {
   184  		disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.Nodes[i].ID(), Error: err})
   185  	}
   186  	if err := s.TestDisconnected(disconnects...); err != nil {
   187  		t.Fatal(err)
   188  	}
   189  }
   190  
   191  type dummyHook struct {
   192  	peer  *Peer
   193  	size  uint32
   194  	msg   interface{}
   195  	send  bool
   196  	err   error
   197  	waitC chan struct{}
   198  }
   199  
   200  type dummyMsg struct {
   201  	Content string
   202  }
   203  
   204  func (d *dummyHook) Send(peer *Peer, size uint32, msg interface{}) error {
   205  	d.peer = peer
   206  	d.size = size
   207  	d.msg = msg
   208  	d.send = true
   209  	return d.err
   210  }
   211  
   212  func (d *dummyHook) Receive(peer *Peer, size uint32, msg interface{}) error {
   213  	d.peer = peer
   214  	d.size = size
   215  	d.msg = msg
   216  	d.send = false
   217  	d.waitC <- struct{}{}
   218  	return d.err
   219  }
   220  
   221  func TestProtocolHook(t *testing.T) {
   222  	testHook := &dummyHook{
   223  		waitC: make(chan struct{}, 1),
   224  	}
   225  	spec := &Spec{
   226  		Name:       "test",
   227  		Version:    42,
   228  		MaxMsgSize: 10 * 1024,
   229  		Messages: []interface{}{
   230  			dummyMsg{},
   231  		},
   232  		Hook: testHook,
   233  	}
   234  
   235  	runFunc := func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
   236  		peer := NewPeer(p, rw, spec)
   237  		ctx := context.TODO()
   238  		err := peer.Send(ctx, &dummyMsg{
   239  			Content: "handshake"})
   240  
   241  		if err != nil {
   242  			t.Fatal(err)
   243  		}
   244  
   245  		handle := func(ctx context.Context, msg interface{}) error {
   246  			return nil
   247  		}
   248  
   249  		return peer.Run(handle)
   250  	}
   251  
   252  	conf := adapters.RandomNodeConfig()
   253  	tester := p2ptest.NewProtocolTester(conf.ID, 2, runFunc)
   254  	err := tester.TestExchanges(p2ptest.Exchange{
   255  		Expects: []p2ptest.Expect{
   256  			{
   257  				Code: 0,
   258  				Msg:  &dummyMsg{Content: "handshake"},
   259  				Peer: tester.Nodes[0].ID(),
   260  			},
   261  		},
   262  	})
   263  	if err != nil {
   264  		t.Fatal(err)
   265  	}
   266  	if testHook.msg == nil || testHook.msg.(*dummyMsg).Content != "handshake" {
   267  		t.Fatal("Expected msg to be set, but it is not")
   268  	}
   269  	if !testHook.send {
   270  		t.Fatal("Expected a send message, but it is not")
   271  	}
   272  	if testHook.peer == nil {
   273  		t.Fatal("Expected peer to be set, is nil")
   274  	}
   275  	if peerId := testHook.peer.ID(); peerId != tester.Nodes[0].ID() && peerId != tester.Nodes[1].ID() {
   276  		t.Fatalf("Expected peer ID to be set correctly, but it is not (got %v, exp %v or %v", peerId, tester.Nodes[0].ID(), tester.Nodes[1].ID())
   277  	}
   278  	if testHook.size != 11 { //11 is the length of the encoded message
   279  		t.Fatalf("Expected size to be %d, but it is %d ", 1, testHook.size)
   280  	}
   281  
   282  	err = tester.TestExchanges(p2ptest.Exchange{
   283  		Triggers: []p2ptest.Trigger{
   284  			{
   285  				Code: 0,
   286  				Msg:  &dummyMsg{Content: "response"},
   287  				Peer: tester.Nodes[1].ID(),
   288  			},
   289  		},
   290  	})
   291  
   292  	<-testHook.waitC
   293  
   294  	if err != nil {
   295  		t.Fatal(err)
   296  	}
   297  	if testHook.msg == nil || testHook.msg.(*dummyMsg).Content != "response" {
   298  		t.Fatal("Expected msg to be set, but it is not")
   299  	}
   300  	if testHook.send {
   301  		t.Fatal("Expected a send message, but it is not")
   302  	}
   303  	if testHook.peer == nil || testHook.peer.ID() != tester.Nodes[1].ID() {
   304  		t.Fatal("Expected peer ID to be set correctly, but it is not")
   305  	}
   306  	if testHook.size != 10 { //11 is the length of the encoded message
   307  		t.Fatalf("Expected size to be %d, but it is %d ", 1, testHook.size)
   308  	}
   309  
   310  	testHook.err = fmt.Errorf("dummy error")
   311  	err = tester.TestExchanges(p2ptest.Exchange{
   312  		Triggers: []p2ptest.Trigger{
   313  			{
   314  				Code: 0,
   315  				Msg:  &dummyMsg{Content: "response"},
   316  				Peer: tester.Nodes[1].ID(),
   317  			},
   318  		},
   319  	})
   320  
   321  	<-testHook.waitC
   322  
   323  	time.Sleep(100 * time.Millisecond)
   324  	err = tester.TestDisconnected(&p2ptest.Disconnect{Peer: tester.Nodes[1].ID(), Error: testHook.err})
   325  	if err != nil {
   326  		t.Fatalf("Expected a specific disconnect error, but got different one: %v", err)
   327  	}
   328  
   329  }
   330  
   331  //We need to test that if the hook is not defined, then message infrastructure
   332  //(send,receive) still works
   333  func TestNoHook(t *testing.T) {
   334  	//create a test spec
   335  	spec := createTestSpec()
   336  	//a random node
   337  	id := adapters.RandomNodeConfig().ID
   338  	//a peer
   339  	p := p2p.NewPeer(id, "testPeer", nil)
   340  	rw := &dummyRW{}
   341  	peer := NewPeer(p, rw, spec)
   342  	ctx := context.TODO()
   343  	msg := &perBytesMsgSenderPays{Content: "testBalance"}
   344  	//send a message
   345  	err := peer.Send(ctx, msg)
   346  	if err != nil {
   347  		t.Fatal(err)
   348  	}
   349  	//simulate receiving a message
   350  	rw.msg = msg
   351  	peer.handleIncoming(func(ctx context.Context, msg interface{}) error {
   352  		return nil
   353  	})
   354  	//all should just work and not result in any error
   355  }
   356  
   357  func TestProtoHandshakeVersionMismatch(t *testing.T) {
   358  	runProtoHandshake(t, &protoHandshake{41, "420"}, errorf(ErrHandshake, errorf(ErrHandler, "(msg code 0): 41 (!= 42)").Error()))
   359  }
   360  
   361  func TestProtoHandshakeNetworkIDMismatch(t *testing.T) {
   362  	runProtoHandshake(t, &protoHandshake{42, "421"}, errorf(ErrHandshake, errorf(ErrHandler, "(msg code 0): 421 (!= 420)").Error()))
   363  }
   364  
   365  func TestProtoHandshakeSuccess(t *testing.T) {
   366  	runProtoHandshake(t, &protoHandshake{42, "420"})
   367  }
   368  
   369  func moduleHandshakeExchange(id enode.ID, resp uint) []p2ptest.Exchange {
   370  
   371  	return []p2ptest.Exchange{
   372  		{
   373  			Expects: []p2ptest.Expect{
   374  				{
   375  					Code: 1,
   376  					Msg:  &hs0{42},
   377  					Peer: id,
   378  				},
   379  			},
   380  		},
   381  		{
   382  			Triggers: []p2ptest.Trigger{
   383  				{
   384  					Code: 1,
   385  					Msg:  &hs0{resp},
   386  					Peer: id,
   387  				},
   388  			},
   389  		},
   390  	}
   391  }
   392  
   393  func runModuleHandshake(t *testing.T, resp uint, errs ...error) {
   394  	pp := p2ptest.NewTestPeerPool()
   395  	s := protocolTester(pp)
   396  	node := s.Nodes[0]
   397  	if err := s.TestExchanges(protoHandshakeExchange(node.ID(), &protoHandshake{42, "420"})...); err != nil {
   398  		t.Fatal(err)
   399  	}
   400  	if err := s.TestExchanges(moduleHandshakeExchange(node.ID(), resp)...); err != nil {
   401  		t.Fatal(err)
   402  	}
   403  	var disconnects []*p2ptest.Disconnect
   404  	for i, err := range errs {
   405  		disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.Nodes[i].ID(), Error: err})
   406  	}
   407  	if err := s.TestDisconnected(disconnects...); err != nil {
   408  		t.Fatal(err)
   409  	}
   410  }
   411  
   412  func TestModuleHandshakeError(t *testing.T) {
   413  	runModuleHandshake(t, 43, fmt.Errorf("handshake mismatch remote 43 > local 42"))
   414  }
   415  
   416  func TestModuleHandshakeSuccess(t *testing.T) {
   417  	runModuleHandshake(t, 42)
   418  }
   419  
   420  // testing complex interactions over multiple peers, relaying, dropping
   421  func testMultiPeerSetup(a, b enode.ID) []p2ptest.Exchange {
   422  
   423  	return []p2ptest.Exchange{
   424  		{
   425  			Label: "primary handshake",
   426  			Expects: []p2ptest.Expect{
   427  				{
   428  					Code: 0,
   429  					Msg:  &protoHandshake{42, "420"},
   430  					Peer: a,
   431  				},
   432  				{
   433  					Code: 0,
   434  					Msg:  &protoHandshake{42, "420"},
   435  					Peer: b,
   436  				},
   437  			},
   438  		},
   439  		{
   440  			Label: "module handshake",
   441  			Triggers: []p2ptest.Trigger{
   442  				{
   443  					Code: 0,
   444  					Msg:  &protoHandshake{42, "420"},
   445  					Peer: a,
   446  				},
   447  				{
   448  					Code: 0,
   449  					Msg:  &protoHandshake{42, "420"},
   450  					Peer: b,
   451  				},
   452  			},
   453  			Expects: []p2ptest.Expect{
   454  				{
   455  					Code: 1,
   456  					Msg:  &hs0{42},
   457  					Peer: a,
   458  				},
   459  				{
   460  					Code: 1,
   461  					Msg:  &hs0{42},
   462  					Peer: b,
   463  				},
   464  			},
   465  		},
   466  
   467  		{Label: "alternative module handshake", Triggers: []p2ptest.Trigger{{Code: 1, Msg: &hs0{41}, Peer: a},
   468  			{Code: 1, Msg: &hs0{41}, Peer: b}}},
   469  		{Label: "repeated module handshake", Triggers: []p2ptest.Trigger{{Code: 1, Msg: &hs0{1}, Peer: a}}},
   470  		{Label: "receiving repeated module handshake", Expects: []p2ptest.Expect{{Code: 1, Msg: &hs0{43}, Peer: a}}}}
   471  }
   472  
   473  func runMultiplePeers(t *testing.T, peer int, errs ...error) {
   474  	pp := p2ptest.NewTestPeerPool()
   475  	s := protocolTester(pp)
   476  
   477  	if err := s.TestExchanges(testMultiPeerSetup(s.Nodes[0].ID(), s.Nodes[1].ID())...); err != nil {
   478  		t.Fatal(err)
   479  	}
   480  	// after some exchanges of messages, we can test state changes
   481  	// here this is simply demonstrated by the peerPool
   482  	// after the handshake negotiations peers must be added to the pool
   483  	// time.Sleep(1)
   484  	tick := time.NewTicker(10 * time.Millisecond)
   485  	timeout := time.NewTimer(1 * time.Second)
   486  WAIT:
   487  	for {
   488  		select {
   489  		case <-tick.C:
   490  			if pp.Has(s.Nodes[0].ID()) {
   491  				break WAIT
   492  			}
   493  		case <-timeout.C:
   494  			t.Fatal("timeout")
   495  		}
   496  	}
   497  	if !pp.Has(s.Nodes[1].ID()) {
   498  		t.Fatalf("missing peer test-1: %v (%v)", pp, s.Nodes)
   499  	}
   500  
   501  	// peer 0 sends kill request for peer with index <peer>
   502  	err := s.TestExchanges(p2ptest.Exchange{
   503  		Triggers: []p2ptest.Trigger{
   504  			{
   505  				Code: 2,
   506  				Msg:  &kill{s.Nodes[peer].ID()},
   507  				Peer: s.Nodes[0].ID(),
   508  			},
   509  		},
   510  	})
   511  
   512  	if err != nil {
   513  		t.Fatal(err)
   514  	}
   515  
   516  	// the peer not killed sends a drop request
   517  	err = s.TestExchanges(p2ptest.Exchange{
   518  		Triggers: []p2ptest.Trigger{
   519  			{
   520  				Code: 3,
   521  				Msg:  &drop{},
   522  				Peer: s.Nodes[(peer+1)%2].ID(),
   523  			},
   524  		},
   525  	})
   526  
   527  	if err != nil {
   528  		t.Fatal(err)
   529  	}
   530  
   531  	// check the actual discconnect errors on the individual peers
   532  	var disconnects []*p2ptest.Disconnect
   533  	for i, err := range errs {
   534  		disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.Nodes[i].ID(), Error: err})
   535  	}
   536  	if err := s.TestDisconnected(disconnects...); err != nil {
   537  		t.Fatal(err)
   538  	}
   539  	// test if disconnected peers have been removed from peerPool
   540  	if pp.Has(s.Nodes[peer].ID()) {
   541  		t.Fatalf("peer test-%v not dropped: %v (%v)", peer, pp, s.Nodes)
   542  	}
   543  
   544  }
   545  func XTestMultiplePeersDropSelf(t *testing.T) {
   546  	runMultiplePeers(t, 0,
   547  		fmt.Errorf("subprotocol error"),
   548  		fmt.Errorf("Message handler error: (msg code 3): dropped"),
   549  	)
   550  }
   551  
   552  func XTestMultiplePeersDropOther(t *testing.T) {
   553  	runMultiplePeers(t, 1,
   554  		fmt.Errorf("Message handler error: (msg code 3): dropped"),
   555  		fmt.Errorf("subprotocol error"),
   556  	)
   557  }
   558  
   559  //dummy implementation of a MsgReadWriter
   560  //this allows for quick and easy unit tests without
   561  //having to build up the complete protocol
   562  type dummyRW struct {
   563  	msg  interface{}
   564  	size uint32
   565  	code uint64
   566  }
   567  
   568  func (d *dummyRW) WriteMsg(msg p2p.Msg) error {
   569  	return nil
   570  }
   571  
   572  func (d *dummyRW) ReadMsg() (p2p.Msg, error) {
   573  	enc := bytes.NewReader(d.getDummyMsg())
   574  	return p2p.Msg{
   575  		Code:       d.code,
   576  		Size:       d.size,
   577  		Payload:    enc,
   578  		ReceivedAt: time.Now(),
   579  	}, nil
   580  }
   581  
   582  func (d *dummyRW) getDummyMsg() []byte {
   583  	r, _ := rlp.EncodeToBytes(d.msg)
   584  	var b bytes.Buffer
   585  	wmsg := WrappedMsg{
   586  		Context: b.Bytes(),
   587  		Size:    uint32(len(r)),
   588  		Payload: r,
   589  	}
   590  	rr, _ := rlp.EncodeToBytes(wmsg)
   591  	d.size = uint32(len(rr))
   592  	return rr
   593  }