Skip to content

Commit

Permalink
Jesse/loadtest legacy mode (#92)
Browse files Browse the repository at this point in the history
* default to dynamic tx with legacy flag as option

* uncomment nonce

* suggest gas tip cap and calculate fee cap

* use london signer

* remove test line

* only legacy mode control force gas price

* pass in legacy flag to wisely force gas prices

* remove redundant option configure call & refractor

* remove some redundant arguments

* clean up

* use transactOpts function to determine signer

* remove header stuff

* just use transact opts

* push

* minor fix: legacy mode and priority gas fee

* revert fuzz

* variable name change for lint

* remove error catch

* gas tip price estimation only for non legacy

* lint :)

* hello

* swap again

* real gucci

* real fix

* feat: adding tx type to monitor

* Jesse/loadtest chain id query (#95)

* auto determine chain ID

* chain id 0

* chain id command

* Update cmd/loadtest/loadtest.go

Co-authored-by: John Hilliard <[email protected]>

* fix: minor duplicate log messages

---------

Co-authored-by: John Hilliard <[email protected]>

* last changes for logic

* gas price logic

* remove empty if statement

* 1559 aware monitor variables (#97)

* 1559 aware monitor variables

* put back

* add to struct

---------

Co-authored-by: John Hilliard <[email protected]>
  • Loading branch information
gatsbyz and praetoriansentry committed Jun 30, 2023
1 parent ab47b71 commit 3820c52
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 19 deletions.
110 changes: 91 additions & 19 deletions cmd/loadtest/loadtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"crypto/ecdsa"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"math"
Expand Down Expand Up @@ -153,8 +154,6 @@ var LoadtestCmd = &cobra.Command{
Short: "A simple script for quickly running a load test",
Long: `Loadtest gives us a simple way to run a generic load test against an eth/EVM style json RPC endpoint`,
RunE: func(cmd *cobra.Command, args []string) error {
log.Debug().Msg("Starting Loadtest")

err := runLoadTest(cmd.Context())
if err != nil {
return err
Expand Down Expand Up @@ -236,16 +235,20 @@ type (
ForceContractDeploy *bool
ForceGasLimit *uint64
ForceGasPrice *uint64
ForcePriorityGasPrice *uint64
ShouldProduceSummary *bool
SummaryOutputMode *string
LegacyTransactionMode *bool

// Computed
CurrentGas *big.Int
CurrentNonce *uint64
ECDSAPrivateKey *ecdsa.PrivateKey
FromETHAddress *ethcommon.Address
ToETHAddress *ethcommon.Address
SendAmount *big.Int
CurrentGas *big.Int
CurrentGasTipCap *big.Int
CurrentNonce *uint64
ECDSAPrivateKey *ecdsa.PrivateKey
FromETHAddress *ethcommon.Address
ToETHAddress *ethcommon.Address
SendAmount *big.Int
BaseFee *big.Int

ToAvailAddress *gstypes.MultiAddress
FromAvailAddress *gssignature.KeyringPair
Expand All @@ -268,7 +271,7 @@ func init() {

// extended parameters
ltp.PrivateKey = LoadtestCmd.PersistentFlags().String("private-key", codeQualityPrivateKey, "The hex encoded private key that we'll use to sending transactions")
ltp.ChainID = LoadtestCmd.PersistentFlags().Uint64("chain-id", 1256, "The chain id for the transactions that we're going to send")
ltp.ChainID = LoadtestCmd.PersistentFlags().Uint64("chain-id", 0, "The chain id for the transactions that we're going to send")
ltp.ToAddress = LoadtestCmd.PersistentFlags().String("to-address", "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF", "The address that we're going to send to")
ltp.ToRandom = LoadtestCmd.PersistentFlags().Bool("to-random", false, "When doing a transfer test, should we send to random addresses rather than DEADBEEFx5")
ltp.HexSendAmount = LoadtestCmd.PersistentFlags().String("send-amount", "0x38D7EA4C68000", "The amount of wei that we'll send every transaction")
Expand Down Expand Up @@ -302,9 +305,11 @@ r - random modes
ltp.ForceContractDeploy = LoadtestCmd.PersistentFlags().Bool("force-contract-deploy", false, "Some loadtest modes don't require a contract deployment. Set this flag to true to force contract deployments. This will still respect the --del-address and --il-address flags.")
ltp.ForceGasLimit = LoadtestCmd.PersistentFlags().Uint64("gas-limit", 0, "In environments where the gas limit can't be computed on the fly, we can specify it manually")
ltp.ForceGasPrice = LoadtestCmd.PersistentFlags().Uint64("gas-price", 0, "In environments where the gas price can't be estimated, we can specify it manually")
ltp.ForcePriorityGasPrice = LoadtestCmd.PersistentFlags().Uint64("priority-gas-price", 0, "Specify Gas Tip Price in the case of EIP-1559")
ltp.ShouldProduceSummary = LoadtestCmd.PersistentFlags().Bool("summarize", false, "Should we produce an execution summary after the load test has finished. If you're running a large loadtest, this can take a long time")
ltp.BatchSize = LoadtestCmd.PersistentFlags().Uint64("batch-size", 999, "Number of batches to perform at a time for receipt fetching. Default is 999 requests at a time.")
ltp.SummaryOutputMode = LoadtestCmd.PersistentFlags().String("output-mode", "text", "Format mode for summary output (json | text)")
ltp.LegacyTransactionMode = LoadtestCmd.PersistentFlags().Bool("legacy", false, "Send a legacy transaction instead of an EIP1559 transaction.")
inputLoadTestParams = *ltp

// TODO batch size
Expand All @@ -321,6 +326,16 @@ func initializeLoadTestParams(ctx context.Context, c *ethclient.Client) error {
}
log.Trace().Interface("gasprice", gas).Msg("Retreived current gas price")

if !*inputLoadTestParams.LegacyTransactionMode {
gasTipCap, _err := c.SuggestGasTipCap(ctx)
if _err != nil {
log.Error().Err(_err).Msg("Unable to retrieve gas tip cap")
return _err
}
log.Trace().Interface("gastipcap", gasTipCap).Msg("Retreived current gas tip cap")
inputLoadTestParams.CurrentGasTipCap = gasTipCap
}

privateKey, err := ethcrypto.HexToECDSA(*inputLoadTestParams.PrivateKey)
if err != nil {
log.Error().Err(err).Msg("Couldn't process the hex private key")
Expand Down Expand Up @@ -357,12 +372,37 @@ func initializeLoadTestParams(ctx context.Context, c *ethclient.Client) error {
return err
}

header, err := c.HeaderByNumber(ctx, nil)
if err != nil {
log.Error().Err(err).Msg("Unable to get header")
return err
}

chainID, err := c.ChainID(ctx)
if err != nil {
log.Error().Err(err).Msg("Unable to fetch chain ID")
return err
}
log.Trace().Uint64("chainID", chainID.Uint64()).Msg("Detected Chain ID")

if *inputLoadTestParams.LegacyTransactionMode && *inputLoadTestParams.ForcePriorityGasPrice > 0 {
log.Warn().Msg("Cannot set priority gas price in legacy mode")
}
if *inputLoadTestParams.ForceGasPrice < *inputLoadTestParams.ForcePriorityGasPrice {
log.Error().Msg("Max priority fee per gas higher than max fee per gas")
return errors.New("max priority fee per gas higher than max fee per gas")
}

inputLoadTestParams.ToETHAddress = &toAddr
inputLoadTestParams.SendAmount = amt
inputLoadTestParams.CurrentGas = gas
inputLoadTestParams.CurrentNonce = &nonce
inputLoadTestParams.ECDSAPrivateKey = privateKey
inputLoadTestParams.FromETHAddress = &ethAddress
if *inputLoadTestParams.ChainID == 0 {
*inputLoadTestParams.ChainID = chainID.Uint64()
}
inputLoadTestParams.BaseFee = header.BaseFee

rand.Seed(*inputLoadTestParams.Seed)

Expand Down Expand Up @@ -427,7 +467,6 @@ func runLoadTest(ctx context.Context) error {
}

} else {
log.Info().Msg("Starting Load Test")
loopFunc = func() error {
err = initializeLoadTestParams(ctx, ec)
if err != nil {
Expand Down Expand Up @@ -667,8 +706,6 @@ func mainLoop(ctx context.Context, c *ethclient.Client, rpc *ethrpc.Client) erro
}

tops.Nonce = new(big.Int).SetUint64(currentNonce)
tops = configureTransactOpts(tops)
tops.GasLimit = 10000000

_, err = erc20Contract.Mint(tops, metrics.UnitMegaether)
if err != nil {
Expand Down Expand Up @@ -720,8 +757,6 @@ func mainLoop(ctx context.Context, c *ethclient.Client, rpc *ethrpc.Client) erro
}

tops.Nonce = new(big.Int).SetUint64(currentNonce)
tops = configureTransactOpts(tops)
tops.GasLimit = 10000000

err = blockUntilSuccessful(ctx, c, func() error {
_, err = erc721Contract.MintBatch(tops, *ltp.FromETHAddress, new(big.Int).SetUint64(1))
Expand Down Expand Up @@ -952,8 +987,6 @@ func blockUntilSuccessful(ctx context.Context, c *ethclient.Client, f func() err
func loadtestTransaction(ctx context.Context, c *ethclient.Client, nonce uint64) (t1 time.Time, t2 time.Time, err error) {
ltp := inputLoadTestParams

gasPrice := ltp.CurrentGas

to := ltp.ToETHAddress
if *ltp.ToRandom {
to = getRandomAddress()
Expand All @@ -963,9 +996,34 @@ func loadtestTransaction(ctx context.Context, c *ethclient.Client, nonce uint64)
chainID := new(big.Int).SetUint64(*ltp.ChainID)
privateKey := ltp.ECDSAPrivateKey

gasLimit := uint64(21000)
tx := ethtypes.NewTransaction(nonce, *to, amount, gasLimit, gasPrice, nil)
stx, err := ethtypes.SignTx(tx, ethtypes.NewEIP155Signer(chainID), privateKey)
tops, err := bind.NewKeyedTransactorWithChainID(privateKey, chainID)
if err != nil {
log.Error().Err(err).Msg("Unable create transaction signer")
return
}
tops.GasLimit = uint64(21000)
tops = configureTransactOpts(tops)

var tx *ethtypes.Transaction
if *ltp.LegacyTransactionMode {
tx = ethtypes.NewTransaction(nonce, *to, amount, tops.GasLimit, tops.GasPrice, nil)
} else {
gasTipCap := tops.GasTipCap
gasFeeCap := new(big.Int).Add(gasTipCap, ltp.BaseFee)
dynamicFeeTx := &ethtypes.DynamicFeeTx{
ChainID: chainID,
Nonce: nonce,
To: to,
Gas: tops.GasLimit,
GasFeeCap: gasFeeCap,
GasTipCap: gasTipCap,
Data: nil,
Value: amount,
}
tx = ethtypes.NewTx(dynamicFeeTx)
}

stx, err := tops.Signer(*ltp.FromETHAddress, tx)
if err != nil {
log.Error().Err(err).Msg("Unable to sign transaction")
return
Expand Down Expand Up @@ -1490,6 +1548,20 @@ func configureTransactOpts(tops *bind.TransactOpts) *bind.TransactOpts {
ltp := inputLoadTestParams
if ltp.ForceGasPrice != nil && *ltp.ForceGasPrice != 0 {
tops.GasPrice = big.NewInt(0).SetUint64(*ltp.ForceGasPrice)
} else {
tops.GasPrice = ltp.CurrentGas
}
if !*ltp.LegacyTransactionMode {
if ltp.ForceGasPrice != nil && *ltp.ForceGasPrice != 0 {
tops.GasPrice = big.NewInt(0).SetUint64(*ltp.ForceGasPrice)
} else {
tops.GasPrice = big.NewInt(0).Add(ltp.BaseFee, ltp.CurrentGasTipCap)
}
if ltp.ForcePriorityGasPrice != nil && *ltp.ForcePriorityGasPrice != 0 {
tops.GasTipCap = big.NewInt(0).SetUint64(*ltp.ForcePriorityGasPrice)
} else {
tops.GasTipCap = ltp.CurrentGasTipCap
}
}
if ltp.ForceGasLimit != nil && *ltp.ForceGasLimit != 0 {
tops.GasLimit = *ltp.ForceGasLimit
Expand Down
3 changes: 3 additions & 0 deletions metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,10 @@ func GetSimpleTxFields(tx rpctypes.PolyTransaction, chainID, baseFee *big.Int) [
fields = append(fields, fmt.Sprintf("Value: %s", tx.Value()))
fields = append(fields, fmt.Sprintf("Gas Limit: %d", tx.Gas()))
fields = append(fields, fmt.Sprintf("Gas Price: %s", tx.GasPrice()))
fields = append(fields, fmt.Sprintf("Gas Tip: %d", tx.MaxPriorityFeePerGas()))
fields = append(fields, fmt.Sprintf("Gas Fee: %d", tx.MaxFeePerGas()))
fields = append(fields, fmt.Sprintf("Nonce: %d", tx.Nonce()))
fields = append(fields, fmt.Sprintf("Type: %d", tx.Type()))
fields = append(fields, fmt.Sprintf("Data Len: %d", len(tx.Data())))
fields = append(fields, fmt.Sprintf("Data: %s", hex.EncodeToString(tx.Data())))

Expand Down
18 changes: 18 additions & 0 deletions rpctypes/rpctypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ type (
// gasPrice: QUANTITY - gas price provided by the sender in Wei.
GasPrice RawQuantityResponse `json:"gasPrice"`

// gas: QUANTITY - gas provided by the sender.
MaxPriorityFeePerGas RawQuantityResponse `json:"maxPriorityFeePerGas"`

// gas: QUANTITY - gas provided by the sender.
MaxFeePerGas RawQuantityResponse `json:"maxFeePerGas"`

// hash: DATA, 32 Bytes - hash of the transaction.
Hash RawData32Response `json:"hash"`

Expand Down Expand Up @@ -215,6 +221,9 @@ type (
Nonce() uint64
String() string
MarshalJSON() ([]byte, error)
Type() uint64
MaxPriorityFeePerGas() uint64
MaxFeePerGas() uint64
V() *big.Int
R() *big.Int
S() *big.Int
Expand Down Expand Up @@ -342,9 +351,18 @@ func (i *implPolyTransaction) GasPrice() *big.Int {
func (i *implPolyTransaction) Gas() uint64 {
return i.inner.Gas.ToUint64()
}
func (i *implPolyTransaction) MaxPriorityFeePerGas() uint64 {
return i.inner.MaxPriorityFeePerGas.ToUint64()
}
func (i *implPolyTransaction) MaxFeePerGas() uint64 {
return i.inner.MaxFeePerGas.ToUint64()
}
func (i *implPolyTransaction) Nonce() uint64 {
return i.inner.Nonce.ToUint64()
}
func (i *implPolyTransaction) Type() uint64 {
return i.inner.Type.ToUint64()
}
func (i *implPolyTransaction) Value() *big.Int {
return i.inner.Value.ToBigInt()
}
Expand Down

0 comments on commit 3820c52

Please sign in to comment.