github.com/juliankolbe/go-ethereum@v1.9.992/cmd/devp2p/internal/ethtest/types.go (about)

     1  // Copyright 2020 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 ethtest
    18  
    19  import (
    20  	"crypto/ecdsa"
    21  	"fmt"
    22  	"reflect"
    23  	"time"
    24  
    25  	"github.com/juliankolbe/go-ethereum/core/types"
    26  	"github.com/juliankolbe/go-ethereum/crypto"
    27  	"github.com/juliankolbe/go-ethereum/eth/protocols/eth"
    28  	"github.com/juliankolbe/go-ethereum/internal/utesting"
    29  	"github.com/juliankolbe/go-ethereum/p2p"
    30  	"github.com/juliankolbe/go-ethereum/p2p/rlpx"
    31  	"github.com/juliankolbe/go-ethereum/rlp"
    32  )
    33  
    34  type Message interface {
    35  	Code() int
    36  }
    37  
    38  type Error struct {
    39  	err error
    40  }
    41  
    42  func (e *Error) Unwrap() error  { return e.err }
    43  func (e *Error) Error() string  { return e.err.Error() }
    44  func (e *Error) Code() int      { return -1 }
    45  func (e *Error) String() string { return e.Error() }
    46  
    47  func errorf(format string, args ...interface{}) *Error {
    48  	return &Error{fmt.Errorf(format, args...)}
    49  }
    50  
    51  // Hello is the RLP structure of the protocol handshake.
    52  type Hello struct {
    53  	Version    uint64
    54  	Name       string
    55  	Caps       []p2p.Cap
    56  	ListenPort uint64
    57  	ID         []byte // secp256k1 public key
    58  
    59  	// Ignore additional fields (for forward compatibility).
    60  	Rest []rlp.RawValue `rlp:"tail"`
    61  }
    62  
    63  func (h Hello) Code() int { return 0x00 }
    64  
    65  // Disconnect is the RLP structure for a disconnect message.
    66  type Disconnect struct {
    67  	Reason p2p.DiscReason
    68  }
    69  
    70  func (d Disconnect) Code() int { return 0x01 }
    71  
    72  type Ping struct{}
    73  
    74  func (p Ping) Code() int { return 0x02 }
    75  
    76  type Pong struct{}
    77  
    78  func (p Pong) Code() int { return 0x03 }
    79  
    80  // Status is the network packet for the status message for eth/64 and later.
    81  type Status eth.StatusPacket
    82  
    83  func (s Status) Code() int { return 16 }
    84  
    85  // NewBlockHashes is the network packet for the block announcements.
    86  type NewBlockHashes eth.NewBlockHashesPacket
    87  
    88  func (nbh NewBlockHashes) Code() int { return 17 }
    89  
    90  type Transactions eth.TransactionsPacket
    91  
    92  func (t Transactions) Code() int { return 18 }
    93  
    94  // GetBlockHeaders represents a block header query.
    95  type GetBlockHeaders eth.GetBlockHeadersPacket
    96  
    97  func (g GetBlockHeaders) Code() int { return 19 }
    98  
    99  type BlockHeaders eth.BlockHeadersPacket
   100  
   101  func (bh BlockHeaders) Code() int { return 20 }
   102  
   103  // GetBlockBodies represents a GetBlockBodies request
   104  type GetBlockBodies eth.GetBlockBodiesPacket
   105  
   106  func (gbb GetBlockBodies) Code() int { return 21 }
   107  
   108  // BlockBodies is the network packet for block content distribution.
   109  type BlockBodies eth.BlockBodiesPacket
   110  
   111  func (bb BlockBodies) Code() int { return 22 }
   112  
   113  // NewBlock is the network packet for the block propagation message.
   114  type NewBlock eth.NewBlockPacket
   115  
   116  func (nb NewBlock) Code() int { return 23 }
   117  
   118  // NewPooledTransactionHashes is the network packet for the tx hash propagation message.
   119  type NewPooledTransactionHashes eth.NewPooledTransactionHashesPacket
   120  
   121  func (nb NewPooledTransactionHashes) Code() int { return 24 }
   122  
   123  // Conn represents an individual connection with a peer
   124  type Conn struct {
   125  	*rlpx.Conn
   126  	ourKey             *ecdsa.PrivateKey
   127  	ethProtocolVersion uint
   128  	caps               []p2p.Cap
   129  }
   130  
   131  func (c *Conn) Read() Message {
   132  	code, rawData, _, err := c.Conn.Read()
   133  	if err != nil {
   134  		return errorf("could not read from connection: %v", err)
   135  	}
   136  
   137  	var msg Message
   138  	switch int(code) {
   139  	case (Hello{}).Code():
   140  		msg = new(Hello)
   141  	case (Ping{}).Code():
   142  		msg = new(Ping)
   143  	case (Pong{}).Code():
   144  		msg = new(Pong)
   145  	case (Disconnect{}).Code():
   146  		msg = new(Disconnect)
   147  	case (Status{}).Code():
   148  		msg = new(Status)
   149  	case (GetBlockHeaders{}).Code():
   150  		msg = new(GetBlockHeaders)
   151  	case (BlockHeaders{}).Code():
   152  		msg = new(BlockHeaders)
   153  	case (GetBlockBodies{}).Code():
   154  		msg = new(GetBlockBodies)
   155  	case (BlockBodies{}).Code():
   156  		msg = new(BlockBodies)
   157  	case (NewBlock{}).Code():
   158  		msg = new(NewBlock)
   159  	case (NewBlockHashes{}).Code():
   160  		msg = new(NewBlockHashes)
   161  	case (Transactions{}).Code():
   162  		msg = new(Transactions)
   163  	case (NewPooledTransactionHashes{}).Code():
   164  		msg = new(NewPooledTransactionHashes)
   165  	default:
   166  		return errorf("invalid message code: %d", code)
   167  	}
   168  	// if message is devp2p, decode here
   169  	if err := rlp.DecodeBytes(rawData, msg); err != nil {
   170  		return errorf("could not rlp decode message: %v", err)
   171  	}
   172  	return msg
   173  }
   174  
   175  // ReadAndServe serves GetBlockHeaders requests while waiting
   176  // on another message from the node.
   177  func (c *Conn) ReadAndServe(chain *Chain, timeout time.Duration) Message {
   178  	start := time.Now()
   179  	for time.Since(start) < timeout {
   180  		timeout := time.Now().Add(10 * time.Second)
   181  		c.SetReadDeadline(timeout)
   182  		switch msg := c.Read().(type) {
   183  		case *Ping:
   184  			c.Write(&Pong{})
   185  		case *GetBlockHeaders:
   186  			req := *msg
   187  			headers, err := chain.GetHeaders(req)
   188  			if err != nil {
   189  				return errorf("could not get headers for inbound header request: %v", err)
   190  			}
   191  
   192  			if err := c.Write(headers); err != nil {
   193  				return errorf("could not write to connection: %v", err)
   194  			}
   195  		default:
   196  			return msg
   197  		}
   198  	}
   199  	return errorf("no message received within %v", timeout)
   200  }
   201  
   202  func (c *Conn) Write(msg Message) error {
   203  	// check if message is eth protocol message
   204  	var (
   205  		payload []byte
   206  		err     error
   207  	)
   208  	payload, err = rlp.EncodeToBytes(msg)
   209  	if err != nil {
   210  		return err
   211  	}
   212  	_, err = c.Conn.Write(uint64(msg.Code()), payload)
   213  	return err
   214  }
   215  
   216  // handshake checks to make sure a `HELLO` is received.
   217  func (c *Conn) handshake(t *utesting.T) Message {
   218  	defer c.SetDeadline(time.Time{})
   219  	c.SetDeadline(time.Now().Add(10 * time.Second))
   220  
   221  	// write hello to client
   222  	pub0 := crypto.FromECDSAPub(&c.ourKey.PublicKey)[1:]
   223  	ourHandshake := &Hello{
   224  		Version: 5,
   225  		Caps:    c.caps,
   226  		ID:      pub0,
   227  	}
   228  	if err := c.Write(ourHandshake); err != nil {
   229  		t.Fatalf("could not write to connection: %v", err)
   230  	}
   231  	// read hello from client
   232  	switch msg := c.Read().(type) {
   233  	case *Hello:
   234  		// set snappy if version is at least 5
   235  		if msg.Version >= 5 {
   236  			c.SetSnappy(true)
   237  		}
   238  		c.negotiateEthProtocol(msg.Caps)
   239  		if c.ethProtocolVersion == 0 {
   240  			t.Fatalf("unexpected eth protocol version")
   241  		}
   242  		return msg
   243  	default:
   244  		t.Fatalf("bad handshake: %#v", msg)
   245  		return nil
   246  	}
   247  }
   248  
   249  // negotiateEthProtocol sets the Conn's eth protocol version
   250  // to highest advertised capability from peer
   251  func (c *Conn) negotiateEthProtocol(caps []p2p.Cap) {
   252  	var highestEthVersion uint
   253  	for _, capability := range caps {
   254  		if capability.Name != "eth" {
   255  			continue
   256  		}
   257  		if capability.Version > highestEthVersion && capability.Version <= 65 {
   258  			highestEthVersion = capability.Version
   259  		}
   260  	}
   261  	c.ethProtocolVersion = highestEthVersion
   262  }
   263  
   264  // statusExchange performs a `Status` message exchange with the given
   265  // node.
   266  func (c *Conn) statusExchange(t *utesting.T, chain *Chain, status *Status) Message {
   267  	defer c.SetDeadline(time.Time{})
   268  	c.SetDeadline(time.Now().Add(20 * time.Second))
   269  
   270  	// read status message from client
   271  	var message Message
   272  loop:
   273  	for {
   274  		switch msg := c.Read().(type) {
   275  		case *Status:
   276  			if msg.Head != chain.blocks[chain.Len()-1].Hash() {
   277  				t.Fatalf("wrong head block in status: %s", msg.Head.String())
   278  			}
   279  			if msg.TD.Cmp(chain.TD(chain.Len())) != 0 {
   280  				t.Fatalf("wrong TD in status: %v", msg.TD)
   281  			}
   282  			if !reflect.DeepEqual(msg.ForkID, chain.ForkID()) {
   283  				t.Fatalf("wrong fork ID in status: %v", msg.ForkID)
   284  			}
   285  			message = msg
   286  			break loop
   287  		case *Disconnect:
   288  			t.Fatalf("disconnect received: %v", msg.Reason)
   289  		case *Ping:
   290  			c.Write(&Pong{}) // TODO (renaynay): in the future, this should be an error
   291  			// (PINGs should not be a response upon fresh connection)
   292  		default:
   293  			t.Fatalf("bad status message: %s", pretty.Sdump(msg))
   294  		}
   295  	}
   296  	// make sure eth protocol version is set for negotiation
   297  	if c.ethProtocolVersion == 0 {
   298  		t.Fatalf("eth protocol version must be set in Conn")
   299  	}
   300  	if status == nil {
   301  		// write status message to client
   302  		status = &Status{
   303  			ProtocolVersion: uint32(c.ethProtocolVersion),
   304  			NetworkID:       chain.chainConfig.ChainID.Uint64(),
   305  			TD:              chain.TD(chain.Len()),
   306  			Head:            chain.blocks[chain.Len()-1].Hash(),
   307  			Genesis:         chain.blocks[0].Hash(),
   308  			ForkID:          chain.ForkID(),
   309  		}
   310  	}
   311  
   312  	if err := c.Write(status); err != nil {
   313  		t.Fatalf("could not write to connection: %v", err)
   314  	}
   315  
   316  	return message
   317  }
   318  
   319  // waitForBlock waits for confirmation from the client that it has
   320  // imported the given block.
   321  func (c *Conn) waitForBlock(block *types.Block) error {
   322  	defer c.SetReadDeadline(time.Time{})
   323  
   324  	timeout := time.Now().Add(20 * time.Second)
   325  	c.SetReadDeadline(timeout)
   326  	for {
   327  		req := &GetBlockHeaders{Origin: eth.HashOrNumber{Hash: block.Hash()}, Amount: 1}
   328  		if err := c.Write(req); err != nil {
   329  			return err
   330  		}
   331  		switch msg := c.Read().(type) {
   332  		case *BlockHeaders:
   333  			if len(*msg) > 0 {
   334  				return nil
   335  			}
   336  			time.Sleep(100 * time.Millisecond)
   337  		default:
   338  			return fmt.Errorf("invalid message: %s", pretty.Sdump(msg))
   339  		}
   340  	}
   341  }