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, ð.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, ð.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 }