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