2020-10-07 10:22:44 -05:00
// Copyright 2020 The go-ethereum Authors
2022-05-24 13:39:40 -05:00
// This file is part of go-ethereum.
2020-10-07 10:22:44 -05:00
//
2022-05-24 13:39:40 -05:00
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
2020-10-07 10:22:44 -05:00
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
2022-05-24 13:39:40 -05:00
// go-ethereum is distributed in the hope that it will be useful,
2020-10-07 10:22:44 -05:00
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2022-05-24 13:39:40 -05:00
// GNU General Public License for more details.
2020-10-07 10:22:44 -05:00
//
2022-05-24 13:39:40 -05:00
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
2020-10-07 10:22:44 -05:00
2020-09-23 08:18:17 -05:00
package ethtest
import (
2025-03-01 07:10:38 -06:00
"context"
2023-12-20 10:23:48 -06:00
"crypto/rand"
2025-03-01 07:10:38 -06:00
"fmt"
2023-12-20 10:23:48 -06:00
"reflect"
2025-03-01 07:10:38 -06:00
"sync"
"time"
2020-09-23 08:18:17 -05:00
2021-05-25 16:09:11 -05:00
"github.com/ethereum/go-ethereum/common"
2023-12-20 10:23:48 -06:00
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
2021-02-16 08:23:03 -06:00
"github.com/ethereum/go-ethereum/eth/protocols/eth"
2020-09-23 08:18:17 -05:00
"github.com/ethereum/go-ethereum/internal/utesting"
2023-12-20 10:23:48 -06:00
"github.com/ethereum/go-ethereum/p2p"
2020-09-23 08:18:17 -05:00
"github.com/ethereum/go-ethereum/p2p/enode"
2023-12-20 10:23:48 -06:00
"github.com/holiman/uint256"
2020-09-23 08:18:17 -05:00
)
2021-05-25 16:09:11 -05:00
// Suite represents a structure used to test a node's conformance
// to the eth protocol.
2020-09-23 08:18:17 -05:00
type Suite struct {
2023-12-20 10:23:48 -06:00
Dest * enode . Node
chain * Chain
engine * EngineClient
2020-09-23 08:18:17 -05:00
}
// NewSuite creates and returns a new eth-test suite that can
// be used to test the given node against the given blockchain
// data.
2023-12-20 10:23:48 -06:00
func NewSuite ( dest * enode . Node , chainDir , engineURL , jwt string ) ( * Suite , error ) {
chain , err := NewChain ( chainDir )
2020-09-23 08:18:17 -05:00
if err != nil {
2021-02-25 11:36:01 -06:00
return nil , err
2020-09-23 08:18:17 -05:00
}
2023-12-20 10:23:48 -06:00
engine , err := NewEngineClient ( chainDir , engineURL , jwt )
if err != nil {
return nil , err
}
2020-09-23 08:18:17 -05:00
return & Suite {
2023-12-20 10:23:48 -06:00
Dest : dest ,
chain : chain ,
engine : engine ,
2021-02-25 11:36:01 -06:00
} , nil
2020-09-23 08:18:17 -05:00
}
2022-08-02 07:48:55 -05:00
func ( s * Suite ) EthTests ( ) [ ] utesting . Test {
2020-09-23 08:18:17 -05:00
return [ ] utesting . Test {
2021-02-25 11:36:01 -06:00
// status
2024-02-15 12:43:37 -06:00
{ Name : "Status" , Fn : s . TestStatus } ,
2021-02-25 11:36:01 -06:00
// get block headers
2024-02-15 12:43:37 -06:00
{ Name : "GetBlockHeaders" , Fn : s . TestGetBlockHeaders } ,
{ Name : "SimultaneousRequests" , Fn : s . TestSimultaneousRequests } ,
{ Name : "SameRequestID" , Fn : s . TestSameRequestID } ,
{ Name : "ZeroRequestID" , Fn : s . TestZeroRequestID } ,
2021-02-25 11:36:01 -06:00
// get block bodies
2024-02-15 12:43:37 -06:00
{ Name : "GetBlockBodies" , Fn : s . TestGetBlockBodies } ,
2023-12-20 10:23:48 -06:00
// // malicious handshakes + status
2024-02-15 12:43:37 -06:00
{ Name : "MaliciousHandshake" , Fn : s . TestMaliciousHandshake } ,
2021-02-25 11:36:01 -06:00
// test transactions
2024-02-15 12:43:37 -06:00
{ Name : "LargeTxRequest" , Fn : s . TestLargeTxRequest , Slow : true } ,
{ Name : "Transaction" , Fn : s . TestTransaction } ,
{ Name : "InvalidTxs" , Fn : s . TestInvalidTxs } ,
{ Name : "NewPooledTxs" , Fn : s . TestNewPooledTxs } ,
{ Name : "BlobViolations" , Fn : s . TestBlobViolations } ,
2025-03-01 07:10:38 -06:00
{ Name : "TestBlobTxWithoutSidecar" , Fn : s . TestBlobTxWithoutSidecar } ,
{ Name : "TestBlobTxWithMismatchedSidecar" , Fn : s . TestBlobTxWithMismatchedSidecar } ,
2021-03-19 09:15:39 -05:00
}
}
2022-02-04 08:24:32 -06:00
func ( s * Suite ) SnapTests ( ) [ ] utesting . Test {
return [ ] utesting . Test {
2023-12-20 10:23:48 -06:00
{ Name : "Status" , Fn : s . TestSnapStatus } ,
{ Name : "AccountRange" , Fn : s . TestSnapGetAccountRange } ,
{ Name : "GetByteCodes" , Fn : s . TestSnapGetByteCodes } ,
{ Name : "GetTrieNodes" , Fn : s . TestSnapTrieNodes } ,
{ Name : "GetStorageRanges" , Fn : s . TestSnapGetStorageRanges } ,
2022-02-04 08:24:32 -06:00
}
}
2022-08-02 07:48:55 -05:00
func ( s * Suite ) TestStatus ( t * utesting . T ) {
2024-02-15 12:43:37 -06:00
t . Log ( ` This test is just a sanity check. It performs an eth protocol handshake. ` )
2020-09-23 08:18:17 -05:00
conn , err := s . dial ( )
if err != nil {
2021-05-25 16:09:11 -05:00
t . Fatalf ( "dial failed: %v" , err )
2020-09-23 08:18:17 -05:00
}
2021-04-23 04:15:42 -05:00
defer conn . Close ( )
2021-05-25 16:09:11 -05:00
if err := conn . peer ( s . chain , nil ) ; err != nil {
t . Fatalf ( "peering failed: %v" , err )
2020-09-23 08:18:17 -05:00
}
}
2023-12-20 10:23:48 -06:00
// headersMatch returns whether the received headers match the given request
func headersMatch ( expected [ ] * types . Header , headers [ ] * types . Header ) bool {
return reflect . DeepEqual ( expected , headers )
}
2022-08-02 07:48:55 -05:00
func ( s * Suite ) TestGetBlockHeaders ( t * utesting . T ) {
2024-02-15 12:43:37 -06:00
t . Log ( ` This test requests block headers from the node. ` )
2020-09-23 08:18:17 -05:00
conn , err := s . dial ( )
if err != nil {
2021-05-25 16:09:11 -05:00
t . Fatalf ( "dial failed: %v" , err )
2020-09-23 08:18:17 -05:00
}
2021-04-23 04:15:42 -05:00
defer conn . Close ( )
2021-05-25 16:09:11 -05:00
if err = conn . peer ( s . chain , nil ) ; err != nil {
t . Fatalf ( "peering failed: %v" , err )
}
2023-12-20 10:23:48 -06:00
// Send headers request.
req := & eth . GetBlockHeadersPacket {
RequestId : 33 ,
2023-10-03 07:03:19 -05:00
GetBlockHeadersRequest : & eth . GetBlockHeadersRequest {
2022-08-02 07:48:55 -05:00
Origin : eth . HashOrNumber { Hash : s . chain . blocks [ 1 ] . Hash ( ) } ,
Amount : 2 ,
Skip : 1 ,
Reverse : false ,
2020-09-23 08:18:17 -05:00
} ,
}
2023-12-20 10:23:48 -06:00
// Read headers response.
if err := conn . Write ( ethProto , eth . GetBlockHeadersMsg , req ) ; err != nil {
t . Fatalf ( "could not write to connection: %v" , err )
}
headers := new ( eth . BlockHeadersPacket )
if err := conn . ReadMsg ( ethProto , eth . BlockHeadersMsg , & headers ) ; err != nil {
t . Fatalf ( "error reading msg: %v" , err )
2021-05-25 16:09:11 -05:00
}
2023-12-20 10:23:48 -06:00
if got , want := headers . RequestId , req . RequestId ; got != want {
t . Fatalf ( "unexpected request id" )
}
// Check for correct headers.
2022-08-02 07:48:55 -05:00
expected , err := s . chain . GetHeaders ( req )
2021-05-25 16:09:11 -05:00
if err != nil {
t . Fatalf ( "failed to get headers for given request: %v" , err )
}
2023-12-20 10:23:48 -06:00
if ! headersMatch ( expected , headers . BlockHeadersRequest ) {
2021-05-25 16:09:11 -05:00
t . Fatalf ( "header mismatch: \nexpected %v \ngot %v" , expected , headers )
}
}
2020-09-23 08:18:17 -05:00
2022-08-02 07:48:55 -05:00
func ( s * Suite ) TestSimultaneousRequests ( t * utesting . T ) {
2024-02-15 12:43:37 -06:00
t . Log ( ` This test requests blocks headers from the node , performing two requests
concurrently , with different request IDs . ` )
2022-08-02 07:48:55 -05:00
conn , err := s . dial ( )
2021-05-25 16:09:11 -05:00
if err != nil {
t . Fatalf ( "dial failed: %v" , err )
}
defer conn . Close ( )
if err := conn . peer ( s . chain , nil ) ; err != nil {
t . Fatalf ( "peering failed: %v" , err )
}
2022-08-02 07:48:55 -05:00
2023-12-20 10:23:48 -06:00
// Create two different requests.
req1 := & eth . GetBlockHeadersPacket {
2021-05-25 16:09:11 -05:00
RequestId : uint64 ( 111 ) ,
2023-10-03 07:03:19 -05:00
GetBlockHeadersRequest : & eth . GetBlockHeadersRequest {
2021-05-25 16:09:11 -05:00
Origin : eth . HashOrNumber {
Hash : s . chain . blocks [ 1 ] . Hash ( ) ,
} ,
Amount : 2 ,
Skip : 1 ,
Reverse : false ,
} ,
}
2023-12-20 10:23:48 -06:00
req2 := & eth . GetBlockHeadersPacket {
2021-05-25 16:09:11 -05:00
RequestId : uint64 ( 222 ) ,
2023-10-03 07:03:19 -05:00
GetBlockHeadersRequest : & eth . GetBlockHeadersRequest {
2021-05-25 16:09:11 -05:00
Origin : eth . HashOrNumber {
Hash : s . chain . blocks [ 1 ] . Hash ( ) ,
} ,
Amount : 4 ,
Skip : 1 ,
Reverse : false ,
} ,
}
2022-08-02 07:48:55 -05:00
2023-12-20 10:23:48 -06:00
// Send both requests.
if err := conn . Write ( ethProto , eth . GetBlockHeadersMsg , req1 ) ; err != nil {
2021-05-25 16:09:11 -05:00
t . Fatalf ( "failed to write to connection: %v" , err )
}
2023-12-20 10:23:48 -06:00
if err := conn . Write ( ethProto , eth . GetBlockHeadersMsg , req2 ) ; err != nil {
2021-05-25 16:09:11 -05:00
t . Fatalf ( "failed to write to connection: %v" , err )
}
2022-08-02 07:48:55 -05:00
2023-12-20 10:23:48 -06:00
// Wait for responses.
headers1 := new ( eth . BlockHeadersPacket )
if err := conn . ReadMsg ( ethProto , eth . BlockHeadersMsg , & headers1 ) ; err != nil {
t . Fatalf ( "error reading block headers msg: %v" , err )
}
if got , want := headers1 . RequestId , req1 . RequestId ; got != want {
t . Fatalf ( "unexpected request id in response: got %d, want %d" , got , want )
}
headers2 := new ( eth . BlockHeadersPacket )
if err := conn . ReadMsg ( ethProto , eth . BlockHeadersMsg , & headers2 ) ; err != nil {
t . Fatalf ( "error reading block headers msg: %v" , err )
2021-05-25 16:09:11 -05:00
}
2023-12-20 10:23:48 -06:00
if got , want := headers2 . RequestId , req2 . RequestId ; got != want {
t . Fatalf ( "unexpected request id in response: got %d, want %d" , got , want )
2021-05-25 16:09:11 -05:00
}
2022-08-02 07:48:55 -05:00
2023-12-20 10:23:48 -06:00
// Check received headers for accuracy.
if expected , err := s . chain . GetHeaders ( req1 ) ; err != nil {
2021-05-25 16:09:11 -05:00
t . Fatalf ( "failed to get expected headers for request 1: %v" , err )
2023-12-20 10:23:48 -06:00
} else if ! headersMatch ( expected , headers1 . BlockHeadersRequest ) {
t . Fatalf ( "header mismatch: \nexpected %v \ngot %v" , expected , headers1 )
2021-05-25 16:09:11 -05:00
}
2023-12-20 10:23:48 -06:00
if expected , err := s . chain . GetHeaders ( req2 ) ; err != nil {
2021-05-25 16:09:11 -05:00
t . Fatalf ( "failed to get expected headers for request 2: %v" , err )
2023-12-20 10:23:48 -06:00
} else if ! headersMatch ( expected , headers2 . BlockHeadersRequest ) {
t . Fatalf ( "header mismatch: \nexpected %v \ngot %v" , expected , headers2 )
2021-05-25 16:09:11 -05:00
}
}
2020-09-23 08:18:17 -05:00
2022-08-02 07:48:55 -05:00
func ( s * Suite ) TestSameRequestID ( t * utesting . T ) {
2024-02-15 12:43:37 -06:00
t . Log ( ` This test requests block headers , performing two concurrent requests with the
same request ID . The node should handle the request by responding to both requests . ` )
2022-08-02 07:48:55 -05:00
conn , err := s . dial ( )
2021-05-25 16:09:11 -05:00
if err != nil {
t . Fatalf ( "dial failed: %v" , err )
}
defer conn . Close ( )
if err := conn . peer ( s . chain , nil ) ; err != nil {
t . Fatalf ( "peering failed: %v" , err )
}
2023-12-20 10:23:48 -06:00
// Create two different requests with the same ID.
2021-05-25 16:09:11 -05:00
reqID := uint64 ( 1234 )
2023-12-20 10:23:48 -06:00
request1 := & eth . GetBlockHeadersPacket {
2021-05-25 16:09:11 -05:00
RequestId : reqID ,
2023-10-03 07:03:19 -05:00
GetBlockHeadersRequest : & eth . GetBlockHeadersRequest {
2021-05-25 16:09:11 -05:00
Origin : eth . HashOrNumber {
Number : 1 ,
} ,
Amount : 2 ,
} ,
}
2023-12-20 10:23:48 -06:00
request2 := & eth . GetBlockHeadersPacket {
2021-05-25 16:09:11 -05:00
RequestId : reqID ,
2023-10-03 07:03:19 -05:00
GetBlockHeadersRequest : & eth . GetBlockHeadersRequest {
2021-05-25 16:09:11 -05:00
Origin : eth . HashOrNumber {
Number : 33 ,
} ,
Amount : 2 ,
} ,
}
2022-08-02 07:48:55 -05:00
2023-12-20 10:23:48 -06:00
// Send the requests.
if err = conn . Write ( ethProto , eth . GetBlockHeadersMsg , request1 ) ; err != nil {
2021-05-25 16:09:11 -05:00
t . Fatalf ( "failed to write to connection: %v" , err )
}
2023-12-20 10:23:48 -06:00
if err = conn . Write ( ethProto , eth . GetBlockHeadersMsg , request2 ) ; err != nil {
2021-05-25 16:09:11 -05:00
t . Fatalf ( "failed to write to connection: %v" , err )
}
2022-08-02 07:48:55 -05:00
2023-12-20 10:23:48 -06:00
// Wait for the responses.
headers1 := new ( eth . BlockHeadersPacket )
if err := conn . ReadMsg ( ethProto , eth . BlockHeadersMsg , & headers1 ) ; err != nil {
t . Fatalf ( "error reading from connection: %v" , err )
}
if got , want := headers1 . RequestId , request1 . RequestId ; got != want {
t . Fatalf ( "unexpected request id: got %d, want %d" , got , want )
}
headers2 := new ( eth . BlockHeadersPacket )
if err := conn . ReadMsg ( ethProto , eth . BlockHeadersMsg , & headers2 ) ; err != nil {
t . Fatalf ( "error reading from connection: %v" , err )
2021-05-25 16:09:11 -05:00
}
2023-12-20 10:23:48 -06:00
if got , want := headers2 . RequestId , request2 . RequestId ; got != want {
t . Fatalf ( "unexpected request id: got %d, want %d" , got , want )
2021-05-25 16:09:11 -05:00
}
2022-08-02 07:48:55 -05:00
2023-12-20 10:23:48 -06:00
// Check if headers match.
if expected , err := s . chain . GetHeaders ( request1 ) ; err != nil {
2021-05-25 16:09:11 -05:00
t . Fatalf ( "failed to get expected block headers: %v" , err )
2023-12-20 10:23:48 -06:00
} else if ! headersMatch ( expected , headers1 . BlockHeadersRequest ) {
t . Fatalf ( "header mismatch: \nexpected %v \ngot %v" , expected , headers1 )
2021-05-25 16:09:11 -05:00
}
2023-12-20 10:23:48 -06:00
if expected , err := s . chain . GetHeaders ( request2 ) ; err != nil {
2021-05-25 16:09:11 -05:00
t . Fatalf ( "failed to get expected block headers: %v" , err )
2023-12-20 10:23:48 -06:00
} else if ! headersMatch ( expected , headers2 . BlockHeadersRequest ) {
t . Fatalf ( "header mismatch: \nexpected %v \ngot %v" , expected , headers2 )
2021-05-25 16:09:11 -05:00
}
}
2022-08-02 07:48:55 -05:00
func ( s * Suite ) TestZeroRequestID ( t * utesting . T ) {
2024-02-15 12:43:37 -06:00
t . Log ( ` This test sends a GetBlockHeaders message with a request - id of zero ,
and expects a response . ` )
2022-08-02 07:48:55 -05:00
conn , err := s . dial ( )
2021-05-25 16:09:11 -05:00
if err != nil {
t . Fatalf ( "dial failed: %v" , err )
}
defer conn . Close ( )
if err := conn . peer ( s . chain , nil ) ; err != nil {
t . Fatalf ( "peering failed: %v" , err )
}
2023-12-20 10:23:48 -06:00
req := & eth . GetBlockHeadersPacket {
2023-10-03 07:03:19 -05:00
GetBlockHeadersRequest : & eth . GetBlockHeadersRequest {
2022-08-02 07:48:55 -05:00
Origin : eth . HashOrNumber { Number : 0 } ,
Amount : 2 ,
2021-05-25 16:09:11 -05:00
} ,
}
2023-12-20 10:23:48 -06:00
// Read headers response.
if err := conn . Write ( ethProto , eth . GetBlockHeadersMsg , req ) ; err != nil {
t . Fatalf ( "could not write to connection: %v" , err )
2021-05-25 16:09:11 -05:00
}
2023-12-20 10:23:48 -06:00
headers := new ( eth . BlockHeadersPacket )
if err := conn . ReadMsg ( ethProto , eth . BlockHeadersMsg , & headers ) ; err != nil {
t . Fatalf ( "error reading msg: %v" , err )
}
if got , want := headers . RequestId , req . RequestId ; got != want {
t . Fatalf ( "unexpected request id" )
2021-05-25 16:09:11 -05:00
}
2023-12-20 10:23:48 -06:00
if expected , err := s . chain . GetHeaders ( req ) ; err != nil {
t . Fatalf ( "failed to get expected block headers: %v" , err )
} else if ! headersMatch ( expected , headers . BlockHeadersRequest ) {
2021-05-25 16:09:11 -05:00
t . Fatalf ( "header mismatch: \nexpected %v \ngot %v" , expected , headers )
2020-09-23 08:18:17 -05:00
}
}
2022-08-02 07:48:55 -05:00
func ( s * Suite ) TestGetBlockBodies ( t * utesting . T ) {
2024-02-15 12:43:37 -06:00
t . Log ( ` This test sends GetBlockBodies requests to the node for known blocks in the test chain. ` )
2020-09-23 08:18:17 -05:00
conn , err := s . dial ( )
if err != nil {
2021-05-25 16:09:11 -05:00
t . Fatalf ( "dial failed: %v" , err )
2020-09-23 08:18:17 -05:00
}
2021-04-23 04:15:42 -05:00
defer conn . Close ( )
2021-05-25 16:09:11 -05:00
if err := conn . peer ( s . chain , nil ) ; err != nil {
t . Fatalf ( "peering failed: %v" , err )
}
2023-12-20 10:23:48 -06:00
// Create block bodies request.
req := & eth . GetBlockBodiesPacket {
RequestId : 55 ,
2023-10-03 07:03:19 -05:00
GetBlockBodiesRequest : eth . GetBlockBodiesRequest {
2021-05-25 16:09:11 -05:00
s . chain . blocks [ 54 ] . Hash ( ) ,
s . chain . blocks [ 75 ] . Hash ( ) ,
} ,
}
2023-12-20 10:23:48 -06:00
if err := conn . Write ( ethProto , eth . GetBlockBodiesMsg , req ) ; err != nil {
2021-05-25 16:09:11 -05:00
t . Fatalf ( "could not write to connection: %v" , err )
}
2023-12-20 10:23:48 -06:00
// Wait for response.
resp := new ( eth . BlockBodiesPacket )
if err := conn . ReadMsg ( ethProto , eth . BlockBodiesMsg , & resp ) ; err != nil {
t . Fatalf ( "error reading block bodies msg: %v" , err )
}
if got , want := resp . RequestId , req . RequestId ; got != want {
t . Fatalf ( "unexpected request id in respond" , got , want )
2021-05-25 16:09:11 -05:00
}
2023-10-03 07:03:19 -05:00
bodies := resp . BlockBodiesResponse
if len ( bodies ) != len ( req . GetBlockBodiesRequest ) {
2023-12-20 10:23:48 -06:00
t . Fatalf ( "wrong bodies in response: expected %d bodies, got %d" , len ( req . GetBlockBodiesRequest ) , len ( bodies ) )
2021-05-25 16:09:11 -05:00
}
2021-04-30 15:47:36 -05:00
}
2023-12-20 10:23:48 -06:00
// randBuf makes a random buffer size kilobytes large.
func randBuf ( size int ) [ ] byte {
buf := make ( [ ] byte , size * 1024 )
rand . Read ( buf )
return buf
2020-11-24 09:09:17 -06:00
}
2023-12-20 10:23:48 -06:00
func ( s * Suite ) TestMaliciousHandshake ( t * utesting . T ) {
2024-02-15 12:43:37 -06:00
t . Log ( ` This test tries to send malicious data during the devp2p handshake, in various ways. ` )
2023-12-20 10:23:48 -06:00
// Write hello to client.
var (
2024-02-15 12:43:37 -06:00
key , _ = crypto . GenerateKey ( )
2023-12-20 10:23:48 -06:00
pub0 = crypto . FromECDSAPub ( & key . PublicKey ) [ 1 : ]
version = eth . ProtocolVersions [ 0 ]
)
handshakes := [ ] * protoHandshake {
2020-11-24 09:09:17 -06:00
{
2023-12-20 10:23:48 -06:00
Version : 5 ,
Caps : [ ] p2p . Cap {
{ Name : string ( randBuf ( 2 ) ) , Version : version } ,
} ,
ID : pub0 ,
2020-11-24 09:09:17 -06:00
} ,
{
2023-12-20 10:23:48 -06:00
Version : 5 ,
Caps : [ ] p2p . Cap {
{ Name : "eth" , Version : version } ,
} ,
ID : append ( pub0 , byte ( 0 ) ) ,
2020-11-24 09:09:17 -06:00
} ,
{
2023-12-20 10:23:48 -06:00
Version : 5 ,
Caps : [ ] p2p . Cap {
{ Name : "eth" , Version : version } ,
} ,
ID : append ( pub0 , pub0 ... ) ,
} ,
{
Version : 5 ,
Caps : [ ] p2p . Cap {
{ Name : "eth" , Version : version } ,
} ,
ID : randBuf ( 2 ) ,
} ,
{
Version : 5 ,
Caps : [ ] p2p . Cap {
{ Name : string ( randBuf ( 2 ) ) , Version : version } ,
} ,
ID : randBuf ( 2 ) ,
2020-11-24 09:09:17 -06:00
} ,
}
2023-12-20 10:23:48 -06:00
for _ , handshake := range handshakes {
conn , err := s . dialAs ( key )
2021-05-25 16:09:11 -05:00
if err != nil {
t . Fatalf ( "dial failed: %v" , err )
}
2023-12-20 10:23:48 -06:00
defer conn . Close ( )
if err := conn . Write ( ethProto , handshakeMsg , handshake ) ; err != nil {
2020-11-24 09:09:17 -06:00
t . Fatalf ( "could not write to connection: %v" , err )
}
2023-12-20 10:23:48 -06:00
// Check that the peer disconnected
for i := 0 ; i < 2 ; i ++ {
code , _ , err := conn . Read ( )
if err != nil {
// Client may have disconnected without sending disconnect msg.
continue
}
switch code {
case discMsg :
case handshakeMsg :
// Discard one hello as Hello's are sent concurrently
continue
default :
t . Fatalf ( "unexpected msg: code %d" , code )
}
2020-11-24 09:09:17 -06:00
}
2021-05-25 16:09:11 -05:00
}
2021-03-20 13:50:44 -05:00
}
2022-08-02 07:48:55 -05:00
func ( s * Suite ) TestTransaction ( t * utesting . T ) {
2024-02-15 12:43:37 -06:00
t . Log ( ` This test sends a valid transaction to the node and checks if the
transaction gets propagated . ` )
2023-12-20 10:23:48 -06:00
// Nudge client out of syncing mode to accept pending txs.
if err := s . engine . sendForkchoiceUpdated ( ) ; err != nil {
t . Fatalf ( "failed to send next block: %v" , err )
}
from , nonce := s . chain . GetSender ( 0 )
inner := & types . DynamicFeeTx {
ChainID : s . chain . config . ChainID ,
Nonce : nonce ,
GasTipCap : common . Big1 ,
GasFeeCap : s . chain . Head ( ) . BaseFee ( ) ,
Gas : 30000 ,
To : & common . Address { 0xaa } ,
Value : common . Big1 ,
}
tx , err := s . chain . SignTx ( from , types . NewTx ( inner ) )
if err != nil {
t . Fatalf ( "failed to sign tx: %v" , err )
}
2024-02-15 12:43:37 -06:00
if err := s . sendTxs ( t , [ ] * types . Transaction { tx } ) ; err != nil {
2021-05-25 16:09:11 -05:00
t . Fatal ( err )
2020-09-23 08:18:17 -05:00
}
2023-12-20 10:23:48 -06:00
s . chain . IncNonce ( from , 1 )
2020-11-24 09:09:17 -06:00
}
2020-09-23 08:18:17 -05:00
2023-12-20 10:23:48 -06:00
func ( s * Suite ) TestInvalidTxs ( t * utesting . T ) {
2024-02-15 12:43:37 -06:00
t . Log ( ` This test sends several kinds of invalid transactions and checks that the node
does not propagate them . ` )
2023-12-20 10:23:48 -06:00
// Nudge client out of syncing mode to accept pending txs.
if err := s . engine . sendForkchoiceUpdated ( ) ; err != nil {
t . Fatalf ( "failed to send next block: %v" , err )
}
from , nonce := s . chain . GetSender ( 0 )
inner := & types . DynamicFeeTx {
ChainID : s . chain . config . ChainID ,
Nonce : nonce ,
GasTipCap : common . Big1 ,
GasFeeCap : s . chain . Head ( ) . BaseFee ( ) ,
Gas : 30000 ,
To : & common . Address { 0xaa } ,
}
tx , err := s . chain . SignTx ( from , types . NewTx ( inner ) )
if err != nil {
t . Fatalf ( "failed to sign tx: %v" , err )
}
2024-02-15 12:43:37 -06:00
if err := s . sendTxs ( t , [ ] * types . Transaction { tx } ) ; err != nil {
2023-12-20 10:23:48 -06:00
t . Fatalf ( "failed to send txs: %v" , err )
}
s . chain . IncNonce ( from , 1 )
inners := [ ] * types . DynamicFeeTx {
// Nonce already used
{
ChainID : s . chain . config . ChainID ,
Nonce : nonce - 1 ,
GasTipCap : common . Big1 ,
GasFeeCap : s . chain . Head ( ) . BaseFee ( ) ,
Gas : 100000 ,
} ,
// Value exceeds balance
{
Nonce : nonce ,
GasTipCap : common . Big1 ,
GasFeeCap : s . chain . Head ( ) . BaseFee ( ) ,
Gas : 100000 ,
Value : s . chain . Balance ( from ) ,
} ,
// Gas limit too low
{
Nonce : nonce ,
GasTipCap : common . Big1 ,
GasFeeCap : s . chain . Head ( ) . BaseFee ( ) ,
Gas : 1337 ,
} ,
// Code size too large
{
Nonce : nonce ,
GasTipCap : common . Big1 ,
GasFeeCap : s . chain . Head ( ) . BaseFee ( ) ,
Data : randBuf ( 50 ) ,
Gas : 1_000_000 ,
} ,
// Data too large
{
Nonce : nonce ,
GasTipCap : common . Big1 ,
GasFeeCap : s . chain . Head ( ) . BaseFee ( ) ,
To : & common . Address { 0xaa } ,
Data : randBuf ( 128 ) ,
Gas : 5_000_000 ,
} ,
}
var txs [ ] * types . Transaction
for _ , inner := range inners {
tx , err := s . chain . SignTx ( from , types . NewTx ( inner ) )
if err != nil {
t . Fatalf ( "failed to sign tx: %v" , err )
}
txs = append ( txs , tx )
}
2024-02-15 12:43:37 -06:00
if err := s . sendInvalidTxs ( t , txs ) ; err != nil {
2023-12-20 10:23:48 -06:00
t . Fatalf ( "failed to send invalid txs: %v" , err )
2021-05-25 16:09:11 -05:00
}
}
2022-08-02 07:48:55 -05:00
func ( s * Suite ) TestLargeTxRequest ( t * utesting . T ) {
2024-02-15 12:43:37 -06:00
t . Log ( ` This test first send ~ 2000 transactions to the node , then requests them
on another peer connection using GetPooledTransactions . ` )
2023-12-20 10:23:48 -06:00
// Nudge client out of syncing mode to accept pending txs.
if err := s . engine . sendForkchoiceUpdated ( ) ; err != nil {
2021-05-25 16:09:11 -05:00
t . Fatalf ( "failed to send next block: %v" , err )
}
2023-12-20 10:23:48 -06:00
// Generate many transactions to seed target with.
var (
from , nonce = s . chain . GetSender ( 1 )
count = 2000
txs [ ] * types . Transaction
hashes [ ] common . Hash
set = make ( map [ common . Hash ] struct { } )
)
for i := 0 ; i < count ; i ++ {
inner := & types . DynamicFeeTx {
ChainID : s . chain . config . ChainID ,
Nonce : nonce + uint64 ( i ) ,
GasTipCap : common . Big1 ,
GasFeeCap : s . chain . Head ( ) . BaseFee ( ) ,
Gas : 75000 ,
}
tx , err := s . chain . SignTx ( from , types . NewTx ( inner ) )
if err != nil {
t . Fatalf ( "failed to sign tx: err" )
}
txs = append ( txs , tx )
set [ tx . Hash ( ) ] = struct { } { }
hashes = append ( hashes , tx . Hash ( ) )
2020-09-23 08:18:17 -05:00
}
2023-12-20 10:23:48 -06:00
s . chain . IncNonce ( from , uint64 ( count ) )
// Send txs.
2024-02-15 12:43:37 -06:00
if err := s . sendTxs ( t , txs ) ; err != nil {
2023-12-20 10:23:48 -06:00
t . Fatalf ( "failed to send txs: %v" , err )
2021-05-25 16:09:11 -05:00
}
2023-12-20 10:23:48 -06:00
// Set up receive connection to ensure node is peered with the receiving
// connection before tx request is sent.
2022-08-02 07:48:55 -05:00
conn , err := s . dial ( )
2020-09-23 08:18:17 -05:00
if err != nil {
2021-05-25 16:09:11 -05:00
t . Fatalf ( "dial failed: %v" , err )
2020-09-23 08:18:17 -05:00
}
2021-05-25 16:09:11 -05:00
defer conn . Close ( )
if err = conn . peer ( s . chain , nil ) ; err != nil {
t . Fatalf ( "peering failed: %v" , err )
}
2023-12-20 10:23:48 -06:00
// Create and send pooled tx request.
req := & eth . GetPooledTransactionsPacket {
2023-10-03 07:03:19 -05:00
RequestId : 1234 ,
GetPooledTransactionsRequest : hashes ,
2021-05-25 16:09:11 -05:00
}
2023-12-20 10:23:48 -06:00
if err = conn . Write ( ethProto , eth . GetPooledTransactionsMsg , req ) ; err != nil {
2021-05-25 16:09:11 -05:00
t . Fatalf ( "could not write to conn: %v" , err )
}
2023-12-20 10:23:48 -06:00
// Check that all received transactions match those that were sent to node.
msg := new ( eth . PooledTransactionsPacket )
if err := conn . ReadMsg ( ethProto , eth . PooledTransactionsMsg , & msg ) ; err != nil {
t . Fatalf ( "error reading from connection: %v" , err )
}
if got , want := msg . RequestId , req . RequestId ; got != want {
t . Fatalf ( "unexpected request id in response: got %d, want %d" , got , want )
}
for _ , got := range msg . PooledTransactionsResponse {
if _ , exists := set [ got . Hash ( ) ] ; ! exists {
t . Fatalf ( "unexpected tx received: %v" , got . Hash ( ) )
2021-05-25 16:09:11 -05:00
}
2021-02-25 11:36:01 -06:00
}
2020-09-23 08:18:17 -05:00
}
2020-11-30 08:23:48 -06:00
2022-08-02 07:48:55 -05:00
func ( s * Suite ) TestNewPooledTxs ( t * utesting . T ) {
2024-02-15 12:43:37 -06:00
t . Log ( ` This test announces transaction hashes to the node and expects it to fetch
the transactions using a GetPooledTransactions request . ` )
2023-12-20 10:23:48 -06:00
// Nudge client out of syncing mode to accept pending txs.
if err := s . engine . sendForkchoiceUpdated ( ) ; err != nil {
2021-05-25 16:09:11 -05:00
t . Fatalf ( "failed to send next block: %v" , err )
2020-11-30 08:23:48 -06:00
}
2024-02-15 12:43:37 -06:00
2023-12-20 10:23:48 -06:00
var (
count = 50
from , nonce = s . chain . GetSender ( 1 )
hashes = make ( [ ] common . Hash , count )
txTypes = make ( [ ] byte , count )
sizes = make ( [ ] uint32 , count )
)
for i := 0 ; i < count ; i ++ {
inner := & types . DynamicFeeTx {
ChainID : s . chain . config . ChainID ,
Nonce : nonce + uint64 ( i ) ,
GasTipCap : common . Big1 ,
GasFeeCap : s . chain . Head ( ) . BaseFee ( ) ,
Gas : 75000 ,
}
tx , err := s . chain . SignTx ( from , types . NewTx ( inner ) )
if err != nil {
t . Fatalf ( "failed to sign tx: err" )
}
2022-11-07 13:47:04 -06:00
hashes [ i ] = tx . Hash ( )
2023-12-20 10:23:48 -06:00
txTypes [ i ] = tx . Type ( )
2022-11-07 13:47:04 -06:00
sizes [ i ] = uint32 ( tx . Size ( ) )
2021-05-25 16:09:11 -05:00
}
2023-12-20 10:23:48 -06:00
s . chain . IncNonce ( from , uint64 ( count ) )
2021-05-27 13:53:33 -05:00
2023-12-20 10:23:48 -06:00
// Connect to peer.
2022-08-02 07:48:55 -05:00
conn , err := s . dial ( )
2021-05-25 16:09:11 -05:00
if err != nil {
t . Fatalf ( "dial failed: %v" , err )
}
defer conn . Close ( )
if err = conn . peer ( s . chain , nil ) ; err != nil {
t . Fatalf ( "peering failed: %v" , err )
}
2022-11-07 13:47:04 -06:00
2023-12-20 10:23:48 -06:00
// Send announcement.
2024-02-08 07:49:19 -06:00
ann := eth . NewPooledTransactionHashesPacket { Types : txTypes , Sizes : sizes , Hashes : hashes }
2023-12-20 10:23:48 -06:00
err = conn . Write ( ethProto , eth . NewPooledTransactionHashesMsg , ann )
2022-11-07 13:47:04 -06:00
if err != nil {
2021-05-25 16:09:11 -05:00
t . Fatalf ( "failed to write to connection: %v" , err )
}
2021-05-27 13:53:33 -05:00
2023-12-20 10:23:48 -06:00
// Wait for GetPooledTxs request.
2021-05-25 16:09:11 -05:00
for {
2023-12-20 10:23:48 -06:00
msg , err := conn . ReadEth ( )
if err != nil {
t . Fatalf ( "failed to read eth msg: %v" , err )
}
2021-05-25 16:09:11 -05:00
switch msg := msg . ( type ) {
2023-12-20 10:23:48 -06:00
case * eth . GetPooledTransactionsPacket :
2023-10-03 07:03:19 -05:00
if len ( msg . GetPooledTransactionsRequest ) != len ( hashes ) {
t . Fatalf ( "unexpected number of txs requested: wanted %d, got %d" , len ( hashes ) , len ( msg . GetPooledTransactionsRequest ) )
2021-05-25 16:09:11 -05:00
}
return
2024-02-08 07:49:19 -06:00
case * eth . NewPooledTransactionHashesPacket :
2022-11-07 13:47:04 -06:00
continue
2023-12-20 10:23:48 -06:00
case * eth . TransactionsPacket :
2021-05-25 16:09:11 -05:00
continue
default :
t . Fatalf ( "unexpected %s" , pretty . Sdump ( msg ) )
2021-04-23 04:15:42 -05:00
}
2020-11-30 08:23:48 -06:00
}
}
2023-12-20 10:23:48 -06:00
func makeSidecar ( data ... byte ) * types . BlobTxSidecar {
var (
blobs = make ( [ ] kzg4844 . Blob , len ( data ) )
commitments [ ] kzg4844 . Commitment
proofs [ ] kzg4844 . Proof
)
for i := range blobs {
blobs [ i ] [ 0 ] = data [ i ]
2024-03-13 01:51:46 -05:00
c , _ := kzg4844 . BlobToCommitment ( & blobs [ i ] )
p , _ := kzg4844 . ComputeBlobProof ( & blobs [ i ] , c )
2023-12-20 10:23:48 -06:00
commitments = append ( commitments , c )
proofs = append ( proofs , p )
}
return & types . BlobTxSidecar {
Blobs : blobs ,
Commitments : commitments ,
Proofs : proofs ,
}
}
func ( s * Suite ) makeBlobTxs ( count , blobs int , discriminator byte ) ( txs types . Transactions ) {
from , nonce := s . chain . GetSender ( 5 )
for i := 0 ; i < count ; i ++ {
// Make blob data, max of 2 blobs per tx.
2024-02-15 03:22:03 -06:00
blobdata := make ( [ ] byte , blobs % 3 )
2023-12-20 10:23:48 -06:00
for i := range blobdata {
blobdata [ i ] = discriminator
blobs -= 1
}
inner := & types . BlobTx {
ChainID : uint256 . MustFromBig ( s . chain . config . ChainID ) ,
Nonce : nonce + uint64 ( i ) ,
GasTipCap : uint256 . NewInt ( 1 ) ,
GasFeeCap : uint256 . MustFromBig ( s . chain . Head ( ) . BaseFee ( ) ) ,
Gas : 100000 ,
2025-02-04 08:43:18 -06:00
BlobFeeCap : uint256 . MustFromBig ( eip4844 . CalcBlobFee ( s . chain . config , s . chain . Head ( ) . Header ( ) ) ) ,
2023-12-20 10:23:48 -06:00
BlobHashes : makeSidecar ( blobdata ... ) . BlobHashes ( ) ,
Sidecar : makeSidecar ( blobdata ... ) ,
}
tx , err := s . chain . SignTx ( from , types . NewTx ( inner ) )
if err != nil {
panic ( "blob tx signing failed" )
}
txs = append ( txs , tx )
}
return txs
}
func ( s * Suite ) TestBlobViolations ( t * utesting . T ) {
2024-02-15 12:43:37 -06:00
t . Log ( ` This test sends some invalid blob tx announcements and expects the node to disconnect. ` )
2023-12-20 10:23:48 -06:00
if err := s . engine . sendForkchoiceUpdated ( ) ; err != nil {
t . Fatalf ( "send fcu failed: %v" , err )
}
2024-01-08 13:31:22 -06:00
// Create blob txs for each tests with unique tx hashes.
2023-12-20 10:23:48 -06:00
var (
t1 = s . makeBlobTxs ( 2 , 3 , 0x1 )
t2 = s . makeBlobTxs ( 2 , 3 , 0x2 )
)
for _ , test := range [ ] struct {
2024-02-08 07:49:19 -06:00
ann eth . NewPooledTransactionHashesPacket
2023-12-20 10:23:48 -06:00
resp eth . PooledTransactionsResponse
} {
// Invalid tx size.
{
2024-02-08 07:49:19 -06:00
ann : eth . NewPooledTransactionHashesPacket {
2023-12-20 10:23:48 -06:00
Types : [ ] byte { types . BlobTxType , types . BlobTxType } ,
Sizes : [ ] uint32 { uint32 ( t1 [ 0 ] . Size ( ) ) , uint32 ( t1 [ 1 ] . Size ( ) + 10 ) } ,
Hashes : [ ] common . Hash { t1 [ 0 ] . Hash ( ) , t1 [ 1 ] . Hash ( ) } ,
} ,
resp : eth . PooledTransactionsResponse ( t1 ) ,
} ,
// Wrong tx type.
{
2024-02-08 07:49:19 -06:00
ann : eth . NewPooledTransactionHashesPacket {
2023-12-20 10:23:48 -06:00
Types : [ ] byte { types . DynamicFeeTxType , types . BlobTxType } ,
Sizes : [ ] uint32 { uint32 ( t2 [ 0 ] . Size ( ) ) , uint32 ( t2 [ 1 ] . Size ( ) ) } ,
Hashes : [ ] common . Hash { t2 [ 0 ] . Hash ( ) , t2 [ 1 ] . Hash ( ) } ,
} ,
resp : eth . PooledTransactionsResponse ( t2 ) ,
} ,
} {
conn , err := s . dial ( )
if err != nil {
t . Fatalf ( "dial fail: %v" , err )
}
if err := conn . peer ( s . chain , nil ) ; err != nil {
t . Fatalf ( "peering failed: %v" , err )
}
if err := conn . Write ( ethProto , eth . NewPooledTransactionHashesMsg , test . ann ) ; err != nil {
t . Fatalf ( "sending announcement failed: %v" , err )
}
req := new ( eth . GetPooledTransactionsPacket )
if err := conn . ReadMsg ( ethProto , eth . GetPooledTransactionsMsg , req ) ; err != nil {
t . Fatalf ( "reading pooled tx request failed: %v" , err )
}
resp := eth . PooledTransactionsPacket { RequestId : req . RequestId , PooledTransactionsResponse : test . resp }
if err := conn . Write ( ethProto , eth . PooledTransactionsMsg , resp ) ; err != nil {
t . Fatalf ( "writing pooled tx response failed: %v" , err )
}
if code , _ , err := conn . Read ( ) ; err != nil {
t . Fatalf ( "expected disconnect on blob violation, got err: %v" , err )
} else if code != discMsg {
2024-09-06 05:32:01 -05:00
if code == protoOffset ( ethProto ) + eth . NewPooledTransactionHashesMsg {
// sometimes we'll get a blob transaction hashes announcement before the disconnect
// because blob transactions are scheduled to be fetched right away.
if code , _ , err = conn . Read ( ) ; err != nil {
t . Fatalf ( "expected disconnect on blob violation, got err on second read: %v" , err )
}
}
if code != discMsg {
t . Fatalf ( "expected disconnect on blob violation, got msg code: %d" , code )
}
2023-12-20 10:23:48 -06:00
}
conn . Close ( )
}
}
2025-03-01 07:10:38 -06:00
// mangleSidecar returns a copy of the given blob transaction where the sidecar
// data has been modified to produce a different commitment hash.
func mangleSidecar ( tx * types . Transaction ) * types . Transaction {
sidecar := tx . BlobTxSidecar ( )
copy := types . BlobTxSidecar {
Blobs : append ( [ ] kzg4844 . Blob { } , sidecar . Blobs ... ) ,
Commitments : append ( [ ] kzg4844 . Commitment { } , sidecar . Commitments ... ) ,
Proofs : append ( [ ] kzg4844 . Proof { } , sidecar . Proofs ... ) ,
}
// zero the first commitment to alter the sidecar hash
copy . Commitments [ 0 ] = kzg4844 . Commitment { }
return tx . WithBlobTxSidecar ( & copy )
}
func ( s * Suite ) TestBlobTxWithoutSidecar ( t * utesting . T ) {
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. ` )
tx := s . makeBlobTxs ( 1 , 2 , 42 ) [ 0 ]
badTx := tx . WithoutBlobTxSidecar ( )
s . testBadBlobTx ( t , tx , badTx )
}
func ( s * Suite ) TestBlobTxWithMismatchedSidecar ( t * utesting . T ) {
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. ` )
tx := s . makeBlobTxs ( 1 , 2 , 43 ) [ 0 ]
badTx := mangleSidecar ( tx )
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 res := received . ( type ) {
case * 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 , cancel := context . WithTimeout ( context . Background ( ) , 100 * time . Millisecond )
defer cancel ( )
_ , err := readUntil [ struct { } ] ( ctx , conn )
return err == errDisc
}
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 . Add ( 1 )
stage2 . Add ( 1 )
stage3 . Add ( 1 )
errc := make ( chan error )
badPeer := func ( ) {
// announce the correct hash from the bad peer.
// when the transaction is first requested before transmitting it from the bad peer,
// trigger step 2: connection and announcement by good peers
conn , err := s . dial ( )
if err != nil {
errc <- fmt . Errorf ( "dial fail: %v" , err )
return
}
defer conn . Close ( )
if err := conn . peer ( s . chain , nil ) ; err != nil {
errc <- fmt . Errorf ( "bad peer: peering failed: %v" , err )
return
}
ann := eth . NewPooledTransactionHashesPacket {
Types : [ ] byte { types . BlobTxType } ,
Sizes : [ ] uint32 { uint32 ( badTx . Size ( ) ) } ,
Hashes : [ ] common . Hash { badTx . Hash ( ) } ,
}
if err := conn . Write ( ethProto , eth . NewPooledTransactionHashesMsg , ann ) ; err != nil {
errc <- fmt . Errorf ( "sending announcement failed: %v" , err )
return
}
req , err := readUntil [ eth . GetPooledTransactionsPacket ] ( context . Background ( ) , conn )
if err != nil {
errc <- fmt . Errorf ( "failed to read GetPooledTransactions message: %v" , err )
return
}
stage1 . Done ( )
stage2 . Wait ( )
// the good peer is connected, and has announced the tx.
// proceed to send the incorrect one from the bad peer.
resp := eth . PooledTransactionsPacket { RequestId : req . RequestId , PooledTransactionsResponse : eth . PooledTransactionsResponse ( types . Transactions { badTx } ) }
if err := conn . Write ( ethProto , eth . PooledTransactionsMsg , resp ) ; err != nil {
errc <- fmt . Errorf ( "writing pooled tx response failed: %v" , err )
return
}
if ! readUntilDisconnect ( conn ) {
errc <- fmt . Errorf ( "expected bad peer to be disconnected" )
return
}
stage3 . Done ( )
}
goodPeer := func ( ) {
stage1 . Wait ( )
conn , err := s . dial ( )
if err != nil {
errc <- fmt . Errorf ( "dial fail: %v" , err )
return
}
defer conn . Close ( )
if err := conn . peer ( s . chain , nil ) ; err != nil {
errc <- fmt . Errorf ( "peering failed: %v" , err )
return
}
ann := eth . NewPooledTransactionHashesPacket {
Types : [ ] byte { types . BlobTxType } ,
Sizes : [ ] uint32 { uint32 ( tx . Size ( ) ) } ,
Hashes : [ ] common . Hash { tx . Hash ( ) } ,
}
if err := conn . Write ( ethProto , eth . NewPooledTransactionHashesMsg , ann ) ; err != nil {
errc <- fmt . Errorf ( "sending announcement failed: %v" , err )
return
}
// wait until the bad peer has transmitted the incorrect transaction
stage2 . Done ( )
stage3 . Wait ( )
// the bad peer has transmitted the bad tx, and been disconnected.
// transmit the same tx but with correct sidecar from the good peer.
var req * eth . GetPooledTransactionsPacket
req , err = readUntil [ eth . GetPooledTransactionsPacket ] ( context . Background ( ) , conn )
if err != nil {
errc <- fmt . Errorf ( "reading pooled tx request failed: %v" , err )
return
}
if req . GetPooledTransactionsRequest [ 0 ] != tx . Hash ( ) {
errc <- fmt . Errorf ( "requested unknown tx hash" )
return
}
resp := eth . PooledTransactionsPacket { RequestId : req . RequestId , PooledTransactionsResponse : eth . PooledTransactionsResponse ( types . Transactions { tx } ) }
if err := conn . Write ( ethProto , eth . PooledTransactionsMsg , resp ) ; err != nil {
errc <- fmt . Errorf ( "writing pooled tx response failed: %v" , err )
return
}
if readUntilDisconnect ( conn ) {
errc <- fmt . Errorf ( "unexpected disconnect" )
return
}
close ( errc )
}
if err := s . engine . sendForkchoiceUpdated ( ) ; err != nil {
t . Fatalf ( "send fcu failed: %v" , err )
}
go goodPeer ( )
go badPeer ( )
err := <- errc
if err != nil {
t . Fatalf ( "%v" , err )
}
}