github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/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 "time" 23 24 "github.com/davecgh/go-spew/spew" 25 "github.com/kisexp/xdchain/core/types" 26 "github.com/kisexp/xdchain/crypto" 27 "github.com/kisexp/xdchain/internal/utesting" 28 "github.com/kisexp/xdchain/p2p" 29 "github.com/kisexp/xdchain/p2p/enode" 30 "github.com/kisexp/xdchain/p2p/rlpx" 31 "github.com/stretchr/testify/assert" 32 ) 33 34 var pretty = spew.ConfigState{ 35 Indent: " ", 36 DisableCapacities: true, 37 DisablePointerAddresses: true, 38 SortKeys: true, 39 } 40 41 var timeout = 20 * time.Second 42 43 // Suite represents a structure used to test the eth 44 // protocol of a node(s). 45 type Suite struct { 46 Dest *enode.Node 47 48 chain *Chain 49 fullChain *Chain 50 } 51 52 // NewSuite creates and returns a new eth-test suite that can 53 // be used to test the given node against the given blockchain 54 // data. 55 func NewSuite(dest *enode.Node, chainfile string, genesisfile string) *Suite { 56 chain, err := loadChain(chainfile, genesisfile) 57 if err != nil { 58 panic(err) 59 } 60 return &Suite{ 61 Dest: dest, 62 chain: chain.Shorten(1000), 63 fullChain: chain, 64 } 65 } 66 67 func (s *Suite) AllTests() []utesting.Test { 68 return []utesting.Test{ 69 {Name: "Status", Fn: s.TestStatus}, 70 {Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders}, 71 {Name: "Broadcast", Fn: s.TestBroadcast}, 72 {Name: "GetBlockBodies", Fn: s.TestGetBlockBodies}, 73 {Name: "TestLargeAnnounce", Fn: s.TestLargeAnnounce}, 74 {Name: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake}, 75 {Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus}, 76 {Name: "TestTransactions", Fn: s.TestTransaction}, 77 {Name: "TestMaliciousTransactions", Fn: s.TestMaliciousTx}, 78 } 79 } 80 81 // TestStatus attempts to connect to the given node and exchange 82 // a status message with it, and then check to make sure 83 // the chain head is correct. 84 func (s *Suite) TestStatus(t *utesting.T) { 85 conn, err := s.dial() 86 if err != nil { 87 t.Fatalf("could not dial: %v", err) 88 } 89 // get protoHandshake 90 conn.handshake(t) 91 // get status 92 switch msg := conn.statusExchange(t, s.chain, nil).(type) { 93 case *Status: 94 t.Logf("got status message: %s", pretty.Sdump(msg)) 95 default: 96 t.Fatalf("unexpected: %s", pretty.Sdump(msg)) 97 } 98 } 99 100 // TestMaliciousStatus sends a status package with a large total difficulty. 101 func (s *Suite) TestMaliciousStatus(t *utesting.T) { 102 conn, err := s.dial() 103 if err != nil { 104 t.Fatalf("could not dial: %v", err) 105 } 106 // get protoHandshake 107 conn.handshake(t) 108 status := &Status{ 109 ProtocolVersion: uint32(conn.ethProtocolVersion), 110 NetworkID: s.chain.chainConfig.ChainID.Uint64(), 111 TD: largeNumber(2), 112 Head: s.chain.blocks[s.chain.Len()-1].Hash(), 113 Genesis: s.chain.blocks[0].Hash(), 114 ForkID: s.chain.ForkID(), 115 } 116 // get status 117 switch msg := conn.statusExchange(t, s.chain, status).(type) { 118 case *Status: 119 t.Logf("%+v\n", msg) 120 default: 121 t.Fatalf("expected status, got: %#v ", msg) 122 } 123 // wait for disconnect 124 switch msg := conn.ReadAndServe(s.chain, timeout).(type) { 125 case *Disconnect: 126 case *Error: 127 return 128 default: 129 t.Fatalf("expected disconnect, got: %s", pretty.Sdump(msg)) 130 } 131 } 132 133 // TestGetBlockHeaders tests whether the given node can respond to 134 // a `GetBlockHeaders` request and that the response is accurate. 135 func (s *Suite) TestGetBlockHeaders(t *utesting.T) { 136 conn, err := s.dial() 137 if err != nil { 138 t.Fatalf("could not dial: %v", err) 139 } 140 141 conn.handshake(t) 142 conn.statusExchange(t, s.chain, nil) 143 144 // get block headers 145 req := &GetBlockHeaders{ 146 Origin: hashOrNumber{ 147 Hash: s.chain.blocks[1].Hash(), 148 }, 149 Amount: 2, 150 Skip: 1, 151 Reverse: false, 152 } 153 154 if err := conn.Write(req); err != nil { 155 t.Fatalf("could not write to connection: %v", err) 156 } 157 158 switch msg := conn.ReadAndServe(s.chain, timeout).(type) { 159 case *BlockHeaders: 160 headers := msg 161 for _, header := range *headers { 162 num := header.Number.Uint64() 163 t.Logf("received header (%d): %s", num, pretty.Sdump(header)) 164 assert.Equal(t, s.chain.blocks[int(num)].Header(), header) 165 } 166 default: 167 t.Fatalf("unexpected: %s", pretty.Sdump(msg)) 168 } 169 } 170 171 // TestGetBlockBodies tests whether the given node can respond to 172 // a `GetBlockBodies` request and that the response is accurate. 173 func (s *Suite) TestGetBlockBodies(t *utesting.T) { 174 conn, err := s.dial() 175 if err != nil { 176 t.Fatalf("could not dial: %v", err) 177 } 178 179 conn.handshake(t) 180 conn.statusExchange(t, s.chain, nil) 181 // create block bodies request 182 req := &GetBlockBodies{s.chain.blocks[54].Hash(), s.chain.blocks[75].Hash()} 183 if err := conn.Write(req); err != nil { 184 t.Fatalf("could not write to connection: %v", err) 185 } 186 187 switch msg := conn.ReadAndServe(s.chain, timeout).(type) { 188 case *BlockBodies: 189 t.Logf("received %d block bodies", len(*msg)) 190 default: 191 t.Fatalf("unexpected: %s", pretty.Sdump(msg)) 192 } 193 } 194 195 // TestBroadcast tests whether a block announcement is correctly 196 // propagated to the given node's peer(s). 197 func (s *Suite) TestBroadcast(t *utesting.T) { 198 sendConn, receiveConn := s.setupConnection(t), s.setupConnection(t) 199 nextBlock := len(s.chain.blocks) 200 blockAnnouncement := &NewBlock{ 201 Block: s.fullChain.blocks[nextBlock], 202 TD: s.fullChain.TD(nextBlock + 1), 203 } 204 s.testAnnounce(t, sendConn, receiveConn, blockAnnouncement) 205 // update test suite chain 206 s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[nextBlock]) 207 // wait for client to update its chain 208 if err := receiveConn.waitForBlock(s.chain.Head()); err != nil { 209 t.Fatal(err) 210 } 211 } 212 213 // TestMaliciousHandshake tries to send malicious data during the handshake. 214 func (s *Suite) TestMaliciousHandshake(t *utesting.T) { 215 conn, err := s.dial() 216 if err != nil { 217 t.Fatalf("could not dial: %v", err) 218 } 219 // write hello to client 220 pub0 := crypto.FromECDSAPub(&conn.ourKey.PublicKey)[1:] 221 handshakes := []*Hello{ 222 { 223 Version: 5, 224 Caps: []p2p.Cap{ 225 {Name: largeString(2), Version: 64}, 226 }, 227 ID: pub0, 228 }, 229 { 230 Version: 5, 231 Caps: []p2p.Cap{ 232 {Name: "eth", Version: 64}, 233 {Name: "eth", Version: 65}, 234 }, 235 ID: append(pub0, byte(0)), 236 }, 237 { 238 Version: 5, 239 Caps: []p2p.Cap{ 240 {Name: "eth", Version: 64}, 241 {Name: "eth", Version: 65}, 242 }, 243 ID: append(pub0, pub0...), 244 }, 245 { 246 Version: 5, 247 Caps: []p2p.Cap{ 248 {Name: "eth", Version: 64}, 249 {Name: "eth", Version: 65}, 250 }, 251 ID: largeBuffer(2), 252 }, 253 { 254 Version: 5, 255 Caps: []p2p.Cap{ 256 {Name: largeString(2), Version: 64}, 257 }, 258 ID: largeBuffer(2), 259 }, 260 } 261 for i, handshake := range handshakes { 262 t.Logf("Testing malicious handshake %v\n", i) 263 // Init the handshake 264 if err := conn.Write(handshake); err != nil { 265 t.Fatalf("could not write to connection: %v", err) 266 } 267 // check that the peer disconnected 268 timeout := 20 * time.Second 269 // Discard one hello 270 for i := 0; i < 2; i++ { 271 switch msg := conn.ReadAndServe(s.chain, timeout).(type) { 272 case *Disconnect: 273 case *Error: 274 case *Hello: 275 // Hello's are send concurrently, so ignore them 276 continue 277 default: 278 t.Fatalf("unexpected: %s", pretty.Sdump(msg)) 279 } 280 } 281 // Dial for the next round 282 conn, err = s.dial() 283 if err != nil { 284 t.Fatalf("could not dial: %v", err) 285 } 286 } 287 } 288 289 // TestLargeAnnounce tests the announcement mechanism with a large block. 290 func (s *Suite) TestLargeAnnounce(t *utesting.T) { 291 nextBlock := len(s.chain.blocks) 292 blocks := []*NewBlock{ 293 { 294 Block: largeBlock(), 295 TD: s.fullChain.TD(nextBlock + 1), 296 }, 297 { 298 Block: s.fullChain.blocks[nextBlock], 299 TD: largeNumber(2), 300 }, 301 { 302 Block: largeBlock(), 303 TD: largeNumber(2), 304 }, 305 { 306 Block: s.fullChain.blocks[nextBlock], 307 TD: s.fullChain.TD(nextBlock + 1), 308 }, 309 } 310 311 for i, blockAnnouncement := range blocks[0:3] { 312 t.Logf("Testing malicious announcement: %v\n", i) 313 sendConn := s.setupConnection(t) 314 if err := sendConn.Write(blockAnnouncement); err != nil { 315 t.Fatalf("could not write to connection: %v", err) 316 } 317 // Invalid announcement, check that peer disconnected 318 switch msg := sendConn.ReadAndServe(s.chain, timeout).(type) { 319 case *Disconnect: 320 case *Error: 321 break 322 default: 323 t.Fatalf("unexpected: %s wanted disconnect", pretty.Sdump(msg)) 324 } 325 } 326 // Test the last block as a valid block 327 sendConn := s.setupConnection(t) 328 receiveConn := s.setupConnection(t) 329 s.testAnnounce(t, sendConn, receiveConn, blocks[3]) 330 // update test suite chain 331 s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[nextBlock]) 332 // wait for client to update its chain 333 if err := receiveConn.waitForBlock(s.fullChain.blocks[nextBlock]); err != nil { 334 t.Fatal(err) 335 } 336 } 337 338 func (s *Suite) testAnnounce(t *utesting.T, sendConn, receiveConn *Conn, blockAnnouncement *NewBlock) { 339 // Announce the block. 340 if err := sendConn.Write(blockAnnouncement); err != nil { 341 t.Fatalf("could not write to connection: %v", err) 342 } 343 s.waitAnnounce(t, receiveConn, blockAnnouncement) 344 } 345 346 func (s *Suite) waitAnnounce(t *utesting.T, conn *Conn, blockAnnouncement *NewBlock) { 347 timeout := 20 * time.Second 348 switch msg := conn.ReadAndServe(s.chain, timeout).(type) { 349 case *NewBlock: 350 t.Logf("received NewBlock message: %s", pretty.Sdump(msg.Block)) 351 assert.Equal(t, 352 blockAnnouncement.Block.Header(), msg.Block.Header(), 353 "wrong block header in announcement", 354 ) 355 assert.Equal(t, 356 blockAnnouncement.TD, msg.TD, 357 "wrong TD in announcement", 358 ) 359 case *NewBlockHashes: 360 hashes := *msg 361 t.Logf("received NewBlockHashes message: %s", pretty.Sdump(hashes)) 362 assert.Equal(t, 363 blockAnnouncement.Block.Hash(), hashes[0].Hash, 364 "wrong block hash in announcement", 365 ) 366 default: 367 t.Fatalf("unexpected: %s", pretty.Sdump(msg)) 368 } 369 } 370 371 func (s *Suite) setupConnection(t *utesting.T) *Conn { 372 // create conn 373 sendConn, err := s.dial() 374 if err != nil { 375 t.Fatalf("could not dial: %v", err) 376 } 377 sendConn.handshake(t) 378 sendConn.statusExchange(t, s.chain, nil) 379 return sendConn 380 } 381 382 // dial attempts to dial the given node and perform a handshake, 383 // returning the created Conn if successful. 384 func (s *Suite) dial() (*Conn, error) { 385 var conn Conn 386 387 fd, err := net.Dial("tcp", fmt.Sprintf("%v:%d", s.Dest.IP(), s.Dest.TCP())) 388 if err != nil { 389 return nil, err 390 } 391 conn.Conn = rlpx.NewConn(fd, s.Dest.Pubkey()) 392 393 // do encHandshake 394 conn.ourKey, _ = crypto.GenerateKey() 395 _, err = conn.Handshake(conn.ourKey) 396 if err != nil { 397 return nil, err 398 } 399 400 return &conn, nil 401 } 402 403 func (s *Suite) TestTransaction(t *utesting.T) { 404 tests := []*types.Transaction{ 405 getNextTxFromChain(t, s), 406 unknownTx(t, s), 407 } 408 for i, tx := range tests { 409 t.Logf("Testing tx propagation: %v\n", i) 410 sendSuccessfulTx(t, s, tx) 411 } 412 } 413 414 func (s *Suite) TestMaliciousTx(t *utesting.T) { 415 tests := []*types.Transaction{ 416 getOldTxFromChain(t, s), 417 invalidNonceTx(t, s), 418 hugeAmount(t, s), 419 hugeGasPrice(t, s), 420 hugeData(t, s), 421 } 422 for i, tx := range tests { 423 t.Logf("Testing malicious tx propagation: %v\n", i) 424 sendFailingTx(t, s, tx) 425 } 426 }