-
Notifications
You must be signed in to change notification settings - Fork 722
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
Add ACP-176 e2e tests #3749
base: master
Are you sure you want to change the base?
Add ACP-176 e2e tests #3749
Changes from all commits
37e79d4
1015b2d
7becdbd
88a1280
5a7e044
3b4be16
94ff15e
7cf4478
8a429c9
229c265
a611d22
3f127cd
8ed8135
3ad0268
0ae624b
1703e5c
668bd4e
ca9acee
2ed1ec2
5e6bc5f
ebcc841
0ceba59
408d5aa
a5d85c0
ef9ed1d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package c | ||
|
||
const ( | ||
// consumeGasCompiledContract is the compiled bytecode of the contract | ||
// defined in consume_gas.sol. | ||
consumeGasCompiledContract = "6080604052348015600f57600080fd5b5060788061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063c040622614602d575b600080fd5b60336035565b005b5b6001156040576036565b56fea264697066735822122070cfeeb0992270b4ff725a1264654534853d25ea6bb28c85d986beccfdbc997164736f6c63430008060033" | ||
// consumeGasABIJson is the ABI of the contract defined in consume_gas.sol. | ||
consumeGasABIJson = `[{"inputs":[],"name":"run","outputs":[],"stateMutability":"nonpayable","type":"function"}]` | ||
// consumeGasFunction is the name of the function to call to consume gas. | ||
consumeGasFunction = "run" | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity = 0.8.6; | ||
|
||
contract ConsumeGas { | ||
function run() public { | ||
while (true){} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,37 +6,66 @@ package c | |
import ( | ||
"math/big" | ||
"strings" | ||
"time" | ||
|
||
"github.com/ava-labs/coreth/core/types" | ||
"github.com/ava-labs/coreth/params" | ||
"github.com/ava-labs/coreth/plugin/evm/upgrade/acp176" | ||
"github.com/ava-labs/coreth/plugin/evm/upgrade/cortina" | ||
"github.com/ethereum/go-ethereum/accounts/abi" | ||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/onsi/ginkgo/v2" | ||
"github.com/stretchr/testify/require" | ||
"go.uber.org/zap" | ||
|
||
"github.com/ava-labs/avalanchego/api/info" | ||
"github.com/ava-labs/avalanchego/tests/fixture/e2e" | ||
"github.com/ava-labs/avalanchego/tests/fixture/tmpnet" | ||
) | ||
|
||
// This test uses the compiled bin for `hashing.sol` as | ||
// well as its ABI contained in `hashing_contract.go`. | ||
// This test uses the compiled bytecode for `consume_gas.sol` as well as its ABI | ||
// contained in `consume_gas.go`. | ||
|
||
var _ = e2e.DescribeCChain("[Dynamic Fees]", func() { | ||
tc := e2e.NewTestContext() | ||
require := require.New(tc) | ||
|
||
// Need a gas limit much larger than the standard 21_000 to enable | ||
// the contract to induce a gas price increase | ||
const largeGasLimit = uint64(8_000_000) | ||
|
||
// TODO(marun) What is the significance of this value? | ||
gasTip := big.NewInt(1000 * params.GWei) | ||
const ( | ||
// maxFeePerGas is the maximum fee that transactions issued by this test | ||
// will be willing to pay. The actual value doesn't really matter, it | ||
// just needs to be higher than the `targetGasPrice` calculated below. | ||
maxFeePerGas = 1000 * params.GWei | ||
// minFeePerGas is the minimum fee that transactions issued by this test | ||
// will pay. The mempool enforces that this value is non-zero. | ||
minFeePerGas = 1 * params.Wei | ||
|
||
// expectedGasPriceIncreaseNumerator/expectedGasPriceIncreaseDenominator | ||
// is the multiplier that the gas price is attempted to reach. So if the | ||
// initial gas price is 5 GWei, the test will attempt to increase the | ||
// gas price to 6 GWei. | ||
expectedGasPriceIncreaseNumerator = 6 | ||
expectedGasPriceIncreaseDenominator = 5 | ||
) | ||
var ( | ||
bigExpectedGasPriceIncreaseNumerator = big.NewInt(expectedGasPriceIncreaseNumerator) | ||
bigExpectedGasPriceIncreaseDenominator = big.NewInt(expectedGasPriceIncreaseDenominator) | ||
bigExpectedGasPriceIncreaseDenominatorMinus1 = big.NewInt(expectedGasPriceIncreaseDenominator - 1) | ||
|
||
gasFeeCap = big.NewInt(maxFeePerGas) | ||
gasTipCap = big.NewInt(minFeePerGas) | ||
) | ||
|
||
ginkgo.It("should ensure that the gas price is affected by load", func() { | ||
tc.By("creating a new private network to ensure isolation from other tests") | ||
env := e2e.GetEnv(tc) | ||
publicNetwork := env.GetNetwork() | ||
|
||
privateNetwork := tmpnet.NewDefaultNetwork("avalanchego-e2e-dynamic-fees") | ||
e2e.GetEnv(tc).StartPrivateNetwork(privateNetwork) | ||
// Copy over the defaults from the normal test suite to include settings | ||
// like the upgrade config. | ||
privateNetwork.DefaultFlags = tmpnet.FlagsMap{} | ||
privateNetwork.DefaultFlags.SetDefaults(publicNetwork.DefaultFlags) | ||
env.StartPrivateNetwork(privateNetwork) | ||
|
||
// Avoid emitting a spec-scoped metrics link for the shared | ||
// network since the link emitted by the start of the private | ||
|
@@ -60,71 +89,109 @@ var _ = e2e.DescribeCChain("[Dynamic Fees]", func() { | |
tc.By("initializing a transaction signer") | ||
cChainID, err := ethClient.ChainID(tc.DefaultContext()) | ||
require.NoError(err) | ||
signer := types.NewEIP155Signer(cChainID) | ||
signer := types.NewLondonSigner(cChainID) | ||
ecdsaKey := key.ToECDSA() | ||
sign := func(tx *types.Transaction) *types.Transaction { | ||
signedTx, err := types.SignTx(tx, signer, ecdsaKey) | ||
require.NoError(err) | ||
return signedTx | ||
} | ||
|
||
// gasLimit is the maximum amount of gas that can be used in a block. | ||
var gasLimit uint64 | ||
tc.By("checking if Fortuna is activated", func() { | ||
infoClient := info.NewClient(nodeURI.URI) | ||
upgrades, err := infoClient.Upgrades(tc.DefaultContext()) | ||
require.NoError(err) | ||
|
||
now := time.Now() | ||
if upgrades.IsFUpgradeActivated(now) { | ||
gasLimit = acp176.MinMaxCapacity | ||
} else { | ||
gasLimit = cortina.GasLimit | ||
} | ||
}) | ||
tc.Log().Info("set gas limit", | ||
zap.Uint64("gasLimit", gasLimit), | ||
) | ||
|
||
var contractAddress common.Address | ||
tc.By("deploying an expensive contract", func() { | ||
// Create transaction | ||
nonce, err := ethClient.AcceptedNonceAt(tc.DefaultContext(), ethAddress) | ||
require.NoError(err) | ||
compiledContract := common.Hex2Bytes(hashingCompiledContract) | ||
tx := types.NewTx(&types.LegacyTx{ | ||
Nonce: nonce, | ||
GasPrice: gasTip, | ||
Gas: largeGasLimit, | ||
Value: common.Big0, | ||
Data: compiledContract, | ||
compiledContract := common.Hex2Bytes(consumeGasCompiledContract) | ||
tx := types.NewTx(&types.DynamicFeeTx{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Specifically, previously the This allows this test to specify such a high |
||
ChainID: cChainID, | ||
Nonce: nonce, | ||
GasTipCap: gasTipCap, | ||
GasFeeCap: gasFeeCap, | ||
Gas: gasLimit, | ||
To: nil, // contract creation | ||
Data: compiledContract, | ||
}) | ||
|
||
// Send the transaction and wait for acceptance | ||
signedTx := sign(tx) | ||
receipt := e2e.SendEthTransaction(tc, ethClient, signedTx) | ||
require.Equal(types.ReceiptStatusSuccessful, receipt.Status) | ||
|
||
contractAddress = receipt.ContractAddress | ||
}) | ||
|
||
var gasPrice *big.Int | ||
tc.By("calling the expensive contract repeatedly until a gas price increase is detected", func() { | ||
latest, err := ethClient.HeaderByNumber(tc.DefaultContext(), nil) | ||
require.NoError(err) | ||
|
||
initialGasPrice := latest.BaseFee | ||
targetGasPrice := new(big.Int).Set(initialGasPrice) | ||
targetGasPrice.Mul(targetGasPrice, bigExpectedGasPriceIncreaseNumerator) | ||
targetGasPrice.Add(targetGasPrice, bigExpectedGasPriceIncreaseDenominatorMinus1) | ||
targetGasPrice.Div(targetGasPrice, bigExpectedGasPriceIncreaseDenominator) | ||
|
||
tc.Log().Info("initializing gas prices", | ||
zap.Stringer("initialPrice", initialGasPrice), | ||
zap.Stringer("targetPrice", targetGasPrice), | ||
) | ||
|
||
tc.By("calling the contract repeatedly until a sufficient gas price increase is detected", func() { | ||
// Evaluate the bytes representation of the contract | ||
hashingABI, err := abi.JSON(strings.NewReader(hashingABIJson)) | ||
hashingABI, err := abi.JSON(strings.NewReader(consumeGasABIJson)) | ||
require.NoError(err) | ||
contractData, err := hashingABI.Pack("hashIt") | ||
contractData, err := hashingABI.Pack(consumeGasFunction) | ||
require.NoError(err) | ||
|
||
var initialGasPrice *big.Int | ||
tc.Eventually(func() bool { | ||
// Check the gas price | ||
var err error | ||
gasPrice, err = ethClient.SuggestGasPrice(tc.DefaultContext()) | ||
latest, err := ethClient.HeaderByNumber(tc.DefaultContext(), nil) | ||
require.NoError(err) | ||
if initialGasPrice == nil { | ||
initialGasPrice = gasPrice | ||
tc.Log().Info("initial gas price", | ||
zap.Uint64("price", initialGasPrice.Uint64()), | ||
) | ||
} else if gasPrice.Cmp(initialGasPrice) > 0 { | ||
|
||
// If the gas price has increased, stop the loop. | ||
if latest.BaseFee.Cmp(targetGasPrice) >= 0 { | ||
tc.Log().Info("gas price has increased", | ||
zap.Uint64("price", gasPrice.Uint64()), | ||
zap.Stringer("initialPrice", initialGasPrice), | ||
zap.Stringer("targetPrice", targetGasPrice), | ||
zap.Stringer("newPrice", latest.BaseFee), | ||
) | ||
return true | ||
} | ||
|
||
tc.Log().Info("gas price hasn't sufficiently increased", | ||
zap.Stringer("initialPrice", initialGasPrice), | ||
zap.Stringer("newPrice", latest.BaseFee), | ||
zap.Stringer("targetPrice", targetGasPrice), | ||
) | ||
|
||
// Create the transaction | ||
nonce, err := ethClient.AcceptedNonceAt(tc.DefaultContext(), ethAddress) | ||
require.NoError(err) | ||
tx := types.NewTx(&types.LegacyTx{ | ||
Nonce: nonce, | ||
GasPrice: gasTip, | ||
Gas: largeGasLimit, | ||
To: &contractAddress, | ||
Value: common.Big0, | ||
Data: contractData, | ||
tx := types.NewTx(&types.DynamicFeeTx{ | ||
ChainID: cChainID, | ||
Nonce: nonce, | ||
GasTipCap: gasTipCap, | ||
GasFeeCap: gasFeeCap, | ||
Gas: gasLimit, | ||
To: &contractAddress, | ||
Data: contractData, | ||
}) | ||
|
||
// Send the transaction and wait for acceptance | ||
|
@@ -136,40 +203,51 @@ var _ = e2e.DescribeCChain("[Dynamic Fees]", func() { | |
}, e2e.DefaultTimeout, e2e.DefaultPollingInterval, "failed to see gas price increase before timeout") | ||
}) | ||
|
||
tc.By("waiting for the gas price to decrease...", func() { | ||
initialGasPrice := gasPrice | ||
// Create a recipient address | ||
var ( | ||
recipientKey = e2e.NewPrivateKey(tc) | ||
recipientEthAddress = recipientKey.EthAddress() | ||
) | ||
tc.By("sending small transactions until a sufficient gas price decrease is detected", func() { | ||
tc.Eventually(func() bool { | ||
var err error | ||
gasPrice, err = ethClient.SuggestGasPrice(tc.DefaultContext()) | ||
// Check the gas price | ||
latest, err := ethClient.HeaderByNumber(tc.DefaultContext(), nil) | ||
require.NoError(err) | ||
return initialGasPrice.Cmp(gasPrice) > 0 | ||
}, e2e.DefaultTimeout, e2e.DefaultPollingInterval, "failed to see gas price decrease before timeout") | ||
tc.Log().Info("gas price has decreased", | ||
zap.Uint64("price", gasPrice.Uint64()), | ||
) | ||
}) | ||
|
||
tc.By("sending funds at the current gas price", func() { | ||
// Create a recipient address | ||
var ( | ||
recipientKey = e2e.NewPrivateKey(tc) | ||
recipientEthAddress = recipientKey.EthAddress() | ||
) | ||
// If the gas price has decreased, stop the loop. | ||
if latest.BaseFee.Cmp(initialGasPrice) <= 0 { | ||
tc.Log().Info("gas price has decreased", | ||
zap.Stringer("initialPrice", initialGasPrice), | ||
zap.Stringer("newPrice", latest.BaseFee), | ||
) | ||
return true | ||
} | ||
|
||
tc.Log().Info("gas price hasn't sufficiently decreased", | ||
zap.Stringer("initialPrice", initialGasPrice), | ||
zap.Stringer("newPrice", latest.BaseFee), | ||
) | ||
|
||
// Create transaction | ||
nonce, err := ethClient.AcceptedNonceAt(tc.DefaultContext(), ethAddress) | ||
require.NoError(err) | ||
tx := types.NewTx(&types.LegacyTx{ | ||
Nonce: nonce, | ||
GasPrice: gasPrice, | ||
Gas: e2e.DefaultGasLimit, | ||
To: &recipientEthAddress, | ||
Value: common.Big0, | ||
}) | ||
// Create the transaction | ||
nonce, err := ethClient.AcceptedNonceAt(tc.DefaultContext(), ethAddress) | ||
require.NoError(err) | ||
tx := types.NewTx(&types.DynamicFeeTx{ | ||
ChainID: cChainID, | ||
Nonce: nonce, | ||
GasTipCap: gasTipCap, | ||
GasFeeCap: gasFeeCap, | ||
Gas: e2e.DefaultGasLimit, | ||
To: &recipientEthAddress, | ||
}) | ||
|
||
// Send the transaction and wait for acceptance | ||
signedTx := sign(tx) | ||
_ = e2e.SendEthTransaction(tc, ethClient, signedTx) | ||
// Send the transaction and wait for acceptance | ||
signedTx := sign(tx) | ||
receipt := e2e.SendEthTransaction(tc, ethClient, signedTx) | ||
require.Equal(types.ReceiptStatusSuccessful, receipt.Status) | ||
|
||
// The gas price will be checked at the start of the next iteration | ||
return false | ||
}, e2e.DefaultTimeout, e2e.DefaultPollingInterval, "failed to see gas price decrease before timeout") | ||
}) | ||
|
||
_ = e2e.CheckBootstrapIsPossible(tc, privateNetwork) | ||
|
This file was deleted.
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The london signer supports the
DynamicFeeTx