github.com/ethereum/go-ethereum@v1.16.1/cmd/devp2p/internal/ethtest/suite.go (about) 1 // Copyright 2020 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU 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 // go-ethereum 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 General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 17 package ethtest 18 19 import ( 20 "context" 21 "crypto/rand" 22 "fmt" 23 "reflect" 24 "sync" 25 "time" 26 27 "github.com/ethereum/go-ethereum/common" 28 "github.com/ethereum/go-ethereum/consensus/misc/eip4844" 29 "github.com/ethereum/go-ethereum/core/types" 30 "github.com/ethereum/go-ethereum/crypto" 31 "github.com/ethereum/go-ethereum/crypto/kzg4844" 32 "github.com/ethereum/go-ethereum/eth/protocols/eth" 33 "github.com/ethereum/go-ethereum/internal/utesting" 34 "github.com/ethereum/go-ethereum/p2p" 35 "github.com/ethereum/go-ethereum/p2p/enode" 36 "github.com/holiman/uint256" 37 ) 38 39 // Suite represents a structure used to test a node's conformance 40 // to the eth protocol. 41 type Suite struct { 42 Dest *enode.Node 43 chain *Chain 44 engine *EngineClient 45 } 46 47 // NewSuite creates and returns a new eth-test suite that can 48 // be used to test the given node against the given blockchain 49 // data. 50 func NewSuite(dest *enode.Node, chainDir, engineURL, jwt string) (*Suite, error) { 51 chain, err := NewChain(chainDir) 52 if err != nil { 53 return nil, err 54 } 55 engine, err := NewEngineClient(chainDir, engineURL, jwt) 56 if err != nil { 57 return nil, err 58 } 59 60 return &Suite{ 61 Dest: dest, 62 chain: chain, 63 engine: engine, 64 }, nil 65 } 66 67 func (s *Suite) EthTests() []utesting.Test { 68 return []utesting.Test{ 69 // status 70 {Name: "Status", Fn: s.TestStatus}, 71 {Name: "MaliciousHandshake", Fn: s.TestMaliciousHandshake}, 72 {Name: "BlockRangeUpdateExpired", Fn: s.TestBlockRangeUpdateHistoryExp}, 73 {Name: "BlockRangeUpdateFuture", Fn: s.TestBlockRangeUpdateFuture}, 74 {Name: "BlockRangeUpdateInvalid", Fn: s.TestBlockRangeUpdateInvalid}, 75 // get block headers 76 {Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders}, 77 {Name: "GetNonexistentBlockHeaders", Fn: s.TestGetNonexistentBlockHeaders}, 78 {Name: "SimultaneousRequests", Fn: s.TestSimultaneousRequests}, 79 {Name: "SameRequestID", Fn: s.TestSameRequestID}, 80 {Name: "ZeroRequestID", Fn: s.TestZeroRequestID}, 81 // get history 82 {Name: "GetBlockBodies", Fn: s.TestGetBlockBodies}, 83 {Name: "GetReceipts", Fn: s.TestGetReceipts}, 84 // test transactions 85 {Name: "LargeTxRequest", Fn: s.TestLargeTxRequest, Slow: true}, 86 {Name: "Transaction", Fn: s.TestTransaction}, 87 {Name: "InvalidTxs", Fn: s.TestInvalidTxs}, 88 {Name: "NewPooledTxs", Fn: s.TestNewPooledTxs}, 89 {Name: "BlobViolations", Fn: s.TestBlobViolations}, 90 {Name: "TestBlobTxWithoutSidecar", Fn: s.TestBlobTxWithoutSidecar}, 91 {Name: "TestBlobTxWithMismatchedSidecar", Fn: s.TestBlobTxWithMismatchedSidecar}, 92 } 93 } 94 95 func (s *Suite) SnapTests() []utesting.Test { 96 return []utesting.Test{ 97 {Name: "Status", Fn: s.TestSnapStatus}, 98 {Name: "AccountRange", Fn: s.TestSnapGetAccountRange}, 99 {Name: "GetByteCodes", Fn: s.TestSnapGetByteCodes}, 100 {Name: "GetTrieNodes", Fn: s.TestSnapTrieNodes}, 101 {Name: "GetStorageRanges", Fn: s.TestSnapGetStorageRanges}, 102 } 103 } 104 105 func (s *Suite) TestStatus(t *utesting.T) { 106 t.Log(`This test is just a sanity check. It performs an eth protocol handshake.`) 107 conn, err := s.dialAndPeer(nil) 108 if err != nil { 109 t.Fatal("peering failed:", err) 110 } 111 conn.Close() 112 } 113 114 // headersMatch returns whether the received headers match the given request 115 func headersMatch(expected []*types.Header, headers []*types.Header) bool { 116 return reflect.DeepEqual(expected, headers) 117 } 118 119 func (s *Suite) TestGetBlockHeaders(t *utesting.T) { 120 t.Log(`This test requests block headers from the node.`) 121 conn, err := s.dialAndPeer(nil) 122 if err != nil { 123 t.Fatalf("peering failed: %v", err) 124 } 125 defer conn.Close() 126 127 // Send headers request. 128 req := ð.GetBlockHeadersPacket{ 129 RequestId: 33, 130 GetBlockHeadersRequest: ð.GetBlockHeadersRequest{ 131 Origin: eth.HashOrNumber{Hash: s.chain.blocks[1].Hash()}, 132 Amount: 2, 133 Skip: 1, 134 Reverse: false, 135 }, 136 } 137 // Read headers response. 138 if err := conn.Write(ethProto, eth.GetBlockHeadersMsg, req); err != nil { 139 t.Fatalf("could not write to connection: %v", err) 140 } 141 headers := new(eth.BlockHeadersPacket) 142 if err := conn.ReadMsg(ethProto, eth.BlockHeadersMsg, &headers); err != nil { 143 t.Fatalf("error reading msg: %v", err) 144 } 145 if got, want := headers.RequestId, req.RequestId; got != want { 146 t.Fatalf("unexpected request id") 147 } 148 // Check for correct headers. 149 expected, err := s.chain.GetHeaders(req) 150 if err != nil { 151 t.Fatalf("failed to get headers for given request: %v", err) 152 } 153 if !headersMatch(expected, headers.BlockHeadersRequest) { 154 t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers) 155 } 156 } 157 158 func (s *Suite) TestGetNonexistentBlockHeaders(t *utesting.T) { 159 t.Log(`This test sends GetBlockHeaders requests for nonexistent blocks (using max uint64 value) 160 to check if the node disconnects after receiving multiple invalid requests.`) 161 conn, err := s.dialAndPeer(nil) 162 if err != nil { 163 t.Fatalf("peering failed: %v", err) 164 } 165 defer conn.Close() 166 167 // Create request with max uint64 value for a nonexistent block 168 badReq := ð.GetBlockHeadersPacket{ 169 GetBlockHeadersRequest: ð.GetBlockHeadersRequest{ 170 Origin: eth.HashOrNumber{Number: ^uint64(0)}, 171 Amount: 1, 172 Skip: 0, 173 Reverse: false, 174 }, 175 } 176 177 // Send request 10 times. Some clients are lient on the first few invalids. 178 for i := 0; i < 10; i++ { 179 badReq.RequestId = uint64(i) 180 if err := conn.Write(ethProto, eth.GetBlockHeadersMsg, badReq); err != nil { 181 if err == errDisc { 182 t.Fatalf("peer disconnected after %d requests", i+1) 183 } 184 t.Fatalf("write failed: %v", err) 185 } 186 } 187 188 // Check if peer disconnects at the end. 189 code, _, err := conn.Read() 190 if err == errDisc || code == discMsg { 191 t.Fatal("peer improperly disconnected") 192 } 193 } 194 195 func (s *Suite) TestSimultaneousRequests(t *utesting.T) { 196 t.Log(`This test requests blocks headers from the node, performing two requests 197 concurrently, with different request IDs.`) 198 conn, err := s.dialAndPeer(nil) 199 if err != nil { 200 t.Fatalf("peering failed: %v", err) 201 } 202 defer conn.Close() 203 204 // Create two different requests. 205 req1 := ð.GetBlockHeadersPacket{ 206 RequestId: uint64(111), 207 GetBlockHeadersRequest: ð.GetBlockHeadersRequest{ 208 Origin: eth.HashOrNumber{ 209 Hash: s.chain.blocks[1].Hash(), 210 }, 211 Amount: 2, 212 Skip: 1, 213 Reverse: false, 214 }, 215 } 216 req2 := ð.GetBlockHeadersPacket{ 217 RequestId: uint64(222), 218 GetBlockHeadersRequest: ð.GetBlockHeadersRequest{ 219 Origin: eth.HashOrNumber{ 220 Hash: s.chain.blocks[1].Hash(), 221 }, 222 Amount: 4, 223 Skip: 1, 224 Reverse: false, 225 }, 226 } 227 228 // Send both requests. 229 if err := conn.Write(ethProto, eth.GetBlockHeadersMsg, req1); err != nil { 230 t.Fatalf("failed to write to connection: %v", err) 231 } 232 if err := conn.Write(ethProto, eth.GetBlockHeadersMsg, req2); err != nil { 233 t.Fatalf("failed to write to connection: %v", err) 234 } 235 236 // Wait for responses. 237 headers1 := new(eth.BlockHeadersPacket) 238 if err := conn.ReadMsg(ethProto, eth.BlockHeadersMsg, &headers1); err != nil { 239 t.Fatalf("error reading block headers msg: %v", err) 240 } 241 if got, want := headers1.RequestId, req1.RequestId; got != want { 242 t.Fatalf("unexpected request id in response: got %d, want %d", got, want) 243 } 244 headers2 := new(eth.BlockHeadersPacket) 245 if err := conn.ReadMsg(ethProto, eth.BlockHeadersMsg, &headers2); err != nil { 246 t.Fatalf("error reading block headers msg: %v", err) 247 } 248 if got, want := headers2.RequestId, req2.RequestId; got != want { 249 t.Fatalf("unexpected request id in response: got %d, want %d", got, want) 250 } 251 252 // Check received headers for accuracy. 253 if expected, err := s.chain.GetHeaders(req1); err != nil { 254 t.Fatalf("failed to get expected headers for request 1: %v", err) 255 } else if !headersMatch(expected, headers1.BlockHeadersRequest) { 256 t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers1) 257 } 258 if expected, err := s.chain.GetHeaders(req2); err != nil { 259 t.Fatalf("failed to get expected headers for request 2: %v", err) 260 } else if !headersMatch(expected, headers2.BlockHeadersRequest) { 261 t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers2) 262 } 263 } 264 265 func (s *Suite) TestSameRequestID(t *utesting.T) { 266 t.Log(`This test requests block headers, performing two concurrent requests with the 267 same request ID. The node should handle the request by responding to both requests.`) 268 conn, err := s.dialAndPeer(nil) 269 if err != nil { 270 t.Fatalf("peering failed: %v", err) 271 } 272 defer conn.Close() 273 274 // Create two different requests with the same ID. 275 reqID := uint64(1234) 276 request1 := ð.GetBlockHeadersPacket{ 277 RequestId: reqID, 278 GetBlockHeadersRequest: ð.GetBlockHeadersRequest{ 279 Origin: eth.HashOrNumber{ 280 Number: 1, 281 }, 282 Amount: 2, 283 }, 284 } 285 request2 := ð.GetBlockHeadersPacket{ 286 RequestId: reqID, 287 GetBlockHeadersRequest: ð.GetBlockHeadersRequest{ 288 Origin: eth.HashOrNumber{ 289 Number: 33, 290 }, 291 Amount: 2, 292 }, 293 } 294 295 // Send the requests. 296 if err = conn.Write(ethProto, eth.GetBlockHeadersMsg, request1); err != nil { 297 t.Fatalf("failed to write to connection: %v", err) 298 } 299 if err = conn.Write(ethProto, eth.GetBlockHeadersMsg, request2); err != nil { 300 t.Fatalf("failed to write to connection: %v", err) 301 } 302 303 // Wait for the responses. 304 headers1 := new(eth.BlockHeadersPacket) 305 if err := conn.ReadMsg(ethProto, eth.BlockHeadersMsg, &headers1); err != nil { 306 t.Fatalf("error reading from connection: %v", err) 307 } 308 if got, want := headers1.RequestId, request1.RequestId; got != want { 309 t.Fatalf("unexpected request id: got %d, want %d", got, want) 310 } 311 headers2 := new(eth.BlockHeadersPacket) 312 if err := conn.ReadMsg(ethProto, eth.BlockHeadersMsg, &headers2); err != nil { 313 t.Fatalf("error reading from connection: %v", err) 314 } 315 if got, want := headers2.RequestId, request2.RequestId; got != want { 316 t.Fatalf("unexpected request id: got %d, want %d", got, want) 317 } 318 319 // Check if headers match. 320 if expected, err := s.chain.GetHeaders(request1); err != nil { 321 t.Fatalf("failed to get expected block headers: %v", err) 322 } else if !headersMatch(expected, headers1.BlockHeadersRequest) { 323 t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers1) 324 } 325 if expected, err := s.chain.GetHeaders(request2); err != nil { 326 t.Fatalf("failed to get expected block headers: %v", err) 327 } else if !headersMatch(expected, headers2.BlockHeadersRequest) { 328 t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers2) 329 } 330 } 331 332 func (s *Suite) TestZeroRequestID(t *utesting.T) { 333 t.Log(`This test sends a GetBlockHeaders message with a request-id of zero, 334 and expects a response.`) 335 conn, err := s.dialAndPeer(nil) 336 if err != nil { 337 t.Fatalf("peering failed: %v", err) 338 } 339 defer conn.Close() 340 341 req := ð.GetBlockHeadersPacket{ 342 GetBlockHeadersRequest: ð.GetBlockHeadersRequest{ 343 Origin: eth.HashOrNumber{Number: 0}, 344 Amount: 2, 345 }, 346 } 347 // Read headers response. 348 if err := conn.Write(ethProto, eth.GetBlockHeadersMsg, req); err != nil { 349 t.Fatalf("could not write to connection: %v", err) 350 } 351 headers := new(eth.BlockHeadersPacket) 352 if err := conn.ReadMsg(ethProto, eth.BlockHeadersMsg, &headers); err != nil { 353 t.Fatalf("error reading msg: %v", err) 354 } 355 if got, want := headers.RequestId, req.RequestId; got != want { 356 t.Fatalf("unexpected request id") 357 } 358 if expected, err := s.chain.GetHeaders(req); err != nil { 359 t.Fatalf("failed to get expected block headers: %v", err) 360 } else if !headersMatch(expected, headers.BlockHeadersRequest) { 361 t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers) 362 } 363 } 364 365 func (s *Suite) TestGetBlockBodies(t *utesting.T) { 366 t.Log(`This test sends GetBlockBodies requests to the node for known blocks in the test chain.`) 367 conn, err := s.dialAndPeer(nil) 368 if err != nil { 369 t.Fatalf("peering failed: %v", err) 370 } 371 defer conn.Close() 372 373 // Create block bodies request. 374 req := ð.GetBlockBodiesPacket{ 375 RequestId: 55, 376 GetBlockBodiesRequest: eth.GetBlockBodiesRequest{ 377 s.chain.blocks[54].Hash(), 378 s.chain.blocks[75].Hash(), 379 }, 380 } 381 if err := conn.Write(ethProto, eth.GetBlockBodiesMsg, req); err != nil { 382 t.Fatalf("could not write to connection: %v", err) 383 } 384 // Wait for response. 385 resp := new(eth.BlockBodiesPacket) 386 if err := conn.ReadMsg(ethProto, eth.BlockBodiesMsg, &resp); err != nil { 387 t.Fatalf("error reading block bodies msg: %v", err) 388 } 389 if got, want := resp.RequestId, req.RequestId; got != want { 390 t.Fatalf("unexpected request id in respond", got, want) 391 } 392 bodies := resp.BlockBodiesResponse 393 if len(bodies) != len(req.GetBlockBodiesRequest) { 394 t.Fatalf("wrong bodies in response: expected %d bodies, got %d", len(req.GetBlockBodiesRequest), len(bodies)) 395 } 396 } 397 398 func (s *Suite) TestGetReceipts(t *utesting.T) { 399 t.Log(`This test sends GetReceipts requests to the node for known blocks in the test chain.`) 400 conn, err := s.dialAndPeer(nil) 401 if err != nil { 402 t.Fatalf("peering failed: %v", err) 403 } 404 defer conn.Close() 405 406 // Find some blocks containing receipts. 407 var hashes = make([]common.Hash, 0, 3) 408 for i := range s.chain.Len() { 409 block := s.chain.GetBlock(i) 410 if len(block.Transactions()) > 0 { 411 hashes = append(hashes, block.Hash()) 412 } 413 if len(hashes) == cap(hashes) { 414 break 415 } 416 } 417 418 // Create block bodies request. 419 req := ð.GetReceiptsPacket{ 420 RequestId: 66, 421 GetReceiptsRequest: (eth.GetReceiptsRequest)(hashes), 422 } 423 if err := conn.Write(ethProto, eth.GetReceiptsMsg, req); err != nil { 424 t.Fatalf("could not write to connection: %v", err) 425 } 426 // Wait for response. 427 resp := new(eth.ReceiptsPacket[*eth.ReceiptList69]) 428 if err := conn.ReadMsg(ethProto, eth.ReceiptsMsg, &resp); err != nil { 429 t.Fatalf("error reading block bodies msg: %v", err) 430 } 431 if got, want := resp.RequestId, req.RequestId; got != want { 432 t.Fatalf("unexpected request id in respond", got, want) 433 } 434 if len(resp.List) != len(req.GetReceiptsRequest) { 435 t.Fatalf("wrong bodies in response: expected %d bodies, got %d", len(req.GetReceiptsRequest), len(resp.List)) 436 } 437 } 438 439 // randBuf makes a random buffer size kilobytes large. 440 func randBuf(size int) []byte { 441 buf := make([]byte, size*1024) 442 rand.Read(buf) 443 return buf 444 } 445 446 func (s *Suite) TestMaliciousHandshake(t *utesting.T) { 447 t.Log(`This test tries to send malicious data during the devp2p handshake, in various ways.`) 448 449 // Write hello to client. 450 var ( 451 key, _ = crypto.GenerateKey() 452 pub0 = crypto.FromECDSAPub(&key.PublicKey)[1:] 453 version = eth.ProtocolVersions[0] 454 ) 455 handshakes := []*protoHandshake{ 456 { 457 Version: 5, 458 Caps: []p2p.Cap{ 459 {Name: string(randBuf(2)), Version: version}, 460 }, 461 ID: pub0, 462 }, 463 { 464 Version: 5, 465 Caps: []p2p.Cap{ 466 {Name: "eth", Version: version}, 467 }, 468 ID: append(pub0, byte(0)), 469 }, 470 { 471 Version: 5, 472 Caps: []p2p.Cap{ 473 {Name: "eth", Version: version}, 474 }, 475 ID: append(pub0, pub0...), 476 }, 477 { 478 Version: 5, 479 Caps: []p2p.Cap{ 480 {Name: "eth", Version: version}, 481 }, 482 ID: randBuf(2), 483 }, 484 { 485 Version: 5, 486 Caps: []p2p.Cap{ 487 {Name: string(randBuf(2)), Version: version}, 488 }, 489 ID: randBuf(2), 490 }, 491 } 492 for _, handshake := range handshakes { 493 conn, err := s.dialAs(key) 494 if err != nil { 495 t.Fatalf("dial failed: %v", err) 496 } 497 defer conn.Close() 498 499 if err := conn.Write(ethProto, handshakeMsg, handshake); err != nil { 500 t.Fatalf("could not write to connection: %v", err) 501 } 502 // Check that the peer disconnected 503 for i := 0; i < 2; i++ { 504 code, _, err := conn.Read() 505 if err != nil { 506 // Client may have disconnected without sending disconnect msg. 507 continue 508 } 509 switch code { 510 case discMsg: 511 case handshakeMsg: 512 // Discard one hello as Hello's are sent concurrently 513 continue 514 default: 515 t.Fatalf("unexpected msg: code %d", code) 516 } 517 } 518 } 519 } 520 521 func (s *Suite) TestBlockRangeUpdateInvalid(t *utesting.T) { 522 t.Log(`This test sends an invalid BlockRangeUpdate message to the node and expects to be disconnected.`) 523 conn, err := s.dialAndPeer(nil) 524 if err != nil { 525 t.Fatal(err) 526 } 527 defer conn.Close() 528 529 conn.Write(ethProto, eth.BlockRangeUpdateMsg, ð.BlockRangeUpdatePacket{ 530 EarliestBlock: 10, 531 LatestBlock: 8, 532 LatestBlockHash: s.chain.GetBlock(8).Hash(), 533 }) 534 535 if code, _, err := conn.Read(); err != nil { 536 t.Fatalf("expected disconnect, got err: %v", err) 537 } else if code != discMsg { 538 t.Fatalf("expected disconnect message, got msg code %d", code) 539 } 540 } 541 542 func (s *Suite) TestBlockRangeUpdateFuture(t *utesting.T) { 543 t.Log(`This test sends a BlockRangeUpdate that is beyond the chain head. 544 The node should accept the update and should not disonnect.`) 545 conn, err := s.dialAndPeer(nil) 546 if err != nil { 547 t.Fatal(err) 548 } 549 defer conn.Close() 550 551 head := s.chain.Head().NumberU64() 552 var hash common.Hash 553 rand.Read(hash[:]) 554 conn.Write(ethProto, eth.BlockRangeUpdateMsg, ð.BlockRangeUpdatePacket{ 555 EarliestBlock: head + 10, 556 LatestBlock: head + 50, 557 LatestBlockHash: hash, 558 }) 559 560 // Ensure the node does not disconnect us. 561 // Just send a few ping messages. 562 for range 10 { 563 time.Sleep(100 * time.Millisecond) 564 if err := conn.Write(baseProto, pingMsg, []any{}); err != nil { 565 t.Fatal("write error:", err) 566 } 567 code, _, err := conn.Read() 568 switch { 569 case err != nil: 570 t.Fatal("read error:", err) 571 case code == discMsg: 572 t.Fatal("got disconnect") 573 case code == pongMsg: 574 } 575 } 576 } 577 578 func (s *Suite) TestBlockRangeUpdateHistoryExp(t *utesting.T) { 579 t.Log(`This test sends a BlockRangeUpdate announcing incomplete (expired) history. 580 The node should accept the update and should not disonnect.`) 581 conn, err := s.dialAndPeer(nil) 582 if err != nil { 583 t.Fatal(err) 584 } 585 defer conn.Close() 586 587 head := s.chain.Head() 588 conn.Write(ethProto, eth.BlockRangeUpdateMsg, ð.BlockRangeUpdatePacket{ 589 EarliestBlock: head.NumberU64() - 10, 590 LatestBlock: head.NumberU64(), 591 LatestBlockHash: head.Hash(), 592 }) 593 594 // Ensure the node does not disconnect us. 595 // Just send a few ping messages. 596 for range 10 { 597 time.Sleep(100 * time.Millisecond) 598 if err := conn.Write(baseProto, pingMsg, []any{}); err != nil { 599 t.Fatal("write error:", err) 600 } 601 code, _, err := conn.Read() 602 switch { 603 case err != nil: 604 t.Fatal("read error:", err) 605 case code == discMsg: 606 t.Fatal("got disconnect") 607 case code == pongMsg: 608 } 609 } 610 } 611 612 func (s *Suite) TestTransaction(t *utesting.T) { 613 t.Log(`This test sends a valid transaction to the node and checks if the 614 transaction gets propagated.`) 615 616 // Nudge client out of syncing mode to accept pending txs. 617 if err := s.engine.sendForkchoiceUpdated(); err != nil { 618 t.Fatalf("failed to send next block: %v", err) 619 } 620 from, nonce := s.chain.GetSender(0) 621 inner := &types.DynamicFeeTx{ 622 ChainID: s.chain.config.ChainID, 623 Nonce: nonce, 624 GasTipCap: common.Big1, 625 GasFeeCap: s.chain.Head().BaseFee(), 626 Gas: 30000, 627 To: &common.Address{0xaa}, 628 Value: common.Big1, 629 } 630 tx, err := s.chain.SignTx(from, types.NewTx(inner)) 631 if err != nil { 632 t.Fatalf("failed to sign tx: %v", err) 633 } 634 if err := s.sendTxs(t, []*types.Transaction{tx}); err != nil { 635 t.Fatal(err) 636 } 637 s.chain.IncNonce(from, 1) 638 } 639 640 func (s *Suite) TestInvalidTxs(t *utesting.T) { 641 t.Log(`This test sends several kinds of invalid transactions and checks that the node 642 does not propagate them.`) 643 644 // Nudge client out of syncing mode to accept pending txs. 645 if err := s.engine.sendForkchoiceUpdated(); err != nil { 646 t.Fatalf("failed to send next block: %v", err) 647 } 648 649 from, nonce := s.chain.GetSender(0) 650 inner := &types.DynamicFeeTx{ 651 ChainID: s.chain.config.ChainID, 652 Nonce: nonce, 653 GasTipCap: common.Big1, 654 GasFeeCap: s.chain.Head().BaseFee(), 655 Gas: 30000, 656 To: &common.Address{0xaa}, 657 } 658 tx, err := s.chain.SignTx(from, types.NewTx(inner)) 659 if err != nil { 660 t.Fatalf("failed to sign tx: %v", err) 661 } 662 if err := s.sendTxs(t, []*types.Transaction{tx}); err != nil { 663 t.Fatalf("failed to send txs: %v", err) 664 } 665 s.chain.IncNonce(from, 1) 666 667 inners := []*types.DynamicFeeTx{ 668 // Nonce already used 669 { 670 ChainID: s.chain.config.ChainID, 671 Nonce: nonce - 1, 672 GasTipCap: common.Big1, 673 GasFeeCap: s.chain.Head().BaseFee(), 674 Gas: 100000, 675 }, 676 // Value exceeds balance 677 { 678 Nonce: nonce, 679 GasTipCap: common.Big1, 680 GasFeeCap: s.chain.Head().BaseFee(), 681 Gas: 100000, 682 Value: s.chain.Balance(from), 683 }, 684 // Gas limit too low 685 { 686 Nonce: nonce, 687 GasTipCap: common.Big1, 688 GasFeeCap: s.chain.Head().BaseFee(), 689 Gas: 1337, 690 }, 691 // Code size too large 692 { 693 Nonce: nonce, 694 GasTipCap: common.Big1, 695 GasFeeCap: s.chain.Head().BaseFee(), 696 Data: randBuf(50), 697 Gas: 1_000_000, 698 }, 699 // Data too large 700 { 701 Nonce: nonce, 702 GasTipCap: common.Big1, 703 GasFeeCap: s.chain.Head().BaseFee(), 704 To: &common.Address{0xaa}, 705 Data: randBuf(128), 706 Gas: 5_000_000, 707 }, 708 } 709 710 var txs []*types.Transaction 711 for _, inner := range inners { 712 tx, err := s.chain.SignTx(from, types.NewTx(inner)) 713 if err != nil { 714 t.Fatalf("failed to sign tx: %v", err) 715 } 716 txs = append(txs, tx) 717 } 718 if err := s.sendInvalidTxs(t, txs); err != nil { 719 t.Fatalf("failed to send invalid txs: %v", err) 720 } 721 } 722 723 func (s *Suite) TestLargeTxRequest(t *utesting.T) { 724 t.Log(`This test first send ~2000 transactions to the node, then requests them 725 on another peer connection using GetPooledTransactions.`) 726 727 // Nudge client out of syncing mode to accept pending txs. 728 if err := s.engine.sendForkchoiceUpdated(); err != nil { 729 t.Fatalf("failed to send next block: %v", err) 730 } 731 732 // Generate many transactions to seed target with. 733 var ( 734 from, nonce = s.chain.GetSender(1) 735 count = 2000 736 txs []*types.Transaction 737 hashes []common.Hash 738 set = make(map[common.Hash]struct{}) 739 ) 740 for i := 0; i < count; i++ { 741 inner := &types.DynamicFeeTx{ 742 ChainID: s.chain.config.ChainID, 743 Nonce: nonce + uint64(i), 744 GasTipCap: common.Big1, 745 GasFeeCap: s.chain.Head().BaseFee(), 746 Gas: 75000, 747 } 748 tx, err := s.chain.SignTx(from, types.NewTx(inner)) 749 if err != nil { 750 t.Fatalf("failed to sign tx: err") 751 } 752 txs = append(txs, tx) 753 set[tx.Hash()] = struct{}{} 754 hashes = append(hashes, tx.Hash()) 755 } 756 s.chain.IncNonce(from, uint64(count)) 757 758 // Send txs. 759 if err := s.sendTxs(t, txs); err != nil { 760 t.Fatalf("failed to send txs: %v", err) 761 } 762 763 // Set up receive connection to ensure node is peered with the receiving 764 // connection before tx request is sent. 765 conn, err := s.dial() 766 if err != nil { 767 t.Fatalf("dial failed: %v", err) 768 } 769 defer conn.Close() 770 if err = conn.peer(s.chain, nil); err != nil { 771 t.Fatalf("peering failed: %v", err) 772 } 773 // Create and send pooled tx request. 774 req := ð.GetPooledTransactionsPacket{ 775 RequestId: 1234, 776 GetPooledTransactionsRequest: hashes, 777 } 778 if err = conn.Write(ethProto, eth.GetPooledTransactionsMsg, req); err != nil { 779 t.Fatalf("could not write to conn: %v", err) 780 } 781 // Check that all received transactions match those that were sent to node. 782 msg := new(eth.PooledTransactionsPacket) 783 if err := conn.ReadMsg(ethProto, eth.PooledTransactionsMsg, &msg); err != nil { 784 t.Fatalf("error reading from connection: %v", err) 785 } 786 if got, want := msg.RequestId, req.RequestId; got != want { 787 t.Fatalf("unexpected request id in response: got %d, want %d", got, want) 788 } 789 for _, got := range msg.PooledTransactionsResponse { 790 if _, exists := set[got.Hash()]; !exists { 791 t.Fatalf("unexpected tx received: %v", got.Hash()) 792 } 793 } 794 } 795 796 func (s *Suite) TestNewPooledTxs(t *utesting.T) { 797 t.Log(`This test announces transaction hashes to the node and expects it to fetch 798 the transactions using a GetPooledTransactions request.`) 799 800 // Nudge client out of syncing mode to accept pending txs. 801 if err := s.engine.sendForkchoiceUpdated(); err != nil { 802 t.Fatalf("failed to send next block: %v", err) 803 } 804 805 var ( 806 count = 50 807 from, nonce = s.chain.GetSender(1) 808 hashes = make([]common.Hash, count) 809 txTypes = make([]byte, count) 810 sizes = make([]uint32, count) 811 ) 812 for i := 0; i < count; i++ { 813 inner := &types.DynamicFeeTx{ 814 ChainID: s.chain.config.ChainID, 815 Nonce: nonce + uint64(i), 816 GasTipCap: common.Big1, 817 GasFeeCap: s.chain.Head().BaseFee(), 818 Gas: 75000, 819 } 820 tx, err := s.chain.SignTx(from, types.NewTx(inner)) 821 if err != nil { 822 t.Fatalf("failed to sign tx: err") 823 } 824 hashes[i] = tx.Hash() 825 txTypes[i] = tx.Type() 826 sizes[i] = uint32(tx.Size()) 827 } 828 s.chain.IncNonce(from, uint64(count)) 829 830 // Connect to peer. 831 conn, err := s.dial() 832 if err != nil { 833 t.Fatalf("dial failed: %v", err) 834 } 835 defer conn.Close() 836 if err = conn.peer(s.chain, nil); err != nil { 837 t.Fatalf("peering failed: %v", err) 838 } 839 840 // Send announcement. 841 ann := eth.NewPooledTransactionHashesPacket{Types: txTypes, Sizes: sizes, Hashes: hashes} 842 err = conn.Write(ethProto, eth.NewPooledTransactionHashesMsg, ann) 843 if err != nil { 844 t.Fatalf("failed to write to connection: %v", err) 845 } 846 847 // Wait for GetPooledTxs request. 848 for { 849 msg, err := conn.ReadEth() 850 if err != nil { 851 t.Fatalf("failed to read eth msg: %v", err) 852 } 853 switch msg := msg.(type) { 854 case *eth.GetPooledTransactionsPacket: 855 if len(msg.GetPooledTransactionsRequest) != len(hashes) { 856 t.Fatalf("unexpected number of txs requested: wanted %d, got %d", len(hashes), len(msg.GetPooledTransactionsRequest)) 857 } 858 return 859 case *eth.NewPooledTransactionHashesPacket: 860 continue 861 case *eth.TransactionsPacket: 862 continue 863 default: 864 t.Fatalf("unexpected %s", pretty.Sdump(msg)) 865 } 866 } 867 } 868 869 func makeSidecar(data ...byte) *types.BlobTxSidecar { 870 var ( 871 blobs = make([]kzg4844.Blob, len(data)) 872 commitments []kzg4844.Commitment 873 proofs []kzg4844.Proof 874 ) 875 for i := range blobs { 876 blobs[i][0] = data[i] 877 c, _ := kzg4844.BlobToCommitment(&blobs[i]) 878 p, _ := kzg4844.ComputeBlobProof(&blobs[i], c) 879 commitments = append(commitments, c) 880 proofs = append(proofs, p) 881 } 882 return &types.BlobTxSidecar{ 883 Blobs: blobs, 884 Commitments: commitments, 885 Proofs: proofs, 886 } 887 } 888 889 func (s *Suite) makeBlobTxs(count, blobs int, discriminator byte) (txs types.Transactions) { 890 from, nonce := s.chain.GetSender(5) 891 for i := 0; i < count; i++ { 892 // Make blob data, max of 2 blobs per tx. 893 blobdata := make([]byte, blobs%3) 894 for i := range blobdata { 895 blobdata[i] = discriminator 896 blobs -= 1 897 } 898 inner := &types.BlobTx{ 899 ChainID: uint256.MustFromBig(s.chain.config.ChainID), 900 Nonce: nonce + uint64(i), 901 GasTipCap: uint256.NewInt(1), 902 GasFeeCap: uint256.MustFromBig(s.chain.Head().BaseFee()), 903 Gas: 100000, 904 BlobFeeCap: uint256.MustFromBig(eip4844.CalcBlobFee(s.chain.config, s.chain.Head().Header())), 905 BlobHashes: makeSidecar(blobdata...).BlobHashes(), 906 Sidecar: makeSidecar(blobdata...), 907 } 908 tx, err := s.chain.SignTx(from, types.NewTx(inner)) 909 if err != nil { 910 panic("blob tx signing failed") 911 } 912 txs = append(txs, tx) 913 } 914 return txs 915 } 916 917 func (s *Suite) TestBlobViolations(t *utesting.T) { 918 t.Log(`This test sends some invalid blob tx announcements and expects the node to disconnect.`) 919 920 if err := s.engine.sendForkchoiceUpdated(); err != nil { 921 t.Fatalf("send fcu failed: %v", err) 922 } 923 // Create blob txs for each tests with unique tx hashes. 924 var ( 925 t1 = s.makeBlobTxs(2, 3, 0x1) 926 t2 = s.makeBlobTxs(2, 3, 0x2) 927 ) 928 for _, test := range []struct { 929 ann eth.NewPooledTransactionHashesPacket 930 resp eth.PooledTransactionsResponse 931 }{ 932 // Invalid tx size. 933 { 934 ann: eth.NewPooledTransactionHashesPacket{ 935 Types: []byte{types.BlobTxType, types.BlobTxType}, 936 Sizes: []uint32{uint32(t1[0].Size()), uint32(t1[1].Size() + 10)}, 937 Hashes: []common.Hash{t1[0].Hash(), t1[1].Hash()}, 938 }, 939 resp: eth.PooledTransactionsResponse(t1), 940 }, 941 // Wrong tx type. 942 { 943 ann: eth.NewPooledTransactionHashesPacket{ 944 Types: []byte{types.DynamicFeeTxType, types.BlobTxType}, 945 Sizes: []uint32{uint32(t2[0].Size()), uint32(t2[1].Size())}, 946 Hashes: []common.Hash{t2[0].Hash(), t2[1].Hash()}, 947 }, 948 resp: eth.PooledTransactionsResponse(t2), 949 }, 950 } { 951 conn, err := s.dial() 952 if err != nil { 953 t.Fatalf("dial fail: %v", err) 954 } 955 if err := conn.peer(s.chain, nil); err != nil { 956 t.Fatalf("peering failed: %v", err) 957 } 958 if err := conn.Write(ethProto, eth.NewPooledTransactionHashesMsg, test.ann); err != nil { 959 t.Fatalf("sending announcement failed: %v", err) 960 } 961 req := new(eth.GetPooledTransactionsPacket) 962 if err := conn.ReadMsg(ethProto, eth.GetPooledTransactionsMsg, req); err != nil { 963 t.Fatalf("reading pooled tx request failed: %v", err) 964 } 965 resp := eth.PooledTransactionsPacket{RequestId: req.RequestId, PooledTransactionsResponse: test.resp} 966 if err := conn.Write(ethProto, eth.PooledTransactionsMsg, resp); err != nil { 967 t.Fatalf("writing pooled tx response failed: %v", err) 968 } 969 if code, _, err := conn.Read(); err != nil { 970 t.Fatalf("expected disconnect on blob violation, got err: %v", err) 971 } else if code != discMsg { 972 if code == protoOffset(ethProto)+eth.NewPooledTransactionHashesMsg { 973 // sometimes we'll get a blob transaction hashes announcement before the disconnect 974 // because blob transactions are scheduled to be fetched right away. 975 if code, _, err = conn.Read(); err != nil { 976 t.Fatalf("expected disconnect on blob violation, got err on second read: %v", err) 977 } 978 } 979 if code != discMsg { 980 t.Fatalf("expected disconnect on blob violation, got msg code: %d", code) 981 } 982 } 983 conn.Close() 984 } 985 } 986 987 // mangleSidecar returns a copy of the given blob transaction where the sidecar 988 // data has been modified to produce a different commitment hash. 989 func mangleSidecar(tx *types.Transaction) *types.Transaction { 990 sidecar := tx.BlobTxSidecar() 991 copy := types.BlobTxSidecar{ 992 Blobs: append([]kzg4844.Blob{}, sidecar.Blobs...), 993 Commitments: append([]kzg4844.Commitment{}, sidecar.Commitments...), 994 Proofs: append([]kzg4844.Proof{}, sidecar.Proofs...), 995 } 996 // zero the first commitment to alter the sidecar hash 997 copy.Commitments[0] = kzg4844.Commitment{} 998 return tx.WithBlobTxSidecar(©) 999 } 1000 1001 func (s *Suite) TestBlobTxWithoutSidecar(t *utesting.T) { 1002 t.Log(`This test checks that a blob transaction first advertised/transmitted without blobs will result in the sending peer being disconnected, and the full transaction should be successfully retrieved from another peer.`) 1003 tx := s.makeBlobTxs(1, 2, 42)[0] 1004 badTx := tx.WithoutBlobTxSidecar() 1005 s.testBadBlobTx(t, tx, badTx) 1006 } 1007 1008 func (s *Suite) TestBlobTxWithMismatchedSidecar(t *utesting.T) { 1009 t.Log(`This test checks that a blob transaction first advertised/transmitted without blobs, whose commitment don't correspond to the blob_versioned_hashes in the transaction, will result in the sending peer being disconnected, and the full transaction should be successfully retrieved from another peer.`) 1010 tx := s.makeBlobTxs(1, 2, 43)[0] 1011 badTx := mangleSidecar(tx) 1012 s.testBadBlobTx(t, tx, badTx) 1013 } 1014 1015 // readUntil reads eth protocol messages until a message of the target type is 1016 // received. It returns an error if there is a disconnect, or if the context 1017 // is cancelled before a message of the desired type can be read. 1018 func readUntil[T any](ctx context.Context, conn *Conn) (*T, error) { 1019 for { 1020 select { 1021 case <-ctx.Done(): 1022 return nil, context.Canceled 1023 default: 1024 } 1025 received, err := conn.ReadEth() 1026 if err != nil { 1027 if err == errDisc { 1028 return nil, errDisc 1029 } 1030 continue 1031 } 1032 1033 switch res := received.(type) { 1034 case *T: 1035 return res, nil 1036 } 1037 } 1038 } 1039 1040 // readUntilDisconnect reads eth protocol messages until the peer disconnects. 1041 // It returns whether the peer disconnects in the next 100ms. 1042 func readUntilDisconnect(conn *Conn) (disconnected bool) { 1043 ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) 1044 defer cancel() 1045 _, err := readUntil[struct{}](ctx, conn) 1046 return err == errDisc 1047 } 1048 1049 func (s *Suite) testBadBlobTx(t *utesting.T, tx *types.Transaction, badTx *types.Transaction) { 1050 stage1, stage2, stage3 := new(sync.WaitGroup), new(sync.WaitGroup), new(sync.WaitGroup) 1051 stage1.Add(1) 1052 stage2.Add(1) 1053 stage3.Add(1) 1054 1055 errc := make(chan error) 1056 1057 badPeer := func() { 1058 // announce the correct hash from the bad peer. 1059 // when the transaction is first requested before transmitting it from the bad peer, 1060 // trigger step 2: connection and announcement by good peers 1061 1062 conn, err := s.dial() 1063 if err != nil { 1064 errc <- fmt.Errorf("dial fail: %v", err) 1065 return 1066 } 1067 defer conn.Close() 1068 1069 if err := conn.peer(s.chain, nil); err != nil { 1070 errc <- fmt.Errorf("bad peer: peering failed: %v", err) 1071 return 1072 } 1073 1074 ann := eth.NewPooledTransactionHashesPacket{ 1075 Types: []byte{types.BlobTxType}, 1076 Sizes: []uint32{uint32(badTx.Size())}, 1077 Hashes: []common.Hash{badTx.Hash()}, 1078 } 1079 1080 if err := conn.Write(ethProto, eth.NewPooledTransactionHashesMsg, ann); err != nil { 1081 errc <- fmt.Errorf("sending announcement failed: %v", err) 1082 return 1083 } 1084 1085 req, err := readUntil[eth.GetPooledTransactionsPacket](context.Background(), conn) 1086 if err != nil { 1087 errc <- fmt.Errorf("failed to read GetPooledTransactions message: %v", err) 1088 return 1089 } 1090 1091 stage1.Done() 1092 stage2.Wait() 1093 1094 // the good peer is connected, and has announced the tx. 1095 // proceed to send the incorrect one from the bad peer. 1096 1097 resp := eth.PooledTransactionsPacket{RequestId: req.RequestId, PooledTransactionsResponse: eth.PooledTransactionsResponse(types.Transactions{badTx})} 1098 if err := conn.Write(ethProto, eth.PooledTransactionsMsg, resp); err != nil { 1099 errc <- fmt.Errorf("writing pooled tx response failed: %v", err) 1100 return 1101 } 1102 if !readUntilDisconnect(conn) { 1103 errc <- fmt.Errorf("expected bad peer to be disconnected") 1104 return 1105 } 1106 stage3.Done() 1107 } 1108 1109 goodPeer := func() { 1110 stage1.Wait() 1111 1112 conn, err := s.dial() 1113 if err != nil { 1114 errc <- fmt.Errorf("dial fail: %v", err) 1115 return 1116 } 1117 defer conn.Close() 1118 1119 if err := conn.peer(s.chain, nil); err != nil { 1120 errc <- fmt.Errorf("peering failed: %v", err) 1121 return 1122 } 1123 1124 ann := eth.NewPooledTransactionHashesPacket{ 1125 Types: []byte{types.BlobTxType}, 1126 Sizes: []uint32{uint32(tx.Size())}, 1127 Hashes: []common.Hash{tx.Hash()}, 1128 } 1129 1130 if err := conn.Write(ethProto, eth.NewPooledTransactionHashesMsg, ann); err != nil { 1131 errc <- fmt.Errorf("sending announcement failed: %v", err) 1132 return 1133 } 1134 1135 // wait until the bad peer has transmitted the incorrect transaction 1136 stage2.Done() 1137 stage3.Wait() 1138 1139 // the bad peer has transmitted the bad tx, and been disconnected. 1140 // transmit the same tx but with correct sidecar from the good peer. 1141 1142 var req *eth.GetPooledTransactionsPacket 1143 req, err = readUntil[eth.GetPooledTransactionsPacket](context.Background(), conn) 1144 if err != nil { 1145 errc <- fmt.Errorf("reading pooled tx request failed: %v", err) 1146 return 1147 } 1148 1149 if req.GetPooledTransactionsRequest[0] != tx.Hash() { 1150 errc <- fmt.Errorf("requested unknown tx hash") 1151 return 1152 } 1153 1154 resp := eth.PooledTransactionsPacket{RequestId: req.RequestId, PooledTransactionsResponse: eth.PooledTransactionsResponse(types.Transactions{tx})} 1155 if err := conn.Write(ethProto, eth.PooledTransactionsMsg, resp); err != nil { 1156 errc <- fmt.Errorf("writing pooled tx response failed: %v", err) 1157 return 1158 } 1159 if readUntilDisconnect(conn) { 1160 errc <- fmt.Errorf("unexpected disconnect") 1161 return 1162 } 1163 close(errc) 1164 } 1165 1166 if err := s.engine.sendForkchoiceUpdated(); err != nil { 1167 t.Fatalf("send fcu failed: %v", err) 1168 } 1169 1170 go goodPeer() 1171 go badPeer() 1172 err := <-errc 1173 if err != nil { 1174 t.Fatalf("%v", err) 1175 } 1176 }