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

Automatic gas in load tests #12416

Merged
merged 38 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
b0c312e
use experimental seth, use dynamic gas also when sending funds
Tofel Mar 13, 2024
3edf8ea
fix a situation, when we lose transaction timeout setting for network…
Tofel Mar 13, 2024
74e384e
Merge branch 'fix_network_override' into experiment_automatic_gas
Tofel Mar 13, 2024
98b4519
go mod tidy
davidcauchi Mar 13, 2024
31a2bd4
add gas limit for Fiji, fix a situation when new networks were ignored
Tofel Mar 14, 2024
22b3d06
Merge branch 'fix_network_override' into experiment_automatic_gas
Tofel Mar 14, 2024
8e8a690
update Seth version
Tofel Mar 14, 2024
b9f9fc5
fix lints
Tofel Mar 14, 2024
d9bb497
Merge branch 'fix_network_override' into experiment_automatic_gas
Tofel Mar 14, 2024
e7d33dc
fix lints
Tofel Mar 14, 2024
6c21266
newer seth
Tofel Mar 15, 2024
776e73f
newer Seth
Tofel Mar 15, 2024
a1eabad
Merge branch 'develop' into experiment_automatic_gas
Tofel Mar 15, 2024
f3e6253
use transfer fee not gas limit for sending funds, modify defaults for…
Tofel Mar 15, 2024
19bfeef
use latest Seth that uses block headers not entire blocks
Tofel Mar 15, 2024
d7c64b1
try new Seth config; more refund logic
Tofel Mar 18, 2024
354e42b
use latest seth, fix fund return issue where if a retry was used, onl…
Tofel Mar 19, 2024
e7e22c1
Merge branch 'develop' into experiment_automatic_gas
Tofel Mar 19, 2024
96aa2f0
do not return, but continue, if one node has has less balance than tx…
Tofel Mar 19, 2024
600132a
go mod tidy
davidcauchi Mar 20, 2024
4148869
Merge branch 'BCF-2835-disable-backup-lp' of github.com:smartcontract…
Tofel Mar 21, 2024
676db2a
validate seth config before creating k8s env in ocr soak test, better…
Tofel Mar 21, 2024
c2fed33
init seth client, before staring ocr soak test
Tofel Mar 21, 2024
860d9de
fix complile errors
Tofel Mar 21, 2024
3f6aaf2
go mod
Tofel Mar 21, 2024
ac63f97
use latest seth
Tofel Mar 21, 2024
b3658fa
Merge branch 'develop' into experiment_automatic_gas
Tofel Apr 2, 2024
95e388a
couple of renames and streamlines
Tofel Apr 2, 2024
d9b5d6e
use Seth Network urls if provided, otherwise take url from evmnetwork
Tofel Apr 2, 2024
c54e9f8
testconfig will now correctly use custom EVMNetwork
Tofel Apr 2, 2024
8d64567
latest Seth; set gas limit to 0 in TOMLs, so that it can be estimated…
Tofel Apr 2, 2024
7a58778
go mod
Tofel Apr 2, 2024
b48b9e0
Merge branch 'develop' into experiment_automatic_gas
Tofel Apr 2, 2024
2d99a31
skip funds return for given CL node if balance is 0
Tofel Apr 3, 2024
31eef68
latest seth
Tofel Apr 3, 2024
dda1869
Merge branch 'develop' into experiment_automatic_gas
Tofel Apr 8, 2024
4a43eb7
gomodtidy + latest seth
Tofel Apr 8, 2024
20d7574
fix load compile
Tofel Apr 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 57 additions & 15 deletions integration-tests/actions/seth/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,17 @@ type FundsToSendPayload struct {
ToAddress common.Address
Amount *big.Int
PrivateKey *ecdsa.PrivateKey
GasLimit *uint64
GasLimit *int64
GasPrice *big.Int
GasFeeCap *big.Int
GasTipCap *big.Int
TxTimeout *time.Duration
}

