github.com/ledgerwatch/erigon-lib@v1.0.0/txpool/pool_test.go (about) 1 /* 2 Copyright 2021 The Erigon contributors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package txpool 18 19 import ( 20 "bytes" 21 "context" 22 23 // "crypto/rand" 24 "fmt" 25 "math" 26 "math/big" 27 "testing" 28 29 gokzg4844 "github.com/crate-crypto/go-kzg-4844" 30 "github.com/holiman/uint256" 31 "github.com/ledgerwatch/log/v3" 32 "github.com/stretchr/testify/assert" 33 "github.com/stretchr/testify/require" 34 35 "github.com/ledgerwatch/erigon-lib/common" 36 "github.com/ledgerwatch/erigon-lib/common/fixedgas" 37 "github.com/ledgerwatch/erigon-lib/common/hexutility" 38 "github.com/ledgerwatch/erigon-lib/common/u256" 39 "github.com/ledgerwatch/erigon-lib/crypto/kzg" 40 "github.com/ledgerwatch/erigon-lib/gointerfaces" 41 "github.com/ledgerwatch/erigon-lib/gointerfaces/remote" 42 "github.com/ledgerwatch/erigon-lib/kv" 43 "github.com/ledgerwatch/erigon-lib/kv/kvcache" 44 "github.com/ledgerwatch/erigon-lib/kv/memdb" 45 "github.com/ledgerwatch/erigon-lib/txpool/txpoolcfg" 46 "github.com/ledgerwatch/erigon-lib/types" 47 ) 48 49 func TestNonceFromAddress(t *testing.T) { 50 assert, require := assert.New(t), require.New(t) 51 ch := make(chan types.Announcements, 100) 52 db, coreDB := memdb.NewTestPoolDB(t), memdb.NewTestDB(t) 53 54 cfg := txpoolcfg.DefaultConfig 55 sendersCache := kvcache.New(kvcache.DefaultCoherentConfig) 56 pool, err := New(ch, coreDB, cfg, sendersCache, *u256.N1, nil, nil, log.New()) 57 assert.NoError(err) 58 require.True(pool != nil) 59 ctx := context.Background() 60 var stateVersionID uint64 = 0 61 pendingBaseFee := uint64(200000) 62 // start blocks from 0, set empty hash - then kvcache will also work on this 63 h1 := gointerfaces.ConvertHashToH256([32]byte{}) 64 change := &remote.StateChangeBatch{ 65 StateVersionId: stateVersionID, 66 PendingBlockBaseFee: pendingBaseFee, 67 BlockGasLimit: 1000000, 68 ChangeBatch: []*remote.StateChange{ 69 {BlockHeight: 0, BlockHash: h1}, 70 }, 71 } 72 var addr [20]byte 73 addr[0] = 1 74 v := make([]byte, types.EncodeSenderLengthForStorage(2, *uint256.NewInt(1 * common.Ether))) 75 types.EncodeSender(2, *uint256.NewInt(1 * common.Ether), v) 76 change.ChangeBatch[0].Changes = append(change.ChangeBatch[0].Changes, &remote.AccountChange{ 77 Action: remote.Action_UPSERT, 78 Address: gointerfaces.ConvertAddressToH160(addr), 79 Data: v, 80 }) 81 tx, err := db.BeginRw(ctx) 82 require.NoError(err) 83 defer tx.Rollback() 84 err = pool.OnNewBlock(ctx, change, types.TxSlots{}, types.TxSlots{}, tx) 85 assert.NoError(err) 86 87 { 88 var txSlots types.TxSlots 89 txSlot1 := &types.TxSlot{ 90 Tip: *uint256.NewInt(300000), 91 FeeCap: *uint256.NewInt(300000), 92 Gas: 100000, 93 Nonce: 3, 94 } 95 txSlot1.IDHash[0] = 1 96 txSlots.Append(txSlot1, addr[:], true) 97 98 reasons, err := pool.AddLocalTxs(ctx, txSlots, tx) 99 assert.NoError(err) 100 for _, reason := range reasons { 101 assert.Equal(txpoolcfg.Success, reason, reason.String()) 102 } 103 } 104 105 { 106 txSlots := types.TxSlots{} 107 txSlot2 := &types.TxSlot{ 108 Tip: *uint256.NewInt(300000), 109 FeeCap: *uint256.NewInt(300000), 110 Gas: 100000, 111 Nonce: 4, 112 } 113 txSlot2.IDHash[0] = 2 114 txSlot3 := &types.TxSlot{ 115 Tip: *uint256.NewInt(300000), 116 FeeCap: *uint256.NewInt(300000), 117 Gas: 100000, 118 Nonce: 6, 119 } 120 txSlot3.IDHash[0] = 3 121 txSlots.Append(txSlot2, addr[:], true) 122 txSlots.Append(txSlot3, addr[:], true) 123 reasons, err := pool.AddLocalTxs(ctx, txSlots, tx) 124 assert.NoError(err) 125 for _, reason := range reasons { 126 assert.Equal(txpoolcfg.Success, reason, reason.String()) 127 } 128 nonce, ok := pool.NonceFromAddress(addr) 129 assert.True(ok) 130 assert.Equal(uint64(6), nonce) 131 } 132 // test too expensive tx 133 { 134 var txSlots types.TxSlots 135 txSlot1 := &types.TxSlot{ 136 Tip: *uint256.NewInt(300000), 137 FeeCap: *uint256.NewInt(9 * common.Ether), 138 Gas: 100000, 139 Nonce: 3, 140 } 141 txSlot1.IDHash[0] = 4 142 txSlots.Append(txSlot1, addr[:], true) 143 reasons, err := pool.AddLocalTxs(ctx, txSlots, tx) 144 assert.NoError(err) 145 for _, reason := range reasons { 146 assert.Equal(txpoolcfg.InsufficientFunds, reason, reason.String()) 147 } 148 } 149 150 // test too low nonce 151 { 152 var txSlots types.TxSlots 153 txSlot1 := &types.TxSlot{ 154 Tip: *uint256.NewInt(300000), 155 FeeCap: *uint256.NewInt(300000), 156 Gas: 100000, 157 Nonce: 1, 158 } 159 txSlot1.IDHash[0] = 5 160 txSlots.Append(txSlot1, addr[:], true) 161 reasons, err := pool.AddLocalTxs(ctx, txSlots, tx) 162 assert.NoError(err) 163 for _, reason := range reasons { 164 assert.Equal(txpoolcfg.NonceTooLow, reason, reason.String()) 165 } 166 } 167 } 168 169 func TestReplaceWithHigherFee(t *testing.T) { 170 assert, require := assert.New(t), require.New(t) 171 ch := make(chan types.Announcements, 100) 172 db, coreDB := memdb.NewTestPoolDB(t), memdb.NewTestDB(t) 173 174 cfg := txpoolcfg.DefaultConfig 175 sendersCache := kvcache.New(kvcache.DefaultCoherentConfig) 176 pool, err := New(ch, coreDB, cfg, sendersCache, *u256.N1, nil, nil, log.New()) 177 assert.NoError(err) 178 require.True(pool != nil) 179 ctx := context.Background() 180 var stateVersionID uint64 = 0 181 pendingBaseFee := uint64(200000) 182 // start blocks from 0, set empty hash - then kvcache will also work on this 183 h1 := gointerfaces.ConvertHashToH256([32]byte{}) 184 change := &remote.StateChangeBatch{ 185 StateVersionId: stateVersionID, 186 PendingBlockBaseFee: pendingBaseFee, 187 BlockGasLimit: 1000000, 188 ChangeBatch: []*remote.StateChange{ 189 {BlockHeight: 0, BlockHash: h1}, 190 }, 191 } 192 var addr [20]byte 193 addr[0] = 1 194 v := make([]byte, types.EncodeSenderLengthForStorage(2, *uint256.NewInt(1 * common.Ether))) 195 types.EncodeSender(2, *uint256.NewInt(1 * common.Ether), v) 196 change.ChangeBatch[0].Changes = append(change.ChangeBatch[0].Changes, &remote.AccountChange{ 197 Action: remote.Action_UPSERT, 198 Address: gointerfaces.ConvertAddressToH160(addr), 199 Data: v, 200 }) 201 tx, err := db.BeginRw(ctx) 202 require.NoError(err) 203 defer tx.Rollback() 204 err = pool.OnNewBlock(ctx, change, types.TxSlots{}, types.TxSlots{}, tx) 205 assert.NoError(err) 206 207 { 208 var txSlots types.TxSlots 209 txSlot := &types.TxSlot{ 210 Tip: *uint256.NewInt(300000), 211 FeeCap: *uint256.NewInt(300000), 212 Gas: 100000, 213 Nonce: 3, 214 } 215 txSlot.IDHash[0] = 1 216 txSlots.Append(txSlot, addr[:], true) 217 218 reasons, err := pool.AddLocalTxs(ctx, txSlots, tx) 219 assert.NoError(err) 220 for _, reason := range reasons { 221 assert.Equal(txpoolcfg.Success, reason, reason.String()) 222 } 223 } 224 // Bumped only feeCap, transaction not accepted 225 { 226 txSlots := types.TxSlots{} 227 txSlot := &types.TxSlot{ 228 Tip: *uint256.NewInt(300000), 229 FeeCap: *uint256.NewInt(3000000), 230 Gas: 100000, 231 Nonce: 3, 232 } 233 txSlot.IDHash[0] = 2 234 txSlots.Append(txSlot, addr[:], true) 235 reasons, err := pool.AddLocalTxs(ctx, txSlots, tx) 236 assert.NoError(err) 237 for _, reason := range reasons { 238 assert.Equal(txpoolcfg.NotReplaced, reason, reason.String()) 239 } 240 nonce, ok := pool.NonceFromAddress(addr) 241 assert.True(ok) 242 assert.Equal(uint64(3), nonce) 243 } 244 // Bumped only tip, transaction not accepted 245 { 246 txSlots := types.TxSlots{} 247 txSlot := &types.TxSlot{ 248 Tip: *uint256.NewInt(3000000), 249 FeeCap: *uint256.NewInt(300000), 250 Gas: 100000, 251 Nonce: 3, 252 } 253 txSlot.IDHash[0] = 3 254 txSlots.Append(txSlot, addr[:], true) 255 reasons, err := pool.AddLocalTxs(ctx, txSlots, tx) 256 assert.NoError(err) 257 for _, reason := range reasons { 258 assert.Equal(txpoolcfg.NotReplaced, reason, reason.String()) 259 } 260 nonce, ok := pool.NonceFromAddress(addr) 261 assert.True(ok) 262 assert.Equal(uint64(3), nonce) 263 } 264 // Bumped both tip and feeCap by 10%, tx accepted 265 { 266 txSlots := types.TxSlots{} 267 txSlot := &types.TxSlot{ 268 Tip: *uint256.NewInt(330000), 269 FeeCap: *uint256.NewInt(330000), 270 Gas: 100000, 271 Nonce: 3, 272 } 273 txSlot.IDHash[0] = 4 274 txSlots.Append(txSlot, addr[:], true) 275 reasons, err := pool.AddLocalTxs(ctx, txSlots, tx) 276 assert.NoError(err) 277 for _, reason := range reasons { 278 assert.Equal(txpoolcfg.Success, reason, reason.String()) 279 } 280 nonce, ok := pool.NonceFromAddress(addr) 281 assert.True(ok) 282 assert.Equal(uint64(3), nonce) 283 } 284 } 285 286 func TestReverseNonces(t *testing.T) { 287 assert, require := assert.New(t), require.New(t) 288 ch := make(chan types.Announcements, 100) 289 db, coreDB := memdb.NewTestPoolDB(t), memdb.NewTestDB(t) 290 291 cfg := txpoolcfg.DefaultConfig 292 sendersCache := kvcache.New(kvcache.DefaultCoherentConfig) 293 pool, err := New(ch, coreDB, cfg, sendersCache, *u256.N1, nil, nil, log.New()) 294 assert.NoError(err) 295 require.True(pool != nil) 296 ctx := context.Background() 297 var stateVersionID uint64 = 0 298 pendingBaseFee := uint64(1_000_000) 299 // start blocks from 0, set empty hash - then kvcache will also work on this 300 h1 := gointerfaces.ConvertHashToH256([32]byte{}) 301 change := &remote.StateChangeBatch{ 302 StateVersionId: stateVersionID, 303 PendingBlockBaseFee: pendingBaseFee, 304 BlockGasLimit: 1000000, 305 ChangeBatch: []*remote.StateChange{ 306 {BlockHeight: 0, BlockHash: h1}, 307 }, 308 } 309 var addr [20]byte 310 addr[0] = 1 311 v := make([]byte, types.EncodeSenderLengthForStorage(2, *uint256.NewInt(1 * common.Ether))) 312 types.EncodeSender(2, *uint256.NewInt(1 * common.Ether), v) 313 change.ChangeBatch[0].Changes = append(change.ChangeBatch[0].Changes, &remote.AccountChange{ 314 Action: remote.Action_UPSERT, 315 Address: gointerfaces.ConvertAddressToH160(addr), 316 Data: v, 317 }) 318 tx, err := db.BeginRw(ctx) 319 require.NoError(err) 320 defer tx.Rollback() 321 err = pool.OnNewBlock(ctx, change, types.TxSlots{}, types.TxSlots{}, tx) 322 assert.NoError(err) 323 // 1. Send high fee transaction with nonce gap 324 { 325 var txSlots types.TxSlots 326 txSlot := &types.TxSlot{ 327 Tip: *uint256.NewInt(500_000), 328 FeeCap: *uint256.NewInt(3_000_000), 329 Gas: 100000, 330 Nonce: 3, 331 } 332 txSlot.IDHash[0] = 1 333 txSlots.Append(txSlot, addr[:], true) 334 335 reasons, err := pool.AddLocalTxs(ctx, txSlots, tx) 336 assert.NoError(err) 337 for _, reason := range reasons { 338 assert.Equal(txpoolcfg.Success, reason, reason.String()) 339 } 340 } 341 fmt.Printf("AFTER TX 1\n") 342 select { 343 case annoucements := <-ch: 344 for i := 0; i < annoucements.Len(); i++ { 345 _, _, hash := annoucements.At(i) 346 fmt.Printf("propagated hash %x\n", hash) 347 } 348 default: 349 350 } 351 // 2. Send low fee (below base fee) transaction without nonce gap 352 { 353 var txSlots types.TxSlots 354 txSlot := &types.TxSlot{ 355 Tip: *uint256.NewInt(500_000), 356 FeeCap: *uint256.NewInt(500_000), 357 Gas: 100000, 358 Nonce: 2, 359 } 360 txSlot.IDHash[0] = 2 361 txSlots.Append(txSlot, addr[:], true) 362 363 reasons, err := pool.AddLocalTxs(ctx, txSlots, tx) 364 assert.NoError(err) 365 for _, reason := range reasons { 366 assert.Equal(txpoolcfg.Success, reason, reason.String()) 367 } 368 } 369 fmt.Printf("AFTER TX 2\n") 370 select { 371 case annoucements := <-ch: 372 for i := 0; i < annoucements.Len(); i++ { 373 _, _, hash := annoucements.At(i) 374 fmt.Printf("propagated hash %x\n", hash) 375 } 376 default: 377 378 } 379 380 { 381 var txSlots types.TxSlots 382 txSlot := &types.TxSlot{ 383 Tip: *uint256.NewInt(600_000), 384 FeeCap: *uint256.NewInt(3_000_000), 385 Gas: 100000, 386 Nonce: 2, 387 } 388 txSlot.IDHash[0] = 3 389 txSlots.Append(txSlot, addr[:], true) 390 391 reasons, err := pool.AddLocalTxs(ctx, txSlots, tx) 392 assert.NoError(err) 393 for _, reason := range reasons { 394 assert.Equal(txpoolcfg.Success, reason, reason.String()) 395 } 396 } 397 fmt.Printf("AFTER TX 3\n") 398 select { 399 case annoucements := <-ch: 400 for i := 0; i < annoucements.Len(); i++ { 401 _, _, hash := annoucements.At(i) 402 fmt.Printf("propagated hash %x\n", hash) 403 } 404 default: 405 406 } 407 } 408 409 // When local transaction is send to the pool, but it cannot replace existing transaction, 410 // the existing transaction gets "poked" and is getting re-broadcasted 411 // this is a workaround for cases when transactions are getting stuck for strange reasons 412 // even though logs show they are broadcast 413 func TestTxPoke(t *testing.T) { 414 assert, require := assert.New(t), require.New(t) 415 ch := make(chan types.Announcements, 100) 416 db, coreDB := memdb.NewTestPoolDB(t), memdb.NewTestDB(t) 417 418 cfg := txpoolcfg.DefaultConfig 419 sendersCache := kvcache.New(kvcache.DefaultCoherentConfig) 420 pool, err := New(ch, coreDB, cfg, sendersCache, *u256.N1, nil, nil, log.New()) 421 assert.NoError(err) 422 require.True(pool != nil) 423 ctx := context.Background() 424 var stateVersionID uint64 = 0 425 pendingBaseFee := uint64(200000) 426 // start blocks from 0, set empty hash - then kvcache will also work on this 427 h1 := gointerfaces.ConvertHashToH256([32]byte{}) 428 change := &remote.StateChangeBatch{ 429 StateVersionId: stateVersionID, 430 PendingBlockBaseFee: pendingBaseFee, 431 BlockGasLimit: 1000000, 432 ChangeBatch: []*remote.StateChange{ 433 {BlockHeight: 0, BlockHash: h1}, 434 }, 435 } 436 var addr [20]byte 437 addr[0] = 1 438 v := make([]byte, types.EncodeSenderLengthForStorage(2, *uint256.NewInt(1 * common.Ether))) 439 types.EncodeSender(2, *uint256.NewInt(1 * common.Ether), v) 440 change.ChangeBatch[0].Changes = append(change.ChangeBatch[0].Changes, &remote.AccountChange{ 441 Action: remote.Action_UPSERT, 442 Address: gointerfaces.ConvertAddressToH160(addr), 443 Data: v, 444 }) 445 tx, err := db.BeginRw(ctx) 446 require.NoError(err) 447 defer tx.Rollback() 448 err = pool.OnNewBlock(ctx, change, types.TxSlots{}, types.TxSlots{}, tx) 449 assert.NoError(err) 450 451 var idHash types.Hashes 452 { 453 var txSlots types.TxSlots 454 txSlot := &types.TxSlot{ 455 Tip: *uint256.NewInt(300000), 456 FeeCap: *uint256.NewInt(300000), 457 Gas: 100000, 458 Nonce: 2, 459 } 460 txSlot.IDHash[0] = 1 461 idHash = append(idHash, txSlot.IDHash[:]...) 462 txSlots.Append(txSlot, addr[:], true) 463 464 reasons, err := pool.AddLocalTxs(ctx, txSlots, tx) 465 assert.NoError(err) 466 for _, reason := range reasons { 467 assert.Equal(txpoolcfg.Success, reason, reason.String()) 468 } 469 } 470 var promoted types.Announcements 471 select { 472 case promoted = <-ch: 473 if !bytes.Equal(idHash, promoted.DedupHashes()) { 474 t.Errorf("expected promoted %x, got %x", idHash, promoted) 475 } 476 default: 477 t.Errorf("expected promotion") 478 } 479 // Send the same transaction, not accepted 480 { 481 txSlots := types.TxSlots{} 482 txSlot := &types.TxSlot{ 483 Tip: *uint256.NewInt(300000), 484 FeeCap: *uint256.NewInt(300000), 485 Gas: 100000, 486 Nonce: 2, 487 } 488 txSlot.IDHash[0] = 1 489 txSlots.Append(txSlot, addr[:], true) 490 reasons, err := pool.AddLocalTxs(ctx, txSlots, tx) 491 assert.NoError(err) 492 for _, reason := range reasons { 493 assert.Equal(txpoolcfg.DuplicateHash, reason, reason.String()) 494 } 495 nonce, ok := pool.NonceFromAddress(addr) 496 assert.True(ok) 497 assert.Equal(uint64(2), nonce) 498 } 499 // Even though transaction not replaced, it gets poked 500 select { 501 case promoted = <-ch: 502 if !bytes.Equal(idHash, promoted.Hashes()) { 503 t.Errorf("expected promoted %x, got %x", idHash, promoted) 504 } 505 default: 506 t.Errorf("expected promotion") 507 } 508 // Send different transaction, but only with tip bumped 509 { 510 txSlots := types.TxSlots{} 511 txSlot := &types.TxSlot{ 512 Tip: *uint256.NewInt(3000000), 513 FeeCap: *uint256.NewInt(300000), 514 Gas: 100000, 515 Nonce: 2, 516 } 517 txSlot.IDHash[0] = 2 518 txSlots.Append(txSlot, addr[:], true) 519 reasons, err := pool.AddLocalTxs(ctx, txSlots, tx) 520 assert.NoError(err) 521 for _, reason := range reasons { 522 assert.Equal(txpoolcfg.NotReplaced, reason, reason.String()) 523 } 524 nonce, ok := pool.NonceFromAddress(addr) 525 assert.True(ok) 526 assert.Equal(uint64(2), nonce) 527 } 528 // Even though transaction not replaced, it gets poked 529 select { 530 case promoted = <-ch: 531 if !bytes.Equal(idHash, promoted.Hashes()) { 532 t.Errorf("expected promoted %x, got %x", idHash, promoted) 533 } 534 default: 535 t.Errorf("expected promotion") 536 } 537 538 // Send the same transaction, but as remote 539 { 540 txSlots := types.TxSlots{} 541 txSlot := &types.TxSlot{ 542 Tip: *uint256.NewInt(300000), 543 FeeCap: *uint256.NewInt(300000), 544 Gas: 100000, 545 Nonce: 2, 546 } 547 txSlot.IDHash[0] = 1 548 txSlots.Append(txSlot, addr[:], true) 549 pool.AddRemoteTxs(ctx, txSlots) 550 nonce, ok := pool.NonceFromAddress(addr) 551 assert.True(ok) 552 assert.Equal(uint64(2), nonce) 553 } 554 // Remote transactions do not cause pokes 555 select { 556 case <-ch: 557 t.Errorf("remote transactions should not cause re-broadcast") 558 default: 559 } 560 // Send different transaction, but only with tip bumped, as a remote 561 { 562 txSlots := types.TxSlots{} 563 txSlot := &types.TxSlot{ 564 Tip: *uint256.NewInt(3000000), 565 FeeCap: *uint256.NewInt(3000000), 566 Gas: 100000, 567 Nonce: 2, 568 } 569 txSlot.IDHash[0] = 2 570 txSlots.Append(txSlot, addr[:], true) 571 pool.AddRemoteTxs(ctx, txSlots) 572 nonce, ok := pool.NonceFromAddress(addr) 573 assert.True(ok) 574 assert.Equal(uint64(2), nonce) 575 } 576 // Remote transactions do not cause pokes 577 select { 578 case <-ch: 579 t.Errorf("remote transactions should not cause re-broadcast") 580 default: 581 } 582 } 583 584 func TestShanghaiIntrinsicGas(t *testing.T) { 585 cases := map[string]struct { 586 expected uint64 587 dataLen uint64 588 dataNonZeroLen uint64 589 creation bool 590 isShanghai bool 591 }{ 592 "simple no data": { 593 expected: 21000, 594 dataLen: 0, 595 dataNonZeroLen: 0, 596 creation: false, 597 isShanghai: false, 598 }, 599 "simple with data": { 600 expected: 21512, 601 dataLen: 32, 602 dataNonZeroLen: 32, 603 creation: false, 604 isShanghai: false, 605 }, 606 "creation with data no shanghai": { 607 expected: 53512, 608 dataLen: 32, 609 dataNonZeroLen: 32, 610 creation: true, 611 isShanghai: false, 612 }, 613 "creation with single word and shanghai": { 614 expected: 53514, // additional gas for single word 615 dataLen: 32, 616 dataNonZeroLen: 32, 617 creation: true, 618 isShanghai: true, 619 }, 620 "creation between word 1 and 2 and shanghai": { 621 expected: 53532, // additional gas for going into 2nd word although not filling it 622 dataLen: 33, 623 dataNonZeroLen: 33, 624 creation: true, 625 isShanghai: true, 626 }, 627 } 628 629 for name, c := range cases { 630 t.Run(name, func(t *testing.T) { 631 gas, reason := txpoolcfg.CalcIntrinsicGas(c.dataLen, c.dataNonZeroLen, nil, c.creation, true, true, c.isShanghai) 632 if reason != txpoolcfg.Success { 633 t.Errorf("expected success but got reason %v", reason) 634 } 635 if gas != c.expected { 636 t.Errorf("expected %v but got %v", c.expected, gas) 637 } 638 }) 639 } 640 } 641 642 func TestShanghaiValidateTx(t *testing.T) { 643 asrt := assert.New(t) 644 tests := map[string]struct { 645 expected txpoolcfg.DiscardReason 646 dataLen int 647 isShanghai bool 648 }{ 649 "no shanghai": { 650 expected: txpoolcfg.Success, 651 dataLen: 32, 652 isShanghai: false, 653 }, 654 "shanghai within bounds": { 655 expected: txpoolcfg.Success, 656 dataLen: 32, 657 isShanghai: true, 658 }, 659 "shanghai exactly on bound": { 660 expected: txpoolcfg.Success, 661 dataLen: fixedgas.MaxInitCodeSize, 662 isShanghai: true, 663 }, 664 "shanghai one over bound": { 665 expected: txpoolcfg.InitCodeTooLarge, 666 dataLen: fixedgas.MaxInitCodeSize + 1, 667 isShanghai: true, 668 }, 669 } 670 671 logger := log.New() 672 673 for name, test := range tests { 674 t.Run(name, func(t *testing.T) { 675 ch := make(chan types.Announcements, 100) 676 _, coreDB := memdb.NewTestPoolDB(t), memdb.NewTestDB(t) 677 cfg := txpoolcfg.DefaultConfig 678 679 var shanghaiTime *big.Int 680 if test.isShanghai { 681 shanghaiTime = big.NewInt(0) 682 } 683 684 cache := &kvcache.DummyCache{} 685 pool, err := New(ch, coreDB, cfg, cache, *u256.N1, shanghaiTime, nil /* cancunTime */, logger) 686 asrt.NoError(err) 687 ctx := context.Background() 688 tx, err := coreDB.BeginRw(ctx) 689 defer tx.Rollback() 690 asrt.NoError(err) 691 692 sndr := sender{nonce: 0, balance: *uint256.NewInt(math.MaxUint64)} 693 sndrBytes := make([]byte, types.EncodeSenderLengthForStorage(sndr.nonce, sndr.balance)) 694 types.EncodeSender(sndr.nonce, sndr.balance, sndrBytes) 695 err = tx.Put(kv.PlainState, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, sndrBytes) 696 asrt.NoError(err) 697 698 txn := &types.TxSlot{ 699 DataLen: test.dataLen, 700 FeeCap: *uint256.NewInt(21000), 701 Gas: 500000, 702 SenderID: 0, 703 Creation: true, 704 } 705 706 txns := types.TxSlots{ 707 Txs: append([]*types.TxSlot{}, txn), 708 Senders: types.Addresses{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 709 } 710 err = pool.senders.registerNewSenders(&txns, logger) 711 asrt.NoError(err) 712 view, err := cache.View(ctx, tx) 713 asrt.NoError(err) 714 715 reason := pool.validateTx(txn, false, view) 716 717 if reason != test.expected { 718 t.Errorf("expected %v, got %v", test.expected, reason) 719 } 720 }) 721 } 722 } 723 724 // Blob gas price bump + other requirements to replace existing txns in the pool 725 func TestBlobTxReplacement(t *testing.T) { 726 assert, require := assert.New(t), require.New(t) 727 ch := make(chan types.Announcements, 5) 728 db, coreDB := memdb.NewTestPoolDB(t), memdb.NewTestDB(t) 729 cfg := txpoolcfg.DefaultConfig 730 sendersCache := kvcache.New(kvcache.DefaultCoherentConfig) 731 pool, err := New(ch, coreDB, cfg, sendersCache, *u256.N1, nil, common.Big0, log.New()) 732 assert.NoError(err) 733 require.True(pool != nil) 734 ctx := context.Background() 735 var stateVersionID uint64 = 0 736 737 h1 := gointerfaces.ConvertHashToH256([32]byte{}) 738 change := &remote.StateChangeBatch{ 739 StateVersionId: stateVersionID, 740 PendingBlockBaseFee: 200_000, 741 BlockGasLimit: 1000000, 742 PendingBlobFeePerGas: 100_000, 743 ChangeBatch: []*remote.StateChange{ 744 {BlockHeight: 0, BlockHash: h1}, 745 }, 746 } 747 var addr [20]byte 748 addr[0] = 1 749 750 // Add 1 eth to the user account, as a part of change 751 v := make([]byte, types.EncodeSenderLengthForStorage(2, *uint256.NewInt(1 * common.Ether))) 752 types.EncodeSender(2, *uint256.NewInt(1 * common.Ether), v) 753 754 change.ChangeBatch[0].Changes = append(change.ChangeBatch[0].Changes, &remote.AccountChange{ 755 Action: remote.Action_UPSERT, 756 Address: gointerfaces.ConvertAddressToH160(addr), 757 Data: v, 758 }) 759 tx, err := db.BeginRw(ctx) 760 require.NoError(err) 761 defer tx.Rollback() 762 err = pool.OnNewBlock(ctx, change, types.TxSlots{}, types.TxSlots{}, tx) 763 assert.NoError(err) 764 765 tip, feeCap, blobFeeCap := uint256.NewInt(100_000), uint256.NewInt(200_000), uint256.NewInt(200_000) 766 767 //add a blob txn to the pool 768 { 769 txSlots := types.TxSlots{} 770 blobTxn := makeBlobTx() 771 772 blobTxn.IDHash[0] = 0x00 773 blobTxn.Nonce = 0x2 774 txSlots.Append(&blobTxn, addr[:], true) 775 reasons, err := pool.AddLocalTxs(ctx, txSlots, tx) 776 assert.NoError(err) 777 t.Logf("Reasons %v", reasons) 778 for _, reason := range reasons { 779 assert.Equal(txpoolcfg.Success, reason, reason.String()) 780 } 781 } 782 783 { 784 // try to replace it with 5% extra blob gas, 2x higher tx fee - should fail 785 txSlots := types.TxSlots{} 786 blobTxn := makeBlobTx() 787 blobTxn.Nonce = 0x2 788 blobTxn.FeeCap.Mul(uint256.NewInt(2), feeCap) 789 blobTxn.Tip.Mul(uint256.NewInt(2), tip) 790 //increase blobFeeCap by 10% - no good 791 blobTxn.BlobFeeCap.Add(blobFeeCap, uint256.NewInt(1).Div(blobFeeCap, uint256.NewInt(10))) 792 blobTxn.IDHash[0] = 0x01 793 txSlots.Append(&blobTxn, addr[:], true) 794 reasons, err := pool.AddLocalTxs(ctx, txSlots, tx) 795 assert.NoError(err) 796 t.Logf("Reasons %v", reasons) 797 for _, reason := range reasons { 798 assert.Equal(txpoolcfg.ReplaceUnderpriced, reason, reason.String()) 799 } 800 } 801 802 { 803 txSlots := types.TxSlots{} 804 //try to replace it with a regular txn - should fail 805 regularTx := types.TxSlot{ 806 DataLen: 32, 807 FeeCap: *uint256.NewInt(1).Mul(uint256.NewInt(10), feeCap), //10x the previous 808 Tip: *uint256.NewInt(1).Mul(uint256.NewInt(10), tip), 809 BlobFeeCap: *uint256.NewInt(1).Mul(uint256.NewInt(10), blobFeeCap), 810 Gas: 500000, 811 SenderID: 0, 812 Creation: true, 813 Nonce: 0x2, 814 } 815 regularTx.IDHash[0] = 0x02 816 txSlots.Append(®ularTx, addr[:], true) 817 reasons, err := pool.AddLocalTxs(ctx, txSlots, tx) 818 assert.NoError(err) 819 t.Logf("Reasons %v", reasons) 820 for _, reason := range reasons { 821 assert.Equal(txpoolcfg.BlobTxReplace, reason, reason.String()) 822 } 823 } 824 825 // Try to replace it with required price bump (configured in pool.cfg.BlobPriceBump for blob txns) to all transaction fields - should be successful only if all are bumped 826 { 827 blobTxn := makeBlobTx() 828 origTip := blobTxn.Tip 829 origFee := blobTxn.FeeCap 830 blobTxn.Nonce = 0x2 831 blobTxn.IDHash[0] = 0x03 832 txSlots := types.TxSlots{} 833 txSlots.Append(&blobTxn, addr[:], true) 834 835 // Get the config of the pool for BlobPriceBump and bump prices 836 requiredPriceBump := pool.cfg.BlobPriceBump 837 838 // Bump the tip only 839 blobTxn.Tip.MulDivOverflow(tip, uint256.NewInt(requiredPriceBump+100), uint256.NewInt(100)) 840 reasons, err := pool.AddLocalTxs(ctx, txSlots, tx) 841 assert.NoError(err) 842 assert.Equal(txpoolcfg.ReplaceUnderpriced, reasons[0], reasons[0].String()) 843 844 // Bump the fee + tip 845 blobTxn.FeeCap.MulDivOverflow(feeCap, uint256.NewInt(requiredPriceBump+100), uint256.NewInt(100)) 846 reasons, err = pool.AddLocalTxs(ctx, txSlots, tx) 847 assert.NoError(err) 848 assert.Equal(txpoolcfg.ReplaceUnderpriced, reasons[0], reasons[0].String()) 849 850 // Bump only Feecap 851 blobTxn.Tip = origTip 852 reasons, err = pool.AddLocalTxs(ctx, txSlots, tx) 853 assert.NoError(err) 854 assert.Equal(txpoolcfg.ReplaceUnderpriced, reasons[0], reasons[0].String()) 855 856 // Bump fee cap + blobFee cap 857 blobTxn.BlobFeeCap.MulDivOverflow(blobFeeCap, uint256.NewInt(requiredPriceBump+100), uint256.NewInt(100)) 858 reasons, err = pool.AddLocalTxs(ctx, txSlots, tx) 859 assert.NoError(err) 860 assert.Equal(txpoolcfg.NotReplaced, reasons[0], reasons[0].String()) 861 862 // Bump only blobFee cap 863 blobTxn.FeeCap = origFee 864 reasons, err = pool.AddLocalTxs(ctx, txSlots, tx) 865 assert.NoError(err) 866 assert.Equal(txpoolcfg.NotReplaced, reasons[0], reasons[0].String()) 867 868 // Bump all prices 869 blobTxn.Tip.MulDivOverflow(tip, uint256.NewInt(requiredPriceBump+100), uint256.NewInt(100)) 870 blobTxn.FeeCap.MulDivOverflow(feeCap, uint256.NewInt(requiredPriceBump+100), uint256.NewInt(100)) 871 reasons, err = pool.AddLocalTxs(ctx, txSlots, tx) 872 assert.NoError(err) 873 assert.Equal(txpoolcfg.Success, reasons[0], reasons[0].String()) 874 } 875 } 876 877 // Todo, make the tx more realistic with good values 878 func makeBlobTx() types.TxSlot { 879 // Some arbitrary hardcoded example 880 bodyRlpHex := "f9012705078502540be4008506fc23ac008357b58494811a752c8cd697e3cb27" + 881 "279c330ed1ada745a8d7808204f7f872f85994de0b295669a9fd93d5f28d9ec85e40f4cb697b" + 882 "aef842a00000000000000000000000000000000000000000000000000000000000000003a000" + 883 "00000000000000000000000000000000000000000000000000000000000007d694bb9bc244d7" + 884 "98123fde783fcc1c72d3bb8c189413c07bf842a0c6bdd1de713471bd6cfa62dd8b5a5b42969e" + 885 "d09e26212d3377f3f8426d8ec210a08aaeccaf3873d07cef005aca28c39f8a9f8bdb1ec8d79f" + 886 "fc25afc0a4fa2ab73601a036b241b061a36a32ab7fe86c7aa9eb592dd59018cd0443adc09035" + 887 "90c16b02b0a05edcc541b4741c5cc6dd347c5ed9577ef293a62787b4510465fadbfe39ee4094" 888 bodyRlp := hexutility.MustDecodeHex(bodyRlpHex) 889 890 blobsRlpPrefix := hexutility.MustDecodeHex("fa040008") 891 blobRlpPrefix := hexutility.MustDecodeHex("ba020000") 892 893 var blob0, blob1 = gokzg4844.Blob{}, gokzg4844.Blob{} 894 copy(blob0[:], hexutility.MustDecodeHex(validBlob1)) 895 copy(blob1[:], hexutility.MustDecodeHex(validBlob2)) 896 897 var err error 898 proofsRlpPrefix := hexutility.MustDecodeHex("f862") 899 commitment0, _ := kzg.Ctx().BlobToKZGCommitment(blob0, 0) 900 commitment1, _ := kzg.Ctx().BlobToKZGCommitment(blob1, 0) 901 902 proof0, err := kzg.Ctx().ComputeBlobKZGProof(blob0, commitment0, 0) 903 if err != nil { 904 fmt.Println("error", err) 905 } 906 proof1, err := kzg.Ctx().ComputeBlobKZGProof(blob1, commitment1, 0) 907 if err != nil { 908 fmt.Println("error", err) 909 } 910 911 wrapperRlp := hexutility.MustDecodeHex("03fa0401fe") 912 wrapperRlp = append(wrapperRlp, bodyRlp...) 913 wrapperRlp = append(wrapperRlp, blobsRlpPrefix...) 914 wrapperRlp = append(wrapperRlp, blobRlpPrefix...) 915 wrapperRlp = append(wrapperRlp, blob0[:]...) 916 wrapperRlp = append(wrapperRlp, blobRlpPrefix...) 917 wrapperRlp = append(wrapperRlp, blob1[:]...) 918 wrapperRlp = append(wrapperRlp, proofsRlpPrefix...) 919 wrapperRlp = append(wrapperRlp, 0xb0) 920 wrapperRlp = append(wrapperRlp, commitment0[:]...) 921 wrapperRlp = append(wrapperRlp, 0xb0) 922 wrapperRlp = append(wrapperRlp, commitment1[:]...) 923 wrapperRlp = append(wrapperRlp, proofsRlpPrefix...) 924 wrapperRlp = append(wrapperRlp, 0xb0) 925 wrapperRlp = append(wrapperRlp, proof0[:]...) 926 wrapperRlp = append(wrapperRlp, 0xb0) 927 wrapperRlp = append(wrapperRlp, proof1[:]...) 928 929 tip, feeCap, blobFeeCap := uint256.NewInt(100_000), uint256.NewInt(200_000), uint256.NewInt(200_000) 930 931 blobTx := types.TxSlot{} 932 tctx := types.NewTxParseContext(*uint256.NewInt(5)) 933 tctx.WithSender(false) 934 tctx.ParseTransaction(wrapperRlp, 0, &blobTx, nil, false, true, nil) 935 blobTx.BlobHashes = make([]common.Hash, 2) 936 blobTx.BlobHashes[0] = common.Hash(kzg.KZGToVersionedHash(commitment0)) 937 blobTx.BlobHashes[1] = common.Hash(kzg.KZGToVersionedHash(commitment1)) 938 939 blobTx.Tip = *tip 940 blobTx.FeeCap = *feeCap 941 blobTx.BlobFeeCap = *blobFeeCap 942 return blobTx 943 }