github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/core/tx_list.go (about) 1 // Copyright 2016 The Spectrum Authors 2 // This file is part of the Spectrum library. 3 // 4 // The Spectrum 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 Spectrum 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 Spectrum 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 25 "sync" 26 27 "github.com/SmartMeshFoundation/Spectrum/common" 28 "github.com/SmartMeshFoundation/Spectrum/core/types" 29 "github.com/SmartMeshFoundation/Spectrum/log" 30 ) 31 32 // nonceHeap is a heap.Interface implementation over 64bit unsigned integers for 33 // retrieving sorted transactions from the possibly gapped future queue. 34 type nonceHeap []uint64 35 36 func (h nonceHeap) Len() int { return len(h) } 37 func (h nonceHeap) Less(i, j int) bool { return h[i] < h[j] } 38 func (h nonceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } 39 40 func (h *nonceHeap) Push(x interface{}) { 41 *h = append(*h, x.(uint64)) 42 } 43 44 func (h *nonceHeap) Pop() interface{} { 45 old := *h 46 n := len(old) 47 x := old[n-1] 48 *h = old[0 : n-1] 49 return x 50 } 51 52 // txSortedMap is a nonce->transaction hash map with a heap based index to allow 53 // iterating over the contents in a nonce-incrementing way. 54 type txSortedMap struct { 55 sync.RWMutex // add by liangc 56 items map[uint64]*types.Transaction // Hash map storing the transaction data 57 index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode) 58 cache types.Transactions // Cache of the transactions already sorted 59 } 60 61 // newTxSortedMap creates a new nonce-sorted transaction map. 62 func newTxSortedMap() *txSortedMap { 63 return &txSortedMap{ 64 items: make(map[uint64]*types.Transaction), 65 index: new(nonceHeap), 66 } 67 } 68 69 // Get retrieves the current transactions associated with the given nonce. 70 func (m *txSortedMap) Get(nonce uint64) *types.Transaction { 71 m.RLock() 72 defer m.RUnlock() 73 return m.items[nonce] 74 } 75 76 // Put inserts a new transaction into the map, also updating the map's nonce 77 // index. If a transaction already exists with the same nonce, it's overwritten. 78 func (m *txSortedMap) Put(tx *types.Transaction) { 79 m.Lock() 80 defer m.Unlock() 81 nonce := tx.Nonce() 82 if m.items[nonce] == nil { 83 heap.Push(m.index, nonce) 84 } 85 m.items[nonce], m.cache = tx, nil 86 } 87 88 // Forward removes all transactions from the map with a nonce lower than the 89 // provided threshold. Every removed transaction is returned for any post-removal 90 // maintenance. 91 func (m *txSortedMap) Forward(threshold uint64) types.Transactions { 92 m.Lock() 93 defer m.Unlock() 94 var removed types.Transactions 95 96 // Pop off heap items until the threshold is reached 97 for m.index.Len() > 0 && (*m.index)[0] < threshold { 98 nonce := heap.Pop(m.index).(uint64) 99 removed = append(removed, m.items[nonce]) 100 delete(m.items, nonce) 101 } 102 // If we had a cached order, shift the front 103 if m.cache != nil { 104 m.cache = m.cache[len(removed):] 105 } 106 return removed 107 } 108 109 // Filter iterates over the list of transactions and removes all of them for which 110 // the specified function evaluates to true. 111 func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions { 112 m.Lock() 113 defer m.Unlock() 114 var removed types.Transactions 115 116 // Collect all the transactions to filter out 117 for nonce, tx := range m.items { 118 if filter(tx) { 119 removed = append(removed, tx) 120 delete(m.items, nonce) 121 } 122 } 123 // If transactions were removed, the heap and cache are ruined 124 if len(removed) > 0 { 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 131 m.cache = nil 132 } 133 return removed 134 } 135 136 // Cap places a hard limit on the number of items, returning all transactions 137 // exceeding that limit. 138 func (m *txSortedMap) Cap(threshold int) types.Transactions { 139 m.Lock() 140 defer m.Unlock() 141 // Short circuit if the number of items is under the limit 142 if len(m.items) <= threshold { 143 return nil 144 } 145 // Otherwise gather and drop the highest nonce'd transactions 146 var drops types.Transactions 147 148 sort.Sort(*m.index) 149 for size := len(m.items); size > threshold; size-- { 150 drops = append(drops, m.items[(*m.index)[size-1]]) 151 delete(m.items, (*m.index)[size-1]) 152 } 153 *m.index = (*m.index)[:threshold] 154 heap.Init(m.index) 155 156 // If we had a cache, shift the back 157 if m.cache != nil { 158 m.cache = m.cache[:len(m.cache)-len(drops)] 159 } 160 return drops 161 } 162 163 // Remove deletes a transaction from the maintained map, returning whether the 164 // transaction was found. 165 func (m *txSortedMap) Remove(nonce uint64) bool { 166 m.Lock() 167 defer m.Unlock() 168 // Short circuit if no transaction is present 169 _, ok := m.items[nonce] 170 if !ok { 171 return false 172 } 173 // Otherwise delete the transaction and fix the heap index 174 for i := 0; i < m.index.Len(); i++ { 175 if (*m.index)[i] == nonce { 176 heap.Remove(m.index, i) 177 break 178 } 179 } 180 delete(m.items, nonce) 181 m.cache = nil 182 183 return true 184 } 185 186 // Ready retrieves a sequentially increasing list of transactions starting at the 187 // provided nonce that is ready for processing. The returned transactions will be 188 // removed from the list. 189 // 190 // Note, all transactions with nonces lower than start will also be returned to 191 // prevent getting into and invalid state. This is not something that should ever 192 // happen but better to be self correcting than failing! 193 func (m *txSortedMap) Ready(start uint64) types.Transactions { 194 m.Lock() 195 defer m.Unlock() 196 // Short circuit if no transactions are available 197 if m.index.Len() == 0 || (*m.index)[0] > start { 198 return nil 199 } 200 // Otherwise start accumulating incremental transactions 201 var ready types.Transactions 202 for next := (*m.index)[0]; m.index.Len() > 0 && (*m.index)[0] == next; next++ { 203 ready = append(ready, m.items[next]) 204 delete(m.items, next) 205 heap.Pop(m.index) 206 } 207 m.cache = nil 208 209 return ready 210 } 211 212 // Len returns the length of the transaction map. 213 func (m *txSortedMap) Len() int { 214 m.RLock() 215 defer m.RUnlock() 216 return len(m.items) 217 } 218 219 // Flatten creates a nonce-sorted slice of transactions based on the loosely 220 // sorted internal representation. The result of the sorting is cached in case 221 // it's requested again before any modifications are made to the contents. 222 func (m *txSortedMap) Flatten() types.Transactions { 223 m.Lock() 224 defer m.Unlock() 225 // If the sorting was not cached yet, create and cache it 226 if m.cache == nil { 227 m.cache = make(types.Transactions, 0, len(m.items)) 228 for _, tx := range m.items { 229 m.cache = append(m.cache, tx) 230 } 231 sort.Sort(types.TxByNonce(m.cache)) 232 } 233 // Copy the cache to prevent accidental modifications 234 txs := make(types.Transactions, len(m.cache)) 235 copy(txs, m.cache) 236 return txs 237 } 238 239 // txList is a "list" of transactions belonging to an account, sorted by account 240 // nonce. The same type can be used both for storing contiguous transactions for 241 // the executable/pending queue; and for storing gapped transactions for the non- 242 // executable/future queue, with minor behavioral changes. 243 type txList struct { 244 strict bool // Whether nonces are strictly continuous or not 245 txs *txSortedMap // Heap indexed sorted hash map of the transactions 246 247 costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance) 248 gascap *big.Int // Gas limit of the highest spending transaction (reset only if exceeds block limit) 249 } 250 251 // newTxList create a new transaction list for maintaining nonce-indexable fast, 252 // gapped, sortable transaction lists. 253 func newTxList(strict bool) *txList { 254 return &txList{ 255 strict: strict, 256 txs: newTxSortedMap(), 257 costcap: new(big.Int), 258 gascap: new(big.Int), 259 } 260 } 261 262 // Overlaps returns whether the transaction specified has the same nonce as one 263 // already contained within the list. 264 func (l *txList) Overlaps(tx *types.Transaction) bool { 265 return l.txs.Get(tx.Nonce()) != nil 266 } 267 268 // Add tries to insert a new transaction into the list, returning whether the 269 // transaction was accepted, and if yes, any previous transaction it replaced. 270 // 271 // If the new transaction is accepted into the list, the lists' cost and gas 272 // thresholds are also potentially updated. 273 func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) { 274 // If there's an older better transaction, abort 275 old := l.txs.Get(tx.Nonce()) 276 if old != nil { 277 threshold := new(big.Int).Div(new(big.Int).Mul(old.GasPrice(), big.NewInt(100+int64(priceBump))), big.NewInt(100)) 278 // Have to ensure that the new gas price is higher than the old gas 279 // price as well as checking the percentage threshold to ensure that 280 // this is accurate for low (Wei-level) gas price replacements 281 if old.GasPrice().Cmp(tx.GasPrice()) >= 0 || threshold.Cmp(tx.GasPrice()) > 0 { 282 return false, nil 283 } 284 } 285 // Otherwise overwrite the old transaction with the current one 286 l.txs.Put(tx) 287 if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 { 288 l.costcap = cost 289 } 290 if gas := tx.Gas(); l.gascap.Cmp(gas) < 0 { 291 l.gascap = gas 292 } 293 return true, old 294 } 295 296 // Forward removes all transactions from the list with a nonce lower than the 297 // provided threshold. Every removed transaction is returned for any post-removal 298 // maintenance. 299 func (l *txList) Forward(threshold uint64) types.Transactions { 300 return l.txs.Forward(threshold) 301 } 302 303 // Filter removes all transactions from the list with a cost or gas limit higher 304 // than the provided thresholds. Every removed transaction is returned for any 305 // post-removal maintenance. Strict-mode invalidated transactions are also 306 // returned. 307 // 308 // This method uses the cached costcap and gascap to quickly decide if there's even 309 // a point in calculating all the costs or if the balance covers all. If the threshold 310 // is lower than the costgas cap, the caps will be reset to a new high after removing 311 // the newly invalidated transactions. 312 func (l *txList) Filter(costLimit, gasLimit *big.Int) (types.Transactions, types.Transactions) { 313 // If all transactions are below the threshold, short circuit 314 if l.costcap.Cmp(costLimit) <= 0 && l.gascap.Cmp(gasLimit) <= 0 { 315 return nil, nil 316 } 317 l.costcap = new(big.Int).Set(costLimit) // Lower the caps to the thresholds 318 l.gascap = new(big.Int).Set(gasLimit) 319 320 // Filter out all the transactions above the account's funds 321 removed := l.txs.Filter(func(tx *types.Transaction) bool { return tx.Cost().Cmp(costLimit) > 0 || tx.Gas().Cmp(gasLimit) > 0 }) 322 323 // If the list was strict, filter anything above the lowest nonce 324 var invalids types.Transactions 325 326 if l.strict && len(removed) > 0 { 327 lowest := uint64(math.MaxUint64) 328 for _, tx := range removed { 329 if nonce := tx.Nonce(); lowest > nonce { 330 lowest = nonce 331 } 332 } 333 invalids = l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > lowest }) 334 } 335 return removed, invalids 336 } 337 338 // Cap places a hard limit on the number of items, returning all transactions 339 // exceeding that limit. 340 func (l *txList) Cap(threshold int) types.Transactions { 341 return l.txs.Cap(threshold) 342 } 343 344 // Remove deletes a transaction from the maintained list, returning whether the 345 // transaction was found, and also returning any transaction invalidated due to 346 // the deletion (strict mode only). 347 func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) { 348 // Remove the transaction from the set 349 nonce := tx.Nonce() 350 if removed := l.txs.Remove(nonce); !removed { 351 return false, nil 352 } 353 // In strict mode, filter out non-executable transactions 354 if l.strict { 355 return true, l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > nonce }) 356 } 357 return true, nil 358 } 359 360 // Ready retrieves a sequentially increasing list of transactions starting at the 361 // provided nonce that is ready for processing. The returned transactions will be 362 // removed from the list. 363 // 364 // Note, all transactions with nonces lower than start will also be returned to 365 // prevent getting into and invalid state. This is not something that should ever 366 // happen but better to be self correcting than failing! 367 func (l *txList) Ready(start uint64) types.Transactions { 368 return l.txs.Ready(start) 369 } 370 371 // Len returns the length of the transaction list. 372 func (l *txList) Len() int { 373 return l.txs.Len() 374 } 375 376 // Empty returns whether the list of transactions is empty or not. 377 func (l *txList) Empty() bool { 378 return l.Len() == 0 379 } 380 381 // Flatten creates a nonce-sorted slice of transactions based on the loosely 382 // sorted internal representation. The result of the sorting is cached in case 383 // it's requested again before any modifications are made to the contents. 384 func (l *txList) Flatten() types.Transactions { 385 return l.txs.Flatten() 386 } 387 388 // priceHeap is a heap.Interface implementation over transactions for retrieving 389 // price-sorted transactions to discard when the pool fills up. 390 type priceHeap []*types.Transaction 391 392 func (h priceHeap) Len() int { return len(h) } 393 func (h priceHeap) Less(i, j int) bool { return h[i].GasPrice().Cmp(h[j].GasPrice()) < 0 } 394 func (h priceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } 395 396 func (h *priceHeap) Push(x interface{}) { 397 *h = append(*h, x.(*types.Transaction)) 398 } 399 400 func (h *priceHeap) Pop() interface{} { 401 old := *h 402 n := len(old) 403 x := old[n-1] 404 *h = old[0 : n-1] 405 return x 406 } 407 408 // txPricedList is a price-sorted heap to allow operating on transactions pool 409 // contents in a price-incrementing way. 410 type txPricedList struct { 411 all *map[common.Hash]*types.Transaction // Pointer to the map of all transactions 412 items *priceHeap // Heap of prices of all the stored transactions 413 stales int // Number of stale price points to (re-heap trigger) 414 } 415 416 // newTxPricedList creates a new price-sorted transaction heap. 417 func newTxPricedList(all *map[common.Hash]*types.Transaction) *txPricedList { 418 return &txPricedList{ 419 all: all, 420 items: new(priceHeap), 421 } 422 } 423 424 // Put inserts a new transaction into the heap. 425 func (l *txPricedList) Put(tx *types.Transaction) { 426 heap.Push(l.items, tx) 427 } 428 429 // Removed notifies the prices transaction list that an old transaction dropped 430 // from the pool. The list will just keep a counter of stale objects and update 431 // the heap if a large enough ratio of transactions go stale. 432 func (l *txPricedList) Removed() { 433 // Bump the stale counter, but exit if still too low (< 25%) 434 l.stales++ 435 if l.stales <= len(*l.items)/4 { 436 return 437 } 438 // Seems we've reached a critical number of stale transactions, reheap 439 reheap := make(priceHeap, 0, len(*l.all)) 440 441 l.stales, l.items = 0, &reheap 442 for _, tx := range *l.all { 443 *l.items = append(*l.items, tx) 444 } 445 heap.Init(l.items) 446 } 447 448 // Cap finds all the transactions below the given price threshold, drops them 449 // from the priced list and returs them for further removal from the entire pool. 450 func (l *txPricedList) Cap(threshold *big.Int, local *accountSet) types.Transactions { 451 drop := make(types.Transactions, 0, 128) // Remote underpriced transactions to drop 452 save := make(types.Transactions, 0, 64) // Local underpriced transactions to keep 453 454 for len(*l.items) > 0 { 455 // Discard stale transactions if found during cleanup 456 tx := heap.Pop(l.items).(*types.Transaction) 457 if _, ok := (*l.all)[tx.Hash()]; !ok { 458 l.stales-- 459 continue 460 } 461 // Stop the discards if we've reached the threshold 462 if tx.GasPrice().Cmp(threshold) >= 0 { 463 save = append(save, tx) 464 break 465 } 466 // Non stale transaction found, discard unless local 467 if local.containsTx(tx) { 468 save = append(save, tx) 469 } else { 470 drop = append(drop, tx) 471 } 472 } 473 for _, tx := range save { 474 heap.Push(l.items, tx) 475 } 476 return drop 477 } 478 479 // Underpriced checks whether a transaction is cheaper than (or as cheap as) the 480 // lowest priced transaction currently being tracked. 481 func (l *txPricedList) Underpriced(tx *types.Transaction, local *accountSet) bool { 482 // Local transactions cannot be underpriced 483 if local.containsTx(tx) { 484 return false 485 } 486 // Discard stale price points if found at the heap start 487 for len(*l.items) > 0 { 488 head := []*types.Transaction(*l.items)[0] 489 if _, ok := (*l.all)[head.Hash()]; !ok { 490 l.stales-- 491 heap.Pop(l.items) 492 continue 493 } 494 break 495 } 496 // Check if the transaction is underpriced or not 497 if len(*l.items) == 0 { 498 log.Error("Pricing query for empty pool") // This cannot happen, print to catch programming errors 499 return false 500 } 501 cheapest := []*types.Transaction(*l.items)[0] 502 return cheapest.GasPrice().Cmp(tx.GasPrice()) >= 0 503 } 504 505 // Discard finds a number of most underpriced transactions, removes them from the 506 // priced list and returns them for further removal from the entire pool. 507 func (l *txPricedList) Discard(count int, local *accountSet) types.Transactions { 508 drop := make(types.Transactions, 0, count) // Remote underpriced transactions to drop 509 save := make(types.Transactions, 0, 64) // Local underpriced transactions to keep 510 511 for len(*l.items) > 0 && count > 0 { 512 // Discard stale transactions if found during cleanup 513 tx := heap.Pop(l.items).(*types.Transaction) 514 if _, ok := (*l.all)[tx.Hash()]; !ok { 515 l.stales-- 516 continue 517 } 518 // Non stale transaction found, discard unless local 519 if local.containsTx(tx) { 520 save = append(save, tx) 521 } else { 522 drop = append(drop, tx) 523 count-- 524 } 525 } 526 for _, tx := range save { 527 heap.Push(l.items, tx) 528 } 529 return drop 530 }