github.com/fff-chain/go-fff@v0.0.0-20220726032732-1c84420b8a99/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/fff-chain/go-fff/core/types"
    26  	"github.com/fff-chain/go-fff/crypto"
    27  	"github.com/fff-chain/go-fff/eth/protocols/eth"
    28  	"github.com/fff-chain/go-fff/internal/utesting"
    29  	"github.com/fff-chain/go-fff/p2p"
    30  	"github.com/fff-chain/go-fff/p2p/rlpx"
    31  	"github.com/fff-chain/go-fff/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  type GetPooledTransactions eth.GetPooledTransactionsPacket
   124  
   125  func (gpt GetPooledTransactions) Code() int { return 25 }
   126  
   127  type PooledTransactions eth.PooledTransactionsPacket
   128  
   129  func (pt PooledTransactions) Code() int { return 26 }
   130  
   131  // Conn represents an individual connection with a peer
   132  type Conn struct {
   133  	*rlpx.Conn
   134  	ourKey                 *ecdsa.PrivateKey
   135  	negotiatedProtoVersion uint
   136  	ourHighestProtoVersion uint
   137  	caps                   []p2p.Cap
   138  }
   139  
   140  func (c *Conn) Read() Message {
   141  	code, rawData, _, err := c.Conn.Read()
   142  	if err != nil {
   143  		return errorf("could not read from connection: %v", err)
   144  	}
   145  
   146  	var msg Message
   147  	switch int(code) {
   148  	case (Hello{}).Code():
   149  		msg = new(Hello)
   150  	case (Ping{}).Code():
   151  		msg = new(Ping)
   152  	case (Pong{}).Code():
   153  		msg = new(Pong)
   154  	case (Disconnect{}).Code():
   155  		msg = new(Disconnect)
   156  	case (Status{}).Code():
   157  		msg = new(Status)
   158  	case (GetBlockHeaders{}).Code():
   159  		msg = new(GetBlockHeaders)
   160  	case (BlockHeaders{}).Code():
   161  		msg = new(BlockHeaders)
   162  	case (GetBlockBodies{}).Code():
   163  		msg = new(GetBlockBodies)
   164  	case (BlockBodies{}).Code():
   165  		msg = new(BlockBodies)
   166  	case (NewBlock{}).Code():
   167  		msg = new(NewBlock)
   168  	case (NewBlockHashes{}).Code():
   169  		msg = new(NewBlockHashes)
   170  	case (Transactions{}).Code():
   171  		msg = new(Transactions)
   172  	case (NewPooledTransactionHashes{}).Code():
   173  		msg = new(NewPooledTransactionHashes)
   174  	case (GetPooledTransactions{}.Code()):
   175  		msg = new(GetPooledTransactions)
   176  	case (PooledTransactions{}.Code()):
   177  		msg = new(PooledTransactions)
   178  	default:
   179  		return errorf("invalid message code: %d", code)
   180  	}
   181  	// if message is devp2p, decode here
   182  	if err := rlp.DecodeBytes(rawData, msg); err != nil {
   183  		return errorf("could not rlp decode message: %v", err)
   184  	}
   185  	return msg
   186  }
   187  
   188  // ReadAndServe serves GetBlockHeaders requests while waiting
   189  // on another message from the node.
   190  func (c *Conn) ReadAndServe(chain *Chain, timeout time.Duration) Message {
   191  	start := time.Now()
   192  	for time.Since(start) < timeout {
   193  		c.SetReadDeadline(time.Now().Add(5 * time.Second))
   194  		switch msg := c.Read().(type) {
   195  		case *Ping:
   196  			c.Write(&Pong{})
   197  		case *GetBlockHeaders:
   198  			req := *msg
   199  			headers, err := chain.GetHeaders(req)
   200  			if err != nil {
   201  				return errorf("could not get headers for inbound header request: %v", err)
   202  			}
   203  
   204  			if err := c.Write(headers); err != nil {
   205  				return errorf("could not write to connection: %v", err)
   206  			}
   207  		default:
   208  			return msg
   209  		}
   210  	}
   211  	return errorf("no message received within %v", timeout)
   212  }
   213  
   214  func (c *Conn) Write(msg Message) error {
   215  	// check if message is eth protocol message
   216  	var (
   217  		payload []byte
   218  		err     error
   219  	)
   220  	payload, err = rlp.EncodeToBytes(msg)
   221  	if err != nil {
   222  		return err
   223  	}
   224  	_, err = c.Conn.Write(uint64(msg.Code()), payload)
   225  	return err
   226  }
   227  
   228  // handshake checks to make sure a `HELLO` is received.
   229  func (c *Conn) handshake(t *utesting.T) Message {
   230  	defer c.SetDeadline(time.Time{})
   231  	c.SetDeadline(time.Now().Add(10 * time.Second))
   232  
   233  	// write hello to client
   234  	pub0 := crypto.FromECDSAPub(&c.ourKey.PublicKey)[1:]
   235  	ourHandshake := &Hello{
   236  		Version: 5,
   237  		Caps:    c.caps,
   238  		ID:      pub0,
   239  	}
   240  	if err := c.Write(ourHandshake); err != nil {
   241  		t.Fatalf("could not write to connection: %v", err)
   242  	}
   243  	// read hello from client
   244  	switch msg := c.Read().(type) {
   245  	case *Hello:
   246  		// set snappy if version is at least 5
   247  		if msg.Version >= 5 {
   248  			c.SetSnappy(true)
   249  		}
   250  		c.negotiateEthProtocol(msg.Caps)
   251  		if c.negotiatedProtoVersion == 0 {
   252  			t.Fatalf("unexpected eth protocol version")
   253  		}
   254  		return msg
   255  	default:
   256  		t.Fatalf("bad handshake: %#v", msg)
   257  		return nil
   258  	}
   259  }
   260  
   261  // negotiateEthProtocol sets the Conn's eth protocol version
   262  // to highest advertised capability from peer
   263  func (c *Conn) negotiateEthProtocol(caps []p2p.Cap) {
   264  	var highestEthVersion uint
   265  	for _, capability := range caps {
   266  		if capability.Name != "eth" {
   267  			continue
   268  		}
   269  		if capability.Version > highestEthVersion && capability.Version <= c.ourHighestProtoVersion {
   270  			highestEthVersion = capability.Version
   271  		}
   272  	}
   273  	c.negotiatedProtoVersion = highestEthVersion
   274  }
   275  
   276  // statusExchange performs a `Status` message exchange with the given
   277  // node.
   278  func (c *Conn) statusExchange(t *utesting.T, chain *Chain, status *Status) Message {
   279  	defer c.SetDeadline(time.Time{})
   280  	c.SetDeadline(time.Now().Add(20 * time.Second))
   281  
   282  	// read status message from client
   283  	var message Message
   284  loop:
   285  	for {
   286  		switch msg := c.Read().(type) {
   287  		case *Status:
   288  			if have, want := msg.Head, chain.blocks[chain.Len()-1].Hash(); have != want {
   289  				t.Fatalf("wrong head block in status, want:  %#x (block %d) have %#x",
   290  					want, chain.blocks[chain.Len()-1].NumberU64(), have)
   291  			}
   292  			if have, want := msg.TD.Cmp(chain.TD(chain.Len())), 0; have != want {
   293  				t.Fatalf("wrong TD in status: have %v want %v", have, want)
   294  			}
   295  			if have, want := msg.ForkID, chain.ForkID(); !reflect.DeepEqual(have, want) {
   296  				t.Fatalf("wrong fork ID in status: have %v, want %v", have, want)
   297  			}
   298  			message = msg
   299  			break loop
   300  		case *Disconnect:
   301  			t.Fatalf("disconnect received: %v", msg.Reason)
   302  		case *Ping:
   303  			c.Write(&Pong{}) // TODO (renaynay): in the future, this should be an error
   304  			// (PINGs should not be a response upon fresh connection)
   305  		default:
   306  			t.Fatalf("bad status message: %s", pretty.Sdump(msg))
   307  		}
   308  	}
   309  	// make sure eth protocol version is set for negotiation
   310  	if c.negotiatedProtoVersion == 0 {
   311  		t.Fatalf("eth protocol version must be set in Conn")
   312  	}
   313  	if status == nil {
   314  		// write status message to client
   315  		status = &Status{
   316  			ProtocolVersion: uint32(c.negotiatedProtoVersion),
   317  			NetworkID:       chain.chainConfig.ChainID.Uint64(),
   318  			TD:              chain.TD(chain.Len()),
   319  			Head:            chain.blocks[chain.Len()-1].Hash(),
   320  			Genesis:         chain.blocks[0].Hash(),
   321  			ForkID:          chain.ForkID(),
   322  		}
   323  	}
   324  
   325  	if err := c.Write(status); err != nil {
   326  		t.Fatalf("could not write to connection: %v", err)
   327  	}
   328  
   329  	return message
   330  }
   331  
   332  // waitForBlock waits for confirmation from the client that it has
   333  // imported the given block.
   334  func (c *Conn) waitForBlock(block *types.Block) error {
   335  	defer c.SetReadDeadline(time.Time{})
   336  
   337  	c.SetReadDeadline(time.Now().Add(20 * time.Second))
   338  	// note: if the node has not yet imported the block, it will respond
   339  	// to the GetBlockHeaders request with an empty BlockHeaders response,
   340  	// so the GetBlockHeaders request must be sent again until the BlockHeaders
   341  	// response contains the desired header.
   342  	for {
   343  		req := &GetBlockHeaders{Origin: eth.HashOrNumber{Hash: block.Hash()}, Amount: 1}
   344  		if err := c.Write(req); err != nil {
   345  			return err
   346  		}
   347  		switch msg := c.Read().(type) {
   348  		case *BlockHeaders:
   349  			for _, header := range *msg {
   350  				if header.Number.Uint64() == block.NumberU64() {
   351  					return nil
   352  				}
   353  			}
   354  			time.Sleep(100 * time.Millisecond)
   355  		default:
   356  			return fmt.Errorf("invalid message: %s", pretty.Sdump(msg))
   357  		}
   358  	}
   359  }