github.com/ethereum/go-ethereum@v1.14.4-0.20240516095835-473ee8fc07a3/core/txpool/blobpool/blobpool_test.go (about)

     1  // Copyright 2023 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 blobpool
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/ecdsa"
    22  	"crypto/sha256"
    23  	"errors"
    24  	"math"
    25  	"math/big"
    26  	"os"
    27  	"path/filepath"
    28  	"sync"
    29  	"testing"
    30  	"time"
    31  
    32  	"github.com/ethereum/go-ethereum/common"
    33  	"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
    34  	"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
    35  	"github.com/ethereum/go-ethereum/core"
    36  	"github.com/ethereum/go-ethereum/core/rawdb"
    37  	"github.com/ethereum/go-ethereum/core/state"
    38  	"github.com/ethereum/go-ethereum/core/tracing"
    39  	"github.com/ethereum/go-ethereum/core/txpool"
    40  	"github.com/ethereum/go-ethereum/core/types"
    41  	"github.com/ethereum/go-ethereum/crypto"
    42  	"github.com/ethereum/go-ethereum/crypto/kzg4844"
    43  	"github.com/ethereum/go-ethereum/ethdb/memorydb"
    44  	"github.com/ethereum/go-ethereum/log"
    45  	"github.com/ethereum/go-ethereum/params"
    46  	"github.com/ethereum/go-ethereum/rlp"
    47  	"github.com/holiman/billy"
    48  	"github.com/holiman/uint256"
    49  )
    50  
    51  var (
    52  	emptyBlob          = new(kzg4844.Blob)
    53  	emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob)
    54  	emptyBlobProof, _  = kzg4844.ComputeBlobProof(emptyBlob, emptyBlobCommit)
    55  	emptyBlobVHash     = kzg4844.CalcBlobHashV1(sha256.New(), &emptyBlobCommit)
    56  )
    57  
    58  // Chain configuration with Cancun enabled.
    59  //
    60  // TODO(karalabe): replace with params.MainnetChainConfig after Cancun.
    61  var testChainConfig *params.ChainConfig
    62  
    63  func init() {
    64  	testChainConfig = new(params.ChainConfig)
    65  	*testChainConfig = *params.MainnetChainConfig
    66  
    67  	testChainConfig.CancunTime = new(uint64)
    68  	*testChainConfig.CancunTime = uint64(time.Now().Unix())
    69  }
    70  
    71  // testBlockChain is a mock of the live chain for testing the pool.
    72  type testBlockChain struct {
    73  	config  *params.ChainConfig
    74  	basefee *uint256.Int
    75  	blobfee *uint256.Int
    76  	statedb *state.StateDB
    77  }
    78  
    79  func (bc *testBlockChain) Config() *params.ChainConfig {
    80  	return bc.config
    81  }
    82  
    83  func (bc *testBlockChain) CurrentBlock() *types.Header {
    84  	// Yolo, life is too short to invert mist.CalcBaseFee and misc.CalcBlobFee,
    85  	// just binary search it them.
    86  
    87  	// The base fee at 5714 ETH translates into the 21000 base gas higher than
    88  	// mainnet ether existence, use that as a cap for the tests.
    89  	var (
    90  		blockNumber = new(big.Int).Add(bc.config.LondonBlock, big.NewInt(1))
    91  		blockTime   = *bc.config.CancunTime + 1
    92  		gasLimit    = uint64(30_000_000)
    93  	)
    94  	lo := new(big.Int)
    95  	hi := new(big.Int).Mul(big.NewInt(5714), new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil))
    96  
    97  	for new(big.Int).Add(lo, big.NewInt(1)).Cmp(hi) != 0 {
    98  		mid := new(big.Int).Add(lo, hi)
    99  		mid.Div(mid, big.NewInt(2))
   100  
   101  		if eip1559.CalcBaseFee(bc.config, &types.Header{
   102  			Number:   blockNumber,
   103  			GasLimit: gasLimit,
   104  			GasUsed:  0,
   105  			BaseFee:  mid,
   106  		}).Cmp(bc.basefee.ToBig()) > 0 {
   107  			hi = mid
   108  		} else {
   109  			lo = mid
   110  		}
   111  	}
   112  	baseFee := lo
   113  
   114  	// The excess blob gas at 2^27 translates into a blob fee higher than mainnet
   115  	// ether existence, use that as a cap for the tests.
   116  	lo = new(big.Int)
   117  	hi = new(big.Int).Exp(big.NewInt(2), big.NewInt(27), nil)
   118  
   119  	for new(big.Int).Add(lo, big.NewInt(1)).Cmp(hi) != 0 {
   120  		mid := new(big.Int).Add(lo, hi)
   121  		mid.Div(mid, big.NewInt(2))
   122  
   123  		if eip4844.CalcBlobFee(mid.Uint64()).Cmp(bc.blobfee.ToBig()) > 0 {
   124  			hi = mid
   125  		} else {
   126  			lo = mid
   127  		}
   128  	}
   129  	excessBlobGas := lo.Uint64()
   130  
   131  	return &types.Header{
   132  		Number:        blockNumber,
   133  		Time:          blockTime,
   134  		GasLimit:      gasLimit,
   135  		BaseFee:       baseFee,
   136  		ExcessBlobGas: &excessBlobGas,
   137  	}
   138  }
   139  
   140  func (bc *testBlockChain) CurrentFinalBlock() *types.Header {
   141  	return &types.Header{
   142  		Number: big.NewInt(0),
   143  	}
   144  }
   145  
   146  func (bt *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {
   147  	return nil
   148  }
   149  
   150  func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) {
   151  	return bc.statedb, nil
   152  }
   153  
   154  // makeAddressReserver is a utility method to sanity check that accounts are
   155  // properly reserved by the blobpool (no duplicate reserves or unreserves).
   156  func makeAddressReserver() txpool.AddressReserver {
   157  	var (
   158  		reserved = make(map[common.Address]struct{})
   159  		lock     sync.Mutex
   160  	)
   161  	return func(addr common.Address, reserve bool) error {
   162  		lock.Lock()
   163  		defer lock.Unlock()
   164  
   165  		_, exists := reserved[addr]
   166  		if reserve {
   167  			if exists {
   168  				panic("already reserved")
   169  			}
   170  			reserved[addr] = struct{}{}
   171  			return nil
   172  		}
   173  		if !exists {
   174  			panic("not reserved")
   175  		}
   176  		delete(reserved, addr)
   177  		return nil
   178  	}
   179  }
   180  
   181  // makeTx is a utility method to construct a random blob transaction and sign it
   182  // with a valid key, only setting the interesting fields from the perspective of
   183  // the blob pool.
   184  func makeTx(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap uint64, key *ecdsa.PrivateKey) *types.Transaction {
   185  	blobtx := makeUnsignedTx(nonce, gasTipCap, gasFeeCap, blobFeeCap)
   186  	return types.MustSignNewTx(key, types.LatestSigner(testChainConfig), blobtx)
   187  }
   188  
   189  // makeUnsignedTx is a utility method to construct a random blob transaction
   190  // without signing it.
   191  func makeUnsignedTx(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap uint64) *types.BlobTx {
   192  	return &types.BlobTx{
   193  		ChainID:    uint256.MustFromBig(testChainConfig.ChainID),
   194  		Nonce:      nonce,
   195  		GasTipCap:  uint256.NewInt(gasTipCap),
   196  		GasFeeCap:  uint256.NewInt(gasFeeCap),
   197  		Gas:        21000,
   198  		BlobFeeCap: uint256.NewInt(blobFeeCap),
   199  		BlobHashes: []common.Hash{emptyBlobVHash},
   200  		Value:      uint256.NewInt(100),
   201  		Sidecar: &types.BlobTxSidecar{
   202  			Blobs:       []kzg4844.Blob{*emptyBlob},
   203  			Commitments: []kzg4844.Commitment{emptyBlobCommit},
   204  			Proofs:      []kzg4844.Proof{emptyBlobProof},
   205  		},
   206  	}
   207  }
   208  
   209  // verifyPoolInternals iterates over all the transactions in the pool and checks
   210  // that sort orders, calculated fields, cumulated fields are correct.
   211  func verifyPoolInternals(t *testing.T, pool *BlobPool) {
   212  	// Mark this method as a helper to remove from stack traces
   213  	t.Helper()
   214  
   215  	// Verify that all items in the index are present in the lookup and nothing more
   216  	seen := make(map[common.Hash]struct{})
   217  	for addr, txs := range pool.index {
   218  		for _, tx := range txs {
   219  			if _, ok := seen[tx.hash]; ok {
   220  				t.Errorf("duplicate hash #%x in transaction index: address %s, nonce %d", tx.hash, addr, tx.nonce)
   221  			}
   222  			seen[tx.hash] = struct{}{}
   223  		}
   224  	}
   225  	for hash, id := range pool.lookup {
   226  		if _, ok := seen[hash]; !ok {
   227  			t.Errorf("lookup entry missing from transaction index: hash #%x, id %d", hash, id)
   228  		}
   229  		delete(seen, hash)
   230  	}
   231  	for hash := range seen {
   232  		t.Errorf("indexed transaction hash #%x missing from lookup table", hash)
   233  	}
   234  	// Verify that transactions are sorted per account and contain no nonce gaps
   235  	for addr, txs := range pool.index {
   236  		for i := 1; i < len(txs); i++ {
   237  			if txs[i].nonce != txs[i-1].nonce+1 {
   238  				t.Errorf("addr %v, tx %d nonce mismatch: have %d, want %d", addr, i, txs[i].nonce, txs[i-1].nonce+1)
   239  			}
   240  		}
   241  	}
   242  	// Verify that calculated evacuation thresholds are correct
   243  	for addr, txs := range pool.index {
   244  		if !txs[0].evictionExecTip.Eq(txs[0].execTipCap) {
   245  			t.Errorf("addr %v, tx %d eviction execution tip mismatch: have %d, want %d", addr, 0, txs[0].evictionExecTip, txs[0].execTipCap)
   246  		}
   247  		if math.Abs(txs[0].evictionExecFeeJumps-txs[0].basefeeJumps) > 0.001 {
   248  			t.Errorf("addr %v, tx %d eviction execution fee jumps mismatch: have %f, want %f", addr, 0, txs[0].evictionExecFeeJumps, txs[0].basefeeJumps)
   249  		}
   250  		if math.Abs(txs[0].evictionBlobFeeJumps-txs[0].blobfeeJumps) > 0.001 {
   251  			t.Errorf("addr %v, tx %d eviction blob fee jumps mismatch: have %f, want %f", addr, 0, txs[0].evictionBlobFeeJumps, txs[0].blobfeeJumps)
   252  		}
   253  		for i := 1; i < len(txs); i++ {
   254  			wantExecTip := txs[i-1].evictionExecTip
   255  			if wantExecTip.Gt(txs[i].execTipCap) {
   256  				wantExecTip = txs[i].execTipCap
   257  			}
   258  			if !txs[i].evictionExecTip.Eq(wantExecTip) {
   259  				t.Errorf("addr %v, tx %d eviction execution tip mismatch: have %d, want %d", addr, i, txs[i].evictionExecTip, wantExecTip)
   260  			}
   261  
   262  			wantExecFeeJumps := txs[i-1].evictionExecFeeJumps
   263  			if wantExecFeeJumps > txs[i].basefeeJumps {
   264  				wantExecFeeJumps = txs[i].basefeeJumps
   265  			}
   266  			if math.Abs(txs[i].evictionExecFeeJumps-wantExecFeeJumps) > 0.001 {
   267  				t.Errorf("addr %v, tx %d eviction execution fee jumps mismatch: have %f, want %f", addr, i, txs[i].evictionExecFeeJumps, wantExecFeeJumps)
   268  			}
   269  
   270  			wantBlobFeeJumps := txs[i-1].evictionBlobFeeJumps
   271  			if wantBlobFeeJumps > txs[i].blobfeeJumps {
   272  				wantBlobFeeJumps = txs[i].blobfeeJumps
   273  			}
   274  			if math.Abs(txs[i].evictionBlobFeeJumps-wantBlobFeeJumps) > 0.001 {
   275  				t.Errorf("addr %v, tx %d eviction blob fee jumps mismatch: have %f, want %f", addr, i, txs[i].evictionBlobFeeJumps, wantBlobFeeJumps)
   276  			}
   277  		}
   278  	}
   279  	// Verify that account balance accumulations are correct
   280  	for addr, txs := range pool.index {
   281  		spent := new(uint256.Int)
   282  		for _, tx := range txs {
   283  			spent.Add(spent, tx.costCap)
   284  		}
   285  		if !pool.spent[addr].Eq(spent) {
   286  			t.Errorf("addr %v expenditure mismatch: have %d, want %d", addr, pool.spent[addr], spent)
   287  		}
   288  	}
   289  	// Verify that pool storage size is correct
   290  	var stored uint64
   291  	for _, txs := range pool.index {
   292  		for _, tx := range txs {
   293  			stored += uint64(tx.size)
   294  		}
   295  	}
   296  	if pool.stored != stored {
   297  		t.Errorf("pool storage mismatch: have %d, want %d", pool.stored, stored)
   298  	}
   299  	// Verify the price heap internals
   300  	verifyHeapInternals(t, pool.evict)
   301  }
   302  
   303  // Tests that transactions can be loaded from disk on startup and that they are
   304  // correctly discarded if invalid.
   305  //
   306  //   - 1. A transaction that cannot be decoded must be dropped
   307  //   - 2. A transaction that cannot be recovered (bad signature) must be dropped
   308  //   - 3. All transactions after a nonce gap must be dropped
   309  //   - 4. All transactions after an already included nonce must be dropped
   310  //   - 5. All transactions after an underpriced one (including it) must be dropped
   311  //   - 6. All transactions after an overdrafting sequence must be dropped
   312  //   - 7. All transactions exceeding the per-account limit must be dropped
   313  //
   314  // Furthermore, some strange corner-cases can also occur after a crash, as Billy's
   315  // simplicity also allows it to resurrect past deleted entities:
   316  //
   317  //   - 8. Fully duplicate transactions (matching hash) must be dropped
   318  //   - 9. Duplicate nonces from the same account must be dropped
   319  func TestOpenDrops(t *testing.T) {
   320  	log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true)))
   321  
   322  	// Create a temporary folder for the persistent backend
   323  	storage, _ := os.MkdirTemp("", "blobpool-")
   324  	defer os.RemoveAll(storage)
   325  
   326  	os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700)
   327  	store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(), nil)
   328  
   329  	// Insert a malformed transaction to verify that decoding errors (or format
   330  	// changes) are handled gracefully (case 1)
   331  	malformed, _ := store.Put([]byte("this is a badly encoded transaction"))
   332  
   333  	// Insert a transaction with a bad signature to verify that stale junk after
   334  	// potential hard-forks can get evicted (case 2)
   335  	tx := types.NewTx(&types.BlobTx{
   336  		ChainID:    uint256.MustFromBig(testChainConfig.ChainID),
   337  		GasTipCap:  new(uint256.Int),
   338  		GasFeeCap:  new(uint256.Int),
   339  		Gas:        0,
   340  		Value:      new(uint256.Int),
   341  		Data:       nil,
   342  		BlobFeeCap: new(uint256.Int),
   343  		V:          new(uint256.Int),
   344  		R:          new(uint256.Int),
   345  		S:          new(uint256.Int),
   346  	})
   347  	blob, _ := rlp.EncodeToBytes(tx)
   348  	badsig, _ := store.Put(blob)
   349  
   350  	// Insert a sequence of transactions with a nonce gap in between to verify
   351  	// that anything gapped will get evicted (case 3).
   352  	var (
   353  		gapper, _ = crypto.GenerateKey()
   354  
   355  		valids = make(map[uint64]struct{})
   356  		gapped = make(map[uint64]struct{})
   357  	)
   358  	for _, nonce := range []uint64{0, 1, 3, 4, 6, 7} { // first gap at #2, another at #5
   359  		tx := makeTx(nonce, 1, 1, 1, gapper)
   360  		blob, _ := rlp.EncodeToBytes(tx)
   361  
   362  		id, _ := store.Put(blob)
   363  		if nonce < 2 {
   364  			valids[id] = struct{}{}
   365  		} else {
   366  			gapped[id] = struct{}{}
   367  		}
   368  	}
   369  	// Insert a sequence of transactions with a gapped starting nonce to verify
   370  	// that the entire set will get dropped (case 3).
   371  	var (
   372  		dangler, _ = crypto.GenerateKey()
   373  		dangling   = make(map[uint64]struct{})
   374  	)
   375  	for _, nonce := range []uint64{1, 2, 3} { // first gap at #0, all set dangling
   376  		tx := makeTx(nonce, 1, 1, 1, dangler)
   377  		blob, _ := rlp.EncodeToBytes(tx)
   378  
   379  		id, _ := store.Put(blob)
   380  		dangling[id] = struct{}{}
   381  	}
   382  	// Insert a sequence of transactions with already passed nonces to veirfy
   383  	// that the entire set will get dropped (case 4).
   384  	var (
   385  		filler, _ = crypto.GenerateKey()
   386  		filled    = make(map[uint64]struct{})
   387  	)
   388  	for _, nonce := range []uint64{0, 1, 2} { // account nonce at 3, all set filled
   389  		tx := makeTx(nonce, 1, 1, 1, filler)
   390  		blob, _ := rlp.EncodeToBytes(tx)
   391  
   392  		id, _ := store.Put(blob)
   393  		filled[id] = struct{}{}
   394  	}
   395  	// Insert a sequence of transactions with partially passed nonces to verify
   396  	// that the included part of the set will get dropped (case 4).
   397  	var (
   398  		overlapper, _ = crypto.GenerateKey()
   399  		overlapped    = make(map[uint64]struct{})
   400  	)
   401  	for _, nonce := range []uint64{0, 1, 2, 3} { // account nonce at 2, half filled
   402  		tx := makeTx(nonce, 1, 1, 1, overlapper)
   403  		blob, _ := rlp.EncodeToBytes(tx)
   404  
   405  		id, _ := store.Put(blob)
   406  		if nonce >= 2 {
   407  			valids[id] = struct{}{}
   408  		} else {
   409  			overlapped[id] = struct{}{}
   410  		}
   411  	}
   412  	// Insert a sequence of transactions with an underpriced first to verify that
   413  	// the entire set will get dropped (case 5).
   414  	var (
   415  		underpayer, _ = crypto.GenerateKey()
   416  		underpaid     = make(map[uint64]struct{})
   417  	)
   418  	for i := 0; i < 5; i++ { // make #0 underpriced
   419  		var tx *types.Transaction
   420  		if i == 0 {
   421  			tx = makeTx(uint64(i), 0, 0, 0, underpayer)
   422  		} else {
   423  			tx = makeTx(uint64(i), 1, 1, 1, underpayer)
   424  		}
   425  		blob, _ := rlp.EncodeToBytes(tx)
   426  
   427  		id, _ := store.Put(blob)
   428  		underpaid[id] = struct{}{}
   429  	}
   430  
   431  	// Insert a sequence of transactions with an underpriced in between to verify
   432  	// that it and anything newly gapped will get evicted (case 5).
   433  	var (
   434  		outpricer, _ = crypto.GenerateKey()
   435  		outpriced    = make(map[uint64]struct{})
   436  	)
   437  	for i := 0; i < 5; i++ { // make #2 underpriced
   438  		var tx *types.Transaction
   439  		if i == 2 {
   440  			tx = makeTx(uint64(i), 0, 0, 0, outpricer)
   441  		} else {
   442  			tx = makeTx(uint64(i), 1, 1, 1, outpricer)
   443  		}
   444  		blob, _ := rlp.EncodeToBytes(tx)
   445  
   446  		id, _ := store.Put(blob)
   447  		if i < 2 {
   448  			valids[id] = struct{}{}
   449  		} else {
   450  			outpriced[id] = struct{}{}
   451  		}
   452  	}
   453  	// Insert a sequence of transactions fully overdrafted to verify that the
   454  	// entire set will get invalidated (case 6).
   455  	var (
   456  		exceeder, _ = crypto.GenerateKey()
   457  		exceeded    = make(map[uint64]struct{})
   458  	)
   459  	for _, nonce := range []uint64{0, 1, 2} { // nonce 0 overdrafts the account
   460  		var tx *types.Transaction
   461  		if nonce == 0 {
   462  			tx = makeTx(nonce, 1, 100, 1, exceeder)
   463  		} else {
   464  			tx = makeTx(nonce, 1, 1, 1, exceeder)
   465  		}
   466  		blob, _ := rlp.EncodeToBytes(tx)
   467  
   468  		id, _ := store.Put(blob)
   469  		exceeded[id] = struct{}{}
   470  	}
   471  	// Insert a sequence of transactions partially overdrafted to verify that part
   472  	// of the set will get invalidated (case 6).
   473  	var (
   474  		overdrafter, _ = crypto.GenerateKey()
   475  		overdrafted    = make(map[uint64]struct{})
   476  	)
   477  	for _, nonce := range []uint64{0, 1, 2} { // nonce 1 overdrafts the account
   478  		var tx *types.Transaction
   479  		if nonce == 1 {
   480  			tx = makeTx(nonce, 1, 100, 1, overdrafter)
   481  		} else {
   482  			tx = makeTx(nonce, 1, 1, 1, overdrafter)
   483  		}
   484  		blob, _ := rlp.EncodeToBytes(tx)
   485  
   486  		id, _ := store.Put(blob)
   487  		if nonce < 1 {
   488  			valids[id] = struct{}{}
   489  		} else {
   490  			overdrafted[id] = struct{}{}
   491  		}
   492  	}
   493  	// Insert a sequence of transactions overflowing the account cap to verify
   494  	// that part of the set will get invalidated (case 7).
   495  	var (
   496  		overcapper, _ = crypto.GenerateKey()
   497  		overcapped    = make(map[uint64]struct{})
   498  	)
   499  	for nonce := uint64(0); nonce < maxTxsPerAccount+3; nonce++ {
   500  		blob, _ := rlp.EncodeToBytes(makeTx(nonce, 1, 1, 1, overcapper))
   501  
   502  		id, _ := store.Put(blob)
   503  		if nonce < maxTxsPerAccount {
   504  			valids[id] = struct{}{}
   505  		} else {
   506  			overcapped[id] = struct{}{}
   507  		}
   508  	}
   509  	// Insert a batch of duplicated transactions to verify that only one of each
   510  	// version will remain (case 8).
   511  	var (
   512  		duplicater, _ = crypto.GenerateKey()
   513  		duplicated    = make(map[uint64]struct{})
   514  	)
   515  	for _, nonce := range []uint64{0, 1, 2} {
   516  		blob, _ := rlp.EncodeToBytes(makeTx(nonce, 1, 1, 1, duplicater))
   517  
   518  		for i := 0; i < int(nonce)+1; i++ {
   519  			id, _ := store.Put(blob)
   520  			if i == 0 {
   521  				valids[id] = struct{}{}
   522  			} else {
   523  				duplicated[id] = struct{}{}
   524  			}
   525  		}
   526  	}
   527  	// Insert a batch of duplicated nonces to verify that only one of each will
   528  	// remain (case 9).
   529  	var (
   530  		repeater, _ = crypto.GenerateKey()
   531  		repeated    = make(map[uint64]struct{})
   532  	)
   533  	for _, nonce := range []uint64{0, 1, 2} {
   534  		for i := 0; i < int(nonce)+1; i++ {
   535  			blob, _ := rlp.EncodeToBytes(makeTx(nonce, 1, uint64(i)+1 /* unique hashes */, 1, repeater))
   536  
   537  			id, _ := store.Put(blob)
   538  			if i == 0 {
   539  				valids[id] = struct{}{}
   540  			} else {
   541  				repeated[id] = struct{}{}
   542  			}
   543  		}
   544  	}
   545  	store.Close()
   546  
   547  	// Create a blob pool out of the pre-seeded data
   548  	statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
   549  	statedb.AddBalance(crypto.PubkeyToAddress(gapper.PublicKey), uint256.NewInt(1000000), tracing.BalanceChangeUnspecified)
   550  	statedb.AddBalance(crypto.PubkeyToAddress(dangler.PublicKey), uint256.NewInt(1000000), tracing.BalanceChangeUnspecified)
   551  	statedb.AddBalance(crypto.PubkeyToAddress(filler.PublicKey), uint256.NewInt(1000000), tracing.BalanceChangeUnspecified)
   552  	statedb.SetNonce(crypto.PubkeyToAddress(filler.PublicKey), 3)
   553  	statedb.AddBalance(crypto.PubkeyToAddress(overlapper.PublicKey), uint256.NewInt(1000000), tracing.BalanceChangeUnspecified)
   554  	statedb.SetNonce(crypto.PubkeyToAddress(overlapper.PublicKey), 2)
   555  	statedb.AddBalance(crypto.PubkeyToAddress(underpayer.PublicKey), uint256.NewInt(1000000), tracing.BalanceChangeUnspecified)
   556  	statedb.AddBalance(crypto.PubkeyToAddress(outpricer.PublicKey), uint256.NewInt(1000000), tracing.BalanceChangeUnspecified)
   557  	statedb.AddBalance(crypto.PubkeyToAddress(exceeder.PublicKey), uint256.NewInt(1000000), tracing.BalanceChangeUnspecified)
   558  	statedb.AddBalance(crypto.PubkeyToAddress(overdrafter.PublicKey), uint256.NewInt(1000000), tracing.BalanceChangeUnspecified)
   559  	statedb.AddBalance(crypto.PubkeyToAddress(overcapper.PublicKey), uint256.NewInt(10000000), tracing.BalanceChangeUnspecified)
   560  	statedb.AddBalance(crypto.PubkeyToAddress(duplicater.PublicKey), uint256.NewInt(1000000), tracing.BalanceChangeUnspecified)
   561  	statedb.AddBalance(crypto.PubkeyToAddress(repeater.PublicKey), uint256.NewInt(1000000), tracing.BalanceChangeUnspecified)
   562  	statedb.Commit(0, true)
   563  
   564  	chain := &testBlockChain{
   565  		config:  testChainConfig,
   566  		basefee: uint256.NewInt(params.InitialBaseFee),
   567  		blobfee: uint256.NewInt(params.BlobTxMinBlobGasprice),
   568  		statedb: statedb,
   569  	}
   570  	pool := New(Config{Datadir: storage}, chain)
   571  	if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
   572  		t.Fatalf("failed to create blob pool: %v", err)
   573  	}
   574  	defer pool.Close()
   575  
   576  	// Verify that the malformed (case 1), badly signed (case 2) and gapped (case
   577  	// 3) txs have been deleted from the pool
   578  	alive := make(map[uint64]struct{})
   579  	for _, txs := range pool.index {
   580  		for _, tx := range txs {
   581  			switch tx.id {
   582  			case malformed:
   583  				t.Errorf("malformed RLP transaction remained in storage")
   584  			case badsig:
   585  				t.Errorf("invalidly signed transaction remained in storage")
   586  			default:
   587  				if _, ok := dangling[tx.id]; ok {
   588  					t.Errorf("dangling transaction remained in storage: %d", tx.id)
   589  				} else if _, ok := filled[tx.id]; ok {
   590  					t.Errorf("filled transaction remained in storage: %d", tx.id)
   591  				} else if _, ok := overlapped[tx.id]; ok {
   592  					t.Errorf("overlapped transaction remained in storage: %d", tx.id)
   593  				} else if _, ok := gapped[tx.id]; ok {
   594  					t.Errorf("gapped transaction remained in storage: %d", tx.id)
   595  				} else if _, ok := underpaid[tx.id]; ok {
   596  					t.Errorf("underpaid transaction remained in storage: %d", tx.id)
   597  				} else if _, ok := outpriced[tx.id]; ok {
   598  					t.Errorf("outpriced transaction remained in storage: %d", tx.id)
   599  				} else if _, ok := exceeded[tx.id]; ok {
   600  					t.Errorf("fully overdrafted transaction remained in storage: %d", tx.id)
   601  				} else if _, ok := overdrafted[tx.id]; ok {
   602  					t.Errorf("partially overdrafted transaction remained in storage: %d", tx.id)
   603  				} else if _, ok := overcapped[tx.id]; ok {
   604  					t.Errorf("overcapped transaction remained in storage: %d", tx.id)
   605  				} else if _, ok := duplicated[tx.id]; ok {
   606  					t.Errorf("duplicated transaction remained in storage: %d", tx.id)
   607  				} else if _, ok := repeated[tx.id]; ok {
   608  					t.Errorf("repeated nonce transaction remained in storage: %d", tx.id)
   609  				} else {
   610  					alive[tx.id] = struct{}{}
   611  				}
   612  			}
   613  		}
   614  	}
   615  	// Verify that the rest of the transactions remained alive
   616  	if len(alive) != len(valids) {
   617  		t.Errorf("valid transaction count mismatch: have %d, want %d", len(alive), len(valids))
   618  	}
   619  	for id := range alive {
   620  		if _, ok := valids[id]; !ok {
   621  			t.Errorf("extra transaction %d", id)
   622  		}
   623  	}
   624  	for id := range valids {
   625  		if _, ok := alive[id]; !ok {
   626  			t.Errorf("missing transaction %d", id)
   627  		}
   628  	}
   629  	// Verify all the calculated pool internals. Interestingly, this is **not**
   630  	// a duplication of the above checks, this actually validates the verifier
   631  	// using the above already hard coded checks.
   632  	//
   633  	// Do not remove this, nor alter the above to be generic.
   634  	verifyPoolInternals(t, pool)
   635  }
   636  
   637  // Tests that transactions loaded from disk are indexed correctly.
   638  //
   639  //   - 1. Transactions must be grouped by sender, sorted by nonce
   640  //   - 2. Eviction thresholds are calculated correctly for the sequences
   641  //   - 3. Balance usage of an account is totals across all transactions
   642  func TestOpenIndex(t *testing.T) {
   643  	log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true)))
   644  
   645  	// Create a temporary folder for the persistent backend
   646  	storage, _ := os.MkdirTemp("", "blobpool-")
   647  	defer os.RemoveAll(storage)
   648  
   649  	os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700)
   650  	store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(), nil)
   651  
   652  	// Insert a sequence of transactions with varying price points to check that
   653  	// the cumulative minimum will be maintained.
   654  	var (
   655  		key, _ = crypto.GenerateKey()
   656  		addr   = crypto.PubkeyToAddress(key.PublicKey)
   657  
   658  		txExecTipCaps = []uint64{10, 25, 5, 7, 1, 100}
   659  		txExecFeeCaps = []uint64{100, 90, 200, 10, 80, 300}
   660  		txBlobFeeCaps = []uint64{55, 66, 77, 33, 22, 11}
   661  
   662  		//basefeeJumps = []float64{39.098, 38.204, 44.983, 19.549, 37.204, 48.426} // log 1.125 (exec fee cap)
   663  		//blobfeeJumps = []float64{34.023, 35.570, 36.879, 29.686, 26.243, 20.358} // log 1.125 (blob fee cap)
   664  
   665  		evictExecTipCaps  = []uint64{10, 10, 5, 5, 1, 1}
   666  		evictExecFeeJumps = []float64{39.098, 38.204, 38.204, 19.549, 19.549, 19.549} //  min(log 1.125 (exec fee cap))
   667  		evictBlobFeeJumps = []float64{34.023, 34.023, 34.023, 29.686, 26.243, 20.358} // min(log 1.125 (blob fee cap))
   668  
   669  		totalSpent = uint256.NewInt(21000*(100+90+200+10+80+300) + blobSize*(55+66+77+33+22+11) + 100*6) // 21000 gas x price + 128KB x blobprice + value
   670  	)
   671  	for _, i := range []int{5, 3, 4, 2, 0, 1} { // Randomize the tx insertion order to force sorting on load
   672  		tx := makeTx(uint64(i), txExecTipCaps[i], txExecFeeCaps[i], txBlobFeeCaps[i], key)
   673  		blob, _ := rlp.EncodeToBytes(tx)
   674  		store.Put(blob)
   675  	}
   676  	store.Close()
   677  
   678  	// Create a blob pool out of the pre-seeded data
   679  	statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
   680  	statedb.AddBalance(addr, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
   681  	statedb.Commit(0, true)
   682  
   683  	chain := &testBlockChain{
   684  		config:  testChainConfig,
   685  		basefee: uint256.NewInt(params.InitialBaseFee),
   686  		blobfee: uint256.NewInt(params.BlobTxMinBlobGasprice),
   687  		statedb: statedb,
   688  	}
   689  	pool := New(Config{Datadir: storage}, chain)
   690  	if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
   691  		t.Fatalf("failed to create blob pool: %v", err)
   692  	}
   693  	defer pool.Close()
   694  
   695  	// Verify that the transactions have been sorted by nonce (case 1)
   696  	for i := 0; i < len(pool.index[addr]); i++ {
   697  		if pool.index[addr][i].nonce != uint64(i) {
   698  			t.Errorf("tx %d nonce mismatch: have %d, want %d", i, pool.index[addr][i].nonce, uint64(i))
   699  		}
   700  	}
   701  	// Verify that the cumulative fee minimums have been correctly calculated (case 2)
   702  	for i, cap := range evictExecTipCaps {
   703  		if !pool.index[addr][i].evictionExecTip.Eq(uint256.NewInt(cap)) {
   704  			t.Errorf("eviction tip cap %d mismatch: have %d, want %d", i, pool.index[addr][i].evictionExecTip, cap)
   705  		}
   706  	}
   707  	for i, jumps := range evictExecFeeJumps {
   708  		if math.Abs(pool.index[addr][i].evictionExecFeeJumps-jumps) > 0.001 {
   709  			t.Errorf("eviction fee cap jumps %d mismatch: have %f, want %f", i, pool.index[addr][i].evictionExecFeeJumps, jumps)
   710  		}
   711  	}
   712  	for i, jumps := range evictBlobFeeJumps {
   713  		if math.Abs(pool.index[addr][i].evictionBlobFeeJumps-jumps) > 0.001 {
   714  			t.Errorf("eviction blob fee cap jumps %d mismatch: have %f, want %f", i, pool.index[addr][i].evictionBlobFeeJumps, jumps)
   715  		}
   716  	}
   717  	// Verify that the balance usage has been correctly calculated (case 3)
   718  	if !pool.spent[addr].Eq(totalSpent) {
   719  		t.Errorf("expenditure mismatch: have %d, want %d", pool.spent[addr], totalSpent)
   720  	}
   721  	// Verify all the calculated pool internals. Interestingly, this is **not**
   722  	// a duplication of the above checks, this actually validates the verifier
   723  	// using the above already hard coded checks.
   724  	//
   725  	// Do not remove this, nor alter the above to be generic.
   726  	verifyPoolInternals(t, pool)
   727  }
   728  
   729  // Tests that after indexing all the loaded transactions from disk, a price heap
   730  // is correctly constructed based on the head basefee and blobfee.
   731  func TestOpenHeap(t *testing.T) {
   732  	log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true)))
   733  
   734  	// Create a temporary folder for the persistent backend
   735  	storage, _ := os.MkdirTemp("", "blobpool-")
   736  	defer os.RemoveAll(storage)
   737  
   738  	os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700)
   739  	store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(), nil)
   740  
   741  	// Insert a few transactions from a few accounts. To remove randomness from
   742  	// the heap initialization, use a deterministic account/tx/priority ordering.
   743  	var (
   744  		key1, _ = crypto.GenerateKey()
   745  		key2, _ = crypto.GenerateKey()
   746  		key3, _ = crypto.GenerateKey()
   747  
   748  		addr1 = crypto.PubkeyToAddress(key1.PublicKey)
   749  		addr2 = crypto.PubkeyToAddress(key2.PublicKey)
   750  		addr3 = crypto.PubkeyToAddress(key3.PublicKey)
   751  	)
   752  	if bytes.Compare(addr1[:], addr2[:]) > 0 {
   753  		key1, addr1, key2, addr2 = key2, addr2, key1, addr1
   754  	}
   755  	if bytes.Compare(addr1[:], addr3[:]) > 0 {
   756  		key1, addr1, key3, addr3 = key3, addr3, key1, addr1
   757  	}
   758  	if bytes.Compare(addr2[:], addr3[:]) > 0 {
   759  		key2, addr2, key3, addr3 = key3, addr3, key2, addr2
   760  	}
   761  	var (
   762  		tx1 = makeTx(0, 1, 1000, 90, key1)
   763  		tx2 = makeTx(0, 1, 800, 70, key2)
   764  		tx3 = makeTx(0, 1, 1500, 110, key3)
   765  
   766  		blob1, _ = rlp.EncodeToBytes(tx1)
   767  		blob2, _ = rlp.EncodeToBytes(tx2)
   768  		blob3, _ = rlp.EncodeToBytes(tx3)
   769  
   770  		heapOrder = []common.Address{addr2, addr1, addr3}
   771  		heapIndex = map[common.Address]int{addr2: 0, addr1: 1, addr3: 2}
   772  	)
   773  	store.Put(blob1)
   774  	store.Put(blob2)
   775  	store.Put(blob3)
   776  	store.Close()
   777  
   778  	// Create a blob pool out of the pre-seeded data
   779  	statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
   780  	statedb.AddBalance(addr1, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
   781  	statedb.AddBalance(addr2, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
   782  	statedb.AddBalance(addr3, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
   783  	statedb.Commit(0, true)
   784  
   785  	chain := &testBlockChain{
   786  		config:  testChainConfig,
   787  		basefee: uint256.NewInt(1050),
   788  		blobfee: uint256.NewInt(105),
   789  		statedb: statedb,
   790  	}
   791  	pool := New(Config{Datadir: storage}, chain)
   792  	if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
   793  		t.Fatalf("failed to create blob pool: %v", err)
   794  	}
   795  	defer pool.Close()
   796  
   797  	// Verify that the heap's internal state matches the expectations
   798  	for i, addr := range pool.evict.addrs {
   799  		if addr != heapOrder[i] {
   800  			t.Errorf("slot %d mismatch: have %v, want %v", i, addr, heapOrder[i])
   801  		}
   802  	}
   803  	for addr, i := range pool.evict.index {
   804  		if i != heapIndex[addr] {
   805  			t.Errorf("index for %v mismatch: have %d, want %d", addr, i, heapIndex[addr])
   806  		}
   807  	}
   808  	// Verify all the calculated pool internals. Interestingly, this is **not**
   809  	// a duplication of the above checks, this actually validates the verifier
   810  	// using the above already hard coded checks.
   811  	//
   812  	// Do not remove this, nor alter the above to be generic.
   813  	verifyPoolInternals(t, pool)
   814  }
   815  
   816  // Tests that after the pool's previous state is loaded back, any transactions
   817  // over the new storage cap will get dropped.
   818  func TestOpenCap(t *testing.T) {
   819  	log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true)))
   820  
   821  	// Create a temporary folder for the persistent backend
   822  	storage, _ := os.MkdirTemp("", "blobpool-")
   823  	defer os.RemoveAll(storage)
   824  
   825  	os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700)
   826  	store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(), nil)
   827  
   828  	// Insert a few transactions from a few accounts
   829  	var (
   830  		key1, _ = crypto.GenerateKey()
   831  		key2, _ = crypto.GenerateKey()
   832  		key3, _ = crypto.GenerateKey()
   833  
   834  		addr1 = crypto.PubkeyToAddress(key1.PublicKey)
   835  		addr2 = crypto.PubkeyToAddress(key2.PublicKey)
   836  		addr3 = crypto.PubkeyToAddress(key3.PublicKey)
   837  
   838  		tx1 = makeTx(0, 1, 1000, 100, key1)
   839  		tx2 = makeTx(0, 1, 800, 70, key2)
   840  		tx3 = makeTx(0, 1, 1500, 110, key3)
   841  
   842  		blob1, _ = rlp.EncodeToBytes(tx1)
   843  		blob2, _ = rlp.EncodeToBytes(tx2)
   844  		blob3, _ = rlp.EncodeToBytes(tx3)
   845  
   846  		keep = []common.Address{addr1, addr3}
   847  		drop = []common.Address{addr2}
   848  		size = uint64(2 * (txAvgSize + blobSize))
   849  	)
   850  	store.Put(blob1)
   851  	store.Put(blob2)
   852  	store.Put(blob3)
   853  	store.Close()
   854  
   855  	// Verify pool capping twice: first by reducing the data cap, then restarting
   856  	// with a high cap to ensure everything was persisted previously
   857  	for _, datacap := range []uint64{2 * (txAvgSize + blobSize), 100 * (txAvgSize + blobSize)} {
   858  		// Create a blob pool out of the pre-seeded data, but cap it to 2 blob transaction
   859  		statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
   860  		statedb.AddBalance(addr1, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
   861  		statedb.AddBalance(addr2, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
   862  		statedb.AddBalance(addr3, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
   863  		statedb.Commit(0, true)
   864  
   865  		chain := &testBlockChain{
   866  			config:  testChainConfig,
   867  			basefee: uint256.NewInt(1050),
   868  			blobfee: uint256.NewInt(105),
   869  			statedb: statedb,
   870  		}
   871  		pool := New(Config{Datadir: storage, Datacap: datacap}, chain)
   872  		if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
   873  			t.Fatalf("failed to create blob pool: %v", err)
   874  		}
   875  		// Verify that enough transactions have been dropped to get the pool's size
   876  		// under the requested limit
   877  		if len(pool.index) != len(keep) {
   878  			t.Errorf("tracked account count mismatch: have %d, want %d", len(pool.index), len(keep))
   879  		}
   880  		for _, addr := range keep {
   881  			if _, ok := pool.index[addr]; !ok {
   882  				t.Errorf("expected account %v missing from pool", addr)
   883  			}
   884  		}
   885  		for _, addr := range drop {
   886  			if _, ok := pool.index[addr]; ok {
   887  				t.Errorf("unexpected account %v present in pool", addr)
   888  			}
   889  		}
   890  		if pool.stored != size {
   891  			t.Errorf("pool stored size mismatch: have %v, want %v", pool.stored, size)
   892  		}
   893  		// Verify all the calculated pool internals. Interestingly, this is **not**
   894  		// a duplication of the above checks, this actually validates the verifier
   895  		// using the above already hard coded checks.
   896  		//
   897  		// Do not remove this, nor alter the above to be generic.
   898  		verifyPoolInternals(t, pool)
   899  
   900  		pool.Close()
   901  	}
   902  }
   903  
   904  // Tests that adding transaction will correctly store it in the persistent store
   905  // and update all the indices.
   906  //
   907  // Note, this tests mostly checks the pool transaction shuffling logic or things
   908  // specific to the blob pool. It does not do an exhaustive transaction validity
   909  // check.
   910  func TestAdd(t *testing.T) {
   911  	log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true)))
   912  
   913  	// seed is a helper tumpe to seed an initial state db and pool
   914  	type seed struct {
   915  		balance uint64
   916  		nonce   uint64
   917  		txs     []*types.BlobTx
   918  	}
   919  
   920  	// addtx is a helper sender/tx tuple to represent a new tx addition
   921  	type addtx struct {
   922  		from string
   923  		tx   *types.BlobTx
   924  		err  error
   925  	}
   926  
   927  	tests := []struct {
   928  		seeds map[string]seed
   929  		adds  []addtx
   930  	}{
   931  		// Transactions from new accounts should be accepted if their initial
   932  		// nonce matches the expected one from the statedb. Higher or lower must
   933  		// be rejected.
   934  		{
   935  			seeds: map[string]seed{
   936  				"alice":  {balance: 21100 + blobSize},
   937  				"bob":    {balance: 21100 + blobSize, nonce: 1},
   938  				"claire": {balance: 21100 + blobSize},
   939  				"dave":   {balance: 21100 + blobSize, nonce: 1},
   940  			},
   941  			adds: []addtx{
   942  				{ // New account, no previous txs: accept nonce 0
   943  					from: "alice",
   944  					tx:   makeUnsignedTx(0, 1, 1, 1),
   945  					err:  nil,
   946  				},
   947  				{ // Old account, 1 tx in chain, 0 pending: accept nonce 1
   948  					from: "bob",
   949  					tx:   makeUnsignedTx(1, 1, 1, 1),
   950  					err:  nil,
   951  				},
   952  				{ // New account, no previous txs: reject nonce 1
   953  					from: "claire",
   954  					tx:   makeUnsignedTx(1, 1, 1, 1),
   955  					err:  core.ErrNonceTooHigh,
   956  				},
   957  				{ // Old account, 1 tx in chain, 0 pending: reject nonce 0
   958  					from: "dave",
   959  					tx:   makeUnsignedTx(0, 1, 1, 1),
   960  					err:  core.ErrNonceTooLow,
   961  				},
   962  				{ // Old account, 1 tx in chain, 0 pending: reject nonce 2
   963  					from: "dave",
   964  					tx:   makeUnsignedTx(2, 1, 1, 1),
   965  					err:  core.ErrNonceTooHigh,
   966  				},
   967  			},
   968  		},
   969  		// Transactions from already pooled accounts should only be accepted if
   970  		// the nonces are contiguous (ignore prices for now, will check later)
   971  		{
   972  			seeds: map[string]seed{
   973  				"alice": {
   974  					balance: 1000000,
   975  					txs: []*types.BlobTx{
   976  						makeUnsignedTx(0, 1, 1, 1),
   977  					},
   978  				},
   979  				"bob": {
   980  					balance: 1000000,
   981  					nonce:   1,
   982  					txs: []*types.BlobTx{
   983  						makeUnsignedTx(1, 1, 1, 1),
   984  					},
   985  				},
   986  			},
   987  			adds: []addtx{
   988  				{ // New account, 1 tx pending: reject duplicate nonce 0
   989  					from: "alice",
   990  					tx:   makeUnsignedTx(0, 1, 1, 1),
   991  					err:  txpool.ErrAlreadyKnown,
   992  				},
   993  				{ // New account, 1 tx pending: reject replacement nonce 0 (ignore price for now)
   994  					from: "alice",
   995  					tx:   makeUnsignedTx(0, 1, 1, 2),
   996  					err:  txpool.ErrReplaceUnderpriced,
   997  				},
   998  				{ // New account, 1 tx pending: accept nonce 1
   999  					from: "alice",
  1000  					tx:   makeUnsignedTx(1, 1, 1, 1),
  1001  					err:  nil,
  1002  				},
  1003  				{ // New account, 2 txs pending: reject nonce 3
  1004  					from: "alice",
  1005  					tx:   makeUnsignedTx(3, 1, 1, 1),
  1006  					err:  core.ErrNonceTooHigh,
  1007  				},
  1008  				{ // New account, 2 txs pending: accept nonce 2
  1009  					from: "alice",
  1010  					tx:   makeUnsignedTx(2, 1, 1, 1),
  1011  					err:  nil,
  1012  				},
  1013  				{ // New account, 3 txs pending: accept nonce 3 now
  1014  					from: "alice",
  1015  					tx:   makeUnsignedTx(3, 1, 1, 1),
  1016  					err:  nil,
  1017  				},
  1018  				{ // Old account, 1 tx in chain, 1 tx pending: reject duplicate nonce 1
  1019  					from: "bob",
  1020  					tx:   makeUnsignedTx(1, 1, 1, 1),
  1021  					err:  txpool.ErrAlreadyKnown,
  1022  				},
  1023  				{ // Old account, 1 tx in chain, 1 tx pending: accept nonce 2 (ignore price for now)
  1024  					from: "bob",
  1025  					tx:   makeUnsignedTx(2, 1, 1, 1),
  1026  					err:  nil,
  1027  				},
  1028  			},
  1029  		},
  1030  		// Transactions should only be accepted into the pool if the cumulative
  1031  		// expenditure doesn't overflow the account balance
  1032  		{
  1033  			seeds: map[string]seed{
  1034  				"alice": {balance: 63299 + 3*blobSize}, // 3 tx - 1 wei
  1035  			},
  1036  			adds: []addtx{
  1037  				{ // New account, no previous txs: accept nonce 0 with 21100 wei spend
  1038  					from: "alice",
  1039  					tx:   makeUnsignedTx(0, 1, 1, 1),
  1040  					err:  nil,
  1041  				},
  1042  				{ // New account, 1 pooled tx with 21100 wei spent: accept nonce 1 with 21100 wei spend
  1043  					from: "alice",
  1044  					tx:   makeUnsignedTx(1, 1, 1, 1),
  1045  					err:  nil,
  1046  				},
  1047  				{ // New account, 2 pooled tx with 42200 wei spent: reject nonce 2 with 21100 wei spend (1 wei overflow)
  1048  					from: "alice",
  1049  					tx:   makeUnsignedTx(2, 1, 1, 1),
  1050  					err:  core.ErrInsufficientFunds,
  1051  				},
  1052  			},
  1053  		},
  1054  		// Transactions should only be accepted into the pool if the total count
  1055  		// from the same account doesn't overflow the pool limits
  1056  		{
  1057  			seeds: map[string]seed{
  1058  				"alice": {balance: 10000000},
  1059  			},
  1060  			adds: []addtx{
  1061  				{ // New account, no previous txs, 16 slots left: accept nonce 0
  1062  					from: "alice",
  1063  					tx:   makeUnsignedTx(0, 1, 1, 1),
  1064  					err:  nil,
  1065  				},
  1066  				{ // New account, 1 pooled tx, 15 slots left: accept nonce 1
  1067  					from: "alice",
  1068  					tx:   makeUnsignedTx(1, 1, 1, 1),
  1069  					err:  nil,
  1070  				},
  1071  				{ // New account, 2 pooled tx, 14 slots left: accept nonce 2
  1072  					from: "alice",
  1073  					tx:   makeUnsignedTx(2, 1, 1, 1),
  1074  					err:  nil,
  1075  				},
  1076  				{ // New account, 3 pooled tx, 13 slots left: accept nonce 3
  1077  					from: "alice",
  1078  					tx:   makeUnsignedTx(3, 1, 1, 1),
  1079  					err:  nil,
  1080  				},
  1081  				{ // New account, 4 pooled tx, 12 slots left: accept nonce 4
  1082  					from: "alice",
  1083  					tx:   makeUnsignedTx(4, 1, 1, 1),
  1084  					err:  nil,
  1085  				},
  1086  				{ // New account, 5 pooled tx, 11 slots left: accept nonce 5
  1087  					from: "alice",
  1088  					tx:   makeUnsignedTx(5, 1, 1, 1),
  1089  					err:  nil,
  1090  				},
  1091  				{ // New account, 6 pooled tx, 10 slots left: accept nonce 6
  1092  					from: "alice",
  1093  					tx:   makeUnsignedTx(6, 1, 1, 1),
  1094  					err:  nil,
  1095  				},
  1096  				{ // New account, 7 pooled tx, 9 slots left: accept nonce 7
  1097  					from: "alice",
  1098  					tx:   makeUnsignedTx(7, 1, 1, 1),
  1099  					err:  nil,
  1100  				},
  1101  				{ // New account, 8 pooled tx, 8 slots left: accept nonce 8
  1102  					from: "alice",
  1103  					tx:   makeUnsignedTx(8, 1, 1, 1),
  1104  					err:  nil,
  1105  				},
  1106  				{ // New account, 9 pooled tx, 7 slots left: accept nonce 9
  1107  					from: "alice",
  1108  					tx:   makeUnsignedTx(9, 1, 1, 1),
  1109  					err:  nil,
  1110  				},
  1111  				{ // New account, 10 pooled tx, 6 slots left: accept nonce 10
  1112  					from: "alice",
  1113  					tx:   makeUnsignedTx(10, 1, 1, 1),
  1114  					err:  nil,
  1115  				},
  1116  				{ // New account, 11 pooled tx, 5 slots left: accept nonce 11
  1117  					from: "alice",
  1118  					tx:   makeUnsignedTx(11, 1, 1, 1),
  1119  					err:  nil,
  1120  				},
  1121  				{ // New account, 12 pooled tx, 4 slots left: accept nonce 12
  1122  					from: "alice",
  1123  					tx:   makeUnsignedTx(12, 1, 1, 1),
  1124  					err:  nil,
  1125  				},
  1126  				{ // New account, 13 pooled tx, 3 slots left: accept nonce 13
  1127  					from: "alice",
  1128  					tx:   makeUnsignedTx(13, 1, 1, 1),
  1129  					err:  nil,
  1130  				},
  1131  				{ // New account, 14 pooled tx, 2 slots left: accept nonce 14
  1132  					from: "alice",
  1133  					tx:   makeUnsignedTx(14, 1, 1, 1),
  1134  					err:  nil,
  1135  				},
  1136  				{ // New account, 15 pooled tx, 1 slots left: accept nonce 15
  1137  					from: "alice",
  1138  					tx:   makeUnsignedTx(15, 1, 1, 1),
  1139  					err:  nil,
  1140  				},
  1141  				{ // New account, 16 pooled tx, 0 slots left: accept nonce 15 replacement
  1142  					from: "alice",
  1143  					tx:   makeUnsignedTx(15, 10, 10, 10),
  1144  					err:  nil,
  1145  				},
  1146  				{ // New account, 16 pooled tx, 0 slots left: reject nonce 16 with overcap
  1147  					from: "alice",
  1148  					tx:   makeUnsignedTx(16, 1, 1, 1),
  1149  					err:  txpool.ErrAccountLimitExceeded,
  1150  				},
  1151  			},
  1152  		},
  1153  		// Previously existing transactions should be allowed to be replaced iff
  1154  		// the new cumulative expenditure can be covered by the account and the
  1155  		// prices are bumped all around (no percentage check here).
  1156  		{
  1157  			seeds: map[string]seed{
  1158  				"alice": {balance: 2*100 + 5*21000 + 3*blobSize},
  1159  			},
  1160  			adds: []addtx{
  1161  				{ // New account, no previous txs: reject nonce 0 with 341172 wei spend
  1162  					from: "alice",
  1163  					tx:   makeUnsignedTx(0, 1, 20, 1),
  1164  					err:  core.ErrInsufficientFunds,
  1165  				},
  1166  				{ // New account, no previous txs: accept nonce 0 with 173172 wei spend
  1167  					from: "alice",
  1168  					tx:   makeUnsignedTx(0, 1, 2, 1),
  1169  					err:  nil,
  1170  				},
  1171  				{ // New account, 1 pooled tx with 173172 wei spent: accept nonce 1 with 152172 wei spend
  1172  					from: "alice",
  1173  					tx:   makeUnsignedTx(1, 1, 1, 1),
  1174  					err:  nil,
  1175  				},
  1176  				{ // New account, 2 pooled tx with 325344 wei spent: reject nonce 0 with 599684 wei spend (173072 extra) (would overflow balance at nonce 1)
  1177  					from: "alice",
  1178  					tx:   makeUnsignedTx(0, 2, 5, 2),
  1179  					err:  core.ErrInsufficientFunds,
  1180  				},
  1181  				{ // New account, 2 pooled tx with 325344 wei spent: reject nonce 0 with no-gastip-bump
  1182  					from: "alice",
  1183  					tx:   makeUnsignedTx(0, 1, 3, 2),
  1184  					err:  txpool.ErrReplaceUnderpriced,
  1185  				},
  1186  				{ // New account, 2 pooled tx with 325344 wei spent: reject nonce 0 with no-gascap-bump
  1187  					from: "alice",
  1188  					tx:   makeUnsignedTx(0, 2, 2, 2),
  1189  					err:  txpool.ErrReplaceUnderpriced,
  1190  				},
  1191  				{ // New account, 2 pooled tx with 325344 wei spent: reject nonce 0 with no-blobcap-bump
  1192  					from: "alice",
  1193  					tx:   makeUnsignedTx(0, 2, 4, 1),
  1194  					err:  txpool.ErrReplaceUnderpriced,
  1195  				},
  1196  				{ // New account, 2 pooled tx with 325344 wei spent: accept nonce 0 with 84100 wei spend (42000 extra)
  1197  					from: "alice",
  1198  					tx:   makeUnsignedTx(0, 2, 4, 2),
  1199  					err:  nil,
  1200  				},
  1201  			},
  1202  		},
  1203  		// Previously existing transactions should be allowed to be replaced iff
  1204  		// the new prices are bumped by a sufficient amount.
  1205  		{
  1206  			seeds: map[string]seed{
  1207  				"alice": {balance: 100 + 8*21000 + 4*blobSize},
  1208  			},
  1209  			adds: []addtx{
  1210  				{ // New account, no previous txs: accept nonce 0
  1211  					from: "alice",
  1212  					tx:   makeUnsignedTx(0, 2, 4, 2),
  1213  					err:  nil,
  1214  				},
  1215  				{ // New account, 1 pooled tx: reject nonce 0 with low-gastip-bump
  1216  					from: "alice",
  1217  					tx:   makeUnsignedTx(0, 3, 8, 4),
  1218  					err:  txpool.ErrReplaceUnderpriced,
  1219  				},
  1220  				{ // New account, 1 pooled tx: reject nonce 0 with low-gascap-bump
  1221  					from: "alice",
  1222  					tx:   makeUnsignedTx(0, 4, 6, 4),
  1223  					err:  txpool.ErrReplaceUnderpriced,
  1224  				},
  1225  				{ // New account, 1 pooled tx: reject nonce 0 with low-blobcap-bump
  1226  					from: "alice",
  1227  					tx:   makeUnsignedTx(0, 4, 8, 3),
  1228  					err:  txpool.ErrReplaceUnderpriced,
  1229  				},
  1230  				{ // New account, 1 pooled tx: accept nonce 0 with all-bumps
  1231  					from: "alice",
  1232  					tx:   makeUnsignedTx(0, 4, 8, 4),
  1233  					err:  nil,
  1234  				},
  1235  			},
  1236  		},
  1237  		// Blob transactions that don't meet the min blob gas price should be rejected
  1238  		{
  1239  			seeds: map[string]seed{
  1240  				"alice": {balance: 10000000},
  1241  			},
  1242  			adds: []addtx{
  1243  				{ // New account, no previous txs, nonce 0, but blob fee cap too low
  1244  					from: "alice",
  1245  					tx:   makeUnsignedTx(0, 1, 1, 0),
  1246  					err:  txpool.ErrUnderpriced,
  1247  				},
  1248  				{ // Same as above but blob fee cap equals minimum, should be accepted
  1249  					from: "alice",
  1250  					tx:   makeUnsignedTx(0, 1, 1, params.BlobTxMinBlobGasprice),
  1251  					err:  nil,
  1252  				},
  1253  			},
  1254  		},
  1255  	}
  1256  	for i, tt := range tests {
  1257  		// Create a temporary folder for the persistent backend
  1258  		storage, _ := os.MkdirTemp("", "blobpool-")
  1259  		defer os.RemoveAll(storage) // late defer, still ok
  1260  
  1261  		os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700)
  1262  		store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(), nil)
  1263  
  1264  		// Insert the seed transactions for the pool startup
  1265  		var (
  1266  			keys  = make(map[string]*ecdsa.PrivateKey)
  1267  			addrs = make(map[string]common.Address)
  1268  		)
  1269  		statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
  1270  		for acc, seed := range tt.seeds {
  1271  			// Generate a new random key/address for the seed account
  1272  			keys[acc], _ = crypto.GenerateKey()
  1273  			addrs[acc] = crypto.PubkeyToAddress(keys[acc].PublicKey)
  1274  
  1275  			// Seed the state database with this account
  1276  			statedb.AddBalance(addrs[acc], new(uint256.Int).SetUint64(seed.balance), tracing.BalanceChangeUnspecified)
  1277  			statedb.SetNonce(addrs[acc], seed.nonce)
  1278  
  1279  			// Sign the seed transactions and store them in the data store
  1280  			for _, tx := range seed.txs {
  1281  				signed := types.MustSignNewTx(keys[acc], types.LatestSigner(testChainConfig), tx)
  1282  				blob, _ := rlp.EncodeToBytes(signed)
  1283  				store.Put(blob)
  1284  			}
  1285  		}
  1286  		statedb.Commit(0, true)
  1287  		store.Close()
  1288  
  1289  		// Create a blob pool out of the pre-seeded dats
  1290  		chain := &testBlockChain{
  1291  			config:  testChainConfig,
  1292  			basefee: uint256.NewInt(1050),
  1293  			blobfee: uint256.NewInt(105),
  1294  			statedb: statedb,
  1295  		}
  1296  		pool := New(Config{Datadir: storage}, chain)
  1297  		if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
  1298  			t.Fatalf("test %d: failed to create blob pool: %v", i, err)
  1299  		}
  1300  		verifyPoolInternals(t, pool)
  1301  
  1302  		// Add each transaction one by one, verifying the pool internals in between
  1303  		for j, add := range tt.adds {
  1304  			signed, _ := types.SignNewTx(keys[add.from], types.LatestSigner(testChainConfig), add.tx)
  1305  			if err := pool.add(signed); !errors.Is(err, add.err) {
  1306  				t.Errorf("test %d, tx %d: adding transaction error mismatch: have %v, want %v", i, j, err, add.err)
  1307  			}
  1308  			verifyPoolInternals(t, pool)
  1309  		}
  1310  		// Verify the pool internals and close down the test
  1311  		verifyPoolInternals(t, pool)
  1312  		pool.Close()
  1313  	}
  1314  }
  1315  
  1316  // Benchmarks the time it takes to assemble the lazy pending transaction list
  1317  // from the pool contents.
  1318  func BenchmarkPoolPending100Mb(b *testing.B) { benchmarkPoolPending(b, 100_000_000) }
  1319  func BenchmarkPoolPending1GB(b *testing.B)   { benchmarkPoolPending(b, 1_000_000_000) }
  1320  func BenchmarkPoolPending10GB(b *testing.B)  { benchmarkPoolPending(b, 10_000_000_000) }
  1321  
  1322  func benchmarkPoolPending(b *testing.B, datacap uint64) {
  1323  	// Calculate the maximum number of transaction that would fit into the pool
  1324  	// and generate a set of random accounts to seed them with.
  1325  	capacity := datacap / params.BlobTxBlobGasPerBlob
  1326  
  1327  	var (
  1328  		basefee    = uint64(1050)
  1329  		blobfee    = uint64(105)
  1330  		signer     = types.LatestSigner(testChainConfig)
  1331  		statedb, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
  1332  		chain      = &testBlockChain{
  1333  			config:  testChainConfig,
  1334  			basefee: uint256.NewInt(basefee),
  1335  			blobfee: uint256.NewInt(blobfee),
  1336  			statedb: statedb,
  1337  		}
  1338  		pool = New(Config{Datadir: ""}, chain)
  1339  	)
  1340  
  1341  	if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
  1342  		b.Fatalf("failed to create blob pool: %v", err)
  1343  	}
  1344  	// Fill the pool up with one random transaction from each account with the
  1345  	// same price and everything to maximize the worst case scenario
  1346  	for i := 0; i < int(capacity); i++ {
  1347  		blobtx := makeUnsignedTx(0, 10, basefee+10, blobfee)
  1348  		blobtx.R = uint256.NewInt(1)
  1349  		blobtx.S = uint256.NewInt(uint64(100 + i))
  1350  		blobtx.V = uint256.NewInt(0)
  1351  		tx := types.NewTx(blobtx)
  1352  		addr, err := types.Sender(signer, tx)
  1353  		if err != nil {
  1354  			b.Fatal(err)
  1355  		}
  1356  		statedb.AddBalance(addr, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
  1357  		pool.add(tx)
  1358  	}
  1359  	statedb.Commit(0, true)
  1360  	defer pool.Close()
  1361  
  1362  	// Benchmark assembling the pending
  1363  	b.ResetTimer()
  1364  	b.ReportAllocs()
  1365  
  1366  	for i := 0; i < b.N; i++ {
  1367  		p := pool.Pending(txpool.PendingFilter{
  1368  			MinTip:  uint256.NewInt(1),
  1369  			BaseFee: chain.basefee,
  1370  			BlobFee: chain.blobfee,
  1371  		})
  1372  		if len(p) != int(capacity) {
  1373  			b.Fatalf("have %d want %d", len(p), capacity)
  1374  		}
  1375  	}
  1376  }