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