github.com/ethereum/go-ethereum@v1.16.1/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 "container/heap" 21 "maps" 22 "math" 23 "slices" 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, len(index)), 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 heap.addrs = slices.SortedFunc(maps.Keys(index), common.Address.Cmp) 58 for i, addr := range heap.addrs { 59 heap.index[addr] = i 60 } 61 heap.reinit(basefee, blobfee, true) 62 return heap 63 } 64 65 // reinit updates the pre-calculated dynamic fee jumps in the price heap and runs 66 // the sorting algorithm from scratch on the entire heap. 67 func (h *evictHeap) reinit(basefee *uint256.Int, blobfee *uint256.Int, force bool) { 68 // If the update is mostly the same as the old, don't sort pointlessly 69 basefeeJumps := dynamicFeeJumps(basefee) 70 blobfeeJumps := dynamicFeeJumps(blobfee) 71 72 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? 73 return 74 } 75 // One or both of the dynamic fees jumped, resort the pool 76 h.basefeeJumps = basefeeJumps 77 h.blobfeeJumps = blobfeeJumps 78 79 heap.Init(h) 80 } 81 82 // Len implements sort.Interface as part of heap.Interface, returning the number 83 // of accounts in the pool which can be considered for eviction. 84 func (h *evictHeap) Len() int { 85 return len(h.addrs) 86 } 87 88 // Less implements sort.Interface as part of heap.Interface, returning which of 89 // the two requested accounts has a cheaper bottleneck. 90 func (h *evictHeap) Less(i, j int) bool { 91 txsI := h.metas[h.addrs[i]] 92 txsJ := h.metas[h.addrs[j]] 93 94 lastI := txsI[len(txsI)-1] 95 lastJ := txsJ[len(txsJ)-1] 96 97 prioI := evictionPriority(h.basefeeJumps, lastI.evictionExecFeeJumps, h.blobfeeJumps, lastI.evictionBlobFeeJumps) 98 if prioI > 0 { 99 prioI = 0 100 } 101 prioJ := evictionPriority(h.basefeeJumps, lastJ.evictionExecFeeJumps, h.blobfeeJumps, lastJ.evictionBlobFeeJumps) 102 if prioJ > 0 { 103 prioJ = 0 104 } 105 if prioI == prioJ { 106 return lastI.evictionExecTip.Lt(lastJ.evictionExecTip) 107 } 108 return prioI < prioJ 109 } 110 111 // Swap implements sort.Interface as part of heap.Interface, maintaining both the 112 // order of the accounts according to the heap, and the account->item slot mapping 113 // for replacements. 114 func (h *evictHeap) Swap(i, j int) { 115 h.index[h.addrs[i]], h.index[h.addrs[j]] = h.index[h.addrs[j]], h.index[h.addrs[i]] 116 h.addrs[i], h.addrs[j] = h.addrs[j], h.addrs[i] 117 } 118 119 // Push implements heap.Interface, appending an item to the end of the account 120 // ordering as well as the address to item slot mapping. 121 func (h *evictHeap) Push(x any) { 122 h.index[x.(common.Address)] = len(h.addrs) 123 h.addrs = append(h.addrs, x.(common.Address)) 124 } 125 126 // Pop implements heap.Interface, removing and returning the last element of the 127 // heap. 128 // 129 // Note, use `heap.Pop`, not `evictHeap.Pop`. This method is used by Go's heap, 130 // to provide the functionality, it does not embed it. 131 func (h *evictHeap) Pop() any { 132 // Remove the last element from the heap 133 size := len(h.addrs) 134 addr := h.addrs[size-1] 135 h.addrs = h.addrs[:size-1] 136 137 // Unindex the removed element and return 138 delete(h.index, addr) 139 return addr 140 }