github.com/MetalBlockchain/subnet-evm@v0.4.9/core/tx_list.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2016 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package core 28 29 import ( 30 "container/heap" 31 "math" 32 "math/big" 33 "sort" 34 "sync" 35 "sync/atomic" 36 "time" 37 38 "github.com/MetalBlockchain/subnet-evm/core/types" 39 "github.com/ethereum/go-ethereum/common" 40 ) 41 42 // nonceHeap is a heap.Interface implementation over 64bit unsigned integers for 43 // retrieving sorted transactions from the possibly gapped future queue. 44 type nonceHeap []uint64 45 46 func (h nonceHeap) Len() int { return len(h) } 47 func (h nonceHeap) Less(i, j int) bool { return h[i] < h[j] } 48 func (h nonceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } 49 50 func (h *nonceHeap) Push(x interface{}) { 51 *h = append(*h, x.(uint64)) 52 } 53 54 func (h *nonceHeap) Pop() interface{} { 55 old := *h 56 n := len(old) 57 x := old[n-1] 58 *h = old[0 : n-1] 59 return x 60 } 61 62 // txSortedMap is a nonce->transaction hash map with a heap based index to allow 63 // iterating over the contents in a nonce-incrementing way. 64 type txSortedMap struct { 65 items map[uint64]*types.Transaction // Hash map storing the transaction data 66 index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode) 67 cache types.Transactions // Cache of the transactions already sorted 68 } 69 70 // newTxSortedMap creates a new nonce-sorted transaction map. 71 func newTxSortedMap() *txSortedMap { 72 return &txSortedMap{ 73 items: make(map[uint64]*types.Transaction), 74 index: new(nonceHeap), 75 } 76 } 77 78 // Get retrieves the current transactions associated with the given nonce. 79 func (m *txSortedMap) Get(nonce uint64) *types.Transaction { 80 return m.items[nonce] 81 } 82 83 // Put inserts a new transaction into the map, also updating the map's nonce 84 // index. If a transaction already exists with the same nonce, it's overwritten. 85 func (m *txSortedMap) Put(tx *types.Transaction) { 86 nonce := tx.Nonce() 87 if m.items[nonce] == nil { 88 heap.Push(m.index, nonce) 89 } 90 m.items[nonce], m.cache = tx, nil 91 } 92 93 // Forward removes all transactions from the map with a nonce lower than the 94 // provided threshold. Every removed transaction is returned for any post-removal 95 // maintenance. 96 func (m *txSortedMap) Forward(threshold uint64) types.Transactions { 97 var removed types.Transactions 98 99 // Pop off heap items until the threshold is reached 100 for m.index.Len() > 0 && (*m.index)[0] < threshold { 101 nonce := heap.Pop(m.index).(uint64) 102 removed = append(removed, m.items[nonce]) 103 delete(m.items, nonce) 104 } 105 // If we had a cached order, shift the front 106 if m.cache != nil { 107 m.cache = m.cache[len(removed):] 108 } 109 return removed 110 } 111 112 // Filter iterates over the list of transactions and removes all of them for which 113 // the specified function evaluates to true. 114 // Filter, as opposed to 'filter', re-initialises the heap after the operation is done. 115 // If you want to do several consecutive filterings, it's therefore better to first 116 // do a .filter(func1) followed by .Filter(func2) or reheap() 117 func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions { 118 removed := m.filter(filter) 119 // If transactions were removed, the heap and cache are ruined 120 if len(removed) > 0 { 121 m.reheap() 122 } 123 return removed 124 } 125 126 func (m *txSortedMap) reheap() { 127 *m.index = make([]uint64, 0, len(m.items)) 128 for nonce := range m.items { 129 *m.index = append(*m.index, nonce) 130 } 131 heap.Init(m.index) 132 m.cache = nil 133 } 134 135 // filter is identical to Filter, but **does not** regenerate the heap. This method 136 // should only be used if followed immediately by a call to Filter or reheap() 137 func (m *txSortedMap) filter(filter func(*types.Transaction) bool) types.Transactions { 138 var removed types.Transactions 139 140 // Collect all the transactions to filter out 141 for nonce, tx := range m.items { 142 if filter(tx) { 143 removed = append(removed, tx) 144 delete(m.items, nonce) 145 } 146 } 147 if len(removed) > 0 { 148 m.cache = nil 149 } 150 return removed 151 } 152 153 // Cap places a hard limit on the number of items, returning all transactions 154 // exceeding that limit. 155 func (m *txSortedMap) Cap(threshold int) types.Transactions { 156 // Short circuit if the number of items is under the limit 157 if len(m.items) <= threshold { 158 return nil 159 } 160 // Otherwise gather and drop the highest nonce'd transactions 161 var drops types.Transactions 162 163 sort.Sort(*m.index) 164 for size := len(m.items); size > threshold; size-- { 165 drops = append(drops, m.items[(*m.index)[size-1]]) 166 delete(m.items, (*m.index)[size-1]) 167 } 168 *m.index = (*m.index)[:threshold] 169 heap.Init(m.index) 170 171 // If we had a cache, shift the back 172 if m.cache != nil { 173 m.cache = m.cache[:len(m.cache)-len(drops)] 174 } 175 return drops 176 } 177 178 // Remove deletes a transaction from the maintained map, returning whether the 179 // transaction was found. 180 func (m *txSortedMap) Remove(nonce uint64) bool { 181 // Short circuit if no transaction is present 182 _, ok := m.items[nonce] 183 if !ok { 184 return false 185 } 186 // Otherwise delete the transaction and fix the heap index 187 for i := 0; i < m.index.Len(); i++ { 188 if (*m.index)[i] == nonce { 189 heap.Remove(m.index, i) 190 break 191 } 192 } 193 delete(m.items, nonce) 194 m.cache = nil 195 196 return true 197 } 198 199 // Ready retrieves a sequentially increasing list of transactions starting at the 200 // provided nonce that is ready for processing. The returned transactions will be 201 // removed from the list. 202 // 203 // Note, all transactions with nonces lower than start will also be returned to 204 // prevent getting into and invalid state. This is not something that should ever 205 // happen but better to be self correcting than failing! 206 func (m *txSortedMap) Ready(start uint64) types.Transactions { 207 // Short circuit if no transactions are available 208 if m.index.Len() == 0 || (*m.index)[0] > start { 209 return nil 210 } 211 // Otherwise start accumulating incremental transactions 212 var ready types.Transactions 213 for next := (*m.index)[0]; m.index.Len() > 0 && (*m.index)[0] == next; next++ { 214 ready = append(ready, m.items[next]) 215 delete(m.items, next) 216 heap.Pop(m.index) 217 } 218 m.cache = nil 219 220 return ready 221 } 222 223 // Len returns the length of the transaction map. 224 func (m *txSortedMap) Len() int { 225 return len(m.items) 226 } 227 228 func (m *txSortedMap) flatten() types.Transactions { 229 // If the sorting was not cached yet, create and cache it 230 if m.cache == nil { 231 m.cache = make(types.Transactions, 0, len(m.items)) 232 for _, tx := range m.items { 233 m.cache = append(m.cache, tx) 234 } 235 sort.Sort(types.TxByNonce(m.cache)) 236 } 237 return m.cache 238 } 239 240 // Flatten creates a nonce-sorted slice of transactions based on the loosely 241 // sorted internal representation. The result of the sorting is cached in case 242 // it's requested again before any modifications are made to the contents. 243 func (m *txSortedMap) Flatten() types.Transactions { 244 // Copy the cache to prevent accidental modifications 245 cache := m.flatten() 246 txs := make(types.Transactions, len(cache)) 247 copy(txs, cache) 248 return txs 249 } 250 251 // LastElement returns the last element of a flattened list, thus, the 252 // transaction with the highest nonce 253 func (m *txSortedMap) LastElement() *types.Transaction { 254 cache := m.flatten() 255 return cache[len(cache)-1] 256 } 257 258 // txList is a "list" of transactions belonging to an account, sorted by account 259 // nonce. The same type can be used both for storing contiguous transactions for 260 // the executable/pending queue; and for storing gapped transactions for the non- 261 // executable/future queue, with minor behavioral changes. 262 type txList struct { 263 strict bool // Whether nonces are strictly continuous or not 264 txs *txSortedMap // Heap indexed sorted hash map of the transactions 265 266 costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance) 267 gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit) 268 } 269 270 // newTxList create a new transaction list for maintaining nonce-indexable fast, 271 // gapped, sortable transaction lists. 272 func newTxList(strict bool) *txList { 273 return &txList{ 274 strict: strict, 275 txs: newTxSortedMap(), 276 costcap: new(big.Int), 277 } 278 } 279 280 // Overlaps returns whether the transaction specified has the same nonce as one 281 // already contained within the list. 282 func (l *txList) Overlaps(tx *types.Transaction) bool { 283 return l.txs.Get(tx.Nonce()) != nil 284 } 285 286 // Add tries to insert a new transaction into the list, returning whether the 287 // transaction was accepted, and if yes, any previous transaction it replaced. 288 // 289 // If the new transaction is accepted into the list, the lists' cost and gas 290 // thresholds are also potentially updated. 291 func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) { 292 // If there's an older better transaction, abort 293 old := l.txs.Get(tx.Nonce()) 294 if old != nil { 295 if old.GasFeeCapCmp(tx) >= 0 || old.GasTipCapCmp(tx) >= 0 { 296 return false, nil 297 } 298 // thresholdFeeCap = oldFC * (100 + priceBump) / 100 299 a := big.NewInt(100 + int64(priceBump)) 300 aFeeCap := new(big.Int).Mul(a, old.GasFeeCap()) 301 aTip := a.Mul(a, old.GasTipCap()) 302 303 // thresholdTip = oldTip * (100 + priceBump) / 100 304 b := big.NewInt(100) 305 thresholdFeeCap := aFeeCap.Div(aFeeCap, b) 306 thresholdTip := aTip.Div(aTip, b) 307 308 // We have to ensure that both the new fee cap and tip are higher than the 309 // old ones as well as checking the percentage threshold to ensure that 310 // this is accurate for low (Wei-level) gas price replacements. 311 if tx.GasFeeCapIntCmp(thresholdFeeCap) < 0 || tx.GasTipCapIntCmp(thresholdTip) < 0 { 312 return false, nil 313 } 314 } 315 // Otherwise overwrite the old transaction with the current one 316 l.txs.Put(tx) 317 if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 { 318 l.costcap = cost 319 } 320 if gas := tx.Gas(); l.gascap < gas { 321 l.gascap = gas 322 } 323 return true, old 324 } 325 326 // Forward removes all transactions from the list with a nonce lower than the 327 // provided threshold. Every removed transaction is returned for any post-removal 328 // maintenance. 329 func (l *txList) Forward(threshold uint64) types.Transactions { 330 return l.txs.Forward(threshold) 331 } 332 333 // Filter removes all transactions from the list with a cost or gas limit higher 334 // than the provided thresholds. Every removed transaction is returned for any 335 // post-removal maintenance. Strict-mode invalidated transactions are also 336 // returned. 337 // 338 // This method uses the cached costcap and gascap to quickly decide if there's even 339 // a point in calculating all the costs or if the balance covers all. If the threshold 340 // is lower than the costgas cap, the caps will be reset to a new high after removing 341 // the newly invalidated transactions. 342 func (l *txList) Filter(costLimit *big.Int, gasLimit uint64) (types.Transactions, types.Transactions) { 343 // If all transactions are below the threshold, short circuit 344 if l.costcap.Cmp(costLimit) <= 0 && l.gascap <= gasLimit { 345 return nil, nil 346 } 347 l.costcap = new(big.Int).Set(costLimit) // Lower the caps to the thresholds 348 l.gascap = gasLimit 349 350 // Filter out all the transactions above the account's funds 351 removed := l.txs.Filter(func(tx *types.Transaction) bool { 352 return tx.Gas() > gasLimit || tx.Cost().Cmp(costLimit) > 0 353 }) 354 355 if len(removed) == 0 { 356 return nil, nil 357 } 358 var invalids types.Transactions 359 // If the list was strict, filter anything above the lowest nonce 360 if l.strict { 361 lowest := uint64(math.MaxUint64) 362 for _, tx := range removed { 363 if nonce := tx.Nonce(); lowest > nonce { 364 lowest = nonce 365 } 366 } 367 invalids = l.txs.filter(func(tx *types.Transaction) bool { return tx.Nonce() > lowest }) 368 } 369 l.txs.reheap() 370 return removed, invalids 371 } 372 373 // Cap places a hard limit on the number of items, returning all transactions 374 // exceeding that limit. 375 func (l *txList) Cap(threshold int) types.Transactions { 376 return l.txs.Cap(threshold) 377 } 378 379 // Remove deletes a transaction from the maintained list, returning whether the 380 // transaction was found, and also returning any transaction invalidated due to 381 // the deletion (strict mode only). 382 func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) { 383 // Remove the transaction from the set 384 nonce := tx.Nonce() 385 if removed := l.txs.Remove(nonce); !removed { 386 return false, nil 387 } 388 // In strict mode, filter out non-executable transactions 389 if l.strict { 390 return true, l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > nonce }) 391 } 392 return true, nil 393 } 394 395 // Ready retrieves a sequentially increasing list of transactions starting at the 396 // provided nonce that is ready for processing. The returned transactions will be 397 // removed from the list. 398 // 399 // Note, all transactions with nonces lower than start will also be returned to 400 // prevent getting into and invalid state. This is not something that should ever 401 // happen but better to be self correcting than failing! 402 func (l *txList) Ready(start uint64) types.Transactions { 403 return l.txs.Ready(start) 404 } 405 406 // Len returns the length of the transaction list. 407 func (l *txList) Len() int { 408 return l.txs.Len() 409 } 410 411 // Empty returns whether the list of transactions is empty or not. 412 func (l *txList) Empty() bool { 413 return l.Len() == 0 414 } 415 416 // Flatten creates a nonce-sorted slice of transactions based on the loosely 417 // sorted internal representation. The result of the sorting is cached in case 418 // it's requested again before any modifications are made to the contents. 419 func (l *txList) Flatten() types.Transactions { 420 return l.txs.Flatten() 421 } 422 423 // LastElement returns the last element of a flattened list, thus, the 424 // transaction with the highest nonce 425 func (l *txList) LastElement() *types.Transaction { 426 return l.txs.LastElement() 427 } 428 429 // priceHeap is a heap.Interface implementation over transactions for retrieving 430 // price-sorted transactions to discard when the pool fills up. If baseFee is set 431 // then the heap is sorted based on the effective tip based on the given base fee. 432 // If baseFee is nil then the sorting is based on gasFeeCap. 433 type priceHeap struct { 434 baseFee *big.Int // heap should always be re-sorted after baseFee is changed 435 list []*types.Transaction 436 } 437 438 func (h *priceHeap) Len() int { return len(h.list) } 439 func (h *priceHeap) Swap(i, j int) { h.list[i], h.list[j] = h.list[j], h.list[i] } 440 441 func (h *priceHeap) Less(i, j int) bool { 442 switch h.cmp(h.list[i], h.list[j]) { 443 case -1: 444 return true 445 case 1: 446 return false 447 default: 448 return h.list[i].Nonce() > h.list[j].Nonce() 449 } 450 } 451 452 func (h *priceHeap) cmp(a, b *types.Transaction) int { 453 if h.baseFee != nil { 454 // Compare effective tips if baseFee is specified 455 if c := a.EffectiveGasTipCmp(b, h.baseFee); c != 0 { 456 return c 457 } 458 } 459 // Compare fee caps if baseFee is not specified or effective tips are equal 460 if c := a.GasFeeCapCmp(b); c != 0 { 461 return c 462 } 463 // Compare tips if effective tips and fee caps are equal 464 return a.GasTipCapCmp(b) 465 } 466 467 func (h *priceHeap) Push(x interface{}) { 468 tx := x.(*types.Transaction) 469 h.list = append(h.list, tx) 470 } 471 472 func (h *priceHeap) Pop() interface{} { 473 old := h.list 474 n := len(old) 475 x := old[n-1] 476 old[n-1] = nil 477 h.list = old[0 : n-1] 478 return x 479 } 480 481 // txPricedList is a price-sorted heap to allow operating on transactions pool 482 // contents in a price-incrementing way. It's built opon the all transactions 483 // in txpool but only interested in the remote part. It means only remote transactions 484 // will be considered for tracking, sorting, eviction, etc. 485 // 486 // Two heaps are used for sorting: the urgent heap (based on effective tip in the next 487 // block) and the floating heap (based on gasFeeCap). Always the bigger heap is chosen for 488 // eviction. Transactions evicted from the urgent heap are first demoted into the floating heap. 489 // In some cases (during a congestion, when blocks are full) the urgent heap can provide 490 // better candidates for inclusion while in other cases (at the top of the baseFee peak) 491 // the floating heap is better. When baseFee is decreasing they behave similarly. 492 type txPricedList struct { 493 // Number of stale price points to (re-heap trigger). 494 // This field is accessed atomically, and must be the first field 495 // to ensure it has correct alignment for atomic.AddInt64. 496 // See https://golang.org/pkg/sync/atomic/#pkg-note-BUG. 497 stales int64 498 499 all *txLookup // Pointer to the map of all transactions 500 urgent, floating priceHeap // Heaps of prices of all the stored **remote** transactions 501 reheapMu sync.Mutex // Mutex asserts that only one routine is reheaping the list 502 } 503 504 const ( 505 // urgentRatio : floatingRatio is the capacity ratio of the two queues 506 urgentRatio = 4 507 floatingRatio = 1 508 ) 509 510 // newTxPricedList creates a new price-sorted transaction heap. 511 func newTxPricedList(all *txLookup) *txPricedList { 512 return &txPricedList{ 513 all: all, 514 } 515 } 516 517 // Put inserts a new transaction into the heap. 518 func (l *txPricedList) Put(tx *types.Transaction, local bool) { 519 if local { 520 return 521 } 522 // Insert every new transaction to the urgent heap first; Discard will balance the heaps 523 heap.Push(&l.urgent, tx) 524 } 525 526 // Removed notifies the prices transaction list that an old transaction dropped 527 // from the pool. The list will just keep a counter of stale objects and update 528 // the heap if a large enough ratio of transactions go stale. 529 func (l *txPricedList) Removed(count int) { 530 // Bump the stale counter, but exit if still too low (< 25%) 531 stales := atomic.AddInt64(&l.stales, int64(count)) 532 if int(stales) <= (len(l.urgent.list)+len(l.floating.list))/4 { 533 return 534 } 535 // Seems we've reached a critical number of stale transactions, reheap 536 l.Reheap() 537 } 538 539 // Underpriced checks whether a transaction is cheaper than (or as cheap as) the 540 // lowest priced (remote) transaction currently being tracked. 541 func (l *txPricedList) Underpriced(tx *types.Transaction) bool { 542 // Note: with two queues, being underpriced is defined as being worse than the worst item 543 // in all non-empty queues if there is any. If both queues are empty then nothing is underpriced. 544 return (l.underpricedFor(&l.urgent, tx) || len(l.urgent.list) == 0) && 545 (l.underpricedFor(&l.floating, tx) || len(l.floating.list) == 0) && 546 (len(l.urgent.list) != 0 || len(l.floating.list) != 0) 547 } 548 549 // underpricedFor checks whether a transaction is cheaper than (or as cheap as) the 550 // lowest priced (remote) transaction in the given heap. 551 func (l *txPricedList) underpricedFor(h *priceHeap, tx *types.Transaction) bool { 552 // Discard stale price points if found at the heap start 553 for len(h.list) > 0 { 554 head := h.list[0] 555 if l.all.GetRemote(head.Hash()) == nil { // Removed or migrated 556 atomic.AddInt64(&l.stales, -1) 557 heap.Pop(h) 558 continue 559 } 560 break 561 } 562 // Check if the transaction is underpriced or not 563 if len(h.list) == 0 { 564 return false // There is no remote transaction at all. 565 } 566 // If the remote transaction is even cheaper than the 567 // cheapest one tracked locally, reject it. 568 return h.cmp(h.list[0], tx) >= 0 569 } 570 571 // Discard finds a number of most underpriced transactions, removes them from the 572 // priced list and returns them for further removal from the entire pool. 573 // 574 // Note local transaction won't be considered for eviction. 575 func (l *txPricedList) Discard(slots int, force bool) (types.Transactions, bool) { 576 drop := make(types.Transactions, 0, slots) // Remote underpriced transactions to drop 577 for slots > 0 { 578 if len(l.urgent.list)*floatingRatio > len(l.floating.list)*urgentRatio || floatingRatio == 0 { 579 // Discard stale transactions if found during cleanup 580 tx := heap.Pop(&l.urgent).(*types.Transaction) 581 if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated 582 atomic.AddInt64(&l.stales, -1) 583 continue 584 } 585 // Non stale transaction found, move to floating heap 586 heap.Push(&l.floating, tx) 587 } else { 588 if len(l.floating.list) == 0 { 589 // Stop if both heaps are empty 590 break 591 } 592 // Discard stale transactions if found during cleanup 593 tx := heap.Pop(&l.floating).(*types.Transaction) 594 if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated 595 atomic.AddInt64(&l.stales, -1) 596 continue 597 } 598 // Non stale transaction found, discard it 599 drop = append(drop, tx) 600 slots -= numSlots(tx) 601 } 602 } 603 // If we still can't make enough room for the new transaction 604 if slots > 0 && !force { 605 for _, tx := range drop { 606 heap.Push(&l.urgent, tx) 607 } 608 return nil, false 609 } 610 return drop, true 611 } 612 613 // Reheap forcibly rebuilds the heap based on the current remote transaction set. 614 func (l *txPricedList) Reheap() { 615 l.reheapMu.Lock() 616 defer l.reheapMu.Unlock() 617 start := time.Now() 618 atomic.StoreInt64(&l.stales, 0) 619 l.urgent.list = make([]*types.Transaction, 0, l.all.RemoteCount()) 620 l.all.Range(func(hash common.Hash, tx *types.Transaction, local bool) bool { 621 l.urgent.list = append(l.urgent.list, tx) 622 return true 623 }, false, true) // Only iterate remotes 624 heap.Init(&l.urgent) 625 626 // balance out the two heaps by moving the worse half of transactions into the 627 // floating heap 628 // Note: Discard would also do this before the first eviction but Reheap can do 629 // is more efficiently. Also, Underpriced would work suboptimally the first time 630 // if the floating queue was empty. 631 floatingCount := len(l.urgent.list) * floatingRatio / (urgentRatio + floatingRatio) 632 l.floating.list = make([]*types.Transaction, floatingCount) 633 for i := 0; i < floatingCount; i++ { 634 l.floating.list[i] = heap.Pop(&l.urgent).(*types.Transaction) 635 } 636 heap.Init(&l.floating) 637 reheapTimer.Update(time.Since(start)) 638 } 639 640 // SetBaseFee updates the base fee and triggers a re-heap. Note that Removed is not 641 // necessary to call right before SetBaseFee when processing a new block. 642 func (l *txPricedList) SetBaseFee(baseFee *big.Int) { 643 l.urgent.baseFee = baseFee 644 l.Reheap() 645 }