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 }