diff options
Diffstat (limited to 'ledger/testing/randomAccounts.go')
-rw-r--r-- | ledger/testing/randomAccounts.go | 344 |
1 files changed, 344 insertions, 0 deletions
diff --git a/ledger/testing/randomAccounts.go b/ledger/testing/randomAccounts.go new file mode 100644 index 000000000..86ec6a60c --- /dev/null +++ b/ledger/testing/randomAccounts.go @@ -0,0 +1,344 @@ +// Copyright (C) 2019-2021 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see <https://www.gnu.org/licenses/>. + +package testing + +import ( + "fmt" + + "github.com/algorand/go-algorand/config" + "github.com/algorand/go-algorand/crypto" + "github.com/algorand/go-algorand/data/basics" + "github.com/algorand/go-algorand/protocol" + //"github.com/algorand/go-algorand/data/bookkeeping" + + "github.com/algorand/go-algorand/ledger/ledgercore" +) + +var testPoolAddr = 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} +var testSinkAddr = basics.Address{0x2c, 0x2a, 0x6c, 0xe9, 0xa9, 0xa7, 0xc2, 0x8c, 0x22, 0x95, 0xfd, 0x32, 0x4f, 0x77, 0xa5, 0x4, 0x8b, 0x42, 0xc2, 0xb7, 0xa8, 0x54, 0x84, 0xb6, 0x80, 0xb1, 0xe1, 0x3d, 0x59, 0x9b, 0xeb, 0x36} + +// RandomAddress generates a random address +func RandomAddress() basics.Address { + var addr basics.Address + crypto.RandBytes(addr[:]) + return addr +} + +// RandomNote generates a random notes data +func RandomNote() []byte { + var note [16]byte + crypto.RandBytes(note[:]) + return note[:] +} + +// RandomAccountData generates a random AccountData +func RandomAccountData(rewardsLevel uint64) basics.AccountData { + var data basics.AccountData + + // Avoid overflowing totals + data.MicroAlgos.Raw = crypto.RandUint64() % (1 << 32) + + switch crypto.RandUint64() % 3 { + case 0: + data.Status = basics.Online + case 1: + data.Status = basics.Offline + default: + data.Status = basics.NotParticipating + } + + data.RewardsBase = rewardsLevel + data.VoteFirstValid = 0 + data.VoteLastValid = 1000 + return data +} + +// RandomFullAccountData generates a random AccountData +func RandomFullAccountData(rewardsLevel, lastCreatableID uint64) (basics.AccountData, uint64) { + data := RandomAccountData(rewardsLevel) + + crypto.RandBytes(data.VoteID[:]) + crypto.RandBytes(data.SelectionID[:]) + data.VoteFirstValid = basics.Round(crypto.RandUint64()) + data.VoteLastValid = basics.Round(crypto.RandUint64()) + data.VoteKeyDilution = crypto.RandUint64() + if 1 == (crypto.RandUint64() % 2) { + // if account has created assets, have these defined. + data.AssetParams = make(map[basics.AssetIndex]basics.AssetParams) + createdAssetsCount := crypto.RandUint64()%20 + 1 + for i := uint64(0); i < createdAssetsCount; i++ { + ap := basics.AssetParams{ + Total: crypto.RandUint64(), + Decimals: uint32(crypto.RandUint64() % 20), + DefaultFrozen: (crypto.RandUint64()%2 == 0), + UnitName: fmt.Sprintf("un%x", uint32(crypto.RandUint64()%0x7fffffff)), + AssetName: fmt.Sprintf("an%x", uint32(crypto.RandUint64()%0x7fffffff)), + URL: fmt.Sprintf("url%x", uint32(crypto.RandUint64()%0x7fffffff)), + } + crypto.RandBytes(ap.MetadataHash[:]) + crypto.RandBytes(ap.Manager[:]) + crypto.RandBytes(ap.Reserve[:]) + crypto.RandBytes(ap.Freeze[:]) + crypto.RandBytes(ap.Clawback[:]) + lastCreatableID++ + data.AssetParams[basics.AssetIndex(lastCreatableID)] = ap + } + } + if 1 == (crypto.RandUint64()%2) && lastCreatableID > 0 { + // if account owns assets + data.Assets = make(map[basics.AssetIndex]basics.AssetHolding) + ownedAssetsCount := crypto.RandUint64()%20 + 1 + for i := uint64(0); i < ownedAssetsCount; i++ { + ah := basics.AssetHolding{ + Amount: crypto.RandUint64(), + Frozen: (crypto.RandUint64()%2 == 0), + } + data.Assets[basics.AssetIndex(crypto.RandUint64()%lastCreatableID)] = ah + } + } + if 1 == (crypto.RandUint64() % 5) { + crypto.RandBytes(data.AuthAddr[:]) + } + + if 1 == (crypto.RandUint64()%3) && lastCreatableID > 0 { + data.AppLocalStates = make(map[basics.AppIndex]basics.AppLocalState) + appStatesCount := crypto.RandUint64()%20 + 1 + for i := uint64(0); i < appStatesCount; i++ { + ap := basics.AppLocalState{ + Schema: basics.StateSchema{ + NumUint: crypto.RandUint64()%5 + 1, + NumByteSlice: crypto.RandUint64() % 5, + }, + KeyValue: make(map[string]basics.TealValue), + } + + for i := uint64(0); i < ap.Schema.NumUint; i++ { + appName := fmt.Sprintf("lapp%x-%x", crypto.RandUint64(), i) + ap.KeyValue[appName] = basics.TealValue{ + Type: basics.TealUintType, + Uint: crypto.RandUint64(), + } + } + for i := uint64(0); i < ap.Schema.NumByteSlice; i++ { + appName := fmt.Sprintf("lapp%x-%x", crypto.RandUint64(), i) + tv := basics.TealValue{ + Type: basics.TealBytesType, + } + bytes := make([]byte, crypto.RandUint64()%uint64(config.MaxBytesKeyValueLen-len(appName))) + crypto.RandBytes(bytes[:]) + tv.Bytes = string(bytes) + ap.KeyValue[appName] = tv + } + if len(ap.KeyValue) == 0 { + ap.KeyValue = nil + } + data.AppLocalStates[basics.AppIndex(crypto.RandUint64()%lastCreatableID)] = ap + } + } + + if 1 == (crypto.RandUint64() % 3) { + data.TotalAppSchema = basics.StateSchema{ + NumUint: crypto.RandUint64() % 50, + NumByteSlice: crypto.RandUint64() % 50, + } + } + if 1 == (crypto.RandUint64() % 3) { + data.AppParams = make(map[basics.AppIndex]basics.AppParams) + appParamsCount := crypto.RandUint64()%5 + 1 + for i := uint64(0); i < appParamsCount; i++ { + ap := basics.AppParams{ + ApprovalProgram: make([]byte, int(crypto.RandUint63())%config.MaxAppProgramLen), + ClearStateProgram: make([]byte, int(crypto.RandUint63())%config.MaxAppProgramLen), + GlobalState: make(basics.TealKeyValue), + StateSchemas: basics.StateSchemas{ + LocalStateSchema: basics.StateSchema{ + NumUint: crypto.RandUint64()%5 + 1, + NumByteSlice: crypto.RandUint64() % 5, + }, + GlobalStateSchema: basics.StateSchema{ + NumUint: crypto.RandUint64()%5 + 1, + NumByteSlice: crypto.RandUint64() % 5, + }, + }, + } + if len(ap.ApprovalProgram) > 0 { + crypto.RandBytes(ap.ApprovalProgram[:]) + } else { + ap.ApprovalProgram = nil + } + if len(ap.ClearStateProgram) > 0 { + crypto.RandBytes(ap.ClearStateProgram[:]) + } else { + ap.ClearStateProgram = nil + } + + for i := uint64(0); i < ap.StateSchemas.LocalStateSchema.NumUint+ap.StateSchemas.GlobalStateSchema.NumUint; i++ { + appName := fmt.Sprintf("tapp%x-%x", crypto.RandUint64(), i) + ap.GlobalState[appName] = basics.TealValue{ + Type: basics.TealUintType, + Uint: crypto.RandUint64(), + } + } + for i := uint64(0); i < ap.StateSchemas.LocalStateSchema.NumByteSlice+ap.StateSchemas.GlobalStateSchema.NumByteSlice; i++ { + appName := fmt.Sprintf("tapp%x-%x", crypto.RandUint64(), i) + tv := basics.TealValue{ + Type: basics.TealBytesType, + } + bytes := make([]byte, crypto.RandUint64()%uint64(config.MaxBytesKeyValueLen)) + crypto.RandBytes(bytes[:]) + tv.Bytes = string(bytes) + ap.GlobalState[appName] = tv + } + if len(ap.GlobalState) == 0 { + ap.GlobalState = nil + } + lastCreatableID++ + data.AppParams[basics.AppIndex(lastCreatableID)] = ap + } + + } + return data, lastCreatableID +} + +// RandomAccounts generates a random set of accounts map +func RandomAccounts(niter int, simpleAccounts bool) map[basics.Address]basics.AccountData { + res := make(map[basics.Address]basics.AccountData) + if simpleAccounts { + for i := 0; i < niter; i++ { + res[RandomAddress()] = RandomAccountData(0) + } + } else { + lastCreatableID := crypto.RandUint64() % 512 + for i := 0; i < niter; i++ { + res[RandomAddress()], lastCreatableID = RandomFullAccountData(0, lastCreatableID) + } + } + return res +} + +// RandomDeltas generates a random set of accounts delta +func RandomDeltas(niter int, base map[basics.Address]basics.AccountData, rewardsLevel uint64) (updates ledgercore.AccountDeltas, totals map[basics.Address]basics.AccountData, imbalance int64) { + updates, totals, imbalance, _ = RandomDeltasImpl(niter, base, rewardsLevel, true, 0) + return +} + +// RandomDeltasFull generates a random set of accounts delta +func RandomDeltasFull(niter int, base map[basics.Address]basics.AccountData, rewardsLevel uint64, lastCreatableIDIn uint64) (updates ledgercore.AccountDeltas, totals map[basics.Address]basics.AccountData, imbalance int64, lastCreatableID uint64) { + updates, totals, imbalance, lastCreatableID = RandomDeltasImpl(niter, base, rewardsLevel, false, lastCreatableIDIn) + return +} + +// RandomDeltasImpl generates a random set of accounts delta +func RandomDeltasImpl(niter int, base map[basics.Address]basics.AccountData, rewardsLevel uint64, simple bool, lastCreatableIDIn uint64) (updates ledgercore.AccountDeltas, totals map[basics.Address]basics.AccountData, imbalance int64, lastCreatableID uint64) { + proto := config.Consensus[protocol.ConsensusCurrentVersion] + totals = make(map[basics.Address]basics.AccountData) + + // copy base -> totals + for addr, data := range base { + totals[addr] = data + } + + // if making a full delta then need to determine max asset/app id to get rid of conflicts + lastCreatableID = lastCreatableIDIn + if !simple { + for _, ad := range base { + for aid := range ad.AssetParams { + if uint64(aid) > lastCreatableID { + lastCreatableID = uint64(aid) + } + } + for aid := range ad.AppParams { + if uint64(aid) > lastCreatableID { + lastCreatableID = uint64(aid) + } + } + } + } + + // Change some existing accounts + { + i := 0 + for addr, old := range base { + if i >= len(base)/2 || i >= niter { + break + } + + if addr == testPoolAddr { + continue + } + i++ + + var new basics.AccountData + if simple { + new = RandomAccountData(rewardsLevel) + } else { + new, lastCreatableID = RandomFullAccountData(rewardsLevel, lastCreatableID) + } + updates.Upsert(addr, new) + imbalance += int64(old.WithUpdatedRewards(proto, rewardsLevel).MicroAlgos.Raw - new.MicroAlgos.Raw) + totals[addr] = new + } + } + + // Change some new accounts + for i := 0; i < niter; i++ { + addr := RandomAddress() + old := totals[addr] + var new basics.AccountData + if simple { + new = RandomAccountData(rewardsLevel) + } else { + new, lastCreatableID = RandomFullAccountData(rewardsLevel, lastCreatableID) + } + updates.Upsert(addr, new) + imbalance += int64(old.WithUpdatedRewards(proto, rewardsLevel).MicroAlgos.Raw - new.MicroAlgos.Raw) + totals[addr] = new + } + + return +} + +// RandomDeltasBalanced generates a random set of accounts delta +func RandomDeltasBalanced(niter int, base map[basics.Address]basics.AccountData, rewardsLevel uint64) (updates ledgercore.AccountDeltas, totals map[basics.Address]basics.AccountData) { + updates, totals, _ = RandomDeltasBalancedImpl(niter, base, rewardsLevel, true, 0) + return +} + +// RandomDeltasBalancedFull generates a random set of accounts delta +func RandomDeltasBalancedFull(niter int, base map[basics.Address]basics.AccountData, rewardsLevel uint64, lastCreatableIDIn uint64) (updates ledgercore.AccountDeltas, totals map[basics.Address]basics.AccountData, lastCreatableID uint64) { + updates, totals, lastCreatableID = RandomDeltasBalancedImpl(niter, base, rewardsLevel, false, lastCreatableIDIn) + return +} + +// RandomDeltasBalancedImpl generates a random set of accounts delta +func RandomDeltasBalancedImpl(niter int, base map[basics.Address]basics.AccountData, rewardsLevel uint64, simple bool, lastCreatableIDIn uint64) (updates ledgercore.AccountDeltas, totals map[basics.Address]basics.AccountData, lastCreatableID uint64) { + var imbalance int64 + if simple { + updates, totals, imbalance = RandomDeltas(niter, base, rewardsLevel) + } else { + updates, totals, imbalance, lastCreatableID = RandomDeltasFull(niter, base, rewardsLevel, lastCreatableIDIn) + } + + oldPool := base[testPoolAddr] + newPool := oldPool + newPool.MicroAlgos.Raw += uint64(imbalance) + + updates.Upsert(testPoolAddr, newPool) + totals[testPoolAddr] = newPool + + return updates, totals, lastCreatableID +} |