Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tools: pingpong improvements #4294

Merged
merged 12 commits into from
Sep 9, 2022
133 changes: 106 additions & 27 deletions cmd/pingpong/runCmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ package main
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"math/rand"
"os"
"path/filepath"
"runtime/pprof"
"strconv"
"strings"
"time"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -67,16 +70,21 @@ var rekey bool
var nftAsaPerSecond uint32
var pidFile string
var cpuprofile string
var randSeed int64
var deterministicKeys bool
var generatedAccountsCount uint32
var generatedAccountSampleMethod string
var configPath string

func init() {
rootCmd.AddCommand(runCmd)
runCmd.PersistentFlags().StringVarP(&dataDir, "datadir", "d", "", "Data directory for the node")

runCmd.Flags().StringVarP(&srcAddress, "src", "s", "", "Account address to use as funding source for new accounts)")
runCmd.Flags().StringVarP(&srcAddress, "src", "s", "", "Account address to use as funding source for new accounts")
runCmd.Flags().Uint32VarP(&numAccounts, "numaccounts", "n", 0, "The number of accounts to include in the transfers")
runCmd.Flags().Uint64VarP(&maxAmount, "ma", "a", 0, "The (max) amount to be transferred")
runCmd.Flags().Uint64VarP(&minAccountFunds, "minaccount", "", 0, "The minimum amount to fund a test account with")
runCmd.Flags().Uint64VarP(&txnPerSec, "tps", "t", 200, "Number of Txn per second that pingpong sends")
runCmd.Flags().Uint64VarP(&txnPerSec, "tps", "t", 0, "Number of Txn per second that pingpong sends")
runCmd.Flags().Int64VarP(&maxFee, "mf", "f", -1, "The MAX fee to be used for transactions, a value of '0' tells the server to use a suggested fee.")
runCmd.Flags().Uint64VarP(&minFee, "minf", "m", 1000, "The MIN fee to be used for randomFee transactions")
runCmd.Flags().BoolVar(&randomAmount, "ra", false, "Set to enable random amounts (up to maxamount)")
Expand All @@ -87,6 +95,7 @@ func init() {
runCmd.Flags().StringVar(&runTime, "run", "", "Duration of time (seconds) to run transfers before resting (0 means non-stop)")
runCmd.Flags().StringVar(&refreshTime, "refresh", "", "Duration of time (seconds) between refilling accounts with money (0 means no refresh)")
runCmd.Flags().StringVar(&logicProg, "program", "", "File containing the compiled program to include as a logic sig")
runCmd.Flags().StringVar(&configPath, "config", "", "path to read config json from, or json literal")
runCmd.Flags().BoolVar(&saveConfig, "save", false, "Save the effective configuration to disk")
runCmd.Flags().BoolVar(&useDefault, "reset", false, "Reset to the default configuration (not read from disk)")
runCmd.Flags().BoolVar(&quietish, "quiet", false, "quietish stdout logging")
Expand All @@ -107,6 +116,10 @@ func init() {
runCmd.Flags().Uint32Var(&nftAsaPerSecond, "nftasapersecond", 0, "The number of NFT-style ASAs to create per second")
runCmd.Flags().StringVar(&pidFile, "pidfile", "", "path to write process id of this pingpong")
runCmd.Flags().StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to `file`")
runCmd.Flags().Int64Var(&randSeed, "seed", 0, "input to math/rand.Seed(), defaults to time.Now().UnixNano()")
runCmd.Flags().BoolVar(&deterministicKeys, "deterministicKeys", false, "Draw from set of netgoal-created accounts using deterministic keys")
runCmd.Flags().Uint32Var(&generatedAccountsCount, "genaccounts", 0, "The total number of accounts pre-generated by netgoal")
runCmd.Flags().StringVar(&generatedAccountSampleMethod, "gensamplemethod", "random", "The method of sampling from the total # of pre-generated accounts")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do the pre-generated accounts get automatically topped off from a source account?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's code to make low balance accounts the destination of natural traffic, so they'll tend to refill.


var runCmd = &cobra.Command{
Expand Down Expand Up @@ -155,17 +168,45 @@ var runCmd = &cobra.Command{
}

// Prepare configuration
dataDirCfgPath := filepath.Join(ac.DataDir(), pingpong.ConfigFilename)
var cfg pingpong.PpConfig
cfgPath := filepath.Join(ac.DataDir(), pingpong.ConfigFilename)
if useDefault {
cfg = pingpong.DefaultConfig
if configPath != "" {
if configPath[0] == '{' {
// json literal as arg
cfg = pingpong.DefaultConfig
lf := strings.NewReader(configPath)
dec := json.NewDecoder(lf)
err = dec.Decode(&cfg)
if err != nil {
reportErrorf("-config: bad config json, %v", err)
}
fmt.Fprintf(os.Stdout, "config from --config:\n")
cfg.Dump(os.Stdout)
} else {
cfg, err = pingpong.LoadConfigFromFile(configPath)
if err != nil {
reportErrorf("%s: bad config json, %v", configPath, err)
}
fmt.Fprintf(os.Stdout, "config from %#v:\n", configPath)
cfg.Dump(os.Stdout)
}
} else {
cfg, err = pingpong.LoadConfigFromFile(cfgPath)
if err != nil && !os.IsNotExist(err) {
reportErrorf("Error loading configuration from '%s': %v\n", cfgPath, err)
if useDefault {
cfg = pingpong.DefaultConfig
} else {
cfg, err = pingpong.LoadConfigFromFile(dataDirCfgPath)
if err != nil && !os.IsNotExist(err) {
reportErrorf("Error loading configuration from '%s': %v\n", dataDirCfgPath, err)
}
}
}

if randSeed == 0 {
rand.Seed(time.Now().UnixNano())
} else {
rand.Seed(randSeed)
}

if srcAddress != "" {
cfg.SrcAccount = srcAddress
}
Expand All @@ -185,10 +226,12 @@ var runCmd = &cobra.Command{
cfg.MinAccountFunds = minAccountFunds
}

if txnPerSec == 0 {
if txnPerSec != 0 {
cfg.TxnPerSec = txnPerSec
}
if cfg.TxnPerSec == 0 {
reportErrorf("cannot set tps to 0")
}
cfg.TxnPerSec = txnPerSec

if randomFee {
if cfg.MinFee > cfg.MaxFee {
Expand All @@ -205,15 +248,15 @@ var runCmd = &cobra.Command{
if randomAmount {
cfg.RandomizeAmt = true
}
cfg.RandomLease = randomLease
cfg.RandomLease = randomLease || cfg.RandomLease
if noRandomAmount {
if randomAmount {
reportErrorf("Error --ra and --nra can't both be specified\n")
}
cfg.RandomizeAmt = false
}
cfg.RandomizeDst = randomDst
cfg.Quiet = quietish
cfg.RandomizeDst = randomDst || cfg.RandomizeDst
cfg.Quiet = quietish || cfg.Quiet
if runTime != "" {
val, err := strconv.ParseUint(runTime, 10, 32)
if err != nil {
Expand Down Expand Up @@ -274,17 +317,27 @@ var runCmd = &cobra.Command{
reportErrorf("Invalid group size: %v\n", groupSize)
}

if numAsset <= 1000 {
if numAsset == 0 {
// nop
} else if numAsset <= 1000 {
cfg.NumAsset = numAsset
} else {
reportErrorf("Invalid number of assets: %d, (valid number: 0 - 1000)\n", numAsset)
}

cfg.AppProgOps = appProgOps
cfg.AppProgHashes = appProgHashes
cfg.AppProgHashSize = appProgHashSize
if appProgOps != 0 {
cfg.AppProgOps = appProgOps
}
if appProgHashes != 0 {
cfg.AppProgHashes = appProgHashes
}
if appProgHashSize != "sha256" {
cfg.AppProgHashSize = appProgHashSize
}

if numApp <= 1000 {
if numApp == 0 {
// nop
} else if numApp <= 1000 {
cfg.NumApp = numApp
} else {
reportErrorf("Invalid number of apps: %d, (valid number: 0 - 1000)\n", numApp)
Expand All @@ -294,7 +347,9 @@ var runCmd = &cobra.Command{
reportErrorf("Cannot opt in %d times of %d total apps\n", numAppOptIn, numApp)
}

cfg.NumAppOptIn = numAppOptIn
if numAppOptIn != 0 {
cfg.NumAppOptIn = numAppOptIn
}

if appProgGlobKeys > 0 {
cfg.AppGlobKeys = appProgGlobKeys
Expand All @@ -303,10 +358,6 @@ var runCmd = &cobra.Command{
cfg.AppLocalKeys = appProgLocalKeys
}

if numAsset != 0 && numApp != 0 {
reportErrorf("only one of numapp and numasset may be specified\n")
}

if rekey {
cfg.Rekey = rekey
if !cfg.RandomLease && !cfg.RandomNote && !cfg.RandomizeFee && !cfg.RandomizeAmt {
Expand All @@ -317,28 +368,56 @@ var runCmd = &cobra.Command{
}
}

cfg.NftAsaPerSecond = nftAsaPerSecond
if nftAsaPerSecond != 0 {
cfg.NftAsaPerSecond = nftAsaPerSecond
}

if deterministicKeys && generatedAccountsCount == 0 {
reportErrorf("deterministicKeys requires setting generatedAccountsCount")
}
if !deterministicKeys && generatedAccountsCount > 0 {
reportErrorf("generatedAccountsCount requires deterministicKeys=true")
}
if deterministicKeys && numAccounts > generatedAccountsCount {
reportErrorf("numAccounts must be <= generatedAccountsCount")
}
cfg.DeterministicKeys = deterministicKeys || cfg.DeterministicKeys
if generatedAccountsCount != 0 {
cfg.GeneratedAccountsCount = generatedAccountsCount
}
if generatedAccountSampleMethod != "" {
cfg.GeneratedAccountSampleMethod = generatedAccountSampleMethod
}

cfg.SetDefaultWeights()
err = cfg.Check()
if err != nil {
reportErrorf("%v", err)
}

reportInfof("Preparing to initialize PingPong with config:\n")
cfg.Dump(os.Stdout)

pps := pingpong.NewPingpong(cfg)

// Initialize accounts if necessary
err = pps.PrepareAccounts(ac)
err = pps.PrepareAccounts(&ac)
if err != nil {
reportErrorf("Error preparing accounts for transfers: %v\n", err)
}

if saveConfig {
cfg.Save(cfgPath)
err = cfg.Save(dataDirCfgPath)
if err != nil {
reportErrorf("%s: could not save config, %v\n", dataDirCfgPath, err)
}
}

reportInfof("Preparing to run PingPong with config:\n")
cfg.Dump(os.Stdout)

// Kick off the real processing
pps.RunPingPong(context.Background(), ac)
pps.RunPingPong(context.Background(), &ac)
},
}

Expand Down
25 changes: 23 additions & 2 deletions libgoal/libgoal.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ type Client struct {
consensus config.ConsensusProtocols
algodVersionAffinity algodclient.APIVersion
kmdVersionAffinity kmdclient.APIVersion

suggestedParamsCache v1.TransactionParams
suggestedParamsExpire time.Time
suggestedParamsMaxAge time.Duration
}

// ClientConfig is data to configure a Client
Expand Down Expand Up @@ -513,7 +517,7 @@ func (c *Client) signAndBroadcastTransactionWithWallet(walletHandle, pw []byte,
// M | M | error
//
func (c *Client) ComputeValidityRounds(firstValid, lastValid, validRounds uint64) (first, last, latest uint64, err error) {
params, err := c.SuggestedParams()
params, err := c.cachedSuggestedParams()
if err != nil {
return 0, 0, 0, err
}
Expand Down Expand Up @@ -576,7 +580,7 @@ func (c *Client) ConstructPayment(from, to string, fee, amount uint64, note []by
}

// Get current round, protocol, genesis ID
params, err := c.SuggestedParams()
params, err := c.cachedSuggestedParams()
if err != nil {
return transactions.Transaction{}, err
}
Expand Down Expand Up @@ -920,6 +924,23 @@ func (c *Client) SuggestedParams() (params v1.TransactionParams, err error) {
return
}

// SetSuggestedParamsCacheAge sets the maximum age for an internal cached version of SuggestedParams() used internally to many libgoal Client functions.
func (c *Client) SetSuggestedParamsCacheAge(maxAge time.Duration) {
c.suggestedParamsMaxAge = maxAge
}

func (c *Client) cachedSuggestedParams() (params v1.TransactionParams, err error) {
if c.suggestedParamsMaxAge == 0 || time.Now().After(c.suggestedParamsExpire) {
params, err = c.SuggestedParams()
if err == nil && c.suggestedParamsMaxAge != 0 {
c.suggestedParamsCache = params
c.suggestedParamsExpire = time.Now().Add(c.suggestedParamsMaxAge)
}
return
}
return c.suggestedParamsCache, nil
}

// GetPendingTransactions gets a snapshot of current pending transactions on the node.
// If maxTxns = 0, fetches as many transactions as possible.
func (c *Client) GetPendingTransactions(maxTxns uint64) (resp v1.PendingTransactions, err error) {
Expand Down
14 changes: 7 additions & 7 deletions libgoal/transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/crypto/merklesignature"
"github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated"
"github.com/algorand/go-algorand/daemon/algod/api/spec/v1"
v1 "github.com/algorand/go-algorand/daemon/algod/api/spec/v1"
"github.com/algorand/go-algorand/data/account"
"github.com/algorand/go-algorand/data/basics"
"github.com/algorand/go-algorand/data/transactions"
Expand Down Expand Up @@ -253,7 +253,7 @@ func generateRegistrationTransaction(part generated.ParticipationKey, fee basics
func (c *Client) MakeRegistrationTransactionWithGenesisID(part account.Participation, fee, txnFirstValid, txnLastValid uint64, leaseBytes [32]byte, includeStateProofKeys bool) (transactions.Transaction, error) {

// Get current round, protocol, genesis ID
params, err := c.SuggestedParams()
params, err := c.cachedSuggestedParams()
if err != nil {
return transactions.Transaction{}, err
}
Expand Down Expand Up @@ -293,7 +293,7 @@ func (c *Client) MakeUnsignedGoOnlineTx(address string, firstValid, lastValid, f
}

// Get current round, protocol, genesis ID
params, err := c.SuggestedParams()
params, err := c.cachedSuggestedParams()
if err != nil {
return transactions.Transaction{}, err
}
Expand Down Expand Up @@ -350,7 +350,7 @@ func (c *Client) MakeUnsignedGoOfflineTx(address string, firstValid, lastValid,
return transactions.Transaction{}, err
}

params, err := c.SuggestedParams()
params, err := c.cachedSuggestedParams()
if err != nil {
return transactions.Transaction{}, err
}
Expand Down Expand Up @@ -405,7 +405,7 @@ func (c *Client) MakeUnsignedBecomeNonparticipatingTx(address string, firstValid
return transactions.Transaction{}, err
}

params, err := c.SuggestedParams()
params, err := c.cachedSuggestedParams()
if err != nil {
return transactions.Transaction{}, err
}
Expand Down Expand Up @@ -460,7 +460,7 @@ func (c *Client) FillUnsignedTxTemplate(sender string, firstValid, lastValid, fe
return transactions.Transaction{}, err
}

params, err := c.SuggestedParams()
params, err := c.cachedSuggestedParams()
if err != nil {
return transactions.Transaction{}, err
}
Expand Down Expand Up @@ -637,7 +637,7 @@ func (c *Client) MakeUnsignedAssetCreateTx(total uint64, defaultFrozen bool, man
}

// Get consensus params so we can get max field lengths
params, err := c.SuggestedParams()
params, err := c.cachedSuggestedParams()
if err != nil {
return transactions.Transaction{}, err
}
Expand Down
Loading