github.com/ethereum/go-ethereum@v1.16.1/cmd/devp2p/internal/ethtest/suite.go (about)

     1  // Copyright 2020 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU 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  // go-ethereum 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 General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package ethtest
    18  
    19  import (
    20  	"context"
    21  	"crypto/rand"
    22  	"fmt"
    23  	"reflect"
    24  	"sync"
    25  	"time"
    26  
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
    29  	"github.com/ethereum/go-ethereum/core/types"
    30  	"github.com/ethereum/go-ethereum/crypto"
    31  	"github.com/ethereum/go-ethereum/crypto/kzg4844"
    32  	"github.com/ethereum/go-ethereum/eth/protocols/eth"
    33  	"github.com/ethereum/go-ethereum/internal/utesting"
    34  	"github.com/ethereum/go-ethereum/p2p"
    35  	"github.com/ethereum/go-ethereum/p2p/enode"
    36  	"github.com/holiman/uint256"
    37  )
    38  
    39  // Suite represents a structure used to test a node's conformance
    40  // to the eth protocol.
    41  type Suite struct {
    42  	Dest   *enode.Node
    43  	chain  *Chain
    44  	engine *EngineClient
    45  }
    46  
    47  // NewSuite creates and returns a new eth-test suite that can
    48  // be used to test the given node against the given blockchain
    49  // data.
    50  func NewSuite(dest *enode.Node, chainDir, engineURL, jwt string) (*Suite, error) {
    51  	chain, err := NewChain(chainDir)
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  	engine, err := NewEngineClient(chainDir, engineURL, jwt)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	return &Suite{
    61  		Dest:   dest,
    62  		chain:  chain,
    63  		engine: engine,
    64  	}, nil
    65  }
    66  
    67  func (s *Suite) EthTests() []utesting.Test {
    68  	return []utesting.Test{
    69  		// status
    70  		{Name: "Status", Fn: s.TestStatus},
    71  		{Name: "MaliciousHandshake", Fn: s.TestMaliciousHandshake},
    72  		{Name: "BlockRangeUpdateExpired", Fn: s.TestBlockRangeUpdateHistoryExp},
    73  		{Name: "BlockRangeUpdateFuture", Fn: s.TestBlockRangeUpdateFuture},
    74  		{Name: "BlockRangeUpdateInvalid", Fn: s.TestBlockRangeUpdateInvalid},
    75  		// get block headers
    76  		{Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders},
    77  		{Name: "GetNonexistentBlockHeaders", Fn: s.TestGetNonexistentBlockHeaders},
    78  		{Name: "SimultaneousRequests", Fn: s.TestSimultaneousRequests},
    79  		{Name: "SameRequestID", Fn: s.TestSameRequestID},
    80  		{Name: "ZeroRequestID", Fn: s.TestZeroRequestID},
    81  		// get history
    82  		{Name: "GetBlockBodies", Fn: s.TestGetBlockBodies},
    83  		{Name: "GetReceipts", Fn: s.TestGetReceipts},
    84  		// test transactions
    85  		{Name: "LargeTxRequest", Fn: s.TestLargeTxRequest, Slow: true},
    86  		{Name: "Transaction", Fn: s.TestTransaction},
    87  		{Name: "InvalidTxs", Fn: s.TestInvalidTxs},
    88  		{Name: "NewPooledTxs", Fn: s.TestNewPooledTxs},
    89  		{Name: "BlobViolations", Fn: s.TestBlobViolations},
    90  		{Name: "TestBlobTxWithoutSidecar", Fn: s.TestBlobTxWithoutSidecar},
    91  		{Name: "TestBlobTxWithMismatchedSidecar", Fn: s.TestBlobTxWithMismatchedSidecar},
    92  	}
    93  }
    94  
    95  func (s *Suite) SnapTests() []utesting.Test {
    96  	return []utesting.Test{
    97  		{Name: "Status", Fn: s.TestSnapStatus},
    98  		{Name: "AccountRange", Fn: s.TestSnapGetAccountRange},
    99  		{Name: "GetByteCodes", Fn: s.TestSnapGetByteCodes},
   100  		{Name: "GetTrieNodes", Fn: s.TestSnapTrieNodes},
   101  		{Name: "GetStorageRanges", Fn: s.TestSnapGetStorageRanges},
   102  	}
   103  }
   104  
   105  func (s *Suite) TestStatus(t *utesting.T) {
   106  	t.Log(`This test is just a sanity check. It performs an eth protocol handshake.`)
   107  	conn, err := s.dialAndPeer(nil)
   108  	if err != nil {
   109  		t.Fatal("peering failed:", err)
   110  	}
   111  	conn.Close()
   112  }
   113  
   114  // headersMatch returns whether the received headers match the given request
   115  func headersMatch(expected []*types.Header, headers []*types.Header) bool {
   116  	return reflect.DeepEqual(expected, headers)
   117  }
   118  
   119  func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
   120  	t.Log(`This test requests block headers from the node.`)
   121  	conn, err := s.dialAndPeer(nil)
   122  	if err != nil {
   123  		t.Fatalf("peering failed: %v", err)
   124  	}
   125  	defer conn.Close()
   126  
   127  	// Send headers request.
   128  	req := &eth.GetBlockHeadersPacket{
   129  		RequestId: 33,
   130  		GetBlockHeadersRequest: &eth.GetBlockHeadersRequest{
   131  			Origin:  eth.HashOrNumber{Hash: s.chain.blocks[1].Hash()},
   132  			Amount:  2,
   133  			Skip:    1,
   134  			Reverse: false,
   135  		},
   136  	}
   137  	// Read headers response.
   138  	if err := conn.Write(ethProto, eth.GetBlockHeadersMsg, req); err != nil {
   139  		t.Fatalf("could not write to connection: %v", err)
   140  	}
   141  	headers := new(eth.BlockHeadersPacket)
   142  	if err := conn.ReadMsg(ethProto, eth.BlockHeadersMsg, &headers); err != nil {
   143  		t.Fatalf("error reading msg: %v", err)
   144  	}
   145  	if got, want := headers.RequestId, req.RequestId; got != want {
   146  		t.Fatalf("unexpected request id")
   147  	}
   148  	// Check for correct headers.
   149  	expected, err := s.chain.GetHeaders(req)
   150  	if err != nil {
   151  		t.Fatalf("failed to get headers for given request: %v", err)
   152  	}
   153  	if !headersMatch(expected, headers.BlockHeadersRequest) {
   154  		t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers)
   155  	}
   156  }
   157  
   158  func (s *Suite) TestGetNonexistentBlockHeaders(t *utesting.T) {
   159  	t.Log(`This test sends GetBlockHeaders requests for nonexistent blocks (using max uint64 value)
   160  to check if the node disconnects after receiving multiple invalid requests.`)
   161  	conn, err := s.dialAndPeer(nil)
   162  	if err != nil {
   163  		t.Fatalf("peering failed: %v", err)
   164  	}
   165  	defer conn.Close()
   166  
   167  	// Create request with max uint64 value for a nonexistent block
   168  	badReq := &eth.GetBlockHeadersPacket{
   169  		GetBlockHeadersRequest: &eth.GetBlockHeadersRequest{
   170  			Origin:  eth.HashOrNumber{Number: ^uint64(0)},
   171  			Amount:  1,
   172  			Skip:    0,
   173  			Reverse: false,
   174  		},
   175  	}
   176  
   177  	// Send request 10 times. Some clients are lient on the first few invalids.
   178  	for i := 0; i < 10; i++ {
   179  		badReq.RequestId = uint64(i)
   180  		if err := conn.Write(ethProto, eth.GetBlockHeadersMsg, badReq); err != nil {
   181  			if err == errDisc {
   182  				t.Fatalf("peer disconnected after %d requests", i+1)
   183  			}
   184  			t.Fatalf("write failed: %v", err)
   185  		}
   186  	}
   187  
   188  	// Check if peer disconnects at the end.
   189  	code, _, err := conn.Read()
   190  	if err == errDisc || code == discMsg {
   191  		t.Fatal("peer improperly disconnected")
   192  	}
   193  }
   194  
   195  func (s *Suite) TestSimultaneousRequests(t *utesting.T) {
   196  	t.Log(`This test requests blocks headers from the node, performing two requests
   197  concurrently, with different request IDs.`)
   198  	conn, err := s.dialAndPeer(nil)
   199  	if err != nil {
   200  		t.Fatalf("peering failed: %v", err)
   201  	}
   202  	defer conn.Close()
   203  
   204  	// Create two different requests.
   205  	req1 := &eth.GetBlockHeadersPacket{
   206  		RequestId: uint64(111),
   207  		GetBlockHeadersRequest: &eth.GetBlockHeadersRequest{
   208  			Origin: eth.HashOrNumber{
   209  				Hash: s.chain.blocks[1].Hash(),
   210  			},
   211  			Amount:  2,
   212  			Skip:    1,
   213  			Reverse: false,
   214  		},
   215  	}
   216  	req2 := &eth.GetBlockHeadersPacket{
   217  		RequestId: uint64(222),
   218  		GetBlockHeadersRequest: &eth.GetBlockHeadersRequest{
   219  			Origin: eth.HashOrNumber{
   220  				Hash: s.chain.blocks[1].Hash(),
   221  			},
   222  			Amount:  4,
   223  			Skip:    1,
   224  			Reverse: false,
   225  		},
   226  	}
   227  
   228  	// Send both requests.
   229  	if err := conn.Write(ethProto, eth.GetBlockHeadersMsg, req1); err != nil {
   230  		t.Fatalf("failed to write to connection: %v", err)
   231  	}
   232  	if err := conn.Write(ethProto, eth.GetBlockHeadersMsg, req2); err != nil {
   233  		t.Fatalf("failed to write to connection: %v", err)
   234  	}
   235  
   236  	// Wait for responses.
   237  	headers1 := new(eth.BlockHeadersPacket)
   238  	if err := conn.ReadMsg(ethProto, eth.BlockHeadersMsg, &headers1); err != nil {
   239  		t.Fatalf("error reading block headers msg: %v", err)
   240  	}
   241  	if got, want := headers1.RequestId, req1.RequestId; got != want {
   242  		t.Fatalf("unexpected request id in response: got %d, want %d", got, want)
   243  	}
   244  	headers2 := new(eth.BlockHeadersPacket)
   245  	if err := conn.ReadMsg(ethProto, eth.BlockHeadersMsg, &headers2); err != nil {
   246  		t.Fatalf("error reading block headers msg: %v", err)
   247  	}
   248  	if got, want := headers2.RequestId, req2.RequestId; got != want {
   249  		t.Fatalf("unexpected request id in response: got %d, want %d", got, want)
   250  	}
   251  
   252  	// Check received headers for accuracy.
   253  	if expected, err := s.chain.GetHeaders(req1); err != nil {
   254  		t.Fatalf("failed to get expected headers for request 1: %v", err)
   255  	} else if !headersMatch(expected, headers1.BlockHeadersRequest) {
   256  		t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers1)
   257  	}
   258  	if expected, err := s.chain.GetHeaders(req2); err != nil {
   259  		t.Fatalf("failed to get expected headers for request 2: %v", err)
   260  	} else if !headersMatch(expected, headers2.BlockHeadersRequest) {
   261  		t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers2)
   262  	}
   263  }
   264  
   265  func (s *Suite) TestSameRequestID(t *utesting.T) {
   266  	t.Log(`This test requests block headers, performing two concurrent requests with the
   267  same request ID. The node should handle the request by responding to both requests.`)
   268  	conn, err := s.dialAndPeer(nil)
   269  	if err != nil {
   270  		t.Fatalf("peering failed: %v", err)
   271  	}
   272  	defer conn.Close()
   273  
   274  	// Create two different requests with the same ID.
   275  	reqID := uint64(1234)
   276  	request1 := &eth.GetBlockHeadersPacket{
   277  		RequestId: reqID,
   278  		GetBlockHeadersRequest: &eth.GetBlockHeadersRequest{
   279  			Origin: eth.HashOrNumber{
   280  				Number: 1,
   281  			},
   282  			Amount: 2,
   283  		},
   284  	}
   285  	request2 := &eth.GetBlockHeadersPacket{
   286  		RequestId: reqID,
   287  		GetBlockHeadersRequest: &eth.GetBlockHeadersRequest{
   288  			Origin: eth.HashOrNumber{
   289  				Number: 33,
   290  			},
   291  			Amount: 2,
   292  		},
   293  	}
   294  
   295  	// Send the requests.
   296  	if err = conn.Write(ethProto, eth.GetBlockHeadersMsg, request1); err != nil {
   297  		t.Fatalf("failed to write to connection: %v", err)
   298  	}
   299  	if err = conn.Write(ethProto, eth.GetBlockHeadersMsg, request2); err != nil {
   300  		t.Fatalf("failed to write to connection: %v", err)
   301  	}
   302  
   303  	// Wait for the responses.
   304  	headers1 := new(eth.BlockHeadersPacket)
   305  	if err := conn.ReadMsg(ethProto, eth.BlockHeadersMsg, &headers1); err != nil {
   306  		t.Fatalf("error reading from connection: %v", err)
   307  	}
   308  	if got, want := headers1.RequestId, request1.RequestId; got != want {
   309  		t.Fatalf("unexpected request id: got %d, want %d", got, want)
   310  	}
   311  	headers2 := new(eth.BlockHeadersPacket)
   312  	if err := conn.ReadMsg(ethProto, eth.BlockHeadersMsg, &headers2); err != nil {
   313  		t.Fatalf("error reading from connection: %v", err)
   314  	}
   315  	if got, want := headers2.RequestId, request2.RequestId; got != want {
   316  		t.Fatalf("unexpected request id: got %d, want %d", got, want)
   317  	}
   318  
   319  	// Check if headers match.
   320  	if expected, err := s.chain.GetHeaders(request1); err != nil {
   321  		t.Fatalf("failed to get expected block headers: %v", err)
   322  	} else if !headersMatch(expected, headers1.BlockHeadersRequest) {
   323  		t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers1)
   324  	}
   325  	if expected, err := s.chain.GetHeaders(request2); err != nil {
   326  		t.Fatalf("failed to get expected block headers: %v", err)
   327  	} else if !headersMatch(expected, headers2.BlockHeadersRequest) {
   328  		t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers2)
   329  	}
   330  }
   331  
   332  func (s *Suite) TestZeroRequestID(t *utesting.T) {
   333  	t.Log(`This test sends a GetBlockHeaders message with a request-id of zero,
   334  and expects a response.`)
   335  	conn, err := s.dialAndPeer(nil)
   336  	if err != nil {
   337  		t.Fatalf("peering failed: %v", err)
   338  	}
   339  	defer conn.Close()
   340  
   341  	req := &eth.GetBlockHeadersPacket{
   342  		GetBlockHeadersRequest: &eth.GetBlockHeadersRequest{
   343  			Origin: eth.HashOrNumber{Number: 0},
   344  			Amount: 2,
   345  		},
   346  	}
   347  	// Read headers response.
   348  	if err := conn.Write(ethProto, eth.GetBlockHeadersMsg, req); err != nil {
   349  		t.Fatalf("could not write to connection: %v", err)
   350  	}
   351  	headers := new(eth.BlockHeadersPacket)
   352  	if err := conn.ReadMsg(ethProto, eth.BlockHeadersMsg, &headers); err != nil {
   353  		t.Fatalf("error reading msg: %v", err)
   354  	}
   355  	if got, want := headers.RequestId, req.RequestId; got != want {
   356  		t.Fatalf("unexpected request id")
   357  	}
   358  	if expected, err := s.chain.GetHeaders(req); err != nil {
   359  		t.Fatalf("failed to get expected block headers: %v", err)
   360  	} else if !headersMatch(expected, headers.BlockHeadersRequest) {
   361  		t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers)
   362  	}
   363  }
   364  
   365  func (s *Suite) TestGetBlockBodies(t *utesting.T) {
   366  	t.Log(`This test sends GetBlockBodies requests to the node for known blocks in the test chain.`)
   367  	conn, err := s.dialAndPeer(nil)
   368  	if err != nil {
   369  		t.Fatalf("peering failed: %v", err)
   370  	}
   371  	defer conn.Close()
   372  
   373  	// Create block bodies request.
   374  	req := &eth.GetBlockBodiesPacket{
   375  		RequestId: 55,
   376  		GetBlockBodiesRequest: eth.GetBlockBodiesRequest{
   377  			s.chain.blocks[54].Hash(),
   378  			s.chain.blocks[75].Hash(),
   379  		},
   380  	}
   381  	if err := conn.Write(ethProto, eth.GetBlockBodiesMsg, req); err != nil {
   382  		t.Fatalf("could not write to connection: %v", err)
   383  	}
   384  	// Wait for response.
   385  	resp := new(eth.BlockBodiesPacket)
   386  	if err := conn.ReadMsg(ethProto, eth.BlockBodiesMsg, &resp); err != nil {
   387  		t.Fatalf("error reading block bodies msg: %v", err)
   388  	}
   389  	if got, want := resp.RequestId, req.RequestId; got != want {
   390  		t.Fatalf("unexpected request id in respond", got, want)
   391  	}
   392  	bodies := resp.BlockBodiesResponse
   393  	if len(bodies) != len(req.GetBlockBodiesRequest) {
   394  		t.Fatalf("wrong bodies in response: expected %d bodies, got %d", len(req.GetBlockBodiesRequest), len(bodies))
   395  	}
   396  }
   397  
   398  func (s *Suite) TestGetReceipts(t *utesting.T) {
   399  	t.Log(`This test sends GetReceipts requests to the node for known blocks in the test chain.`)
   400  	conn, err := s.dialAndPeer(nil)
   401  	if err != nil {
   402  		t.Fatalf("peering failed: %v", err)
   403  	}
   404  	defer conn.Close()
   405  
   406  	// Find some blocks containing receipts.
   407  	var hashes = make([]common.Hash, 0, 3)
   408  	for i := range s.chain.Len() {
   409  		block := s.chain.GetBlock(i)
   410  		if len(block.Transactions()) > 0 {
   411  			hashes = append(hashes, block.Hash())
   412  		}
   413  		if len(hashes) == cap(hashes) {
   414  			break
   415  		}
   416  	}
   417  
   418  	// Create block bodies request.
   419  	req := &eth.GetReceiptsPacket{
   420  		RequestId:          66,
   421  		GetReceiptsRequest: (eth.GetReceiptsRequest)(hashes),
   422  	}
   423  	if err := conn.Write(ethProto, eth.GetReceiptsMsg, req); err != nil {
   424  		t.Fatalf("could not write to connection: %v", err)
   425  	}
   426  	// Wait for response.
   427  	resp := new(eth.ReceiptsPacket[*eth.ReceiptList69])
   428  	if err := conn.ReadMsg(ethProto, eth.ReceiptsMsg, &resp); err != nil {
   429  		t.Fatalf("error reading block bodies msg: %v", err)
   430  	}
   431  	if got, want := resp.RequestId, req.RequestId; got != want {
   432  		t.Fatalf("unexpected request id in respond", got, want)
   433  	}
   434  	if len(resp.List) != len(req.GetReceiptsRequest) {
   435  		t.Fatalf("wrong bodies in response: expected %d bodies, got %d", len(req.GetReceiptsRequest), len(resp.List))
   436  	}
   437  }
   438  
   439  // randBuf makes a random buffer size kilobytes large.
   440  func randBuf(size int) []byte {
   441  	buf := make([]byte, size*1024)
   442  	rand.Read(buf)
   443  	return buf
   444  }
   445  
   446  func (s *Suite) TestMaliciousHandshake(t *utesting.T) {
   447  	t.Log(`This test tries to send malicious data during the devp2p handshake, in various ways.`)
   448  
   449  	// Write hello to client.
   450  	var (
   451  		key, _  = crypto.GenerateKey()
   452  		pub0    = crypto.FromECDSAPub(&key.PublicKey)[1:]
   453  		version = eth.ProtocolVersions[0]
   454  	)
   455  	handshakes := []*protoHandshake{
   456  		{
   457  			Version: 5,
   458  			Caps: []p2p.Cap{
   459  				{Name: string(randBuf(2)), Version: version},
   460  			},
   461  			ID: pub0,
   462  		},
   463  		{
   464  			Version: 5,
   465  			Caps: []p2p.Cap{
   466  				{Name: "eth", Version: version},
   467  			},
   468  			ID: append(pub0, byte(0)),
   469  		},
   470  		{
   471  			Version: 5,
   472  			Caps: []p2p.Cap{
   473  				{Name: "eth", Version: version},
   474  			},
   475  			ID: append(pub0, pub0...),
   476  		},
   477  		{
   478  			Version: 5,
   479  			Caps: []p2p.Cap{
   480  				{Name: "eth", Version: version},
   481  			},
   482  			ID: randBuf(2),
   483  		},
   484  		{
   485  			Version: 5,
   486  			Caps: []p2p.Cap{
   487  				{Name: string(randBuf(2)), Version: version},
   488  			},
   489  			ID: randBuf(2),
   490  		},
   491  	}
   492  	for _, handshake := range handshakes {
   493  		conn, err := s.dialAs(key)
   494  		if err != nil {
   495  			t.Fatalf("dial failed: %v", err)
   496  		}
   497  		defer conn.Close()
   498  
   499  		if err := conn.Write(ethProto, handshakeMsg, handshake); err != nil {
   500  			t.Fatalf("could not write to connection: %v", err)
   501  		}
   502  		// Check that the peer disconnected
   503  		for i := 0; i < 2; i++ {
   504  			code, _, err := conn.Read()
   505  			if err != nil {
   506  				// Client may have disconnected without sending disconnect msg.
   507  				continue
   508  			}
   509  			switch code {
   510  			case discMsg:
   511  			case handshakeMsg:
   512  				// Discard one hello as Hello's are sent concurrently
   513  				continue
   514  			default:
   515  				t.Fatalf("unexpected msg: code %d", code)
   516  			}
   517  		}
   518  	}
   519  }
   520  
   521  func (s *Suite) TestBlockRangeUpdateInvalid(t *utesting.T) {
   522  	t.Log(`This test sends an invalid BlockRangeUpdate message to the node and expects to be disconnected.`)
   523  	conn, err := s.dialAndPeer(nil)
   524  	if err != nil {
   525  		t.Fatal(err)
   526  	}
   527  	defer conn.Close()
   528  
   529  	conn.Write(ethProto, eth.BlockRangeUpdateMsg, &eth.BlockRangeUpdatePacket{
   530  		EarliestBlock:   10,
   531  		LatestBlock:     8,
   532  		LatestBlockHash: s.chain.GetBlock(8).Hash(),
   533  	})
   534  
   535  	if code, _, err := conn.Read(); err != nil {
   536  		t.Fatalf("expected disconnect, got err: %v", err)
   537  	} else if code != discMsg {
   538  		t.Fatalf("expected disconnect message, got msg code %d", code)
   539  	}
   540  }
   541  
   542  func (s *Suite) TestBlockRangeUpdateFuture(t *utesting.T) {
   543  	t.Log(`This test sends a BlockRangeUpdate that is beyond the chain head.
   544  The node should accept the update and should not disonnect.`)
   545  	conn, err := s.dialAndPeer(nil)
   546  	if err != nil {
   547  		t.Fatal(err)
   548  	}
   549  	defer conn.Close()
   550  
   551  	head := s.chain.Head().NumberU64()
   552  	var hash common.Hash
   553  	rand.Read(hash[:])
   554  	conn.Write(ethProto, eth.BlockRangeUpdateMsg, &eth.BlockRangeUpdatePacket{
   555  		EarliestBlock:   head + 10,
   556  		LatestBlock:     head + 50,
   557  		LatestBlockHash: hash,
   558  	})
   559  
   560  	// Ensure the node does not disconnect us.
   561  	// Just send a few ping messages.
   562  	for range 10 {
   563  		time.Sleep(100 * time.Millisecond)
   564  		if err := conn.Write(baseProto, pingMsg, []any{}); err != nil {
   565  			t.Fatal("write error:", err)
   566  		}
   567  		code, _, err := conn.Read()
   568  		switch {
   569  		case err != nil:
   570  			t.Fatal("read error:", err)
   571  		case code == discMsg:
   572  			t.Fatal("got disconnect")
   573  		case code == pongMsg:
   574  		}
   575  	}
   576  }
   577  
   578  func (s *Suite) TestBlockRangeUpdateHistoryExp(t *utesting.T) {
   579  	t.Log(`This test sends a BlockRangeUpdate announcing incomplete (expired) history.
   580  The node should accept the update and should not disonnect.`)
   581  	conn, err := s.dialAndPeer(nil)
   582  	if err != nil {
   583  		t.Fatal(err)
   584  	}
   585  	defer conn.Close()
   586  
   587  	head := s.chain.Head()
   588  	conn.Write(ethProto, eth.BlockRangeUpdateMsg, &eth.BlockRangeUpdatePacket{
   589  		EarliestBlock:   head.NumberU64() - 10,
   590  		LatestBlock:     head.NumberU64(),
   591  		LatestBlockHash: head.Hash(),
   592  	})
   593  
   594  	// Ensure the node does not disconnect us.
   595  	// Just send a few ping messages.
   596  	for range 10 {
   597  		time.Sleep(100 * time.Millisecond)
   598  		if err := conn.Write(baseProto, pingMsg, []any{}); err != nil {
   599  			t.Fatal("write error:", err)
   600  		}
   601  		code, _, err := conn.Read()
   602  		switch {
   603  		case err != nil:
   604  			t.Fatal("read error:", err)
   605  		case code == discMsg:
   606  			t.Fatal("got disconnect")
   607  		case code == pongMsg:
   608  		}
   609  	}
   610  }
   611  
   612  func (s *Suite) TestTransaction(t *utesting.T) {
   613  	t.Log(`This test sends a valid transaction to the node and checks if the
   614  transaction gets propagated.`)
   615  
   616  	// Nudge client out of syncing mode to accept pending txs.
   617  	if err := s.engine.sendForkchoiceUpdated(); err != nil {
   618  		t.Fatalf("failed to send next block: %v", err)
   619  	}
   620  	from, nonce := s.chain.GetSender(0)
   621  	inner := &types.DynamicFeeTx{
   622  		ChainID:   s.chain.config.ChainID,
   623  		Nonce:     nonce,
   624  		GasTipCap: common.Big1,
   625  		GasFeeCap: s.chain.Head().BaseFee(),
   626  		Gas:       30000,
   627  		To:        &common.Address{0xaa},
   628  		Value:     common.Big1,
   629  	}
   630  	tx, err := s.chain.SignTx(from, types.NewTx(inner))
   631  	if err != nil {
   632  		t.Fatalf("failed to sign tx: %v", err)
   633  	}
   634  	if err := s.sendTxs(t, []*types.Transaction{tx}); err != nil {
   635  		t.Fatal(err)
   636  	}
   637  	s.chain.IncNonce(from, 1)
   638  }
   639  
   640  func (s *Suite) TestInvalidTxs(t *utesting.T) {
   641  	t.Log(`This test sends several kinds of invalid transactions and checks that the node
   642  does not propagate them.`)
   643  
   644  	// Nudge client out of syncing mode to accept pending txs.
   645  	if err := s.engine.sendForkchoiceUpdated(); err != nil {
   646  		t.Fatalf("failed to send next block: %v", err)
   647  	}
   648  
   649  	from, nonce := s.chain.GetSender(0)
   650  	inner := &types.DynamicFeeTx{
   651  		ChainID:   s.chain.config.ChainID,
   652  		Nonce:     nonce,
   653  		GasTipCap: common.Big1,
   654  		GasFeeCap: s.chain.Head().BaseFee(),
   655  		Gas:       30000,
   656  		To:        &common.Address{0xaa},
   657  	}
   658  	tx, err := s.chain.SignTx(from, types.NewTx(inner))
   659  	if err != nil {
   660  		t.Fatalf("failed to sign tx: %v", err)
   661  	}
   662  	if err := s.sendTxs(t, []*types.Transaction{tx}); err != nil {
   663  		t.Fatalf("failed to send txs: %v", err)
   664  	}
   665  	s.chain.IncNonce(from, 1)
   666  
   667  	inners := []*types.DynamicFeeTx{
   668  		// Nonce already used
   669  		{
   670  			ChainID:   s.chain.config.ChainID,
   671  			Nonce:     nonce - 1,
   672  			GasTipCap: common.Big1,
   673  			GasFeeCap: s.chain.Head().BaseFee(),
   674  			Gas:       100000,
   675  		},
   676  		// Value exceeds balance
   677  		{
   678  			Nonce:     nonce,
   679  			GasTipCap: common.Big1,
   680  			GasFeeCap: s.chain.Head().BaseFee(),
   681  			Gas:       100000,
   682  			Value:     s.chain.Balance(from),
   683  		},
   684  		// Gas limit too low
   685  		{
   686  			Nonce:     nonce,
   687  			GasTipCap: common.Big1,
   688  			GasFeeCap: s.chain.Head().BaseFee(),
   689  			Gas:       1337,
   690  		},
   691  		// Code size too large
   692  		{
   693  			Nonce:     nonce,
   694  			GasTipCap: common.Big1,
   695  			GasFeeCap: s.chain.Head().BaseFee(),
   696  			Data:      randBuf(50),
   697  			Gas:       1_000_000,
   698  		},
   699  		// Data too large
   700  		{
   701  			Nonce:     nonce,
   702  			GasTipCap: common.Big1,
   703  			GasFeeCap: s.chain.Head().BaseFee(),
   704  			To:        &common.Address{0xaa},
   705  			Data:      randBuf(128),
   706  			Gas:       5_000_000,
   707  		},
   708  	}
   709  
   710  	var txs []*types.Transaction
   711  	for _, inner := range inners {
   712  		tx, err := s.chain.SignTx(from, types.NewTx(inner))
   713  		if err != nil {
   714  			t.Fatalf("failed to sign tx: %v", err)
   715  		}
   716  		txs = append(txs, tx)
   717  	}
   718  	if err := s.sendInvalidTxs(t, txs); err != nil {
   719  		t.Fatalf("failed to send invalid txs: %v", err)
   720  	}
   721  }
   722  
   723  func (s *Suite) TestLargeTxRequest(t *utesting.T) {
   724  	t.Log(`This test first send ~2000 transactions to the node, then requests them
   725  on another peer connection using GetPooledTransactions.`)
   726  
   727  	// Nudge client out of syncing mode to accept pending txs.
   728  	if err := s.engine.sendForkchoiceUpdated(); err != nil {
   729  		t.Fatalf("failed to send next block: %v", err)
   730  	}
   731  
   732  	// Generate many transactions to seed target with.
   733  	var (
   734  		from, nonce = s.chain.GetSender(1)
   735  		count       = 2000
   736  		txs         []*types.Transaction
   737  		hashes      []common.Hash
   738  		set         = make(map[common.Hash]struct{})
   739  	)
   740  	for i := 0; i < count; i++ {
   741  		inner := &types.DynamicFeeTx{
   742  			ChainID:   s.chain.config.ChainID,
   743  			Nonce:     nonce + uint64(i),
   744  			GasTipCap: common.Big1,
   745  			GasFeeCap: s.chain.Head().BaseFee(),
   746  			Gas:       75000,
   747  		}
   748  		tx, err := s.chain.SignTx(from, types.NewTx(inner))
   749  		if err != nil {
   750  			t.Fatalf("failed to sign tx: err")
   751  		}
   752  		txs = append(txs, tx)
   753  		set[tx.Hash()] = struct{}{}
   754  		hashes = append(hashes, tx.Hash())
   755  	}
   756  	s.chain.IncNonce(from, uint64(count))
   757  
   758  	// Send txs.
   759  	if err := s.sendTxs(t, txs); err != nil {
   760  		t.Fatalf("failed to send txs: %v", err)
   761  	}
   762  
   763  	// Set up receive connection to ensure node is peered with the receiving
   764  	// connection before tx request is sent.
   765  	conn, err := s.dial()
   766  	if err != nil {
   767  		t.Fatalf("dial failed: %v", err)
   768  	}
   769  	defer conn.Close()
   770  	if err = conn.peer(s.chain, nil); err != nil {
   771  		t.Fatalf("peering failed: %v", err)
   772  	}
   773  	// Create and send pooled tx request.
   774  	req := &eth.GetPooledTransactionsPacket{
   775  		RequestId:                    1234,
   776  		GetPooledTransactionsRequest: hashes,
   777  	}
   778  	if err = conn.Write(ethProto, eth.GetPooledTransactionsMsg, req); err != nil {
   779  		t.Fatalf("could not write to conn: %v", err)
   780  	}
   781  	// Check that all received transactions match those that were sent to node.
   782  	msg := new(eth.PooledTransactionsPacket)
   783  	if err := conn.ReadMsg(ethProto, eth.PooledTransactionsMsg, &msg); err != nil {
   784  		t.Fatalf("error reading from connection: %v", err)
   785  	}
   786  	if got, want := msg.RequestId, req.RequestId; got != want {
   787  		t.Fatalf("unexpected request id in response: got %d, want %d", got, want)
   788  	}
   789  	for _, got := range msg.PooledTransactionsResponse {
   790  		if _, exists := set[got.Hash()]; !exists {
   791  			t.Fatalf("unexpected tx received: %v", got.Hash())
   792  		}
   793  	}
   794  }
   795  
   796  func (s *Suite) TestNewPooledTxs(t *utesting.T) {
   797  	t.Log(`This test announces transaction hashes to the node and expects it to fetch
   798  the transactions using a GetPooledTransactions request.`)
   799  
   800  	// Nudge client out of syncing mode to accept pending txs.
   801  	if err := s.engine.sendForkchoiceUpdated(); err != nil {
   802  		t.Fatalf("failed to send next block: %v", err)
   803  	}
   804  
   805  	var (
   806  		count       = 50
   807  		from, nonce = s.chain.GetSender(1)
   808  		hashes      = make([]common.Hash, count)
   809  		txTypes     = make([]byte, count)
   810  		sizes       = make([]uint32, count)
   811  	)
   812  	for i := 0; i < count; i++ {
   813  		inner := &types.DynamicFeeTx{
   814  			ChainID:   s.chain.config.ChainID,
   815  			Nonce:     nonce + uint64(i),
   816  			GasTipCap: common.Big1,
   817  			GasFeeCap: s.chain.Head().BaseFee(),
   818  			Gas:       75000,
   819  		}
   820  		tx, err := s.chain.SignTx(from, types.NewTx(inner))
   821  		if err != nil {
   822  			t.Fatalf("failed to sign tx: err")
   823  		}
   824  		hashes[i] = tx.Hash()
   825  		txTypes[i] = tx.Type()
   826  		sizes[i] = uint32(tx.Size())
   827  	}
   828  	s.chain.IncNonce(from, uint64(count))
   829  
   830  	// Connect to peer.
   831  	conn, err := s.dial()
   832  	if err != nil {
   833  		t.Fatalf("dial failed: %v", err)
   834  	}
   835  	defer conn.Close()
   836  	if err = conn.peer(s.chain, nil); err != nil {
   837  		t.Fatalf("peering failed: %v", err)
   838  	}
   839  
   840  	// Send announcement.
   841  	ann := eth.NewPooledTransactionHashesPacket{Types: txTypes, Sizes: sizes, Hashes: hashes}
   842  	err = conn.Write(ethProto, eth.NewPooledTransactionHashesMsg, ann)
   843  	if err != nil {
   844  		t.Fatalf("failed to write to connection: %v", err)
   845  	}
   846  
   847  	// Wait for GetPooledTxs request.
   848  	for {
   849  		msg, err := conn.ReadEth()
   850  		if err != nil {
   851  			t.Fatalf("failed to read eth msg: %v", err)
   852  		}
   853  		switch msg := msg.(type) {
   854  		case *eth.GetPooledTransactionsPacket:
   855  			if len(msg.GetPooledTransactionsRequest) != len(hashes) {
   856  				t.Fatalf("unexpected number of txs requested: wanted %d, got %d", len(hashes), len(msg.GetPooledTransactionsRequest))
   857  			}
   858  			return
   859  		case *eth.NewPooledTransactionHashesPacket:
   860  			continue
   861  		case *eth.TransactionsPacket:
   862  			continue
   863  		default:
   864  			t.Fatalf("unexpected %s", pretty.Sdump(msg))
   865  		}
   866  	}
   867  }
   868  
   869  func makeSidecar(data ...byte) *types.BlobTxSidecar {
   870  	var (
   871  		blobs       = make([]kzg4844.Blob, len(data))
   872  		commitments []kzg4844.Commitment
   873  		proofs      []kzg4844.Proof
   874  	)
   875  	for i := range blobs {
   876  		blobs[i][0] = data[i]
   877  		c, _ := kzg4844.BlobToCommitment(&blobs[i])
   878  		p, _ := kzg4844.ComputeBlobProof(&blobs[i], c)
   879  		commitments = append(commitments, c)
   880  		proofs = append(proofs, p)
   881  	}
   882  	return &types.BlobTxSidecar{
   883  		Blobs:       blobs,
   884  		Commitments: commitments,
   885  		Proofs:      proofs,
   886  	}
   887  }
   888  
   889  func (s *Suite) makeBlobTxs(count, blobs int, discriminator byte) (txs types.Transactions) {
   890  	from, nonce := s.chain.GetSender(5)
   891  	for i := 0; i < count; i++ {
   892  		// Make blob data, max of 2 blobs per tx.
   893  		blobdata := make([]byte, blobs%3)
   894  		for i := range blobdata {
   895  			blobdata[i] = discriminator
   896  			blobs -= 1
   897  		}
   898  		inner := &types.BlobTx{
   899  			ChainID:    uint256.MustFromBig(s.chain.config.ChainID),
   900  			Nonce:      nonce + uint64(i),
   901  			GasTipCap:  uint256.NewInt(1),
   902  			GasFeeCap:  uint256.MustFromBig(s.chain.Head().BaseFee()),
   903  			Gas:        100000,
   904  			BlobFeeCap: uint256.MustFromBig(eip4844.CalcBlobFee(s.chain.config, s.chain.Head().Header())),
   905  			BlobHashes: makeSidecar(blobdata...).BlobHashes(),
   906  			Sidecar:    makeSidecar(blobdata...),
   907  		}
   908  		tx, err := s.chain.SignTx(from, types.NewTx(inner))
   909  		if err != nil {
   910  			panic("blob tx signing failed")
   911  		}
   912  		txs = append(txs, tx)
   913  	}
   914  	return txs
   915  }
   916  
   917  func (s *Suite) TestBlobViolations(t *utesting.T) {
   918  	t.Log(`This test sends some invalid blob tx announcements and expects the node to disconnect.`)
   919  
   920  	if err := s.engine.sendForkchoiceUpdated(); err != nil {
   921  		t.Fatalf("send fcu failed: %v", err)
   922  	}
   923  	// Create blob txs for each tests with unique tx hashes.
   924  	var (
   925  		t1 = s.makeBlobTxs(2, 3, 0x1)
   926  		t2 = s.makeBlobTxs(2, 3, 0x2)
   927  	)
   928  	for _, test := range []struct {
   929  		ann  eth.NewPooledTransactionHashesPacket
   930  		resp eth.PooledTransactionsResponse
   931  	}{
   932  		// Invalid tx size.
   933  		{
   934  			ann: eth.NewPooledTransactionHashesPacket{
   935  				Types:  []byte{types.BlobTxType, types.BlobTxType},
   936  				Sizes:  []uint32{uint32(t1[0].Size()), uint32(t1[1].Size() + 10)},
   937  				Hashes: []common.Hash{t1[0].Hash(), t1[1].Hash()},
   938  			},
   939  			resp: eth.PooledTransactionsResponse(t1),
   940  		},
   941  		// Wrong tx type.
   942  		{
   943  			ann: eth.NewPooledTransactionHashesPacket{
   944  				Types:  []byte{types.DynamicFeeTxType, types.BlobTxType},
   945  				Sizes:  []uint32{uint32(t2[0].Size()), uint32(t2[1].Size())},
   946  				Hashes: []common.Hash{t2[0].Hash(), t2[1].Hash()},
   947  			},
   948  			resp: eth.PooledTransactionsResponse(t2),
   949  		},
   950  	} {
   951  		conn, err := s.dial()
   952  		if err != nil {
   953  			t.Fatalf("dial fail: %v", err)
   954  		}
   955  		if err := conn.peer(s.chain, nil); err != nil {
   956  			t.Fatalf("peering failed: %v", err)
   957  		}
   958  		if err := conn.Write(ethProto, eth.NewPooledTransactionHashesMsg, test.ann); err != nil {
   959  			t.Fatalf("sending announcement failed: %v", err)
   960  		}
   961  		req := new(eth.GetPooledTransactionsPacket)
   962  		if err := conn.ReadMsg(ethProto, eth.GetPooledTransactionsMsg, req); err != nil {
   963  			t.Fatalf("reading pooled tx request failed: %v", err)
   964  		}
   965  		resp := eth.PooledTransactionsPacket{RequestId: req.RequestId, PooledTransactionsResponse: test.resp}
   966  		if err := conn.Write(ethProto, eth.PooledTransactionsMsg, resp); err != nil {
   967  			t.Fatalf("writing pooled tx response failed: %v", err)
   968  		}
   969  		if code, _, err := conn.Read(); err != nil {
   970  			t.Fatalf("expected disconnect on blob violation, got err: %v", err)
   971  		} else if code != discMsg {
   972  			if code == protoOffset(ethProto)+eth.NewPooledTransactionHashesMsg {
   973  				// sometimes we'll get a blob transaction hashes announcement before the disconnect
   974  				// because blob transactions are scheduled to be fetched right away.
   975  				if code, _, err = conn.Read(); err != nil {
   976  					t.Fatalf("expected disconnect on blob violation, got err on second read: %v", err)
   977  				}
   978  			}
   979  			if code != discMsg {
   980  				t.Fatalf("expected disconnect on blob violation, got msg code: %d", code)
   981  			}
   982  		}
   983  		conn.Close()
   984  	}
   985  }
   986  
   987  // mangleSidecar returns a copy of the given blob transaction where the sidecar
   988  // data has been modified to produce a different commitment hash.
   989  func mangleSidecar(tx *types.Transaction) *types.Transaction {
   990  	sidecar := tx.BlobTxSidecar()
   991  	copy := types.BlobTxSidecar{
   992  		Blobs:       append([]kzg4844.Blob{}, sidecar.Blobs...),
   993  		Commitments: append([]kzg4844.Commitment{}, sidecar.Commitments...),
   994  		Proofs:      append([]kzg4844.Proof{}, sidecar.Proofs...),
   995  	}
   996  	// zero the first commitment to alter the sidecar hash
   997  	copy.Commitments[0] = kzg4844.Commitment{}
   998  	return tx.WithBlobTxSidecar(&copy)
   999  }
  1000  
  1001  func (s *Suite) TestBlobTxWithoutSidecar(t *utesting.T) {
  1002  	t.Log(`This test checks that a blob transaction first advertised/transmitted without blobs will result in the sending peer being disconnected, and the full transaction should be successfully retrieved from another peer.`)
  1003  	tx := s.makeBlobTxs(1, 2, 42)[0]
  1004  	badTx := tx.WithoutBlobTxSidecar()
  1005  	s.testBadBlobTx(t, tx, badTx)
  1006  }
  1007  
  1008  func (s *Suite) TestBlobTxWithMismatchedSidecar(t *utesting.T) {
  1009  	t.Log(`This test checks that a blob transaction first advertised/transmitted without blobs, whose commitment don't correspond to the blob_versioned_hashes in the transaction, will result in the sending peer being disconnected, and the full transaction should be successfully retrieved from another peer.`)
  1010  	tx := s.makeBlobTxs(1, 2, 43)[0]
  1011  	badTx := mangleSidecar(tx)
  1012  	s.testBadBlobTx(t, tx, badTx)
  1013  }
  1014  
  1015  // readUntil reads eth protocol messages until a message of the target type is
  1016  // received.  It returns an error if there is a disconnect, or if the context
  1017  // is cancelled before a message of the desired type can be read.
  1018  func readUntil[T any](ctx context.Context, conn *Conn) (*T, error) {
  1019  	for {
  1020  		select {
  1021  		case <-ctx.Done():
  1022  			return nil, context.Canceled
  1023  		default:
  1024  		}
  1025  		received, err := conn.ReadEth()
  1026  		if err != nil {
  1027  			if err == errDisc {
  1028  				return nil, errDisc
  1029  			}
  1030  			continue
  1031  		}
  1032  
  1033  		switch res := received.(type) {
  1034  		case *T:
  1035  			return res, nil
  1036  		}
  1037  	}
  1038  }
  1039  
  1040  // readUntilDisconnect reads eth protocol messages until the peer disconnects.
  1041  // It returns whether the peer disconnects in the next 100ms.
  1042  func readUntilDisconnect(conn *Conn) (disconnected bool) {
  1043  	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
  1044  	defer cancel()
  1045  	_, err := readUntil[struct{}](ctx, conn)
  1046  	return err == errDisc
  1047  }
  1048  
  1049  func (s *Suite) testBadBlobTx(t *utesting.T, tx *types.Transaction, badTx *types.Transaction) {
  1050  	stage1, stage2, stage3 := new(sync.WaitGroup), new(sync.WaitGroup), new(sync.WaitGroup)
  1051  	stage1.Add(1)
  1052  	stage2.Add(1)
  1053  	stage3.Add(1)
  1054  
  1055  	errc := make(chan error)
  1056  
  1057  	badPeer := func() {
  1058  		// announce the correct hash from the bad peer.
  1059  		// when the transaction is first requested before transmitting it from the bad peer,
  1060  		// trigger step 2: connection and announcement by good peers
  1061  
  1062  		conn, err := s.dial()
  1063  		if err != nil {
  1064  			errc <- fmt.Errorf("dial fail: %v", err)
  1065  			return
  1066  		}
  1067  		defer conn.Close()
  1068  
  1069  		if err := conn.peer(s.chain, nil); err != nil {
  1070  			errc <- fmt.Errorf("bad peer: peering failed: %v", err)
  1071  			return
  1072  		}
  1073  
  1074  		ann := eth.NewPooledTransactionHashesPacket{
  1075  			Types:  []byte{types.BlobTxType},
  1076  			Sizes:  []uint32{uint32(badTx.Size())},
  1077  			Hashes: []common.Hash{badTx.Hash()},
  1078  		}
  1079  
  1080  		if err := conn.Write(ethProto, eth.NewPooledTransactionHashesMsg, ann); err != nil {
  1081  			errc <- fmt.Errorf("sending announcement failed: %v", err)
  1082  			return
  1083  		}
  1084  
  1085  		req, err := readUntil[eth.GetPooledTransactionsPacket](context.Background(), conn)
  1086  		if err != nil {
  1087  			errc <- fmt.Errorf("failed to read GetPooledTransactions message: %v", err)
  1088  			return
  1089  		}
  1090  
  1091  		stage1.Done()
  1092  		stage2.Wait()
  1093  
  1094  		// the good peer is connected, and has announced the tx.
  1095  		// proceed to send the incorrect one from the bad peer.
  1096  
  1097  		resp := eth.PooledTransactionsPacket{RequestId: req.RequestId, PooledTransactionsResponse: eth.PooledTransactionsResponse(types.Transactions{badTx})}
  1098  		if err := conn.Write(ethProto, eth.PooledTransactionsMsg, resp); err != nil {
  1099  			errc <- fmt.Errorf("writing pooled tx response failed: %v", err)
  1100  			return
  1101  		}
  1102  		if !readUntilDisconnect(conn) {
  1103  			errc <- fmt.Errorf("expected bad peer to be disconnected")
  1104  			return
  1105  		}
  1106  		stage3.Done()
  1107  	}
  1108  
  1109  	goodPeer := func() {
  1110  		stage1.Wait()
  1111  
  1112  		conn, err := s.dial()
  1113  		if err != nil {
  1114  			errc <- fmt.Errorf("dial fail: %v", err)
  1115  			return
  1116  		}
  1117  		defer conn.Close()
  1118  
  1119  		if err := conn.peer(s.chain, nil); err != nil {
  1120  			errc <- fmt.Errorf("peering failed: %v", err)
  1121  			return
  1122  		}
  1123  
  1124  		ann := eth.NewPooledTransactionHashesPacket{
  1125  			Types:  []byte{types.BlobTxType},
  1126  			Sizes:  []uint32{uint32(tx.Size())},
  1127  			Hashes: []common.Hash{tx.Hash()},
  1128  		}
  1129  
  1130  		if err := conn.Write(ethProto, eth.NewPooledTransactionHashesMsg, ann); err != nil {
  1131  			errc <- fmt.Errorf("sending announcement failed: %v", err)
  1132  			return
  1133  		}
  1134  
  1135  		// wait until the bad peer has transmitted the incorrect transaction
  1136  		stage2.Done()
  1137  		stage3.Wait()
  1138  
  1139  		// the bad peer has transmitted the bad tx, and been disconnected.
  1140  		// transmit the same tx but with correct sidecar from the good peer.
  1141  
  1142  		var req *eth.GetPooledTransactionsPacket
  1143  		req, err = readUntil[eth.GetPooledTransactionsPacket](context.Background(), conn)
  1144  		if err != nil {
  1145  			errc <- fmt.Errorf("reading pooled tx request failed: %v", err)
  1146  			return
  1147  		}
  1148  
  1149  		if req.GetPooledTransactionsRequest[0] != tx.Hash() {
  1150  			errc <- fmt.Errorf("requested unknown tx hash")
  1151  			return
  1152  		}
  1153  
  1154  		resp := eth.PooledTransactionsPacket{RequestId: req.RequestId, PooledTransactionsResponse: eth.PooledTransactionsResponse(types.Transactions{tx})}
  1155  		if err := conn.Write(ethProto, eth.PooledTransactionsMsg, resp); err != nil {
  1156  			errc <- fmt.Errorf("writing pooled tx response failed: %v", err)
  1157  			return
  1158  		}
  1159  		if readUntilDisconnect(conn) {
  1160  			errc <- fmt.Errorf("unexpected disconnect")
  1161  			return
  1162  		}
  1163  		close(errc)
  1164  	}
  1165  
  1166  	if err := s.engine.sendForkchoiceUpdated(); err != nil {
  1167  		t.Fatalf("send fcu failed: %v", err)
  1168  	}
  1169  
  1170  	go goodPeer()
  1171  	go badPeer()
  1172  	err := <-errc
  1173  	if err != nil {
  1174  		t.Fatalf("%v", err)
  1175  	}
  1176  }