From 27624c9edaac64566fd45491d462e6ee788a1ec5 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 2 Nov 2020 11:45:16 +0100 Subject: [PATCH 1/9] cmd/devp2p/internal/ethtest: added large announcement tests --- cmd/devp2p/internal/ethtest/large.go | 80 +++++++++++++++++++++++++++ cmd/devp2p/internal/ethtest/suite.go | 82 ++++++++++++++++++++-------- 2 files changed, 139 insertions(+), 23 deletions(-) create mode 100644 cmd/devp2p/internal/ethtest/large.go diff --git a/cmd/devp2p/internal/ethtest/large.go b/cmd/devp2p/internal/ethtest/large.go new file mode 100644 index 000000000000..c88fe15e8f33 --- /dev/null +++ b/cmd/devp2p/internal/ethtest/large.go @@ -0,0 +1,80 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethtest + +import ( + "crypto/rand" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" +) + +// largeNumber returns a very large big.Int. +func largeNumber(megabytes int) *big.Int { + buf := make([]byte, megabytes*1024*1024) + rand.Read(buf) + bigint := new(big.Int) + bigint.SetBytes(buf) + return bigint +} + +// largeBuffer returns a very large buffer. +func largeBuffer(megabytes int) []byte { + buf := make([]byte, megabytes*1024*1024) + rand.Read(buf) + return buf +} + +// largeString returns a very large string. +func largeString(megabytes int) string { + buf := make([]byte, megabytes*1024*1024) + rand.Read(buf) + return string(hexutil.Encode(buf)) +} + +func largeBlock() *types.Block { + return types.NewBlockWithHeader(largeHeader()) +} + +// Returns a random hash +func randHash() common.Hash { + var h common.Hash + rand.Read(h[:]) + return h +} + +func largeHeader() *types.Header { + return &types.Header{ + MixDigest: randHash(), + ReceiptHash: randHash(), + TxHash: randHash(), + Nonce: types.BlockNonce{}, + Extra: []byte{}, + Bloom: types.Bloom{}, + GasUsed: 0, + Coinbase: common.Address{}, + GasLimit: 0, + UncleHash: randHash(), + Time: 1337, + ParentHash: randHash(), + Root: randHash(), + Number: largeNumber(2), + Difficulty: largeNumber(2), + } +} diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index d5928bede44f..672804a60556 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -155,32 +155,54 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) { // 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) + sendConn, receiveConn := s.setupConnection(t) + blockAnnouncement := &NewBlock{ + Block: s.fullChain.blocks[1000], + TD: s.fullChain.TD(1001), } - // create conn to receive block announcement - receiveConn, err := s.dial() - if err != nil { - t.Fatalf("could not dial: %v", err) + s.testAnnounce(t, sendConn, receiveConn, blockAnnouncement) + // 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) } +} - sendConn.handshake(t) - receiveConn.handshake(t) - - sendConn.statusExchange(t, s.chain) - receiveConn.statusExchange(t, s.chain) +// TestLargeAnnounce tests the announcement mechanism with a large block. +func (s *Suite) TestLargeAnnounce(t *utesting.T) { + sendConn, receiveConn := s.setupConnection(t) - // sendConn sends the block announcement - blockAnnouncement := &NewBlock{ - Block: s.fullChain.blocks[1000], - TD: s.fullChain.TD(1001), + blocks := []*NewBlock{ + { + Block: largeBlock(), + TD: s.fullChain.TD(1001), + }, + { + Block: s.fullChain.blocks[1001], + TD: largeNumber(2), + }, + { + Block: largeBlock(), + TD: largeNumber(2), + }, + } + for _, blockAnnouncement := range blocks { + // sendConn sends the block announcement + s.testAnnounce(t, sendConn, receiveConn, blockAnnouncement) } + // wait for client to update its chain + if err := receiveConn.waitForBlock(s.chain.Head()); err != nil { + t.Fatal(err) + } +} + +func (s *Suite) testAnnounce(t *utesting.T, sendConn, receiveConn *Conn, blockAnnouncement *NewBlock) { + // Announce the block. if err := sendConn.Write(blockAnnouncement); err != nil { t.Fatalf("could not write to connection: %v", err) } - + // Wait for the announcement. timeout := 20 * time.Second switch msg := receiveConn.ReadAndServe(s.chain, timeout).(type) { case *NewBlock: @@ -203,12 +225,26 @@ func (s *Suite) TestBroadcast(t *utesting.T) { default: t.Fatalf("unexpected: %s", pretty.Sdump(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) +} + +func (s *Suite) setupConnection(t *utesting.T) (*Conn, *Conn) { + // 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) + return sendConn, receiveConn } // dial attempts to dial the given node and perform a handshake, From a4bf15393c2ea101ea7e67f7fb225feb3319ec32 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 5 Nov 2020 13:08:05 +0100 Subject: [PATCH 2/9] cmd/devp2p/internal/ethtest: added large announcement tests --- cmd/devp2p/internal/ethtest/suite.go | 71 ++++++++++++++++++---------- cmd/devp2p/internal/ethtest/types.go | 22 +++++++++ 2 files changed, 68 insertions(+), 25 deletions(-) diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index 672804a60556..71a2607faa69 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -62,10 +62,13 @@ func NewSuite(dest *enode.Node, chainfile string, genesisfile string) *Suite { 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}, + /* + {Name: "Status", Fn: s.TestStatus}, + {Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders}, + {Name: "Broadcast", Fn: s.TestBroadcast}, + {Name: "GetBlockBodies", Fn: s.TestGetBlockBodies}, + */ + {Name: "TestLargeAnnounce", Fn: s.TestLargeAnnounce}, } } @@ -155,7 +158,7 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) { // TestBroadcast tests whether a block announcement is correctly // propagated to the given node's peer(s). func (s *Suite) TestBroadcast(t *utesting.T) { - sendConn, receiveConn := s.setupConnection(t) + sendConn, receiveConn := s.setupConnection(t), s.setupConnection(t) blockAnnouncement := &NewBlock{ Block: s.fullChain.blocks[1000], TD: s.fullChain.TD(1001), @@ -171,28 +174,51 @@ func (s *Suite) TestBroadcast(t *utesting.T) { // TestLargeAnnounce tests the announcement mechanism with a large block. func (s *Suite) TestLargeAnnounce(t *utesting.T) { - sendConn, receiveConn := s.setupConnection(t) - + nextBlock := 1000 blocks := []*NewBlock{ { Block: largeBlock(), - TD: s.fullChain.TD(1001), + TD: s.fullChain.TD(nextBlock), }, { - Block: s.fullChain.blocks[1001], + Block: s.fullChain.blocks[nextBlock], TD: largeNumber(2), }, { Block: largeBlock(), TD: largeNumber(2), }, + { + Block: s.fullChain.blocks[nextBlock], + TD: s.fullChain.TD(nextBlock), + }, } - for _, blockAnnouncement := range blocks { - // sendConn sends the block announcement - s.testAnnounce(t, sendConn, receiveConn, blockAnnouncement) + + var receiveConn *Conn + for i, blockAnnouncement := range blocks { + sendConn := s.setupConnection(t) + fmt.Println("Sending block announcement") + // Announce the block. + if err := sendConn.Write(blockAnnouncement); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + if i < 3 { + fmt.Println("Checking for disconnect") + time.Sleep(time.Second) + // check that the peer disconnected + if err := sendConn.waitForMessage(Disconnect{}); err != nil { + fmt.Print(err) + } + } + if i == 3 { + receiveConn = s.setupConnection(t) + } } + s.waitAnnounce(t, receiveConn, blocks[3]) + // update test suite chain + s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[nextBlock]) // wait for client to update its chain - if err := receiveConn.waitForBlock(s.chain.Head()); err != nil { + if err := receiveConn.waitForBlock(s.fullChain.blocks[nextBlock]); err != nil { t.Fatal(err) } } @@ -202,6 +228,10 @@ func (s *Suite) testAnnounce(t *utesting.T, sendConn, receiveConn *Conn, blockAn if err := sendConn.Write(blockAnnouncement); err != nil { t.Fatalf("could not write to connection: %v", err) } + s.waitAnnounce(t, receiveConn, blockAnnouncement) +} + +func (s *Suite) waitAnnounce(t *utesting.T, conn *Conn, blockAnnouncement *NewBlock) { // Wait for the announcement. timeout := 20 * time.Second switch msg := receiveConn.ReadAndServe(s.chain, timeout).(type) { @@ -227,24 +257,15 @@ func (s *Suite) testAnnounce(t *utesting.T, sendConn, receiveConn *Conn, blockAn } } -func (s *Suite) setupConnection(t *utesting.T) (*Conn, *Conn) { - // create conn to send block announcement +func (s *Suite) setupConnection(t *utesting.T) *Conn { + // create conn 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) - return sendConn, receiveConn + return sendConn } // dial attempts to dial the given node and perform a handshake, diff --git a/cmd/devp2p/internal/ethtest/types.go b/cmd/devp2p/internal/ethtest/types.go index 69367cb6cd48..665e87fd4829 100644 --- a/cmd/devp2p/internal/ethtest/types.go +++ b/cmd/devp2p/internal/ethtest/types.go @@ -377,3 +377,25 @@ func (c *Conn) waitForBlock(block *types.Block) error { } } } + +// waitForMessage waits for a certain message type. +func (c *Conn) waitForMessage(msg Message) error { + defer c.SetReadDeadline(time.Time{}) + + timeout := 20 * time.Second + deadline := time.Now().Add(timeout) + c.SetReadDeadline(deadline) + t := time.NewTimer(timeout) + for { + msg := c.Read() + if msg.Code() == msg.Code() { + return nil + } + select { + case <-t.C: + return fmt.Errorf("no message received within timeout") + default: + time.Sleep(100 * time.Millisecond) + } + } +} From b763014252a306ff2a154bb048318b9885b0145a Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Fri, 6 Nov 2020 10:14:21 +0100 Subject: [PATCH 3/9] cmd/devp2p/internal/ethtest: refactored stuff a bit --- cmd/devp2p/internal/ethtest/suite.go | 51 +++++++++++++++++----------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index 71a2607faa69..39eca2f24bc3 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -43,6 +43,7 @@ type Suite struct { chain *Chain fullChain *Chain + head int } // NewSuite creates and returns a new eth-test suite that can @@ -57,6 +58,7 @@ func NewSuite(dest *enode.Node, chainfile string, genesisfile string) *Suite { Dest: dest, chain: chain.Shorten(1000), fullChain: chain, + head: 999, } } @@ -159,13 +161,14 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) { // propagated to the given node's peer(s). func (s *Suite) TestBroadcast(t *utesting.T) { sendConn, receiveConn := s.setupConnection(t), s.setupConnection(t) + block := s.nextBlock() blockAnnouncement := &NewBlock{ - Block: s.fullChain.blocks[1000], - TD: s.fullChain.TD(1001), + Block: s.fullChain.blocks[block], + TD: s.fullChain.TD(block + 1), } s.testAnnounce(t, sendConn, receiveConn, blockAnnouncement) // update test suite chain - s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[1000]) + s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[block]) // wait for client to update its chain if err := receiveConn.waitForBlock(s.chain.Head()); err != nil { t.Fatal(err) @@ -174,11 +177,11 @@ func (s *Suite) TestBroadcast(t *utesting.T) { // TestLargeAnnounce tests the announcement mechanism with a large block. func (s *Suite) TestLargeAnnounce(t *utesting.T) { - nextBlock := 1000 + nextBlock := s.nextBlock() blocks := []*NewBlock{ { Block: largeBlock(), - TD: s.fullChain.TD(nextBlock), + TD: s.fullChain.TD(nextBlock + 1), }, { Block: s.fullChain.blocks[nextBlock], @@ -190,28 +193,21 @@ func (s *Suite) TestLargeAnnounce(t *utesting.T) { }, { Block: s.fullChain.blocks[nextBlock], - TD: s.fullChain.TD(nextBlock), + TD: s.fullChain.TD(nextBlock + 1), }, } var receiveConn *Conn for i, blockAnnouncement := range blocks { sendConn := s.setupConnection(t) - fmt.Println("Sending block announcement") - // Announce the block. - if err := sendConn.Write(blockAnnouncement); err != nil { - t.Fatalf("could not write to connection: %v", err) - } - if i < 3 { - fmt.Println("Checking for disconnect") - time.Sleep(time.Second) - // check that the peer disconnected - if err := sendConn.waitForMessage(Disconnect{}); err != nil { - fmt.Print(err) - } - } if i == 3 { receiveConn = s.setupConnection(t) + // The third block is a valid block + if err := sendConn.Write(blockAnnouncement); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + } else { + s.testDisconnect(t, sendConn, blockAnnouncement) } } s.waitAnnounce(t, receiveConn, blocks[3]) @@ -223,6 +219,18 @@ func (s *Suite) TestLargeAnnounce(t *utesting.T) { } } +func (s *Suite) testDisconnect(t *utesting.T, conn *Conn, msg Message) { + // Announce the block. + if err := conn.Write(msg); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + time.Sleep(100 * time.Millisecond) + // check that the peer disconnected + if err := conn.waitForMessage(Disconnect{}); err != nil { + t.Fatalf("connection was not disconnected: %v", err) + } +} + func (s *Suite) testAnnounce(t *utesting.T, sendConn, receiveConn *Conn, blockAnnouncement *NewBlock) { // Announce the block. if err := sendConn.Write(blockAnnouncement); err != nil { @@ -288,3 +296,8 @@ func (s *Suite) dial() (*Conn, error) { return &conn, nil } + +func (s *Suite) nextBlock() int { + s.head = s.head + 1 + return s.head +} From 2df02a4f89f12f5c4c57837e45fea0d48371f4ed Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Fri, 6 Nov 2020 12:01:09 +0100 Subject: [PATCH 4/9] cmd/devp2p/internal/ethtest: added TestMaliciousStatus/Handshake --- cmd/devp2p/internal/ethtest/suite.go | 122 ++++++++++++++++++++++----- cmd/devp2p/internal/ethtest/types.go | 25 +++--- 2 files changed, 114 insertions(+), 33 deletions(-) diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index 39eca2f24bc3..4b7bd8e2a4d9 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -24,6 +24,7 @@ import ( "github.com/davecgh/go-spew/spew" "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" @@ -43,7 +44,6 @@ type Suite struct { chain *Chain fullChain *Chain - head int } // NewSuite creates and returns a new eth-test suite that can @@ -58,19 +58,18 @@ func NewSuite(dest *enode.Node, chainfile string, genesisfile string) *Suite { Dest: dest, chain: chain.Shorten(1000), fullChain: chain, - head: 999, } } 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}, - */ + {Name: "Status", Fn: s.TestStatus}, + {Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders}, + {Name: "Broadcast", Fn: s.TestBroadcast}, + {Name: "GetBlockBodies", Fn: s.TestGetBlockBodies}, {Name: "TestLargeAnnounce", Fn: s.TestLargeAnnounce}, + {Name: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake}, + {Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus}, } } @@ -85,7 +84,7 @@ func (s *Suite) TestStatus(t *utesting.T) { // get protoHandshake conn.handshake(t) // get status - switch msg := conn.statusExchange(t, s.chain).(type) { + switch msg := conn.statusExchange(t, s.chain, nil).(type) { case *Status: t.Logf("got status message: %s", pretty.Sdump(msg)) default: @@ -93,6 +92,32 @@ func (s *Suite) TestStatus(t *utesting.T) { } } +// TestMaliciousStatus sends a status package with a large total difficulty. +func (s *Suite) TestMaliciousStatus(t *utesting.T) { + conn, err := s.dial() + if err != nil { + t.Fatalf("could not dial: %v", err) + } + // get protoHandshake + conn.handshake(t) + status := &Status{ + ProtocolVersion: uint32(conn.ethProtocolVersion), + NetworkID: s.chain.chainConfig.ChainID.Uint64(), + TD: largeNumber(2), + Head: s.chain.blocks[s.chain.Len()-1].Hash(), + Genesis: s.chain.blocks[0].Hash(), + ForkID: s.chain.ForkID(), + } + // get status + switch msg := conn.statusExchange(t, s.chain, status).(type) { + case *Status: + t.Logf("%+v\n", msg) + default: + t.Fatalf("unexpected: %#v", msg) + } + conn.waitForMessage(Disconnect{}) +} + // 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) { @@ -102,7 +127,7 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) { } conn.handshake(t) - conn.statusExchange(t, s.chain) + conn.statusExchange(t, s.chain, nil) // get block headers req := &GetBlockHeaders{ @@ -141,7 +166,7 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) { } conn.handshake(t) - conn.statusExchange(t, s.chain) + conn.statusExchange(t, s.chain, nil) // create block bodies request req := &GetBlockBodies{s.chain.blocks[54].Hash(), s.chain.blocks[75].Hash()} if err := conn.Write(req); err != nil { @@ -161,23 +186,81 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) { // propagated to the given node's peer(s). func (s *Suite) TestBroadcast(t *utesting.T) { sendConn, receiveConn := s.setupConnection(t), s.setupConnection(t) - block := s.nextBlock() + nextBlock := len(s.chain.blocks) blockAnnouncement := &NewBlock{ - Block: s.fullChain.blocks[block], - TD: s.fullChain.TD(block + 1), + Block: s.fullChain.blocks[nextBlock], + TD: s.fullChain.TD(nextBlock + 1), } s.testAnnounce(t, sendConn, receiveConn, blockAnnouncement) // update test suite chain - s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[block]) + s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[nextBlock]) // wait for client to update its chain if err := receiveConn.waitForBlock(s.chain.Head()); err != nil { t.Fatal(err) } } +// TestMaliciousHandshake tries to send malicious data during the handshake. +func (s *Suite) TestMaliciousHandshake(t *utesting.T) { + conn, err := s.dial() + if err != nil { + t.Fatalf("could not dial: %v", err) + } + // write hello to client + pub0 := crypto.FromECDSAPub(&conn.ourKey.PublicKey)[1:] + handshakes := []*Hello{ + { + Version: 5, + Caps: []p2p.Cap{ + {Name: largeString(2), Version: 64}, + }, + ID: pub0, + }, + { + Version: 5, + Caps: []p2p.Cap{ + {Name: "eth", Version: 64}, + {Name: "eth", Version: 65}, + }, + ID: append(pub0, byte(0)), + }, + { + Version: 5, + Caps: []p2p.Cap{ + {Name: "eth", Version: 64}, + {Name: "eth", Version: 65}, + }, + ID: append(pub0, pub0...), + }, + { + Version: 5, + Caps: []p2p.Cap{ + {Name: "eth", Version: 64}, + {Name: "eth", Version: 65}, + }, + ID: largeBuffer(2), + }, + { + Version: 5, + Caps: []p2p.Cap{ + {Name: largeString(2), Version: 64}, + }, + ID: largeBuffer(2), + }, + } + for _, handshake := range handshakes { + s.testDisconnect(t, conn, handshake) + // Dial for the next round + conn, err = s.dial() + if err != nil { + t.Fatalf("could not dial: %v", err) + } + } +} + // TestLargeAnnounce tests the announcement mechanism with a large block. func (s *Suite) TestLargeAnnounce(t *utesting.T) { - nextBlock := s.nextBlock() + nextBlock := len(s.chain.blocks) blocks := []*NewBlock{ { Block: largeBlock(), @@ -272,7 +355,7 @@ func (s *Suite) setupConnection(t *utesting.T) *Conn { t.Fatalf("could not dial: %v", err) } sendConn.handshake(t) - sendConn.statusExchange(t, s.chain) + sendConn.statusExchange(t, s.chain, nil) return sendConn } @@ -296,8 +379,3 @@ func (s *Suite) dial() (*Conn, error) { return &conn, nil } - -func (s *Suite) nextBlock() int { - s.head = s.head + 1 - return s.head -} diff --git a/cmd/devp2p/internal/ethtest/types.go b/cmd/devp2p/internal/ethtest/types.go index 665e87fd4829..c09f292c4817 100644 --- a/cmd/devp2p/internal/ethtest/types.go +++ b/cmd/devp2p/internal/ethtest/types.go @@ -304,7 +304,7 @@ func (c *Conn) negotiateEthProtocol(caps []p2p.Cap) { // statusExchange performs a `Status` message exchange with the given // node. -func (c *Conn) statusExchange(t *utesting.T, chain *Chain) Message { +func (c *Conn) statusExchange(t *utesting.T, chain *Chain, status *Status) Message { defer c.SetDeadline(time.Time{}) c.SetDeadline(time.Now().Add(20 * time.Second)) @@ -338,16 +338,19 @@ loop: if c.ethProtocolVersion == 0 { t.Fatalf("eth protocol version must be set in Conn") } - // write status message to client - status := Status{ - ProtocolVersion: uint32(c.ethProtocolVersion), - NetworkID: chain.chainConfig.ChainID.Uint64(), - TD: chain.TD(chain.Len()), - Head: chain.blocks[chain.Len()-1].Hash(), - Genesis: chain.blocks[0].Hash(), - ForkID: chain.ForkID(), + if status == nil { + // write status message to client + status = &Status{ + ProtocolVersion: uint32(c.ethProtocolVersion), + NetworkID: chain.chainConfig.ChainID.Uint64(), + 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 { + + if err := c.Write(*status); err != nil { t.Fatalf("could not write to connection: %v", err) } @@ -382,7 +385,7 @@ func (c *Conn) waitForBlock(block *types.Block) error { func (c *Conn) waitForMessage(msg Message) error { defer c.SetReadDeadline(time.Time{}) - timeout := 20 * time.Second + timeout := 10 * time.Second deadline := time.Now().Add(timeout) c.SetReadDeadline(deadline) t := time.NewTimer(timeout) From 4e56a1e757f013b25d11920dda518a6f43662770 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 9 Nov 2020 09:41:17 +0100 Subject: [PATCH 5/9] cmd/devp2p/internal/ethtest: fixed rebasing issue --- cmd/devp2p/internal/ethtest/suite.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index 4b7bd8e2a4d9..d0757acfda3c 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -323,9 +323,8 @@ func (s *Suite) testAnnounce(t *utesting.T, sendConn, receiveConn *Conn, blockAn } func (s *Suite) waitAnnounce(t *utesting.T, conn *Conn, blockAnnouncement *NewBlock) { - // Wait for the announcement. timeout := 20 * time.Second - switch msg := receiveConn.ReadAndServe(s.chain, timeout).(type) { + switch msg := conn.ReadAndServe(s.chain, timeout).(type) { case *NewBlock: t.Logf("received NewBlock message: %s", pretty.Sdump(msg.Block)) assert.Equal(t, From fd690e549b9b5664f9cacde87ec03f8548a35538 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 10 Nov 2020 09:34:18 +0100 Subject: [PATCH 6/9] happy linter, happy life --- cmd/devp2p/internal/ethtest/large.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/devp2p/internal/ethtest/large.go b/cmd/devp2p/internal/ethtest/large.go index c88fe15e8f33..deca00be5384 100644 --- a/cmd/devp2p/internal/ethtest/large.go +++ b/cmd/devp2p/internal/ethtest/large.go @@ -45,7 +45,7 @@ func largeBuffer(megabytes int) []byte { func largeString(megabytes int) string { buf := make([]byte, megabytes*1024*1024) rand.Read(buf) - return string(hexutil.Encode(buf)) + return hexutil.Encode(buf) } func largeBlock() *types.Block { From 791921b134626d730060af886a676e9ba014de71 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 23 Nov 2020 11:21:56 +0100 Subject: [PATCH 7/9] cmd/devp2p/internal/ethtest: used readAndServe --- cmd/devp2p/internal/ethtest/suite.go | 17 ++++++++++++++--- cmd/devp2p/internal/ethtest/types.go | 22 ---------------------- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index d0757acfda3c..ed3fab774802 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -115,7 +115,14 @@ func (s *Suite) TestMaliciousStatus(t *utesting.T) { default: t.Fatalf("unexpected: %#v", msg) } - conn.waitForMessage(Disconnect{}) + timeout := 20 * time.Second + // wait for disconnect + switch msg := conn.ReadAndServe(s.chain, timeout).(type) { + case *Disconnect: + return + default: + t.Fatalf("unexpected: %s", pretty.Sdump(msg)) + } } // TestGetBlockHeaders tests whether the given node can respond to @@ -309,8 +316,12 @@ func (s *Suite) testDisconnect(t *utesting.T, conn *Conn, msg Message) { } time.Sleep(100 * time.Millisecond) // check that the peer disconnected - if err := conn.waitForMessage(Disconnect{}); err != nil { - t.Fatalf("connection was not disconnected: %v", err) + timeout := 20 * time.Second + switch msg := conn.ReadAndServe(s.chain, timeout).(type) { + case *Disconnect: + return + default: + t.Fatalf("unexpected: %s", pretty.Sdump(msg)) } } diff --git a/cmd/devp2p/internal/ethtest/types.go b/cmd/devp2p/internal/ethtest/types.go index c09f292c4817..a20e88c3728f 100644 --- a/cmd/devp2p/internal/ethtest/types.go +++ b/cmd/devp2p/internal/ethtest/types.go @@ -380,25 +380,3 @@ func (c *Conn) waitForBlock(block *types.Block) error { } } } - -// waitForMessage waits for a certain message type. -func (c *Conn) waitForMessage(msg Message) error { - defer c.SetReadDeadline(time.Time{}) - - timeout := 10 * time.Second - deadline := time.Now().Add(timeout) - c.SetReadDeadline(deadline) - t := time.NewTimer(timeout) - for { - msg := c.Read() - if msg.Code() == msg.Code() { - return nil - } - select { - case <-t.C: - return fmt.Errorf("no message received within timeout") - default: - time.Sleep(100 * time.Millisecond) - } - } -} From 44ce7abe1d063b1af82b340d0544e10667b5cd4a Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 23 Nov 2020 12:09:48 +0100 Subject: [PATCH 8/9] stuff --- cmd/devp2p/internal/ethtest/suite.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index ed3fab774802..879eac9b5ec7 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -309,10 +309,10 @@ func (s *Suite) TestLargeAnnounce(t *utesting.T) { } } -func (s *Suite) testDisconnect(t *utesting.T, conn *Conn, msg Message) { +func (s *Suite) testDisconnect(t *utesting.T, conn *Conn, msg Message) error { // Announce the block. if err := conn.Write(msg); err != nil { - t.Fatalf("could not write to connection: %v", err) + return fmt.Errorf("could not write to connection: %v", err) } time.Sleep(100 * time.Millisecond) // check that the peer disconnected @@ -320,8 +320,10 @@ func (s *Suite) testDisconnect(t *utesting.T, conn *Conn, msg Message) { switch msg := conn.ReadAndServe(s.chain, timeout).(type) { case *Disconnect: return + case *Error: + return default: - t.Fatalf("unexpected: %s", pretty.Sdump(msg)) + return fmt.Errorf("unexpected: %s", pretty.Sdump(msg)) } } From df8871d692e3c3c034ba9a1df8bdcdc374078c68 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 23 Nov 2020 14:11:35 +0100 Subject: [PATCH 9/9] cmd/devp2p/internal/ethtest: fixed test cases --- cmd/devp2p/internal/ethtest/suite.go | 73 +++++++++++++++------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index 879eac9b5ec7..0348751f88a5 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -113,15 +113,16 @@ func (s *Suite) TestMaliciousStatus(t *utesting.T) { case *Status: t.Logf("%+v\n", msg) default: - t.Fatalf("unexpected: %#v", msg) + t.Fatalf("expected status, got: %#v ", msg) } timeout := 20 * time.Second // wait for disconnect switch msg := conn.ReadAndServe(s.chain, timeout).(type) { case *Disconnect: + case *Error: return default: - t.Fatalf("unexpected: %s", pretty.Sdump(msg)) + t.Fatalf("expected disconnect, got: %s", pretty.Sdump(msg)) } } @@ -255,8 +256,26 @@ func (s *Suite) TestMaliciousHandshake(t *utesting.T) { ID: largeBuffer(2), }, } - for _, handshake := range handshakes { - s.testDisconnect(t, conn, handshake) + for i, handshake := range handshakes { + fmt.Printf("Testing malicious handshake %v\n", i) + // Init the handshake + if err := conn.Write(handshake); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + // check that the peer disconnected + timeout := 20 * time.Second + // Discard one hello + for i := 0; i < 2; i++ { + switch msg := conn.ReadAndServe(s.chain, timeout).(type) { + case *Disconnect: + case *Error: + case *Hello: + // Hello's are send concurrently, so ignore them + continue + default: + t.Fatalf("unexpected: %s", pretty.Sdump(msg)) + } + } // Dial for the next round conn, err = s.dial() if err != nil { @@ -287,20 +306,26 @@ func (s *Suite) TestLargeAnnounce(t *utesting.T) { }, } - var receiveConn *Conn - for i, blockAnnouncement := range blocks { + for i, blockAnnouncement := range blocks[0:3] { + fmt.Printf("Testing malicious announcement: %v\n", i) sendConn := s.setupConnection(t) - if i == 3 { - receiveConn = s.setupConnection(t) - // The third block is a valid block - if err := sendConn.Write(blockAnnouncement); err != nil { - t.Fatalf("could not write to connection: %v", err) - } - } else { - s.testDisconnect(t, sendConn, blockAnnouncement) + if err := sendConn.Write(blockAnnouncement); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + // Invalid announcement, check that peer disconnected + timeout := 20 * time.Second + switch msg := sendConn.ReadAndServe(s.chain, timeout).(type) { + case *Disconnect: + case *Error: + break + default: + t.Fatalf("unexpected: %s wanted disconnect", pretty.Sdump(msg)) } } - s.waitAnnounce(t, receiveConn, blocks[3]) + // Test the last block as a valid block + sendConn := s.setupConnection(t) + receiveConn := s.setupConnection(t) + s.testAnnounce(t, sendConn, receiveConn, blocks[3]) // update test suite chain s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[nextBlock]) // wait for client to update its chain @@ -309,24 +334,6 @@ func (s *Suite) TestLargeAnnounce(t *utesting.T) { } } -func (s *Suite) testDisconnect(t *utesting.T, conn *Conn, msg Message) error { - // Announce the block. - if err := conn.Write(msg); err != nil { - return fmt.Errorf("could not write to connection: %v", err) - } - time.Sleep(100 * time.Millisecond) - // check that the peer disconnected - timeout := 20 * time.Second - switch msg := conn.ReadAndServe(s.chain, timeout).(type) { - case *Disconnect: - return - case *Error: - return - default: - return fmt.Errorf("unexpected: %s", pretty.Sdump(msg)) - } -} - func (s *Suite) testAnnounce(t *utesting.T, sendConn, receiveConn *Conn, blockAnnouncement *NewBlock) { // Announce the block. if err := sendConn.Write(blockAnnouncement); err != nil {