diff --git a/cmd/devp2p/internal/ethtest/chain.go b/cmd/devp2p/internal/ethtest/chain.go index d67387e80b..83c55181ad 100644 --- a/cmd/devp2p/internal/ethtest/chain.go +++ b/cmd/devp2p/internal/ethtest/chain.go @@ -34,6 +34,7 @@ import ( ) type Chain struct { + genesis core.Genesis blocks []*types.Block chainConfig *params.ChainConfig } @@ -124,16 +125,34 @@ func (c *Chain) GetHeaders(req GetBlockHeaders) (BlockHeaders, error) { // loadChain takes the given chain.rlp file, and decodes and returns // the blocks from the file. func loadChain(chainfile string, genesis string) (*Chain, error) { - chainConfig, err := ioutil.ReadFile(genesis) + gen, err := loadGenesis(genesis) if err != nil { return nil, err } + gblock := gen.ToBlock(nil) + + blocks, err := blocksFromFile(chainfile, gblock) + if err != nil { + return nil, err + } + + c := &Chain{genesis: gen, blocks: blocks, chainConfig: gen.Config} + return c, nil +} + +func loadGenesis(genesisFile string) (core.Genesis, error) { + chainConfig, err := ioutil.ReadFile(genesisFile) + if err != nil { + return core.Genesis{}, err + } var gen core.Genesis if err := json.Unmarshal(chainConfig, &gen); err != nil { - return nil, err + return core.Genesis{}, err } - gblock := gen.ToBlock(nil) + return gen, nil +} +func blocksFromFile(chainfile string, gblock *types.Block) ([]*types.Block, error) { // Load chain.rlp. fh, err := os.Open(chainfile) if err != nil { @@ -161,7 +180,5 @@ func loadChain(chainfile string, genesis string) (*Chain, error) { } blocks = append(blocks, &b) } - - c := &Chain{blocks: blocks, chainConfig: gen.Config} - return c, nil + return blocks, nil } diff --git a/cmd/devp2p/internal/ethtest/eth66_suite.go b/cmd/devp2p/internal/ethtest/eth66_suite.go index 0995dcb3e4..41a4246f30 100644 --- a/cmd/devp2p/internal/ethtest/eth66_suite.go +++ b/cmd/devp2p/internal/ethtest/eth66_suite.go @@ -41,6 +41,7 @@ func (s *Suite) Is_66(t *utesting.T) { // make sure the chain head is correct. func (s *Suite) TestStatus_66(t *utesting.T) { conn := s.dial66(t) + defer conn.Close() // get protoHandshake conn.handshake(t) // get status @@ -60,6 +61,7 @@ func (s *Suite) TestStatus_66(t *utesting.T) { // an eth66 `GetBlockHeaders` request and that the response is accurate. func (s *Suite) TestGetBlockHeaders_66(t *utesting.T) { conn := s.setupConnection66(t) + defer conn.Close() // get block headers req := ð.GetBlockHeadersPacket66{ RequestId: 3, @@ -84,6 +86,8 @@ func (s *Suite) TestGetBlockHeaders_66(t *utesting.T) { func (s *Suite) TestSimultaneousRequests_66(t *utesting.T) { // create two connections conn1, conn2 := s.setupConnection66(t), s.setupConnection66(t) + defer conn1.Close() + defer conn2.Close() // create two requests req1 := ð.GetBlockHeadersPacket66{ RequestId: 111, @@ -122,6 +126,9 @@ func (s *Suite) TestSimultaneousRequests_66(t *utesting.T) { // propagated to the given node's peer(s) on the eth66 protocol. func (s *Suite) TestBroadcast_66(t *utesting.T) { sendConn, receiveConn := s.setupConnection66(t), s.setupConnection66(t) + defer sendConn.Close() + defer receiveConn.Close() + nextBlock := len(s.chain.blocks) blockAnnouncement := &NewBlock{ Block: s.fullChain.blocks[nextBlock], @@ -141,6 +148,7 @@ func (s *Suite) TestBroadcast_66(t *utesting.T) { // the eth66 protocol. func (s *Suite) TestGetBlockBodies_66(t *utesting.T) { conn := s.setupConnection66(t) + defer conn.Close() // create block bodies request id := uint64(55) req := ð.GetBlockBodiesPacket66{ @@ -195,17 +203,20 @@ func (s *Suite) TestLargeAnnounce_66(t *utesting.T) { t.Fatalf("could not write to connection: %v", err) } // Invalid announcement, check that peer disconnected - switch msg := sendConn.ReadAndServe(s.chain, timeout).(type) { + switch msg := sendConn.ReadAndServe(s.chain, time.Second*8).(type) { case *Disconnect: case *Error: break default: t.Fatalf("unexpected: %s wanted disconnect", pretty.Sdump(msg)) } + sendConn.Close() } // Test the last block as a valid block - sendConn := s.setupConnection66(t) - receiveConn := s.setupConnection66(t) + sendConn, receiveConn := s.setupConnection66(t), s.setupConnection66(t) + defer sendConn.Close() + defer receiveConn.Close() + s.testAnnounce66(t, sendConn, receiveConn, blocks[3]) // update test suite chain s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[nextBlock]) @@ -216,12 +227,17 @@ func (s *Suite) TestLargeAnnounce_66(t *utesting.T) { } func (s *Suite) TestOldAnnounce_66(t *utesting.T) { - s.oldAnnounce(t, s.setupConnection66(t), s.setupConnection66(t)) + sendConn, recvConn := s.setupConnection66(t), s.setupConnection66(t) + defer sendConn.Close() + defer recvConn.Close() + + s.oldAnnounce(t, sendConn, recvConn) } // TestMaliciousHandshake_66 tries to send malicious data during the handshake. func (s *Suite) TestMaliciousHandshake_66(t *utesting.T) { conn := s.dial66(t) + defer conn.Close() // write hello to client pub0 := crypto.FromECDSAPub(&conn.ourKey.PublicKey)[1:] handshakes := []*Hello{ @@ -295,6 +311,7 @@ func (s *Suite) TestMaliciousHandshake_66(t *utesting.T) { // TestMaliciousStatus_66 sends a status package with a large total difficulty. func (s *Suite) TestMaliciousStatus_66(t *utesting.T) { conn := s.dial66(t) + defer conn.Close() // get protoHandshake conn.handshake(t) status := &Status{ @@ -334,23 +351,37 @@ func (s *Suite) TestTransaction_66(t *utesting.T) { } func (s *Suite) TestMaliciousTx_66(t *utesting.T) { - tests := []*types.Transaction{ + badTxs := []*types.Transaction{ getOldTxFromChain(t, s), invalidNonceTx(t, s), hugeAmount(t, s), hugeGasPrice(t, s), hugeData(t, s), } - for i, tx := range tests { + sendConn := s.setupConnection66(t) + defer sendConn.Close() + // set up receiving connection before sending txs to make sure + // no announcements are missed + recvConn := s.setupConnection66(t) + defer recvConn.Close() + + for i, tx := range badTxs { t.Logf("Testing malicious tx propagation: %v\n", i) - sendFailingTx66(t, s, tx) + if err := sendConn.Write(&Transactions{tx}); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + } + // check to make sure bad txs aren't propagated + waitForTxPropagation(t, s, badTxs, recvConn) } // TestZeroRequestID_66 checks that a request ID of zero is still handled // by the node. func (s *Suite) TestZeroRequestID_66(t *utesting.T) { conn := s.setupConnection66(t) + defer conn.Close() + req := ð.GetBlockHeadersPacket66{ RequestId: 0, GetBlockHeadersPacket: ð.GetBlockHeadersPacket{ @@ -367,6 +398,7 @@ func (s *Suite) TestZeroRequestID_66(t *utesting.T) { // concurrently to a single node. func (s *Suite) TestSameRequestID_66(t *utesting.T) { conn := s.setupConnection66(t) + defer conn.Close() // create two separate requests with same ID reqID := uint64(1234) req1 := ð.GetBlockHeadersPacket66{ diff --git a/cmd/devp2p/internal/ethtest/eth66_suiteHelpers.go b/cmd/devp2p/internal/ethtest/eth66_suiteHelpers.go index 4ef349740f..40427fcd30 100644 --- a/cmd/devp2p/internal/ethtest/eth66_suiteHelpers.go +++ b/cmd/devp2p/internal/ethtest/eth66_suiteHelpers.go @@ -238,14 +238,10 @@ func (c *Conn) waitForBlock66(block *types.Block) error { func sendSuccessfulTx66(t *utesting.T, s *Suite, tx *types.Transaction) { sendConn := s.setupConnection66(t) + defer sendConn.Close() sendSuccessfulTxWithConn(t, s, tx, sendConn) } -func sendFailingTx66(t *utesting.T, s *Suite, tx *types.Transaction) { - sendConn, recvConn := s.setupConnection66(t), s.setupConnection66(t) - sendFailingTxWithConns(t, s, tx, sendConn, recvConn) -} - func (s *Suite) getBlockHeaders66(t *utesting.T, conn *Conn, req eth.Packet, expectedID uint64) BlockHeaders { if err := conn.write66(req, GetBlockHeaders{}.Code()); err != nil { t.Fatalf("could not write to connection: %v", err) diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index 66fb8026a0..1cae16b7d5 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -69,20 +69,20 @@ func NewSuite(dest *enode.Node, chainfile string, genesisfile string) (*Suite, e func (s *Suite) AllEthTests() []utesting.Test { return []utesting.Test{ // status - {Name: "Status", Fn: s.TestStatus}, - {Name: "Status_66", Fn: s.TestStatus_66}, + {Name: "TestStatus", Fn: s.TestStatus}, + {Name: "TestStatus_66", Fn: s.TestStatus_66}, // get block headers - {Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders}, - {Name: "GetBlockHeaders_66", Fn: s.TestGetBlockHeaders_66}, + {Name: "TestGetBlockHeaders", Fn: s.TestGetBlockHeaders}, + {Name: "TestGetBlockHeaders_66", Fn: s.TestGetBlockHeaders_66}, {Name: "TestSimultaneousRequests_66", Fn: s.TestSimultaneousRequests_66}, {Name: "TestSameRequestID_66", Fn: s.TestSameRequestID_66}, {Name: "TestZeroRequestID_66", Fn: s.TestZeroRequestID_66}, // get block bodies - {Name: "GetBlockBodies", Fn: s.TestGetBlockBodies}, - {Name: "GetBlockBodies_66", Fn: s.TestGetBlockBodies_66}, + {Name: "TestGetBlockBodies", Fn: s.TestGetBlockBodies}, + {Name: "TestGetBlockBodies_66", Fn: s.TestGetBlockBodies_66}, // broadcast - {Name: "Broadcast", Fn: s.TestBroadcast}, - {Name: "Broadcast_66", Fn: s.TestBroadcast_66}, + {Name: "TestBroadcast", Fn: s.TestBroadcast}, + {Name: "TestBroadcast_66", Fn: s.TestBroadcast_66}, {Name: "TestLargeAnnounce", Fn: s.TestLargeAnnounce}, {Name: "TestLargeAnnounce_66", Fn: s.TestLargeAnnounce_66}, {Name: "TestOldAnnounce", Fn: s.TestOldAnnounce}, @@ -91,44 +91,44 @@ func (s *Suite) AllEthTests() []utesting.Test { {Name: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake}, {Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus}, {Name: "TestMaliciousHandshake_66", Fn: s.TestMaliciousHandshake_66}, - {Name: "TestMaliciousStatus_66", Fn: s.TestMaliciousStatus}, + {Name: "TestMaliciousStatus_66", Fn: s.TestMaliciousStatus_66}, // test transactions - {Name: "TestTransactions", Fn: s.TestTransaction}, - {Name: "TestTransactions_66", Fn: s.TestTransaction_66}, - {Name: "TestMaliciousTransactions", Fn: s.TestMaliciousTx}, - {Name: "TestMaliciousTransactions_66", Fn: s.TestMaliciousTx_66}, + {Name: "TestTransaction", Fn: s.TestTransaction}, + {Name: "TestTransaction_66", Fn: s.TestTransaction_66}, + {Name: "TestMaliciousTx", Fn: s.TestMaliciousTx}, + {Name: "TestMaliciousTx_66", Fn: s.TestMaliciousTx_66}, } } func (s *Suite) EthTests() []utesting.Test { return []utesting.Test{ - {Name: "Status", Fn: s.TestStatus}, - {Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders}, - {Name: "GetBlockBodies", Fn: s.TestGetBlockBodies}, - {Name: "Broadcast", Fn: s.TestBroadcast}, + {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: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake}, {Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus}, - {Name: "TestMaliciousStatus_66", Fn: s.TestMaliciousStatus}, - {Name: "TestTransactions", Fn: s.TestTransaction}, - {Name: "TestMaliciousTransactions", Fn: s.TestMaliciousTx}, + {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: "Status_66", Fn: s.TestStatus_66}, - {Name: "GetBlockHeaders_66", Fn: s.TestGetBlockHeaders_66}, + {Name: "TestStatus_66", Fn: s.TestStatus_66}, + {Name: "TestGetBlockHeaders_66", Fn: s.TestGetBlockHeaders_66}, {Name: "TestSimultaneousRequests_66", Fn: s.TestSimultaneousRequests_66}, {Name: "TestSameRequestID_66", Fn: s.TestSameRequestID_66}, {Name: "TestZeroRequestID_66", Fn: s.TestZeroRequestID_66}, - {Name: "GetBlockBodies_66", Fn: s.TestGetBlockBodies_66}, - {Name: "Broadcast_66", Fn: s.TestBroadcast_66}, + {Name: "TestGetBlockBodies_66", Fn: s.TestGetBlockBodies_66}, + {Name: "TestBroadcast_66", Fn: s.TestBroadcast_66}, {Name: "TestLargeAnnounce_66", Fn: s.TestLargeAnnounce_66}, {Name: "TestMaliciousHandshake_66", Fn: s.TestMaliciousHandshake_66}, - {Name: "TestTransactions_66", Fn: s.TestTransaction_66}, - {Name: "TestMaliciousTransactions_66", Fn: s.TestMaliciousTx_66}, + {Name: "TestMaliciousStatus_66", Fn: s.TestMaliciousStatus_66}, + {Name: "TestTransaction_66", Fn: s.TestTransaction_66}, + {Name: "TestMaliciousTx_66", Fn: s.TestMaliciousTx_66}, } } @@ -140,6 +140,7 @@ func (s *Suite) TestStatus(t *utesting.T) { if err != nil { t.Fatalf("could not dial: %v", err) } + defer conn.Close() // get protoHandshake conn.handshake(t) // get status @@ -157,6 +158,7 @@ func (s *Suite) TestMaliciousStatus(t *utesting.T) { if err != nil { t.Fatalf("could not dial: %v", err) } + defer conn.Close() // get protoHandshake conn.handshake(t) status := &Status{ @@ -191,6 +193,7 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) { if err != nil { t.Fatalf("could not dial: %v", err) } + defer conn.Close() conn.handshake(t) conn.statusExchange(t, s.chain, nil) @@ -229,6 +232,7 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) { if err != nil { t.Fatalf("could not dial: %v", err) } + defer conn.Close() conn.handshake(t) conn.statusExchange(t, s.chain, nil) @@ -253,6 +257,9 @@ 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) + defer sendConn.Close() + defer receiveConn.Close() + nextBlock := len(s.chain.blocks) blockAnnouncement := &NewBlock{ Block: s.fullChain.blocks[nextBlock], @@ -273,6 +280,7 @@ func (s *Suite) TestMaliciousHandshake(t *utesting.T) { if err != nil { t.Fatalf("could not dial: %v", err) } + defer conn.Close() // write hello to client pub0 := crypto.FromECDSAPub(&conn.ourKey.PublicKey)[1:] handshakes := []*Hello{ @@ -372,17 +380,21 @@ func (s *Suite) TestLargeAnnounce(t *utesting.T) { t.Fatalf("could not write to connection: %v", err) } // Invalid announcement, check that peer disconnected - switch msg := sendConn.ReadAndServe(s.chain, timeout).(type) { + switch msg := sendConn.ReadAndServe(s.chain, time.Second*8).(type) { case *Disconnect: case *Error: break default: t.Fatalf("unexpected: %s wanted disconnect", pretty.Sdump(msg)) } + sendConn.Close() } // Test the last block as a valid block sendConn := s.setupConnection(t) receiveConn := s.setupConnection(t) + defer sendConn.Close() + defer receiveConn.Close() + s.testAnnounce(t, sendConn, receiveConn, blocks[3]) // update test suite chain s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[nextBlock]) @@ -393,7 +405,11 @@ func (s *Suite) TestLargeAnnounce(t *utesting.T) { } func (s *Suite) TestOldAnnounce(t *utesting.T) { - s.oldAnnounce(t, s.setupConnection(t), s.setupConnection(t)) + sendConn, recvConn := s.setupConnection(t), s.setupConnection(t) + defer sendConn.Close() + defer recvConn.Close() + + s.oldAnnounce(t, sendConn, recvConn) } func (s *Suite) oldAnnounce(t *utesting.T, sendConn, receiveConn *Conn) { @@ -406,11 +422,19 @@ func (s *Suite) oldAnnounce(t *utesting.T, sendConn, receiveConn *Conn) { t.Fatalf("could not write to connection: %v", err) } - switch msg := receiveConn.ReadAndServe(s.chain, timeout*2).(type) { + switch msg := receiveConn.ReadAndServe(s.chain, time.Second*8).(type) { case *NewBlock: - t.Fatalf("unexpected: block propagated: %s", pretty.Sdump(msg)) + block := *msg + if block.Block.Hash() == oldBlockAnnounce.Block.Hash() { + t.Fatalf("unexpected: block propagated: %s", pretty.Sdump(msg)) + } case *NewBlockHashes: - t.Fatalf("unexpected: block announced: %s", pretty.Sdump(msg)) + hashes := *msg + for _, hash := range hashes { + if hash.Hash == oldBlockAnnounce.Block.Hash() { + t.Fatalf("unexpected: block announced: %s", pretty.Sdump(msg)) + } + } case *Error: errMsg := *msg // check to make sure error is timeout (propagation didn't come through == test successful) @@ -502,15 +526,27 @@ func (s *Suite) TestTransaction(t *utesting.T) { } func (s *Suite) TestMaliciousTx(t *utesting.T) { - tests := []*types.Transaction{ + badTxs := []*types.Transaction{ getOldTxFromChain(t, s), invalidNonceTx(t, s), hugeAmount(t, s), hugeGasPrice(t, s), hugeData(t, s), } - for i, tx := range tests { + sendConn := s.setupConnection(t) + defer sendConn.Close() + // set up receiving connection before sending txs to make sure + // no announcements are missed + recvConn := s.setupConnection(t) + defer recvConn.Close() + + for i, tx := range badTxs { t.Logf("Testing malicious tx propagation: %v\n", i) - sendFailingTx(t, s, tx) + if err := sendConn.Write(&Transactions{tx}); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + } + // check to make sure bad txs aren't propagated + waitForTxPropagation(t, s, badTxs, recvConn) } diff --git a/cmd/devp2p/internal/ethtest/suite_test.go b/cmd/devp2p/internal/ethtest/suite_test.go new file mode 100644 index 0000000000..2c628757bc --- /dev/null +++ b/cmd/devp2p/internal/ethtest/suite_test.go @@ -0,0 +1,99 @@ +// 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 ( + "os" + "testing" + + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/ethereum/go-ethereum/internal/utesting" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" +) + +var ( + genesisFile = "./testdata/genesis.json" + halfchainFile = "./testdata/halfchain.rlp" + fullchainFile = "./testdata/chain.rlp" +) + +func TestEthSuite(t *testing.T) { + geth, err := runGeth() + if err != nil { + t.Fatalf("could not run geth: %v", err) + } + defer geth.Close() + + suite, err := NewSuite(geth.Server().Self(), fullchainFile, genesisFile) + if err != nil { + t.Fatalf("could not create new test suite: %v", err) + } + for _, test := range suite.AllEthTests() { + t.Run(test.Name, func(t *testing.T) { + result := utesting.RunTAP([]utesting.Test{{Name: test.Name, Fn: test.Fn}}, os.Stdout) + if result[0].Failed { + t.Fatal() + } + }) + } +} + +// runGeth creates and starts a geth node +func runGeth() (*node.Node, error) { + stack, err := node.New(&node.Config{ + P2P: p2p.Config{ + ListenAddr: "127.0.0.1:0", + NoDiscovery: true, + MaxPeers: 10, // in case a test requires multiple connections, can be changed in the future + NoDial: true, + }, + }) + if err != nil { + return nil, err + } + + err = setupGeth(stack) + if err != nil { + stack.Close() + return nil, err + } + if err = stack.Start(); err != nil { + stack.Close() + return nil, err + } + return stack, nil +} + +func setupGeth(stack *node.Node) error { + chain, err := loadChain(halfchainFile, genesisFile) + if err != nil { + return err + } + + backend, err := eth.New(stack, ðconfig.Config{ + Genesis: &chain.genesis, + NetworkId: chain.genesis.Config.ChainID.Uint64(), // 19763 + }) + if err != nil { + return err + } + + _, err = backend.BlockChain().InsertChain(chain.blocks[1:]) + return err +} diff --git a/cmd/devp2p/internal/ethtest/transaction.go b/cmd/devp2p/internal/ethtest/transaction.go index 21aa221e8b..f8b0a9da81 100644 --- a/cmd/devp2p/internal/ethtest/transaction.go +++ b/cmd/devp2p/internal/ethtest/transaction.go @@ -30,6 +30,7 @@ var faucetKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c666 func sendSuccessfulTx(t *utesting.T, s *Suite, tx *types.Transaction) { sendConn := s.setupConnection(t) + defer sendConn.Close() sendSuccessfulTxWithConn(t, s, tx, sendConn) } @@ -65,29 +66,30 @@ func sendSuccessfulTxWithConn(t *utesting.T, s *Suite, tx *types.Transaction, se } } -func sendFailingTx(t *utesting.T, s *Suite, tx *types.Transaction) { - sendConn, recvConn := s.setupConnection(t), s.setupConnection(t) - sendFailingTxWithConns(t, s, tx, sendConn, recvConn) -} - -func sendFailingTxWithConns(t *utesting.T, s *Suite, tx *types.Transaction, sendConn, recvConn *Conn) { - // Wait for a transaction announcement - switch msg := recvConn.ReadAndServe(s.chain, timeout).(type) { - case *NewPooledTransactionHashes: - break - default: - t.Logf("unexpected message, logging: %v", pretty.Sdump(msg)) - } - // Send the transaction - if err := sendConn.Write(&Transactions{tx}); err != nil { - t.Fatal(err) - } +func waitForTxPropagation(t *utesting.T, s *Suite, txs []*types.Transaction, recvConn *Conn) { // Wait for another transaction announcement - switch msg := recvConn.ReadAndServe(s.chain, timeout).(type) { + switch msg := recvConn.ReadAndServe(s.chain, time.Second*8).(type) { case *Transactions: - t.Fatalf("Received unexpected transaction announcement: %v", msg) + // check to see if any of the failing txs were in the announcement + recvTxs := make([]common.Hash, len(*msg)) + for i, recvTx := range *msg { + recvTxs[i] = recvTx.Hash() + } + badTxs := containsTxs(recvTxs, txs) + if len(badTxs) > 0 { + for _, tx := range badTxs { + t.Logf("received bad tx: %v", tx) + } + t.Fatalf("received %d bad txs", len(badTxs)) + } case *NewPooledTransactionHashes: - t.Fatalf("Received unexpected pooledTx announcement: %v", msg) + badTxs := containsTxs(*msg, txs) + if len(badTxs) > 0 { + for _, tx := range badTxs { + t.Logf("received bad tx: %v", tx) + } + t.Fatalf("received %d bad txs", len(badTxs)) + } case *Error: // Transaction should not be announced -> wait for timeout return @@ -96,6 +98,20 @@ func sendFailingTxWithConns(t *utesting.T, s *Suite, tx *types.Transaction, send } } +// containsTxs checks whether the hashes of the received transactions are present in +// the given set of txs +func containsTxs(recvTxs []common.Hash, txs []*types.Transaction) []common.Hash { + containedTxs := make([]common.Hash, 0) + for _, recvTx := range recvTxs { + for _, tx := range txs { + if recvTx == tx.Hash() { + containedTxs = append(containedTxs, recvTx) + } + } + } + return containedTxs +} + func unknownTx(t *utesting.T, s *Suite) *types.Transaction { tx := getNextTxFromChain(t, s) var to common.Address diff --git a/cmd/devp2p/internal/ethtest/types.go b/cmd/devp2p/internal/ethtest/types.go index 1e2ae77965..50108f2dc3 100644 --- a/cmd/devp2p/internal/ethtest/types.go +++ b/cmd/devp2p/internal/ethtest/types.go @@ -178,8 +178,7 @@ func (c *Conn) Read() Message { func (c *Conn) ReadAndServe(chain *Chain, timeout time.Duration) Message { start := time.Now() for time.Since(start) < timeout { - timeout := time.Now().Add(10 * time.Second) - c.SetReadDeadline(timeout) + c.SetReadDeadline(time.Now().Add(5 * time.Second)) switch msg := c.Read().(type) { case *Ping: c.Write(&Pong{})