From 91185aedafd08d228be61baf64b5edc4caeff359 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 22 Jun 2023 16:37:15 -0400 Subject: [PATCH] Chore: Use exp/slices and exp/maps to simplify some code (#5479) --- agreement/agreementtest/simulate_test.go | 23 +-- agreement/autopsy.go | 5 +- agreement/common_test.go | 16 +- cmd/algokey/keyreg.go | 6 +- cmd/goal/account.go | 13 +- cmd/opdoc/opdoc.go | 2 +- cmd/tealdbg/local.go | 4 +- crypto/merklearray/merkle.go | 3 +- crypto/merkletrie/cache.go | 11 +- crypto/merkletrie/committer.go | 6 +- crypto/merkletrie/committer_test.go | 5 +- crypto/merkletrie/node.go | 4 +- daemon/algod/api/server/v2/account.go | 4 +- .../algod/api/server/v2/test/handlers_test.go | 5 +- daemon/algod/api/server/v2/utils.go | 4 +- data/basics/teal.go | 29 +-- data/basics/userBalance.go | 7 +- data/transactions/application.go | 7 +- data/transactions/common_test.go | 59 ------ data/transactions/logic/eval.go | 14 +- data/transactions/logic/evalCrypto_test.go | 13 +- data/transactions/logic/opcodes.go | 13 +- data/transactions/logic/opcodes_test.go | 13 +- data/transactions/teal.go | 26 +-- data/transactions/transaction.go | 8 +- go.mod | 1 + go.sum | 2 + ledger/acctupdates.go | 94 --------- ledger/acctupdates_test.go | 184 ------------------ ledger/apply/application_test.go | 44 +---- ledger/apply/asset_test.go | 33 ---- ledger/apply/mockBalances_test.go | 58 ++---- ledger/eval/cow.go | 9 +- ledger/eval/txntracer.go | 32 +-- ledger/ledger.go | 18 -- ledger/ledger_test.go | 69 +------ ledger/ledgercore/statedelta.go | 47 ++--- network/phonebook.go | 4 +- stateproof/builder.go | 10 +- .../upgrades/stateproof_participation_test.go | 3 +- test/reflectionhelpers/helpers.go | 16 +- tools/block-generator/go.mod | 1 + tools/block-generator/go.sum | 2 + util/metrics/tagcounter.go | 5 +- 44 files changed, 141 insertions(+), 791 deletions(-) diff --git a/agreement/agreementtest/simulate_test.go b/agreement/agreementtest/simulate_test.go index 0516592072..53c42411ed 100644 --- a/agreement/agreementtest/simulate_test.go +++ b/agreement/agreementtest/simulate_test.go @@ -27,6 +27,7 @@ import ( "github.com/algorand/go-deadlock" "github.com/stretchr/testify/require" + "golang.org/x/exp/maps" "github.com/algorand/go-algorand/agreement" "github.com/algorand/go-algorand/config" @@ -124,24 +125,10 @@ func makeTestLedger(state map[basics.Address]basics.AccountData) agreement.Ledge func (l *testLedger) copy() *testLedger { dup := new(testLedger) - dup.entries = make(map[basics.Round]bookkeeping.Block) - dup.certs = make(map[basics.Round]agreement.Certificate) - dup.state = make(map[basics.Address]basics.AccountData) - dup.notifications = make(map[basics.Round]signal) - - for k, v := range l.entries { - dup.entries[k] = v - } - for k, v := range l.certs { - dup.certs[k] = v - } - for k, v := range l.state { - dup.state[k] = v - } - for k, v := range dup.notifications { - // note that old opened channels will now fire when these are closed - dup.notifications[k] = v - } + dup.entries = maps.Clone(l.entries) + dup.certs = maps.Clone(l.certs) + dup.state = maps.Clone(l.state) + dup.notifications = maps.Clone(l.notifications) // old opened channels will now fire when these are closed dup.nextRound = l.nextRound return dup diff --git a/agreement/autopsy.go b/agreement/autopsy.go index e940ae4bc9..30f41e13cb 100644 --- a/agreement/autopsy.go +++ b/agreement/autopsy.go @@ -24,6 +24,7 @@ import ( "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/logging" "github.com/algorand/go-algorand/protocol" + "golang.org/x/exp/slices" ) // An Autopsy is a trace of the ordered input events and output @@ -102,9 +103,7 @@ func (m *multiCloser) Close() error { // makeMultiCloser returns a Closer that closes all the given closers. func makeMultiCloser(closers ...io.Closer) io.Closer { - r := make([]io.Closer, len(closers)) - copy(r, closers) - return &multiCloser{r} + return &multiCloser{slices.Clone(closers)} } type autopsyTrace struct { diff --git a/agreement/common_test.go b/agreement/common_test.go index 8f2ad8c4f0..0c11d9553d 100644 --- a/agreement/common_test.go +++ b/agreement/common_test.go @@ -24,6 +24,7 @@ import ( "github.com/algorand/go-deadlock" "github.com/stretchr/testify/require" + "golang.org/x/exp/maps" "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" @@ -207,10 +208,7 @@ func makeTestLedger(state map[basics.Address]basics.AccountData) Ledger { l.certs = make(map[basics.Round]Certificate) l.nextRound = 1 - l.state = make(map[basics.Address]basics.AccountData) - for k, v := range state { - l.state[k] = v - } + l.state = maps.Clone(state) l.notifications = make(map[basics.Round]signal) @@ -226,10 +224,7 @@ func makeTestLedgerWithConsensusVersion(state map[basics.Address]basics.AccountD l.certs = make(map[basics.Round]Certificate) l.nextRound = 1 - l.state = make(map[basics.Address]basics.AccountData) - for k, v := range state { - l.state[k] = v - } + l.state = maps.Clone(state) l.notifications = make(map[basics.Round]signal) @@ -245,10 +240,7 @@ func makeTestLedgerMaxBlocks(state map[basics.Address]basics.AccountData, maxNum l.maxNumBlocks = maxNumBlocks - l.state = make(map[basics.Address]basics.AccountData) - for k, v := range state { - l.state[k] = v - } + l.state = maps.Clone(state) l.notifications = make(map[basics.Round]signal) diff --git a/cmd/algokey/keyreg.go b/cmd/algokey/keyreg.go index 02f18649bc..4393664420 100644 --- a/cmd/algokey/keyreg.go +++ b/cmd/algokey/keyreg.go @@ -24,6 +24,7 @@ import ( "strings" "github.com/spf13/cobra" + "golang.org/x/exp/maps" "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/data/account" @@ -94,10 +95,7 @@ func init() { "betanet": mustConvertB64ToDigest("mFgazF+2uRS1tMiL9dsj01hJGySEmPN28B/TjjvpVW0="), "devnet": mustConvertB64ToDigest("sC3P7e2SdbqKJK0tbiCdK9tdSpbe6XeCGKdoNzmlj0E="), } - validNetworkList = make([]string, 0, len(validNetworks)) - for k := range validNetworks { - validNetworkList = append(validNetworkList, k) - } + validNetworkList = maps.Keys(validNetworks) } func mustConvertB64ToDigest(b64 string) (digest crypto.Digest) { diff --git a/cmd/goal/account.go b/cmd/goal/account.go index 1ca85e5677..77b6cc3c70 100644 --- a/cmd/goal/account.go +++ b/cmd/goal/account.go @@ -27,6 +27,7 @@ import ( "time" "github.com/spf13/cobra" + "golang.org/x/exp/slices" "github.com/algorand/go-algorand/cmd/util/datadir" "github.com/algorand/go-algorand/config" @@ -557,8 +558,7 @@ var infoCmd = &cobra.Command{ func printAccountInfo(client libgoal.Client, address string, onlyShowAssetIds bool, account model.Account) bool { var createdAssets []model.Asset if account.CreatedAssets != nil { - createdAssets = make([]model.Asset, len(*account.CreatedAssets)) - copy(createdAssets, *account.CreatedAssets) + createdAssets = slices.Clone(*account.CreatedAssets) sort.Slice(createdAssets, func(i, j int) bool { return createdAssets[i].Index < createdAssets[j].Index }) @@ -566,8 +566,7 @@ func printAccountInfo(client libgoal.Client, address string, onlyShowAssetIds bo var heldAssets []model.AssetHolding if account.Assets != nil { - heldAssets = make([]model.AssetHolding, len(*account.Assets)) - copy(heldAssets, *account.Assets) + heldAssets = slices.Clone(*account.Assets) sort.Slice(heldAssets, func(i, j int) bool { return heldAssets[i].AssetID < heldAssets[j].AssetID }) @@ -575,8 +574,7 @@ func printAccountInfo(client libgoal.Client, address string, onlyShowAssetIds bo var createdApps []model.Application if account.CreatedApps != nil { - createdApps = make([]model.Application, len(*account.CreatedApps)) - copy(createdApps, *account.CreatedApps) + createdApps = slices.Clone(*account.CreatedApps) sort.Slice(createdApps, func(i, j int) bool { return createdApps[i].Id < createdApps[j].Id }) @@ -584,8 +582,7 @@ func printAccountInfo(client libgoal.Client, address string, onlyShowAssetIds bo var optedInApps []model.ApplicationLocalState if account.AppsLocalState != nil { - optedInApps = make([]model.ApplicationLocalState, len(*account.AppsLocalState)) - copy(optedInApps, *account.AppsLocalState) + optedInApps = slices.Clone(*account.AppsLocalState) sort.Slice(optedInApps, func(i, j int) bool { return optedInApps[i].Id < optedInApps[j].Id }) diff --git a/cmd/opdoc/opdoc.go b/cmd/opdoc/opdoc.go index b5e039a2b8..9c0bbd86bd 100644 --- a/cmd/opdoc/opdoc.go +++ b/cmd/opdoc/opdoc.go @@ -482,7 +482,7 @@ func main() { AVMType: t.AVMType.String(), }) } - sort.Slice(named, func(i, j int) bool { return strings.Compare(named[i].Name, named[j].Name) > 0 }) + sort.Slice(named, func(i, j int) bool { return named[i].Name > named[j].Name }) namedStackTypes := create("named_stack_types.md") namedStackTypesMarkdown(namedStackTypes, named) diff --git a/cmd/tealdbg/local.go b/cmd/tealdbg/local.go index d8125362e2..d3a9e57fce 100644 --- a/cmd/tealdbg/local.go +++ b/cmd/tealdbg/local.go @@ -28,6 +28,7 @@ import ( "github.com/algorand/go-algorand/data/transactions/logic" "github.com/algorand/go-algorand/ledger/apply" "github.com/algorand/go-algorand/protocol" + "golang.org/x/exp/slices" ) func protoFromString(protoString string) (name string, proto config.ConsensusParams, err error) { @@ -190,8 +191,7 @@ func (a *AppState) clone() (b AppState) { b.locals[addr][aid] = tkv.Clone() } } - b.logs = make([]string, len(a.logs)) - copy(b.logs, a.logs) + b.logs = slices.Clone(a.logs) b.innerTxns = cloneInners(a.innerTxns) return } diff --git a/crypto/merklearray/merkle.go b/crypto/merklearray/merkle.go index b4591bf6b7..203d92110e 100644 --- a/crypto/merklearray/merkle.go +++ b/crypto/merklearray/merkle.go @@ -24,6 +24,7 @@ import ( "sort" "github.com/algorand/go-algorand/crypto" + "golang.org/x/exp/slices" ) const ( @@ -223,7 +224,7 @@ func (tree *Tree) Prove(idxs []uint64) (*Proof, error) { idxs = VcIdxs } - sort.Slice(idxs, func(i, j int) bool { return idxs[i] < idxs[j] }) + slices.Sort(idxs) return tree.createProof(idxs) } diff --git a/crypto/merkletrie/cache.go b/crypto/merkletrie/cache.go index 01648d73a6..453e51645e 100644 --- a/crypto/merkletrie/cache.go +++ b/crypto/merkletrie/cache.go @@ -21,7 +21,9 @@ import ( "encoding/binary" "errors" "fmt" - "sort" + + "golang.org/x/exp/maps" + "golang.org/x/exp/slices" ) // storedNodeIdentifier is the "equivalent" of a node-ptr, but oriented around persisting the @@ -446,11 +448,8 @@ func (mtc *merkleTrieCache) reallocatePendingPages(stats *CommitStats) (pagesToC } // create a sorted list of created pages - sortedCreatedPages := make([]uint64, 0, len(createdPages)) - for page := range createdPages { - sortedCreatedPages = append(sortedCreatedPages, page) - } - sort.SliceStable(sortedCreatedPages, func(i, j int) bool { return sortedCreatedPages[i] < sortedCreatedPages[j] }) + sortedCreatedPages := maps.Keys(createdPages) + slices.Sort(sortedCreatedPages) mtc.reallocatedPages = make(map[uint64]map[storedNodeIdentifier]*node) diff --git a/crypto/merkletrie/committer.go b/crypto/merkletrie/committer.go index 5e5ae758ab..305ce1f663 100644 --- a/crypto/merkletrie/committer.go +++ b/crypto/merkletrie/committer.go @@ -16,6 +16,8 @@ package merkletrie +import "golang.org/x/exp/slices" + // Committer is the interface supporting serializing tries into persistent storage. type Committer interface { StorePage(page uint64, content []byte) error @@ -40,9 +42,7 @@ func (mc *InMemoryCommitter) StorePage(page uint64, content []byte) error { if content == nil { delete(mc.memStore, page) } else { - storedContent := make([]byte, len(content)) - copy(storedContent, content) - mc.memStore[page] = storedContent + mc.memStore[page] = slices.Clone(content) } return nil } diff --git a/crypto/merkletrie/committer_test.go b/crypto/merkletrie/committer_test.go index c8bfd71435..317261f727 100644 --- a/crypto/merkletrie/committer_test.go +++ b/crypto/merkletrie/committer_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "golang.org/x/exp/slices" "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/test/partitiontest" @@ -33,9 +34,7 @@ func (mc *InMemoryCommitter) Duplicate(flat bool) (out *InMemoryCommitter) { if flat { out.memStore[k] = v } else { - bytes := make([]byte, len(v)) - copy(bytes[:], v[:]) - out.memStore[k] = bytes + out.memStore[k] = slices.Clone(v) } } return diff --git a/crypto/merkletrie/node.go b/crypto/merkletrie/node.go index de765793dd..972ef0e1e9 100644 --- a/crypto/merkletrie/node.go +++ b/crypto/merkletrie/node.go @@ -23,6 +23,7 @@ import ( "unsafe" "github.com/algorand/go-algorand/crypto" + "golang.org/x/exp/slices" ) type childEntry struct { @@ -339,8 +340,7 @@ func deserializeNode(buf []byte) (n *node, s int) { if hashLength2 <= 0 { return nil, hashLength2 } - n.hash = make([]byte, hashLength) - copy(n.hash, buf[hashLength2:hashLength2+int(hashLength)]) + n.hash = slices.Clone(buf[hashLength2 : hashLength2+int(hashLength)]) s = hashLength2 + int(hashLength) isLeaf := (buf[s] == 0) s++ diff --git a/daemon/algod/api/server/v2/account.go b/daemon/algod/api/server/v2/account.go index 9c25021b9c..f5bf773028 100644 --- a/daemon/algod/api/server/v2/account.go +++ b/daemon/algod/api/server/v2/account.go @@ -26,6 +26,7 @@ import ( "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/model" "github.com/algorand/go-algorand/data/basics" + "golang.org/x/exp/slices" ) // AssetHolding converts between basics.AssetHolding and model.AssetHolding @@ -477,8 +478,7 @@ func AssetParamsToAsset(creator string, idx basics.AssetIndex, params *basics.As Reserve: addrOrNil(params.Reserve), } if params.MetadataHash != ([32]byte{}) { - metadataHash := make([]byte, len(params.MetadataHash)) - copy(metadataHash, params.MetadataHash[:]) + metadataHash := slices.Clone(params.MetadataHash[:]) assetParams.MetadataHash = &metadataHash } diff --git a/daemon/algod/api/server/v2/test/handlers_test.go b/daemon/algod/api/server/v2/test/handlers_test.go index a4625d41c0..a56577d549 100644 --- a/daemon/algod/api/server/v2/test/handlers_test.go +++ b/daemon/algod/api/server/v2/test/handlers_test.go @@ -34,6 +34,8 @@ import ( "github.com/algorand/go-algorand/daemon/algod/api/server" "github.com/algorand/go-algorand/ledger/eval" "github.com/algorand/go-algorand/ledger/ledgercore" + "golang.org/x/exp/slices" + "github.com/labstack/echo/v4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -1029,8 +1031,7 @@ int 1`, var expectedFailedAt *[]uint64 if len(scenario.FailedAt) != 0 { - clone := make([]uint64, len(scenario.FailedAt)) - copy(clone, scenario.FailedAt) + clone := slices.Clone(scenario.FailedAt) clone[0]++ expectedFailedAt = &clone } diff --git a/daemon/algod/api/server/v2/utils.go b/daemon/algod/api/server/v2/utils.go index 07f06221f5..77d40089c4 100644 --- a/daemon/algod/api/server/v2/utils.go +++ b/daemon/algod/api/server/v2/utils.go @@ -26,6 +26,7 @@ import ( "github.com/algorand/go-codec/codec" "github.com/labstack/echo/v4" + "golang.org/x/exp/slices" "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/model" "github.com/algorand/go-algorand/data/basics" @@ -431,8 +432,7 @@ func convertTxnGroupResult(txnGroupResult simulation.TxnGroupResult) PreEncodedS } if len(txnGroupResult.FailedAt) > 0 { - failedAt := make([]uint64, len(txnGroupResult.FailedAt)) - copy(failedAt, txnGroupResult.FailedAt) + failedAt := slices.Clone[[]uint64, uint64](txnGroupResult.FailedAt) encoded.FailedAt = &failedAt } diff --git a/data/basics/teal.go b/data/basics/teal.go index 0898c2883b..299fd5c52e 100644 --- a/data/basics/teal.go +++ b/data/basics/teal.go @@ -21,6 +21,7 @@ import ( "fmt" "github.com/algorand/go-algorand/config" + "golang.org/x/exp/maps" ) // DeltaAction is an enum of actions that may be performed when applying a @@ -77,24 +78,7 @@ type StateDelta map[string]ValueDelta // equality because an empty map will encode/decode as nil. So if our generated // map is empty but not nil, we want to equal a decoded nil off the wire. func (sd StateDelta) Equal(o StateDelta) bool { - // Lengths should be the same - if len(sd) != len(o) { - return false - } - // All keys and deltas should be the same - for k, v := range sd { - // Other StateDelta must contain key - ov, ok := o[k] - if !ok { - return false - } - - // Other StateDelta must have same value for key - if ov != v { - return false - } - } - return true + return maps.Equal(sd, o) } // Valid checks whether the keys and values in a StateDelta conform to the @@ -234,14 +218,7 @@ type TealKeyValue map[string]TealValue // Clone returns a copy of a TealKeyValue that may be modified without // affecting the original func (tk TealKeyValue) Clone() TealKeyValue { - if tk == nil { - return nil - } - res := make(TealKeyValue, len(tk)) - for k, v := range tk { - res[k] = v - } - return res + return maps.Clone(tk) } // ToStateSchema calculates the number of each value type in a TealKeyValue and diff --git a/data/basics/userBalance.go b/data/basics/userBalance.go index 7ca18566b9..2d696c50d8 100644 --- a/data/basics/userBalance.go +++ b/data/basics/userBalance.go @@ -26,6 +26,7 @@ import ( "github.com/algorand/go-algorand/crypto/merklesignature" "github.com/algorand/go-algorand/logging" "github.com/algorand/go-algorand/protocol" + "golang.org/x/exp/slices" ) // Status is the delegation status of an account's MicroAlgos @@ -268,10 +269,8 @@ type StateSchemas struct { // affecting the original func (ap *AppParams) Clone() (res AppParams) { res = *ap - res.ApprovalProgram = make([]byte, len(ap.ApprovalProgram)) - copy(res.ApprovalProgram, ap.ApprovalProgram) - res.ClearStateProgram = make([]byte, len(ap.ClearStateProgram)) - copy(res.ClearStateProgram, ap.ClearStateProgram) + res.ApprovalProgram = slices.Clone(ap.ApprovalProgram) + res.ClearStateProgram = slices.Clone(ap.ClearStateProgram) res.GlobalState = ap.GlobalState.Clone() return } diff --git a/data/transactions/application.go b/data/transactions/application.go index 6a319b8c07..241e99193e 100644 --- a/data/transactions/application.go +++ b/data/transactions/application.go @@ -20,6 +20,7 @@ import ( "fmt" "github.com/algorand/go-algorand/data/basics" + "golang.org/x/exp/slices" ) const ( @@ -248,10 +249,8 @@ func (ac *ApplicationCallTxnFields) IndexByAddress(target basics.Address, sender } // Otherwise we index into ac.Accounts - for idx, addr := range ac.Accounts { - if target == addr { - return uint64(idx) + 1, nil - } + if idx := slices.Index(ac.Accounts, target); idx != -1 { + return uint64(idx) + 1, nil } return 0, fmt.Errorf("invalid Account reference %s", target) diff --git a/data/transactions/common_test.go b/data/transactions/common_test.go index 0509241e60..9ad00816a5 100644 --- a/data/transactions/common_test.go +++ b/data/transactions/common_test.go @@ -17,7 +17,6 @@ package transactions import ( - "errors" "math/rand" "github.com/algorand/go-algorand/config" @@ -26,64 +25,6 @@ import ( "github.com/algorand/go-algorand/protocol" ) -var poolAddr = basics.Address{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} - -// BalanceMap is a simple implementation of the balances interface. -type BalanceMap map[basics.Address]basics.BalanceRecord - -func (b BalanceMap) Move(src, dst basics.Address, amount basics.MicroAlgos) error { - var overflowed bool - var tmp basics.MicroAlgos - srcBal, ok := b[src] - if !ok { - return errors.New("Move() called with src not in tx.RelevantAddrs") - } - tmp, overflowed = basics.OSubA(srcBal.MicroAlgos, amount) - if overflowed { - return errors.New("Move(): sender overspent") - } - srcBal.MicroAlgos = tmp - b[src] = srcBal - - dstBal, ok := b[dst] - if !ok { - return errors.New("Move() called with dst not in tx.RelevantAddrs") - } - tmp, overflowed = basics.OAddA(dstBal.MicroAlgos, amount) - if overflowed { - return errors.New("Move(): recipient balance overflowed") - } - dstBal.MicroAlgos = tmp - b[dst] = dstBal - - return nil -} - -func (b BalanceMap) Get(addr basics.Address) (basics.BalanceRecord, error) { - record, ok := b[addr] - if !ok { - return basics.BalanceRecord{}, errors.New("Get() called on an address not in tx.RelevantAddrs") - } - return record, nil -} - -func (b BalanceMap) Put(record basics.BalanceRecord) error { - if _, ok := b[record.Addr]; !ok { - return errors.New("Put() called on an account whose address was not in tx.RelevantAddrs") - } - b[record.Addr] = record - return nil -} - -// set up a BalanceMap for a transaction containing only the transactions RelevantAddrs. -func makeTestBalancesForTransaction(tx Transaction) BalanceMap { - bals := make(BalanceMap) - for _, addr := range tx.RelevantAddrs(SpecialAddresses{RewardsPool: poolAddr}) { - bals[addr] = basics.BalanceRecord{Addr: addr} - } - return bals -} - func generateTestObjects(numTxs, numAccs int) ([]Transaction, []SignedTxn, []*crypto.SignatureSecrets, []basics.Address) { txs := make([]Transaction, numTxs) signed := make([]SignedTxn, numTxs) diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 6c6407b6fd..6feccae243 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -35,6 +35,7 @@ import ( "strings" "golang.org/x/crypto/sha3" + "golang.org/x/exp/slices" "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" @@ -5291,8 +5292,7 @@ func (cx *EvalContext) stackIntoTxnField(sv stackValue, fs *txnFieldSpec, txn *t if len(sv.Bytes) > cx.Proto.MaxTxnNoteBytes { return fmt.Errorf("%s may not exceed %d bytes", fs.field, cx.Proto.MaxTxnNoteBytes) } - txn.Note = make([]byte, len(sv.Bytes)) - copy(txn.Note, sv.Bytes) + txn.Note = slices.Clone(sv.Bytes) // GenesisID, GenesisHash unsettable: surely makes no sense // Group unsettable: Can't make groups from AVM (yet?) // Lease unsettable: This seems potentially useful. @@ -5406,9 +5406,7 @@ func (cx *EvalContext) stackIntoTxnField(sv stackValue, fs *txnFieldSpec, txn *t if len(txn.ApplicationArgs) >= cx.Proto.MaxAppArgs { return errors.New("too many application args") } - new := make([]byte, len(sv.Bytes)) - copy(new, sv.Bytes) - txn.ApplicationArgs = append(txn.ApplicationArgs, new) + txn.ApplicationArgs = append(txn.ApplicationArgs, slices.Clone(sv.Bytes)) case Accounts: var new basics.Address new, err = cx.assignAccount(sv) @@ -5424,15 +5422,13 @@ func (cx *EvalContext) stackIntoTxnField(sv stackValue, fs *txnFieldSpec, txn *t if len(sv.Bytes) > maxPossible { return fmt.Errorf("%s may not exceed %d bytes", fs.field, maxPossible) } - txn.ApprovalProgram = make([]byte, len(sv.Bytes)) - copy(txn.ApprovalProgram, sv.Bytes) + txn.ApprovalProgram = slices.Clone(sv.Bytes) case ClearStateProgram: maxPossible := cx.Proto.MaxAppProgramLen * (1 + cx.Proto.MaxExtraAppProgramPages) if len(sv.Bytes) > maxPossible { return fmt.Errorf("%s may not exceed %d bytes", fs.field, maxPossible) } - txn.ClearStateProgram = make([]byte, len(sv.Bytes)) - copy(txn.ClearStateProgram, sv.Bytes) + txn.ClearStateProgram = slices.Clone(sv.Bytes) case ApprovalProgramPages: maxPossible := cx.Proto.MaxAppProgramLen * (1 + cx.Proto.MaxExtraAppProgramPages) txn.ApprovalProgram = append(txn.ApprovalProgram, sv.Bytes...) diff --git a/data/transactions/logic/evalCrypto_test.go b/data/transactions/logic/evalCrypto_test.go index 65f0787a97..766fe66baa 100644 --- a/data/transactions/logic/evalCrypto_test.go +++ b/data/transactions/logic/evalCrypto_test.go @@ -31,6 +31,7 @@ import ( "github.com/consensys/gnark-crypto/ecc/bn254" "github.com/stretchr/testify/require" + "golang.org/x/exp/slices" "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/crypto/secp256k1" @@ -331,8 +332,7 @@ load 0 byte 0x%s == &&` - pkTampered1 := make([]byte, len(pk)) - copy(pkTampered1, pk) + pkTampered1 := slices.Clone(pk) pkTampered1[0] = 0 // first byte is a prefix of either 0x02 or 0x03 pkTampered2 := make([]byte, len(pk)-1) // must be 33 bytes length copy(pkTampered2, pk) @@ -378,8 +378,7 @@ ecdsa_verify Secp256k1 s := sign[32:64] v := int(sign[64]) - rTampered := make([]byte, len(r)) - copy(rTampered, r) + rTampered := slices.Clone(r) rTampered[0] += byte(1) // intentional overflow var verifyTests = []struct { @@ -487,8 +486,7 @@ load 0 byte 0x%s == &&` - pkTampered1 := make([]byte, len(pk)) - copy(pkTampered1, pk) + pkTampered1 := slices.Clone(pk) pkTampered1[0] = 0 // first byte is a prefix of either 0x02 or 0x03 pkTampered2 := make([]byte, len(pk)-1) // must be 33 bytes length copy(pkTampered2, pk) @@ -533,8 +531,7 @@ ecdsa_verify Secp256r1 r := ri.Bytes() s := si.Bytes() - rTampered := make([]byte, len(r)) - copy(rTampered, r) + rTampered := slices.Clone(r) rTampered[0] += byte(1) // intentional overflow var verifyTests = []struct { diff --git a/data/transactions/logic/opcodes.go b/data/transactions/logic/opcodes.go index 028ceecbdc..6410c8088d 100644 --- a/data/transactions/logic/opcodes.go +++ b/data/transactions/logic/opcodes.go @@ -21,6 +21,8 @@ import ( "sort" "strconv" "strings" + + "golang.org/x/exp/maps" ) // LogicVersion defines default assembler and max eval versions @@ -707,10 +709,7 @@ func OpcodesByVersion(version uint64) []OpSpec { } } } - result := make([]OpSpec, 0, len(subv)) - for _, v := range subv { - result = append(result, v) - } + result := maps.Values(subv) sort.Sort(sortByOpcode(result)) return result } @@ -749,12 +748,8 @@ func init() { // Start from v2 and higher, // copy lower version opcodes and overwrite matching version for v := uint64(2); v <= evalMaxVersion; v++ { - OpsByName[v] = make(map[string]OpSpec, 256) - // Copy opcodes from lower version - for opName, oi := range OpsByName[v-1] { - OpsByName[v][opName] = oi - } + OpsByName[v] = maps.Clone(OpsByName[v-1]) for op, oi := range opsByOpcode[v-1] { opsByOpcode[v][op] = oi } diff --git a/data/transactions/logic/opcodes_test.go b/data/transactions/logic/opcodes_test.go index 2ce3648a69..df69bbf708 100644 --- a/data/transactions/logic/opcodes_test.go +++ b/data/transactions/logic/opcodes_test.go @@ -23,6 +23,7 @@ import ( "github.com/algorand/go-algorand/test/partitiontest" "github.com/stretchr/testify/require" + "golang.org/x/exp/slices" ) func TestOpSpecs(t *testing.T) { @@ -62,11 +63,7 @@ func TestOpcodesByVersionReordered(t *testing.T) { // nolint:paralleltest // man partitiontest.PartitionTest(t) // Make a copy to restore to the original - OpSpecsOrig := make([]OpSpec, len(OpSpecs)) - for idx, opspec := range OpSpecs { - cp := opspec - OpSpecsOrig[idx] = cp - } + OpSpecsOrig := slices.Clone(OpSpecs) defer func() { OpSpecs = OpSpecsOrig }() @@ -88,11 +85,7 @@ func TestOpcodesByVersion(t *testing.T) { func testOpcodesByVersion(t *testing.T) { // Make a copy of the OpSpecs to check if OpcodesByVersion will change it - OpSpecs2 := make([]OpSpec, len(OpSpecs)) - for idx, opspec := range OpSpecs { - cp := opspec - OpSpecs2[idx] = cp - } + OpSpecs2 := slices.Clone(OpSpecs) opSpecs := make([][]OpSpec, LogicVersion) for v := uint64(1); v <= LogicVersion; v++ { diff --git a/data/transactions/teal.go b/data/transactions/teal.go index 37388a0d9f..d0432bde8e 100644 --- a/data/transactions/teal.go +++ b/data/transactions/teal.go @@ -21,6 +21,8 @@ import ( "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/protocol" + "golang.org/x/exp/maps" + "golang.org/x/exp/slices" ) // EvalDelta stores StateDeltas for an application's global key/value store, as @@ -50,39 +52,19 @@ type EvalDelta struct { // because the msgpack codec will encode/decode an empty map as nil, and we want // an empty generated EvalDelta to equal an empty one we decode off the wire. func (ed EvalDelta) Equal(o EvalDelta) bool { - // LocalDeltas length should be the same - if len(ed.LocalDeltas) != len(o.LocalDeltas) { + if !maps.EqualFunc(ed.LocalDeltas, o.LocalDeltas, maps.Equal[basics.StateDelta, basics.StateDelta]) { return false } - // All keys and local StateDeltas should be the same - for k, v := range ed.LocalDeltas { - // Other LocalDelta must have value for key - ov, ok := o.LocalDeltas[k] - if !ok { - return false - } - - // Other LocalDelta must have same value for key - if !ov.Equal(v) { - return false - } - } - // GlobalDeltas must be equal if !ed.GlobalDelta.Equal(o.GlobalDelta) { return false } // Logs must be equal - if len(ed.Logs) != len(o.Logs) { + if !slices.Equal(ed.Logs, o.Logs) { return false } - for i, l := range ed.Logs { - if l != o.Logs[i] { - return false - } - } // InnerTxns must be equal if len(ed.InnerTxns) != len(o.InnerTxns) { diff --git a/data/transactions/transaction.go b/data/transactions/transaction.go index 5a397dc7cd..448a78e297 100644 --- a/data/transactions/transaction.go +++ b/data/transactions/transaction.go @@ -26,6 +26,7 @@ import ( "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/protocol" + "golang.org/x/exp/slices" ) // Txid is a hash used to uniquely identify individual transactions @@ -272,12 +273,7 @@ func (tx Header) Alive(tc TxnContext) error { // MatchAddress checks if the transaction touches a given address. func (tx Transaction) MatchAddress(addr basics.Address, spec SpecialAddresses) bool { - for _, candidate := range tx.RelevantAddrs(spec) { - if addr == candidate { - return true - } - } - return false + return slices.Contains(tx.RelevantAddrs(spec), addr) } var errKeyregTxnFirstVotingRoundGreaterThanLastVotingRound = errors.New("transaction first voting round need to be less than its last voting round") diff --git a/go.mod b/go.mod index f80f6098d3..286c42a1c0 100644 --- a/go.mod +++ b/go.mod @@ -34,6 +34,7 @@ require ( github.com/spf13/cobra v1.3.0 github.com/stretchr/testify v1.8.1 golang.org/x/crypto v0.1.0 + golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 golang.org/x/sys v0.7.0 golang.org/x/text v0.9.0 gopkg.in/sohlich/elogrus.v3 v3.0.0-20180410122755-1fa29e2f2009 diff --git a/go.sum b/go.sum index fea5829a03..471f83fd90 100644 --- a/go.sum +++ b/go.sum @@ -482,6 +482,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= diff --git a/ledger/acctupdates.go b/ledger/acctupdates.go index d2850a51a8..bb495f3f8f 100644 --- a/ledger/acctupdates.go +++ b/ledger/acctupdates.go @@ -586,100 +586,6 @@ func (au *accountUpdates) LookupWithoutRewards(rnd basics.Round, addr basics.Add return } -// ListAssets lists the assets by their asset index, limiting to the first maxResults -func (au *accountUpdates) ListAssets(maxAssetIdx basics.AssetIndex, maxResults uint64) ([]basics.CreatableLocator, error) { - return au.listCreatables(basics.CreatableIndex(maxAssetIdx), maxResults, basics.AssetCreatable) -} - -// ListApplications lists the application by their app index, limiting to the first maxResults -func (au *accountUpdates) ListApplications(maxAppIdx basics.AppIndex, maxResults uint64) ([]basics.CreatableLocator, error) { - return au.listCreatables(basics.CreatableIndex(maxAppIdx), maxResults, basics.AppCreatable) -} - -// listCreatables lists the application/asset by their app/asset index, limiting to the first maxResults -func (au *accountUpdates) listCreatables(maxCreatableIdx basics.CreatableIndex, maxResults uint64, ctype basics.CreatableType) ([]basics.CreatableLocator, error) { - au.accountsMu.RLock() - for { - currentDbRound := au.cachedDBRound - currentDeltaLen := len(au.deltas) - // Sort indices for creatables that have been created/deleted. If this - // turns out to be too inefficient, we could keep around a heap of - // created/deleted asset indices in memory. - keys := make([]basics.CreatableIndex, 0, len(au.creatables)) - for cidx, delta := range au.creatables { - if delta.Ctype != ctype { - continue - } - if cidx <= maxCreatableIdx { - keys = append(keys, cidx) - } - } - sort.Slice(keys, func(i, j int) bool { return keys[i] > keys[j] }) - - // Check for creatables that haven't been synced to disk yet. - unsyncedCreatables := make([]basics.CreatableLocator, 0, len(keys)) - deletedCreatables := make(map[basics.CreatableIndex]bool, len(keys)) - for _, cidx := range keys { - delta := au.creatables[cidx] - if delta.Created { - // Created but only exists in memory - unsyncedCreatables = append(unsyncedCreatables, basics.CreatableLocator{ - Type: delta.Ctype, - Index: cidx, - Creator: delta.Creator, - }) - } else { - // Mark deleted creatables for exclusion from the results set - deletedCreatables[cidx] = true - } - } - - au.accountsMu.RUnlock() - - // Check in-memory created creatables, which will always be newer than anything - // in the database - if uint64(len(unsyncedCreatables)) >= maxResults { - return unsyncedCreatables[:maxResults], nil - } - res := unsyncedCreatables - - // Fetch up to maxResults - len(res) + len(deletedCreatables) from the database, - // so we have enough extras in case creatables were deleted - numToFetch := maxResults - uint64(len(res)) + uint64(len(deletedCreatables)) - dbResults, dbRound, err := au.accountsq.ListCreatables(maxCreatableIdx, numToFetch, ctype) - if err != nil { - return nil, err - } - - if dbRound == currentDbRound { - // Now we merge the database results with the in-memory results - for _, loc := range dbResults { - // Check if we have enough results - if uint64(len(res)) == maxResults { - return res, nil - } - - // Creatable was deleted - if _, ok := deletedCreatables[loc.Index]; ok { - continue - } - - // We're OK to include this result - res = append(res, loc) - } - return res, nil - } - if dbRound < currentDbRound { - au.log.Errorf("listCreatables: database round %d is behind in-memory round %d", dbRound, currentDbRound) - return []basics.CreatableLocator{}, &StaleDatabaseRoundError{databaseRound: dbRound, memoryRound: currentDbRound} - } - au.accountsMu.RLock() - for currentDbRound >= au.cachedDBRound && currentDeltaLen == len(au.deltas) { - au.accountsReadCond.Wait() - } - } -} - // GetCreatorForRound returns the creator for a given asset/app index at a given round func (au *accountUpdates) GetCreatorForRound(rnd basics.Round, cidx basics.CreatableIndex, ctype basics.CreatableType) (creator basics.Address, ok bool, err error) { return au.getCreatorForRound(rnd, cidx, ctype, true /* take the lock */) diff --git a/ledger/acctupdates_test.go b/ledger/acctupdates_test.go index 013ae36f5e..d938b0d530 100644 --- a/ledger/acctupdates_test.go +++ b/ledger/acctupdates_test.go @@ -913,190 +913,6 @@ func testAcctUpdatesUpdatesCorrectness(t *testing.T, cfg config.Local) { t.Run("DiskDB", testFunction) } -// listAndCompareComb lists the assets/applications and then compares against the expected -// It repeats with different combinations of the limit parameters -func listAndCompareComb(t *testing.T, au *accountUpdates, expected map[basics.CreatableIndex]ledgercore.ModifiedCreatable) { - - // test configuration parameters - - // pick the second largest index for the app and asset - // This is to make sure exactly one element is left out - // as a result of max index - maxAss1 := basics.CreatableIndex(0) - maxAss2 := basics.CreatableIndex(0) - maxApp1 := basics.CreatableIndex(0) - maxApp2 := basics.CreatableIndex(0) - for a, b := range expected { - // A moving window of the last two largest indexes: [maxAss1, maxAss2] - if b.Ctype == basics.AssetCreatable { - if maxAss2 < a { - maxAss1 = maxAss2 - maxAss2 = a - } else if maxAss1 < a { - maxAss1 = a - } - } - if b.Ctype == basics.AppCreatable { - if maxApp2 < a { - maxApp1 = maxApp2 - maxApp2 = a - } else if maxApp1 < a { - maxApp1 = a - } - } - } - - // No limits. max asset index, max app index and max results have no effect - // This is to make sure the deleted elements do not show up - maxAssetIdx := basics.AssetIndex(maxAss2) - maxAppIdx := basics.AppIndex(maxApp2) - maxResults := uint64(len(expected)) - listAndCompare(t, maxAssetIdx, maxAppIdx, maxResults, au, expected) - - // Limit with max asset index and max app index (max results has no effect) - maxAssetIdx = basics.AssetIndex(maxAss1) - maxAppIdx = basics.AppIndex(maxApp1) - maxResults = uint64(len(expected)) - listAndCompare(t, maxAssetIdx, maxAppIdx, maxResults, au, expected) - - // Limit with max results - maxResults = 1 - listAndCompare(t, maxAssetIdx, maxAppIdx, maxResults, au, expected) -} - -// listAndCompareComb lists the assets/applications and then compares against the expected -// It uses the provided limit parameters -func listAndCompare(t *testing.T, - maxAssetIdx basics.AssetIndex, - maxAppIdx basics.AppIndex, - maxResults uint64, - au *accountUpdates, - expected map[basics.CreatableIndex]ledgercore.ModifiedCreatable) { - - // get the results with the given parameters - assetRes, err := au.ListAssets(maxAssetIdx, maxResults) - require.NoError(t, err) - appRes, err := au.ListApplications(maxAppIdx, maxResults) - require.NoError(t, err) - - // count the expected number of results - expectedAssetCount := uint64(0) - expectedAppCount := uint64(0) - for a, b := range expected { - if b.Created { - if b.Ctype == basics.AssetCreatable && - a <= basics.CreatableIndex(maxAssetIdx) && - expectedAssetCount < maxResults { - expectedAssetCount++ - } - if b.Ctype == basics.AppCreatable && - a <= basics.CreatableIndex(maxAppIdx) && - expectedAppCount < maxResults { - expectedAppCount++ - } - } - } - - // check the total counts are as expected - require.Equal(t, int(expectedAssetCount), len(assetRes)) - require.Equal(t, int(expectedAppCount), len(appRes)) - - // verify the results are correct - for _, respCrtor := range assetRes { - crtor := expected[respCrtor.Index] - require.NotNil(t, crtor) - require.Equal(t, basics.AssetCreatable, crtor.Ctype) - require.Equal(t, true, crtor.Created) - - require.Equal(t, basics.AssetCreatable, respCrtor.Type) - require.Equal(t, crtor.Creator, respCrtor.Creator) - } - for _, respCrtor := range appRes { - crtor := expected[respCrtor.Index] - require.NotNil(t, crtor) - require.Equal(t, basics.AppCreatable, crtor.Ctype) - require.Equal(t, true, crtor.Created) - - require.Equal(t, basics.AppCreatable, respCrtor.Type) - require.Equal(t, crtor.Creator, respCrtor.Creator) - } -} - -// TestListCreatables tests ListAssets and ListApplications -// It tests with all elements in cache, all synced to database, and combination of both -// It also tests the max results, max app index and max asset index -func TestListCreatables(t *testing.T) { - partitiontest.PartitionTest(t) - - // test configuration parameters - numElementsPerSegement := 25 - - // set up the database - dbs, _ := sqlitedriver.OpenForTesting(t, true) - defer dbs.Close() - - proto := config.Consensus[protocol.ConsensusCurrentVersion] - - err := dbs.Transaction(func(ctx context.Context, tx trackerdb.TransactionScope) (err error) { - accts := make(map[basics.Address]basics.AccountData) - _ = tx.Testing().AccountsInitTest(t, accts, protocol.ConsensusCurrentVersion) - return - }) - require.NoError(t, err) - - au := &accountUpdates{} - au.accountsq, err = dbs.MakeAccountsOptimizedReader() - require.NoError(t, err) - - // ******* All results are obtained from the cache. Empty database ******* - // ******* No deletes ******* - // get random data. Initial batch, no deletes - ctbsList, randomCtbs := randomCreatables(numElementsPerSegement) - expectedDbImage := make(map[basics.CreatableIndex]ledgercore.ModifiedCreatable) - ctbsWithDeletes := randomCreatableSampling(1, ctbsList, randomCtbs, - expectedDbImage, numElementsPerSegement) - // set the cache - au.creatables = ctbsWithDeletes - listAndCompareComb(t, au, expectedDbImage) - - // ******* All results are obtained from the database. Empty cache ******* - // ******* No deletes ******* - // sync with the database - var updates compactAccountDeltas - var resUpdates compactResourcesDeltas - err = dbs.Transaction(func(ctx context.Context, tx trackerdb.TransactionScope) (err error) { - _, _, _, err = accountsNewRound(tx, updates, resUpdates, nil, ctbsWithDeletes, proto, basics.Round(1)) - require.NoError(t, err) - return - }) - require.NoError(t, err) - - // nothing left in cache - au.creatables = make(map[basics.CreatableIndex]ledgercore.ModifiedCreatable) - listAndCompareComb(t, au, expectedDbImage) - - // ******* Results are obtained from the database and from the cache ******* - // ******* No deletes in the database. ******* - // ******* Data in the database deleted in the cache ******* - au.creatables = randomCreatableSampling(2, ctbsList, randomCtbs, - expectedDbImage, numElementsPerSegement) - listAndCompareComb(t, au, expectedDbImage) - - // ******* Results are obtained from the database and from the cache ******* - // ******* Deletes are in the database and in the cache ******* - // sync with the database. This has deletes synced to the database. - err = dbs.Transaction(func(ctx context.Context, tx trackerdb.TransactionScope) (err error) { - _, _, _, err = accountsNewRound(tx, updates, resUpdates, nil, au.creatables, proto, basics.Round(1)) - require.NoError(t, err) - return - }) - require.NoError(t, err) - // get new creatables in the cache. There will be deleted in the cache from the previous batch. - au.creatables = randomCreatableSampling(3, ctbsList, randomCtbs, - expectedDbImage, numElementsPerSegement) - listAndCompareComb(t, au, expectedDbImage) -} - func TestBoxNamesByAppIDs(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() diff --git a/ledger/apply/application_test.go b/ledger/apply/application_test.go index 37c132c135..0e6a1ff9a2 100644 --- a/ledger/apply/application_test.go +++ b/ledger/apply/application_test.go @@ -22,6 +22,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "golang.org/x/exp/maps" "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" @@ -33,28 +34,6 @@ import ( "github.com/algorand/go-algorand/test/partitiontest" ) -// Allocate the map of basics.AppParams if it is nil, and return a copy. We do *not* -// call clone on each basics.AppParams -- callers must do that for any values where -// they intend to modify a contained reference type. -func cloneAppParams(m map[basics.AppIndex]basics.AppParams) map[basics.AppIndex]basics.AppParams { - res := make(map[basics.AppIndex]basics.AppParams, len(m)) - for k, v := range m { - res[k] = v - } - return res -} - -// Allocate the map of LocalStates if it is nil, and return a copy. We do *not* -// call clone on each AppLocalState -- callers must do that for any values -// where they intend to modify a contained reference type. -func cloneAppLocalStates(m map[basics.AppIndex]basics.AppLocalState) map[basics.AppIndex]basics.AppLocalState { - res := make(map[basics.AppIndex]basics.AppLocalState, len(m)) - for k, v := range m { - res[k] = v - } - return res -} - func TestApplicationCallFieldsEmpty(t *testing.T) { partitiontest.PartitionTest(t) @@ -349,20 +328,6 @@ func (b *testBalances) SetParams(params config.ConsensusParams) { b.proto = params } -func TestAppCallCloneEmpty(t *testing.T) { - partitiontest.PartitionTest(t) - - a := require.New(t) - - var ls map[basics.AppIndex]basics.AppLocalState - cls := cloneAppLocalStates(ls) - a.Zero(len(cls)) - - var ap map[basics.AppIndex]basics.AppParams - cap := cloneAppParams(ap) - a.Zero(len(cap)) -} - func TestAppCallGetParam(t *testing.T) { partitiontest.PartitionTest(t) @@ -560,8 +525,8 @@ func TestAppCallApplyCreate(t *testing.T) { // now we give the creator the app params again cp := basics.AccountData{} - cp.AppParams = cloneAppParams(saved.AppParams) - cp.AppLocalStates = cloneAppLocalStates(saved.AppLocalStates) + cp.AppParams = maps.Clone(saved.AppParams) + cp.AppLocalStates = maps.Clone(saved.AppLocalStates) b.balances[creator] = cp err = ApplicationCall(ac, h, b, ad, 0, &ep, txnCounter) a.Error(err) @@ -571,9 +536,6 @@ func TestAppCallApplyCreate(t *testing.T) { a.Zero(b.putAppParams) // ensure original balance record in the mock was not changed // this ensure proper cloning and any in-intended in-memory modifications - // - // known artefact of cloning AppLocalState even with empty update, nil map vs empty map - saved.AppLocalStates = map[basics.AppIndex]basics.AppLocalState{} a.Equal(saved, b.balances[creator]) saved = b.putBalances[creator] diff --git a/ledger/apply/asset_test.go b/ledger/apply/asset_test.go index 2b7908ac64..802c692c82 100644 --- a/ledger/apply/asset_test.go +++ b/ledger/apply/asset_test.go @@ -17,7 +17,6 @@ package apply import ( - "math/rand" "testing" "github.com/stretchr/testify/require" @@ -29,22 +28,6 @@ import ( "github.com/algorand/go-algorand/test/partitiontest" ) -func cloneAssetHoldings(m map[basics.AssetIndex]basics.AssetHolding) map[basics.AssetIndex]basics.AssetHolding { - res := make(map[basics.AssetIndex]basics.AssetHolding, len(m)) - for id, val := range m { - res[id] = val - } - return res -} - -func cloneAssetParams(m map[basics.AssetIndex]basics.AssetParams) map[basics.AssetIndex]basics.AssetParams { - res := make(map[basics.AssetIndex]basics.AssetParams, len(m)) - for id, val := range m { - res[id] = val - } - return res -} - func TestAssetTransfer(t *testing.T) { partitiontest.PartitionTest(t) @@ -117,19 +100,3 @@ func TestAssetTransfer(t *testing.T) { require.Equal(t, dstAmount-toSend, addrs[cls].Assets[1].Amount) } } - -var benchTotal int = 0 - -func BenchmarkAssetCloning(b *testing.B) { - const numAssets = 800 - assets := make(map[basics.AssetIndex]basics.AssetHolding, numAssets) - for j := 0; j < numAssets; j++ { - aidx := basics.AssetIndex(rand.Int63n(100000000)) - assets[aidx] = basics.AssetHolding{Amount: uint64(aidx)} - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - clone := cloneAssetHoldings(assets) - benchTotal += len(clone) // make sure the compiler does not optimize out cloneAssetHoldings call - } -} diff --git a/ledger/apply/mockBalances_test.go b/ledger/apply/mockBalances_test.go index a9b8387159..3d441b03c8 100644 --- a/ledger/apply/mockBalances_test.go +++ b/ledger/apply/mockBalances_test.go @@ -23,6 +23,7 @@ import ( "github.com/algorand/go-algorand/data/transactions/logic" "github.com/algorand/go-algorand/ledger/ledgercore" "github.com/algorand/go-algorand/protocol" + "golang.org/x/exp/maps" ) type mockBalances struct { @@ -161,18 +162,22 @@ func (b *mockCreatableBalances) GetAssetParams(addr basics.Address, aidx basics. return } +// mapWith returns a new map with the given key and value added to it. +// maps.Clone would keep nil inputs as nil, so we make() then map.Copy(). +func mapWith[M ~map[K]V, K comparable, V any](m M, k K, v V) M { + newMap := make(M, len(m)+1) + maps.Copy(newMap, m) + newMap[k] = v + return newMap +} + func (b *mockCreatableBalances) PutAppParams(addr basics.Address, aidx basics.AppIndex, params basics.AppParams) error { b.putAppParams++ acct, err := b.access.getAccount(addr, false) if err != nil { return err } - m := make(map[basics.AppIndex]basics.AppParams, len(acct.AppParams)) - for k, v := range acct.AppParams { - m[k] = v - } - m[aidx] = params - acct.AppParams = m + acct.AppParams = mapWith(acct.AppParams, aidx, params) return b.access.putAccount(addr, acct) } func (b *mockCreatableBalances) PutAppLocalState(addr basics.Address, aidx basics.AppIndex, state basics.AppLocalState) error { @@ -181,12 +186,7 @@ func (b *mockCreatableBalances) PutAppLocalState(addr basics.Address, aidx basic if err != nil { return err } - m := make(map[basics.AppIndex]basics.AppLocalState, len(acct.AppLocalStates)) - for k, v := range acct.AppLocalStates { - m[k] = v - } - m[aidx] = state - acct.AppLocalStates = m + acct.AppLocalStates = mapWith(acct.AppLocalStates, aidx, state) return b.access.putAccount(addr, acct) } func (b *mockCreatableBalances) PutAssetHolding(addr basics.Address, aidx basics.AssetIndex, data basics.AssetHolding) error { @@ -195,12 +195,7 @@ func (b *mockCreatableBalances) PutAssetHolding(addr basics.Address, aidx basics if err != nil { return err } - m := make(map[basics.AssetIndex]basics.AssetHolding, len(acct.Assets)) - for k, v := range acct.Assets { - m[k] = v - } - m[aidx] = data - acct.Assets = m + acct.Assets = mapWith(acct.Assets, aidx, data) return b.access.putAccount(addr, acct) } func (b *mockCreatableBalances) PutAssetParams(addr basics.Address, aidx basics.AssetIndex, data basics.AssetParams) error { @@ -209,12 +204,7 @@ func (b *mockCreatableBalances) PutAssetParams(addr basics.Address, aidx basics. if err != nil { return err } - m := make(map[basics.AssetIndex]basics.AssetParams, len(acct.AssetParams)) - for k, v := range acct.AssetParams { - m[k] = v - } - m[aidx] = data - acct.AssetParams = m + acct.AssetParams = mapWith(acct.AssetParams, aidx, data) return b.access.putAccount(addr, acct) } @@ -224,10 +214,7 @@ func (b *mockCreatableBalances) DeleteAppParams(addr basics.Address, aidx basics if err != nil { return err } - m := make(map[basics.AppIndex]basics.AppParams, len(acct.AppParams)) - for k, v := range acct.AppParams { - m[k] = v - } + m := maps.Clone(acct.AppParams) delete(m, aidx) acct.AppParams = m return b.access.putAccount(addr, acct) @@ -238,10 +225,7 @@ func (b *mockCreatableBalances) DeleteAppLocalState(addr basics.Address, aidx ba if err != nil { return err } - m := make(map[basics.AppIndex]basics.AppLocalState, len(acct.AppLocalStates)) - for k, v := range acct.AppLocalStates { - m[k] = v - } + m := maps.Clone(acct.AppLocalStates) delete(m, aidx) acct.AppLocalStates = m return b.access.putAccount(addr, acct) @@ -252,10 +236,7 @@ func (b *mockCreatableBalances) DeleteAssetHolding(addr basics.Address, aidx bas if err != nil { return err } - m := make(map[basics.AssetIndex]basics.AssetHolding, len(acct.Assets)) - for k, v := range acct.Assets { - m[k] = v - } + m := maps.Clone(acct.Assets) delete(m, aidx) acct.Assets = m return b.access.putAccount(addr, acct) @@ -266,10 +247,7 @@ func (b *mockCreatableBalances) DeleteAssetParams(addr basics.Address, aidx basi if err != nil { return err } - m := make(map[basics.AssetIndex]basics.AssetParams, len(acct.AssetParams)) - for k, v := range acct.AssetParams { - m[k] = v - } + m := maps.Clone(acct.AssetParams) delete(m, aidx) acct.AssetParams = m return b.access.putAccount(addr, acct) diff --git a/ledger/eval/cow.go b/ledger/eval/cow.go index c58f65fc51..8019dc69f7 100644 --- a/ledger/eval/cow.go +++ b/ledger/eval/cow.go @@ -27,6 +27,7 @@ import ( "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/ledger/ledgercore" "github.com/algorand/go-algorand/protocol" + "golang.org/x/exp/maps" ) // ___________________ @@ -336,13 +337,9 @@ func (cb *roundCowState) reset() { cb.proto = config.ConsensusParams{} cb.mods.Reset() cb.txnCount = 0 - for addr := range cb.sdeltas { - delete(cb.sdeltas, addr) - } + maps.Clear(cb.sdeltas) cb.compatibilityMode = false - for addr := range cb.compatibilityGetKeyCache { - delete(cb.compatibilityGetKeyCache, addr) - } + maps.Clear(cb.compatibilityGetKeyCache) cb.prevTotals = ledgercore.AccountTotals{} } diff --git a/ledger/eval/txntracer.go b/ledger/eval/txntracer.go index 036ad773de..f4c6277c71 100644 --- a/ledger/eval/txntracer.go +++ b/ledger/eval/txntracer.go @@ -20,6 +20,8 @@ import ( "fmt" "github.com/algorand/go-deadlock" + "golang.org/x/exp/maps" + "golang.org/x/exp/slices" "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/data/basics" @@ -51,36 +53,22 @@ func convertStateDelta(delta ledgercore.StateDelta) StateDeltaSubset { // The StateDelta object returned through the EvalTracer has its values deleted between txn groups to avoid // reallocation during evaluation. // This means the map values need to be copied (to avoid deletion) since they are all passed by reference. - kvmods := make(map[string]ledgercore.KvValueDelta, len(delta.KvMods)) - for k1, v1 := range delta.KvMods { - kvmods[k1] = v1 - } - txids := make(map[transactions.Txid]ledgercore.IncludedTransactions, len(delta.Txids)) - for k2, v2 := range delta.Txids { - txids[k2] = v2 - } - txleases := make(map[ledgercore.Txlease]basics.Round, len(delta.Txleases)) - for k3, v3 := range delta.Txleases { - txleases[k3] = v3 - } - creatables := make(map[basics.CreatableIndex]ledgercore.ModifiedCreatable, len(delta.Creatables)) - for k4, v4 := range delta.Creatables { - creatables[k4] = v4 - } + kvmods := maps.Clone(delta.KvMods) + txids := maps.Clone(delta.Txids) + txleases := maps.Clone(delta.Txleases) + creatables := maps.Clone(delta.Creatables) + var accR []ledgercore.BalanceRecord var appR []ledgercore.AppResourceRecord var assetR []ledgercore.AssetResourceRecord if len(delta.Accts.Accts) > 0 { - accR = make([]ledgercore.BalanceRecord, len(delta.Accts.Accts)) - copy(accR, delta.Accts.Accts) + accR = slices.Clone(delta.Accts.Accts) } if len(delta.Accts.AppResources) > 0 { - appR = make([]ledgercore.AppResourceRecord, len(delta.Accts.AppResources)) - copy(appR, delta.Accts.AppResources) + appR = slices.Clone(delta.Accts.AppResources) } if len(delta.Accts.AssetResources) > 0 { - assetR = make([]ledgercore.AssetResourceRecord, len(delta.Accts.AssetResources)) - copy(assetR, delta.Accts.AssetResources) + assetR = slices.Clone(delta.Accts.AssetResources) } return StateDeltaSubset{ Accts: ledgercore.AccountDeltas{ diff --git a/ledger/ledger.go b/ledger/ledger.go index 3dc916fd87..88aecb82dc 100644 --- a/ledger/ledger.go +++ b/ledger/ledger.go @@ -502,24 +502,6 @@ func (l *Ledger) GetStateProofVerificationContext(stateProofLastAttestedRound ba return l.spVerification.LookupVerificationContext(stateProofLastAttestedRound) } -// ListAssets takes a maximum asset index and maximum result length, and -// returns up to that many CreatableLocators from the database where app idx is -// less than or equal to the maximum. -func (l *Ledger) ListAssets(maxAssetIdx basics.AssetIndex, maxResults uint64) (results []basics.CreatableLocator, err error) { - l.trackerMu.RLock() - defer l.trackerMu.RUnlock() - return l.accts.ListAssets(maxAssetIdx, maxResults) -} - -// ListApplications takes a maximum app index and maximum result length, and -// returns up to that many CreatableLocators from the database where app idx is -// less than or equal to the maximum. -func (l *Ledger) ListApplications(maxAppIdx basics.AppIndex, maxResults uint64) (results []basics.CreatableLocator, err error) { - l.trackerMu.RLock() - defer l.trackerMu.RUnlock() - return l.accts.ListApplications(maxAppIdx, maxResults) -} - // LookupLatest uses the accounts tracker to return the account state (including // resources) for a given address, for the latest round. The returned account values // reflect the changes of all blocks up to and including the returned round number. diff --git a/ledger/ledger_test.go b/ledger/ledger_test.go index 5ee3f8718c..f4d5fee2d8 100644 --- a/ledger/ledger_test.go +++ b/ledger/ledger_test.go @@ -30,6 +30,7 @@ import ( "time" "github.com/stretchr/testify/require" + "golang.org/x/exp/slices" "github.com/algorand/go-algorand/agreement" "github.com/algorand/go-algorand/config" @@ -696,8 +697,7 @@ func TestLedgerSingleTxV24(t *testing.T) { appIdx = 2 // the second successful txn badTx = correctAppCreate - program := make([]byte, len(approvalProgram)) - copy(program, approvalProgram) + program := slices.Clone(approvalProgram) program[0] = '\x01' badTx.ApprovalProgram = program err = l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad) @@ -1623,71 +1623,6 @@ func generateCreatables(numElementsPerSegement int) ( return } -// TestListAssetsAndApplications tests the ledger.ListAssets and ledger.ListApplications -// interfaces. The detailed test on the correctness of these functions is given in: -// TestListCreatables (acctupdates_test.go) -func TestListAssetsAndApplications(t *testing.T) { - partitiontest.PartitionTest(t) - - numElementsPerSegement := 10 // This is multiplied by 10. see randomCreatables - - //initLedger - genesisInitState, _ := ledgertesting.GenerateInitState(t, protocol.ConsensusCurrentVersion, 100) - const inMem = true - log := logging.TestingLog(t) - cfg := config.GetDefaultLocal() - cfg.Archival = true - ledger, err := OpenLedger(log, t.Name(), inMem, genesisInitState, cfg) - require.NoError(t, err, "could not open ledger") - defer ledger.Close() - - // ******* All results are obtained from the cache. Empty database ******* - // ******* No deletes ******* - // get random data. Initial batch, no deletes - randomCtbs, maxAsset, maxApp, err := generateCreatables(numElementsPerSegement) - require.NoError(t, err) - - // set the cache - ledger.accts.creatables = randomCtbs - - // Test ListAssets - // Check the number of results limit - results, err := ledger.ListAssets(basics.AssetIndex(maxAsset), 2) - require.NoError(t, err) - require.Equal(t, 2, len(results)) - // Check the max asset id limit - results, err = ledger.ListAssets(basics.AssetIndex(maxAsset), 100) - require.NoError(t, err) - assetCount := 0 - for id, ctb := range randomCtbs { - if ctb.Ctype == basics.AssetCreatable && - ctb.Created && - id <= maxAsset { - assetCount++ - } - } - require.Equal(t, assetCount, len(results)) - - // Test ListApplications - // Check the number of results limit - ledger.accts.creatables = randomCtbs - results, err = ledger.ListApplications(basics.AppIndex(maxApp), 2) - require.NoError(t, err) - require.Equal(t, 2, len(results)) - // Check the max application id limit - results, err = ledger.ListApplications(basics.AppIndex(maxApp), 100) - require.NoError(t, err) - appCount := 0 - for id, ctb := range randomCtbs { - if ctb.Ctype == basics.AppCreatable && - ctb.Created && - id <= maxApp { - appCount++ - } - } - require.Equal(t, appCount, len(results)) -} - // TestLedgerVerifiesOldStateProofs test that if stateproof chain is delayed for X intervals (pass StateProofMaxRecoveryIntervals), // The ledger will still be able to verify the state proof - i.e the ledger has the necessary data to verify it. func TestLedgerVerifiesOldStateProofs(t *testing.T) { diff --git a/ledger/ledgercore/statedelta.go b/ledger/ledgercore/statedelta.go index 7e9e25e0d0..a730af251a 100644 --- a/ledger/ledgercore/statedelta.go +++ b/ledger/ledgercore/statedelta.go @@ -22,6 +22,7 @@ import ( "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" "github.com/algorand/go-algorand/data/transactions" + "golang.org/x/exp/maps" ) const ( @@ -293,38 +294,24 @@ func (ad *AccountDeltas) Dehydrate() { if ad.acctsCache == nil { ad.acctsCache = make(map[basics.Address]int) } - for key := range ad.acctsCache { - delete(ad.acctsCache, key) - } + maps.Clear(ad.acctsCache) if ad.appResourcesCache == nil { ad.appResourcesCache = make(map[AccountApp]int) } - for key := range ad.appResourcesCache { - delete(ad.appResourcesCache, key) - } + maps.Clear(ad.appResourcesCache) if ad.assetResourcesCache == nil { ad.assetResourcesCache = make(map[AccountAsset]int) } - for key := range ad.assetResourcesCache { - delete(ad.assetResourcesCache, key) - } + maps.Clear(ad.assetResourcesCache) } // Reset resets the StateDelta for re-use with sync.Pool func (sd *StateDelta) Reset() { sd.Accts.reset() - for txid := range sd.Txids { - delete(sd.Txids, txid) - } - for txLease := range sd.Txleases { - delete(sd.Txleases, txLease) - } - for creatableIndex := range sd.Creatables { - delete(sd.Creatables, creatableIndex) - } - for key := range sd.KvMods { - delete(sd.KvMods, key) - } + maps.Clear(sd.Txids) + maps.Clear(sd.Txleases) + maps.Clear(sd.Creatables) + maps.Clear(sd.KvMods) sd.Totals = AccountTotals{} // these fields are going to be populated on next use but resetting them anyway for safety. @@ -342,15 +329,9 @@ func (ad *AccountDeltas) reset() { ad.AssetResources = ad.AssetResources[:0] // reset the maps - for address := range ad.acctsCache { - delete(ad.acctsCache, address) - } - for aApp := range ad.appResourcesCache { - delete(ad.appResourcesCache, aApp) - } - for aAsset := range ad.assetResourcesCache { - delete(ad.assetResourcesCache, aAsset) - } + maps.Clear(ad.acctsCache) + maps.Clear(ad.appResourcesCache) + maps.Clear(ad.assetResourcesCache) } // notAllocated returns true if any of the fields allocated by MakeAccountDeltas is nil @@ -581,11 +562,7 @@ func (sd *StateDelta) OptimizeAllocatedMemory(maxBalLookback uint64) { // realloc if original allocation capacity greater than length of data, and space difference is significant if 2*sd.initialHint > len(sd.Accts.acctsCache) && uint64(2*sd.initialHint-len(sd.Accts.acctsCache))*accountMapCacheEntrySize*maxBalLookback > stateDeltaTargetOptimizationThreshold { - acctsCache := make(map[basics.Address]int, len(sd.Accts.acctsCache)) - for k, v := range sd.Accts.acctsCache { - acctsCache[k] = v - } - sd.Accts.acctsCache = acctsCache + sd.Accts.acctsCache = maps.Clone(sd.Accts.acctsCache) } } diff --git a/network/phonebook.go b/network/phonebook.go index ac5914a299..ad07a2b5f5 100644 --- a/network/phonebook.go +++ b/network/phonebook.go @@ -22,6 +22,7 @@ import ( "time" "github.com/algorand/go-deadlock" + "golang.org/x/exp/slices" ) // when using GetAddresses with getAllAddresses, all the addresses will be retrieved, regardless @@ -287,8 +288,7 @@ func shuffleStrings(set []string) { func shuffleSelect(set []string, n int) []string { if n >= len(set) || n == getAllAddresses { // return shuffled copy of everything - out := make([]string, len(set)) - copy(out, set) + out := slices.Clone(set) shuffleStrings(out) return out } diff --git a/stateproof/builder.go b/stateproof/builder.go index 7d90e8b731..13f4aa5b9b 100644 --- a/stateproof/builder.go +++ b/stateproof/builder.go @@ -22,7 +22,6 @@ import ( "encoding/binary" "errors" "fmt" - "sort" "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto/stateproof" @@ -35,6 +34,8 @@ import ( "github.com/algorand/go-algorand/network" "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/stateproof/verify" + "golang.org/x/exp/maps" + "golang.org/x/exp/slices" ) var errVotersNotTracked = errors.New("voters not tracked for the given lookback round") @@ -641,11 +642,8 @@ func (spw *Worker) tryBroadcast() { spw.mu.Lock() defer spw.mu.Unlock() - sortedRounds := make([]basics.Round, 0, len(spw.provers)) - for rnd := range spw.provers { - sortedRounds = append(sortedRounds, rnd) - } - sort.Slice(sortedRounds, func(i, j int) bool { return sortedRounds[i] < sortedRounds[j] }) + sortedRounds := maps.Keys(spw.provers) + slices.Sort(sortedRounds) for _, rnd := range sortedRounds { // Iterate over the provers in a sequential manner. If the earlist state proof is not ready/rejected diff --git a/test/e2e-go/upgrades/stateproof_participation_test.go b/test/e2e-go/upgrades/stateproof_participation_test.go index e37fc622de..8823d7c769 100644 --- a/test/e2e-go/upgrades/stateproof_participation_test.go +++ b/test/e2e-go/upgrades/stateproof_participation_test.go @@ -18,7 +18,6 @@ package upgrades import ( "path/filepath" - "strings" "testing" "time" @@ -44,7 +43,7 @@ func waitUntilProtocolUpgrades(a *require.Assertions, fixture *fixtures.RestClie startTime := time.Now() // while consensus version has not upgraded - for strings.Compare(string(curProtocol), string(consensusTestFastUpgrade(protocol.ConsensusV30))) == 0 { + for curProtocol == consensusTestFastUpgrade(protocol.ConsensusV30) { curRound = curRound + 1 fixture.WaitForRoundWithTimeout(curRound + 1) diff --git a/test/reflectionhelpers/helpers.go b/test/reflectionhelpers/helpers.go index 79c24d5e2e..6d228b061f 100644 --- a/test/reflectionhelpers/helpers.go +++ b/test/reflectionhelpers/helpers.go @@ -20,6 +20,8 @@ import ( "fmt" "reflect" "strings" + + "golang.org/x/exp/slices" ) // TypeSegmentKind is a enum for the types of TypeSegment @@ -61,9 +63,7 @@ type TypePath []TypeSegment // Clone creates a deep copy of a TypePath func (p TypePath) Clone() TypePath { - cloned := make(TypePath, len(p)) - copy(cloned, p) - return cloned + return slices.Clone(p) } // AddMapKey adds a map key segment to a TypePath. The modification is done using append, so this @@ -177,15 +177,7 @@ func (p TypePath) ResolveValues(base reflect.Value) []reflect.Value { // Equals returns true if and only if the input TypePath has the exact same segments as this // TypePath. func (p TypePath) Equals(other TypePath) bool { - if len(p) != len(other) { - return false - } - for i := range p { - if p[i] != other[i] { - return false - } - } - return true + return slices.Equal(p, other) } func (p TypePath) String() string { diff --git a/tools/block-generator/go.mod b/tools/block-generator/go.mod index b763275cbe..fe125856fb 100644 --- a/tools/block-generator/go.mod +++ b/tools/block-generator/go.mod @@ -45,6 +45,7 @@ require ( github.com/sirupsen/logrus v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/crypto v0.1.0 // indirect + golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect golang.org/x/net v0.9.0 // indirect golang.org/x/sys v0.7.0 // indirect gopkg.in/sohlich/elogrus.v3 v3.0.0-20180410122755-1fa29e2f2009 // indirect diff --git a/tools/block-generator/go.sum b/tools/block-generator/go.sum index 03dca441b4..b6390723db 100644 --- a/tools/block-generator/go.sum +++ b/tools/block-generator/go.sum @@ -85,6 +85,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= diff --git a/util/metrics/tagcounter.go b/util/metrics/tagcounter.go index 1daf30ba13..ff3b8a732b 100644 --- a/util/metrics/tagcounter.go +++ b/util/metrics/tagcounter.go @@ -22,6 +22,7 @@ import ( "sync/atomic" "github.com/algorand/go-deadlock" + "golang.org/x/exp/maps" ) // NewTagCounterFiltered makes a set of metrics under rootName for tagged counting. @@ -98,9 +99,7 @@ func (tc *TagCounter) Add(tag string, val uint64) { // Still need to add a new tag. // Make a new map so there's never any race. newtags := make(map[string]*uint64, len(tc.tags)+1) - for k, v := range tc.tags { - newtags[k] = v - } + maps.Copy(newtags, tc.tags) var st []uint64 if len(tc.storage) > 0 { st = tc.storage[len(tc.storage)-1]