github.com/codingfuture/orig-energi3@v0.8.4/p2p/protocols/protocol_test.go (about)

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