|
17 | 17 | package types
|
18 | 18 |
|
19 | 19 | import (
|
| 20 | + "container/heap" |
20 | 21 | "crypto/ecdsa"
|
21 | 22 | "errors"
|
22 | 23 | "fmt"
|
23 | 24 | "io"
|
24 | 25 | "math/big"
|
| 26 | + "sort" |
25 | 27 | "sync/atomic"
|
26 | 28 |
|
27 | 29 | "github.com/ethereum/go-ethereum/common"
|
@@ -302,27 +304,78 @@ func TxDifference(a, b Transactions) (keep Transactions) {
|
302 | 304 | return keep
|
303 | 305 | }
|
304 | 306 |
|
305 |
| -type TxByNonce struct{ Transactions } |
| 307 | +// TxByNonce implements the sort interface to allow sorting a list of transactions |
| 308 | +// by their nonces. This is usually only useful for sorting transactions from a |
| 309 | +// single account, otherwise a nonce comparison doesn't make much sense. |
| 310 | +type TxByNonce Transactions |
306 | 311 |
|
307 |
| -func (s TxByNonce) Less(i, j int) bool { |
308 |
| - return s.Transactions[i].data.AccountNonce < s.Transactions[j].data.AccountNonce |
309 |
| -} |
| 312 | +func (s TxByNonce) Len() int { return len(s) } |
| 313 | +func (s TxByNonce) Less(i, j int) bool { return s[i].data.AccountNonce < s[j].data.AccountNonce } |
| 314 | +func (s TxByNonce) Swap(i, j int) { s[i], s[j] = s[j], s[i] } |
| 315 | + |
| 316 | +// TxByPrice implements both the sort and the heap interface, making it useful |
| 317 | +// for all at once sorting as well as individually adding and removing elements. |
| 318 | +type TxByPrice Transactions |
310 | 319 |
|
311 |
| -type TxByPrice struct{ Transactions } |
| 320 | +func (s TxByPrice) Len() int { return len(s) } |
| 321 | +func (s TxByPrice) Less(i, j int) bool { return s[i].data.Price.Cmp(s[j].data.Price) > 0 } |
| 322 | +func (s TxByPrice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } |
312 | 323 |
|
313 |
| -func (s TxByPrice) Less(i, j int) bool { |
314 |
| - return s.Transactions[i].data.Price.Cmp(s.Transactions[j].data.Price) > 0 |
| 324 | +func (s *TxByPrice) Push(x interface{}) { |
| 325 | + *s = append(*s, x.(*Transaction)) |
315 | 326 | }
|
316 | 327 |
|
317 |
| -type TxByPriceAndNonce struct{ Transactions } |
| 328 | +func (s *TxByPrice) Pop() interface{} { |
| 329 | + old := *s |
| 330 | + n := len(old) |
| 331 | + x := old[n-1] |
| 332 | + *s = old[0 : n-1] |
| 333 | + return x |
| 334 | +} |
318 | 335 |
|
319 |
| -func (s TxByPriceAndNonce) Less(i, j int) bool { |
320 |
| - // we can ignore the error here. Sorting shouldn't care about validness |
321 |
| - ifrom, _ := s.Transactions[i].From() |
322 |
| - jfrom, _ := s.Transactions[j].From() |
323 |
| - // favour nonce if they are from the same recipient |
324 |
| - if ifrom == jfrom { |
325 |
| - return s.Transactions[i].data.AccountNonce < s.Transactions[j].data.AccountNonce |
| 336 | +// SortByPriceAndNonce sorts the transactions by price in such a way that the |
| 337 | +// nonce orderings within a single account are maintained. |
| 338 | +// |
| 339 | +// Note, this is not as trivial as it seems from the first look as there are three |
| 340 | +// different criteria that need to be taken into account (price, nonce, account |
| 341 | +// match), which cannot be done with any plain sorting method, as certain items |
| 342 | +// cannot be compared without context. |
| 343 | +// |
| 344 | +// This method first sorts the separates the list of transactions into individual |
| 345 | +// sender accounts and sorts them by nonce. After the account nonce ordering is |
| 346 | +// satisfied, the results are merged back together by price, always comparing only |
| 347 | +// the head transaction from each account. This is done via a heap to keep it fast. |
| 348 | +func SortByPriceAndNonce(txs []*Transaction) { |
| 349 | + // Separate the transactions by account and sort by nonce |
| 350 | + byNonce := make(map[common.Address][]*Transaction) |
| 351 | + for _, tx := range txs { |
| 352 | + acc, _ := tx.From() // we only sort valid txs so this cannot fail |
| 353 | + byNonce[acc] = append(byNonce[acc], tx) |
| 354 | + } |
| 355 | + for _, accTxs := range byNonce { |
| 356 | + sort.Sort(TxByNonce(accTxs)) |
| 357 | + } |
| 358 | + // Initialize a price based heap with the head transactions |
| 359 | + byPrice := make(TxByPrice, 0, len(byNonce)) |
| 360 | + for acc, accTxs := range byNonce { |
| 361 | + byPrice = append(byPrice, accTxs[0]) |
| 362 | + byNonce[acc] = accTxs[1:] |
| 363 | + } |
| 364 | + heap.Init(&byPrice) |
| 365 | + |
| 366 | + // Merge by replacing the best with the next from the same account |
| 367 | + txs = txs[:0] |
| 368 | + for len(byPrice) > 0 { |
| 369 | + // Retrieve the next best transaction by price |
| 370 | + best := heap.Pop(&byPrice).(*Transaction) |
| 371 | + |
| 372 | + // Push in its place the next transaction from the same account |
| 373 | + acc, _ := best.From() // we only sort valid txs so this cannot fail |
| 374 | + if accTxs, ok := byNonce[acc]; ok && len(accTxs) > 0 { |
| 375 | + heap.Push(&byPrice, accTxs[0]) |
| 376 | + byNonce[acc] = accTxs[1:] |
| 377 | + } |
| 378 | + // Accumulate the best priced transaction |
| 379 | + txs = append(txs, best) |
326 | 380 | }
|
327 |
| - return s.Transactions[i].data.Price.Cmp(s.Transactions[j].data.Price) > 0 |
328 | 381 | }
|
0 commit comments