49bde05a55
This PR refactors the eth test suite to make it more readable and easier to use. Some notable differences: - A new file helpers.go stores all of the methods used between both eth66 and eth65 and below tests, as well as methods shared among many test functions. - suite.go now contains all of the test functions for both eth65 tests and eth66 tests. - The utesting.T object doesn't get passed through to other helper methods, but is instead only used within the scope of the test function, whereas helper methods return errors, so only the test function itself can fatal out in the case of an error. - The full test suite now only takes 13.5 seconds to run.
755 lines
23 KiB
Go
755 lines
23 KiB
Go
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
package ethtest
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/eth/protocols/eth"
|
|
"github.com/ethereum/go-ethereum/internal/utesting"
|
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
|
)
|
|
|
|
// Suite represents a structure used to test a node's conformance
|
|
// to the eth protocol.
|
|
type Suite struct {
|
|
Dest *enode.Node
|
|
|
|
chain *Chain
|
|
fullChain *Chain
|
|
}
|
|
|
|
// 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, error) {
|
|
chain, err := loadChain(chainfile, genesisfile)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Suite{
|
|
Dest: dest,
|
|
chain: chain.Shorten(1000),
|
|
fullChain: chain,
|
|
}, nil
|
|
}
|
|
|
|
func (s *Suite) AllEthTests() []utesting.Test {
|
|
return []utesting.Test{
|
|
// status
|
|
{Name: "TestStatus", Fn: s.TestStatus},
|
|
{Name: "TestStatus66", Fn: s.TestStatus66},
|
|
// get block headers
|
|
{Name: "TestGetBlockHeaders", Fn: s.TestGetBlockHeaders},
|
|
{Name: "TestGetBlockHeaders66", Fn: s.TestGetBlockHeaders66},
|
|
{Name: "TestSimultaneousRequests66", Fn: s.TestSimultaneousRequests66},
|
|
{Name: "TestSameRequestID66", Fn: s.TestSameRequestID66},
|
|
{Name: "TestZeroRequestID66", Fn: s.TestZeroRequestID66},
|
|
// get block bodies
|
|
{Name: "TestGetBlockBodies", Fn: s.TestGetBlockBodies},
|
|
{Name: "TestGetBlockBodies66", Fn: s.TestGetBlockBodies66},
|
|
// broadcast
|
|
{Name: "TestBroadcast", Fn: s.TestBroadcast},
|
|
{Name: "TestBroadcast66", Fn: s.TestBroadcast66},
|
|
{Name: "TestLargeAnnounce", Fn: s.TestLargeAnnounce},
|
|
{Name: "TestLargeAnnounce66", Fn: s.TestLargeAnnounce66},
|
|
{Name: "TestOldAnnounce", Fn: s.TestOldAnnounce},
|
|
{Name: "TestOldAnnounce66", Fn: s.TestOldAnnounce66},
|
|
// malicious handshakes + status
|
|
{Name: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake},
|
|
{Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus},
|
|
{Name: "TestMaliciousHandshake66", Fn: s.TestMaliciousHandshake66},
|
|
{Name: "TestMaliciousStatus66", Fn: s.TestMaliciousStatus66},
|
|
// test transactions
|
|
{Name: "TestTransaction", Fn: s.TestTransaction},
|
|
{Name: "TestTransaction66", Fn: s.TestTransaction66},
|
|
{Name: "TestMaliciousTx", Fn: s.TestMaliciousTx},
|
|
{Name: "TestMaliciousTx66", Fn: s.TestMaliciousTx66},
|
|
{Name: "TestLargeTxRequest66", Fn: s.TestLargeTxRequest66},
|
|
{Name: "TestNewPooledTxs66", Fn: s.TestNewPooledTxs66},
|
|
}
|
|
}
|
|
|
|
func (s *Suite) EthTests() []utesting.Test {
|
|
return []utesting.Test{
|
|
{Name: "TestStatus", Fn: s.TestStatus},
|
|
{Name: "TestGetBlockHeaders", Fn: s.TestGetBlockHeaders},
|
|
{Name: "TestGetBlockBodies", Fn: s.TestGetBlockBodies},
|
|
{Name: "TestBroadcast", Fn: s.TestBroadcast},
|
|
{Name: "TestLargeAnnounce", Fn: s.TestLargeAnnounce},
|
|
{Name: "TestOldAnnounce", Fn: s.TestOldAnnounce},
|
|
{Name: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake},
|
|
{Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus},
|
|
{Name: "TestTransaction", Fn: s.TestTransaction},
|
|
{Name: "TestMaliciousTx", Fn: s.TestMaliciousTx},
|
|
}
|
|
}
|
|
|
|
func (s *Suite) Eth66Tests() []utesting.Test {
|
|
return []utesting.Test{
|
|
// only proceed with eth66 test suite if node supports eth 66 protocol
|
|
{Name: "TestStatus66", Fn: s.TestStatus66},
|
|
{Name: "TestGetBlockHeaders66", Fn: s.TestGetBlockHeaders66},
|
|
{Name: "TestSimultaneousRequests66", Fn: s.TestSimultaneousRequests66},
|
|
{Name: "TestSameRequestID66", Fn: s.TestSameRequestID66},
|
|
{Name: "TestZeroRequestID66", Fn: s.TestZeroRequestID66},
|
|
{Name: "TestGetBlockBodies66", Fn: s.TestGetBlockBodies66},
|
|
{Name: "TestBroadcast66", Fn: s.TestBroadcast66},
|
|
{Name: "TestLargeAnnounce66", Fn: s.TestLargeAnnounce66},
|
|
{Name: "TestOldAnnounce66", Fn: s.TestOldAnnounce66},
|
|
{Name: "TestMaliciousHandshake66", Fn: s.TestMaliciousHandshake66},
|
|
{Name: "TestMaliciousStatus66", Fn: s.TestMaliciousStatus66},
|
|
{Name: "TestTransaction66", Fn: s.TestTransaction66},
|
|
{Name: "TestMaliciousTx66", Fn: s.TestMaliciousTx66},
|
|
{Name: "TestLargeTxRequest66", Fn: s.TestLargeTxRequest66},
|
|
{Name: "TestNewPooledTxs66", Fn: s.TestNewPooledTxs66},
|
|
}
|
|
}
|
|
|
|
var (
|
|
eth66 = true // indicates whether suite should negotiate eth66 connection
|
|
eth65 = false // indicates whether suite should negotiate eth65 connection or below.
|
|
)
|
|
|
|
// TestStatus attempts to connect to the given node and exchange
|
|
// a status message with it.
|
|
func (s *Suite) TestStatus(t *utesting.T) {
|
|
conn, err := s.dial()
|
|
if err != nil {
|
|
t.Fatalf("dial failed: %v", err)
|
|
}
|
|
defer conn.Close()
|
|
if err := conn.peer(s.chain, nil); err != nil {
|
|
t.Fatalf("peering failed: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestStatus66 attempts to connect to the given node and exchange
|
|
// a status message with it on the eth66 protocol.
|
|
func (s *Suite) TestStatus66(t *utesting.T) {
|
|
conn, err := s.dial66()
|
|
if err != nil {
|
|
t.Fatalf("dial failed: %v", err)
|
|
}
|
|
defer conn.Close()
|
|
if err := conn.peer(s.chain, nil); err != nil {
|
|
t.Fatalf("peering failed: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestGetBlockHeaders tests whether the given node can respond to
|
|
// a `GetBlockHeaders` request accurately.
|
|
func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
|
|
conn, err := s.dial()
|
|
if err != nil {
|
|
t.Fatalf("dial failed: %v", err)
|
|
}
|
|
defer conn.Close()
|
|
if err := conn.peer(s.chain, nil); err != nil {
|
|
t.Fatalf("handshake(s) failed: %v", err)
|
|
}
|
|
// write request
|
|
req := &GetBlockHeaders{
|
|
Origin: eth.HashOrNumber{
|
|
Hash: s.chain.blocks[1].Hash(),
|
|
},
|
|
Amount: 2,
|
|
Skip: 1,
|
|
Reverse: false,
|
|
}
|
|
headers, err := conn.headersRequest(req, s.chain, eth65, 0)
|
|
if err != nil {
|
|
t.Fatalf("GetBlockHeaders request failed: %v", err)
|
|
}
|
|
// check for correct headers
|
|
expected, err := s.chain.GetHeaders(*req)
|
|
if err != nil {
|
|
t.Fatalf("failed to get headers for given request: %v", err)
|
|
}
|
|
if !headersMatch(expected, headers) {
|
|
t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers)
|
|
}
|
|
}
|
|
|
|
// TestGetBlockHeaders66 tests whether the given node can respond to
|
|
// an eth66 `GetBlockHeaders` request and that the response is accurate.
|
|
func (s *Suite) TestGetBlockHeaders66(t *utesting.T) {
|
|
conn, err := s.dial66()
|
|
if err != nil {
|
|
t.Fatalf("dial failed: %v", err)
|
|
}
|
|
defer conn.Close()
|
|
if err = conn.peer(s.chain, nil); err != nil {
|
|
t.Fatalf("peering failed: %v", err)
|
|
}
|
|
// write request
|
|
req := &GetBlockHeaders{
|
|
Origin: eth.HashOrNumber{
|
|
Hash: s.chain.blocks[1].Hash(),
|
|
},
|
|
Amount: 2,
|
|
Skip: 1,
|
|
Reverse: false,
|
|
}
|
|
headers, err := conn.headersRequest(req, s.chain, eth66, 33)
|
|
if err != nil {
|
|
t.Fatalf("could not get block headers: %v", err)
|
|
}
|
|
// check for correct headers
|
|
expected, err := s.chain.GetHeaders(*req)
|
|
if err != nil {
|
|
t.Fatalf("failed to get headers for given request: %v", err)
|
|
}
|
|
if !headersMatch(expected, headers) {
|
|
t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers)
|
|
}
|
|
}
|
|
|
|
// TestSimultaneousRequests66 sends two simultaneous `GetBlockHeader` requests from
|
|
// the same connection with different request IDs and checks to make sure the node
|
|
// responds with the correct headers per request.
|
|
func (s *Suite) TestSimultaneousRequests66(t *utesting.T) {
|
|
// create a connection
|
|
conn, err := s.dial66()
|
|
if err != nil {
|
|
t.Fatalf("dial failed: %v", err)
|
|
}
|
|
defer conn.Close()
|
|
if err := conn.peer(s.chain, nil); err != nil {
|
|
t.Fatalf("peering failed: %v", err)
|
|
}
|
|
// create two requests
|
|
req1 := ð.GetBlockHeadersPacket66{
|
|
RequestId: uint64(111),
|
|
GetBlockHeadersPacket: ð.GetBlockHeadersPacket{
|
|
Origin: eth.HashOrNumber{
|
|
Hash: s.chain.blocks[1].Hash(),
|
|
},
|
|
Amount: 2,
|
|
Skip: 1,
|
|
Reverse: false,
|
|
},
|
|
}
|
|
req2 := ð.GetBlockHeadersPacket66{
|
|
RequestId: uint64(222),
|
|
GetBlockHeadersPacket: ð.GetBlockHeadersPacket{
|
|
Origin: eth.HashOrNumber{
|
|
Hash: s.chain.blocks[1].Hash(),
|
|
},
|
|
Amount: 4,
|
|
Skip: 1,
|
|
Reverse: false,
|
|
},
|
|
}
|
|
// write the first request
|
|
if err := conn.Write66(req1, GetBlockHeaders{}.Code()); err != nil {
|
|
t.Fatalf("failed to write to connection: %v", err)
|
|
}
|
|
// write the second request
|
|
if err := conn.Write66(req2, GetBlockHeaders{}.Code()); err != nil {
|
|
t.Fatalf("failed to write to connection: %v", err)
|
|
}
|
|
// wait for responses
|
|
msg := conn.waitForResponse(s.chain, timeout, req1.RequestId)
|
|
headers1, ok := msg.(BlockHeaders)
|
|
if !ok {
|
|
t.Fatalf("unexpected %s", pretty.Sdump(msg))
|
|
}
|
|
msg = conn.waitForResponse(s.chain, timeout, req2.RequestId)
|
|
headers2, ok := msg.(BlockHeaders)
|
|
if !ok {
|
|
t.Fatalf("unexpected %s", pretty.Sdump(msg))
|
|
}
|
|
// check received headers for accuracy
|
|
expected1, err := s.chain.GetHeaders(GetBlockHeaders(*req1.GetBlockHeadersPacket))
|
|
if err != nil {
|
|
t.Fatalf("failed to get expected headers for request 1: %v", err)
|
|
}
|
|
expected2, err := s.chain.GetHeaders(GetBlockHeaders(*req2.GetBlockHeadersPacket))
|
|
if err != nil {
|
|
t.Fatalf("failed to get expected headers for request 2: %v", err)
|
|
}
|
|
if !headersMatch(expected1, headers1) {
|
|
t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected1, headers1)
|
|
}
|
|
if !headersMatch(expected2, headers2) {
|
|
t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected2, headers2)
|
|
}
|
|
}
|
|
|
|
// TestSameRequestID66 sends two requests with the same request ID to a
|
|
// single node.
|
|
func (s *Suite) TestSameRequestID66(t *utesting.T) {
|
|
conn, err := s.dial66()
|
|
if err != nil {
|
|
t.Fatalf("dial failed: %v", err)
|
|
}
|
|
defer conn.Close()
|
|
if err := conn.peer(s.chain, nil); err != nil {
|
|
t.Fatalf("peering failed: %v", err)
|
|
}
|
|
// create requests
|
|
reqID := uint64(1234)
|
|
request1 := ð.GetBlockHeadersPacket66{
|
|
RequestId: reqID,
|
|
GetBlockHeadersPacket: ð.GetBlockHeadersPacket{
|
|
Origin: eth.HashOrNumber{
|
|
Number: 1,
|
|
},
|
|
Amount: 2,
|
|
},
|
|
}
|
|
request2 := ð.GetBlockHeadersPacket66{
|
|
RequestId: reqID,
|
|
GetBlockHeadersPacket: ð.GetBlockHeadersPacket{
|
|
Origin: eth.HashOrNumber{
|
|
Number: 33,
|
|
},
|
|
Amount: 2,
|
|
},
|
|
}
|
|
// write the requests
|
|
if err = conn.Write66(request1, GetBlockHeaders{}.Code()); err != nil {
|
|
t.Fatalf("failed to write to connection: %v", err)
|
|
}
|
|
if err = conn.Write66(request2, GetBlockHeaders{}.Code()); err != nil {
|
|
t.Fatalf("failed to write to connection: %v", err)
|
|
}
|
|
// wait for responses
|
|
msg := conn.waitForResponse(s.chain, timeout, reqID)
|
|
headers1, ok := msg.(BlockHeaders)
|
|
if !ok {
|
|
t.Fatalf("unexpected %s", pretty.Sdump(msg))
|
|
}
|
|
msg = conn.waitForResponse(s.chain, timeout, reqID)
|
|
headers2, ok := msg.(BlockHeaders)
|
|
if !ok {
|
|
t.Fatalf("unexpected %s", pretty.Sdump(msg))
|
|
}
|
|
// check if headers match
|
|
expected1, err := s.chain.GetHeaders(GetBlockHeaders(*request1.GetBlockHeadersPacket))
|
|
if err != nil {
|
|
t.Fatalf("failed to get expected block headers: %v", err)
|
|
}
|
|
expected2, err := s.chain.GetHeaders(GetBlockHeaders(*request2.GetBlockHeadersPacket))
|
|
if err != nil {
|
|
t.Fatalf("failed to get expected block headers: %v", err)
|
|
}
|
|
if !headersMatch(expected1, headers1) {
|
|
t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected1, headers1)
|
|
}
|
|
if !headersMatch(expected2, headers2) {
|
|
t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected2, headers2)
|
|
}
|
|
}
|
|
|
|
// TestZeroRequestID_66 checks that a message with a request ID of zero is still handled
|
|
// by the node.
|
|
func (s *Suite) TestZeroRequestID66(t *utesting.T) {
|
|
conn, err := s.dial66()
|
|
if err != nil {
|
|
t.Fatalf("dial failed: %v", err)
|
|
}
|
|
defer conn.Close()
|
|
if err := conn.peer(s.chain, nil); err != nil {
|
|
t.Fatalf("peering failed: %v", err)
|
|
}
|
|
req := &GetBlockHeaders{
|
|
Origin: eth.HashOrNumber{
|
|
Number: 0,
|
|
},
|
|
Amount: 2,
|
|
}
|
|
headers, err := conn.headersRequest(req, s.chain, eth66, 0)
|
|
if err != nil {
|
|
t.Fatalf("failed to get block headers: %v", err)
|
|
}
|
|
expected, err := s.chain.GetHeaders(*req)
|
|
if err != nil {
|
|
t.Fatalf("failed to get expected block headers: %v", err)
|
|
}
|
|
if !headersMatch(expected, headers) {
|
|
t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers)
|
|
}
|
|
}
|
|
|
|
// 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("dial failed: %v", err)
|
|
}
|
|
defer conn.Close()
|
|
if err := conn.peer(s.chain, nil); err != nil {
|
|
t.Fatalf("peering failed: %v", err)
|
|
}
|
|
// 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)
|
|
}
|
|
// wait for response
|
|
switch msg := conn.readAndServe(s.chain, timeout).(type) {
|
|
case *BlockBodies:
|
|
t.Logf("received %d block bodies", len(*msg))
|
|
if len(*msg) != len(*req) {
|
|
t.Fatalf("wrong bodies in response: expected %d bodies, "+
|
|
"got %d", len(*req), len(*msg))
|
|
}
|
|
default:
|
|
t.Fatalf("unexpected: %s", pretty.Sdump(msg))
|
|
}
|
|
}
|
|
|
|
// TestGetBlockBodies66 tests whether the given node can respond to
|
|
// a `GetBlockBodies` request and that the response is accurate over
|
|
// the eth66 protocol.
|
|
func (s *Suite) TestGetBlockBodies66(t *utesting.T) {
|
|
conn, err := s.dial66()
|
|
if err != nil {
|
|
t.Fatalf("dial failed: %v", err)
|
|
}
|
|
defer conn.Close()
|
|
if err := conn.peer(s.chain, nil); err != nil {
|
|
t.Fatalf("peering failed: %v", err)
|
|
}
|
|
// create block bodies request
|
|
req := ð.GetBlockBodiesPacket66{
|
|
RequestId: uint64(55),
|
|
GetBlockBodiesPacket: eth.GetBlockBodiesPacket{
|
|
s.chain.blocks[54].Hash(),
|
|
s.chain.blocks[75].Hash(),
|
|
},
|
|
}
|
|
if err := conn.Write66(req, GetBlockBodies{}.Code()); err != nil {
|
|
t.Fatalf("could not write to connection: %v", err)
|
|
}
|
|
// wait for block bodies response
|
|
msg := conn.waitForResponse(s.chain, timeout, req.RequestId)
|
|
blockBodies, ok := msg.(BlockBodies)
|
|
if !ok {
|
|
t.Fatalf("unexpected: %s", pretty.Sdump(msg))
|
|
}
|
|
t.Logf("received %d block bodies", len(blockBodies))
|
|
if len(blockBodies) != len(req.GetBlockBodiesPacket) {
|
|
t.Fatalf("wrong bodies in response: expected %d bodies, "+
|
|
"got %d", len(req.GetBlockBodiesPacket), len(blockBodies))
|
|
}
|
|
}
|
|
|
|
// TestBroadcast tests whether a block announcement is correctly
|
|
// propagated to the given node's peer(s).
|
|
func (s *Suite) TestBroadcast(t *utesting.T) {
|
|
if err := s.sendNextBlock(eth65); err != nil {
|
|
t.Fatalf("block broadcast failed: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestBroadcast66 tests whether a block announcement is correctly
|
|
// propagated to the given node's peer(s) on the eth66 protocol.
|
|
func (s *Suite) TestBroadcast66(t *utesting.T) {
|
|
if err := s.sendNextBlock(eth66); err != nil {
|
|
t.Fatalf("block broadcast failed: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestLargeAnnounce tests the announcement mechanism with a large block.
|
|
func (s *Suite) TestLargeAnnounce(t *utesting.T) {
|
|
nextBlock := len(s.chain.blocks)
|
|
blocks := []*NewBlock{
|
|
{
|
|
Block: largeBlock(),
|
|
TD: s.fullChain.TotalDifficultyAt(nextBlock),
|
|
},
|
|
{
|
|
Block: s.fullChain.blocks[nextBlock],
|
|
TD: largeNumber(2),
|
|
},
|
|
{
|
|
Block: largeBlock(),
|
|
TD: largeNumber(2),
|
|
},
|
|
}
|
|
|
|
for i, blockAnnouncement := range blocks {
|
|
t.Logf("Testing malicious announcement: %v\n", i)
|
|
conn, err := s.dial()
|
|
if err != nil {
|
|
t.Fatalf("dial failed: %v", err)
|
|
}
|
|
if err = conn.peer(s.chain, nil); err != nil {
|
|
t.Fatalf("peering failed: %v", err)
|
|
}
|
|
if err = conn.Write(blockAnnouncement); err != nil {
|
|
t.Fatalf("could not write to connection: %v", err)
|
|
}
|
|
// Invalid announcement, check that peer disconnected
|
|
switch msg := conn.readAndServe(s.chain, time.Second*8).(type) {
|
|
case *Disconnect:
|
|
case *Error:
|
|
break
|
|
default:
|
|
t.Fatalf("unexpected: %s wanted disconnect", pretty.Sdump(msg))
|
|
}
|
|
conn.Close()
|
|
}
|
|
// Test the last block as a valid block
|
|
if err := s.sendNextBlock(eth65); err != nil {
|
|
t.Fatalf("failed to broadcast next block: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestLargeAnnounce66 tests the announcement mechanism with a large
|
|
// block over the eth66 protocol.
|
|
func (s *Suite) TestLargeAnnounce66(t *utesting.T) {
|
|
nextBlock := len(s.chain.blocks)
|
|
blocks := []*NewBlock{
|
|
{
|
|
Block: largeBlock(),
|
|
TD: s.fullChain.TotalDifficultyAt(nextBlock),
|
|
},
|
|
{
|
|
Block: s.fullChain.blocks[nextBlock],
|
|
TD: largeNumber(2),
|
|
},
|
|
{
|
|
Block: largeBlock(),
|
|
TD: largeNumber(2),
|
|
},
|
|
}
|
|
|
|
for i, blockAnnouncement := range blocks[0:3] {
|
|
t.Logf("Testing malicious announcement: %v\n", i)
|
|
conn, err := s.dial66()
|
|
if err != nil {
|
|
t.Fatalf("dial failed: %v", err)
|
|
}
|
|
if err := conn.peer(s.chain, nil); err != nil {
|
|
t.Fatalf("peering failed: %v", err)
|
|
}
|
|
if err := conn.Write(blockAnnouncement); err != nil {
|
|
t.Fatalf("could not write to connection: %v", err)
|
|
}
|
|
// Invalid announcement, check that peer disconnected
|
|
switch msg := conn.readAndServe(s.chain, time.Second*8).(type) {
|
|
case *Disconnect:
|
|
case *Error:
|
|
break
|
|
default:
|
|
t.Fatalf("unexpected: %s wanted disconnect", pretty.Sdump(msg))
|
|
}
|
|
conn.Close()
|
|
}
|
|
// Test the last block as a valid block
|
|
if err := s.sendNextBlock(eth66); err != nil {
|
|
t.Fatalf("failed to broadcast next block: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestOldAnnounce tests the announcement mechanism with an old block.
|
|
func (s *Suite) TestOldAnnounce(t *utesting.T) {
|
|
if err := s.oldAnnounce(eth65); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// TestOldAnnounce66 tests the announcement mechanism with an old block,
|
|
// over the eth66 protocol.
|
|
func (s *Suite) TestOldAnnounce66(t *utesting.T) {
|
|
if err := s.oldAnnounce(eth66); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// TestMaliciousHandshake tries to send malicious data during the handshake.
|
|
func (s *Suite) TestMaliciousHandshake(t *utesting.T) {
|
|
if err := s.maliciousHandshakes(t, eth65); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// TestMaliciousHandshake66 tries to send malicious data during the handshake.
|
|
func (s *Suite) TestMaliciousHandshake66(t *utesting.T) {
|
|
if err := s.maliciousHandshakes(t, eth66); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// 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("dial failed: %v", err)
|
|
}
|
|
defer conn.Close()
|
|
|
|
if err := s.maliciousStatus(conn); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// TestMaliciousStatus66 sends a status package with a large total
|
|
// difficulty over the eth66 protocol.
|
|
func (s *Suite) TestMaliciousStatus66(t *utesting.T) {
|
|
conn, err := s.dial66()
|
|
if err != nil {
|
|
t.Fatalf("dial failed: %v", err)
|
|
}
|
|
defer conn.Close()
|
|
|
|
if err := s.maliciousStatus(conn); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// TestTransaction sends a valid transaction to the node and
|
|
// checks if the transaction gets propagated.
|
|
func (s *Suite) TestTransaction(t *utesting.T) {
|
|
if err := s.sendSuccessfulTxs(t, eth65); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// TestTransaction66 sends a valid transaction to the node and
|
|
// checks if the transaction gets propagated.
|
|
func (s *Suite) TestTransaction66(t *utesting.T) {
|
|
if err := s.sendSuccessfulTxs(t, eth66); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// TestMaliciousTx sends several invalid transactions and tests whether
|
|
// the node will propagate them.
|
|
func (s *Suite) TestMaliciousTx(t *utesting.T) {
|
|
if err := s.sendMaliciousTxs(t, eth65); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// TestMaliciousTx66 sends several invalid transactions and tests whether
|
|
// the node will propagate them.
|
|
func (s *Suite) TestMaliciousTx66(t *utesting.T) {
|
|
if err := s.sendMaliciousTxs(t, eth66); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// TestLargeTxRequest66 tests whether a node can fulfill a large GetPooledTransactions
|
|
// request.
|
|
func (s *Suite) TestLargeTxRequest66(t *utesting.T) {
|
|
// send the next block to ensure the node is no longer syncing and
|
|
// is able to accept txs
|
|
if err := s.sendNextBlock(eth66); err != nil {
|
|
t.Fatalf("failed to send next block: %v", err)
|
|
}
|
|
// send 2000 transactions to the node
|
|
hashMap, txs, err := generateTxs(s, 2000)
|
|
if err != nil {
|
|
t.Fatalf("failed to generate transactions: %v", err)
|
|
}
|
|
if err = sendMultipleSuccessfulTxs(t, s, txs); err != nil {
|
|
t.Fatalf("failed to send multiple txs: %v", err)
|
|
}
|
|
// set up connection to receive to ensure node is peered with the receiving connection
|
|
// before tx request is sent
|
|
conn, err := s.dial66()
|
|
if err != nil {
|
|
t.Fatalf("dial failed: %v", err)
|
|
}
|
|
defer conn.Close()
|
|
if err = conn.peer(s.chain, nil); err != nil {
|
|
t.Fatalf("peering failed: %v", err)
|
|
}
|
|
// create and send pooled tx request
|
|
hashes := make([]common.Hash, 0)
|
|
for _, hash := range hashMap {
|
|
hashes = append(hashes, hash)
|
|
}
|
|
getTxReq := ð.GetPooledTransactionsPacket66{
|
|
RequestId: 1234,
|
|
GetPooledTransactionsPacket: hashes,
|
|
}
|
|
if err = conn.Write66(getTxReq, GetPooledTransactions{}.Code()); err != nil {
|
|
t.Fatalf("could not write to conn: %v", err)
|
|
}
|
|
// check that all received transactions match those that were sent to node
|
|
switch msg := conn.waitForResponse(s.chain, timeout, getTxReq.RequestId).(type) {
|
|
case PooledTransactions:
|
|
for _, gotTx := range msg {
|
|
if _, exists := hashMap[gotTx.Hash()]; !exists {
|
|
t.Fatalf("unexpected tx received: %v", gotTx.Hash())
|
|
}
|
|
}
|
|
default:
|
|
t.Fatalf("unexpected %s", pretty.Sdump(msg))
|
|
}
|
|
}
|
|
|
|
// TestNewPooledTxs_66 tests whether a node will do a GetPooledTransactions
|
|
// request upon receiving a NewPooledTransactionHashes announcement.
|
|
func (s *Suite) TestNewPooledTxs66(t *utesting.T) {
|
|
// send the next block to ensure the node is no longer syncing and
|
|
// is able to accept txs
|
|
if err := s.sendNextBlock(eth66); err != nil {
|
|
t.Fatalf("failed to send next block: %v", err)
|
|
}
|
|
// generate 50 txs
|
|
hashMap, _, err := generateTxs(s, 50)
|
|
if err != nil {
|
|
t.Fatalf("failed to generate transactions: %v", err)
|
|
}
|
|
// create new pooled tx hashes announcement
|
|
hashes := make([]common.Hash, 0)
|
|
for _, hash := range hashMap {
|
|
hashes = append(hashes, hash)
|
|
}
|
|
announce := NewPooledTransactionHashes(hashes)
|
|
// send announcement
|
|
conn, err := s.dial66()
|
|
if err != nil {
|
|
t.Fatalf("dial failed: %v", err)
|
|
}
|
|
defer conn.Close()
|
|
if err = conn.peer(s.chain, nil); err != nil {
|
|
t.Fatalf("peering failed: %v", err)
|
|
}
|
|
if err = conn.Write(announce); err != nil {
|
|
t.Fatalf("failed to write to connection: %v", err)
|
|
}
|
|
// wait for GetPooledTxs request
|
|
for {
|
|
_, msg := conn.readAndServe66(s.chain, timeout)
|
|
switch msg := msg.(type) {
|
|
case GetPooledTransactions:
|
|
if len(msg) != len(hashes) {
|
|
t.Fatalf("unexpected number of txs requested: wanted %d, got %d", len(hashes), len(msg))
|
|
}
|
|
return
|
|
case *NewPooledTransactionHashes:
|
|
// ignore propagated txs from old tests
|
|
continue
|
|
default:
|
|
t.Fatalf("unexpected %s", pretty.Sdump(msg))
|
|
}
|
|
}
|
|
}
|