github.com/fff-chain/go-fff@v0.0.0-20220726032732-1c84420b8a99/cmd/devp2p/internal/ethtest/suite.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 "fmt" 21 "net" 22 "strings" 23 "time" 24 25 "github.com/davecgh/go-spew/spew" 26 "github.com/fff-chain/go-fff/core/types" 27 "github.com/fff-chain/go-fff/crypto" 28 "github.com/fff-chain/go-fff/eth/protocols/eth" 29 "github.com/fff-chain/go-fff/internal/utesting" 30 "github.com/fff-chain/go-fff/p2p" 31 "github.com/fff-chain/go-fff/p2p/enode" 32 "github.com/fff-chain/go-fff/p2p/rlpx" 33 "github.com/stretchr/testify/assert" 34 ) 35 36 var pretty = spew.ConfigState{ 37 Indent: " ", 38 DisableCapacities: true, 39 DisablePointerAddresses: true, 40 SortKeys: true, 41 } 42 43 var timeout = 20 * time.Second 44 45 // Suite represents a structure used to test the eth 46 // protocol of a node(s). 47 type Suite struct { 48 Dest *enode.Node 49 50 chain *Chain 51 fullChain *Chain 52 } 53 54 // NewSuite creates and returns a new eth-test suite that can 55 // be used to test the given node against the given blockchain 56 // data. 57 func NewSuite(dest *enode.Node, chainfile string, genesisfile string) (*Suite, error) { 58 chain, err := loadChain(chainfile, genesisfile) 59 if err != nil { 60 return nil, err 61 } 62 return &Suite{ 63 Dest: dest, 64 chain: chain.Shorten(1000), 65 fullChain: chain, 66 }, nil 67 } 68 69 func (s *Suite) AllEthTests() []utesting.Test { 70 return []utesting.Test{ 71 // status 72 {Name: "TestStatus", Fn: s.TestStatus}, 73 {Name: "TestStatus_66", Fn: s.TestStatus_66}, 74 // get block headers 75 {Name: "TestGetBlockHeaders", Fn: s.TestGetBlockHeaders}, 76 {Name: "TestGetBlockHeaders_66", Fn: s.TestGetBlockHeaders_66}, 77 {Name: "TestSimultaneousRequests_66", Fn: s.TestSimultaneousRequests_66}, 78 {Name: "TestSameRequestID_66", Fn: s.TestSameRequestID_66}, 79 {Name: "TestZeroRequestID_66", Fn: s.TestZeroRequestID_66}, 80 // get block bodies 81 {Name: "TestGetBlockBodies", Fn: s.TestGetBlockBodies}, 82 {Name: "TestGetBlockBodies_66", Fn: s.TestGetBlockBodies_66}, 83 // broadcast 84 {Name: "TestBroadcast", Fn: s.TestBroadcast}, 85 {Name: "TestBroadcast_66", Fn: s.TestBroadcast_66}, 86 {Name: "TestLargeAnnounce", Fn: s.TestLargeAnnounce}, 87 {Name: "TestLargeAnnounce_66", Fn: s.TestLargeAnnounce_66}, 88 {Name: "TestOldAnnounce", Fn: s.TestOldAnnounce}, 89 {Name: "TestOldAnnounce_66", Fn: s.TestOldAnnounce_66}, 90 // malicious handshakes + status 91 {Name: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake}, 92 {Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus}, 93 {Name: "TestMaliciousHandshake_66", Fn: s.TestMaliciousHandshake_66}, 94 {Name: "TestMaliciousStatus_66", Fn: s.TestMaliciousStatus_66}, 95 // test transactions 96 {Name: "TestTransaction", Fn: s.TestTransaction}, 97 {Name: "TestTransaction_66", Fn: s.TestTransaction_66}, 98 {Name: "TestMaliciousTx", Fn: s.TestMaliciousTx}, 99 {Name: "TestMaliciousTx_66", Fn: s.TestMaliciousTx_66}, 100 {Name: "TestLargeTxRequest_66", Fn: s.TestLargeTxRequest_66}, 101 {Name: "TestNewPooledTxs_66", Fn: s.TestNewPooledTxs_66}, 102 } 103 } 104 105 func (s *Suite) EthTests() []utesting.Test { 106 return []utesting.Test{ 107 {Name: "TestStatus", Fn: s.TestStatus}, 108 {Name: "TestGetBlockHeaders", Fn: s.TestGetBlockHeaders}, 109 {Name: "TestGetBlockBodies", Fn: s.TestGetBlockBodies}, 110 {Name: "TestBroadcast", Fn: s.TestBroadcast}, 111 {Name: "TestLargeAnnounce", Fn: s.TestLargeAnnounce}, 112 {Name: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake}, 113 {Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus}, 114 {Name: "TestTransaction", Fn: s.TestTransaction}, 115 {Name: "TestMaliciousTx", Fn: s.TestMaliciousTx}, 116 } 117 } 118 119 func (s *Suite) Eth66Tests() []utesting.Test { 120 return []utesting.Test{ 121 // only proceed with eth66 test suite if node supports eth 66 protocol 122 {Name: "TestStatus_66", Fn: s.TestStatus_66}, 123 {Name: "TestGetBlockHeaders_66", Fn: s.TestGetBlockHeaders_66}, 124 {Name: "TestSimultaneousRequests_66", Fn: s.TestSimultaneousRequests_66}, 125 {Name: "TestSameRequestID_66", Fn: s.TestSameRequestID_66}, 126 {Name: "TestZeroRequestID_66", Fn: s.TestZeroRequestID_66}, 127 {Name: "TestGetBlockBodies_66", Fn: s.TestGetBlockBodies_66}, 128 {Name: "TestBroadcast_66", Fn: s.TestBroadcast_66}, 129 {Name: "TestLargeAnnounce_66", Fn: s.TestLargeAnnounce_66}, 130 {Name: "TestMaliciousHandshake_66", Fn: s.TestMaliciousHandshake_66}, 131 {Name: "TestMaliciousStatus_66", Fn: s.TestMaliciousStatus_66}, 132 {Name: "TestTransaction_66", Fn: s.TestTransaction_66}, 133 {Name: "TestMaliciousTx_66", Fn: s.TestMaliciousTx_66}, 134 {Name: "TestLargeTxRequest_66", Fn: s.TestLargeTxRequest_66}, 135 {Name: "TestNewPooledTxs_66", Fn: s.TestNewPooledTxs_66}, 136 } 137 } 138 139 // TestStatus attempts to connect to the given node and exchange 140 // a status message with it, and then check to make sure 141 // the chain head is correct. 142 func (s *Suite) TestStatus(t *utesting.T) { 143 conn, err := s.dial() 144 if err != nil { 145 t.Fatalf("could not dial: %v", err) 146 } 147 defer conn.Close() 148 // get protoHandshake 149 conn.handshake(t) 150 // get status 151 switch msg := conn.statusExchange(t, s.chain, nil).(type) { 152 case *Status: 153 t.Logf("got status message: %s", pretty.Sdump(msg)) 154 default: 155 t.Fatalf("unexpected: %s", pretty.Sdump(msg)) 156 } 157 } 158 159 // TestMaliciousStatus sends a status package with a large total difficulty. 160 func (s *Suite) TestMaliciousStatus(t *utesting.T) { 161 conn, err := s.dial() 162 if err != nil { 163 t.Fatalf("could not dial: %v", err) 164 } 165 defer conn.Close() 166 // get protoHandshake 167 conn.handshake(t) 168 status := &Status{ 169 ProtocolVersion: uint32(conn.negotiatedProtoVersion), 170 NetworkID: s.chain.chainConfig.ChainID.Uint64(), 171 TD: largeNumber(2), 172 Head: s.chain.blocks[s.chain.Len()-1].Hash(), 173 Genesis: s.chain.blocks[0].Hash(), 174 ForkID: s.chain.ForkID(), 175 } 176 // get status 177 switch msg := conn.statusExchange(t, s.chain, status).(type) { 178 case *Status: 179 t.Logf("%+v\n", msg) 180 default: 181 t.Fatalf("expected status, got: %#v ", msg) 182 } 183 // wait for disconnect 184 switch msg := conn.ReadAndServe(s.chain, timeout).(type) { 185 case *Disconnect: 186 case *Error: 187 return 188 default: 189 t.Fatalf("expected disconnect, got: %s", pretty.Sdump(msg)) 190 } 191 } 192 193 // TestGetBlockHeaders tests whether the given node can respond to 194 // a `GetBlockHeaders` request and that the response is accurate. 195 func (s *Suite) TestGetBlockHeaders(t *utesting.T) { 196 conn, err := s.dial() 197 if err != nil { 198 t.Fatalf("could not dial: %v", err) 199 } 200 defer conn.Close() 201 202 conn.handshake(t) 203 conn.statusExchange(t, s.chain, nil) 204 205 // get block headers 206 req := &GetBlockHeaders{ 207 Origin: eth.HashOrNumber{ 208 Hash: s.chain.blocks[1].Hash(), 209 }, 210 Amount: 2, 211 Skip: 1, 212 Reverse: false, 213 } 214 215 if err := conn.Write(req); err != nil { 216 t.Fatalf("could not write to connection: %v", err) 217 } 218 219 switch msg := conn.ReadAndServe(s.chain, timeout).(type) { 220 case *BlockHeaders: 221 headers := *msg 222 for _, header := range headers { 223 num := header.Number.Uint64() 224 t.Logf("received header (%d): %s", num, pretty.Sdump(header.Hash())) 225 assert.Equal(t, s.chain.blocks[int(num)].Header(), header) 226 } 227 default: 228 t.Fatalf("unexpected: %s", pretty.Sdump(msg)) 229 } 230 } 231 232 // TestGetBlockBodies tests whether the given node can respond to 233 // a `GetBlockBodies` request and that the response is accurate. 234 func (s *Suite) TestGetBlockBodies(t *utesting.T) { 235 conn, err := s.dial() 236 if err != nil { 237 t.Fatalf("could not dial: %v", err) 238 } 239 defer conn.Close() 240 241 conn.handshake(t) 242 conn.statusExchange(t, s.chain, nil) 243 // create block bodies request 244 req := &GetBlockBodies{ 245 s.chain.blocks[54].Hash(), 246 s.chain.blocks[75].Hash(), 247 } 248 if err := conn.Write(req); err != nil { 249 t.Fatalf("could not write to connection: %v", err) 250 } 251 252 switch msg := conn.ReadAndServe(s.chain, timeout).(type) { 253 case *BlockBodies: 254 t.Logf("received %d block bodies", len(*msg)) 255 default: 256 t.Fatalf("unexpected: %s", pretty.Sdump(msg)) 257 } 258 } 259 260 // TestBroadcast tests whether a block announcement is correctly 261 // propagated to the given node's peer(s). 262 func (s *Suite) TestBroadcast(t *utesting.T) { 263 s.sendNextBlock(t) 264 } 265 266 func (s *Suite) sendNextBlock(t *utesting.T) { 267 sendConn, receiveConn := s.setupConnection(t), s.setupConnection(t) 268 defer sendConn.Close() 269 defer receiveConn.Close() 270 271 // create new block announcement 272 nextBlock := len(s.chain.blocks) 273 blockAnnouncement := &NewBlock{ 274 Block: s.fullChain.blocks[nextBlock], 275 TD: s.fullChain.TD(nextBlock + 1), 276 } 277 // send announcement and wait for node to request the header 278 s.testAnnounce(t, sendConn, receiveConn, blockAnnouncement) 279 // wait for client to update its chain 280 if err := receiveConn.waitForBlock(s.fullChain.blocks[nextBlock]); err != nil { 281 t.Fatal(err) 282 } 283 // update test suite chain 284 s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[nextBlock]) 285 } 286 287 // TestMaliciousHandshake tries to send malicious data during the handshake. 288 func (s *Suite) TestMaliciousHandshake(t *utesting.T) { 289 conn, err := s.dial() 290 if err != nil { 291 t.Fatalf("could not dial: %v", err) 292 } 293 defer conn.Close() 294 // write hello to client 295 pub0 := crypto.FromECDSAPub(&conn.ourKey.PublicKey)[1:] 296 handshakes := []*Hello{ 297 { 298 Version: 5, 299 Caps: []p2p.Cap{ 300 {Name: largeString(2), Version: 64}, 301 }, 302 ID: pub0, 303 }, 304 { 305 Version: 5, 306 Caps: []p2p.Cap{ 307 {Name: "eth", Version: 64}, 308 {Name: "eth", Version: 65}, 309 }, 310 ID: append(pub0, byte(0)), 311 }, 312 { 313 Version: 5, 314 Caps: []p2p.Cap{ 315 {Name: "eth", Version: 64}, 316 {Name: "eth", Version: 65}, 317 }, 318 ID: append(pub0, pub0...), 319 }, 320 { 321 Version: 5, 322 Caps: []p2p.Cap{ 323 {Name: "eth", Version: 64}, 324 {Name: "eth", Version: 65}, 325 }, 326 ID: largeBuffer(2), 327 }, 328 { 329 Version: 5, 330 Caps: []p2p.Cap{ 331 {Name: largeString(2), Version: 64}, 332 }, 333 ID: largeBuffer(2), 334 }, 335 } 336 for i, handshake := range handshakes { 337 t.Logf("Testing malicious handshake %v\n", i) 338 // Init the handshake 339 if err := conn.Write(handshake); err != nil { 340 t.Fatalf("could not write to connection: %v", err) 341 } 342 // check that the peer disconnected 343 timeout := 20 * time.Second 344 // Discard one hello 345 for i := 0; i < 2; i++ { 346 switch msg := conn.ReadAndServe(s.chain, timeout).(type) { 347 case *Disconnect: 348 case *Error: 349 case *Hello: 350 // Hello's are send concurrently, so ignore them 351 continue 352 default: 353 t.Fatalf("unexpected: %s", pretty.Sdump(msg)) 354 } 355 } 356 // Dial for the next round 357 conn, err = s.dial() 358 if err != nil { 359 t.Fatalf("could not dial: %v", err) 360 } 361 } 362 } 363 364 // TestLargeAnnounce tests the announcement mechanism with a large block. 365 func (s *Suite) TestLargeAnnounce(t *utesting.T) { 366 nextBlock := len(s.chain.blocks) 367 blocks := []*NewBlock{ 368 { 369 Block: largeBlock(), 370 TD: s.fullChain.TD(nextBlock + 1), 371 }, 372 { 373 Block: s.fullChain.blocks[nextBlock], 374 TD: largeNumber(2), 375 }, 376 { 377 Block: largeBlock(), 378 TD: largeNumber(2), 379 }, 380 { 381 Block: s.fullChain.blocks[nextBlock], 382 TD: s.fullChain.TD(nextBlock + 1), 383 }, 384 } 385 386 for i, blockAnnouncement := range blocks[0:3] { 387 t.Logf("Testing malicious announcement: %v\n", i) 388 sendConn := s.setupConnection(t) 389 if err := sendConn.Write(blockAnnouncement); err != nil { 390 t.Fatalf("could not write to connection: %v", err) 391 } 392 // Invalid announcement, check that peer disconnected 393 switch msg := sendConn.ReadAndServe(s.chain, time.Second*8).(type) { 394 case *Disconnect: 395 case *Error: 396 break 397 default: 398 t.Fatalf("unexpected: %s wanted disconnect", pretty.Sdump(msg)) 399 } 400 sendConn.Close() 401 } 402 // Test the last block as a valid block 403 s.sendNextBlock(t) 404 } 405 406 func (s *Suite) TestOldAnnounce(t *utesting.T) { 407 sendConn, recvConn := s.setupConnection(t), s.setupConnection(t) 408 defer sendConn.Close() 409 defer recvConn.Close() 410 411 s.oldAnnounce(t, sendConn, recvConn) 412 } 413 414 func (s *Suite) oldAnnounce(t *utesting.T, sendConn, receiveConn *Conn) { 415 oldBlockAnnounce := &NewBlock{ 416 Block: s.chain.blocks[len(s.chain.blocks)/2], 417 TD: s.chain.blocks[len(s.chain.blocks)/2].Difficulty(), 418 } 419 420 if err := sendConn.Write(oldBlockAnnounce); err != nil { 421 t.Fatalf("could not write to connection: %v", err) 422 } 423 424 switch msg := receiveConn.ReadAndServe(s.chain, time.Second*8).(type) { 425 case *NewBlock: 426 block := *msg 427 if block.Block.Hash() == oldBlockAnnounce.Block.Hash() { 428 t.Fatalf("unexpected: block propagated: %s", pretty.Sdump(msg)) 429 } 430 case *NewBlockHashes: 431 hashes := *msg 432 for _, hash := range hashes { 433 if hash.Hash == oldBlockAnnounce.Block.Hash() { 434 t.Fatalf("unexpected: block announced: %s", pretty.Sdump(msg)) 435 } 436 } 437 case *Error: 438 errMsg := *msg 439 // check to make sure error is timeout (propagation didn't come through == test successful) 440 if !strings.Contains(errMsg.String(), "timeout") { 441 t.Fatalf("unexpected error: %v", pretty.Sdump(msg)) 442 } 443 default: 444 t.Fatalf("unexpected: %s", pretty.Sdump(msg)) 445 } 446 } 447 448 func (s *Suite) testAnnounce(t *utesting.T, sendConn, receiveConn *Conn, blockAnnouncement *NewBlock) { 449 // Announce the block. 450 if err := sendConn.Write(blockAnnouncement); err != nil { 451 t.Fatalf("could not write to connection: %v", err) 452 } 453 s.waitAnnounce(t, receiveConn, blockAnnouncement) 454 } 455 456 func (s *Suite) waitAnnounce(t *utesting.T, conn *Conn, blockAnnouncement *NewBlock) { 457 switch msg := conn.ReadAndServe(s.chain, timeout).(type) { 458 case *NewBlock: 459 t.Logf("received NewBlock message: %s", pretty.Sdump(msg.Block)) 460 assert.Equal(t, 461 blockAnnouncement.Block.Header(), msg.Block.Header(), 462 "wrong block header in announcement", 463 ) 464 assert.Equal(t, 465 blockAnnouncement.TD, msg.TD, 466 "wrong TD in announcement", 467 ) 468 case *NewBlockHashes: 469 message := *msg 470 t.Logf("received NewBlockHashes message: %s", pretty.Sdump(message)) 471 assert.Equal(t, blockAnnouncement.Block.Hash(), message[0].Hash, 472 "wrong block hash in announcement", 473 ) 474 default: 475 t.Fatalf("unexpected: %s", pretty.Sdump(msg)) 476 } 477 } 478 479 func (s *Suite) setupConnection(t *utesting.T) *Conn { 480 // create conn 481 sendConn, err := s.dial() 482 if err != nil { 483 t.Fatalf("could not dial: %v", err) 484 } 485 sendConn.handshake(t) 486 sendConn.statusExchange(t, s.chain, nil) 487 return sendConn 488 } 489 490 // dial attempts to dial the given node and perform a handshake, 491 // returning the created Conn if successful. 492 func (s *Suite) dial() (*Conn, error) { 493 var conn Conn 494 // dial 495 fd, err := net.Dial("tcp", fmt.Sprintf("%v:%d", s.Dest.IP(), s.Dest.TCP())) 496 if err != nil { 497 return nil, err 498 } 499 conn.Conn = rlpx.NewConn(fd, s.Dest.Pubkey()) 500 // do encHandshake 501 conn.ourKey, _ = crypto.GenerateKey() 502 _, err = conn.Handshake(conn.ourKey) 503 if err != nil { 504 return nil, err 505 } 506 // set default p2p capabilities 507 conn.caps = []p2p.Cap{ 508 {Name: "eth", Version: 64}, 509 {Name: "eth", Version: 65}, 510 } 511 conn.ourHighestProtoVersion = 65 512 return &conn, nil 513 } 514 515 func (s *Suite) TestTransaction(t *utesting.T) { 516 tests := []*types.Transaction{ 517 getNextTxFromChain(t, s), 518 unknownTx(t, s), 519 } 520 for i, tx := range tests { 521 t.Logf("Testing tx propagation: %v\n", i) 522 sendSuccessfulTx(t, s, tx) 523 } 524 } 525 526 func (s *Suite) TestMaliciousTx(t *utesting.T) { 527 badTxs := []*types.Transaction{ 528 getOldTxFromChain(t, s), 529 invalidNonceTx(t, s), 530 hugeAmount(t, s), 531 hugeGasPrice(t, s), 532 hugeData(t, s), 533 } 534 sendConn := s.setupConnection(t) 535 defer sendConn.Close() 536 // set up receiving connection before sending txs to make sure 537 // no announcements are missed 538 recvConn := s.setupConnection(t) 539 defer recvConn.Close() 540 541 for i, tx := range badTxs { 542 t.Logf("Testing malicious tx propagation: %v\n", i) 543 if err := sendConn.Write(&Transactions{tx}); err != nil { 544 t.Fatalf("could not write to connection: %v", err) 545 } 546 547 } 548 // check to make sure bad txs aren't propagated 549 waitForTxPropagation(t, s, badTxs, recvConn) 550 }