github.com/fff-chain/go-fff@v0.0.0-20220726032732-1c84420b8a99/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/fff-chain/go-fff/core/types" 26 "github.com/fff-chain/go-fff/crypto" 27 "github.com/fff-chain/go-fff/eth/protocols/eth" 28 "github.com/fff-chain/go-fff/internal/utesting" 29 "github.com/fff-chain/go-fff/p2p" 30 "github.com/fff-chain/go-fff/p2p/rlpx" 31 "github.com/fff-chain/go-fff/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 type GetPooledTransactions eth.GetPooledTransactionsPacket 124 125 func (gpt GetPooledTransactions) Code() int { return 25 } 126 127 type PooledTransactions eth.PooledTransactionsPacket 128 129 func (pt PooledTransactions) Code() int { return 26 } 130 131 // Conn represents an individual connection with a peer 132 type Conn struct { 133 *rlpx.Conn 134 ourKey *ecdsa.PrivateKey 135 negotiatedProtoVersion uint 136 ourHighestProtoVersion uint 137 caps []p2p.Cap 138 } 139 140 func (c *Conn) Read() Message { 141 code, rawData, _, err := c.Conn.Read() 142 if err != nil { 143 return errorf("could not read from connection: %v", err) 144 } 145 146 var msg Message 147 switch int(code) { 148 case (Hello{}).Code(): 149 msg = new(Hello) 150 case (Ping{}).Code(): 151 msg = new(Ping) 152 case (Pong{}).Code(): 153 msg = new(Pong) 154 case (Disconnect{}).Code(): 155 msg = new(Disconnect) 156 case (Status{}).Code(): 157 msg = new(Status) 158 case (GetBlockHeaders{}).Code(): 159 msg = new(GetBlockHeaders) 160 case (BlockHeaders{}).Code(): 161 msg = new(BlockHeaders) 162 case (GetBlockBodies{}).Code(): 163 msg = new(GetBlockBodies) 164 case (BlockBodies{}).Code(): 165 msg = new(BlockBodies) 166 case (NewBlock{}).Code(): 167 msg = new(NewBlock) 168 case (NewBlockHashes{}).Code(): 169 msg = new(NewBlockHashes) 170 case (Transactions{}).Code(): 171 msg = new(Transactions) 172 case (NewPooledTransactionHashes{}).Code(): 173 msg = new(NewPooledTransactionHashes) 174 case (GetPooledTransactions{}.Code()): 175 msg = new(GetPooledTransactions) 176 case (PooledTransactions{}.Code()): 177 msg = new(PooledTransactions) 178 default: 179 return errorf("invalid message code: %d", code) 180 } 181 // if message is devp2p, decode here 182 if err := rlp.DecodeBytes(rawData, msg); err != nil { 183 return errorf("could not rlp decode message: %v", err) 184 } 185 return msg 186 } 187 188 // ReadAndServe serves GetBlockHeaders requests while waiting 189 // on another message from the node. 190 func (c *Conn) ReadAndServe(chain *Chain, timeout time.Duration) Message { 191 start := time.Now() 192 for time.Since(start) < timeout { 193 c.SetReadDeadline(time.Now().Add(5 * time.Second)) 194 switch msg := c.Read().(type) { 195 case *Ping: 196 c.Write(&Pong{}) 197 case *GetBlockHeaders: 198 req := *msg 199 headers, err := chain.GetHeaders(req) 200 if err != nil { 201 return errorf("could not get headers for inbound header request: %v", err) 202 } 203 204 if err := c.Write(headers); err != nil { 205 return errorf("could not write to connection: %v", err) 206 } 207 default: 208 return msg 209 } 210 } 211 return errorf("no message received within %v", timeout) 212 } 213 214 func (c *Conn) Write(msg Message) error { 215 // check if message is eth protocol message 216 var ( 217 payload []byte 218 err error 219 ) 220 payload, err = rlp.EncodeToBytes(msg) 221 if err != nil { 222 return err 223 } 224 _, err = c.Conn.Write(uint64(msg.Code()), payload) 225 return err 226 } 227 228 // handshake checks to make sure a `HELLO` is received. 229 func (c *Conn) handshake(t *utesting.T) Message { 230 defer c.SetDeadline(time.Time{}) 231 c.SetDeadline(time.Now().Add(10 * time.Second)) 232 233 // write hello to client 234 pub0 := crypto.FromECDSAPub(&c.ourKey.PublicKey)[1:] 235 ourHandshake := &Hello{ 236 Version: 5, 237 Caps: c.caps, 238 ID: pub0, 239 } 240 if err := c.Write(ourHandshake); err != nil { 241 t.Fatalf("could not write to connection: %v", err) 242 } 243 // read hello from client 244 switch msg := c.Read().(type) { 245 case *Hello: 246 // set snappy if version is at least 5 247 if msg.Version >= 5 { 248 c.SetSnappy(true) 249 } 250 c.negotiateEthProtocol(msg.Caps) 251 if c.negotiatedProtoVersion == 0 { 252 t.Fatalf("unexpected eth protocol version") 253 } 254 return msg 255 default: 256 t.Fatalf("bad handshake: %#v", msg) 257 return nil 258 } 259 } 260 261 // negotiateEthProtocol sets the Conn's eth protocol version 262 // to highest advertised capability from peer 263 func (c *Conn) negotiateEthProtocol(caps []p2p.Cap) { 264 var highestEthVersion uint 265 for _, capability := range caps { 266 if capability.Name != "eth" { 267 continue 268 } 269 if capability.Version > highestEthVersion && capability.Version <= c.ourHighestProtoVersion { 270 highestEthVersion = capability.Version 271 } 272 } 273 c.negotiatedProtoVersion = highestEthVersion 274 } 275 276 // statusExchange performs a `Status` message exchange with the given 277 // node. 278 func (c *Conn) statusExchange(t *utesting.T, chain *Chain, status *Status) Message { 279 defer c.SetDeadline(time.Time{}) 280 c.SetDeadline(time.Now().Add(20 * time.Second)) 281 282 // read status message from client 283 var message Message 284 loop: 285 for { 286 switch msg := c.Read().(type) { 287 case *Status: 288 if have, want := msg.Head, chain.blocks[chain.Len()-1].Hash(); have != want { 289 t.Fatalf("wrong head block in status, want: %#x (block %d) have %#x", 290 want, chain.blocks[chain.Len()-1].NumberU64(), have) 291 } 292 if have, want := msg.TD.Cmp(chain.TD(chain.Len())), 0; have != want { 293 t.Fatalf("wrong TD in status: have %v want %v", have, want) 294 } 295 if have, want := msg.ForkID, chain.ForkID(); !reflect.DeepEqual(have, want) { 296 t.Fatalf("wrong fork ID in status: have %v, want %v", have, want) 297 } 298 message = msg 299 break loop 300 case *Disconnect: 301 t.Fatalf("disconnect received: %v", msg.Reason) 302 case *Ping: 303 c.Write(&Pong{}) // TODO (renaynay): in the future, this should be an error 304 // (PINGs should not be a response upon fresh connection) 305 default: 306 t.Fatalf("bad status message: %s", pretty.Sdump(msg)) 307 } 308 } 309 // make sure eth protocol version is set for negotiation 310 if c.negotiatedProtoVersion == 0 { 311 t.Fatalf("eth protocol version must be set in Conn") 312 } 313 if status == nil { 314 // write status message to client 315 status = &Status{ 316 ProtocolVersion: uint32(c.negotiatedProtoVersion), 317 NetworkID: chain.chainConfig.ChainID.Uint64(), 318 TD: chain.TD(chain.Len()), 319 Head: chain.blocks[chain.Len()-1].Hash(), 320 Genesis: chain.blocks[0].Hash(), 321 ForkID: chain.ForkID(), 322 } 323 } 324 325 if err := c.Write(status); err != nil { 326 t.Fatalf("could not write to connection: %v", err) 327 } 328 329 return message 330 } 331 332 // waitForBlock waits for confirmation from the client that it has 333 // imported the given block. 334 func (c *Conn) waitForBlock(block *types.Block) error { 335 defer c.SetReadDeadline(time.Time{}) 336 337 c.SetReadDeadline(time.Now().Add(20 * time.Second)) 338 // note: if the node has not yet imported the block, it will respond 339 // to the GetBlockHeaders request with an empty BlockHeaders response, 340 // so the GetBlockHeaders request must be sent again until the BlockHeaders 341 // response contains the desired header. 342 for { 343 req := &GetBlockHeaders{Origin: eth.HashOrNumber{Hash: block.Hash()}, Amount: 1} 344 if err := c.Write(req); err != nil { 345 return err 346 } 347 switch msg := c.Read().(type) { 348 case *BlockHeaders: 349 for _, header := range *msg { 350 if header.Number.Uint64() == block.NumberU64() { 351 return nil 352 } 353 } 354 time.Sleep(100 * time.Millisecond) 355 default: 356 return fmt.Errorf("invalid message: %s", pretty.Sdump(msg)) 357 } 358 } 359 }