Skip to content

Commit 35f62a3

Browse files
MariusVanDerWijdenenriquefynn
authored andcommitted
cmd/devp2p/internal/ethtest: add transaction tests (ethereum#21857)
1 parent 8d7e394 commit 35f62a3

File tree

9 files changed

+243
-24
lines changed

9 files changed

+243
-24
lines changed

cmd/devp2p/internal/ethtest/chain_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func TestEthProtocolNegotiation(t *testing.T) {
7373
// TestChain_GetHeaders tests whether the test suite can correctly
7474
// respond to a GetBlockHeaders request from a node.
7575
func TestChain_GetHeaders(t *testing.T) {
76-
chainFile, err := filepath.Abs("./testdata/fullchain.rlp.gz")
76+
chainFile, err := filepath.Abs("./testdata/chain.rlp")
7777
if err != nil {
7878
t.Fatal(err)
7979
}

cmd/devp2p/internal/ethtest/suite.go

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"time"
2323

2424
"github.com/davecgh/go-spew/spew"
25+
"github.com/ethereum/go-ethereum/core/types"
2526
"github.com/ethereum/go-ethereum/crypto"
2627
"github.com/ethereum/go-ethereum/internal/utesting"
2728
"github.com/ethereum/go-ethereum/p2p"
@@ -37,6 +38,8 @@ var pretty = spew.ConfigState{
3738
SortKeys: true,
3839
}
3940

41+
var timeout = 20 * time.Second
42+
4043
// Suite represents a structure used to test the eth
4144
// protocol of a node(s).
4245
type Suite struct {
@@ -70,6 +73,8 @@ func (s *Suite) AllTests() []utesting.Test {
7073
{Name: "TestLargeAnnounce", Fn: s.TestLargeAnnounce},
7174
{Name: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake},
7275
{Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus},
76+
{Name: "TestTransactions", Fn: s.TestTransaction},
77+
{Name: "TestMaliciousTransactions", Fn: s.TestMaliciousTx},
7378
}
7479
}
7580

@@ -115,7 +120,6 @@ func (s *Suite) TestMaliciousStatus(t *utesting.T) {
115120
default:
116121
t.Fatalf("expected status, got: %#v ", msg)
117122
}
118-
timeout := 20 * time.Second
119123
// wait for disconnect
120124
switch msg := conn.ReadAndServe(s.chain, timeout).(type) {
121125
case *Disconnect:
@@ -151,7 +155,6 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
151155
t.Fatalf("could not write to connection: %v", err)
152156
}
153157

154-
timeout := 20 * time.Second
155158
switch msg := conn.ReadAndServe(s.chain, timeout).(type) {
156159
case *BlockHeaders:
157160
headers := msg
@@ -181,7 +184,6 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) {
181184
t.Fatalf("could not write to connection: %v", err)
182185
}
183186

184-
timeout := 20 * time.Second
185187
switch msg := conn.ReadAndServe(s.chain, timeout).(type) {
186188
case *BlockBodies:
187189
t.Logf("received %d block bodies", len(*msg))
@@ -257,7 +259,7 @@ func (s *Suite) TestMaliciousHandshake(t *utesting.T) {
257259
},
258260
}
259261
for i, handshake := range handshakes {
260-
fmt.Printf("Testing malicious handshake %v\n", i)
262+
t.Logf("Testing malicious handshake %v\n", i)
261263
// Init the handshake
262264
if err := conn.Write(handshake); err != nil {
263265
t.Fatalf("could not write to connection: %v", err)
@@ -307,13 +309,12 @@ func (s *Suite) TestLargeAnnounce(t *utesting.T) {
307309
}
308310

309311
for i, blockAnnouncement := range blocks[0:3] {
310-
fmt.Printf("Testing malicious announcement: %v\n", i)
312+
t.Logf("Testing malicious announcement: %v\n", i)
311313
sendConn := s.setupConnection(t)
312314
if err := sendConn.Write(blockAnnouncement); err != nil {
313315
t.Fatalf("could not write to connection: %v", err)
314316
}
315317
// Invalid announcement, check that peer disconnected
316-
timeout := 20 * time.Second
317318
switch msg := sendConn.ReadAndServe(s.chain, timeout).(type) {
318319
case *Disconnect:
319320
case *Error:
@@ -398,3 +399,28 @@ func (s *Suite) dial() (*Conn, error) {
398399

399400
return &conn, nil
400401
}
402+
403+
func (s *Suite) TestTransaction(t *utesting.T) {
404+
tests := []*types.Transaction{
405+
getNextTxFromChain(t, s),
406+
unknownTx(t, s),
407+
}
408+
for i, tx := range tests {
409+
t.Logf("Testing tx propagation: %v\n", i)
410+
sendSuccessfulTx(t, s, tx)
411+
}
412+
}
413+
414+
func (s *Suite) TestMaliciousTx(t *utesting.T) {
415+
tests := []*types.Transaction{
416+
getOldTxFromChain(t, s),
417+
invalidNonceTx(t, s),
418+
hugeAmount(t, s),
419+
hugeGasPrice(t, s),
420+
hugeData(t, s),
421+
}
422+
for i, tx := range tests {
423+
t.Logf("Testing malicious tx propagation: %v\n", i)
424+
sendFailingTx(t, s, tx)
425+
}
426+
}
1.51 MB
Binary file not shown.
-247 KB
Binary file not shown.

cmd/devp2p/internal/ethtest/testdata/genesis.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"coinbase": "0x0000000000000000000000000000000000000000",
1818
"alloc": {
1919
"71562b71999873db5b286df957af199ec94617f7": {
20-
"balance": "0xffffffff"
20+
"balance": "0xffffffffffffffffffffffffff"
2121
}
2222
},
2323
"number": "0x0",
515 KB
Binary file not shown.
-123 KB
Binary file not shown.
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
// Copyright 2020 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package ethtest
18+
19+
import (
20+
"time"
21+
22+
"github.com/ethereum/go-ethereum/common"
23+
"github.com/ethereum/go-ethereum/core/types"
24+
"github.com/ethereum/go-ethereum/crypto"
25+
"github.com/ethereum/go-ethereum/internal/utesting"
26+
)
27+
28+
//var faucetAddr = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7")
29+
var faucetKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
30+
31+
func sendSuccessfulTx(t *utesting.T, s *Suite, tx *types.Transaction) {
32+
sendConn := s.setupConnection(t)
33+
t.Logf("sending tx: %v %v %v\n", tx.Hash().String(), tx.GasPrice(), tx.Gas())
34+
// Send the transaction
35+
if err := sendConn.Write(Transactions([]*types.Transaction{tx})); err != nil {
36+
t.Fatal(err)
37+
}
38+
time.Sleep(100 * time.Millisecond)
39+
recvConn := s.setupConnection(t)
40+
// Wait for the transaction announcement
41+
switch msg := recvConn.ReadAndServe(s.chain, timeout).(type) {
42+
case *Transactions:
43+
recTxs := *msg
44+
if len(recTxs) < 1 {
45+
t.Fatalf("received transactions do not match send: %v", recTxs)
46+
}
47+
if tx.Hash() != recTxs[len(recTxs)-1].Hash() {
48+
t.Fatalf("received transactions do not match send: got %v want %v", recTxs, tx)
49+
}
50+
case *NewPooledTransactionHashes:
51+
txHashes := *msg
52+
if len(txHashes) < 1 {
53+
t.Fatalf("received transactions do not match send: %v", txHashes)
54+
}
55+
if tx.Hash() != txHashes[len(txHashes)-1] {
56+
t.Fatalf("wrong announcement received, wanted %v got %v", tx, txHashes)
57+
}
58+
default:
59+
t.Fatalf("unexpected message in sendSuccessfulTx: %s", pretty.Sdump(msg))
60+
}
61+
}
62+
63+
func sendFailingTx(t *utesting.T, s *Suite, tx *types.Transaction) {
64+
sendConn, recvConn := s.setupConnection(t), s.setupConnection(t)
65+
// Wait for a transaction announcement
66+
switch msg := recvConn.ReadAndServe(s.chain, timeout).(type) {
67+
case *NewPooledTransactionHashes:
68+
break
69+
default:
70+
t.Logf("unexpected message, logging: %v", pretty.Sdump(msg))
71+
}
72+
// Send the transaction
73+
if err := sendConn.Write(Transactions([]*types.Transaction{tx})); err != nil {
74+
t.Fatal(err)
75+
}
76+
// Wait for another transaction announcement
77+
switch msg := recvConn.ReadAndServe(s.chain, timeout).(type) {
78+
case *Transactions:
79+
t.Fatalf("Received unexpected transaction announcement: %v", msg)
80+
case *NewPooledTransactionHashes:
81+
t.Fatalf("Received unexpected pooledTx announcement: %v", msg)
82+
case *Error:
83+
// Transaction should not be announced -> wait for timeout
84+
return
85+
default:
86+
t.Fatalf("unexpected message in sendFailingTx: %s", pretty.Sdump(msg))
87+
}
88+
}
89+
90+
func unknownTx(t *utesting.T, s *Suite) *types.Transaction {
91+
tx := getNextTxFromChain(t, s)
92+
var to common.Address
93+
if tx.To() != nil {
94+
to = *tx.To()
95+
}
96+
txNew := types.NewTransaction(tx.Nonce()+1, to, tx.Value(), tx.Gas(), tx.GasPrice(), tx.Data())
97+
return signWithFaucet(t, txNew)
98+
}
99+
100+
func getNextTxFromChain(t *utesting.T, s *Suite) *types.Transaction {
101+
// Get a new transaction
102+
var tx *types.Transaction
103+
for _, blocks := range s.fullChain.blocks[s.chain.Len():] {
104+
txs := blocks.Transactions()
105+
if txs.Len() != 0 {
106+
tx = txs[0]
107+
break
108+
}
109+
}
110+
if tx == nil {
111+
t.Fatal("could not find transaction")
112+
}
113+
return tx
114+
}
115+
116+
func getOldTxFromChain(t *utesting.T, s *Suite) *types.Transaction {
117+
var tx *types.Transaction
118+
for _, blocks := range s.fullChain.blocks[:s.chain.Len()-1] {
119+
txs := blocks.Transactions()
120+
if txs.Len() != 0 {
121+
tx = txs[0]
122+
break
123+
}
124+
}
125+
if tx == nil {
126+
t.Fatal("could not find transaction")
127+
}
128+
return tx
129+
}
130+
131+
func invalidNonceTx(t *utesting.T, s *Suite) *types.Transaction {
132+
tx := getNextTxFromChain(t, s)
133+
var to common.Address
134+
if tx.To() != nil {
135+
to = *tx.To()
136+
}
137+
txNew := types.NewTransaction(tx.Nonce()-2, to, tx.Value(), tx.Gas(), tx.GasPrice(), tx.Data())
138+
return signWithFaucet(t, txNew)
139+
}
140+
141+
func hugeAmount(t *utesting.T, s *Suite) *types.Transaction {
142+
tx := getNextTxFromChain(t, s)
143+
amount := largeNumber(2)
144+
var to common.Address
145+
if tx.To() != nil {
146+
to = *tx.To()
147+
}
148+
txNew := types.NewTransaction(tx.Nonce(), to, amount, tx.Gas(), tx.GasPrice(), tx.Data())
149+
return signWithFaucet(t, txNew)
150+
}
151+
152+
func hugeGasPrice(t *utesting.T, s *Suite) *types.Transaction {
153+
tx := getNextTxFromChain(t, s)
154+
gasPrice := largeNumber(2)
155+
var to common.Address
156+
if tx.To() != nil {
157+
to = *tx.To()
158+
}
159+
txNew := types.NewTransaction(tx.Nonce(), to, tx.Value(), tx.Gas(), gasPrice, tx.Data())
160+
return signWithFaucet(t, txNew)
161+
}
162+
163+
func hugeData(t *utesting.T, s *Suite) *types.Transaction {
164+
tx := getNextTxFromChain(t, s)
165+
var to common.Address
166+
if tx.To() != nil {
167+
to = *tx.To()
168+
}
169+
txNew := types.NewTransaction(tx.Nonce(), to, tx.Value(), tx.Gas(), tx.GasPrice(), largeBuffer(2))
170+
return signWithFaucet(t, txNew)
171+
}
172+
173+
func signWithFaucet(t *utesting.T, tx *types.Transaction) *types.Transaction {
174+
signer := types.HomesteadSigner{}
175+
signedTx, err := types.SignTx(tx, signer, faucetKey)
176+
if err != nil {
177+
t.Fatalf("could not sign tx: %v\n", err)
178+
}
179+
return signedTx
180+
}

cmd/devp2p/internal/ethtest/types.go

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,9 @@ type NewBlockHashes []struct {
100100

101101
func (nbh NewBlockHashes) Code() int { return 17 }
102102

103-
// NewBlock is the network packet for the block propagation message.
104-
type NewBlock struct {
105-
Block *types.Block
106-
TD *big.Int
107-
}
103+
type Transactions []*types.Transaction
108104

109-
func (nb NewBlock) Code() int { return 23 }
105+
func (t Transactions) Code() int { return 18 }
110106

111107
// GetBlockHeaders represents a block header query.
112108
type GetBlockHeaders struct {
@@ -122,6 +118,29 @@ type BlockHeaders []*types.Header
122118

123119
func (bh BlockHeaders) Code() int { return 20 }
124120

121+
// GetBlockBodies represents a GetBlockBodies request
122+
type GetBlockBodies []common.Hash
123+
124+
func (gbb GetBlockBodies) Code() int { return 21 }
125+
126+
// BlockBodies is the network packet for block content distribution.
127+
type BlockBodies []*types.Body
128+
129+
func (bb BlockBodies) Code() int { return 22 }
130+
131+
// NewBlock is the network packet for the block propagation message.
132+
type NewBlock struct {
133+
Block *types.Block
134+
TD *big.Int
135+
}
136+
137+
func (nb NewBlock) Code() int { return 23 }
138+
139+
// NewPooledTransactionHashes is the network packet for the tx hash propagation message.
140+
type NewPooledTransactionHashes [][32]byte
141+
142+
func (nb NewPooledTransactionHashes) Code() int { return 24 }
143+
125144
// HashOrNumber is a combined field for specifying an origin block.
126145
type hashOrNumber struct {
127146
Hash common.Hash // Block hash from which to retrieve headers (excludes Number)
@@ -158,16 +177,6 @@ func (hn *hashOrNumber) DecodeRLP(s *rlp.Stream) error {
158177
return err
159178
}
160179

161-
// GetBlockBodies represents a GetBlockBodies request
162-
type GetBlockBodies []common.Hash
163-
164-
func (gbb GetBlockBodies) Code() int { return 21 }
165-
166-
// BlockBodies is the network packet for block content distribution.
167-
type BlockBodies []*types.Body
168-
169-
func (bb BlockBodies) Code() int { return 22 }
170-
171180
// Conn represents an individual connection with a peer
172181
type Conn struct {
173182
*rlpx.Conn
@@ -205,6 +214,10 @@ func (c *Conn) Read() Message {
205214
msg = new(NewBlock)
206215
case (NewBlockHashes{}).Code():
207216
msg = new(NewBlockHashes)
217+
case (Transactions{}).Code():
218+
msg = new(Transactions)
219+
case (NewPooledTransactionHashes{}).Code():
220+
msg = new(NewPooledTransactionHashes)
208221
default:
209222
return errorf("invalid message code: %d", code)
210223
}

0 commit comments

Comments
 (0)