Skip to content

Commit

Permalink
eth: use slices package for sorting (ethereum#27490)
Browse files Browse the repository at this point in the history
Also adds Hash.Less method for sorting purposes.

---------

Co-authored-by: Felix Lange <fjl@twurst.com>
  • Loading branch information
2 people authored and devopsbo3 committed Nov 10, 2023
1 parent 8a351f6 commit 53e07ce
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 104 deletions.
5 changes: 5 additions & 0 deletions common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) }
// If b is larger than len(h), b will be cropped from the left.
func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) }

// Less compares two hashes.
func (h Hash) Less(other Hash) bool {
return bytes.Compare(h[:], other[:]) < 0
}

// Bytes gets the byte representation of the underlying hash.
func (h Hash) Bytes() []byte { return h[:] }

Expand Down
12 changes: 3 additions & 9 deletions eth/api_debug_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"fmt"
"math/big"
"reflect"
"sort"
"testing"

"github.com/davecgh/go-spew/spew"
Expand All @@ -31,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/trie"
"golang.org/x/exp/slices"
)

var dumper = spew.ConfigState{Indent: " "}
Expand Down Expand Up @@ -58,12 +58,6 @@ func accountRangeTest(t *testing.T, trie *state.Trie, statedb *state.StateDB, st
return result
}

type resultHash []common.Hash

func (h resultHash) Len() int { return len(h) }
func (h resultHash) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h resultHash) Less(i, j int) bool { return bytes.Compare(h[i].Bytes(), h[j].Bytes()) < 0 }

