mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 23:26:44 +00:00
make Conn.ReadEth return an error when the connection disconnects. In bad blob tx tests, when reading eth protocol messages, ensure that we read-and-ignore any pending messages that are unrelated to the message that the test is expecting to proceed.
This commit is contained in:
parent
efd74e05d9
commit
feb85dd4e5
2 changed files with 50 additions and 22 deletions
|
|
@ -130,11 +130,16 @@ func (c *Conn) Write(proto Proto, code uint64, msg any) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errDisc error = fmt.Errorf("disconnect")
|
||||||
|
|
||||||
// ReadEth reads an Eth sub-protocol wire message.
|
// ReadEth reads an Eth sub-protocol wire message.
|
||||||
func (c *Conn) ReadEth() (any, error) {
|
func (c *Conn) ReadEth() (any, error) {
|
||||||
c.SetReadDeadline(time.Now().Add(timeout))
|
c.SetReadDeadline(time.Now().Add(timeout))
|
||||||
for {
|
for {
|
||||||
code, data, _, err := c.Conn.Read()
|
code, data, _, err := c.Conn.Read()
|
||||||
|
if code == discMsg {
|
||||||
|
return nil, errDisc
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,12 @@
|
||||||
package ethtest
|
package ethtest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||||
|
|
@ -64,6 +66,7 @@ func NewSuite(dest *enode.Node, chainDir, engineURL, jwt string) (*Suite, error)
|
||||||
|
|
||||||
func (s *Suite) EthTests() []utesting.Test {
|
func (s *Suite) EthTests() []utesting.Test {
|
||||||
return []utesting.Test{
|
return []utesting.Test{
|
||||||
|
|
||||||
// status
|
// status
|
||||||
{Name: "Status", Fn: s.TestStatus},
|
{Name: "Status", Fn: s.TestStatus},
|
||||||
// get block headers
|
// get block headers
|
||||||
|
|
@ -76,7 +79,7 @@ func (s *Suite) EthTests() []utesting.Test {
|
||||||
// // malicious handshakes + status
|
// // malicious handshakes + status
|
||||||
{Name: "MaliciousHandshake", Fn: s.TestMaliciousHandshake},
|
{Name: "MaliciousHandshake", Fn: s.TestMaliciousHandshake},
|
||||||
// test transactions
|
// test transactions
|
||||||
//{Name: "LargeTxRequest", Fn: s.TestLargeTxRequest, Slow: true},
|
{Name: "LargeTxRequest", Fn: s.TestLargeTxRequest, Slow: true},
|
||||||
{Name: "Transaction", Fn: s.TestTransaction},
|
{Name: "Transaction", Fn: s.TestTransaction},
|
||||||
{Name: "InvalidTxs", Fn: s.TestInvalidTxs},
|
{Name: "InvalidTxs", Fn: s.TestInvalidTxs},
|
||||||
{Name: "NewPooledTxs", Fn: s.TestNewPooledTxs},
|
{Name: "NewPooledTxs", Fn: s.TestNewPooledTxs},
|
||||||
|
|
@ -844,33 +847,53 @@ func mangleSidecar(tx *types.Transaction) *types.Transaction {
|
||||||
return tx.WithBlobTxSidecar(©)
|
return tx.WithBlobTxSidecar(©)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
TestBlobTxWithoutSidecar tests the following scenario:
|
|
||||||
Peers connect to the client. Some are "good", and some are "bad". The test
|
|
||||||
proceeds as so:
|
|
||||||
- "bad" peer connects to the client, advertises a blob transaction without a
|
|
||||||
sidecar
|
|
||||||
- before the transaction can be transmitted from bad peer, good peer connects
|
|
||||||
to the client advertising the same tx hash (but with size incorporating the
|
|
||||||
sidecar)
|
|
||||||
- bad peer transmits the sidecar-less transaction
|
|
||||||
- client receives and should only disconnect bad peer
|
|
||||||
- the transaction is requested from a good peer, and good peer responds with
|
|
||||||
transaction that should be pooled by the client.
|
|
||||||
- Only bad peers are disconnected from while good peer is not.
|
|
||||||
*/
|
|
||||||
func (s *Suite) TestBlobTxWithoutSidecar(t *utesting.T) {
|
func (s *Suite) TestBlobTxWithoutSidecar(t *utesting.T) {
|
||||||
|
t.Log(`This test tests that a transaction first advertised/transmitted without a sidecar will result in the sending peer being disconnected, and the full transaction should be successfully retrieved from another peer.`)
|
||||||
tx := s.makeBlobTxs(1, 2, 42)[0]
|
tx := s.makeBlobTxs(1, 2, 42)[0]
|
||||||
badTx := tx.WithoutBlobTxSidecar()
|
badTx := tx.WithoutBlobTxSidecar()
|
||||||
s.testBadBlobTx(t, tx, badTx)
|
s.testBadBlobTx(t, tx, badTx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestBlobTxWithMismatchedSidecar(t *utesting.T) {
|
func (s *Suite) TestBlobTxWithMismatchedSidecar(t *utesting.T) {
|
||||||
|
t.Log(`This test tests that a transaction first advertised/transmitted with a sidecar whose commitment don't correspond to the blob_versioned_hashes in the transaction header will result in the sending peer being disconnected, and the full transaction should be successfully retrieved from another peer.`)
|
||||||
tx := s.makeBlobTxs(1, 2, 43)[0]
|
tx := s.makeBlobTxs(1, 2, 43)[0]
|
||||||
badTx := mangleSidecar(tx)
|
badTx := mangleSidecar(tx)
|
||||||
s.testBadBlobTx(t, tx, badTx)
|
s.testBadBlobTx(t, tx, badTx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readUntil reads eth protocol messages until a message of the target type is
|
||||||
|
// received. It returns an error if there is a disconnect, or if the context
|
||||||
|
// is cancelled before a message of the desired type can be read.
|
||||||
|
func readUntil[T any](ctx context.Context, conn *Conn) (*T, error) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, context.Canceled
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
received, err := conn.ReadEth()
|
||||||
|
if err != nil {
|
||||||
|
if err == errDisc {
|
||||||
|
return nil, errDisc
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch received.(type) {
|
||||||
|
case *T:
|
||||||
|
res := received.(*T)
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// readUntilDisconnect reads eth protocol messages until the peer disconnects.
|
||||||
|
// It returns whether the peer disconnects in the next 100ms.
|
||||||
|
func readUntilDisconnect(conn *Conn) (disconnected bool) {
|
||||||
|
ctx, _ := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||||
|
_, err := readUntil[struct{}](ctx, conn)
|
||||||
|
return err == errDisc
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Suite) testBadBlobTx(t *utesting.T, tx *types.Transaction, badTx *types.Transaction) {
|
func (s *Suite) testBadBlobTx(t *utesting.T, tx *types.Transaction, badTx *types.Transaction) {
|
||||||
stage1, stage2, stage3 := new(sync.WaitGroup), new(sync.WaitGroup), new(sync.WaitGroup)
|
stage1, stage2, stage3 := new(sync.WaitGroup), new(sync.WaitGroup), new(sync.WaitGroup)
|
||||||
stage1.Add(1)
|
stage1.Add(1)
|
||||||
|
|
@ -906,8 +929,7 @@ func (s *Suite) testBadBlobTx(t *utesting.T, tx *types.Transaction, badTx *types
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req := new(eth.GetPooledTransactionsPacket)
|
req, err := readUntil[eth.GetPooledTransactionsPacket](context.Background(), conn)
|
||||||
err = conn.ReadMsg(ethProto, eth.GetPooledTransactionsMsg, req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errc <- fmt.Errorf("failed to read GetPooledTransactions message: %v", err)
|
errc <- fmt.Errorf("failed to read GetPooledTransactions message: %v", err)
|
||||||
return
|
return
|
||||||
|
|
@ -924,7 +946,7 @@ func (s *Suite) testBadBlobTx(t *utesting.T, tx *types.Transaction, badTx *types
|
||||||
errc <- fmt.Errorf("writing pooled tx response failed: %v", err)
|
errc <- fmt.Errorf("writing pooled tx response failed: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if code, _, _ := conn.Read(); code != discMsg {
|
if !readUntilDisconnect(conn) {
|
||||||
errc <- fmt.Errorf("expected bad peer to be disconnected")
|
errc <- fmt.Errorf("expected bad peer to be disconnected")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -966,8 +988,9 @@ func (s *Suite) testBadBlobTx(t *utesting.T, tx *types.Transaction, badTx *types
|
||||||
// the bad peer has responded with sidecar-less transaction.
|
// the bad peer has responded with sidecar-less transaction.
|
||||||
// bad peers are presumably disconnected now.
|
// bad peers are presumably disconnected now.
|
||||||
|
|
||||||
req := new(eth.GetPooledTransactionsPacket)
|
var req *eth.GetPooledTransactionsPacket
|
||||||
if err := conn.ReadMsg(ethProto, eth.GetPooledTransactionsMsg, req); err != nil {
|
req, err = readUntil[eth.GetPooledTransactionsPacket](context.Background(), conn)
|
||||||
|
if err != nil {
|
||||||
errc <- fmt.Errorf("reading pooled tx request failed: %v", err)
|
errc <- fmt.Errorf("reading pooled tx request failed: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -982,7 +1005,7 @@ func (s *Suite) testBadBlobTx(t *utesting.T, tx *types.Transaction, badTx *types
|
||||||
errc <- fmt.Errorf("writing pooled tx response failed: %v", err)
|
errc <- fmt.Errorf("writing pooled tx response failed: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if code, _, _ := conn.Read(); code == discMsg {
|
if readUntilDisconnect(conn) {
|
||||||
errc <- fmt.Errorf("unexpected disconnect")
|
errc <- fmt.Errorf("unexpected disconnect")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue