github.com/juliankolbe/go-ethereum@v1.9.992/cmd/devp2p/internal/ethtest/types.go (about) 1 // Copyright 2020 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser 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 // The go-ethereum library 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 Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package ethtest 18 19 import ( 20 "crypto/ecdsa" 21 "fmt" 22 "reflect" 23 "time" 24 25 "github.com/juliankolbe/go-ethereum/core/types" 26 "github.com/juliankolbe/go-ethereum/crypto" 27 "github.com/juliankolbe/go-ethereum/eth/protocols/eth" 28 "github.com/juliankolbe/go-ethereum/internal/utesting" 29 "github.com/juliankolbe/go-ethereum/p2p" 30 "github.com/juliankolbe/go-ethereum/p2p/rlpx" 31 "github.com/juliankolbe/go-ethereum/rlp" 32 ) 33 34 type Message interface { 35 Code() int 36 } 37 38 type Error struct { 39 err error 40 } 41 42 func (e *Error) Unwrap() error { return e.err } 43 func (e *Error) Error() string { return e.err.Error() } 44 func (e *Error) Code() int { return -1 } 45 func (e *Error) String() string { return e.Error() } 46 47 func errorf(format string, args ...interface{}) *Error { 48 return &Error{fmt.Errorf(format, args...)} 49 } 50 51 // Hello is the RLP structure of the protocol handshake. 52 type Hello struct { 53 Version uint64 54 Name string 55 Caps []p2p.Cap 56 ListenPort uint64 57 ID []byte // secp256k1 public key 58 59 // Ignore additional fields (for forward compatibility). 60 Rest []rlp.RawValue `rlp:"tail"` 61 } 62 63 func (h Hello) Code() int { return 0x00 } 64 65 // Disconnect is the RLP structure for a disconnect message. 66 type Disconnect struct { 67 Reason p2p.DiscReason 68 } 69 70 func (d Disconnect) Code() int { return 0x01 } 71 72 type Ping struct{} 73 74 func (p Ping) Code() int { return 0x02 } 75 76 type Pong struct{} 77 78 func (p Pong) Code() int { return 0x03 } 79 80 // Status is the network packet for the status message for eth/64 and later. 81 type Status eth.StatusPacket 82 83 func (s Status) Code() int { return 16 } 84 85 // NewBlockHashes is the network packet for the block announcements. 86 type NewBlockHashes eth.NewBlockHashesPacket 87 88 func (nbh NewBlockHashes) Code() int { return 17 } 89 90 type Transactions eth.TransactionsPacket 91 92 func (t Transactions) Code() int { return 18 } 93 94 // GetBlockHeaders represents a block header query. 95 type GetBlockHeaders eth.GetBlockHeadersPacket 96 97 func (g GetBlockHeaders) Code() int { return 19 } 98 99 type BlockHeaders eth.BlockHeadersPacket 100 101 func (bh BlockHeaders) Code() int { return 20 } 102 103 // GetBlockBodies represents a GetBlockBodies request 104 type GetBlockBodies eth.GetBlockBodiesPacket 105 106 func (gbb GetBlockBodies) Code() int { return 21 } 107 108 // BlockBodies is the network packet for block content distribution. 109 type BlockBodies eth.BlockBodiesPacket 110 111 func (bb BlockBodies) Code() int { return 22 } 112 113 // NewBlock is the network packet for the block propagation message. 114 type NewBlock eth.NewBlockPacket 115 116 func (nb NewBlock) Code() int { return 23 } 117 118 // NewPooledTransactionHashes is the network packet for the tx hash propagation message. 119 type NewPooledTransactionHashes eth.NewPooledTransactionHashesPacket 120 121 func (nb NewPooledTransactionHashes) Code() int { return 24 } 122 123 // Conn represents an individual connection with a peer 124 type Conn struct { 125 *rlpx.Conn 126 ourKey *ecdsa.PrivateKey 127 ethProtocolVersion uint 128 caps []p2p.Cap 129 } 130 131 func (c *Conn) Read() Message { 132 code, rawData, _, err := c.Conn.Read() 133 if err != nil { 134 return errorf("could not read from connection: %v", err) 135 } 136 137 var msg Message 138 switch int(code) { 139 case (Hello{}).Code(): 140 msg = new(Hello) 141 case (Ping{}).Code(): 142 msg = new(Ping) 143 case (Pong{}).Code(): 144 msg = new(Pong) 145 case (Disconnect{}).Code(): 146 msg = new(Disconnect) 147 case (Status{}).Code(): 148 msg = new(Status) 149 case (GetBlockHeaders{}).Code(): 150 msg = new(GetBlockHeaders) 151 case (BlockHeaders{}).Code(): 152 msg = new(BlockHeaders) 153 case (GetBlockBodies{}).Code(): 154 msg = new(GetBlockBodies) 155 case (BlockBodies{}).Code(): 156 msg = new(BlockBodies) 157 case (NewBlock{}).Code(): 158 msg = new(NewBlock) 159 case (NewBlockHashes{}).Code(): 160 msg = new(NewBlockHashes) 161 case (Transactions{}).Code(): 162 msg = new(Transactions) 163 case (NewPooledTransactionHashes{}).Code(): 164 msg = new(NewPooledTransactionHashes) 165 default: 166 return errorf("invalid message code: %d", code) 167 } 168 // if message is devp2p, decode here 169 if err := rlp.DecodeBytes(rawData, msg); err != nil { 170 return errorf("could not rlp decode message: %v", err) 171 } 172 return msg 173 } 174 175 // ReadAndServe serves GetBlockHeaders requests while waiting 176 // on another message from the node. 177 func (c *Conn) ReadAndServe(chain *Chain, timeout time.Duration) Message { 178 start := time.Now() 179 for time.Since(start) < timeout { 180 timeout := time.Now().Add(10 * time.Second) 181 c.SetReadDeadline(timeout) 182 switch msg := c.Read().(type) { 183 case *Ping: 184 c.Write(&Pong{}) 185 case *GetBlockHeaders: 186 req := *msg 187 headers, err := chain.GetHeaders(req) 188 if err != nil { 189 return errorf("could not get headers for inbound header request: %v", err) 190 } 191 192 if err := c.Write(headers); err != nil { 193 return errorf("could not write to connection: %v", err) 194 } 195 default: 196 return msg 197 } 198 } 199 return errorf("no message received within %v", timeout) 200 } 201 202 func (c *Conn) Write(msg Message) error { 203 // check if message is eth protocol message 204 var ( 205 payload []byte 206 err error 207 ) 208 payload, err = rlp.EncodeToBytes(msg) 209 if err != nil { 210 return err 211 } 212 _, err = c.Conn.Write(uint64(msg.Code()), payload) 213 return err 214 } 215 216 // handshake checks to make sure a `HELLO` is received. 217 func (c *Conn) handshake(t *utesting.T) Message { 218 defer c.SetDeadline(time.Time{}) 219 c.SetDeadline(time.Now().Add(10 * time.Second)) 220 221 // write hello to client 222 pub0 := crypto.FromECDSAPub(&c.ourKey.PublicKey)[1:] 223 ourHandshake := &Hello{ 224 Version: 5, 225 Caps: c.caps, 226 ID: pub0, 227 } 228 if err := c.Write(ourHandshake); err != nil { 229 t.Fatalf("could not write to connection: %v", err) 230 } 231 // read hello from client 232 switch msg := c.Read().(type) { 233 case *Hello: 234 // set snappy if version is at least 5 235 if msg.Version >= 5 { 236 c.SetSnappy(true) 237 } 238 c.negotiateEthProtocol(msg.Caps) 239 if c.ethProtocolVersion == 0 { 240 t.Fatalf("unexpected eth protocol version") 241 } 242 return msg 243 default: 244 t.Fatalf("bad handshake: %#v", msg) 245 return nil 246 } 247 } 248 249 // negotiateEthProtocol sets the Conn's eth protocol version 250 // to highest advertised capability from peer 251 func (c *Conn) negotiateEthProtocol(caps []p2p.Cap) { 252 var highestEthVersion uint 253 for _, capability := range caps { 254 if capability.Name != "eth" { 255 continue 256 } 257 if capability.Version > highestEthVersion && capability.Version <= 65 { 258 highestEthVersion = capability.Version 259 } 260 } 261 c.ethProtocolVersion = highestEthVersion 262 } 263 264 // statusExchange performs a `Status` message exchange with the given 265 // node. 266 func (c *Conn) statusExchange(t *utesting.T, chain *Chain, status *Status) Message { 267 defer c.SetDeadline(time.Time{}) 268 c.SetDeadline(time.Now().Add(20 * time.Second)) 269 270 // read status message from client 271 var message Message 272 loop: 273 for { 274 switch msg := c.Read().(type) { 275 case *Status: 276 if msg.Head != chain.blocks[chain.Len()-1].Hash() { 277 t.Fatalf("wrong head block in status: %s", msg.Head.String()) 278 } 279 if msg.TD.Cmp(chain.TD(chain.Len())) != 0 { 280 t.Fatalf("wrong TD in status: %v", msg.TD) 281 } 282 if !reflect.DeepEqual(msg.ForkID, chain.ForkID()) { 283 t.Fatalf("wrong fork ID in status: %v", msg.ForkID) 284 } 285 message = msg 286 break loop 287 case *Disconnect: 288 t.Fatalf("disconnect received: %v", msg.Reason) 289 case *Ping: 290 c.Write(&Pong{}) // TODO (renaynay): in the future, this should be an error 291 // (PINGs should not be a response upon fresh connection) 292 default: 293 t.Fatalf("bad status message: %s", pretty.Sdump(msg)) 294 } 295 } 296 // make sure eth protocol version is set for negotiation 297 if c.ethProtocolVersion == 0 { 298 t.Fatalf("eth protocol version must be set in Conn") 299 } 300 if status == nil { 301 // write status message to client 302 status = &Status{ 303 ProtocolVersion: uint32(c.ethProtocolVersion), 304 NetworkID: chain.chainConfig.ChainID.Uint64(), 305 TD: chain.TD(chain.Len()), 306 Head: chain.blocks[chain.Len()-1].Hash(), 307 Genesis: chain.blocks[0].Hash(), 308 ForkID: chain.ForkID(), 309 } 310 } 311 312 if err := c.Write(status); err != nil { 313 t.Fatalf("could not write to connection: %v", err) 314 } 315 316 return message 317 } 318 319 // waitForBlock waits for confirmation from the client that it has 320 // imported the given block. 321 func (c *Conn) waitForBlock(block *types.Block) error { 322 defer c.SetReadDeadline(time.Time{}) 323 324 timeout := time.Now().Add(20 * time.Second) 325 c.SetReadDeadline(timeout) 326 for { 327 req := &GetBlockHeaders{Origin: eth.HashOrNumber{Hash: block.Hash()}, Amount: 1} 328 if err := c.Write(req); err != nil { 329 return err 330 } 331 switch msg := c.Read().(type) { 332 case *BlockHeaders: 333 if len(*msg) > 0 { 334 return nil 335 } 336 time.Sleep(100 * time.Millisecond) 337 default: 338 return fmt.Errorf("invalid message: %s", pretty.Sdump(msg)) 339 } 340 } 341 }