// TODO: move to CTF?
// SendFunds sends native token amount (expressed in human-scale) from address controlled by private key
// to given address. If no gas limit is set, then network's default will be used.
// to given address. You can override any or none of the following: gas limit, gas price, gas fee cap, gas tip cap.
// Values that are not set will be estimated or taken from config.
func SendFunds(logger zerolog.Logger, client *seth.Client, payload FundsToSendPayload) (*types.Receipt, error) {
fromAddress, err := privateKeyToAddress(payload.PrivateKey)
if err != nil {
Expand All @@ -117,38 +122,75 @@ func SendFunds(logger zerolog.Logger, client *seth.Client, payload FundsToSendPa
}

gasLimit := uint64(client.Cfg.Network.TransferGasFee)
gasPrice := big.NewInt(0)
gasFeeCap := big.NewInt(0)
gasTipCap := big.NewInt(0)

if payload.GasLimit != nil {
gasLimit = *payload.GasLimit
gasLimit = uint64(*payload.GasLimit)
}

var signedTx *types.Transaction
if client.Cfg.Network.EIP1559DynamicFees {
// if any of the dynamic fees are not set, we need to either estimate them or read them from config
if payload.GasFeeCap == nil || payload.GasTipCap == nil {
// estimatior or config reading happens here
txOptions := client.NewTXOpts(seth.WithGasLimit(gasLimit))
gasFeeCap = txOptions.GasFeeCap
gasTipCap = txOptions.GasTipCap
}

// override with payload values if they are set
if payload.GasFeeCap != nil {
gasFeeCap = payload.GasFeeCap
}

if payload.GasTipCap != nil {
gasTipCap = payload.GasTipCap
}
}

if !client.Cfg.Network.EIP1559DynamicFees {
if payload.GasPrice == nil {
txOptions := client.NewTXOpts((seth.WithGasLimit(gasLimit)))
gasPrice = txOptions.GasPrice
} else {
gasPrice = payload.GasPrice
}
}

var rawTx types.TxData

if client.Cfg.Network.EIP1559DynamicFees {
rawTx := &types.DynamicFeeTx{
rawTx = &types.DynamicFeeTx{
Nonce: nonce,
To: &payload.ToAddress,
Value: payload.Amount,
Gas: gasLimit,
GasFeeCap: big.NewInt(client.Cfg.Network.GasFeeCap),
GasTipCap: big.NewInt(client.Cfg.Network.GasTipCap),
GasFeeCap: gasFeeCap,
GasTipCap: gasTipCap,
}
signedTx, err = types.SignNewTx(payload.PrivateKey, types.NewLondonSigner(big.NewInt(client.ChainID)), rawTx)
} else {
rawTx := &types.LegacyTx{
rawTx = &types.LegacyTx{
Nonce: nonce,
To: &payload.ToAddress,
Value: payload.Amount,
Gas: gasLimit,
GasPrice: big.NewInt(client.Cfg.Network.GasPrice),
GasPrice: gasPrice,
}
signedTx, err = types.SignNewTx(payload.PrivateKey, types.NewEIP155Signer(big.NewInt(client.ChainID)), rawTx)
}

signedTx, err := types.SignNewTx(payload.PrivateKey, types.LatestSignerForChainID(big.NewInt(client.ChainID)), rawTx)

if err != nil {
return nil, errors.Wrap(err, "failed to sign tx")
}

ctx, cancel = context.WithTimeout(ctx, client.Cfg.Network.TxnTimeout.Duration())
txTimeout := client.Cfg.Network.TxnTimeout.Duration()
if payload.TxTimeout != nil {
txTimeout = *payload.TxTimeout
}

ctx, cancel = context.WithTimeout(ctx, txTimeout)
defer cancel()
err = client.Client.SendTransaction(ctx, signedTx)
if err != nil {
Expand All @@ -162,9 +204,9 @@ func SendFunds(logger zerolog.Logger, client *seth.Client, payload FundsToSendPa
Str("Amount", conversions.WeiToEther(payload.Amount).String()).
Uint64("Nonce", nonce).
Uint64("Gas Limit", gasLimit).
Int64("Gas Price", client.Cfg.Network.GasPrice).
Int64("Gas Fee Cap", client.Cfg.Network.GasFeeCap).
Int64("Gas Tip Cap", client.Cfg.Network.GasTipCap).
Str("Gas Price", gasPrice.String()).
Str("Gas Fee Cap", gasFeeCap.String()).
Str("Gas Tip Cap", gasTipCap.String()).
Bool("Dynamic fees", client.Cfg.Network.EIP1559DynamicFees).
Msg("Sent funds")

Expand Down
82 changes: 58 additions & 24 deletions integration-tests/actions/seth/refund.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"regexp"
"strconv"
"strings"
"time"

"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
Expand All @@ -19,7 +20,7 @@ import (

"github.com/smartcontractkit/chainlink-testing-framework/blockchain"

"github.com/smartcontractkit/chainlink/integration-tests/client"
clClient "github.com/smartcontractkit/chainlink/integration-tests/client"
"github.com/smartcontractkit/chainlink/integration-tests/contracts"
)

Expand Down Expand Up @@ -103,7 +104,7 @@ func (r *InsufficientFundTransferRetrier) Retry(ctx context.Context, logger zero
// by doubling the gas limit and retrying until reaching maxGasLimit
type GasTooLowTransferRetrier struct {
nextRetrier TransactionRetrier
maxGasLimit uint64
maxGasLimit int64
}

func (r *GasTooLowTransferRetrier) Retry(ctx context.Context, logger zerolog.Logger, client *seth.Client, txErr error, payload FundsToSendPayload, currentAttempt int) error {
Expand All @@ -120,18 +121,18 @@ func (r *GasTooLowTransferRetrier) Retry(ctx context.Context, logger zerolog.Log
for txErr != nil && strings.Contains(txErr.Error(), GasTooLowErr) {
logger.Info().
Msg("Too low gas error detected, retrying with more gas")
var newGasLimit uint64
var newGasLimit int64
if payload.GasLimit != nil {
newGasLimit = *payload.GasLimit * 2
} else {
newGasLimit = uint64(client.Cfg.Network.TransferGasFee) * 2
newGasLimit = client.Cfg.Network.TransferGasFee * 2
}

logger.Debug().
Str("retier", "GasTooLowTransferRetrier").
Uint64("old gas limit", newGasLimit/2).
Uint64("new gas limit", newGasLimit).
Uint64("diff", newGasLimit).
Int64("old gas limit", newGasLimit/2).
Int64("new gas limit", newGasLimit).
Int64("diff", newGasLimit).
Msg("New gas limit to use")

payload.GasLimit = &newGasLimit
Expand Down Expand Up @@ -231,21 +232,21 @@ func (r *OvershotTransferRetrier) Retry(ctx context.Context, logger zerolog.Logg
// ReturnFunds returns funds from the given chainlink nodes to the default network wallet. It will use a variety
// of strategies to attempt to return funds, including retrying with less funds if the transaction fails due to
// insufficient funds, and retrying with a higher gas limit if the transaction fails due to gas too low.
func ReturnFunds(log zerolog.Logger, seth *seth.Client, chainlinkNodes []contracts.ChainlinkNodeWithKeysAndAddress) error {
if seth == nil {
func ReturnFunds(log zerolog.Logger, sethClient *seth.Client, chainlinkNodes []contracts.ChainlinkNodeWithKeysAndAddress) error {
if sethClient == nil {
return fmt.Errorf("Seth client is nil, unable to return funds from chainlink nodes")
}
log.Info().Msg("Attempting to return Chainlink node funds to default network wallets")
if seth.Cfg.IsSimulatedNetwork() {
log.Info().Str("Network Name", seth.Cfg.Network.Name).
if sethClient.Cfg.IsSimulatedNetwork() {
log.Info().Str("Network Name", sethClient.Cfg.Network.Name).
Msg("Network is a simulated network. Skipping fund return.")
return nil
}

failedReturns := []common.Address{}

for _, chainlinkNode := range chainlinkNodes {
fundedKeys, err := chainlinkNode.ExportEVMKeysForChain(fmt.Sprint(seth.ChainID))
fundedKeys, err := chainlinkNode.ExportEVMKeysForChain(fmt.Sprint(sethClient.ChainID))
if err != nil {
return err
}
Expand All @@ -256,7 +257,7 @@ func ReturnFunds(log zerolog.Logger, seth *seth.Client, chainlinkNodes []contrac
}
// This can take up a good bit of RAM and time. When running on the remote-test-runner, this can lead to OOM
// issues. So we avoid running in parallel; slower, but safer.
decryptedKey, err := keystore.DecryptKey(keyToDecrypt, client.ChainlinkKeyPassword)
decryptedKey, err := keystore.DecryptKey(keyToDecrypt, clClient.ChainlinkKeyPassword)
if err != nil {
return err
}
Expand All @@ -268,24 +269,48 @@ func ReturnFunds(log zerolog.Logger, seth *seth.Client, chainlinkNodes []contrac
}
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)

balance, err := seth.Client.BalanceAt(context.Background(), fromAddress, nil)
balance, err := sethClient.Client.BalanceAt(context.Background(), fromAddress, nil)
if err != nil {
return err
}

var totalGasCost *big.Int
if seth.Cfg.Network.EIP1559DynamicFees {
totalGasCost = new(big.Int).Mul(big.NewInt(0).SetInt64(seth.Cfg.Network.TransferGasFee), big.NewInt(0).SetInt64(seth.Cfg.Network.GasFeeCap))
if balance.Cmp(big.NewInt(0)) == 0 {
log.Info().
Str("Address", fromAddress.String()).
Msg("No balance to return. Skipping return.")
}

// if not set, it will be just set to empty string, which is okay as long as gas estimation is disabled
txPriority := sethClient.Cfg.Network.GasEstimationTxPriority
txTimeout := sethClient.Cfg.Network.TxnTimeout.Duration()

if sethClient.Cfg.IsExperimentEnabled(seth.Experiment_SlowFundsReturn) {
txPriority = "slow"
thirtyMinutes := time.Duration(30 * time.Minute)
txTimeout = thirtyMinutes
}

estimations := sethClient.CalculateGasEstimations(seth.GasEstimationRequest{
GasEstimationEnabled: sethClient.Cfg.Network.GasEstimationEnabled,
FallbackGasPrice: sethClient.Cfg.Network.GasPrice,
FallbackGasFeeCap: sethClient.Cfg.Network.GasFeeCap,
FallbackGasTipCap: sethClient.Cfg.Network.GasTipCap,
Priority: txPriority,
})

var maxTotalGasCost *big.Int
if sethClient.Cfg.Network.EIP1559DynamicFees {
maxTotalGasCost = new(big.Int).Mul(big.NewInt(0).SetInt64(sethClient.Cfg.Network.TransferGasFee), estimations.GasFeeCap)
} else {
totalGasCost = new(big.Int).Mul(big.NewInt(0).SetInt64(seth.Cfg.Network.TransferGasFee), big.NewInt(0).SetInt64(seth.Cfg.Network.GasPrice))
maxTotalGasCost = new(big.Int).Mul(big.NewInt(0).SetInt64(sethClient.Cfg.Network.TransferGasFee), estimations.GasPrice)
}

toSend := new(big.Int).Sub(balance, totalGasCost)
toSend := new(big.Int).Sub(balance, maxTotalGasCost)

if toSend.Cmp(big.NewInt(0)) <= 0 {
log.Warn().
Str("Address", fromAddress.String()).
Str("Estimated total cost", totalGasCost.String()).
Str("Estimated maximum total gas cost", maxTotalGasCost.String()).
Str("Balance", balance.String()).
Str("To send", toSend.String()).
Msg("Not enough balance to cover gas cost. Skipping return.")
Expand All @@ -294,12 +319,21 @@ func ReturnFunds(log zerolog.Logger, seth *seth.Client, chainlinkNodes []contrac
continue
}

payload := FundsToSendPayload{ToAddress: seth.Addresses[0], Amount: toSend, PrivateKey: decryptedKey.PrivateKey}
payload := FundsToSendPayload{
ToAddress: sethClient.Addresses[0],
Amount: toSend,
PrivateKey: decryptedKey.PrivateKey,
GasLimit: &sethClient.Cfg.Network.TransferGasFee,
GasPrice: estimations.GasPrice,
GasFeeCap: estimations.GasFeeCap,
GasTipCap: estimations.GasTipCap,
TxTimeout: &txTimeout,
}

_, err = SendFunds(log, seth, payload)
_, err = SendFunds(log, sethClient, payload)
if err != nil {
handler := OvershotTransferRetrier{maxRetries: 10, nextRetrier: &InsufficientFundTransferRetrier{maxRetries: 10, nextRetrier: &GasTooLowTransferRetrier{maxGasLimit: uint64(seth.Cfg.Network.TransferGasFee * 10)}}}
err = handler.Retry(context.Background(), log, seth, err, payload, 0)
handler := OvershotTransferRetrier{maxRetries: 10, nextRetrier: &InsufficientFundTransferRetrier{maxRetries: 10, nextRetrier: &GasTooLowTransferRetrier{maxGasLimit: sethClient.Cfg.Network.TransferGasFee * 10}}}
err = handler.Retry(context.Background(), log, sethClient, err, payload, 0)
if err != nil {
log.Error().
Err(err).
Expand Down
3 changes: 2 additions & 1 deletion integration-tests/chaos/ocr_chaos_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ func TestOCRChaos(t *testing.T) {
network := networks.MustGetSelectedNetworkConfig(cfg.GetNetworkConfig())[0]
network = utils.MustReplaceSimulatedNetworkUrlWithK8(l, network, *testEnvironment)

sethCfg := utils.MergeSethAndEvmNetworkConfigs(l, network, *readSethCfg)
sethCfg, err := utils.MergeSethAndEvmNetworkConfigs(network, *readSethCfg)
require.NoError(t, err, "Error merging seth and evm network configs")
err = utils.ValidateSethNetworkConfig(sethCfg.Network)
require.NoError(t, err, "Error validating seth network config")
seth, err := seth.NewClientWithConfig(&sethCfg)
Expand Down
10 changes: 8 additions & 2 deletions integration-tests/docker/test_env/test_env_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,10 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) {

if b.hasSeth {
readSethCfg := b.testConfig.GetSethConfig()
sethCfg := utils.MergeSethAndEvmNetworkConfigs(b.l, networkConfig, *readSethCfg)
sethCfg, err := utils.MergeSethAndEvmNetworkConfigs(networkConfig, *readSethCfg)
if err != nil {
return nil, err
}
err = utils.ValidateSethNetworkConfig(sethCfg.Network)
if err != nil {
return nil, err
Expand Down Expand Up @@ -421,7 +424,10 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) {
if b.hasSeth {
b.te.sethClients = make(map[int64]*seth.Client)
readSethCfg := b.testConfig.GetSethConfig()
sethCfg := utils.MergeSethAndEvmNetworkConfigs(b.l, networkConfig, *readSethCfg)
sethCfg, err := utils.MergeSethAndEvmNetworkConfigs(networkConfig, *readSethCfg)
if err != nil {
return nil, err
}
err = utils.ValidateSethNetworkConfig(sethCfg.Network)
if err != nil {
return nil, err
Expand Down
39 changes: 39 additions & 0 deletions integration-tests/experiments/gas_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package experiments

import (
"testing"
"time"

"github.com/smartcontractkit/seth"
"github.com/stretchr/testify/require"

"github.com/smartcontractkit/chainlink-testing-framework/logging"
"github.com/smartcontractkit/chainlink-testing-framework/networks"
"github.com/smartcontractkit/chainlink/integration-tests/contracts"
tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig"
"github.com/smartcontractkit/chainlink/integration-tests/utils"
)

func TestGasExperiment(t *testing.T) {
l := logging.GetTestLogger(t)
config, err := tc.GetConfig("Soak", tc.OCR)
require.NoError(t, err, "Error getting config")

network := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0]
readSethCfg := config.GetSethConfig()
require.NotNil(t, readSethCfg, "Seth config shouldn't be nil")

sethCfg, err := utils.MergeSethAndEvmNetworkConfigs(network, *readSethCfg)
require.NoError(t, err, "Error merging seth and evm network configs")
err = utils.ValidateSethNetworkConfig(sethCfg.Network)
require.NoError(t, err, "Error validating seth network config")

seth, err := seth.NewClientWithConfig(&sethCfg)
require.NoError(t, err, "Error creating seth client")

for i := 0; i < 1; i++ {
_, err = contracts.DeployLinkTokenContract(l, seth)
require.NoError(t, err, "Error deploying LINK contract")
time.Sleep(2 * time.Second)
}
}
4 changes: 1 addition & 3 deletions integration-tests/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ require (
github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868
github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000
github.com/smartcontractkit/libocr v0.0.0-20240326191951-2bbe9382d052
github.com/smartcontractkit/seth v0.1.2
github.com/smartcontractkit/seth v0.1.3
github.com/smartcontractkit/wasp v0.4.5
github.com/spf13/cobra v1.8.0
github.com/stretchr/testify v1.9.0
Expand Down Expand Up @@ -337,8 +337,6 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
github.com/mwitkow/grpc-proxy v0.0.0-20230212185441-f345521cb9c9 // indirect
github.com/naoina/go-stringutil v0.1.0 // indirect
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
Expand Down
Loading
Loading