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