Skip to content

Commit b15f8aa

Browse files
committed
add loadtest support to a file with private keys to set the sending addresses
1 parent a04b772 commit b15f8aa

File tree

6 files changed

+72
-5
lines changed

6 files changed

+72
-5
lines changed

cmd/loadtest/account.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,18 @@ func (ap *AccountPool) AddRandom() error {
136136
return ap.Add(privateKey)
137137
}
138138

139+
// Adds multiple accounts to the pool with the given private keys
140+
func (ap *AccountPool) AddN(privateKeys ...*ecdsa.PrivateKey) error {
141+
for _, privateKey := range privateKeys {
142+
err := ap.Add(privateKey)
143+
if err != nil {
144+
return fmt.Errorf("failed to add account: %w", err)
145+
}
146+
}
147+
148+
return nil
149+
}
150+
139151
// Adds an account to the pool with the given private key
140152
func (ap *AccountPool) Add(privateKey *ecdsa.PrivateKey) error {
141153
ap.mu.Lock()

cmd/loadtest/app.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ type (
8686
AddressFundingAmount *uint64
8787
PreFundSendingAddresses *bool
8888
KeepFundedAmount *bool
89+
SendingAddressesFile *string
8990

9091
// Computed
9192
CurrentGasPrice *big.Int
@@ -250,6 +251,7 @@ func initFlags() {
250251
ltp.AddressFundingAmount = LoadtestCmd.Flags().Uint64("address-funding-amount", 1000000000000000000, "The amount in gwei to fund the sending addresses with.")
251252
ltp.PreFundSendingAddresses = LoadtestCmd.Flags().Bool("pre-fund-sending-addresses", false, "If set to true, the sending addresses will be fund at the start of the execution, otherwise all addresses will be funded when used for the first time.")
252253
ltp.KeepFundedAmount = LoadtestCmd.Flags().Bool("keep-funded-amount", false, "If set to true, the funded amount will be kept in the sending addresses. Otherwise, the funded amount will be refunded back to the account used to fund the account.")
254+
ltp.SendingAddressesFile = LoadtestCmd.Flags().String("sending-addresses-file", "", "The file containing the sending addresses private keys, one per line. This is useful for avoiding pool account queue but also to keep the same sending addresses for different execution cycles.")
253255

254256
// Local flags.
255257
ltp.Modes = LoadtestCmd.Flags().StringSliceP("mode", "m", []string{"t"}, `The testing mode to use. It can be multiple like: "c,d,f,t"

cmd/loadtest/loadtest.go

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package loadtest
22

33
import (
4+
"bufio"
45
"context"
6+
"crypto/ecdsa"
57
_ "embed"
68
"encoding/hex"
79
"encoding/json"
@@ -329,10 +331,30 @@ func initializeLoadTestParams(ctx context.Context, c *ethclient.Client) error {
329331
// setup account pool
330332
fundingAmount := *inputLoadTestParams.AddressFundingAmount
331333
sendingAddressCount := *inputLoadTestParams.SendingAddressCount
334+
sendingAddressesFile := *inputLoadTestParams.SendingAddressesFile
332335
accountPool = NewAccountPool(ctx, c, privateKey, big.NewInt(0).SetUint64(fundingAmount))
333-
if sendingAddressCount > 1 {
336+
if len(sendingAddressesFile) > 0 {
337+
log.Trace().
338+
Str("sendingAddressFile", sendingAddressesFile).
339+
Msg("Adding accounts from file to the account pool")
340+
341+
privateKeys, iErr := readPrivateKeysFromFile(sendingAddressesFile)
342+
if iErr != nil {
343+
log.Error().
344+
Err(iErr).
345+
Msg("Unable to read private keys from file")
346+
return fmt.Errorf("unable to read private keys from file. %w", iErr)
347+
}
348+
err = accountPool.AddN(privateKeys...)
349+
} else if sendingAddressCount > 1 {
350+
log.Trace().
351+
Uint64("sendingAddressCount", sendingAddressCount).
352+
Msg("Adding random accounts to the account pool")
334353
err = accountPool.AddRandomN(sendingAddressCount)
335354
} else {
355+
log.Trace().
356+
Uint64("sendingAddressCount", sendingAddressCount).
357+
Msg("Using the same account for all transactions")
336358
err = accountPool.Add(privateKey)
337359
}
338360
if err != nil {
@@ -352,6 +374,35 @@ func initializeLoadTestParams(ctx context.Context, c *ethclient.Client) error {
352374
return nil
353375
}
354376

377+
func readPrivateKeysFromFile(sendingAddressesFile string) ([]*ecdsa.PrivateKey, error) {
378+
file, err := os.Open(sendingAddressesFile)
379+
if err != nil {
380+
return nil, fmt.Errorf("unable to open sending addresses file: %w", err)
381+
}
382+
defer file.Close()
383+
384+
var privateKeys []*ecdsa.PrivateKey
385+
scanner := bufio.NewScanner(file)
386+
for scanner.Scan() {
387+
line := strings.TrimSpace(scanner.Text())
388+
if len(line) == 0 {
389+
continue
390+
}
391+
privateKey, err := ethcrypto.HexToECDSA(strings.TrimPrefix(line, "0x"))
392+
if err != nil {
393+
log.Error().Err(err).Str("key", line).Msg("Unable to parse private key")
394+
return nil, fmt.Errorf("unable to parse private key: %w", err)
395+
}
396+
privateKeys = append(privateKeys, privateKey)
397+
}
398+
399+
if err := scanner.Err(); err != nil {
400+
return nil, fmt.Errorf("error reading sending address file: %w", err)
401+
}
402+
403+
return privateKeys, nil
404+
}
405+
355406
func initNonce(ctx context.Context, c *ethclient.Client) error {
356407
currentNonceMutex.Lock()
357408
defer currentNonceMutex.Unlock()

doc/polycli_fund.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ $ cast balance 0x5D8121cf716B70d3e345adB58157752304eED5C3
7979
```bash
8080
--addresses strings Comma-separated list of wallet addresses to fund
8181
--contract-address string The address of a pre-deployed Funder contract
82-
-a, --eth-amount float The amount of ether to send to each wallet (default 0.05)
82+
-a, --eth-amount uint The amount of ether in wei to send to each wallet (default 50000000000000000)
8383
-f, --file string The output JSON file path for storing the addresses and private keys of funded wallets (default "wallets.json")
8484
--hd-derivation Derive wallets to fund from the private key in a deterministic way (default true)
8585
-h, --help help for fund

doc/polycli_loadtest.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,18 +117,18 @@ The codebase has a contract that used for load testing. It's written in Solidity
117117
--contract-call-payable Use this flag if the function is payable, the value amount passed will be from --eth-amount. This must be paired up with --mode contract-call and --contract-address
118118
--erc20-address string The address of a pre-deployed ERC20 contract
119119
--erc721-address string The address of a pre-deployed ERC721 contract
120-
--eth-amount float The amount of ether to send on every transaction
120+
--eth-amount uint The amount of ether in wei to send on every transaction
121121
--force-contract-deploy Some load test modes don't require a contract deployment. Set this flag to true to force contract deployments. This will still respect the --lt-address flags.
122122
-f, --function uint A specific function to be called if running with --mode f or a specific precompiled contract when running with --mode a (default 1)
123123
--function-arg strings The arguments that will be passed to a contract function call. This must be paired up with "--mode contract-call" and "--contract-address". Args can be passed multiple times: "--function-arg 'test' --function-arg 999" or comma separated values "--function-arg "test",9". The ordering of the arguments must match the ordering of the function parameters.
124124
--function-signature string The contract's function signature that will be called. The format is '<function name>(<types...>)'. This must be paired up with '--mode contract-call' and '--contract-address'. If the function requires parameters you can pass them with '--function-arg <value>'.
125-
--fund-sending-addresses-on-demand If set to true, the sending addresses will be funded when used for the first time, otherwise all addresses will be fund at the start of the execution. (default true)
126125
--gas-limit uint In environments where the gas limit can't be computed on the fly, we can specify it manually. This can also be used to avoid eth_estimateGas
127126
--gas-price uint In environments where the gas price can't be determined automatically, we can specify it manually
128127
--gas-price-multiplier float A multiplier to increase or decrease the gas price (default 1)
129128
-h, --help help for loadtest
130129
--inscription-content string The inscription content that will be encoded as calldata. This must be paired up with --mode inscription (default "data:,{\"p\":\"erc-20\",\"op\":\"mint\",\"tick\":\"TEST\",\"amt\":\"1\"}")
131130
-i, --iterations uint If we're making contract calls, this controls how many times the contract will execute the instruction in a loop. If we are making ERC721 Mints, this indicates the minting batch size (default 1)
131+
--keep-funded-amount If set to true, the funded amount will be kept in the sending addresses. Otherwise, the funded amount will be refunded back to the account used to fund the account.
132132
--legacy Send a legacy transaction instead of an EIP1559 transaction.
133133
--lt-address string The address of a pre-deployed load test contract
134134
-m, --mode strings The testing mode to use. It can be multiple like: "c,d,f,t"
@@ -151,6 +151,7 @@ The codebase has a contract that used for load testing. It's written in Solidity
151151
v3, uniswapv3 - Perform UniswapV3 swaps (default [t])
152152
--nonce uint Use this flag to manually set the starting nonce
153153
--output-mode string Format mode for summary output (json | text) (default "text")
154+
--pre-fund-sending-addresses If set to true, the sending addresses will be fund at the start of the execution, otherwise all addresses will be funded when used for the first time.
154155
--priority-gas-price uint Specify Gas Tip Price in the case of EIP-1559
155156
--private-key string The hex encoded private key that we'll use to send transactions (default "42b6e34dc21598a807dc19d7784c71b2a7a01f6480dc6f58258f78e539f1a1fa")
156157
--rate-limit float An overall limit to the number of requests per second. Give a number less than zero to remove this limit all together (default 4)
@@ -160,6 +161,7 @@ The codebase has a contract that used for load testing. It's written in Solidity
160161
--seed int A seed for generating random values and addresses (default 123456)
161162
--send-only Send transactions and load without waiting for it to be mined.
162163
--sending-address-count uint The number of sending addresses to use. This is useful for avoiding pool account queue. (default 1)
164+
--sending-addresses-file string The file containing the sending addresses private keys, one per line. This is useful for avoiding pool account queue but also to keep the same sending addresses for different execution cycles.
163165
--steady-state-tx-pool-size uint When using adaptive rate limiting, this value sets the target queue size. If the queue is smaller than this value, we'll speed up. If the queue is smaller than this value, we'll back off. (default 1000)
164166
--summarize Should we produce an execution summary after the load test has finished. If you're running a large load test, this can take a long time
165167
-t, --time-limit int Maximum number of seconds to spend for benchmarking. Use this to benchmark within a fixed total amount of time. Per default there is no time limit. (default -1)

doc/polycli_loadtest_uniswapv3.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ The command also inherits flags from parent commands.
8686
--chain-id uint The chain id for the transactions.
8787
-c, --concurrency int Number of requests to perform concurrently. Default is one request at a time. (default 1)
8888
--config string config file (default is $HOME/.polygon-cli.yaml)
89-
--eth-amount float The amount of ether to send on every transaction
89+
--eth-amount uint The amount of ether in wei to send on every transaction
9090
--gas-limit uint In environments where the gas limit can't be computed on the fly, we can specify it manually. This can also be used to avoid eth_estimateGas
9191
--gas-price uint In environments where the gas price can't be determined automatically, we can specify it manually
9292
--gas-price-multiplier float A multiplier to increase or decrease the gas price (default 1)

0 commit comments

Comments
 (0)