diff --git a/accounts/keystore/account_cache.go b/accounts/keystore/account_cache.go index 12f92d261951..7d3c0efbdd34 100644 --- a/accounts/keystore/account_cache.go +++ b/accounts/keystore/account_cache.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" + "golang.org/x/exp/slices" ) // Minimum amount of time between cache reloads. This limit applies if the platform does @@ -38,11 +39,9 @@ import ( // exist yet, the code will attempt to create a watcher at most this often. const minReloadInterval = 2 * time.Second -type accountsByURL []accounts.Account - -func (s accountsByURL) Len() int { return len(s) } -func (s accountsByURL) Less(i, j int) bool { return s[i].URL.Cmp(s[j].URL) < 0 } -func (s accountsByURL) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func accountsByURLLess(a, b accounts.Account) bool { + return a.URL.Cmp(b.URL) < 0 +} // AmbiguousAddrError is returned when attempting to unlock // an address for which more than one file exists. @@ -67,7 +66,7 @@ type accountCache struct { keydir string watcher *watcher mu sync.Mutex - all accountsByURL + all []accounts.Account byAddr map[common.Address][]accounts.Account throttle *time.Timer notify chan struct{} @@ -194,7 +193,7 @@ func (ac *accountCache) find(a accounts.Account) (accounts.Account, error) { default: err := &AmbiguousAddrError{Addr: a.Address, Matches: make([]accounts.Account, len(matches))} copy(err.Matches, matches) - sort.Sort(accountsByURL(err.Matches)) + slices.SortFunc(err.Matches, accountsByURLLess) return accounts.Account{}, err } } diff --git a/accounts/keystore/account_cache_test.go b/accounts/keystore/account_cache_test.go index 7605d0fbf013..1c3effa7ae89 100644 --- a/accounts/keystore/account_cache_test.go +++ b/accounts/keystore/account_cache_test.go @@ -23,7 +23,6 @@ import ( "os" "path/filepath" "reflect" - "sort" "testing" "time" @@ -31,6 +30,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" + "golang.org/x/exp/slices" ) var ( @@ -203,7 +203,7 @@ func TestCacheAddDeleteOrder(t *testing.T) { // Check that the account list is sorted by filename. wantAccounts := make([]accounts.Account, len(accs)) copy(wantAccounts, accs) - sort.Sort(accountsByURL(wantAccounts)) + slices.SortFunc(wantAccounts, accountsByURLLess) list := cache.accounts() if !reflect.DeepEqual(list, wantAccounts) { t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accs), spew.Sdump(wantAccounts)) diff --git a/accounts/keystore/keystore_test.go b/accounts/keystore/keystore_test.go index f90d809b55f4..a0d872789d58 100644 --- a/accounts/keystore/keystore_test.go +++ b/accounts/keystore/keystore_test.go @@ -20,7 +20,6 @@ import ( "math/rand" "os" "runtime" - "sort" "strings" "sync" "sync/atomic" @@ -31,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/event" + "golang.org/x/exp/slices" ) var testSigData = make([]byte, 32) @@ -424,7 +424,7 @@ func checkAccounts(t *testing.T, live map[common.Address]accounts.Account, walle for _, account := range live { liveList = append(liveList, account) } - sort.Sort(accountsByURL(liveList)) + slices.SortFunc(liveList, accountsByURLLess) for j, wallet := range wallets { if accs := wallet.Accounts(); len(accs) != 1 { t.Errorf("wallet %d: contains invalid number of accounts: have %d, want 1", j, len(accs)) diff --git a/build/update-license.go b/build/update-license.go index f61536470a19..f7a0d33486e3 100644 --- a/build/update-license.go +++ b/build/update-license.go @@ -46,12 +46,13 @@ import ( "path/filepath" "regexp" "runtime" - "sort" "strconv" "strings" "sync" "text/template" "time" + + "golang.org/x/exp/slices" ) var ( @@ -152,13 +153,6 @@ func (i info) gpl() bool { return false } -// authors implements the sort.Interface for strings in case-insensitive mode. -type authors []string - -func (as authors) Len() int { return len(as) } -func (as authors) Less(i, j int) bool { return strings.ToLower(as[i]) < strings.ToLower(as[j]) } -func (as authors) Swap(i, j int) { as[i], as[j] = as[j], as[i] } - func main() { var ( files = getFiles() @@ -299,7 +293,7 @@ func writeAuthors(files []string) { } } // Write sorted list of authors back to the file. - sort.Sort(authors(list)) + slices.Sort(list) content := new(bytes.Buffer) content.WriteString(authorsFileHeader) for _, a := range list { diff --git a/cmd/devp2p/dns_route53.go b/cmd/devp2p/dns_route53.go index 6c97fa937ee8..eae6f70f5827 100644 --- a/cmd/devp2p/dns_route53.go +++ b/cmd/devp2p/dns_route53.go @@ -20,7 +20,6 @@ import ( "context" "errors" "fmt" - "sort" "strconv" "strings" "time" @@ -33,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p/dnsdisc" "github.com/urfave/cli/v2" + "golang.org/x/exp/slices" ) const ( @@ -288,11 +288,11 @@ func makeDeletionChanges(records map[string]recordSet, keep map[string]string) [ // sortChanges ensures DNS changes are in leaf-added -> root-changed -> leaf-deleted order. func sortChanges(changes []types.Change) { score := map[string]int{"CREATE": 1, "UPSERT": 2, "DELETE": 3} - sort.Slice(changes, func(i, j int) bool { - if changes[i].Action == changes[j].Action { - return *changes[i].ResourceRecordSet.Name < *changes[j].ResourceRecordSet.Name + slices.SortFunc(changes, func(a, b types.Change) bool { + if a.Action == b.Action { + return *a.ResourceRecordSet.Name < *b.ResourceRecordSet.Name } - return score[string(changes[i].Action)] < score[string(changes[j].Action)] + return score[string(a.Action)] < score[string(b.Action)] }) } diff --git a/cmd/devp2p/nodeset.go b/cmd/devp2p/nodeset.go index 33c39f4b9e31..c040c1a908ba 100644 --- a/cmd/devp2p/nodeset.go +++ b/cmd/devp2p/nodeset.go @@ -21,11 +21,11 @@ import ( "encoding/json" "fmt" "os" - "sort" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/p2p/enode" + "golang.org/x/exp/slices" ) const jsonIndent = " " @@ -77,8 +77,8 @@ func (ns nodeSet) nodes() []*enode.Node { result = append(result, n.N) } // Sort by ID. - sort.Slice(result, func(i, j int) bool { - return bytes.Compare(result[i].ID().Bytes(), result[j].ID().Bytes()) < 0 + slices.SortFunc(result, func(a, b *enode.Node) bool { + return bytes.Compare(a.ID().Bytes(), b.ID().Bytes()) < 0 }) return result } @@ -103,8 +103,8 @@ func (ns nodeSet) topN(n int) nodeSet { for _, v := range ns { byscore = append(byscore, v) } - sort.Slice(byscore, func(i, j int) bool { - return byscore[i].Score >= byscore[j].Score + slices.SortFunc(byscore, func(a, b nodeJSON) bool { + return a.Score >= b.Score }) result := make(nodeSet, n) for _, v := range byscore[:n] { diff --git a/cmd/devp2p/nodesetcmd.go b/cmd/devp2p/nodesetcmd.go index f1dd220d5708..0dc6bf5fe708 100644 --- a/cmd/devp2p/nodesetcmd.go +++ b/cmd/devp2p/nodesetcmd.go @@ -20,7 +20,6 @@ import ( "errors" "fmt" "net" - "sort" "strconv" "strings" "time" @@ -30,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/urfave/cli/v2" + "golang.org/x/exp/slices" ) var ( @@ -89,7 +89,7 @@ func showAttributeCounts(ns nodeSet) { maxlength = len(key) } } - sort.Strings(keys) + slices.Sort(keys) fmt.Println("ENR attribute counts:") for _, key := range keys { fmt.Printf("%s%s: %d\n", strings.Repeat(" ", maxlength-len(key)+1), key, attrcount[key]) diff --git a/consensus/clique/snapshot.go b/consensus/clique/snapshot.go index e5efa5108fd6..0533340e2c29 100644 --- a/consensus/clique/snapshot.go +++ b/consensus/clique/snapshot.go @@ -19,7 +19,6 @@ package clique import ( "bytes" "encoding/json" - "sort" "time" "github.com/ethereum/go-ethereum/common" @@ -29,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" + "golang.org/x/exp/slices" ) // Vote represents a single vote that an authorized signer made to modify the @@ -62,13 +62,6 @@ type Snapshot struct { Tally map[common.Address]Tally `json:"tally"` // Current vote tally to avoid recalculating } -// signersAscending implements the sort interface to allow sorting a list of addresses -type signersAscending []common.Address - -func (s signersAscending) Len() int { return len(s) } -func (s signersAscending) Less(i, j int) bool { return bytes.Compare(s[i][:], s[j][:]) < 0 } -func (s signersAscending) Swap(i, j int) { s[i], s[j] = s[j], s[i] } - // newSnapshot creates a new snapshot with the specified startup parameters. This // method does not initialize the set of recent signers, so only ever use if for // the genesis block. @@ -315,7 +308,9 @@ func (s *Snapshot) signers() []common.Address { for sig := range s.Signers { sigs = append(sigs, sig) } - sort.Sort(signersAscending(sigs)) + slices.SortFunc(sigs, func(a, b common.Address) bool { + return bytes.Compare(a[:], b[:]) < 0 + }) return sigs } diff --git a/consensus/clique/snapshot_test.go b/consensus/clique/snapshot_test.go index 66e667276ff8..b3e63154d964 100644 --- a/consensus/clique/snapshot_test.go +++ b/consensus/clique/snapshot_test.go @@ -21,7 +21,6 @@ import ( "crypto/ecdsa" "fmt" "math/big" - "sort" "testing" "github.com/ethereum/go-ethereum/common" @@ -31,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" + "golang.org/x/exp/slices" ) // testerAccountPool is a pool to maintain currently active tester accounts, @@ -53,7 +53,9 @@ func (ap *testerAccountPool) checkpoint(header *types.Header, signers []string) for i, signer := range signers { auths[i] = ap.address(signer) } - sort.Sort(signersAscending(auths)) + slices.SortFunc(auths, func(a, b common.Address) bool { + return bytes.Compare(a[:], b[:]) < 0 + }) for i, auth := range auths { copy(header.Extra[extraVanity+i*common.AddressLength:], auth.Bytes()) } diff --git a/console/console.go b/console/console.go index cdee53684ecf..1ef8971e5858 100644 --- a/console/console.go +++ b/console/console.go @@ -24,7 +24,6 @@ import ( "os/signal" "path/filepath" "regexp" - "sort" "strings" "sync" "syscall" @@ -38,6 +37,7 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/mattn/go-colorable" "github.com/peterh/liner" + "golang.org/x/exp/slices" ) var ( @@ -342,7 +342,7 @@ func (c *Console) Welcome() { for api, version := range apis { modules = append(modules, fmt.Sprintf("%s:%s", api, version)) } - sort.Strings(modules) + slices.Sort(modules) message += " modules: " + strings.Join(modules, " ") + "\n" } message += "\nTo exit, press ctrl-d or type exit" diff --git a/core/blockchain.go b/core/blockchain.go index c9b80bc2fffb..03a66195bea0 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -23,7 +23,6 @@ import ( "io" "math/big" "runtime" - "sort" "strings" "sync" "sync/atomic" @@ -48,6 +47,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" + "golang.org/x/exp/slices" ) var ( @@ -1015,8 +1015,8 @@ func (bc *BlockChain) procFutureBlocks() { } } if len(blocks) > 0 { - sort.Slice(blocks, func(i, j int) bool { - return blocks[i].NumberU64() < blocks[j].NumberU64() + slices.SortFunc(blocks, func(a, b *types.Block) bool { + return a.NumberU64() < b.NumberU64() }) // Insert one by one as chain insertion needs contiguous ancestry between blocks for i := range blocks { diff --git a/core/forkid/forkid.go b/core/forkid/forkid.go index f536019dac12..8964558845d3 100644 --- a/core/forkid/forkid.go +++ b/core/forkid/forkid.go @@ -24,13 +24,13 @@ import ( "math" "math/big" "reflect" - "sort" "strings" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" + "golang.org/x/exp/slices" ) var ( @@ -270,8 +270,8 @@ func gatherForks(config *params.ChainConfig) ([]uint64, []uint64) { } } } - sort.Slice(forksByBlock, func(i, j int) bool { return forksByBlock[i] < forksByBlock[j] }) - sort.Slice(forksByTime, func(i, j int) bool { return forksByTime[i] < forksByTime[j] }) + slices.Sort(forksByBlock) + slices.Sort(forksByTime) // Deduplicate fork identifiers applying multiple forks for i := 1; i < len(forksByBlock); i++ { diff --git a/core/mkalloc.go b/core/mkalloc.go index e4c2ec0d83e9..277b00e3577c 100644 --- a/core/mkalloc.go +++ b/core/mkalloc.go @@ -39,14 +39,8 @@ import ( type allocItem struct{ Addr, Balance *big.Int } -type allocList []allocItem - -func (a allocList) Len() int { return len(a) } -func (a allocList) Less(i, j int) bool { return a[i].Addr.Cmp(a[j].Addr) < 0 } -func (a allocList) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -func makelist(g *core.Genesis) allocList { - a := make(allocList, 0, len(g.Alloc)) +func makelist(g *core.Genesis) []allocItem { + a := make([]allocItem, 0, len(g.Alloc)) for addr, account := range g.Alloc { if len(account.Storage) > 0 || len(account.Code) > 0 || account.Nonce != 0 { panic(fmt.Sprintf("can't encode account %x", addr)) @@ -54,7 +48,9 @@ func makelist(g *core.Genesis) allocList { bigAddr := new(big.Int).SetBytes(addr.Bytes()) a = append(a, allocItem{bigAddr, account.Balance}) } - sort.Sort(a) + slices.SortFunc(a, func(b, c allocItem) bool { + return b.Addr.Cmp(c.Addr) < 0 + }) return a } diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index b479655b0b74..dd418abd4070 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -22,7 +22,6 @@ import ( "errors" "fmt" "math/big" - "sort" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -31,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" + "golang.org/x/exp/slices" ) // ReadCanonicalHash retrieves the hash assigned to a canonical block number. @@ -836,15 +836,9 @@ type badBlock struct { Body *types.Body } -// badBlockList implements the sort interface to allow sorting a list of -// bad blocks by their number in the reverse order. -type badBlockList []*badBlock - -func (s badBlockList) Len() int { return len(s) } -func (s badBlockList) Less(i, j int) bool { - return s[i].Header.Number.Uint64() < s[j].Header.Number.Uint64() +func (b *badBlock) Less(other *badBlock) bool { + return b.Header.Number.Uint64() < other.Header.Number.Uint64() } -func (s badBlockList) Swap(i, j int) { s[i], s[j] = s[j], s[i] } // ReadBadBlock retrieves the bad block with the corresponding block hash. func ReadBadBlock(db ethdb.Reader, hash common.Hash) *types.Block { @@ -852,7 +846,7 @@ func ReadBadBlock(db ethdb.Reader, hash common.Hash) *types.Block { if err != nil { return nil } - var badBlocks badBlockList + var badBlocks []*badBlock if err := rlp.DecodeBytes(blob, &badBlocks); err != nil { return nil } @@ -871,7 +865,7 @@ func ReadAllBadBlocks(db ethdb.Reader) []*types.Block { if err != nil { return nil } - var badBlocks badBlockList + var badBlocks []*badBlock if err := rlp.DecodeBytes(blob, &badBlocks); err != nil { return nil } @@ -889,7 +883,7 @@ func WriteBadBlock(db ethdb.KeyValueStore, block *types.Block) { if err != nil { log.Warn("Failed to load old bad blocks", "error", err) } - var badBlocks badBlockList + var badBlocks []*badBlock if len(blob) > 0 { if err := rlp.DecodeBytes(blob, &badBlocks); err != nil { log.Crit("Failed to decode old bad blocks", "error", err) @@ -905,7 +899,9 @@ func WriteBadBlock(db ethdb.KeyValueStore, block *types.Block) { Header: block.Header(), Body: block.Body(), }) - sort.Sort(sort.Reverse(badBlocks)) + slices.SortFunc(badBlocks, func(a, b *badBlock) bool { + return !a.Less(b) // Sort reverse order + }) if len(badBlocks) > badBlockToKeep { badBlocks = badBlocks[:badBlockToKeep] } diff --git a/core/rawdb/chain_iterator_test.go b/core/rawdb/chain_iterator_test.go index e1f5159753f0..fd405e9d6997 100644 --- a/core/rawdb/chain_iterator_test.go +++ b/core/rawdb/chain_iterator_test.go @@ -19,12 +19,12 @@ package rawdb import ( "math/big" "reflect" - "sort" "sync" "testing" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "golang.org/x/exp/slices" ) func TestChainIterator(t *testing.T) { @@ -92,9 +92,11 @@ func TestChainIterator(t *testing.T) { } } if !c.reverse { - sort.Ints(numbers) + slices.Sort(numbers) } else { - sort.Sort(sort.Reverse(sort.IntSlice(numbers))) + slices.SortFunc(numbers, func(a, b int) bool { + return a > b // Sort descending + }) } if !reflect.DeepEqual(numbers, c.expect) { t.Fatalf("Case %d failed, visit element mismatch, want %v, got %v", i, c.expect, numbers) diff --git a/core/state/snapshot/difflayer.go b/core/state/snapshot/difflayer.go index 8869a8471672..293c85115067 100644 --- a/core/state/snapshot/difflayer.go +++ b/core/state/snapshot/difflayer.go @@ -21,7 +21,6 @@ import ( "fmt" "math" "math/rand" - "sort" "sync" "sync/atomic" "time" @@ -30,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rlp" bloomfilter "github.com/holiman/bloomfilter/v2" + "golang.org/x/exp/slices" ) var ( @@ -525,7 +525,7 @@ func (dl *diffLayer) AccountList() []common.Hash { dl.accountList = append(dl.accountList, hash) } } - sort.Sort(hashes(dl.accountList)) + slices.SortFunc(dl.accountList, hashesLess) dl.memory += uint64(len(dl.accountList) * common.HashLength) return dl.accountList } @@ -563,7 +563,7 @@ func (dl *diffLayer) StorageList(accountHash common.Hash) ([]common.Hash, bool) for k := range storageMap { storageList = append(storageList, k) } - sort.Sort(hashes(storageList)) + slices.SortFunc(storageList, hashesLess) dl.storageList[accountHash] = storageList dl.memory += uint64(len(dl.storageList)*common.HashLength + common.HashLength) return storageList, destructed diff --git a/core/state/snapshot/iterator_fast.go b/core/state/snapshot/iterator_fast.go index 1a042c7cd3c0..339f930ffd81 100644 --- a/core/state/snapshot/iterator_fast.go +++ b/core/state/snapshot/iterator_fast.go @@ -22,6 +22,7 @@ import ( "sort" "github.com/ethereum/go-ethereum/common" + "golang.org/x/exp/slices" ) // weightedIterator is a iterator with an assigned weight. It is used to prioritise @@ -32,18 +33,10 @@ type weightedIterator struct { priority int } -// weightedIterators is a set of iterators implementing the sort.Interface. -type weightedIterators []*weightedIterator - -// Len implements sort.Interface, returning the number of active iterators. -func (its weightedIterators) Len() int { return len(its) } - -// Less implements sort.Interface, returning which of two iterators in the stack -// is before the other. -func (its weightedIterators) Less(i, j int) bool { +func (it *weightedIterator) Less(other *weightedIterator) bool { // Order the iterators primarily by the account hashes - hashI := its[i].it.Hash() - hashJ := its[j].it.Hash() + hashI := it.it.Hash() + hashJ := other.it.Hash() switch bytes.Compare(hashI[:], hashJ[:]) { case -1: @@ -52,12 +45,7 @@ func (its weightedIterators) Less(i, j int) bool { return false } // Same account/storage-slot in multiple layers, split by priority - return its[i].priority < its[j].priority -} - -// Swap implements sort.Interface, swapping two entries in the iterator stack. -func (its weightedIterators) Swap(i, j int) { - its[i], its[j] = its[j], its[i] + return it.priority < other.priority } // fastIterator is a more optimized multi-layer iterator which maintains a @@ -69,7 +57,7 @@ type fastIterator struct { curAccount []byte curSlot []byte - iterators weightedIterators + iterators []*weightedIterator initiated bool account bool fail error @@ -167,7 +155,9 @@ func (fi *fastIterator) init() { } } // Re-sort the entire list - sort.Sort(fi.iterators) + slices.SortFunc(fi.iterators, func(a, b *weightedIterator) bool { + return a.Less(b) + }) fi.initiated = false } diff --git a/core/state/snapshot/sort.go b/core/state/snapshot/sort.go index 88841231d917..10d544d26941 100644 --- a/core/state/snapshot/sort.go +++ b/core/state/snapshot/sort.go @@ -22,15 +22,6 @@ import ( "github.com/ethereum/go-ethereum/common" ) -// hashes is a helper to implement sort.Interface. -type hashes []common.Hash - -// Len is the number of elements in the collection. -func (hs hashes) Len() int { return len(hs) } - -// Less reports whether the element with index i should sort before the element -// with index j. -func (hs hashes) Less(i, j int) bool { return bytes.Compare(hs[i][:], hs[j][:]) < 0 } - -// Swap swaps the elements with indexes i and j. -func (hs hashes) Swap(i, j int) { hs[i], hs[j] = hs[j], hs[i] } +func hashesLess(a, b common.Hash) bool { + return bytes.Compare(a[:], b[:]) < 0 +} diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 139572540260..866b45a4f694 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -20,7 +20,6 @@ import ( "errors" "math" "math/big" - "sort" "sync" "sync/atomic" "time" @@ -35,6 +34,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" + "golang.org/x/exp/slices" ) const ( @@ -1495,13 +1495,15 @@ func (pool *TxPool) truncateQueue() { } // Sort all accounts with queued transactions by heartbeat - addresses := make(addressesByHeartbeat, 0, len(pool.queue)) + addresses := make([]addressByHeartbeat, 0, len(pool.queue)) for addr := range pool.queue { if !pool.locals.contains(addr) { // don't drop locals addresses = append(addresses, addressByHeartbeat{addr, pool.beats[addr]}) } } - sort.Sort(sort.Reverse(addresses)) + slices.SortFunc(addresses, func(a, b addressByHeartbeat) bool { + return !a.heartbeat.Before(b.heartbeat) // Sort descending + }) // Drop transactions until the total is below the limit or only locals remain for drop := queued - pool.config.GlobalQueue; drop > 0 && len(addresses) > 0; { @@ -1594,12 +1596,6 @@ type addressByHeartbeat struct { heartbeat time.Time } -type addressesByHeartbeat []addressByHeartbeat - -func (a addressesByHeartbeat) Len() int { return len(a) } -func (a addressesByHeartbeat) Less(i, j int) bool { return a[i].heartbeat.Before(a[j].heartbeat) } -func (a addressesByHeartbeat) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - // accountSet is simply a set of addresses to check for existence, and a signer // capable of deriving addresses from transactions. type accountSet struct { diff --git a/core/vm/eips.go b/core/vm/eips.go index ff1f132cb355..5510ddb1d28a 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -18,11 +18,11 @@ package vm import ( "fmt" - "sort" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" + "golang.org/x/exp/slices" ) var activators = map[int]func(*JumpTable){ @@ -58,7 +58,7 @@ func ActivateableEips() []string { for k := range activators { nums = append(nums, fmt.Sprintf("%d", k)) } - sort.Strings(nums) + slices.Sort(nums) return nums } diff --git a/eth/api_debug_test.go b/eth/api_debug_test.go index dec1b34ddc1f..f530d3657e43 100644 --- a/eth/api_debug_test.go +++ b/eth/api_debug_test.go @@ -21,7 +21,6 @@ import ( "fmt" "math/big" "reflect" - "sort" "testing" "github.com/davecgh/go-spew/spew" @@ -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: " "} @@ -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() @@ -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. @@ -111,7 +105,9 @@ 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, func(a, b common.Hash) bool { + return bytes.Compare(a.Bytes(), b.Bytes()) < 0 + }) middleH := hList[AccountRangeMaxResults/2] middleResult := accountRangeTest(t, &trie, state, middleH, AccountRangeMaxResults, AccountRangeMaxResults) missing, infirst, insecond := 0, 0, 0 diff --git a/eth/fetcher/tx_fetcher.go b/eth/fetcher/tx_fetcher.go index 39727e0079d7..55d9fb485c8a 100644 --- a/eth/fetcher/tx_fetcher.go +++ b/eth/fetcher/tx_fetcher.go @@ -21,7 +21,6 @@ import ( "errors" "fmt" mrand "math/rand" - "sort" "time" mapset "github.com/deckarep/golang-set/v2" @@ -31,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" + "golang.org/x/exp/slices" ) const ( @@ -843,7 +843,7 @@ func (f *TxFetcher) forEachPeer(peers map[string]struct{}, do func(peer string)) for peer := range peers { list = append(list, peer) } - sort.Strings(list) + slices.Sort(list) rotateStrings(list, f.rand.Intn(len(list))) for _, peer := range list { do(peer) diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index ee0b0ebcca1c..c04837706213 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -23,7 +23,6 @@ import ( "fmt" "math" "math/big" - "sort" "sync/atomic" "github.com/ethereum/go-ethereum/common" @@ -31,6 +30,7 @@ import ( "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 ( @@ -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 @@ -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 diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index a3dd83d79fb2..9eccd7242862 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -19,7 +19,6 @@ package gasprice import ( "context" "math/big" - "sort" "sync" "github.com/ethereum/go-ethereum/common" @@ -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 @@ -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 { @@ -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 @@ -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 } @@ -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] } diff --git a/eth/protocols/snap/sync_test.go b/eth/protocols/snap/sync_test.go index 58cde8d1ebb9..b8df597dff45 100644 --- a/eth/protocols/snap/sync_test.go +++ b/eth/protocols/snap/sync_test.go @@ -22,7 +22,6 @@ import ( "encoding/binary" "fmt" "math/big" - "sort" "sync" "testing" "time" @@ -38,6 +37,7 @@ import ( "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" "golang.org/x/crypto/sha3" + "golang.org/x/exp/slices" ) func TestHashing(t *testing.T) { @@ -127,9 +127,9 @@ type testPeer struct { remote *Syncer logger log.Logger accountTrie *trie.Trie - accountValues entrySlice + accountValues []*kv storageTries map[common.Hash]*trie.Trie - storageValues map[common.Hash]entrySlice + storageValues map[common.Hash][]*kv accountRequestHandler accountHandlerFunc storageRequestHandler storageHandlerFunc @@ -1321,12 +1321,10 @@ type kv struct { k, v []byte } -// Some helpers for sorting -type entrySlice []*kv - -func (p entrySlice) Len() int { return len(p) } -func (p entrySlice) Less(i, j int) bool { return bytes.Compare(p[i].k, p[j].k) < 0 } -func (p entrySlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +// Helper for sorting +func kvLessFunc(a, b *kv) bool { + return bytes.Compare(a.k, b.k) < 0 +} func key32(i uint64) []byte { key := make([]byte, 32) @@ -1367,11 +1365,11 @@ func getCodeByHash(hash common.Hash) []byte { } // makeAccountTrieNoStorage spits out a trie, along with the leafs -func makeAccountTrieNoStorage(n int) (string, *trie.Trie, entrySlice) { +func makeAccountTrieNoStorage(n int) (string, *trie.Trie, []*kv) { var ( db = trie.NewDatabase(rawdb.NewMemoryDatabase()) accTrie = trie.NewEmpty(db) - entries entrySlice + entries []*kv ) for i := uint64(1); i <= uint64(n); i++ { value, _ := rlp.EncodeToBytes(&types.StateAccount{ @@ -1385,7 +1383,7 @@ func makeAccountTrieNoStorage(n int) (string, *trie.Trie, entrySlice) { accTrie.MustUpdate(elem.k, elem.v) entries = append(entries, elem) } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) // Commit the state changes into db and re-create the trie // for accessing later. @@ -1399,9 +1397,9 @@ func makeAccountTrieNoStorage(n int) (string, *trie.Trie, entrySlice) { // makeBoundaryAccountTrie constructs an account trie. Instead of filling // accounts normally, this function will fill a few accounts which have // boundary hash. -func makeBoundaryAccountTrie(n int) (string, *trie.Trie, entrySlice) { +func makeBoundaryAccountTrie(n int) (string, *trie.Trie, []*kv) { var ( - entries entrySlice + entries []*kv boundaries []common.Hash db = trie.NewDatabase(rawdb.NewMemoryDatabase()) @@ -1447,7 +1445,7 @@ func makeBoundaryAccountTrie(n int) (string, *trie.Trie, entrySlice) { accTrie.MustUpdate(elem.k, elem.v) entries = append(entries, elem) } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) // Commit the state changes into db and re-create the trie // for accessing later. @@ -1460,14 +1458,14 @@ func makeBoundaryAccountTrie(n int) (string, *trie.Trie, entrySlice) { // makeAccountTrieWithStorageWithUniqueStorage creates an account trie where each accounts // has a unique storage set. -func makeAccountTrieWithStorageWithUniqueStorage(accounts, slots int, code bool) (string, *trie.Trie, entrySlice, map[common.Hash]*trie.Trie, map[common.Hash]entrySlice) { +func makeAccountTrieWithStorageWithUniqueStorage(accounts, slots int, code bool) (string, *trie.Trie, []*kv, map[common.Hash]*trie.Trie, map[common.Hash][]*kv) { var ( db = trie.NewDatabase(rawdb.NewMemoryDatabase()) accTrie = trie.NewEmpty(db) - entries entrySlice + entries []*kv storageRoots = make(map[common.Hash]common.Hash) storageTries = make(map[common.Hash]*trie.Trie) - storageEntries = make(map[common.Hash]entrySlice) + storageEntries = make(map[common.Hash][]*kv) nodes = trienode.NewMergedNodeSet() ) // Create n accounts in the trie @@ -1494,7 +1492,7 @@ func makeAccountTrieWithStorageWithUniqueStorage(accounts, slots int, code bool) storageRoots[common.BytesToHash(key)] = stRoot storageEntries[common.BytesToHash(key)] = stEntries } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) // Commit account trie root, set := accTrie.Commit(true) @@ -1515,14 +1513,14 @@ func makeAccountTrieWithStorageWithUniqueStorage(accounts, slots int, code bool) } // makeAccountTrieWithStorage spits out a trie, along with the leafs -func makeAccountTrieWithStorage(accounts, slots int, code, boundary bool) (string, *trie.Trie, entrySlice, map[common.Hash]*trie.Trie, map[common.Hash]entrySlice) { +func makeAccountTrieWithStorage(accounts, slots int, code, boundary bool) (string, *trie.Trie, []*kv, map[common.Hash]*trie.Trie, map[common.Hash][]*kv) { var ( db = trie.NewDatabase(rawdb.NewMemoryDatabase()) accTrie = trie.NewEmpty(db) - entries entrySlice + entries []*kv storageRoots = make(map[common.Hash]common.Hash) storageTries = make(map[common.Hash]*trie.Trie) - storageEntries = make(map[common.Hash]entrySlice) + storageEntries = make(map[common.Hash][]*kv) nodes = trienode.NewMergedNodeSet() ) // Create n accounts in the trie @@ -1536,7 +1534,7 @@ func makeAccountTrieWithStorage(accounts, slots int, code, boundary bool) (strin var ( stRoot common.Hash stNodes *trienode.NodeSet - stEntries entrySlice + stEntries []*kv ) if boundary { stRoot, stNodes, stEntries = makeBoundaryStorageTrie(common.BytesToHash(key), slots, db) @@ -1559,7 +1557,7 @@ func makeAccountTrieWithStorage(accounts, slots int, code, boundary bool) (strin storageRoots[common.BytesToHash(key)] = stRoot storageEntries[common.BytesToHash(key)] = stEntries } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) // Commit account trie root, set := accTrie.Commit(true) @@ -1588,9 +1586,9 @@ func makeAccountTrieWithStorage(accounts, slots int, code, boundary bool) (strin // makeStorageTrieWithSeed fills a storage trie with n items, returning the // not-yet-committed trie and the sorted entries. The seeds can be used to ensure // that tries are unique. -func makeStorageTrieWithSeed(owner common.Hash, n, seed uint64, db *trie.Database) (common.Hash, *trienode.NodeSet, entrySlice) { +func makeStorageTrieWithSeed(owner common.Hash, n, seed uint64, db *trie.Database) (common.Hash, *trienode.NodeSet, []*kv) { trie, _ := trie.New(trie.StorageTrieID(types.EmptyRootHash, owner, types.EmptyRootHash), db) - var entries entrySlice + var entries []*kv for i := uint64(1); i <= n; i++ { // store 'x' at slot 'x' slotValue := key32(i + seed) @@ -1603,7 +1601,7 @@ func makeStorageTrieWithSeed(owner common.Hash, n, seed uint64, db *trie.Databas trie.MustUpdate(elem.k, elem.v) entries = append(entries, elem) } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) root, nodes := trie.Commit(false) return root, nodes, entries } @@ -1611,9 +1609,9 @@ func makeStorageTrieWithSeed(owner common.Hash, n, seed uint64, db *trie.Databas // makeBoundaryStorageTrie constructs a storage trie. Instead of filling // storage slots normally, this function will fill a few slots which have // boundary hash. -func makeBoundaryStorageTrie(owner common.Hash, n int, db *trie.Database) (common.Hash, *trienode.NodeSet, entrySlice) { +func makeBoundaryStorageTrie(owner common.Hash, n int, db *trie.Database) (common.Hash, *trienode.NodeSet, []*kv) { var ( - entries entrySlice + entries []*kv boundaries []common.Hash trie, _ = trie.New(trie.StorageTrieID(types.EmptyRootHash, owner, types.EmptyRootHash), db) ) @@ -1654,7 +1652,7 @@ func makeBoundaryStorageTrie(owner common.Hash, n int, db *trie.Database) (commo trie.MustUpdate(elem.k, elem.v) entries = append(entries, elem) } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) root, nodes := trie.Commit(false) return root, nodes, entries } diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index fdb02b18950e..55515dbea897 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -25,7 +25,6 @@ import ( "fmt" "math/big" "reflect" - "sort" "sync/atomic" "testing" "time" @@ -45,6 +44,7 @@ import ( "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/exp/slices" ) var ( @@ -785,19 +785,15 @@ type Account struct { addr common.Address } -type Accounts []Account - -func (a Accounts) Len() int { return len(a) } -func (a Accounts) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a Accounts) Less(i, j int) bool { return bytes.Compare(a[i].addr.Bytes(), a[j].addr.Bytes()) < 0 } - -func newAccounts(n int) (accounts Accounts) { +func newAccounts(n int) (accounts []Account) { for i := 0; i < n; i++ { key, _ := crypto.GenerateKey() addr := crypto.PubkeyToAddress(key.PublicKey) accounts = append(accounts, Account{key: key, addr: addr}) } - sort.Sort(accounts) + slices.SortFunc(accounts, func(a, b Account) bool { + return bytes.Compare(a.addr.Bytes(), b.addr.Bytes()) < 0 + }) return accounts } diff --git a/ethdb/dbtest/testsuite.go b/ethdb/dbtest/testsuite.go index d82dbd699282..b22a02e7f2eb 100644 --- a/ethdb/dbtest/testsuite.go +++ b/ethdb/dbtest/testsuite.go @@ -20,10 +20,10 @@ import ( "bytes" "crypto/rand" "reflect" - "sort" "testing" "github.com/ethereum/go-ethereum/ethdb" + "golang.org/x/exp/slices" ) // TestDatabaseSuite runs a suite of tests against a KeyValueStore database @@ -134,7 +134,7 @@ func TestDatabaseSuite(t *testing.T, New func() ethdb.KeyValueStore) { defer db.Close() keys := []string{"1", "2", "3", "4", "6", "10", "11", "12", "20", "21", "22"} - sort.Strings(keys) // 1, 10, 11, etc + slices.Sort(keys) // 1, 10, 11, etc for _, k := range keys { if err := db.Put([]byte(k), nil); err != nil { @@ -504,7 +504,7 @@ func iterateKeys(it ethdb.Iterator) []string { for it.Next() { keys = append(keys, string(it.Key())) } - sort.Strings(keys) + slices.Sort(keys) it.Release() return keys } @@ -526,7 +526,7 @@ func makeDataset(size, ksize, vsize int, order bool) ([][]byte, [][]byte) { vals = append(vals, randBytes(vsize)) } if order { - sort.Slice(keys, func(i, j int) bool { return bytes.Compare(keys[i], keys[j]) < 0 }) + slices.SortFunc(keys, func(a, b []byte) bool { return bytes.Compare(a, b) < 0 }) } return keys, vals } diff --git a/ethdb/memorydb/memorydb.go b/ethdb/memorydb/memorydb.go index f9f74322b575..e177ddfdbd28 100644 --- a/ethdb/memorydb/memorydb.go +++ b/ethdb/memorydb/memorydb.go @@ -19,12 +19,12 @@ package memorydb import ( "errors" - "sort" "strings" "sync" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" + "golang.org/x/exp/slices" ) var ( @@ -164,7 +164,7 @@ func (db *Database) NewIterator(prefix []byte, start []byte) ethdb.Iterator { } } // Sort the items and retrieve the associated values - sort.Strings(keys) + slices.Sort(keys) for _, key := range keys { values = append(values, db.db[key]) } diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index f80f11147212..8ebca49b2c3f 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -25,7 +25,6 @@ import ( "hash" "math/big" "reflect" - "sort" "testing" "time" @@ -48,6 +47,7 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/stretchr/testify/require" "golang.org/x/crypto/sha3" + "golang.org/x/exp/slices" ) func TestTransaction_RoundTripRpcJSON(t *testing.T) { @@ -649,19 +649,15 @@ type Account struct { addr common.Address } -type Accounts []Account - -func (a Accounts) Len() int { return len(a) } -func (a Accounts) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a Accounts) Less(i, j int) bool { return bytes.Compare(a[i].addr.Bytes(), a[j].addr.Bytes()) < 0 } - -func newAccounts(n int) (accounts Accounts) { +func newAccounts(n int) (accounts []Account) { for i := 0; i < n; i++ { key, _ := crypto.GenerateKey() addr := crypto.PubkeyToAddress(key.PublicKey) accounts = append(accounts, Account{key: key, addr: addr}) } - sort.Sort(accounts) + slices.SortFunc(accounts, func(a, b Account) bool { + return bytes.Compare(a.addr.Bytes(), b.addr.Bytes()) < 0 + }) return accounts } diff --git a/internal/jsre/completion.go b/internal/jsre/completion.go index 844a0532fdba..18322a7778a3 100644 --- a/internal/jsre/completion.go +++ b/internal/jsre/completion.go @@ -18,10 +18,10 @@ package jsre import ( "regexp" - "sort" "strings" "github.com/dop251/goja" + "golang.org/x/exp/slices" ) // JS numerical token @@ -88,6 +88,6 @@ func getCompletions(vm *goja.Runtime, line string) (results []string) { } } - sort.Strings(results) + slices.Sort(results) return results } diff --git a/internal/jsre/pretty.go b/internal/jsre/pretty.go index bd772b4927c2..702c800a7c93 100644 --- a/internal/jsre/pretty.go +++ b/internal/jsre/pretty.go @@ -20,12 +20,12 @@ import ( "fmt" "io" "reflect" - "sort" "strconv" "strings" "github.com/dop251/goja" "github.com/fatih/color" + "golang.org/x/exp/slices" ) const ( @@ -221,8 +221,8 @@ func (ctx ppctx) fields(obj *goja.Object) []string { } } iterOwnAndConstructorKeys(ctx.vm, obj, add) - sort.Strings(vals) - sort.Strings(methods) + slices.Sort(vals) + slices.Sort(methods) return append(vals, methods...) } diff --git a/les/peer_test.go b/les/peer_test.go index 021d5cb594c4..89caf3861ae2 100644 --- a/les/peer_test.go +++ b/les/peer_test.go @@ -21,7 +21,6 @@ import ( "errors" "math/big" "reflect" - "sort" "testing" "time" @@ -32,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/params" + "golang.org/x/exp/slices" ) type testServerPeerSub struct { @@ -58,8 +58,8 @@ func TestPeerSubscription(t *testing.T) { if len(given) == 0 && len(expect) == 0 { return } - sort.Strings(given) - sort.Strings(expect) + slices.Sort(given) + slices.Sort(expect) if !reflect.DeepEqual(given, expect) { t.Fatalf("all peer ids mismatch, want %v, given %v", expect, given) } diff --git a/les/servingqueue.go b/les/servingqueue.go index b4b53d8df548..c25925403ad7 100644 --- a/les/servingqueue.go +++ b/les/servingqueue.go @@ -17,12 +17,12 @@ package les import ( - "sort" "sync" "sync/atomic" "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/common/prque" + "golang.org/x/exp/slices" ) // servingQueue allows running tasks in a limited number of threads and puts the @@ -180,35 +180,19 @@ func (sq *servingQueue) threadController() { } } -type ( - // peerTasks lists the tasks received from a given peer when selecting peers to freeze - peerTasks struct { - peer *clientPeer - list []*servingTask - sumTime uint64 - priority float64 - } - // peerList is a sortable list of peerTasks - peerList []*peerTasks -) - -func (l peerList) Len() int { - return len(l) -} - -func (l peerList) Less(i, j int) bool { - return l[i].priority < l[j].priority -} - -func (l peerList) Swap(i, j int) { - l[i], l[j] = l[j], l[i] +// peerTasks lists the tasks received from a given peer when selecting peers to freeze +type peerTasks struct { + peer *clientPeer + list []*servingTask + sumTime uint64 + priority float64 } // freezePeers selects the peers with the worst priority queued tasks and freezes // them until burstTime goes under burstDropLimit or all peers are frozen func (sq *servingQueue) freezePeers() { peerMap := make(map[*clientPeer]*peerTasks) - var peerList peerList + var peerList []*peerTasks if sq.best != nil { sq.queue.Push(sq.best, sq.best.priority) } @@ -231,7 +215,9 @@ func (sq *servingQueue) freezePeers() { tasks.list = append(tasks.list, task) tasks.sumTime += task.expTime } - sort.Sort(peerList) + slices.SortFunc(peerList, func(a, b *peerTasks) bool { + return a.priority < b.priority + }) drop := true for _, tasks := range peerList { if drop { diff --git a/les/utils/limiter.go b/les/utils/limiter.go index 84d186efd63e..155bd801500a 100644 --- a/les/utils/limiter.go +++ b/les/utils/limiter.go @@ -17,10 +17,10 @@ package utils import ( - "sort" "sync" "github.com/ethereum/go-ethereum/p2p/enode" + "golang.org/x/exp/slices" ) const maxSelectionWeight = 1000000000 // maximum selection weight of each individual node/address group @@ -340,24 +340,9 @@ func (l *Limiter) Stop() { l.cond.Signal() } -type ( - dropList []dropListItem - dropListItem struct { - nq *nodeQueue - priority float64 - } -) - -func (l dropList) Len() int { - return len(l) -} - -func (l dropList) Less(i, j int) bool { - return l[i].priority < l[j].priority -} - -func (l dropList) Swap(i, j int) { - l[i], l[j] = l[j], l[i] +type dropListItem struct { + nq *nodeQueue + priority float64 } // dropRequests selects the nodes with the highest queued request cost to selection @@ -366,7 +351,7 @@ func (l dropList) Swap(i, j int) { func (l *Limiter) dropRequests() { var ( sumValue float64 - list dropList + list []dropListItem ) for _, nq := range l.nodes { sumValue += nq.value @@ -384,7 +369,9 @@ func (l *Limiter) dropRequests() { priority: w / float64(nq.sumCost), }) } - sort.Sort(list) + slices.SortFunc(list, func(a, b dropListItem) bool { + return a.priority < b.priority + }) for _, item := range list { for _, request := range item.nq.queue { close(request.process) diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go index d966fa9a8666..1627fc9db994 100644 --- a/metrics/prometheus/prometheus.go +++ b/metrics/prometheus/prometheus.go @@ -20,10 +20,10 @@ package prometheus import ( "fmt" "net/http" - "sort" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" + "golang.org/x/exp/slices" ) // Handler returns an HTTP handler which dump metrics in Prometheus format. @@ -34,7 +34,7 @@ func Handler(reg metrics.Registry) http.Handler { reg.Each(func(name string, i interface{}) { names = append(names, name) }) - sort.Strings(names) + slices.Sort(names) // Aggregate all the metrics into a Prometheus collector c := newCollector() diff --git a/metrics/resetting_timer.go b/metrics/resetting_timer.go index e5327d3bd339..924ba11052c1 100644 --- a/metrics/resetting_timer.go +++ b/metrics/resetting_timer.go @@ -2,9 +2,10 @@ package metrics import ( "math" - "sort" "sync" "time" + + "golang.org/x/exp/slices" ) // Initial slice capacity for the values stored in a ResettingTimer @@ -186,7 +187,7 @@ func (t *ResettingTimerSnapshot) Mean() float64 { } func (t *ResettingTimerSnapshot) calc(percentiles []float64) { - sort.Sort(Int64Slice(t.values)) + slices.Sort(t.values) count := len(t.values) if count > 0 { @@ -232,10 +233,3 @@ func (t *ResettingTimerSnapshot) calc(percentiles []float64) { t.calculated = true } - -// Int64Slice attaches the methods of sort.Interface to []int64, sorting in increasing order. -type Int64Slice []int64 - -func (s Int64Slice) Len() int { return len(s) } -func (s Int64Slice) Less(i, j int) bool { return s[i] < s[j] } -func (s Int64Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } diff --git a/metrics/sample.go b/metrics/sample.go index afcaa2118426..252a878f581b 100644 --- a/metrics/sample.go +++ b/metrics/sample.go @@ -3,9 +3,10 @@ package metrics import ( "math" "math/rand" - "sort" "sync" "time" + + "golang.org/x/exp/slices" ) const rescaleThreshold = time.Hour @@ -282,17 +283,17 @@ func SampleMin(values []int64) int64 { } // SamplePercentiles returns an arbitrary percentile of the slice of int64. -func SamplePercentile(values int64Slice, p float64) float64 { +func SamplePercentile(values []int64, p float64) float64 { return SamplePercentiles(values, []float64{p})[0] } // SamplePercentiles returns a slice of arbitrary percentiles of the slice of // int64. -func SamplePercentiles(values int64Slice, ps []float64) []float64 { +func SamplePercentiles(values []int64, ps []float64) []float64 { scores := make([]float64, len(ps)) size := len(values) if size > 0 { - sort.Sort(values) + slices.Sort(values) for i, p := range ps { pos := p * float64(size+1) if pos < 1.0 { @@ -633,9 +634,3 @@ func (h *expDecaySampleHeap) down(i, n int) { i = j } } - -type int64Slice []int64 - -func (p int64Slice) Len() int { return len(p) } -func (p int64Slice) Less(i, j int) bool { return p[i] < p[j] } -func (p int64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } diff --git a/metrics/writer.go b/metrics/writer.go index 256fbd14c9b9..43d3252f3043 100644 --- a/metrics/writer.go +++ b/metrics/writer.go @@ -3,8 +3,9 @@ package metrics import ( "fmt" "io" - "sort" "time" + + "golang.org/x/exp/slices" ) // Write sorts writes each metric in the given registry periodically to the @@ -18,12 +19,12 @@ func Write(r Registry, d time.Duration, w io.Writer) { // WriteOnce sorts and writes metrics in the given registry to the given // io.Writer. func WriteOnce(r Registry, w io.Writer) { - var namedMetrics namedMetricSlice + var namedMetrics []namedMetric r.Each(func(name string, i interface{}) { namedMetrics = append(namedMetrics, namedMetric{name, i}) }) - sort.Sort(namedMetrics) + slices.SortFunc(namedMetrics, namedMetricLessFunc) for _, namedMetric := range namedMetrics { switch metric := namedMetric.m.(type) { case Counter: @@ -91,13 +92,6 @@ type namedMetric struct { m interface{} } -// namedMetricSlice is a slice of namedMetrics that implements sort.Interface. -type namedMetricSlice []namedMetric - -func (nms namedMetricSlice) Len() int { return len(nms) } - -func (nms namedMetricSlice) Swap(i, j int) { nms[i], nms[j] = nms[j], nms[i] } - -func (nms namedMetricSlice) Less(i, j int) bool { - return nms[i].name < nms[j].name +func namedMetricLessFunc(a, b namedMetric) bool { + return a.name < b.name } diff --git a/metrics/writer_test.go b/metrics/writer_test.go index 1aacc287121b..53b06013cd5a 100644 --- a/metrics/writer_test.go +++ b/metrics/writer_test.go @@ -1,19 +1,20 @@ package metrics import ( - "sort" "testing" + + "golang.org/x/exp/slices" ) func TestMetricsSorting(t *testing.T) { - var namedMetrics = namedMetricSlice{ + var namedMetrics = []namedMetric{ {name: "zzz"}, {name: "bbb"}, {name: "fff"}, {name: "ggg"}, } - sort.Sort(namedMetrics) + slices.SortFunc(namedMetrics, namedMetricLessFunc) for i, name := range []string{"bbb", "fff", "ggg", "zzz"} { if namedMetrics[i].name != name { t.Fail() diff --git a/node/rpcstack.go b/node/rpcstack.go index e91585a2b630..e91079d74292 100644 --- a/node/rpcstack.go +++ b/node/rpcstack.go @@ -23,7 +23,6 @@ import ( "io" "net" "net/http" - "sort" "strconv" "strings" "sync" @@ -33,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rpc" "github.com/rs/cors" + "golang.org/x/exp/slices" ) // httpConfig is the JSON-RPC/HTTP configuration. @@ -182,7 +182,7 @@ func (h *httpServer) start() error { for path := range h.handlerNames { paths = append(paths, path) } - sort.Strings(paths) + slices.Sort(paths) logged := make(map[string]bool, len(paths)) for _, path := range paths { name := h.handlerNames[path] diff --git a/p2p/discover/ntp.go b/p2p/discover/ntp.go index 48ceffe95b8d..3f9157808f12 100644 --- a/p2p/discover/ntp.go +++ b/p2p/discover/ntp.go @@ -22,10 +22,10 @@ package discover import ( "fmt" "net" - "sort" "time" "github.com/ethereum/go-ethereum/log" + "golang.org/x/exp/slices" ) const ( @@ -33,14 +33,6 @@ const ( ntpChecks = 3 // Number of measurements to do against the NTP server ) -// durationSlice attaches the methods of sort.Interface to []time.Duration, -// sorting in increasing order. -type durationSlice []time.Duration - -func (s durationSlice) Len() int { return len(s) } -func (s durationSlice) Less(i, j int) bool { return s[i] < s[j] } -func (s durationSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } - // checkClockDrift queries an NTP server for clock drifts and warns the user if // one large enough is detected. func checkClockDrift() { @@ -109,7 +101,7 @@ func sntpDrift(measurements int) (time.Duration, error) { drifts = append(drifts, sent.Sub(t)+elapsed/2) } // Calculate average drift (drop two extremities to avoid outliers) - sort.Sort(durationSlice(drifts)) + slices.Sort(drifts) drift := time.Duration(0) for i := 1; i < len(drifts)-1; i++ { diff --git a/p2p/discover/table_util_test.go b/p2p/discover/table_util_test.go index 5b5e9a0431f4..52544a2c9348 100644 --- a/p2p/discover/table_util_test.go +++ b/p2p/discover/table_util_test.go @@ -24,12 +24,12 @@ import ( "fmt" "math/rand" "net" - "sort" "sync" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enr" + "golang.org/x/exp/slices" ) var nullNode *enode.Node @@ -217,14 +217,14 @@ func nodeEqual(n1 *enode.Node, n2 *enode.Node) bool { } func sortByID(nodes []*enode.Node) { - sort.Slice(nodes, func(i, j int) bool { - return string(nodes[i].ID().Bytes()) < string(nodes[j].ID().Bytes()) + slices.SortFunc(nodes, func(a, b *enode.Node) bool { + return string(a.ID().Bytes()) < string(b.ID().Bytes()) }) } func sortedByDistanceTo(distbase enode.ID, slice []*node) bool { - return sort.SliceIsSorted(slice, func(i, j int) bool { - return enode.DistCmp(distbase, slice[i].ID(), slice[j].ID()) < 0 + return slices.IsSortedFunc(slice, func(a, b *node) bool { + return enode.DistCmp(distbase, a.ID(), b.ID()) < 0 }) } diff --git a/p2p/discover/v4_lookup_test.go b/p2p/discover/v4_lookup_test.go index a00de9ca18c7..83c462628867 100644 --- a/p2p/discover/v4_lookup_test.go +++ b/p2p/discover/v4_lookup_test.go @@ -20,13 +20,13 @@ import ( "crypto/ecdsa" "fmt" "net" - "sort" "testing" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/p2p/discover/v4wire" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enr" + "golang.org/x/exp/slices" ) func TestUDPv4_Lookup(t *testing.T) { @@ -302,8 +302,8 @@ func (tn *preminedTestnet) closest(n int) (nodes []*enode.Node) { nodes = append(nodes, tn.node(d, i)) } } - sort.Slice(nodes, func(i, j int) bool { - return enode.DistCmp(tn.target.id(), nodes[i].ID(), nodes[j].ID()) < 0 + slices.SortFunc(nodes, func(a, b *enode.Node) bool { + return enode.DistCmp(tn.target.id(), a.ID(), b.ID()) < 0 }) return nodes[:n] } diff --git a/p2p/discover/v5_udp_test.go b/p2p/discover/v5_udp_test.go index 392a95b61e3b..887d46c4975a 100644 --- a/p2p/discover/v5_udp_test.go +++ b/p2p/discover/v5_udp_test.go @@ -24,7 +24,6 @@ import ( "math/rand" "net" "reflect" - "sort" "testing" "time" @@ -35,6 +34,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/rlp" "github.com/stretchr/testify/require" + "golang.org/x/exp/slices" ) // Real sockets, real crypto: this test checks end-to-end connectivity for UDPv5. @@ -61,8 +61,8 @@ func TestUDPv5_lookupE2E(t *testing.T) { for i := range nodes { expectedResult[i] = nodes[i].Self() } - sort.Slice(expectedResult, func(i, j int) bool { - return enode.DistCmp(target.ID(), expectedResult[i].ID(), expectedResult[j].ID()) < 0 + slices.SortFunc(expectedResult, func(a, b *enode.Node) bool { + return enode.DistCmp(target.ID(), a.ID(), b.ID()) < 0 }) // Do the lookup. diff --git a/p2p/dnsdisc/tree.go b/p2p/dnsdisc/tree.go index a3f426e42806..f91aff458fb0 100644 --- a/p2p/dnsdisc/tree.go +++ b/p2p/dnsdisc/tree.go @@ -23,7 +23,6 @@ import ( "encoding/base64" "fmt" "io" - "sort" "strings" "github.com/ethereum/go-ethereum/crypto" @@ -31,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/rlp" "golang.org/x/crypto/sha3" + "golang.org/x/exp/slices" ) // Tree is a merkle tree of node records. @@ -214,8 +214,8 @@ func (t *Tree) build(entries []entry) entry { } func sortByID(nodes []*enode.Node) []*enode.Node { - sort.Slice(nodes, func(i, j int) bool { - return bytes.Compare(nodes[i].ID().Bytes(), nodes[j].ID().Bytes()) < 0 + slices.SortFunc(nodes, func(a, b *enode.Node) bool { + return bytes.Compare(a.ID().Bytes(), b.ID().Bytes()) < 0 }) return nodes } diff --git a/p2p/msgrate/msgrate.go b/p2p/msgrate/msgrate.go index 4f08792242af..90920f2d4804 100644 --- a/p2p/msgrate/msgrate.go +++ b/p2p/msgrate/msgrate.go @@ -20,11 +20,11 @@ package msgrate import ( "errors" "math" - "sort" "sync" "time" "github.com/ethereum/go-ethereum/log" + "golang.org/x/exp/slices" ) // measurementImpact is the impact a single measurement has on a peer's final @@ -293,7 +293,7 @@ func (t *Trackers) medianRoundTrip() time.Duration { rtts = append(rtts, float64(tt.roundtrip)) tt.lock.RUnlock() } - sort.Float64s(rtts) + slices.Sort(rtts) var median time.Duration switch len(rtts) { diff --git a/p2p/netutil/net.go b/p2p/netutil/net.go index d5da3c694f8e..32b7512adfe5 100644 --- a/p2p/netutil/net.go +++ b/p2p/netutil/net.go @@ -22,8 +22,9 @@ import ( "errors" "fmt" "net" - "sort" "strings" + + "golang.org/x/exp/slices" ) var lan4, lan6, special4, special6 Netlist @@ -303,7 +304,7 @@ func (s DistinctNetSet) String() string { for k := range s.members { keys = append(keys, k) } - sort.Strings(keys) + slices.Sort(keys) for i, k := range keys { var ip net.IP if k[0] == '4' { diff --git a/p2p/peer.go b/p2p/peer.go index a4b22fb6d463..b564646fbaea 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -21,7 +21,6 @@ import ( "fmt" "io" "net" - "sort" "sync" "time" @@ -32,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/rlp" + "golang.org/x/exp/slices" ) var ( @@ -375,7 +375,7 @@ func countMatchingProtocols(protocols []Protocol, caps []Cap) int { // matchProtocols creates structures for matching named subprotocols. func matchProtocols(protocols []Protocol, caps []Cap, rw MsgReadWriter) map[string]*protoRW { - sort.Sort(capsByNameAndVersion(caps)) + slices.SortFunc(caps, capLessFunc) offset := baseProtocolLength result := make(map[string]*protoRW) diff --git a/p2p/protocol.go b/p2p/protocol.go index fa23a087c281..5effd35ed774 100644 --- a/p2p/protocol.go +++ b/p2p/protocol.go @@ -77,10 +77,6 @@ func (cap Cap) String() string { return fmt.Sprintf("%s/%d", cap.Name, cap.Version) } -type capsByNameAndVersion []Cap - -func (cs capsByNameAndVersion) Len() int { return len(cs) } -func (cs capsByNameAndVersion) Swap(i, j int) { cs[i], cs[j] = cs[j], cs[i] } -func (cs capsByNameAndVersion) Less(i, j int) bool { - return cs[i].Name < cs[j].Name || (cs[i].Name == cs[j].Name && cs[i].Version < cs[j].Version) +func capLessFunc(a, b Cap) bool { + return a.Name < b.Name || (a.Name == b.Name && a.Version < b.Version) } diff --git a/p2p/server.go b/p2p/server.go index f7bf948b6901..debe120c7cc9 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -24,7 +24,6 @@ import ( "errors" "fmt" "net" - "sort" "sync" "sync/atomic" "time" @@ -39,6 +38,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/netutil" + "golang.org/x/exp/slices" ) const ( @@ -498,7 +498,7 @@ func (srv *Server) setupLocalNode() error { for _, p := range srv.Protocols { srv.ourHandshake.Caps = append(srv.ourHandshake.Caps, p.cap()) } - sort.Sort(capsByNameAndVersion(srv.ourHandshake.Caps)) + slices.SortFunc(srv.ourHandshake.Caps, capLessFunc) // Create the local node. db, err := enode.OpenDB(srv.NodeDatabase) diff --git a/rlp/rlpgen/gen.go b/rlp/rlpgen/gen.go index 0c6586482698..048865d76127 100644 --- a/rlp/rlpgen/gen.go +++ b/rlp/rlpgen/gen.go @@ -21,9 +21,9 @@ import ( "fmt" "go/format" "go/types" - "sort" "github.com/ethereum/go-ethereum/rlp/internal/rlpstruct" + "golang.org/x/exp/slices" ) // buildContext keeps the data needed for make*Op. @@ -131,7 +131,7 @@ func (ctx *genContext) importsList() []string { for k := range ctx.imports { imp = append(imp, k) } - sort.Strings(imp) + slices.Sort(imp) return imp } diff --git a/signer/core/apitypes/types.go b/signer/core/apitypes/types.go index 8218e754d36c..40d2b5fe2b72 100644 --- a/signer/core/apitypes/types.go +++ b/signer/core/apitypes/types.go @@ -24,7 +24,6 @@ import ( "math/big" "reflect" "regexp" - "sort" "strconv" "strings" @@ -34,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "golang.org/x/exp/slices" ) var typedDataReferenceTypeRegexp = regexp.MustCompile(`^[A-Za-z](\w*)(\[\])?$`) @@ -306,7 +306,7 @@ func (typedData *TypedData) EncodeType(primaryType string) hexutil.Bytes { deps := typedData.Dependencies(primaryType, []string{}) if len(deps) > 0 { slicedDeps := deps[1:] - sort.Strings(slicedDeps) + slices.Sort(slicedDeps) deps = append([]string{primaryType}, slicedDeps...) } diff --git a/tests/fuzzers/rangeproof/rangeproof-fuzzer.go b/tests/fuzzers/rangeproof/rangeproof-fuzzer.go index 2881c7a7c211..19bd39df983b 100644 --- a/tests/fuzzers/rangeproof/rangeproof-fuzzer.go +++ b/tests/fuzzers/rangeproof/rangeproof-fuzzer.go @@ -21,12 +21,12 @@ import ( "encoding/binary" "fmt" "io" - "sort" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/ethdb/memorydb" "github.com/ethereum/go-ethereum/trie" + "golang.org/x/exp/slices" ) type kv struct { @@ -34,12 +34,6 @@ type kv struct { t bool } -type entrySlice []*kv - -func (p entrySlice) Len() int { return len(p) } -func (p entrySlice) Less(i, j int) bool { return bytes.Compare(p[i].k, p[j].k) < 0 } -func (p entrySlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } - type fuzzer struct { input io.Reader exhausted bool @@ -97,14 +91,16 @@ func (f *fuzzer) fuzz() int { if f.exhausted { return 0 // input too short } - var entries entrySlice + var entries []*kv for _, kv := range vals { entries = append(entries, kv) } if len(entries) <= 1 { return 0 } - sort.Sort(entries) + slices.SortFunc(entries, func(a, b *kv) bool { + return bytes.Compare(a.k, b.k) < 0 + }) var ok = 0 for { diff --git a/tests/fuzzers/stacktrie/trie_fuzzer.go b/tests/fuzzers/stacktrie/trie_fuzzer.go index 0099e9e16986..77e602f807ea 100644 --- a/tests/fuzzers/stacktrie/trie_fuzzer.go +++ b/tests/fuzzers/stacktrie/trie_fuzzer.go @@ -23,7 +23,6 @@ import ( "fmt" "hash" "io" - "sort" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" @@ -33,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" "golang.org/x/crypto/sha3" + "golang.org/x/exp/slices" ) type fuzzer struct { @@ -104,19 +104,6 @@ func (b *spongeBatch) Replay(w ethdb.KeyValueWriter) error { return nil } type kv struct { k, v []byte } -type kvs []kv - -func (k kvs) Len() int { - return len(k) -} - -func (k kvs) Less(i, j int) bool { - return bytes.Compare(k[i].k, k[j].k) < 0 -} - -func (k kvs) Swap(i, j int) { - k[j], k[i] = k[i], k[j] -} // Fuzz is the fuzzing entry-point. // The function must return @@ -156,7 +143,7 @@ func (f *fuzzer) fuzz() int { trieB = trie.NewStackTrie(func(owner common.Hash, path []byte, hash common.Hash, blob []byte) { rawdb.WriteTrieNode(spongeB, owner, path, hash, blob, dbB.Scheme()) }) - vals kvs + vals []kv useful bool maxElements = 10000 // operate on unique keys only @@ -192,7 +179,9 @@ func (f *fuzzer) fuzz() int { dbA.Commit(rootA, false) // Stacktrie requires sorted insertion - sort.Sort(vals) + slices.SortFunc(vals, func(a, b kv) bool { + return bytes.Compare(a.k, b.k) < 0 + }) for _, kv := range vals { if f.debugging { fmt.Printf("{\"%#x\" , \"%#x\"} // stacktrie.Update\n", kv.k, kv.v) diff --git a/tests/init.go b/tests/init.go index 869f1bfcdd67..32bf0a38cc49 100644 --- a/tests/init.go +++ b/tests/init.go @@ -19,9 +19,9 @@ package tests import ( "fmt" "math/big" - "sort" "github.com/ethereum/go-ethereum/params" + "golang.org/x/exp/slices" ) func u64(val uint64) *uint64 { return &val } @@ -307,7 +307,7 @@ func AvailableForks() []string { for k := range Forks { availableForks = append(availableForks, k) } - sort.Strings(availableForks) + slices.Sort(availableForks) return availableForks } diff --git a/tests/init_test.go b/tests/init_test.go index 7d8743efcc71..15877e6a9450 100644 --- a/tests/init_test.go +++ b/tests/init_test.go @@ -26,11 +26,11 @@ import ( "reflect" "regexp" "runtime" - "sort" "strings" "testing" "github.com/ethereum/go-ethereum/params" + "golang.org/x/exp/slices" ) var ( @@ -266,7 +266,7 @@ func sortedMapKeys(m reflect.Value) []string { for i, k := range m.MapKeys() { keys[i] = k.String() } - sort.Strings(keys) + slices.Sort(keys) return keys } diff --git a/trie/iterator_test.go b/trie/iterator_test.go index 8003858e5beb..34dfa92acc12 100644 --- a/trie/iterator_test.go +++ b/trie/iterator_test.go @@ -84,6 +84,10 @@ type kv struct { t bool } +func kvLessFunc(a, b *kv) bool { + return bytes.Compare(a.k, b.k) < 0 +} + func TestIteratorLargeData(t *testing.T) { trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) vals := make(map[string]*kv) diff --git a/trie/proof_test.go b/trie/proof_test.go index 69e3f8e9ceea..120862f39c8c 100644 --- a/trie/proof_test.go +++ b/trie/proof_test.go @@ -22,13 +22,13 @@ import ( "encoding/binary" "fmt" mrand "math/rand" - "sort" "testing" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb/memorydb" + "golang.org/x/exp/slices" ) // Prng is a pseudo random number generator seeded by strong randomness. @@ -165,21 +165,15 @@ func TestMissingKeyProof(t *testing.T) { } } -type entrySlice []*kv - -func (p entrySlice) Len() int { return len(p) } -func (p entrySlice) Less(i, j int) bool { return bytes.Compare(p[i].k, p[j].k) < 0 } -func (p entrySlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } - // TestRangeProof tests normal range proof with both edge proofs // as the existent proof. The test cases are generated randomly. func TestRangeProof(t *testing.T) { trie, vals := randomTrie(4096) - var entries entrySlice + var entries []*kv for _, kv := range vals { entries = append(entries, kv) } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) for i := 0; i < 500; i++ { start := mrand.Intn(len(entries)) end := mrand.Intn(len(entries)-start) + start + 1 @@ -208,11 +202,11 @@ func TestRangeProof(t *testing.T) { // The test cases are generated randomly. func TestRangeProofWithNonExistentProof(t *testing.T) { trie, vals := randomTrie(4096) - var entries entrySlice + var entries []*kv for _, kv := range vals { entries = append(entries, kv) } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) for i := 0; i < 500; i++ { start := mrand.Intn(len(entries)) end := mrand.Intn(len(entries)-start) + start + 1 @@ -280,11 +274,11 @@ func TestRangeProofWithNonExistentProof(t *testing.T) { // - There exists a gap between the last element and the right edge proof func TestRangeProofWithInvalidNonExistentProof(t *testing.T) { trie, vals := randomTrie(4096) - var entries entrySlice + var entries []*kv for _, kv := range vals { entries = append(entries, kv) } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) // Case 1 start, end := 100, 200 @@ -337,11 +331,11 @@ func TestRangeProofWithInvalidNonExistentProof(t *testing.T) { // non-existent one. func TestOneElementRangeProof(t *testing.T) { trie, vals := randomTrie(4096) - var entries entrySlice + var entries []*kv for _, kv := range vals { entries = append(entries, kv) } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) // One element with existent edge proof, both edge proofs // point to the SAME key. @@ -424,11 +418,11 @@ func TestOneElementRangeProof(t *testing.T) { // The edge proofs can be nil. func TestAllElementsProof(t *testing.T) { trie, vals := randomTrie(4096) - var entries entrySlice + var entries []*kv for _, kv := range vals { entries = append(entries, kv) } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) var k [][]byte var v [][]byte @@ -474,13 +468,13 @@ func TestAllElementsProof(t *testing.T) { func TestSingleSideRangeProof(t *testing.T) { for i := 0; i < 64; i++ { trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) - var entries entrySlice + var entries []*kv for i := 0; i < 4096; i++ { value := &kv{randBytes(32), randBytes(20), false} trie.MustUpdate(value.k, value.v) entries = append(entries, value) } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) var cases = []int{0, 1, 50, 100, 1000, 2000, len(entries) - 1} for _, pos := range cases { @@ -509,13 +503,13 @@ func TestSingleSideRangeProof(t *testing.T) { func TestReverseSingleSideRangeProof(t *testing.T) { for i := 0; i < 64; i++ { trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) - var entries entrySlice + var entries []*kv for i := 0; i < 4096; i++ { value := &kv{randBytes(32), randBytes(20), false} trie.MustUpdate(value.k, value.v) entries = append(entries, value) } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) var cases = []int{0, 1, 50, 100, 1000, 2000, len(entries) - 1} for _, pos := range cases { @@ -545,11 +539,11 @@ func TestReverseSingleSideRangeProof(t *testing.T) { // The prover is expected to detect the error. func TestBadRangeProof(t *testing.T) { trie, vals := randomTrie(4096) - var entries entrySlice + var entries []*kv for _, kv := range vals { entries = append(entries, kv) } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) for i := 0; i < 500; i++ { start := mrand.Intn(len(entries)) @@ -648,11 +642,11 @@ func TestGappedRangeProof(t *testing.T) { // TestSameSideProofs tests the element is not in the range covered by proofs func TestSameSideProofs(t *testing.T) { trie, vals := randomTrie(4096) - var entries entrySlice + var entries []*kv for _, kv := range vals { entries = append(entries, kv) } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) pos := 1000 first := decreaseKey(common.CopyBytes(entries[pos].k)) @@ -690,13 +684,13 @@ func TestSameSideProofs(t *testing.T) { func TestHasRightElement(t *testing.T) { trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) - var entries entrySlice + var entries []*kv for i := 0; i < 4096; i++ { value := &kv{randBytes(32), randBytes(20), false} trie.MustUpdate(value.k, value.v) entries = append(entries, value) } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) var cases = []struct { start int @@ -764,11 +758,11 @@ func TestHasRightElement(t *testing.T) { // The first edge proof must be a non-existent proof. func TestEmptyRangeProof(t *testing.T) { trie, vals := randomTrie(4096) - var entries entrySlice + var entries []*kv for _, kv := range vals { entries = append(entries, kv) } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) var cases = []struct { pos int @@ -799,11 +793,11 @@ func TestEmptyRangeProof(t *testing.T) { func TestBloatedProof(t *testing.T) { // Use a small trie trie, kvs := nonRandomTrie(100) - var entries entrySlice + var entries []*kv for _, kv := range kvs { entries = append(entries, kv) } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) var keys [][]byte var vals [][]byte @@ -833,11 +827,11 @@ func TestBloatedProof(t *testing.T) { // noop technically, but practically should be rejected. func TestEmptyValueRangeProof(t *testing.T) { trie, values := randomTrie(512) - var entries entrySlice + var entries []*kv for _, kv := range values { entries = append(entries, kv) } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) // Create a new entry with a slightly modified key mid := len(entries) / 2 @@ -877,11 +871,11 @@ func TestEmptyValueRangeProof(t *testing.T) { // practically should be rejected. func TestAllElementsEmptyValueRangeProof(t *testing.T) { trie, values := randomTrie(512) - var entries entrySlice + var entries []*kv for _, kv := range values { entries = append(entries, kv) } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) // Create a new entry with a slightly modified key mid := len(entries) / 2 @@ -983,11 +977,11 @@ func BenchmarkVerifyRangeProof5000(b *testing.B) { benchmarkVerifyRangeProof(b, func benchmarkVerifyRangeProof(b *testing.B, size int) { trie, vals := randomTrie(8192) - var entries entrySlice + var entries []*kv for _, kv := range vals { entries = append(entries, kv) } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) start := 2 end := start + size @@ -1020,11 +1014,11 @@ func BenchmarkVerifyRangeNoProof1000(b *testing.B) { benchmarkVerifyRangeNoProof func benchmarkVerifyRangeNoProof(b *testing.B, size int) { trie, vals := randomTrie(size) - var entries entrySlice + var entries []*kv for _, kv := range vals { entries = append(entries, kv) } - sort.Sort(entries) + slices.SortFunc(entries, kvLessFunc) var keys [][]byte var values [][]byte diff --git a/trie/trienode/node.go b/trie/trienode/node.go index 8152eab6c0f7..b233f517f040 100644 --- a/trie/trienode/node.go +++ b/trie/trienode/node.go @@ -18,10 +18,10 @@ package trienode import ( "fmt" - "sort" "strings" "github.com/ethereum/go-ethereum/common" + "golang.org/x/exp/slices" ) // Node is a wrapper which contains the encoded blob of the trie node and its @@ -100,12 +100,14 @@ func NewNodeSet(owner common.Hash) *NodeSet { // ForEachWithOrder iterates the nodes with the order from bottom to top, // right to left, nodes with the longest path will be iterated first. func (set *NodeSet) ForEachWithOrder(callback func(path string, n *Node)) { - var paths sort.StringSlice + var paths []string for path := range set.Nodes { paths = append(paths, path) } // Bottom-up, longest path first - sort.Sort(sort.Reverse(paths)) + slices.SortFunc(paths, func(a, b string) bool { + return a > b // Sort in reverse order + }) for _, path := range paths { callback(path, set.Nodes[path].Unwrap()) }