github.com/amazechain/amc@v0.1.3/internal/txspool/txs_list.go (about) 1 // Copyright 2022 The AmazeChain Authors 2 // This file is part of the AmazeChain library. 3 // 4 // The AmazeChain 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 AmazeChain 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 AmazeChain library. If not, see <http://www.gnu.org/licenses/>. 16 17 package txspool 18 19 import ( 20 "container/heap" 21 "github.com/amazechain/amc/common/transaction" 22 "github.com/amazechain/amc/common/types" 23 "github.com/holiman/uint256" 24 "math" 25 "sort" 26 "sync" 27 "sync/atomic" 28 "time" 29 "unsafe" 30 ) 31 32 // addressByHeartbeat is an account address tagged with its last activity timestamp. 33 type addressByHeartbeat struct { 34 address types.Address 35 heartbeat time.Time 36 } 37 38 type addressesByHeartbeat []addressByHeartbeat 39 40 func (a addressesByHeartbeat) Len() int { return len(a) } 41 func (a addressesByHeartbeat) Less(i, j int) bool { return a[i].heartbeat.Before(a[j].heartbeat) } 42 func (a addressesByHeartbeat) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 43 44 // accountSet is simply a set of addresses to check for existence, and a signer 45 // capable of deriving addresses from transactions. 46 type accountSet struct { 47 accounts map[types.Address]struct{} 48 cache *[]types.Address 49 } 50 51 // newAccountSet creates a new address set with an associated signer for sender 52 // derivations. 53 func newAccountSet(addrs ...types.Address) *accountSet { 54 as := &accountSet{ 55 accounts: make(map[types.Address]struct{}), 56 } 57 for _, addr := range addrs { 58 as.add(addr) 59 } 60 return as 61 } 62 63 // contains checks if a given address is contained within the set. 64 func (as *accountSet) contains(addr types.Address) bool { 65 _, exist := as.accounts[addr] 66 return exist 67 } 68 69 func (as *accountSet) empty() bool { 70 return len(as.accounts) == 0 71 } 72 73 // containsTx checks if the sender of a given tx is within the set. If the sender 74 // cannot be derived, this method returns false. 75 func (as *accountSet) containsTx(tx *transaction.Transaction) bool { 76 return as.contains(*tx.From()) 77 } 78 79 // add inserts a new address into the set to track. 80 func (as *accountSet) add(addr types.Address) { 81 as.accounts[addr] = struct{}{} 82 as.cache = nil 83 } 84 85 // addTx adds the sender of tx into the set. 86 func (as *accountSet) addTx(tx *transaction.Transaction) { 87 as.add(*tx.From()) 88 } 89 90 // flatten returns the list of addresses within this set, also caching it for later 91 // reuse. The returned slice should not be changed! 92 func (as *accountSet) flatten() []types.Address { 93 if as.cache == nil { 94 accounts := make([]types.Address, 0, len(as.accounts)) 95 for account := range as.accounts { 96 accounts = append(accounts, account) 97 } 98 as.cache = &accounts 99 } 100 return *as.cache 101 } 102 103 // merge adds all addresses from the 'other' set into 'as'. 104 func (as *accountSet) merge(other *accountSet) { 105 for addr := range other.accounts { 106 as.accounts[addr] = struct{}{} 107 } 108 as.cache = nil 109 } 110 111 // txLookup is used internally by TxPool to track transactions while allowing 112 // lookup without mutex contention. 113 // 114 // Note, although this type is properly protected against concurrent access, it 115 // is **not** a type that should ever be mutated or even exposed outside of the 116 // transaction pool, since its internal state is tightly coupled with the pools 117 // internal mechanisms. The sole purpose of the type is to permit out-of-bound 118 // peeking into the pool in TxPool.Get without having to acquire the widely scoped 119 // TxPool.mu mutex. 120 // 121 // This lookup set combines the notion of "local transactions", which is useful 122 // to build upper-level structure. 123 type txLookup struct { 124 slots int 125 lock sync.RWMutex 126 locals map[types.Hash]*transaction.Transaction 127 remotes map[types.Hash]*transaction.Transaction 128 } 129 130 // newTxLookup returns a new txLookup structure. 131 func newTxLookup() *txLookup { 132 return &txLookup{ 133 locals: make(map[types.Hash]*transaction.Transaction), 134 remotes: make(map[types.Hash]*transaction.Transaction), 135 } 136 } 137 138 // Range calls f on each key and value present in the map. The callback passed 139 // should return the indicator whether the iteration needs to be continued. 140 // Callers need to specify which set (or both) to be iterated. 141 func (t *txLookup) Range(f func(hash types.Hash, tx *transaction.Transaction, local bool) bool, local bool, remote bool) { 142 t.lock.RLock() 143 defer t.lock.RUnlock() 144 145 if local { 146 for key, value := range t.locals { 147 if !f(key, value, true) { 148 return 149 } 150 } 151 } 152 if remote { 153 for key, value := range t.remotes { 154 if !f(key, value, false) { 155 return 156 } 157 } 158 } 159 } 160 161 // Get returns a transaction if it exists in the lookup, or nil if not found. 162 func (t *txLookup) Get(hash types.Hash) *transaction.Transaction { 163 t.lock.RLock() 164 defer t.lock.RUnlock() 165 166 if tx := t.locals[hash]; tx != nil { 167 return tx 168 } 169 return t.remotes[hash] 170 } 171 172 // GetLocal returns a transaction if it exists in the lookup, or nil if not found. 173 func (t *txLookup) GetLocal(hash types.Hash) *transaction.Transaction { 174 t.lock.RLock() 175 defer t.lock.RUnlock() 176 177 return t.locals[hash] 178 } 179 180 // GetRemote returns a transaction if it exists in the lookup, or nil if not found. 181 func (t *txLookup) GetRemote(hash types.Hash) *transaction.Transaction { 182 t.lock.RLock() 183 defer t.lock.RUnlock() 184 185 return t.remotes[hash] 186 } 187 188 // Count returns the current number of transactions in the lookup. 189 func (t *txLookup) Count() int { 190 t.lock.RLock() 191 defer t.lock.RUnlock() 192 193 return len(t.locals) + len(t.remotes) 194 } 195 196 // LocalCount returns the current number of local transactions in the lookup. 197 func (t *txLookup) LocalCount() int { 198 t.lock.RLock() 199 defer t.lock.RUnlock() 200 201 return len(t.locals) 202 } 203 204 // RemoteCount returns the current number of remote transactions in the lookup. 205 func (t *txLookup) RemoteCount() int { 206 t.lock.RLock() 207 defer t.lock.RUnlock() 208 209 return len(t.remotes) 210 } 211 212 // Slots returns the current number of slots used in the lookup. 213 func (t *txLookup) Slots() int { 214 t.lock.RLock() 215 defer t.lock.RUnlock() 216 217 return t.slots 218 } 219 220 // Add adds a transaction to the lookup. 221 func (t *txLookup) Add(tx *transaction.Transaction, local bool) { 222 t.lock.Lock() 223 defer t.lock.Unlock() 224 225 t.slots += numSlots(tx) 226 //todo slotsGauge.Update(int64(t.slots)) 227 228 hash := tx.Hash() 229 if local { 230 t.locals[hash] = tx 231 } else { 232 t.remotes[hash] = tx 233 } 234 } 235 236 // Remove removes a transaction from the lookup. 237 func (t *txLookup) Remove(hash types.Hash) { 238 t.lock.Lock() 239 defer t.lock.Unlock() 240 241 tx, ok := t.locals[hash] 242 if !ok { 243 tx, ok = t.remotes[hash] 244 } 245 if !ok { 246 //todo 247 //log.Error("No transaction found to be deleted", "hash", hash) 248 return 249 } 250 t.slots -= numSlots(tx) 251 //todo slotsGauge.Update(int64(t.slots)) 252 253 delete(t.locals, hash) 254 delete(t.remotes, hash) 255 } 256 257 // RemoteToLocals migrates the transactions belongs to the given locals to locals 258 // set. The assumption is held the locals set is thread-safe to be used. 259 func (t *txLookup) RemoteToLocals(locals *accountSet) int { 260 t.lock.Lock() 261 defer t.lock.Unlock() 262 263 var migrated int 264 for hash, tx := range t.remotes { 265 if locals.containsTx(tx) { 266 t.locals[hash] = tx 267 delete(t.remotes, hash) 268 migrated += 1 269 } 270 } 271 return migrated 272 } 273 274 // RemotesBelowTip finds all remote transactions below the given tip threshold. 275 func (t *txLookup) RemotesBelowTip(threshold uint256.Int) []*transaction.Transaction { 276 found := make([]*transaction.Transaction, 0, 128) 277 t.Range(func(hash types.Hash, tx *transaction.Transaction, local bool) bool { 278 if tx.GasPrice().Cmp(&threshold) < 0 { 279 found = append(found, tx) 280 } 281 return true 282 }, false, true) // Only iterate remotes 283 return found 284 } 285 286 // numSlots calculates the number of slots needed for a single transaction. 287 func numSlots(tx *transaction.Transaction) int { 288 //todo 289 return int((int(unsafe.Sizeof(tx)) + txSlotSize - 1) / txSlotSize) 290 } 291 292 // TxByNonce implements the sort interface to allow sorting a list of transactions by their nonces. 293 type TxByNonce []*transaction.Transaction 294 295 func (s TxByNonce) Len() int { return len(s) } 296 func (s TxByNonce) Less(i, j int) bool { return s[i].Nonce() < s[j].Nonce() } 297 func (s TxByNonce) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 298 299 // nonceHeap is a heap.Interface implementation over 64bit unsigned integers for 300 // retrieving sorted transactions from the possibly gapped future queue. 301 type nonceHeap []uint64 302 303 func (h nonceHeap) Len() int { return len(h) } 304 func (h nonceHeap) Less(i, j int) bool { return h[i] < h[j] } 305 func (h nonceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } 306 307 func (h *nonceHeap) Push(x interface{}) { 308 *h = append(*h, x.(uint64)) 309 } 310 311 func (h *nonceHeap) Pop() interface{} { 312 old := *h 313 n := len(old) 314 x := old[n-1] 315 *h = old[0 : n-1] 316 return x 317 } 318 319 // txsSortedMap 320 type txsSortedMap struct { 321 items map[uint64]*transaction.Transaction // hash map 322 index *nonceHeap // 323 cache []*transaction.Transaction // Cache 324 } 325 326 // newTxSortedMap creates a new nonce-sorted transaction map. 327 func newTxSortedMap() *txsSortedMap { 328 return &txsSortedMap{ 329 items: make(map[uint64]*transaction.Transaction), 330 index: new(nonceHeap), 331 } 332 } 333 334 // Get retrieves the current transactions associated with the given nonce. 335 func (m *txsSortedMap) Get(nonce uint64) *transaction.Transaction { 336 return m.items[nonce] 337 } 338 339 // Put 340 func (m *txsSortedMap) Put(tx *transaction.Transaction) { 341 nonce := tx.Nonce() 342 if m.items[nonce] == nil { 343 heap.Push(m.index, nonce) 344 } 345 m.items[nonce], m.cache = tx, nil 346 } 347 348 // Forward 349 func (m *txsSortedMap) Forward(threshold uint64) []*transaction.Transaction { 350 var removed []*transaction.Transaction 351 352 // Pop off heap items until the threshold is reached 353 for m.index.Len() > 0 && (*m.index)[0] < threshold { 354 nonce := heap.Pop(m.index).(uint64) 355 removed = append(removed, m.items[nonce]) 356 delete(m.items, nonce) 357 } 358 // If we had a cached order, shift the front 359 if m.cache != nil { 360 m.cache = m.cache[len(removed):] 361 } 362 return removed 363 } 364 365 // Filter 366 func (m *txsSortedMap) Filter(filter func(*transaction.Transaction) bool) []*transaction.Transaction { 367 removed := m.filter(filter) 368 // If transactions were removed, the heap and cache are ruined 369 if len(removed) > 0 { 370 m.reheap() 371 } 372 return removed 373 } 374 375 func (m *txsSortedMap) reheap() { 376 *m.index = make([]uint64, 0, len(m.items)) 377 for nonce := range m.items { 378 *m.index = append(*m.index, nonce) 379 } 380 heap.Init(m.index) 381 m.cache = nil 382 } 383 384 // filter is identical to Filter, but **does not** regenerate the heap. This method 385 // should only be used if followed immediately by a call to Filter or reheap() 386 func (m *txsSortedMap) filter(filter func(*transaction.Transaction) bool) []*transaction.Transaction { 387 var removed []*transaction.Transaction 388 389 // Collect all the transactions to filter out 390 for nonce, tx := range m.items { 391 if filter(tx) { 392 removed = append(removed, tx) 393 delete(m.items, nonce) 394 } 395 } 396 if len(removed) > 0 { 397 m.cache = nil 398 } 399 return removed 400 } 401 402 // Cap 403 func (m *txsSortedMap) Cap(threshold int) []*transaction.Transaction { 404 // Short circuit if the number of items is under the limit 405 if len(m.items) <= threshold { 406 return nil 407 } 408 // Otherwise gather and drop the highest nonce'd transactions 409 var drops []*transaction.Transaction 410 411 sort.Sort(*m.index) 412 for size := len(m.items); size > threshold; size-- { 413 drops = append(drops, m.items[(*m.index)[size-1]]) 414 delete(m.items, (*m.index)[size-1]) 415 } 416 *m.index = (*m.index)[:threshold] 417 heap.Init(m.index) 418 419 // If we had a cache, shift the back 420 if m.cache != nil { 421 m.cache = m.cache[:len(m.cache)-len(drops)] 422 } 423 return drops 424 } 425 426 // Remove 427 func (m *txsSortedMap) Remove(nonce uint64) bool { 428 // Short circuit if no transaction is present 429 _, ok := m.items[nonce] 430 if !ok { 431 return false 432 } 433 // Otherwise delete the transaction and fix the heap index 434 for i := 0; i < m.index.Len(); i++ { 435 if (*m.index)[i] == nonce { 436 heap.Remove(m.index, i) 437 break 438 } 439 } 440 delete(m.items, nonce) 441 m.cache = nil 442 443 return true 444 } 445 446 // Ready retrieves a sequentially increasing list of transactions starting at the 447 // provided nonce that is ready for processing. The returned transactions will be 448 // removed from the list. 449 // 450 // Note, all transactions with nonces lower than start will also be returned to 451 // prevent getting into and invalid state. This is not something that should ever 452 // happen but better to be self correcting than failing! 453 func (m *txsSortedMap) Ready(start uint64) []*transaction.Transaction { 454 // Short circuit if no transactions are available 455 if m.index.Len() == 0 || (*m.index)[0] > start { 456 return nil 457 } 458 // Otherwise start accumulating incremental transactions 459 var ready []*transaction.Transaction 460 for next := (*m.index)[0]; m.index.Len() > 0 && (*m.index)[0] == next; next++ { 461 ready = append(ready, m.items[next]) 462 delete(m.items, next) 463 heap.Pop(m.index) 464 } 465 m.cache = nil 466 467 return ready 468 } 469 470 // Len returns the length of the transaction map. 471 func (m *txsSortedMap) Len() int { 472 return len(m.items) 473 } 474 475 func (m *txsSortedMap) flatten() []*transaction.Transaction { 476 // If the sorting was not cached yet, create and cache it 477 if m.cache == nil { 478 m.cache = make([]*transaction.Transaction, 0, len(m.items)) 479 for _, tx := range m.items { 480 m.cache = append(m.cache, tx) 481 } 482 sort.Sort(TxByNonce(m.cache)) 483 } 484 return m.cache 485 } 486 487 // Flatten creates a nonce-sorted slice of transactions based on the loosely 488 // sorted internal representation. The result of the sorting is cached in case 489 // it's requested again before any modifications are made to the contents. 490 func (m *txsSortedMap) Flatten() []*transaction.Transaction { 491 // Copy the cache to prevent accidental modifications 492 cache := m.flatten() 493 txs := make([]*transaction.Transaction, len(cache)) 494 copy(txs, cache) 495 return txs 496 } 497 498 // LastElement returns the last element of a flattened list, thus, the 499 // transaction with the highest nonce 500 func (m *txsSortedMap) LastElement() *transaction.Transaction { 501 cache := m.flatten() 502 return cache[len(cache)-1] 503 } 504 505 // txsList is a "list" of transactions belonging to an account, sorted by account 506 // nonce. The same type can be used both for storing contiguous transactions for 507 // the executable/pending queue; and for storing gapped transactions for the non- 508 // executable/future queue, with minor behavioral changes. 509 type txsList struct { 510 strict bool 511 txs *txsSortedMap 512 costcap uint256.Int 513 gascap uint64 514 } 515 516 // newtxsList create a new transaction list for maintaining nonce-indexable fast, 517 // gapped, sortable transaction lists. 518 func newTxsList(strict bool) *txsList { 519 return &txsList{ 520 strict: strict, 521 txs: newTxSortedMap(), 522 costcap: *uint256.NewInt(0), 523 } 524 } 525 526 // Overlaps returns whether the transaction specified has the same nonce as one 527 // already contained within the list. 528 func (l *txsList) Overlaps(tx *transaction.Transaction) bool { 529 return l.txs.Get(tx.Nonce()) != nil 530 } 531 532 // Add tries to insert a new transaction into the list, returning whether the 533 // transaction was accepted, and if yes, any previous transaction it replaced. 534 // 535 // If the new transaction is accepted into the list, the lists' cost and gas 536 // thresholds are also potentially updated. 537 func (l *txsList) Add(tx *transaction.Transaction, priceBump uint64) (bool, *transaction.Transaction) { 538 // If there's an older better transaction, abort 539 540 old := l.txs.Get(tx.Nonce()) 541 if old != nil { 542 if old.GasFeeCapCmp(tx) >= 1 || old.GasTipCapCmp(tx) >= 1 { 543 return false, nil 544 } 545 // thresholdFeeCap = oldFC * (100 + priceBump) / 100 546 a := uint256.NewInt(100 + priceBump) 547 aFeeCap := new(uint256.Int).Mul(a, old.GasFeeCap()) 548 aTip := a.Mul(a, old.GasTipCap()) 549 550 // thresholdTip = oldTip * (100 + priceBump) / 100 551 b := uint256.NewInt(100) 552 thresholdFeeCap := aFeeCap.Div(aFeeCap, b) 553 thresholdTip := aTip.Div(aTip, b) 554 555 // We have to ensure that both the new fee cap and tip are higher than the 556 // old ones as well as checking the percentage threshold to ensure that 557 // this is accurate for low (Wei-level) gas price replacements. 558 if tx.GasFeeCapIntCmp(thresholdFeeCap) < 0 || tx.GasTipCapIntCmp(thresholdTip) < 0 { 559 return false, nil 560 } 561 } 562 // Otherwise overwrite the old transaction with the current one 563 l.txs.Put(tx) 564 565 cost := uint256.NewInt(0).Add(new(uint256.Int).Mul(tx.GasPrice(), uint256.NewInt(tx.Gas())), tx.Value()) 566 567 if l.costcap.Cmp(cost) < 0 { 568 l.costcap = *cost 569 } 570 if gas := tx.Gas(); l.gascap < gas { 571 l.gascap = gas 572 } 573 return true, old 574 } 575 576 // Forward removes all transactions from the list with a nonce lower than the 577 // provided threshold. Every removed transaction is returned for any post-removal 578 // maintenance. 579 func (l *txsList) Forward(threshold uint64) []*transaction.Transaction { 580 return l.txs.Forward(threshold) 581 } 582 583 // Filter removes all transactions from the list with a cost or gas limit higher 584 // than the provided thresholds. Every removed transaction is returned for any 585 // post-removal maintenance. Strict-mode invalidated transactions are also 586 // returned. 587 // 588 // This method uses the cached costcap and gascap to quickly decide if there's even 589 // a point in calculating all the costs or if the balance covers all. If the threshold 590 // is lower than the costgas cap, the caps will be reset to a new high after removing 591 // the newly invalidated transactions. 592 func (l *txsList) Filter(costLimit uint256.Int, gasLimit uint64) ([]*transaction.Transaction, []*transaction.Transaction) { 593 // If all transactions are below the threshold, short circuit 594 if l.costcap.Cmp(&costLimit) <= 0 && l.gascap <= gasLimit { 595 return nil, nil 596 } 597 l.costcap = costLimit // Lower the caps to the thresholds 598 l.gascap = gasLimit 599 600 // Filter out all the transactions above the account's funds 601 removed := l.txs.Filter(func(tx *transaction.Transaction) bool { 602 cost := uint256.NewInt(0).Add(tx.Value(), uint256.NewInt(0).Mul(tx.GasPrice(), uint256.NewInt(tx.Gas()))) 603 return tx.Gas() > gasLimit || cost.Cmp(&costLimit) > 0 604 }) 605 606 if len(removed) == 0 { 607 return nil, nil 608 } 609 var invalids []*transaction.Transaction 610 // If the list was strict, filter anything above the lowest nonce 611 if l.strict { 612 lowest := uint64(math.MaxUint64) 613 for _, tx := range removed { 614 if nonce := tx.Nonce(); lowest > nonce { 615 lowest = nonce 616 } 617 } 618 invalids = l.txs.filter(func(tx *transaction.Transaction) bool { return tx.Nonce() > lowest }) 619 } 620 l.txs.reheap() 621 return removed, invalids 622 } 623 624 // Cap places a hard limit on the number of items, returning all transactions 625 // exceeding that limit. 626 func (l *txsList) Cap(threshold int) []*transaction.Transaction { 627 return l.txs.Cap(threshold) 628 } 629 630 // Remove deletes a transaction from the maintained list, returning whether the 631 // transaction was found, and also returning any transaction invalidated due to 632 // the deletion (strict mode only). 633 func (l *txsList) Remove(tx *transaction.Transaction) (bool, []*transaction.Transaction) { 634 // Remove the transaction from the set 635 nonce := tx.Nonce() 636 if removed := l.txs.Remove(nonce); !removed { 637 return false, nil 638 } 639 // In strict mode, filter out non-executable transactions 640 if l.strict { 641 return true, l.txs.Filter(func(tx *transaction.Transaction) bool { return tx.Nonce() > nonce }) 642 } 643 return true, nil 644 } 645 646 // Ready retrieves a sequentially increasing list of transactions starting at the 647 // provided nonce that is ready for processing. The returned transactions will be 648 // removed from the list. 649 // 650 // Note, all transactions with nonces lower than start will also be returned to 651 // prevent getting into and invalid state. This is not something that should ever 652 // happen but better to be self correcting than failing! 653 func (l *txsList) Ready(start uint64) []*transaction.Transaction { 654 return l.txs.Ready(start) 655 } 656 657 // Len returns the length of the transaction list. 658 func (l *txsList) Len() int { 659 return l.txs.Len() 660 } 661 662 // Empty returns whether the list of transactions is empty or not. 663 func (l *txsList) Empty() bool { 664 return l.Len() == 0 665 } 666 667 // Flatten creates a nonce-sorted slice of transactions based on the loosely 668 // sorted internal representation. The result of the sorting is cached in case 669 // it's requested again before any modifications are made to the contents. 670 func (l *txsList) Flatten() []*transaction.Transaction { 671 return l.txs.Flatten() 672 } 673 674 // LastElement returns the last element of a flattened list, thus, the 675 // transaction with the highest nonce 676 func (l *txsList) LastElement() *transaction.Transaction { 677 return l.txs.LastElement() 678 } 679 680 // priceHeap is a heap.Interface implementation over transactions for retrieving 681 // price-sorted transactions to discard when the pool fills up. If baseFee is set 682 // then the heap is sorted based on the effective tip based on the given base fee. 683 // If baseFee is nil then the sorting is based on gasFeeCap. 684 type priceHeap struct { 685 baseFee *uint256.Int // heap should always be re-sorted after baseFee is changed 686 list []*transaction.Transaction 687 } 688 689 func (h *priceHeap) Len() int { return len(h.list) } 690 func (h *priceHeap) Swap(i, j int) { h.list[i], h.list[j] = h.list[j], h.list[i] } 691 692 func (h *priceHeap) Less(i, j int) bool { 693 switch h.cmp(h.list[i], h.list[j]) { 694 case -1: 695 return true 696 case 1: 697 return false 698 default: 699 return h.list[i].Nonce() > h.list[j].Nonce() 700 } 701 } 702 703 func (h *priceHeap) cmp(a, b *transaction.Transaction) int { 704 if h.baseFee != nil { 705 // Compare effective tips if baseFee is specified 706 if c := a.EffectiveGasTipCmp(b, h.baseFee); c != 0 { 707 return c 708 } 709 } 710 // Compare fee caps if baseFee is not specified or effective tips are equal 711 if c := a.GasPrice().Cmp(b.GasPrice()); c != 0 { 712 return c 713 } 714 // Compare tips if effective tips and fee caps are equal 715 return a.GasPrice().Cmp(b.GasPrice()) 716 } 717 718 func (h *priceHeap) Push(x interface{}) { 719 tx := x.(*transaction.Transaction) 720 h.list = append(h.list, tx) 721 } 722 723 func (h *priceHeap) Pop() interface{} { 724 old := h.list 725 n := len(old) 726 x := old[n-1] 727 old[n-1] = nil 728 h.list = old[0 : n-1] 729 return x 730 } 731 732 // txPricedList is a price-sorted heap to allow operating on transactions pool 733 // contents in a price-incrementing way. It's built opon the all transactions 734 // in txpool but only interested in the remote part. It means only remote transactions 735 // will be considered for tracking, sorting, eviction, etc. 736 // 737 // Two heaps are used for sorting: the urgent heap (based on effective tip in the next 738 // block) and the floating heap (based on gasFeeCap). Always the bigger heap is chosen for 739 // eviction. Transactions evicted from the urgent heap are first demoted into the floating heap. 740 // In some cases (during a congestion, when blocks are full) the urgent heap can provide 741 // better candidates for inclusion while in other cases (at the top of the baseFee peak) 742 // the floating heap is better. When baseFee is decreasing they behave similarly. 743 type txPricedList struct { 744 // Number of stale price points to (re-heap trigger). 745 // This field is accessed atomically, and must be the first field 746 // to ensure it has correct alignment for atomic.AddInt64. 747 // See https://golang.org/pkg/sync/atomic/#pkg-note-BUG. 748 stales int64 749 750 all *txLookup // Pointer to the map of all transactions 751 urgent, floating priceHeap // Heaps of prices of all the stored **remote** transactions 752 reheapMu sync.Mutex // Mutex asserts that only one routine is reheaping the list 753 } 754 755 const ( 756 // urgentRatio : floatingRatio is the capacity ratio of the two queues 757 urgentRatio = 4 758 floatingRatio = 1 759 ) 760 761 // newTxPricedList creates a new price-sorted transaction heap. 762 func newTxPricedList(all *txLookup) *txPricedList { 763 return &txPricedList{ 764 all: all, 765 } 766 } 767 768 // Put inserts a new transaction into the heap. 769 func (l *txPricedList) Put(tx *transaction.Transaction, local bool) { 770 if local { 771 return 772 } 773 // Insert every new transaction to the urgent heap first; Discard will balance the heaps 774 heap.Push(&l.urgent, tx) 775 } 776 777 // Removed notifies the prices transaction list that an old transaction dropped 778 // from the pool. The list will just keep a counter of stale objects and update 779 // the heap if a large enough ratio of transactions go stale. 780 func (l *txPricedList) Removed(count int) { 781 // Bump the stale counter, but exit if still too low (< 25%) 782 stales := atomic.AddInt64(&l.stales, int64(count)) 783 if int(stales) <= (len(l.urgent.list)+len(l.floating.list))/4 { 784 return 785 } 786 // Seems we've reached a critical number of stale transactions, reheap 787 l.Reheap() 788 } 789 790 // Underpriced checks whether a transaction is cheaper than (or as cheap as) the 791 // lowest priced (remote) transaction currently being tracked. 792 func (l *txPricedList) Underpriced(tx *transaction.Transaction) bool { 793 // Note: with two queues, being underpriced is defined as being worse than the worst item 794 // in all non-empty queues if there is any. If both queues are empty then nothing is underpriced. 795 return (l.underpricedFor(&l.urgent, tx) || len(l.urgent.list) == 0) && 796 (l.underpricedFor(&l.floating, tx) || len(l.floating.list) == 0) && 797 (len(l.urgent.list) != 0 || len(l.floating.list) != 0) 798 } 799 800 // underpricedFor checks whether a transaction is cheaper than (or as cheap as) the 801 // lowest priced (remote) transaction in the given heap. 802 func (l *txPricedList) underpricedFor(h *priceHeap, tx *transaction.Transaction) bool { 803 // Discard stale price points if found at the heap start 804 for len(h.list) > 0 { 805 hash := h.list[0].Hash() 806 if l.all.GetRemote(hash) == nil { // Removed or migrated 807 atomic.AddInt64(&l.stales, -1) 808 heap.Pop(h) 809 continue 810 } 811 break 812 } 813 // Check if the transaction is underpriced or not 814 if len(h.list) == 0 { 815 return false // There is no remote transaction at all. 816 } 817 // If the remote transaction is even cheaper than the 818 // cheapest one tracked locally, reject it. 819 return h.cmp(h.list[0], tx) >= 0 820 } 821 822 // Discard finds a number of most underpriced transactions, removes them from the 823 // priced list and returns them for further removal from the entire pool. 824 // 825 // Note local transaction won't be considered for eviction. 826 func (l *txPricedList) Discard(slots int, force bool) ([]*transaction.Transaction, bool) { 827 drop := make([]*transaction.Transaction, 0, slots) // Remote underpriced transactions to drop 828 for slots > 0 { 829 if len(l.urgent.list)*floatingRatio > len(l.floating.list)*urgentRatio || floatingRatio == 0 { 830 // Discard stale transactions if found during cleanup 831 tx := heap.Pop(&l.urgent).(*transaction.Transaction) 832 hash := tx.Hash() 833 if l.all.GetRemote(hash) == nil { // Removed or migrated 834 atomic.AddInt64(&l.stales, -1) 835 continue 836 } 837 // Non stale transaction found, move to floating heap 838 heap.Push(&l.floating, tx) 839 } else { 840 if len(l.floating.list) == 0 { 841 // Stop if both heaps are empty 842 break 843 } 844 // Discard stale transactions if found during cleanup 845 tx := heap.Pop(&l.floating).(*transaction.Transaction) 846 hash := tx.Hash() 847 if l.all.GetRemote(hash) == nil { // Removed or migrated 848 atomic.AddInt64(&l.stales, -1) 849 continue 850 } 851 // Non stale transaction found, discard it 852 drop = append(drop, tx) 853 slots -= numSlots(tx) 854 } 855 } 856 // If we still can't make enough room for the new transaction 857 if slots > 0 && !force { 858 for _, tx := range drop { 859 heap.Push(&l.urgent, tx) 860 } 861 return nil, false 862 } 863 return drop, true 864 } 865 866 // Reheap forcibly rebuilds the heap based on the current remote transaction set. 867 func (l *txPricedList) Reheap() { 868 l.reheapMu.Lock() 869 defer l.reheapMu.Unlock() 870 //start := time.Now() 871 atomic.StoreInt64(&l.stales, 0) 872 l.urgent.list = make([]*transaction.Transaction, 0, l.all.RemoteCount()) 873 l.all.Range(func(hash types.Hash, tx *transaction.Transaction, local bool) bool { 874 l.urgent.list = append(l.urgent.list, tx) 875 return true 876 }, false, true) // Only iterate remotes 877 heap.Init(&l.urgent) 878 879 // balance out the two heaps by moving the worse half of transactions into the 880 // floating heap 881 // Note: Discard would also do this before the first eviction but Reheap can do 882 // is more efficiently. Also, Underpriced would work suboptimally the first time 883 // if the floating queue was empty. 884 floatingCount := len(l.urgent.list) * floatingRatio / (urgentRatio + floatingRatio) 885 l.floating.list = make([]*transaction.Transaction, floatingCount) 886 for i := 0; i < floatingCount; i++ { 887 l.floating.list[i] = heap.Pop(&l.urgent).(*transaction.Transaction) 888 } 889 heap.Init(&l.floating) 890 891 //todo reheapTimer.Update(time.Since(start)) 892 } 893 894 // SetBaseFee updates the base fee and triggers a re-heap. Note that Removed is not 895 // necessary to call right before SetBaseFee when processing a new block. 896 func (l *txPricedList) SetBaseFee(baseFee *uint256.Int) { 897 l.urgent.baseFee = baseFee 898 l.Reheap() 899 }