github.com/ethereum/go-ethereum@v1.14.4-0.20240516095835-473ee8fc07a3/core/txpool/blobpool/evictheap.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  	"container/heap"
    22  	"math"
    23  	"sort"
    24  
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/holiman/uint256"
    27  )
    28  
    29  // evictHeap is a helper data structure to keep track of the cheapest bottleneck
    30  // transaction from each account to determine which account to evict from.
    31  //
    32  // The heap internally tracks a slice of cheapest transactions from each account
    33  // and a mapping from addresses to indices for direct removals/updates.
    34  //
    35  // The goal of the heap is to decide which account has the worst bottleneck to
    36  // evict transactions from.
    37  type evictHeap struct {
    38  	metas *map[common.Address][]*blobTxMeta // Pointer to the blob pool's index for price retrievals
    39  
    40  	basefeeJumps float64 // Pre-calculated absolute dynamic fee jumps for the base fee
    41  	blobfeeJumps float64 // Pre-calculated absolute dynamic fee jumps for the blob fee
    42  
    43  	addrs []common.Address       // Heap of addresses to retrieve the cheapest out of
    44  	index map[common.Address]int // Indices into the heap for replacements
    45  }
    46  
    47  // newPriceHeap creates a new heap of cheapest accounts in the blob pool to evict
    48  // from in case of over saturation.
    49  func newPriceHeap(basefee *uint256.Int, blobfee *uint256.Int, index *map[common.Address][]*blobTxMeta) *evictHeap {
    50  	heap := &evictHeap{
    51  		metas: index,
    52  		index: make(map[common.Address]int),
    53  	}
    54  	// Populate the heap in account sort order. Not really needed in practice,
    55  	// but it makes the heap initialization deterministic and less annoying to
    56  	// test in unit tests.
    57  	addrs := make([]common.Address, 0, len(*index))
    58  	for addr := range *index {
    59  		addrs = append(addrs, addr)
    60  	}
    61  	sort.Slice(addrs, func(i, j int) bool { return bytes.Compare(addrs[i][:], addrs[j][:]) < 0 })
    62  
    63  	for _, addr := range addrs {
    64  		heap.index[addr] = len(heap.addrs)
    65  		heap.addrs = append(heap.addrs, addr)
    66  	}
    67  	heap.reinit(basefee, blobfee, true)
    68  	return heap
    69  }
    70  
    71  // reinit updates the pre-calculated dynamic fee jumps in the price heap and runs
    72  // the sorting algorithm from scratch on the entire heap.
    73  func (h *evictHeap) reinit(basefee *uint256.Int, blobfee *uint256.Int, force bool) {
    74  	// If the update is mostly the same as the old, don't sort pointlessly
    75  	basefeeJumps := dynamicFeeJumps(basefee)
    76  	blobfeeJumps := dynamicFeeJumps(blobfee)
    77  
    78  	if !force && math.Abs(h.basefeeJumps-basefeeJumps) < 0.01 && math.Abs(h.blobfeeJumps-blobfeeJumps) < 0.01 { // TODO(karalabe): 0.01 enough, maybe should be smaller? Maybe this optimization is moot?
    79  		return
    80  	}
    81  	// One or both of the dynamic fees jumped, resort the pool
    82  	h.basefeeJumps = basefeeJumps
    83  	h.blobfeeJumps = blobfeeJumps
    84  
    85  	heap.Init(h)
    86  }
    87  
    88  // Len implements sort.Interface as part of heap.Interface, returning the number
    89  // of accounts in the pool which can be considered for eviction.
    90  func (h *evictHeap) Len() int {
    91  	return len(h.addrs)
    92  }
    93  
    94  // Less implements sort.Interface as part of heap.Interface, returning which of
    95  // the two requested accounts has a cheaper bottleneck.
    96  func (h *evictHeap) Less(i, j int) bool {
    97  	txsI := (*(h.metas))[h.addrs[i]]
    98  	txsJ := (*(h.metas))[h.addrs[j]]
    99  
   100  	lastI := txsI[len(txsI)-1]
   101  	lastJ := txsJ[len(txsJ)-1]
   102  
   103  	prioI := evictionPriority(h.basefeeJumps, lastI.evictionExecFeeJumps, h.blobfeeJumps, lastI.evictionBlobFeeJumps)
   104  	if prioI > 0 {
   105  		prioI = 0
   106  	}
   107  	prioJ := evictionPriority(h.basefeeJumps, lastJ.evictionExecFeeJumps, h.blobfeeJumps, lastJ.evictionBlobFeeJumps)
   108  	if prioJ > 0 {
   109  		prioJ = 0
   110  	}
   111  	if prioI == prioJ {
   112  		return lastI.evictionExecTip.Lt(lastJ.evictionExecTip)
   113  	}
   114  	return prioI < prioJ
   115  }
   116  
   117  // Swap implements sort.Interface as part of heap.Interface, maintaining both the
   118  // order of the accounts according to the heap, and the account->item slot mapping
   119  // for replacements.
   120  func (h *evictHeap) Swap(i, j int) {
   121  	h.index[h.addrs[i]], h.index[h.addrs[j]] = h.index[h.addrs[j]], h.index[h.addrs[i]]
   122  	h.addrs[i], h.addrs[j] = h.addrs[j], h.addrs[i]
   123  }
   124  
   125  // Push implements heap.Interface, appending an item to the end of the account
   126  // ordering as well as the address to item slot mapping.
   127  func (h *evictHeap) Push(x any) {
   128  	h.index[x.(common.Address)] = len(h.addrs)
   129  	h.addrs = append(h.addrs, x.(common.Address))
   130  }
   131  
   132  // Pop implements heap.Interface, removing and returning the last element of the
   133  // heap.
   134  //
   135  // Note, use `heap.Pop`, not `evictHeap.Pop`. This method is used by Go's heap,
   136  // to provide the functionality, it does not embed it.
   137  func (h *evictHeap) Pop() any {
   138  	// Remove the last element from the heap
   139  	size := len(h.addrs)
   140  	addr := h.addrs[size-1]
   141  	h.addrs = h.addrs[:size-1]
   142  
   143  	// Unindex the removed element and return
   144  	delete(h.index, addr)
   145  	return addr
   146  }