cosmos-sdk/x/mock/simulation/util.go
ValarDragon bb624b36aa simulation: Use a simulation.Account struct
This removes privkeys and addresses from function signatures.
This comes with a 11% performance improvement to the simulator,
as we no longer keep recomputing the pubkeys.
Once we move the transient store clearing to endblock, we can
further raise the size of make test_sim_gaia_fast

concretely, `make test_sim_gaia_fast` went from 16.8 seconds to 13.5 seconds on my system.
2018-09-22 20:33:32 -07:00

162 lines
4.5 KiB
Go

package simulation
import (
"fmt"
"math/rand"
"os"
"sort"
"strings"
"testing"
"time"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/secp256k1"
"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/mock"
)
// shamelessly copied from https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang#31832326
// TODO we should probably move this to tendermint/libs/common/random.go
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const (
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)
// Generate a random string of a particular length
func RandStringOfLength(r *rand.Rand, n int) string {
b := make([]byte, n)
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
for i, cache, remain := n-1, r.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = r.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
b[i] = letterBytes[idx]
i--
}
cache >>= letterIdxBits
remain--
}
return string(b)
}
// Pretty-print events as a table
func DisplayEvents(events map[string]uint) {
var keys []string
for key := range events {
keys = append(keys, key)
}
sort.Strings(keys)
fmt.Printf("Event statistics: \n")
for _, key := range keys {
fmt.Printf(" % 60s => %d\n", key, events[key])
}
}
// RandomAcc pick a random account from an array
func RandomAcc(r *rand.Rand, accs []Account) Account {
return accs[r.Intn(
len(accs),
)]
}
// Generate a random amount
func RandomAmount(r *rand.Rand, max sdk.Int) sdk.Int {
return sdk.NewInt(int64(r.Intn(int(max.Int64()))))
}
// RandomAccounts generates n random accounts
func RandomAccounts(r *rand.Rand, n int) []Account {
accs := make([]Account, n)
for i := 0; i < n; i++ {
// don't need that much entropy for simulation
privkeySeed := make([]byte, 15)
r.Read(privkeySeed)
useSecp := r.Int63()%2 == 0
if useSecp {
accs[i].PrivKey = secp256k1.GenPrivKeySecp256k1(privkeySeed)
} else {
accs[i].PrivKey = ed25519.GenPrivKeyFromSecret(privkeySeed)
}
accs[i].PubKey = accs[i].PrivKey.PubKey()
accs[i].Address = sdk.AccAddress(accs[i].PubKey.Address())
}
return accs
}
// Builds a function to add logs for this particular block
func addLogMessage(testingmode bool, blockLogBuilders []*strings.Builder, height int) func(string) {
if testingmode {
blockLogBuilders[height] = &strings.Builder{}
return func(x string) {
(*blockLogBuilders[height]).WriteString(x)
(*blockLogBuilders[height]).WriteString("\n")
}
}
return func(x string) {}
}
// assertAllInvariants asserts a list of provided invariants against application state
func assertAllInvariants(t *testing.T, app *baseapp.BaseApp, invariants []Invariant, displayLogs func()) {
for i := 0; i < len(invariants); i++ {
err := invariants[i](app)
if err != nil {
fmt.Println(err.Error())
displayLogs()
t.Fatal()
}
}
}
// RandomSetGenesis wraps mock.RandomSetGenesis, but using simulation accounts
func RandomSetGenesis(r *rand.Rand, app *mock.App, accs []Account, denoms []string) {
addrs := make([]sdk.AccAddress, len(accs))
for i := 0; i < len(accs); i++ {
addrs[i] = accs[i].Address
}
mock.RandomSetGenesis(r, app, addrs, denoms)
}
// Creates a function to print out the logs
func logPrinter(testingmode bool, logs []*strings.Builder) func() {
if testingmode {
return func() {
numLoggers := 0
for i := 0; i < len(logs); i++ {
// We're passed the last created block
if logs[i] == nil {
numLoggers = i - 1
break
}
}
var f *os.File
if numLoggers > 10 {
fileName := fmt.Sprintf("simulation_log_%s.txt", time.Now().Format("2006-01-02 15:04:05"))
fmt.Printf("Too many logs to display, instead writing to %s\n", fileName)
f, _ = os.Create(fileName)
}
for i := 0; i < numLoggers; i++ {
if f != nil {
_, err := f.WriteString(fmt.Sprintf("Begin block %d\n", i))
if err != nil {
panic("Failed to write logs to file")
}
_, err = f.WriteString((*logs[i]).String())
if err != nil {
panic("Failed to write logs to file")
}
} else {
fmt.Printf("Begin block %d\n", i)
fmt.Println((*logs[i]).String())
}
}
}
}
return func() {}
}