func TestAccountRange(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -97,7 +91,7 @@ func TestAccountRange(t *testing.T) {
firstResult := accountRangeTest(t, &trie, state, common.Hash{}, AccountRangeMaxResults, AccountRangeMaxResults)
secondResult := accountRangeTest(t, &trie, state, common.BytesToHash(firstResult.Next), AccountRangeMaxResults, AccountRangeMaxResults)

hList := make(resultHash, 0)
hList := make([]common.Hash, 0)
for addr1 := range firstResult.Accounts {
// If address is empty, then it makes no sense to compare
// them as they might be two different accounts.
Expand All @@ -111,7 +105,7 @@ func TestAccountRange(t *testing.T) {
}
// Test to see if it's possible to recover from the middle of the previous
// set and get an even split between the first and second sets.
sort.Sort(hList)
slices.SortFunc(hList, common.Hash.Less)
middleH := hList[AccountRangeMaxResults/2]
middleResult := accountRangeTest(t, &trie, state, middleH, AccountRangeMaxResults, AccountRangeMaxResults)
missing, infirst, insecond := 0, 0, 0
Expand Down
25 changes: 8 additions & 17 deletions eth/gasprice/feehistory.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ import (
"fmt"
"math"
"math/big"
"sort"
"sync/atomic"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
"golang.org/x/exp/slices"
)

var (
Expand Down Expand Up @@ -69,20 +69,9 @@ type processedFees struct {
}

// txGasAndReward is sorted in ascending order based on reward
type (
txGasAndReward struct {
gasUsed uint64
reward *big.Int
}
sortGasAndReward []txGasAndReward
)

func (s sortGasAndReward) Len() int { return len(s) }
func (s sortGasAndReward) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s sortGasAndReward) Less(i, j int) bool {
return s[i].reward.Cmp(s[j].reward) < 0
type txGasAndReward struct {
gasUsed uint64
reward *big.Int
}

// processBlock takes a blockFees structure with the blockNumber, the header and optionally
Expand Down Expand Up @@ -117,12 +106,14 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) {
return
}

sorter := make(sortGasAndReward, len(bf.block.Transactions()))
sorter := make([]txGasAndReward, len(bf.block.Transactions()))
for i, tx := range bf.block.Transactions() {
reward, _ := tx.EffectiveGasTip(bf.block.BaseFee())
sorter[i] = txGasAndReward{gasUsed: bf.receipts[i].GasUsed, reward: reward}
}
sort.Stable(sorter)
slices.SortStableFunc(sorter, func(a, b txGasAndReward) bool {
return a.reward.Cmp(b.reward) < 0
})

var txIndex int
sumGasUsed := sorter[0].gasUsed
Expand Down
53 changes: 15 additions & 38 deletions eth/gasprice/gasprice.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package gasprice
import (
"context"
"math/big"
"sort"
"sync"

"github.com/ethereum/go-ethereum/common"
Expand All @@ -30,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"golang.org/x/exp/slices"
)

const sampleNumber = 3 // Number of transactions sampled in a block
Expand Down Expand Up @@ -208,7 +208,7 @@ func (oracle *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) {
}
price := lastPrice
if len(results) > 0 {
sort.Sort(bigIntArray(results))
slices.SortFunc(results, func(a, b *big.Int) bool { return a.Cmp(b) < 0 })
price = results[(len(results)-1)*oracle.percentile/100]
}
if price.Cmp(oracle.maxPrice) > 0 {
Expand All @@ -227,30 +227,6 @@ type results struct {
err error
}

type txSorter struct {
txs []*types.Transaction
baseFee *big.Int
}

func newSorter(txs []*types.Transaction, baseFee *big.Int) *txSorter {
return &txSorter{
txs: txs,
baseFee: baseFee,
}
}

func (s *txSorter) Len() int { return len(s.txs) }
func (s *txSorter) Swap(i, j int) {
s.txs[i], s.txs[j] = s.txs[j], s.txs[i]
}
func (s *txSorter) Less(i, j int) bool {
// It's okay to discard the error because a tx would never be
// accepted into a block with an invalid effective tip.
tip1, _ := s.txs[i].EffectiveGasTip(s.baseFee)
tip2, _ := s.txs[j].EffectiveGasTip(s.baseFee)
return tip1.Cmp(tip2) < 0
}

// getBlockValues calculates the lowest transaction gas price in a given block
// and sends it to the result channel. If the block is empty or all transactions
// are sent by the miner itself(it doesn't make any sense to include this kind of
Expand All @@ -267,14 +243,21 @@ func (oracle *Oracle) getBlockValues(ctx context.Context, blockNum uint64, limit
signer := types.MakeSigner(oracle.backend.ChainConfig(), block.Number(), block.Time())

// Sort the transaction by effective tip in ascending sort.
txs := make([]*types.Transaction, len(block.Transactions()))
copy(txs, block.Transactions())
sorter := newSorter(txs, block.BaseFee())
sort.Sort(sorter)
txs := block.Transactions()
sortedTxs := make([]*types.Transaction, len(txs))
copy(sortedTxs, txs)
baseFee := block.BaseFee()
slices.SortFunc(sortedTxs, func(a, b *types.Transaction) bool {
// It's okay to discard the error because a tx would never be
// accepted into a block with an invalid effective tip.
tip1, _ := a.EffectiveGasTip(baseFee)
tip2, _ := b.EffectiveGasTip(baseFee)
return tip1.Cmp(tip2) < 0
})

var prices []*big.Int
for _, tx := range sorter.txs {
tip, _ := tx.EffectiveGasTip(block.BaseFee())
for _, tx := range sortedTxs {
tip, _ := tx.EffectiveGasTip(baseFee)
if ignoreUnder != nil && tip.Cmp(ignoreUnder) == -1 {
continue
}
Expand All @@ -291,9 +274,3 @@ func (oracle *Oracle) getBlockValues(ctx context.Context, blockNum uint64, limit
case <-quit:
}
}

type bigIntArray []*big.Int

func (s bigIntArray) Len() int { return len(s) }
func (s bigIntArray) Less(i, j int) bool { return s[i].Cmp(s[j]) < 0 }
func (s bigIntArray) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
Loading

0 comments on commit 53e07ce

Please sign in to comment.