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