github.com/CommerciumBlockchain/go-commercium@v0.0.0-20220709212705-b46438a77516/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 "io" 23 "math/big" 24 "reflect" 25 "time" 26 27 "github.com/CommerciumBlockchain/go-commercium/common" 28 "github.com/CommerciumBlockchain/go-commercium/core/forkid" 29 "github.com/CommerciumBlockchain/go-commercium/core/types" 30 "github.com/CommerciumBlockchain/go-commercium/crypto" 31 "github.com/CommerciumBlockchain/go-commercium/internal/utesting" 32 "github.com/CommerciumBlockchain/go-commercium/p2p" 33 "github.com/CommerciumBlockchain/go-commercium/p2p/rlpx" 34 "github.com/CommerciumBlockchain/go-commercium/rlp" 35 ) 36 37 type Message interface { 38 Code() int 39 } 40 41 type Error struct { 42 err error 43 } 44 45 func (e *Error) Unwrap() error { return e.err } 46 func (e *Error) Error() string { return e.err.Error() } 47 func (e *Error) Code() int { return -1 } 48 func (e *Error) String() string { return e.Error() } 49 50 func errorf(format string, args ...interface{}) *Error { 51 return &Error{fmt.Errorf(format, args...)} 52 } 53 54 // Hello is the RLP structure of the protocol handshake. 55 type Hello struct { 56 Version uint64 57 Name string 58 Caps []p2p.Cap 59 ListenPort uint64 60 ID []byte // secp256k1 public key 61 62 // Ignore additional fields (for forward compatibility). 63 Rest []rlp.RawValue `rlp:"tail"` 64 } 65 66 func (h Hello) Code() int { return 0x00 } 67 68 // Disconnect is the RLP structure for a disconnect message. 69 type Disconnect struct { 70 Reason p2p.DiscReason 71 } 72 73 func (d Disconnect) Code() int { return 0x01 } 74 75 type Ping struct{} 76 77 func (p Ping) Code() int { return 0x02 } 78 79 type Pong struct{} 80 81 func (p Pong) Code() int { return 0x03 } 82 83 // Status is the network packet for the status message for eth/64 and later. 84 type Status struct { 85 ProtocolVersion uint32 86 NetworkID uint64 87 TD *big.Int 88 Head common.Hash 89 Genesis common.Hash 90 ForkID forkid.ID 91 } 92 93 func (s Status) Code() int { return 16 } 94 95 // NewBlockHashes is the network packet for the block announcements. 96 type NewBlockHashes []struct { 97 Hash common.Hash // Hash of one particular block being announced 98 Number uint64 // Number of one particular block being announced 99 } 100 101 func (nbh NewBlockHashes) Code() int { return 17 } 102 103 type Transactions []*types.Transaction 104 105 func (t Transactions) Code() int { return 18 } 106 107 // GetBlockHeaders represents a block header query. 108 type GetBlockHeaders struct { 109 Origin hashOrNumber // Block from which to retrieve headers 110 Amount uint64 // Maximum number of headers to retrieve 111 Skip uint64 // Blocks to skip between consecutive headers 112 Reverse bool // Query direction (false = rising towards latest, true = falling towards genesis) 113 } 114 115 func (g GetBlockHeaders) Code() int { return 19 } 116 117 type BlockHeaders []*types.Header 118 119 func (bh BlockHeaders) Code() int { return 20 } 120 121 // GetBlockBodies represents a GetBlockBodies request 122 type GetBlockBodies []common.Hash 123 124 func (gbb GetBlockBodies) Code() int { return 21 } 125 126 // BlockBodies is the network packet for block content distribution. 127 type BlockBodies []*types.Body 128 129 func (bb BlockBodies) Code() int { return 22 } 130 131 // NewBlock is the network packet for the block propagation message. 132 type NewBlock struct { 133 Block *types.Block 134 TD *big.Int 135 } 136 137 func (nb NewBlock) Code() int { return 23 } 138 139 // NewPooledTransactionHashes is the network packet for the tx hash propagation message. 140 type NewPooledTransactionHashes [][32]byte 141 142 func (nb NewPooledTransactionHashes) Code() int { return 24 } 143 144 // HashOrNumber is a combined field for specifying an origin block. 145 type hashOrNumber struct { 146 Hash common.Hash // Block hash from which to retrieve headers (excludes Number) 147 Number uint64 // Block hash from which to retrieve headers (excludes Hash) 148 } 149 150 // EncodeRLP is a specialized encoder for hashOrNumber to encode only one of the 151 // two contained union fields. 152 func (hn *hashOrNumber) EncodeRLP(w io.Writer) error { 153 if hn.Hash == (common.Hash{}) { 154 return rlp.Encode(w, hn.Number) 155 } 156 if hn.Number != 0 { 157 return fmt.Errorf("both origin hash (%x) and number (%d) provided", hn.Hash, hn.Number) 158 } 159 return rlp.Encode(w, hn.Hash) 160 } 161 162 // DecodeRLP is a specialized decoder for hashOrNumber to decode the contents 163 // into either a block hash or a block number. 164 func (hn *hashOrNumber) DecodeRLP(s *rlp.Stream) error { 165 _, size, _ := s.Kind() 166 origin, err := s.Raw() 167 if err == nil { 168 switch { 169 case size == 32: 170 err = rlp.DecodeBytes(origin, &hn.Hash) 171 case size <= 8: 172 err = rlp.DecodeBytes(origin, &hn.Number) 173 default: 174 err = fmt.Errorf("invalid input size %d for origin", size) 175 } 176 } 177 return err 178 } 179 180 // Conn represents an individual connection with a peer 181 type Conn struct { 182 *rlpx.Conn 183 ourKey *ecdsa.PrivateKey 184 ethProtocolVersion uint 185 } 186 187 func (c *Conn) Read() Message { 188 code, rawData, _, err := c.Conn.Read() 189 if err != nil { 190 return errorf("could not read from connection: %v", err) 191 } 192 193 var msg Message 194 switch int(code) { 195 case (Hello{}).Code(): 196 msg = new(Hello) 197 case (Ping{}).Code(): 198 msg = new(Ping) 199 case (Pong{}).Code(): 200 msg = new(Pong) 201 case (Disconnect{}).Code(): 202 msg = new(Disconnect) 203 case (Status{}).Code(): 204 msg = new(Status) 205 case (GetBlockHeaders{}).Code(): 206 msg = new(GetBlockHeaders) 207 case (BlockHeaders{}).Code(): 208 msg = new(BlockHeaders) 209 case (GetBlockBodies{}).Code(): 210 msg = new(GetBlockBodies) 211 case (BlockBodies{}).Code(): 212 msg = new(BlockBodies) 213 case (NewBlock{}).Code(): 214 msg = new(NewBlock) 215 case (NewBlockHashes{}).Code(): 216 msg = new(NewBlockHashes) 217 case (Transactions{}).Code(): 218 msg = new(Transactions) 219 case (NewPooledTransactionHashes{}).Code(): 220 msg = new(NewPooledTransactionHashes) 221 default: 222 return errorf("invalid message code: %d", code) 223 } 224 225 if err := rlp.DecodeBytes(rawData, msg); err != nil { 226 return errorf("could not rlp decode message: %v", err) 227 } 228 return msg 229 } 230 231 // ReadAndServe serves GetBlockHeaders requests while waiting 232 // on another message from the node. 233 func (c *Conn) ReadAndServe(chain *Chain, timeout time.Duration) Message { 234 start := time.Now() 235 for time.Since(start) < timeout { 236 timeout := time.Now().Add(10 * time.Second) 237 c.SetReadDeadline(timeout) 238 switch msg := c.Read().(type) { 239 case *Ping: 240 c.Write(&Pong{}) 241 case *GetBlockHeaders: 242 req := *msg 243 headers, err := chain.GetHeaders(req) 244 if err != nil { 245 return errorf("could not get headers for inbound header request: %v", err) 246 } 247 248 if err := c.Write(headers); err != nil { 249 return errorf("could not write to connection: %v", err) 250 } 251 default: 252 return msg 253 } 254 } 255 return errorf("no message received within %v", timeout) 256 } 257 258 func (c *Conn) Write(msg Message) error { 259 payload, err := rlp.EncodeToBytes(msg) 260 if err != nil { 261 return err 262 } 263 _, err = c.Conn.Write(uint64(msg.Code()), payload) 264 return err 265 } 266 267 // handshake checks to make sure a `HELLO` is received. 268 func (c *Conn) handshake(t *utesting.T) Message { 269 defer c.SetDeadline(time.Time{}) 270 c.SetDeadline(time.Now().Add(10 * time.Second)) 271 272 // write hello to client 273 pub0 := crypto.FromECDSAPub(&c.ourKey.PublicKey)[1:] 274 ourHandshake := &Hello{ 275 Version: 5, 276 Caps: []p2p.Cap{ 277 {Name: "eth", Version: 64}, 278 {Name: "eth", Version: 65}, 279 }, 280 ID: pub0, 281 } 282 if err := c.Write(ourHandshake); err != nil { 283 t.Fatalf("could not write to connection: %v", err) 284 } 285 // read hello from client 286 switch msg := c.Read().(type) { 287 case *Hello: 288 // set snappy if version is at least 5 289 if msg.Version >= 5 { 290 c.SetSnappy(true) 291 } 292 c.negotiateEthProtocol(msg.Caps) 293 if c.ethProtocolVersion == 0 { 294 t.Fatalf("unexpected eth protocol version") 295 } 296 return msg 297 default: 298 t.Fatalf("bad handshake: %#v", msg) 299 return nil 300 } 301 } 302 303 // negotiateEthProtocol sets the Conn's eth protocol version 304 // to highest advertised capability from peer 305 func (c *Conn) negotiateEthProtocol(caps []p2p.Cap) { 306 var highestEthVersion uint 307 for _, capability := range caps { 308 if capability.Name != "eth" { 309 continue 310 } 311 if capability.Version > highestEthVersion && capability.Version <= 65 { 312 highestEthVersion = capability.Version 313 } 314 } 315 c.ethProtocolVersion = highestEthVersion 316 } 317 318 // statusExchange performs a `Status` message exchange with the given 319 // node. 320 func (c *Conn) statusExchange(t *utesting.T, chain *Chain, status *Status) Message { 321 defer c.SetDeadline(time.Time{}) 322 c.SetDeadline(time.Now().Add(20 * time.Second)) 323 324 // read status message from client 325 var message Message 326 loop: 327 for { 328 switch msg := c.Read().(type) { 329 case *Status: 330 if msg.Head != chain.blocks[chain.Len()-1].Hash() { 331 t.Fatalf("wrong head block in status: %s", msg.Head.String()) 332 } 333 if msg.TD.Cmp(chain.TD(chain.Len())) != 0 { 334 t.Fatalf("wrong TD in status: %v", msg.TD) 335 } 336 if !reflect.DeepEqual(msg.ForkID, chain.ForkID()) { 337 t.Fatalf("wrong fork ID in status: %v", msg.ForkID) 338 } 339 message = msg 340 break loop 341 case *Disconnect: 342 t.Fatalf("disconnect received: %v", msg.Reason) 343 case *Ping: 344 c.Write(&Pong{}) // TODO (renaynay): in the future, this should be an error 345 // (PINGs should not be a response upon fresh connection) 346 default: 347 t.Fatalf("bad status message: %s", pretty.Sdump(msg)) 348 } 349 } 350 // make sure eth protocol version is set for negotiation 351 if c.ethProtocolVersion == 0 { 352 t.Fatalf("eth protocol version must be set in Conn") 353 } 354 if status == nil { 355 // write status message to client 356 status = &Status{ 357 ProtocolVersion: uint32(c.ethProtocolVersion), 358 NetworkID: chain.chainConfig.ChainID.Uint64(), 359 TD: chain.TD(chain.Len()), 360 Head: chain.blocks[chain.Len()-1].Hash(), 361 Genesis: chain.blocks[0].Hash(), 362 ForkID: chain.ForkID(), 363 } 364 } 365 366 if err := c.Write(*status); err != nil { 367 t.Fatalf("could not write to connection: %v", err) 368 } 369 370 return message 371 } 372 373 // waitForBlock waits for confirmation from the client that it has 374 // imported the given block. 375 func (c *Conn) waitForBlock(block *types.Block) error { 376 defer c.SetReadDeadline(time.Time{}) 377 378 timeout := time.Now().Add(20 * time.Second) 379 c.SetReadDeadline(timeout) 380 for { 381 req := &GetBlockHeaders{Origin: hashOrNumber{Hash: block.Hash()}, Amount: 1} 382 if err := c.Write(req); err != nil { 383 return err 384 } 385 switch msg := c.Read().(type) { 386 case *BlockHeaders: 387 if len(*msg) > 0 { 388 return nil 389 } 390 time.Sleep(100 * time.Millisecond) 391 default: 392 return fmt.Errorf("invalid message: %s", pretty.Sdump(msg)) 393 } 394 } 395 }