github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/cmd/devp2p/internal/ethtest/transaction.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  	"errors"
    21  	"fmt"
    22  	"os"
    23  	"time"
    24  
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/ethereum/go-ethereum/core/types"
    27  	"github.com/ethereum/go-ethereum/eth/protocols/eth"
    28  	"github.com/ethereum/go-ethereum/internal/utesting"
    29  )
    30  
    31  // sendTxs sends the given transactions to the node and
    32  // expects the node to accept and propagate them.
    33  func (s *Suite) sendTxs(t *utesting.T, txs []*types.Transaction) error {
    34  	// Open sending conn.
    35  	sendConn, err := s.dial()
    36  	if err != nil {
    37  		return err
    38  	}
    39  	defer sendConn.Close()
    40  	if err = sendConn.peer(s.chain, nil); err != nil {
    41  		return fmt.Errorf("peering failed: %v", err)
    42  	}
    43  
    44  	// Open receiving conn.
    45  	recvConn, err := s.dial()
    46  	if err != nil {
    47  		return err
    48  	}
    49  	defer recvConn.Close()
    50  	if err = recvConn.peer(s.chain, nil); err != nil {
    51  		return fmt.Errorf("peering failed: %v", err)
    52  	}
    53  
    54  	if err = sendConn.Write(ethProto, eth.TransactionsMsg, eth.TransactionsPacket(txs)); err != nil {
    55  		return fmt.Errorf("failed to write message to connection: %v", err)
    56  	}
    57  
    58  	var (
    59  		got = make(map[common.Hash]bool)
    60  		end = time.Now().Add(timeout)
    61  	)
    62  
    63  	// Wait for the transaction announcements, make sure all txs ar propagated.
    64  	for time.Now().Before(end) {
    65  		msg, err := recvConn.ReadEth()
    66  		if err != nil {
    67  			return fmt.Errorf("failed to read from connection: %w", err)
    68  		}
    69  		switch msg := msg.(type) {
    70  		case *eth.TransactionsPacket:
    71  			for _, tx := range *msg {
    72  				got[tx.Hash()] = true
    73  			}
    74  		case *eth.NewPooledTransactionHashesPacket:
    75  			for _, hash := range msg.Hashes {
    76  				got[hash] = true
    77  			}
    78  		case *eth.GetBlockHeadersPacket:
    79  			headers, err := s.chain.GetHeaders(msg)
    80  			if err != nil {
    81  				t.Logf("invalid GetBlockHeaders request: %v", err)
    82  			}
    83  			recvConn.Write(ethProto, eth.BlockHeadersMsg, &eth.BlockHeadersPacket{
    84  				RequestId:           msg.RequestId,
    85  				BlockHeadersRequest: headers,
    86  			})
    87  		default:
    88  			return fmt.Errorf("unexpected eth wire msg: %s", pretty.Sdump(msg))
    89  		}
    90  
    91  		// Check if all txs received.
    92  		allReceived := func() bool {
    93  			for _, tx := range txs {
    94  				if !got[tx.Hash()] {
    95  					return false
    96  				}
    97  			}
    98  			return true
    99  		}
   100  		if allReceived() {
   101  			return nil
   102  		}
   103  	}
   104  
   105  	return errors.New("timed out waiting for txs")
   106  }
   107  
   108  func (s *Suite) sendInvalidTxs(t *utesting.T, txs []*types.Transaction) error {
   109  	// Open sending conn.
   110  	sendConn, err := s.dial()
   111  	if err != nil {
   112  		return err
   113  	}
   114  	defer sendConn.Close()
   115  	if err = sendConn.peer(s.chain, nil); err != nil {
   116  		return fmt.Errorf("peering failed: %v", err)
   117  	}
   118  	sendConn.SetDeadline(time.Now().Add(timeout))
   119  
   120  	// Open receiving conn.
   121  	recvConn, err := s.dial()
   122  	if err != nil {
   123  		return err
   124  	}
   125  	defer recvConn.Close()
   126  	if err = recvConn.peer(s.chain, nil); err != nil {
   127  		return fmt.Errorf("peering failed: %v", err)
   128  	}
   129  	recvConn.SetDeadline(time.Now().Add(timeout))
   130  
   131  	if err = sendConn.Write(ethProto, eth.TransactionsMsg, txs); err != nil {
   132  		return fmt.Errorf("failed to write message to connection: %w", err)
   133  	}
   134  
   135  	// Make map of invalid txs.
   136  	invalids := make(map[common.Hash]struct{})
   137  	for _, tx := range txs {
   138  		invalids[tx.Hash()] = struct{}{}
   139  	}
   140  
   141  	// Get responses.
   142  	recvConn.SetReadDeadline(time.Now().Add(timeout))
   143  	for {
   144  		msg, err := recvConn.ReadEth()
   145  		if errors.Is(err, os.ErrDeadlineExceeded) {
   146  			// Successful if no invalid txs are propagated before timeout.
   147  			return nil
   148  		} else if err != nil {
   149  			return fmt.Errorf("failed to read from connection: %w", err)
   150  		}
   151  
   152  		switch msg := msg.(type) {
   153  		case *eth.TransactionsPacket:
   154  			for _, tx := range txs {
   155  				if _, ok := invalids[tx.Hash()]; ok {
   156  					return fmt.Errorf("received bad tx: %s", tx.Hash())
   157  				}
   158  			}
   159  		case *eth.NewPooledTransactionHashesPacket:
   160  			for _, hash := range msg.Hashes {
   161  				if _, ok := invalids[hash]; ok {
   162  					return fmt.Errorf("received bad tx: %s", hash)
   163  				}
   164  			}
   165  		case *eth.GetBlockHeadersPacket:
   166  			headers, err := s.chain.GetHeaders(msg)
   167  			if err != nil {
   168  				t.Logf("invalid GetBlockHeaders request: %v", err)
   169  			}
   170  			recvConn.Write(ethProto, eth.BlockHeadersMsg, &eth.BlockHeadersPacket{
   171  				RequestId:           msg.RequestId,
   172  				BlockHeadersRequest: headers,
   173  			})
   174  		default:
   175  			return fmt.Errorf("unexpected eth message: %v", pretty.Sdump(msg))
   176  		}
   177  	}
   178  }