cmd/devp2p: add eth protocol test suite (#21598)
This change adds a test framework for the "eth" protocol and some basic tests. The tests can be run using the './devp2p rlpx eth-test' command.
This commit is contained in:
		
							
								
								
									
										113
									
								
								cmd/devp2p/internal/ethtest/chain.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								cmd/devp2p/internal/ethtest/chain.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,113 @@
 | 
				
			|||||||
 | 
					package ethtest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"compress/gzip"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"math/big"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/core"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/core/forkid"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/core/types"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/params"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/rlp"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Chain struct {
 | 
				
			||||||
 | 
						blocks      []*types.Block
 | 
				
			||||||
 | 
						chainConfig *params.ChainConfig
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Chain) WriteTo(writer io.Writer) error {
 | 
				
			||||||
 | 
						for _, block := range c.blocks {
 | 
				
			||||||
 | 
							if err := rlp.Encode(writer, block); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Len returns the length of the chain.
 | 
				
			||||||
 | 
					func (c *Chain) Len() int {
 | 
				
			||||||
 | 
						return len(c.blocks)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TD calculates the total difficulty of the chain.
 | 
				
			||||||
 | 
					func (c *Chain) TD(height int) *big.Int { // TODO later on channge scheme so that the height is included in range
 | 
				
			||||||
 | 
						sum := big.NewInt(0)
 | 
				
			||||||
 | 
						for _, block := range c.blocks[:height] {
 | 
				
			||||||
 | 
							sum.Add(sum, block.Difficulty())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return sum
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ForkID gets the fork id of the chain.
 | 
				
			||||||
 | 
					func (c *Chain) ForkID() forkid.ID {
 | 
				
			||||||
 | 
						return forkid.NewID(c.chainConfig, c.blocks[0].Hash(), uint64(c.Len()))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Shorten returns a copy chain of a desired height from the imported
 | 
				
			||||||
 | 
					func (c *Chain) Shorten(height int) *Chain {
 | 
				
			||||||
 | 
						blocks := make([]*types.Block, height)
 | 
				
			||||||
 | 
						copy(blocks, c.blocks[:height])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						config := *c.chainConfig
 | 
				
			||||||
 | 
						return &Chain{
 | 
				
			||||||
 | 
							blocks:      blocks,
 | 
				
			||||||
 | 
							chainConfig: &config,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Head returns the chain head.
 | 
				
			||||||
 | 
					func (c *Chain) Head() *types.Block {
 | 
				
			||||||
 | 
						return c.blocks[c.Len()-1]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// loadChain takes the given chain.rlp file, and decodes and returns
 | 
				
			||||||
 | 
					// the blocks from the file.
 | 
				
			||||||
 | 
					func loadChain(chainfile string, genesis string) (*Chain, error) {
 | 
				
			||||||
 | 
						// Open the file handle and potentially unwrap the gzip stream
 | 
				
			||||||
 | 
						fh, err := os.Open(chainfile)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer fh.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var reader io.Reader = fh
 | 
				
			||||||
 | 
						if strings.HasSuffix(chainfile, ".gz") {
 | 
				
			||||||
 | 
							if reader, err = gzip.NewReader(reader); err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						stream := rlp.NewStream(reader, 0)
 | 
				
			||||||
 | 
						var blocks []*types.Block
 | 
				
			||||||
 | 
						for i := 0; ; i++ {
 | 
				
			||||||
 | 
							var b types.Block
 | 
				
			||||||
 | 
							if err := stream.Decode(&b); err == io.EOF {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							} else if err != nil {
 | 
				
			||||||
 | 
								return nil, fmt.Errorf("at block %d: %v", i, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							blocks = append(blocks, &b)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Open the file handle and potentially unwrap the gzip stream
 | 
				
			||||||
 | 
						chainConfig, err := ioutil.ReadFile(genesis)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var gen core.Genesis
 | 
				
			||||||
 | 
						if err := json.Unmarshal(chainConfig, &gen); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &Chain{
 | 
				
			||||||
 | 
							blocks:      blocks,
 | 
				
			||||||
 | 
							chainConfig: gen.Config,
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										337
									
								
								cmd/devp2p/internal/ethtest/suite.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										337
									
								
								cmd/devp2p/internal/ethtest/suite.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,337 @@
 | 
				
			|||||||
 | 
					package ethtest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"crypto/ecdsa"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/core/types"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/crypto"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/internal/utesting"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/p2p"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/p2p/enode"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/p2p/rlpx"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/rlp"
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Suite represents a structure used to test the eth
 | 
				
			||||||
 | 
					// protocol of a node(s).
 | 
				
			||||||
 | 
					type Suite struct {
 | 
				
			||||||
 | 
						Dest *enode.Node
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						chain     *Chain
 | 
				
			||||||
 | 
						fullChain *Chain
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Conn struct {
 | 
				
			||||||
 | 
						*rlpx.Conn
 | 
				
			||||||
 | 
						ourKey *ecdsa.PrivateKey
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Conn) Read() Message {
 | 
				
			||||||
 | 
						code, rawData, _, err := c.Conn.Read()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return &Error{fmt.Errorf("could not read from connection: %v", err)}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var msg Message
 | 
				
			||||||
 | 
						switch int(code) {
 | 
				
			||||||
 | 
						case (Hello{}).Code():
 | 
				
			||||||
 | 
							msg = new(Hello)
 | 
				
			||||||
 | 
						case (Disconnect{}).Code():
 | 
				
			||||||
 | 
							msg = new(Disconnect)
 | 
				
			||||||
 | 
						case (Status{}).Code():
 | 
				
			||||||
 | 
							msg = new(Status)
 | 
				
			||||||
 | 
						case (GetBlockHeaders{}).Code():
 | 
				
			||||||
 | 
							msg = new(GetBlockHeaders)
 | 
				
			||||||
 | 
						case (BlockHeaders{}).Code():
 | 
				
			||||||
 | 
							msg = new(BlockHeaders)
 | 
				
			||||||
 | 
						case (GetBlockBodies{}).Code():
 | 
				
			||||||
 | 
							msg = new(GetBlockBodies)
 | 
				
			||||||
 | 
						case (BlockBodies{}).Code():
 | 
				
			||||||
 | 
							msg = new(BlockBodies)
 | 
				
			||||||
 | 
						case (NewBlock{}).Code():
 | 
				
			||||||
 | 
							msg = new(NewBlock)
 | 
				
			||||||
 | 
						case (NewBlockHashes{}).Code():
 | 
				
			||||||
 | 
							msg = new(NewBlockHashes)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return &Error{fmt.Errorf("invalid message code: %d", code)}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := rlp.DecodeBytes(rawData, msg); err != nil {
 | 
				
			||||||
 | 
							return &Error{fmt.Errorf("could not rlp decode message: %v", err)}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return msg
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Conn) Write(msg Message) error {
 | 
				
			||||||
 | 
						payload, err := rlp.EncodeToBytes(msg)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, err = c.Conn.Write(uint64(msg.Code()), payload)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// handshake checks to make sure a `HELLO` is received.
 | 
				
			||||||
 | 
					func (c *Conn) handshake(t *utesting.T) Message {
 | 
				
			||||||
 | 
						// write protoHandshake to client
 | 
				
			||||||
 | 
						pub0 := crypto.FromECDSAPub(&c.ourKey.PublicKey)[1:]
 | 
				
			||||||
 | 
						ourHandshake := &Hello{
 | 
				
			||||||
 | 
							Version: 3,
 | 
				
			||||||
 | 
							Caps:    []p2p.Cap{{Name: "eth", Version: 64}, {Name: "eth", Version: 65}},
 | 
				
			||||||
 | 
							ID:      pub0,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := c.Write(ourHandshake); err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("could not write to connection: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// read protoHandshake from client
 | 
				
			||||||
 | 
						switch msg := c.Read().(type) {
 | 
				
			||||||
 | 
						case *Hello:
 | 
				
			||||||
 | 
							return msg
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							t.Fatalf("bad handshake: %v", msg)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// statusExchange performs a `Status` message exchange with the given
 | 
				
			||||||
 | 
					// node.
 | 
				
			||||||
 | 
					func (c *Conn) statusExchange(t *utesting.T, chain *Chain) Message {
 | 
				
			||||||
 | 
						// read status message from client
 | 
				
			||||||
 | 
						var message Message
 | 
				
			||||||
 | 
						switch msg := c.Read().(type) {
 | 
				
			||||||
 | 
						case *Status:
 | 
				
			||||||
 | 
							if msg.Head != chain.blocks[chain.Len()-1].Hash() {
 | 
				
			||||||
 | 
								t.Fatalf("wrong head in status: %v", msg.Head)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if msg.TD.Cmp(chain.TD(chain.Len())) != 0 {
 | 
				
			||||||
 | 
								t.Fatalf("wrong TD in status: %v", msg.TD)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !reflect.DeepEqual(msg.ForkID, chain.ForkID()) {
 | 
				
			||||||
 | 
								t.Fatalf("wrong fork ID in status: %v", msg.ForkID)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							message = msg
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							t.Fatalf("bad status message: %v", msg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// write status message to client
 | 
				
			||||||
 | 
						status := Status{
 | 
				
			||||||
 | 
							ProtocolVersion: 65,
 | 
				
			||||||
 | 
							NetworkID:       1,
 | 
				
			||||||
 | 
							TD:              chain.TD(chain.Len()),
 | 
				
			||||||
 | 
							Head:            chain.blocks[chain.Len()-1].Hash(),
 | 
				
			||||||
 | 
							Genesis:         chain.blocks[0].Hash(),
 | 
				
			||||||
 | 
							ForkID:          chain.ForkID(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := c.Write(status); err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("could not write to connection: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return message
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// waitForBlock waits for confirmation from the client that it has
 | 
				
			||||||
 | 
					// imported the given block.
 | 
				
			||||||
 | 
					func (c *Conn) waitForBlock(block *types.Block) error {
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							req := &GetBlockHeaders{Origin: hashOrNumber{Hash: block.Hash()}, Amount: 1}
 | 
				
			||||||
 | 
							if err := c.Write(req); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							switch msg := c.Read().(type) {
 | 
				
			||||||
 | 
							case *BlockHeaders:
 | 
				
			||||||
 | 
								if len(*msg) > 0 {
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								time.Sleep(100 * time.Millisecond)
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return fmt.Errorf("invalid message: %v", msg)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewSuite creates and returns a new eth-test suite that can
 | 
				
			||||||
 | 
					// be used to test the given node against the given blockchain
 | 
				
			||||||
 | 
					// data.
 | 
				
			||||||
 | 
					func NewSuite(dest *enode.Node, chainfile string, genesisfile string) *Suite {
 | 
				
			||||||
 | 
						chain, err := loadChain(chainfile, genesisfile)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return &Suite{
 | 
				
			||||||
 | 
							Dest:      dest,
 | 
				
			||||||
 | 
							chain:     chain.Shorten(1000),
 | 
				
			||||||
 | 
							fullChain: chain,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *Suite) AllTests() []utesting.Test {
 | 
				
			||||||
 | 
						return []utesting.Test{
 | 
				
			||||||
 | 
							{Name: "Status", Fn: s.TestStatus},
 | 
				
			||||||
 | 
							{Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders},
 | 
				
			||||||
 | 
							{Name: "Broadcast", Fn: s.TestBroadcast},
 | 
				
			||||||
 | 
							{Name: "GetBlockBodies", Fn: s.TestGetBlockBodies},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TestStatus attempts to connect to the given node and exchange
 | 
				
			||||||
 | 
					// a status message with it, and then check to make sure
 | 
				
			||||||
 | 
					// the chain head is correct.
 | 
				
			||||||
 | 
					func (s *Suite) TestStatus(t *utesting.T) {
 | 
				
			||||||
 | 
						conn, err := s.dial()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("could not dial: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// get protoHandshake
 | 
				
			||||||
 | 
						conn.handshake(t)
 | 
				
			||||||
 | 
						// get status
 | 
				
			||||||
 | 
						switch msg := conn.statusExchange(t, s.chain).(type) {
 | 
				
			||||||
 | 
						case *Status:
 | 
				
			||||||
 | 
							t.Logf("%+v\n", msg)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							t.Fatalf("error: %v", msg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TestGetBlockHeaders tests whether the given node can respond to
 | 
				
			||||||
 | 
					// a `GetBlockHeaders` request and that the response is accurate.
 | 
				
			||||||
 | 
					func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
 | 
				
			||||||
 | 
						conn, err := s.dial()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("could not dial: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						conn.handshake(t)
 | 
				
			||||||
 | 
						conn.statusExchange(t, s.chain)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// get block headers
 | 
				
			||||||
 | 
						req := &GetBlockHeaders{
 | 
				
			||||||
 | 
							Origin: hashOrNumber{
 | 
				
			||||||
 | 
								Hash: s.chain.blocks[1].Hash(),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Amount:  2,
 | 
				
			||||||
 | 
							Skip:    1,
 | 
				
			||||||
 | 
							Reverse: false,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := conn.Write(req); err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("could not write to connection: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch msg := conn.Read().(type) {
 | 
				
			||||||
 | 
						case *BlockHeaders:
 | 
				
			||||||
 | 
							headers := msg
 | 
				
			||||||
 | 
							for _, header := range *headers {
 | 
				
			||||||
 | 
								num := header.Number.Uint64()
 | 
				
			||||||
 | 
								assert.Equal(t, s.chain.blocks[int(num)].Header(), header)
 | 
				
			||||||
 | 
								t.Logf("\nHEADER FOR BLOCK NUMBER %d: %+v\n", header.Number, header)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							t.Fatalf("error: %v", msg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TestGetBlockBodies tests whether the given node can respond to
 | 
				
			||||||
 | 
					// a `GetBlockBodies` request and that the response is accurate.
 | 
				
			||||||
 | 
					func (s *Suite) TestGetBlockBodies(t *utesting.T) {
 | 
				
			||||||
 | 
						conn, err := s.dial()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("could not dial: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						conn.handshake(t)
 | 
				
			||||||
 | 
						conn.statusExchange(t, s.chain)
 | 
				
			||||||
 | 
						// create block bodies request
 | 
				
			||||||
 | 
						req := &GetBlockBodies{s.chain.blocks[54].Hash(), s.chain.blocks[75].Hash()}
 | 
				
			||||||
 | 
						if err := conn.Write(req); err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("could not write to connection: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch msg := conn.Read().(type) {
 | 
				
			||||||
 | 
						case *BlockBodies:
 | 
				
			||||||
 | 
							bodies := msg
 | 
				
			||||||
 | 
							for _, body := range *bodies {
 | 
				
			||||||
 | 
								t.Logf("\nBODY: %+v\n", body)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							t.Fatalf("error: %v", msg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TestBroadcast tests whether a block announcement is correctly
 | 
				
			||||||
 | 
					// propagated to the given node's peer(s).
 | 
				
			||||||
 | 
					func (s *Suite) TestBroadcast(t *utesting.T) {
 | 
				
			||||||
 | 
						// create conn to send block announcement
 | 
				
			||||||
 | 
						sendConn, err := s.dial()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("could not dial: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// create conn to receive block announcement
 | 
				
			||||||
 | 
						receiveConn, err := s.dial()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("could not dial: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sendConn.handshake(t)
 | 
				
			||||||
 | 
						receiveConn.handshake(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sendConn.statusExchange(t, s.chain)
 | 
				
			||||||
 | 
						receiveConn.statusExchange(t, s.chain)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// sendConn sends the block announcement
 | 
				
			||||||
 | 
						blockAnnouncement := &NewBlock{
 | 
				
			||||||
 | 
							Block: s.fullChain.blocks[1000],
 | 
				
			||||||
 | 
							TD:    s.fullChain.TD(1001),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := sendConn.Write(blockAnnouncement); err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("could not write to connection: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch msg := receiveConn.Read().(type) {
 | 
				
			||||||
 | 
						case *NewBlock:
 | 
				
			||||||
 | 
							assert.Equal(t, blockAnnouncement.Block.Header(), msg.Block.Header(),
 | 
				
			||||||
 | 
								"wrong block header in announcement")
 | 
				
			||||||
 | 
							assert.Equal(t, blockAnnouncement.TD, msg.TD,
 | 
				
			||||||
 | 
								"wrong TD in announcement")
 | 
				
			||||||
 | 
						case *NewBlockHashes:
 | 
				
			||||||
 | 
							hashes := *msg
 | 
				
			||||||
 | 
							assert.Equal(t, blockAnnouncement.Block.Hash(), hashes[0].Hash,
 | 
				
			||||||
 | 
								"wrong block hash in announcement")
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							t.Fatal(msg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// update test suite chain
 | 
				
			||||||
 | 
						s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[1000])
 | 
				
			||||||
 | 
						// wait for client to update its chain
 | 
				
			||||||
 | 
						if err := receiveConn.waitForBlock(s.chain.Head()); err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// dial attempts to dial the given node and perform a handshake,
 | 
				
			||||||
 | 
					// returning the created Conn if successful.
 | 
				
			||||||
 | 
					func (s *Suite) dial() (*Conn, error) {
 | 
				
			||||||
 | 
						var conn Conn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fd, err := net.Dial("tcp", fmt.Sprintf("%v:%d", s.Dest.IP(), s.Dest.TCP()))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						conn.Conn = rlpx.NewConn(fd, s.Dest.Pubkey())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// do encHandshake
 | 
				
			||||||
 | 
						conn.ourKey, _ = crypto.GenerateKey()
 | 
				
			||||||
 | 
						_, err = conn.Handshake(conn.ourKey)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &conn, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										134
									
								
								cmd/devp2p/internal/ethtest/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								cmd/devp2p/internal/ethtest/types.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,134 @@
 | 
				
			|||||||
 | 
					package ethtest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"math/big"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/common"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/core/forkid"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/core/types"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/p2p"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/rlp"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Message interface {
 | 
				
			||||||
 | 
						Code() int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Error struct {
 | 
				
			||||||
 | 
						err error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *Error) Unwrap() error { return e.err }
 | 
				
			||||||
 | 
					func (e *Error) Error() string { return e.err.Error() }
 | 
				
			||||||
 | 
					func (e *Error) Code() int     { return -1 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Hello is the RLP structure of the protocol handshake.
 | 
				
			||||||
 | 
					type Hello struct {
 | 
				
			||||||
 | 
						Version    uint64
 | 
				
			||||||
 | 
						Name       string
 | 
				
			||||||
 | 
						Caps       []p2p.Cap
 | 
				
			||||||
 | 
						ListenPort uint64
 | 
				
			||||||
 | 
						ID         []byte // secp256k1 public key
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Ignore additional fields (for forward compatibility).
 | 
				
			||||||
 | 
						Rest []rlp.RawValue `rlp:"tail"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h Hello) Code() int { return 0x00 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Disconnect is the RLP structure for a disconnect message.
 | 
				
			||||||
 | 
					type Disconnect struct {
 | 
				
			||||||
 | 
						Reason p2p.DiscReason
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d Disconnect) Code() int { return 0x01 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Status is the network packet for the status message for eth/64 and later.
 | 
				
			||||||
 | 
					type Status struct {
 | 
				
			||||||
 | 
						ProtocolVersion uint32
 | 
				
			||||||
 | 
						NetworkID       uint64
 | 
				
			||||||
 | 
						TD              *big.Int
 | 
				
			||||||
 | 
						Head            common.Hash
 | 
				
			||||||
 | 
						Genesis         common.Hash
 | 
				
			||||||
 | 
						ForkID          forkid.ID
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s Status) Code() int { return 16 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewBlockHashes is the network packet for the block announcements.
 | 
				
			||||||
 | 
					type NewBlockHashes []struct {
 | 
				
			||||||
 | 
						Hash   common.Hash // Hash of one particular block being announced
 | 
				
			||||||
 | 
						Number uint64      // Number of one particular block being announced
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (nbh NewBlockHashes) Code() int { return 17 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewBlock is the network packet for the block propagation message.
 | 
				
			||||||
 | 
					type NewBlock struct {
 | 
				
			||||||
 | 
						Block *types.Block
 | 
				
			||||||
 | 
						TD    *big.Int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (nb NewBlock) Code() int { return 23 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetBlockHeaders represents a block header query.
 | 
				
			||||||
 | 
					type GetBlockHeaders struct {
 | 
				
			||||||
 | 
						Origin  hashOrNumber // Block from which to retrieve headers
 | 
				
			||||||
 | 
						Amount  uint64       // Maximum number of headers to retrieve
 | 
				
			||||||
 | 
						Skip    uint64       // Blocks to skip between consecutive headers
 | 
				
			||||||
 | 
						Reverse bool         // Query direction (false = rising towards latest, true = falling towards genesis)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (g GetBlockHeaders) Code() int { return 19 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type BlockHeaders []*types.Header
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (bh BlockHeaders) Code() int { return 20 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// HashOrNumber is a combined field for specifying an origin block.
 | 
				
			||||||
 | 
					type hashOrNumber struct {
 | 
				
			||||||
 | 
						Hash   common.Hash // Block hash from which to retrieve headers (excludes Number)
 | 
				
			||||||
 | 
						Number uint64      // Block hash from which to retrieve headers (excludes Hash)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// EncodeRLP is a specialized encoder for hashOrNumber to encode only one of the
 | 
				
			||||||
 | 
					// two contained union fields.
 | 
				
			||||||
 | 
					func (hn *hashOrNumber) EncodeRLP(w io.Writer) error {
 | 
				
			||||||
 | 
						if hn.Hash == (common.Hash{}) {
 | 
				
			||||||
 | 
							return rlp.Encode(w, hn.Number)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if hn.Number != 0 {
 | 
				
			||||||
 | 
							return fmt.Errorf("both origin hash (%x) and number (%d) provided", hn.Hash, hn.Number)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return rlp.Encode(w, hn.Hash)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DecodeRLP is a specialized decoder for hashOrNumber to decode the contents
 | 
				
			||||||
 | 
					// into either a block hash or a block number.
 | 
				
			||||||
 | 
					func (hn *hashOrNumber) DecodeRLP(s *rlp.Stream) error {
 | 
				
			||||||
 | 
						_, size, _ := s.Kind()
 | 
				
			||||||
 | 
						origin, err := s.Raw()
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							switch {
 | 
				
			||||||
 | 
							case size == 32:
 | 
				
			||||||
 | 
								err = rlp.DecodeBytes(origin, &hn.Hash)
 | 
				
			||||||
 | 
							case size <= 8:
 | 
				
			||||||
 | 
								err = rlp.DecodeBytes(origin, &hn.Number)
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								err = fmt.Errorf("invalid input size %d for origin", size)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetBlockBodies represents a GetBlockBodies request
 | 
				
			||||||
 | 
					type GetBlockBodies []common.Hash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (gbb GetBlockBodies) Code() int { return 21 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BlockBodies is the network packet for block content distribution.
 | 
				
			||||||
 | 
					type BlockBodies []*types.Body
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (bb BlockBodies) Code() int { return 22 }
 | 
				
			||||||
@@ -81,7 +81,7 @@ func commandHasFlag(ctx *cli.Context, flag cli.Flag) bool {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// getNodeArg handles the common case of a single node descriptor argument.
 | 
					// getNodeArg handles the common case of a single node descriptor argument.
 | 
				
			||||||
func getNodeArg(ctx *cli.Context) *enode.Node {
 | 
					func getNodeArg(ctx *cli.Context) *enode.Node {
 | 
				
			||||||
	if ctx.NArg() != 1 {
 | 
						if ctx.NArg() < 1 {
 | 
				
			||||||
		exit("missing node as command-line argument")
 | 
							exit("missing node as command-line argument")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	n, err := parseNode(ctx.Args()[0])
 | 
						n, err := parseNode(ctx.Args()[0])
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,9 +19,11 @@ package main
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/common/hexutil"
 | 
						"github.com/ethereum/go-ethereum/cmd/devp2p/internal/ethtest"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/crypto"
 | 
						"github.com/ethereum/go-ethereum/crypto"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/internal/utesting"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/p2p"
 | 
						"github.com/ethereum/go-ethereum/p2p"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/p2p/rlpx"
 | 
						"github.com/ethereum/go-ethereum/p2p/rlpx"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/rlp"
 | 
						"github.com/ethereum/go-ethereum/rlp"
 | 
				
			||||||
@@ -34,38 +36,42 @@ var (
 | 
				
			|||||||
		Usage: "RLPx Commands",
 | 
							Usage: "RLPx Commands",
 | 
				
			||||||
		Subcommands: []cli.Command{
 | 
							Subcommands: []cli.Command{
 | 
				
			||||||
			rlpxPingCommand,
 | 
								rlpxPingCommand,
 | 
				
			||||||
 | 
								rlpxEthTestCommand,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	rlpxPingCommand = cli.Command{
 | 
						rlpxPingCommand = cli.Command{
 | 
				
			||||||
		Name:      "ping",
 | 
							Name:   "ping",
 | 
				
			||||||
		Usage:     "Perform a RLPx handshake",
 | 
							Usage:  "ping <node>",
 | 
				
			||||||
		ArgsUsage: "<node>",
 | 
							Action: rlpxPing,
 | 
				
			||||||
		Action:    rlpxPing,
 | 
						}
 | 
				
			||||||
 | 
						rlpxEthTestCommand = cli.Command{
 | 
				
			||||||
 | 
							Name:      "eth-test",
 | 
				
			||||||
 | 
							Usage:     "Runs tests against a node",
 | 
				
			||||||
 | 
							ArgsUsage: "<node> <path_to_chain.rlp_file>",
 | 
				
			||||||
 | 
							Action:    rlpxEthTest,
 | 
				
			||||||
 | 
							Flags:     []cli.Flag{testPatternFlag},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func rlpxPing(ctx *cli.Context) error {
 | 
					func rlpxPing(ctx *cli.Context) error {
 | 
				
			||||||
	n := getNodeArg(ctx)
 | 
						n := getNodeArg(ctx)
 | 
				
			||||||
 | 
					 | 
				
			||||||
	fd, err := net.Dial("tcp", fmt.Sprintf("%v:%d", n.IP(), n.TCP()))
 | 
						fd, err := net.Dial("tcp", fmt.Sprintf("%v:%d", n.IP(), n.TCP()))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	conn := rlpx.NewConn(fd, n.Pubkey())
 | 
						conn := rlpx.NewConn(fd, n.Pubkey())
 | 
				
			||||||
 | 
					 | 
				
			||||||
	ourKey, _ := crypto.GenerateKey()
 | 
						ourKey, _ := crypto.GenerateKey()
 | 
				
			||||||
	_, err = conn.Handshake(ourKey)
 | 
						_, err = conn.Handshake(ourKey)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	code, data, _, err := conn.Read()
 | 
						code, data, _, err := conn.Read()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	switch code {
 | 
						switch code {
 | 
				
			||||||
	case 0:
 | 
						case 0:
 | 
				
			||||||
		var h devp2pHandshake
 | 
							var h ethtest.Hello
 | 
				
			||||||
		if err := rlp.DecodeBytes(data, &h); err != nil {
 | 
							if err := rlp.DecodeBytes(data, &h); err != nil {
 | 
				
			||||||
			return fmt.Errorf("invalid handshake: %v", err)
 | 
								return fmt.Errorf("invalid handshake: %v", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -82,13 +88,22 @@ func rlpxPing(ctx *cli.Context) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// devp2pHandshake is the RLP structure of the devp2p protocol handshake.
 | 
					func rlpxEthTest(ctx *cli.Context) error {
 | 
				
			||||||
type devp2pHandshake struct {
 | 
						if ctx.NArg() < 3 {
 | 
				
			||||||
	Version    uint64
 | 
							exit("missing path to chain.rlp as command-line argument")
 | 
				
			||||||
	Name       string
 | 
						}
 | 
				
			||||||
	Caps       []p2p.Cap
 | 
					
 | 
				
			||||||
	ListenPort uint64
 | 
						suite := ethtest.NewSuite(getNodeArg(ctx), ctx.Args()[1], ctx.Args()[2])
 | 
				
			||||||
	ID         hexutil.Bytes // secp256k1 public key
 | 
					
 | 
				
			||||||
	// Ignore additional fields (for forward compatibility).
 | 
						// Filter and run test cases.
 | 
				
			||||||
	Rest []rlp.RawValue `rlp:"tail"`
 | 
						tests := suite.AllTests()
 | 
				
			||||||
 | 
						if ctx.IsSet(testPatternFlag.Name) {
 | 
				
			||||||
 | 
							tests = utesting.MatchTests(tests, ctx.String(testPatternFlag.Name))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						results := utesting.RunTests(tests, os.Stdout)
 | 
				
			||||||
 | 
						if fails := utesting.CountFailures(results); fails > 0 {
 | 
				
			||||||
 | 
							return fmt.Errorf("%v of %v tests passed.", len(tests)-fails, len(tests))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fmt.Printf("all tests passed\n")
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -65,19 +65,8 @@ type ID struct {
 | 
				
			|||||||
// Filter is a fork id filter to validate a remotely advertised ID.
 | 
					// Filter is a fork id filter to validate a remotely advertised ID.
 | 
				
			||||||
type Filter func(id ID) error
 | 
					type Filter func(id ID) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewID calculates the Ethereum fork ID from the chain config and head.
 | 
					// NewID calculates the Ethereum fork ID from the chain config, genesis hash, and head.
 | 
				
			||||||
func NewID(chain Blockchain) ID {
 | 
					func NewID(config *params.ChainConfig, genesis common.Hash, head uint64) ID {
 | 
				
			||||||
	return newID(
 | 
					 | 
				
			||||||
		chain.Config(),
 | 
					 | 
				
			||||||
		chain.Genesis().Hash(),
 | 
					 | 
				
			||||||
		chain.CurrentHeader().Number.Uint64(),
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// newID is the internal version of NewID, which takes extracted values as its
 | 
					 | 
				
			||||||
// arguments instead of a chain. The reason is to allow testing the IDs without
 | 
					 | 
				
			||||||
// having to simulate an entire blockchain.
 | 
					 | 
				
			||||||
func newID(config *params.ChainConfig, genesis common.Hash, head uint64) ID {
 | 
					 | 
				
			||||||
	// Calculate the starting checksum from the genesis hash
 | 
						// Calculate the starting checksum from the genesis hash
 | 
				
			||||||
	hash := crc32.ChecksumIEEE(genesis[:])
 | 
						hash := crc32.ChecksumIEEE(genesis[:])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -118,7 +118,7 @@ func TestCreation(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	for i, tt := range tests {
 | 
						for i, tt := range tests {
 | 
				
			||||||
		for j, ttt := range tt.cases {
 | 
							for j, ttt := range tt.cases {
 | 
				
			||||||
			if have := newID(tt.config, tt.genesis, ttt.head); have != ttt.want {
 | 
								if have := NewID(tt.config, tt.genesis, ttt.head); have != ttt.want {
 | 
				
			||||||
				t.Errorf("test %d, case %d: fork ID mismatch: have %x, want %x", i, j, have, ttt.want)
 | 
									t.Errorf("test %d, case %d: fork ID mismatch: have %x, want %x", i, j, have, ttt.want)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -60,7 +60,8 @@ func (eth *Ethereum) startEthEntryUpdate(ln *enode.LocalNode) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (eth *Ethereum) currentEthEntry() *ethEntry {
 | 
					func (eth *Ethereum) currentEthEntry() *ethEntry {
 | 
				
			||||||
	return ðEntry{ForkID: forkid.NewID(eth.blockchain)}
 | 
						return ðEntry{ForkID: forkid.NewID(eth.blockchain.Config(), eth.blockchain.Genesis().Hash(),
 | 
				
			||||||
 | 
							eth.blockchain.CurrentHeader().Number.Uint64())}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// setupDiscovery creates the node discovery source for the eth protocol.
 | 
					// setupDiscovery creates the node discovery source for the eth protocol.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -319,7 +319,8 @@ func (pm *ProtocolManager) handle(p *peer) error {
 | 
				
			|||||||
		number  = head.Number.Uint64()
 | 
							number  = head.Number.Uint64()
 | 
				
			||||||
		td      = pm.blockchain.GetTd(hash, number)
 | 
							td      = pm.blockchain.GetTd(hash, number)
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	if err := p.Handshake(pm.networkID, td, hash, genesis.Hash(), forkid.NewID(pm.blockchain), pm.forkFilter); err != nil {
 | 
						forkID := forkid.NewID(pm.blockchain.Config(), pm.blockchain.Genesis().Hash(), pm.blockchain.CurrentHeader().Number.Uint64())
 | 
				
			||||||
 | 
						if err := p.Handshake(pm.networkID, td, hash, genesis.Hash(), forkID, pm.forkFilter); err != nil {
 | 
				
			||||||
		p.Log().Debug("Ethereum handshake failed", "err", err)
 | 
							p.Log().Debug("Ethereum handshake failed", "err", err)
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -185,7 +185,8 @@ func newTestPeer(name string, version int, pm *ProtocolManager, shake bool) (*te
 | 
				
			|||||||
			head    = pm.blockchain.CurrentHeader()
 | 
								head    = pm.blockchain.CurrentHeader()
 | 
				
			||||||
			td      = pm.blockchain.GetTd(head.Hash(), head.Number.Uint64())
 | 
								td      = pm.blockchain.GetTd(head.Hash(), head.Number.Uint64())
 | 
				
			||||||
		)
 | 
							)
 | 
				
			||||||
		tp.handshake(nil, td, head.Hash(), genesis.Hash(), forkid.NewID(pm.blockchain), forkid.NewFilter(pm.blockchain))
 | 
							forkID := forkid.NewID(pm.blockchain.Config(), pm.blockchain.Genesis().Hash(), pm.blockchain.CurrentHeader().Number.Uint64())
 | 
				
			||||||
 | 
							tp.handshake(nil, td, head.Hash(), genesis.Hash(), forkID, forkid.NewFilter(pm.blockchain))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return tp, errc
 | 
						return tp, errc
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -104,7 +104,7 @@ func TestStatusMsgErrors64(t *testing.T) {
 | 
				
			|||||||
		genesis = pm.blockchain.Genesis()
 | 
							genesis = pm.blockchain.Genesis()
 | 
				
			||||||
		head    = pm.blockchain.CurrentHeader()
 | 
							head    = pm.blockchain.CurrentHeader()
 | 
				
			||||||
		td      = pm.blockchain.GetTd(head.Hash(), head.Number.Uint64())
 | 
							td      = pm.blockchain.GetTd(head.Hash(), head.Number.Uint64())
 | 
				
			||||||
		forkID  = forkid.NewID(pm.blockchain)
 | 
							forkID  = forkid.NewID(pm.blockchain.Config(), pm.blockchain.Genesis().Hash(), pm.blockchain.CurrentHeader().Number.Uint64())
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	defer pm.Stop()
 | 
						defer pm.Stop()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user