From 0f20c9fe0744121a4eb0cd411c4bbd89ad987c0d Mon Sep 17 00:00:00 2001 From: renaynay <41963722+renaynay@users.noreply.github.com> Date: Tue, 22 Sep 2020 18:16:55 +0200 Subject: [PATCH 1/8] all changes, not modified to master yet --- cmd/devp2p/internal/ethtest/chain.go | 113 ++++++++++ cmd/devp2p/internal/ethtest/suite.go | 304 +++++++++++++++++++++++++++ cmd/devp2p/internal/ethtest/types.go | 178 ++++++++++++++++ cmd/devp2p/main.go | 2 +- cmd/devp2p/rlpxcmd.go | 59 ++++-- core/forkid/forkid.go | 6 +- core/forkid/forkid_test.go | 2 +- 7 files changed, 638 insertions(+), 26 deletions(-) create mode 100644 cmd/devp2p/internal/ethtest/chain.go create mode 100644 cmd/devp2p/internal/ethtest/suite.go create mode 100644 cmd/devp2p/internal/ethtest/types.go diff --git a/cmd/devp2p/internal/ethtest/chain.go b/cmd/devp2p/internal/ethtest/chain.go new file mode 100644 index 000000000000..6c20cbdf4d27 --- /dev/null +++ b/cmd/devp2p/internal/ethtest/chain.go @@ -0,0 +1,113 @@ +package ethtest + +import ( + "compress/gzip" + "encoding/json" + "fmt" + "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" + "io" + "io/ioutil" + "math/big" + "os" + "strings" +) + +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.NewStaticID(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 +} diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go new file mode 100644 index 000000000000..f0e3713bc369 --- /dev/null +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -0,0 +1,304 @@ +package ethtest + +import ( + "crypto/ecdsa" + "fmt" + "github.com/ethereum/go-ethereum/core/types" + "time" + + "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/stretchr/testify/assert" + "net" + "reflect" +) + +// 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 +} + +// 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{{"eth", 64}, {"eth", 65}}, + ID: pub0, + } + if err := Write(c.Conn, ourHandshake); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + // read protoHandshake from client + switch msg := Read(c.Conn).(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 := Read(c.Conn).(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 := Write(c.Conn, 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 := Write(c.Conn, req); err != nil { + return err + } + + switch msg := Read(c.Conn).(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{ + {"Ping", s.TestPing}, + {"Status", s.TestStatus}, + {"GetBlockHeaders", s.TestGetBlockHeaders}, + {"Broadcast", s.TestBroadcast}, + {"GetBlockBodies", s.TestGetBlockBodies}, + } +} + +// TestPing attempts to exchange `HELLO` messages +// with the given node. +func (s *Suite) TestPing(t *utesting.T) { + conn, err := s.dial() + if err != nil { + t.Fatalf("could not dial: %v", err) + } + // get protoHandshake + msg := conn.handshake(t) + t.Logf("%+v\n", msg) +} + +// 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 + msg := conn.statusExchange(t, s.chain) // todo make this a switch + t.Logf("%+v\n", 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 // TODO eventually make this customizable with CL args (take from a file)? + req := &GetBlockHeaders{ + Origin: hashOrNumber{ + Hash: s.chain.blocks[1].Hash(), + }, + Amount: 2, + Skip: 1, + Reverse: false, + } + + if err := Write(conn.Conn, req); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + + msg := Read(conn.Conn) + switch msg.Code() { + case 20: + headers, ok := msg.(*BlockHeaders) + if !ok { + t.Fatalf("message %v does not match code %d", msg, msg.Code()) + } + for _, header := range *headers { + t.Logf("\nHEADER FOR BLOCK NUMBER %d: %+v\n", header.Number, header) // TODO eventually check against our own data + } + default: + t.Fatalf("error reading message: %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 := Write(conn.Conn, req); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + + msg := Read(conn.Conn) + switch msg.Code() { + case 22: + bodies, ok := msg.(*BlockBodies) + if !ok { + t.Fatalf("message %v does not match code %d", msg, msg.Code()) // TODO eventually check against our own data + } + for _, body := range *bodies { + t.Logf("\nBODY: %+v\n", body) + } + default: + t.Fatalf("error reading message: %v", msg) + } +} + +// TestBroadcast // TODO how to make sure this is compatible with the imported blockchain of the node? +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 := Write(sendConn.Conn, blockAnnouncement); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + + switch msg := Read(receiveConn.Conn).(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 +} diff --git a/cmd/devp2p/internal/ethtest/types.go b/cmd/devp2p/internal/ethtest/types.go new file mode 100644 index 000000000000..e0cd57038392 --- /dev/null +++ b/cmd/devp2p/internal/ethtest/types.go @@ -0,0 +1,178 @@ +package ethtest + +import ( +"fmt" +"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/p2p/rlpx" +"github.com/ethereum/go-ethereum/rlp" +"io" +"math/big" +) + +type Message interface { + Code() int + //Protocol() int // TODO +} + +func Read(conn *rlpx.Conn) Message { + code, rawData, err := 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 (Disc{}).Code(): + msg = new(Disc) + 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 Write(conn *rlpx.Conn, msg Message) error { // TODO eventually put this method on the Conn + size, payload, err := rlp.EncodeToReader(msg) + if err != nil { + return err + } + _, err = conn.WriteMsg(uint64(msg.Code()), uint32(size), payload) + return err +} + +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 } + +type Disc struct { + Reason p2p.DiscReason +} + +func (d Disc) 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 } + +//func (sd *statusData) Protocol() int { +// +//} + +// 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 } diff --git a/cmd/devp2p/main.go b/cmd/devp2p/main.go index 9eebd9b1375b..4a4e905a424e 100644 --- a/cmd/devp2p/main.go +++ b/cmd/devp2p/main.go @@ -81,7 +81,7 @@ func commandHasFlag(ctx *cli.Context, flag cli.Flag) bool { // getNodeArg handles the common case of a single node descriptor argument. func getNodeArg(ctx *cli.Context) *enode.Node { - if ctx.NArg() != 1 { + if ctx.NArg() < 1 { exit("missing node as command-line argument") } n, err := parseNode(ctx.Args()[0]) diff --git a/cmd/devp2p/rlpxcmd.go b/cmd/devp2p/rlpxcmd.go index 14eb5989d1e3..4eb45faff5dc 100644 --- a/cmd/devp2p/rlpxcmd.go +++ b/cmd/devp2p/rlpxcmd.go @@ -18,54 +18,62 @@ package main import ( "fmt" - "net" - - "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/internal/utesting" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/rlpx" "github.com/ethereum/go-ethereum/rlp" "gopkg.in/urfave/cli.v1" + "net" + "os" ) +// TODO TO TEST: +// TODO: - GETBLOCKHEADERS, GETBLOCKBODIES, 2 CONN BLOCK PROPAGATION TEST + var ( rlpxCommand = cli.Command{ Name: "rlpx", Usage: "RLPx Commands", Subcommands: []cli.Command{ rlpxPingCommand, + rlpxEthTestCommand, }, } rlpxPingCommand = cli.Command{ - Name: "ping", - Usage: "Perform a RLPx handshake", - ArgsUsage: "", - Action: rlpxPing, + Name: "ping", + Usage: "ping ", + Action: rlpxPing, + } + rlpxEthTestCommand = cli.Command{ + Name: "eth-test", + Usage: "Runs tests against a node", + ArgsUsage: " ", // TODO maybe better? + Action: rlpxEthTest, + Flags: []cli.Flag{testPatternFlag}, } ) func rlpxPing(ctx *cli.Context) error { n := getNodeArg(ctx) - fd, err := net.Dial("tcp", fmt.Sprintf("%v:%d", n.IP(), n.TCP())) if err != nil { return err } conn := rlpx.NewConn(fd, n.Pubkey()) - ourKey, _ := crypto.GenerateKey() _, err = conn.Handshake(ourKey) if err != nil { return err } - - code, data, _, err := conn.Read() + code, data, err := conn.Read() if err != nil { return err } switch code { case 0: - var h devp2pHandshake + var h ethtest.Hello if err := rlp.DecodeBytes(data, &h); err != nil { return fmt.Errorf("invalid handshake: %v", err) } @@ -82,13 +90,22 @@ func rlpxPing(ctx *cli.Context) error { return nil } -// devp2pHandshake is the RLP structure of the devp2p protocol handshake. -type devp2pHandshake struct { - Version uint64 - Name string - Caps []p2p.Cap - ListenPort uint64 - ID hexutil.Bytes // secp256k1 public key - // Ignore additional fields (for forward compatibility). - Rest []rlp.RawValue `rlp:"tail"` +func rlpxEthTest(ctx *cli.Context) error { + if ctx.NArg() < 3 { + exit("missing path to chain.rlp as command-line argument") + } + + suite := ethtest.NewSuite(getNodeArg(ctx), ctx.Args()[1], ctx.Args()[2]) + + // Filter and run test cases. + 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 } diff --git a/core/forkid/forkid.go b/core/forkid/forkid.go index b8d670f3997b..60c4b6274d85 100644 --- a/core/forkid/forkid.go +++ b/core/forkid/forkid.go @@ -67,17 +67,17 @@ type Filter func(id ID) error // NewID calculates the Ethereum fork ID from the chain config and head. func NewID(chain Blockchain) ID { - return newID( + return NewStaticID( chain.Config(), chain.Genesis().Hash(), chain.CurrentHeader().Number.Uint64(), ) } -// newID is the internal version of NewID, which takes extracted values as its +// NewStaticID 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 { +func NewStaticID(config *params.ChainConfig, genesis common.Hash, head uint64) ID { // Calculate the starting checksum from the genesis hash hash := crc32.ChecksumIEEE(genesis[:]) diff --git a/core/forkid/forkid_test.go b/core/forkid/forkid_test.go index da5653021429..b7b216e03d6b 100644 --- a/core/forkid/forkid_test.go +++ b/core/forkid/forkid_test.go @@ -118,7 +118,7 @@ func TestCreation(t *testing.T) { } for i, tt := range tests { for j, ttt := range tt.cases { - if have := newID(tt.config, tt.genesis, ttt.head); have != ttt.want { + if have := NewStaticID(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) } } From 9a7b7b2b6adc19403492423a61adf409dd0ba498 Mon Sep 17 00:00:00 2001 From: renaynay <41963722+renaynay@users.noreply.github.com> Date: Tue, 22 Sep 2020 18:57:58 +0200 Subject: [PATCH 2/8] fixed --- cmd/devp2p/internal/ethtest/suite.go | 72 +++++++++++++++++++++++----- cmd/devp2p/internal/ethtest/types.go | 64 ++++--------------------- cmd/devp2p/rlpxcmd.go | 7 +-- 3 files changed, 70 insertions(+), 73 deletions(-) diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index f0e3713bc369..deffa5df3116 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -4,6 +4,7 @@ import ( "crypto/ecdsa" "fmt" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" "time" "github.com/ethereum/go-ethereum/crypto" @@ -30,6 +31,53 @@ type Conn struct { 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 (Disc{}).Code(): + msg = new(Disc) + 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 @@ -39,11 +87,11 @@ func (c *Conn) handshake(t *utesting.T) Message { Caps: []p2p.Cap{{"eth", 64}, {"eth", 65}}, ID: pub0, } - if err := Write(c.Conn, ourHandshake); err != nil { + if err := c.Write(ourHandshake); err != nil { t.Fatalf("could not write to connection: %v", err) } // read protoHandshake from client - switch msg := Read(c.Conn).(type) { + switch msg := c.Read().(type) { case *Hello: return msg default: @@ -57,7 +105,7 @@ func (c *Conn) handshake(t *utesting.T) Message { func (c *Conn) statusExchange(t *utesting.T, chain *Chain) Message { // read status message from client var message Message - switch msg := Read(c.Conn).(type) { + 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) @@ -81,7 +129,7 @@ func (c *Conn) statusExchange(t *utesting.T, chain *Chain) Message { Genesis: chain.blocks[0].Hash(), ForkID: chain.ForkID(), } - if err := Write(c.Conn, status) + if err := c.Write(status) err != nil { t.Fatalf("could not write to connection: %v", err) } @@ -94,11 +142,11 @@ func (c *Conn) statusExchange(t *utesting.T, chain *Chain) Message { func (c *Conn) waitForBlock(block *types.Block) error { for { req := &GetBlockHeaders{Origin: hashOrNumber{Hash: block.Hash()}, Amount: 1} - if err := Write(c.Conn, req); err != nil { + if err := c.Write(req); err != nil { return err } - switch msg := Read(c.Conn).(type) { + switch msg := c.Read().(type) { case *BlockHeaders: if len(*msg) > 0 { return nil @@ -183,11 +231,11 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) { Reverse: false, } - if err := Write(conn.Conn, req); err != nil { + if err := conn.Write(req); err != nil { t.Fatalf("could not write to connection: %v", err) } - msg := Read(conn.Conn) + msg := conn.Read() switch msg.Code() { case 20: headers, ok := msg.(*BlockHeaders) @@ -214,11 +262,11 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) { conn.statusExchange(t, s.chain) // create block bodies request req := &GetBlockBodies{s.chain.blocks[54].Hash(), s.chain.blocks[75].Hash()} - if err := Write(conn.Conn, req); err != nil { + if err := conn.Write(req); err != nil { t.Fatalf("could not write to connection: %v", err) } - msg := Read(conn.Conn) + msg := conn.Read() switch msg.Code() { case 22: bodies, ok := msg.(*BlockBodies) @@ -257,11 +305,11 @@ func (s *Suite) TestBroadcast(t *utesting.T) { Block: s.fullChain.blocks[1000], TD: s.fullChain.TD(1001), } - if err := Write(sendConn.Conn, blockAnnouncement); err != nil { + if err := sendConn.Write(blockAnnouncement); err != nil { t.Fatalf("could not write to connection: %v", err) } - switch msg := Read(receiveConn.Conn).(type) { + switch msg := receiveConn.Read().(type) { case *NewBlock: assert.Equal(t, blockAnnouncement.Block.Header(), msg.Block.Header(), "wrong block header in announcement") diff --git a/cmd/devp2p/internal/ethtest/types.go b/cmd/devp2p/internal/ethtest/types.go index e0cd57038392..edef627139d0 100644 --- a/cmd/devp2p/internal/ethtest/types.go +++ b/cmd/devp2p/internal/ethtest/types.go @@ -1,66 +1,18 @@ package ethtest import ( -"fmt" -"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/p2p/rlpx" -"github.com/ethereum/go-ethereum/rlp" -"io" -"math/big" + "fmt" + "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" + "io" + "math/big" ) type Message interface { Code() int - //Protocol() int // TODO -} - -func Read(conn *rlpx.Conn) Message { - code, rawData, err := 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 (Disc{}).Code(): - msg = new(Disc) - 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 Write(conn *rlpx.Conn, msg Message) error { // TODO eventually put this method on the Conn - size, payload, err := rlp.EncodeToReader(msg) - if err != nil { - return err - } - _, err = conn.WriteMsg(uint64(msg.Code()), uint32(size), payload) - return err } type Error struct { diff --git a/cmd/devp2p/rlpxcmd.go b/cmd/devp2p/rlpxcmd.go index 4eb45faff5dc..6fd8a02e539d 100644 --- a/cmd/devp2p/rlpxcmd.go +++ b/cmd/devp2p/rlpxcmd.go @@ -29,9 +29,6 @@ import ( "os" ) -// TODO TO TEST: -// TODO: - GETBLOCKHEADERS, GETBLOCKBODIES, 2 CONN BLOCK PROPAGATION TEST - var ( rlpxCommand = cli.Command{ Name: "rlpx", @@ -49,7 +46,7 @@ var ( rlpxEthTestCommand = cli.Command{ Name: "eth-test", Usage: "Runs tests against a node", - ArgsUsage: " ", // TODO maybe better? + ArgsUsage: " ", Action: rlpxEthTest, Flags: []cli.Flag{testPatternFlag}, } @@ -67,7 +64,7 @@ func rlpxPing(ctx *cli.Context) error { if err != nil { return err } - code, data, err := conn.Read() + code, data, _, err := conn.Read() if err != nil { return err } From 0c87eb7b3ec2af70298b4a4915cfc775435252dd Mon Sep 17 00:00:00 2001 From: renaynay <41963722+renaynay@users.noreply.github.com> Date: Tue, 22 Sep 2020 19:39:50 +0200 Subject: [PATCH 3/8] fixed some todos --- cmd/devp2p/internal/ethtest/suite.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index deffa5df3116..399299592e17 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -206,8 +206,12 @@ func (s *Suite) TestStatus(t *utesting.T) { // get protoHandshake conn.handshake(t) // get status - msg := conn.statusExchange(t, s.chain) // todo make this a switch - t.Logf("%+v\n", msg) + 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 @@ -221,7 +225,7 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) { conn.handshake(t) conn.statusExchange(t, s.chain) - // get block headers // TODO eventually make this customizable with CL args (take from a file)? + // get block headers req := &GetBlockHeaders{ Origin: hashOrNumber{ Hash: s.chain.blocks[1].Hash(), @@ -246,7 +250,7 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) { t.Logf("\nHEADER FOR BLOCK NUMBER %d: %+v\n", header.Number, header) // TODO eventually check against our own data } default: - t.Fatalf("error reading message: %v", msg) + t.Fatalf("error: %v", msg) } } @@ -277,11 +281,12 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) { t.Logf("\nBODY: %+v\n", body) } default: - t.Fatalf("error reading message: %v", msg) + t.Fatalf("error: %v", msg) } } -// TestBroadcast // TODO how to make sure this is compatible with the imported blockchain of the node? +// 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() From 2c44ed325d3a0448245ec18e4db1ac9aa425ddc7 Mon Sep 17 00:00:00 2001 From: renaynay <41963722+renaynay@users.noreply.github.com> Date: Tue, 22 Sep 2020 19:51:07 +0200 Subject: [PATCH 4/8] cleaned up some code and added check for headers --- cmd/devp2p/internal/ethtest/suite.go | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index 399299592e17..fb938cbafcb3 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -239,15 +239,13 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) { t.Fatalf("could not write to connection: %v", err) } - msg := conn.Read() - switch msg.Code() { - case 20: - headers, ok := msg.(*BlockHeaders) - if !ok { - t.Fatalf("message %v does not match code %d", msg, msg.Code()) - } + switch msg := conn.Read().(type) { + case *BlockHeaders: + headers := msg for _, header := range *headers { - t.Logf("\nHEADER FOR BLOCK NUMBER %d: %+v\n", header.Number, header) // TODO eventually check against our own data + 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) @@ -270,13 +268,9 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) { t.Fatalf("could not write to connection: %v", err) } - msg := conn.Read() - switch msg.Code() { - case 22: - bodies, ok := msg.(*BlockBodies) - if !ok { - t.Fatalf("message %v does not match code %d", msg, msg.Code()) // TODO eventually check against our own data - } + switch msg := conn.Read().(type) { + case *BlockBodies: + bodies := msg for _, body := range *bodies { t.Logf("\nBODY: %+v\n", body) } From 885d05389b5a1d07b0e93b06b5a5c21a4abb924b Mon Sep 17 00:00:00 2001 From: renaynay <41963722+renaynay@users.noreply.github.com> Date: Tue, 22 Sep 2020 19:55:53 +0200 Subject: [PATCH 5/8] lint --- cmd/devp2p/internal/ethtest/chain.go | 14 +++++++------- cmd/devp2p/internal/ethtest/suite.go | 25 ++++++++++++------------- cmd/devp2p/internal/ethtest/types.go | 11 +++++++++-- cmd/devp2p/rlpxcmd.go | 17 +++++++++-------- 4 files changed, 37 insertions(+), 30 deletions(-) diff --git a/cmd/devp2p/internal/ethtest/chain.go b/cmd/devp2p/internal/ethtest/chain.go index 6c20cbdf4d27..6fc09ebac095 100644 --- a/cmd/devp2p/internal/ethtest/chain.go +++ b/cmd/devp2p/internal/ethtest/chain.go @@ -4,16 +4,17 @@ import ( "compress/gzip" "encoding/json" "fmt" - "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" "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 { @@ -28,7 +29,6 @@ func (c *Chain) WriteTo(writer io.Writer) error { } } - return nil } @@ -58,7 +58,7 @@ func (c *Chain) Shorten(height int) *Chain { config := *c.chainConfig return &Chain{ - blocks: blocks, + blocks: blocks, chainConfig: &config, } } diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index fb938cbafcb3..64139903f046 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -3,18 +3,18 @@ package ethtest import ( "crypto/ecdsa" "fmt" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/rlp" + "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" - "net" - "reflect" ) // Suite represents a structure used to test the eth @@ -84,7 +84,7 @@ func (c *Conn) handshake(t *utesting.T) Message { pub0 := crypto.FromECDSAPub(&c.ourKey.PublicKey)[1:] ourHandshake := &Hello{ Version: 3, - Caps: []p2p.Cap{{"eth", 64}, {"eth", 65}}, + Caps: []p2p.Cap{{Name: "eth", Version: 64}, {Name: "eth", Version: 65}}, ID: pub0, } if err := c.Write(ourHandshake); err != nil { @@ -129,8 +129,7 @@ func (c *Conn) statusExchange(t *utesting.T, chain *Chain) Message { Genesis: chain.blocks[0].Hash(), ForkID: chain.ForkID(), } - if err := c.Write(status) - err != nil { + if err := c.Write(status); err != nil { t.Fatalf("could not write to connection: %v", err) } @@ -151,7 +150,7 @@ func (c *Conn) waitForBlock(block *types.Block) error { if len(*msg) > 0 { return nil } - time.Sleep(100*time.Millisecond) + time.Sleep(100 * time.Millisecond) default: return fmt.Errorf("invalid message: %v", msg) } @@ -175,11 +174,11 @@ func NewSuite(dest *enode.Node, chainfile string, genesisfile string) *Suite { func (s *Suite) AllTests() []utesting.Test { return []utesting.Test{ - {"Ping", s.TestPing}, - {"Status", s.TestStatus}, - {"GetBlockHeaders", s.TestGetBlockHeaders}, - {"Broadcast", s.TestBroadcast}, - {"GetBlockBodies", s.TestGetBlockBodies}, + {Name: "Ping", Fn: s.TestPing}, + {Name: "Status", Fn: s.TestStatus}, + {Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders}, + {Name: "Broadcast", Fn: s.TestBroadcast}, + {Name: "GetBlockBodies", Fn: s.TestGetBlockBodies}, } } diff --git a/cmd/devp2p/internal/ethtest/types.go b/cmd/devp2p/internal/ethtest/types.go index edef627139d0..c240a17cf90e 100644 --- a/cmd/devp2p/internal/ethtest/types.go +++ b/cmd/devp2p/internal/ethtest/types.go @@ -2,13 +2,14 @@ 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" - "io" - "math/big" ) type Message interface { @@ -64,6 +65,7 @@ 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. @@ -71,6 +73,7 @@ type NewBlock struct { Block *types.Block TD *big.Int } + func (nb NewBlock) Code() int { return 23 } // GetBlockHeaders represents a block header query. @@ -80,9 +83,11 @@ type GetBlockHeaders struct { 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. @@ -123,8 +128,10 @@ func (hn *hashOrNumber) DecodeRLP(s *rlp.Stream) error { // 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 } diff --git a/cmd/devp2p/rlpxcmd.go b/cmd/devp2p/rlpxcmd.go index 6fd8a02e539d..17019aee0044 100644 --- a/cmd/devp2p/rlpxcmd.go +++ b/cmd/devp2p/rlpxcmd.go @@ -18,6 +18,9 @@ package main import ( "fmt" + "net" + "os" + "github.com/ethereum/go-ethereum/cmd/devp2p/internal/ethtest" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/internal/utesting" @@ -25,8 +28,6 @@ import ( "github.com/ethereum/go-ethereum/p2p/rlpx" "github.com/ethereum/go-ethereum/rlp" "gopkg.in/urfave/cli.v1" - "net" - "os" ) var ( @@ -39,16 +40,16 @@ var ( }, } rlpxPingCommand = cli.Command{ - Name: "ping", - Usage: "ping ", + Name: "ping", + Usage: "ping ", Action: rlpxPing, } rlpxEthTestCommand = cli.Command{ - Name: "eth-test", - Usage: "Runs tests against a node", + Name: "eth-test", + Usage: "Runs tests against a node", ArgsUsage: " ", - Action: rlpxEthTest, - Flags: []cli.Flag{testPatternFlag}, + Action: rlpxEthTest, + Flags: []cli.Flag{testPatternFlag}, } ) From db4c0f95cec321917e778aa2c14c5a6e475bba72 Mon Sep 17 00:00:00 2001 From: renaynay <41963722+renaynay@users.noreply.github.com> Date: Wed, 23 Sep 2020 11:14:16 +0200 Subject: [PATCH 6/8] NewStaticID -> NewID --- cmd/devp2p/internal/ethtest/chain.go | 2 +- core/forkid/forkid.go | 15 ++------------- core/forkid/forkid_test.go | 2 +- eth/discovery.go | 7 ++++++- eth/handler.go | 3 ++- eth/helper_test.go | 3 ++- eth/protocol_test.go | 2 +- 7 files changed, 15 insertions(+), 19 deletions(-) diff --git a/cmd/devp2p/internal/ethtest/chain.go b/cmd/devp2p/internal/ethtest/chain.go index 6fc09ebac095..250be64fe68a 100644 --- a/cmd/devp2p/internal/ethtest/chain.go +++ b/cmd/devp2p/internal/ethtest/chain.go @@ -48,7 +48,7 @@ func (c *Chain) TD(height int) *big.Int { // TODO later on channge scheme so tha // ForkID gets the fork id of the chain. func (c *Chain) ForkID() forkid.ID { - return forkid.NewStaticID(c.chainConfig, c.blocks[0].Hash(), uint64(c.Len())) + return forkid.NewID(c.chainConfig, c.blocks[0].Hash(), uint64(c.Len())) } // Shorten returns a copy chain of a desired height from the imported diff --git a/core/forkid/forkid.go b/core/forkid/forkid.go index 60c4b6274d85..c432858617e2 100644 --- a/core/forkid/forkid.go +++ b/core/forkid/forkid.go @@ -65,19 +65,8 @@ type ID struct { // Filter is a fork id filter to validate a remotely advertised ID. type Filter func(id ID) error -// NewID calculates the Ethereum fork ID from the chain config and head. -func NewID(chain Blockchain) ID { - return NewStaticID( - chain.Config(), - chain.Genesis().Hash(), - chain.CurrentHeader().Number.Uint64(), - ) -} - -// NewStaticID 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 NewStaticID(config *params.ChainConfig, genesis common.Hash, head uint64) ID { +// NewID calculates the Ethereum fork ID from the chain config, genesis hash, and head. +func NewID(config *params.ChainConfig, genesis common.Hash, head uint64) ID { // Calculate the starting checksum from the genesis hash hash := crc32.ChecksumIEEE(genesis[:]) diff --git a/core/forkid/forkid_test.go b/core/forkid/forkid_test.go index b7b216e03d6b..888b55347524 100644 --- a/core/forkid/forkid_test.go +++ b/core/forkid/forkid_test.go @@ -118,7 +118,7 @@ func TestCreation(t *testing.T) { } for i, tt := range tests { for j, ttt := range tt.cases { - if have := NewStaticID(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) } } diff --git a/eth/discovery.go b/eth/discovery.go index 97d6322ca16d..97e0328a4bc3 100644 --- a/eth/discovery.go +++ b/eth/discovery.go @@ -60,7 +60,12 @@ func (eth *Ethereum) startEthEntryUpdate(ln *enode.LocalNode) { } 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. diff --git a/eth/handler.go b/eth/handler.go index 7482a2f96ef2..f5ec2c4fbf55 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -319,7 +319,8 @@ func (pm *ProtocolManager) handle(p *peer) error { number = head.Number.Uint64() 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) return err } diff --git a/eth/helper_test.go b/eth/helper_test.go index 65effcc16537..c0bda181ea39 100644 --- a/eth/helper_test.go +++ b/eth/helper_test.go @@ -185,7 +185,8 @@ func newTestPeer(name string, version int, pm *ProtocolManager, shake bool) (*te head = pm.blockchain.CurrentHeader() 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 } diff --git a/eth/protocol_test.go b/eth/protocol_test.go index fc916a2263c9..331dd05ce108 100644 --- a/eth/protocol_test.go +++ b/eth/protocol_test.go @@ -104,7 +104,7 @@ func TestStatusMsgErrors64(t *testing.T) { genesis = pm.blockchain.Genesis() head = pm.blockchain.CurrentHeader() 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() From 822c5a4c9736abfce5b38473fafdcfa07dc273c4 Mon Sep 17 00:00:00 2001 From: renaynay <41963722+renaynay@users.noreply.github.com> Date: Wed, 23 Sep 2020 11:17:29 +0200 Subject: [PATCH 7/8] disc -> disconnect + cleanup --- cmd/devp2p/internal/ethtest/suite.go | 4 ++-- cmd/devp2p/internal/ethtest/types.go | 9 +++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index 64139903f046..5a4ff36b07b2 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -41,8 +41,8 @@ func (c *Conn) Read() Message { switch int(code) { case (Hello{}).Code(): msg = new(Hello) - case (Disc{}).Code(): - msg = new(Disc) + case (Disconnect{}).Code(): + msg = new(Disconnect) case (Status{}).Code(): msg = new(Status) case (GetBlockHeaders{}).Code(): diff --git a/cmd/devp2p/internal/ethtest/types.go b/cmd/devp2p/internal/ethtest/types.go index c240a17cf90e..ef2c52ddfdcd 100644 --- a/cmd/devp2p/internal/ethtest/types.go +++ b/cmd/devp2p/internal/ethtest/types.go @@ -38,11 +38,12 @@ type Hello struct { func (h Hello) Code() int { return 0x00 } -type Disc struct { +// Disconnect is the RLP structure for a disconnect message. +type Disconnect struct { Reason p2p.DiscReason } -func (d Disc) Code() int { return 0x01 } +func (d Disconnect) Code() int { return 0x01 } // Status is the network packet for the status message for eth/64 and later. type Status struct { @@ -56,10 +57,6 @@ type Status struct { func (s Status) Code() int { return 16 } -//func (sd *statusData) Protocol() int { -// -//} - // NewBlockHashes is the network packet for the block announcements. type NewBlockHashes []struct { Hash common.Hash // Hash of one particular block being announced From a46bddc3b295a178733617d72e08af2dc7c01e43 Mon Sep 17 00:00:00 2001 From: renaynay <41963722+renaynay@users.noreply.github.com> Date: Wed, 23 Sep 2020 11:21:21 +0200 Subject: [PATCH 8/8] lint + remove redundant ping test --- cmd/devp2p/internal/ethtest/suite.go | 13 ------------- eth/discovery.go | 8 ++------ 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index 5a4ff36b07b2..8a9438bebd72 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -174,7 +174,6 @@ func NewSuite(dest *enode.Node, chainfile string, genesisfile string) *Suite { func (s *Suite) AllTests() []utesting.Test { return []utesting.Test{ - {Name: "Ping", Fn: s.TestPing}, {Name: "Status", Fn: s.TestStatus}, {Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders}, {Name: "Broadcast", Fn: s.TestBroadcast}, @@ -182,18 +181,6 @@ func (s *Suite) AllTests() []utesting.Test { } } -// TestPing attempts to exchange `HELLO` messages -// with the given node. -func (s *Suite) TestPing(t *utesting.T) { - conn, err := s.dial() - if err != nil { - t.Fatalf("could not dial: %v", err) - } - // get protoHandshake - msg := conn.handshake(t) - t.Logf("%+v\n", msg) -} - // 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. diff --git a/eth/discovery.go b/eth/discovery.go index 97e0328a4bc3..48f6159017cc 100644 --- a/eth/discovery.go +++ b/eth/discovery.go @@ -60,12 +60,8 @@ func (eth *Ethereum) startEthEntryUpdate(ln *enode.LocalNode) { } func (eth *Ethereum) currentEthEntry() *ethEntry { - return ðEntry{ForkID: - forkid.NewID( - eth.blockchain.Config(), - eth.blockchain.Genesis().Hash(), - eth.blockchain.CurrentHeader().Number.Uint64()), - } + 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.