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

e2e: Migrate dynamic fees test from kurtosis #1792

Merged
merged 6 commits into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
169 changes: 169 additions & 0 deletions tests/e2e/c/dynamic_fees.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package c

import (
"math/big"
"strings"

ginkgo "github.com/onsi/ginkgo/v2"

"github.com/stretchr/testify/require"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"

"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/params"
"github.com/ava-labs/coreth/plugin/evm"

"github.com/ava-labs/avalanchego/tests"
"github.com/ava-labs/avalanchego/tests/e2e"
"github.com/ava-labs/avalanchego/tests/fixture/testnet"
"github.com/ava-labs/avalanchego/utils/crypto/secp256k1"
)

// This test uses the compiled bin for `hashing.sol` as
// well as its ABI contained in `hashing_contract.go`.

var _ = e2e.DescribeCChain("[Dynamic Fees]", func() {
require := require.New(ginkgo.GinkgoT())

// 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?
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@aaronbuchwald Any hints as to why this is an appropriate value?

gasTip := big.NewInt(1000 * params.GWei)

ginkgo.It("should ensure that the gas price is affected by load", func() {
ginkgo.By("creating a new private network to ensure isolation from other tests")
privateNetwork := e2e.Env.NewPrivateNetwork()

ginkgo.By("allocating a pre-funded key")
key := privateNetwork.GetConfig().FundedKeys[0]
ethAddress := evm.GetEthAddress(key)

ginkgo.By("initializing a coreth client")
node := privateNetwork.GetNodes()[0]
nodeURI := testnet.NodeURI{
NodeID: node.GetID(),
URI: node.GetProcessContext().URI,
}
ethClient := e2e.Env.NewEthClient(nodeURI)

ginkgo.By("initializing a transaction signer")
cChainID, err := ethClient.ChainID(e2e.DefaultContext())
require.NoError(err)
signer := types.NewEIP155Signer(cChainID)
ecdsaKey := key.ToECDSA()
sign := func(tx *types.Transaction) *types.Transaction {
signedTx, err := types.SignTx(tx, signer, ecdsaKey)
require.NoError(err)
return signedTx
}

var contractAddress common.Address
ginkgo.By("deploying an expensive contract", func() {
// Create transaction
nonce, err := ethClient.AcceptedNonceAt(e2e.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,
})

// Send the transaction and wait for acceptance
signedTx := sign(tx)
receipt := e2e.SendEthTransaction(ethClient, signedTx)

contractAddress = receipt.ContractAddress
})

var gasPrice *big.Int
ginkgo.By("calling the expensive contract repeatedly until a gas price increase is detected", func() {
// Evaluate the bytes representation of the contract
hashingABI, err := abi.JSON(strings.NewReader(hashingABIJson))
require.NoError(err)
contractData, err := hashingABI.Pack("hashIt")
require.NoError(err)

var initialGasPrice *big.Int
e2e.Eventually(func() bool {
// Check the gas price
var err error
gasPrice, err = ethClient.SuggestGasPrice(e2e.DefaultContext())
marun marked this conversation as resolved.
Show resolved Hide resolved
require.NoError(err)
if initialGasPrice == nil {
initialGasPrice = gasPrice
tests.Outf("{{blue}}initial gas price is %v{{/}}\n", initialGasPrice)
} else if gasPrice.Cmp(initialGasPrice) > 0 {
// Gas price has increased
tests.Outf("{{blue}}gas price has increased to %v{{/}}\n", gasPrice)
return true
}

// Create the transaction
nonce, err := ethClient.AcceptedNonceAt(e2e.DefaultContext(), ethAddress)
require.NoError(err)
tx := types.NewTx(&types.LegacyTx{
Nonce: nonce,
GasPrice: gasTip,
Gas: largeGasLimit,
To: &contractAddress,
Value: common.Big0,
Data: contractData,
})

// Send the transaction and wait for acceptance
signedTx := sign(tx)
_ = e2e.SendEthTransaction(ethClient, signedTx)

// The gas price will be checked at the start of the next iteration
return false
}, e2e.DefaultTimeout, e2e.DefaultPollingInterval, "failed to see gas price increase before timeout")
})

ginkgo.By("waiting for the gas price to decrease...", func() {
initialGasPrice := gasPrice
e2e.Eventually(func() bool {
var err error
gasPrice, err = ethClient.SuggestGasPrice(e2e.DefaultContext())
hexfusion marked this conversation as resolved.
Show resolved Hide resolved
require.NoError(err)
tests.Outf("{{blue}}.{{/}}")
return initialGasPrice.Cmp(gasPrice) > 0
}, e2e.DefaultTimeout, e2e.DefaultPollingInterval, "failed to see gas price decrease before timeout")
tests.Outf("\n{{blue}}gas price has decreased to %v{{/}}\n", gasPrice)
})

ginkgo.By("sending funds at the current gas price", func() {
// Create a recipient address
factory := secp256k1.Factory{}
recipientKey, err := factory.NewPrivateKey()
require.NoError(err)
recipientEthAddress := evm.GetEthAddress(recipientKey)

// Create transaction
nonce, err := ethClient.AcceptedNonceAt(e2e.DefaultContext(), ethAddress)
require.NoError(err)
tx := types.NewTx(&types.LegacyTx{
Nonce: nonce,
GasPrice: gasPrice,
Gas: e2e.DefaultGasLimit,
To: &recipientEthAddress,
Value: common.Big0,
})

// Send the transaction and wait for acceptance
signedTx := sign(tx)
_ = e2e.SendEthTransaction(ethClient, signedTx)
})

e2e.CheckBootstrapIsPossible(privateNetwork)
})
})
15 changes: 15 additions & 0 deletions tests/e2e/c/hashing.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT

pragma solidity = 0.8.6;
hexfusion marked this conversation as resolved.
Show resolved Hide resolved

contract ConsumeGas {

bytes hashVar = bytes("This is the hashing text for the test");

function hashIt() public {
for (uint i=0; i<3700; i++) {
ripemd160(hashVar);
}
}

}
11 changes: 11 additions & 0 deletions tests/e2e/c/hashing_contract.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

// AUTOMATICALLY GENERATED. DO NOT EDIT!
// Generated from hashing.sol by compile-contract.sh

Choose a reason for hiding this comment

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

Can we add compile-contract.sh to this directory?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I lifted this verbatim from avalanche-testing, and the script wasn't committed in that case either. iirc from Aaron that the script in question is only one part of a larger dependency chain and I don't think we would want to commit it here. I figure we have something that works and in the unlikely even that we ever need to modify it we can engage our resident solidity experts to do so.

Copy link
Collaborator

Choose a reason for hiding this comment

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

We supported this until go-ethereum removed their Solidity compiler package. I'm not opposed to installing solc and using that where necessary.

I don't have a strong opinion on whether it's better to install solc and compile on-demand or hardcode and am open to either.

package c

const (
hashingCompiledContract = "60806040526040518060600160405280602581526020016103ba6025913960009080519060200190610032929190610045565b5034801561003f57600080fd5b50610149565b828054610051906100e8565b90600052602060002090601f01602090048101928261007357600085556100ba565b82601f1061008c57805160ff19168380011785556100ba565b828001600101855582156100ba579182015b828111156100b957825182559160200191906001019061009e565b5b5090506100c791906100cb565b5090565b5b808211156100e45760008160009055506001016100cc565b5090565b6000600282049050600182168061010057607f821691505b602082108114156101145761011361011a565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b610262806101586000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80636f37ecea14610030575b600080fd5b61003861003a565b005b60005b610e7481101561009057600360006040516100589190610112565b602060405180830381855afa158015610075573d6000803e3d6000fd5b5050506040515150808061008890610185565b91505061003d565b50565b600081546100a081610153565b6100aa818661013e565b945060018216600081146100c557600181146100d657610109565b60ff19831686528186019350610109565b6100df85610129565b60005b83811015610101578154818901526001820191506020810190506100e2565b838801955050505b50505092915050565b600061011e8284610093565b915081905092915050565b60008190508160005260206000209050919050565b600081905092915050565b6000819050919050565b6000600282049050600182168061016b57607f821691505b6020821081141561017f5761017e6101fd565b5b50919050565b600061019082610149565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156101c3576101c26101ce565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fdfea2646970667358221220cc5de5cd3c7aa5bda60e63e0f3156691253f7a78191eb336ec6699b38a8a777c64736f6c6343000806003354686973206973207468652068617368696e67207465787420666f72207468652074657374"
hashingABIJson = `[{"inputs":[],"name":"hashIt","outputs":[],"stateMutability":"nonpayable","type":"function"}]`
)
7 changes: 2 additions & 5 deletions tests/e2e/c/interchain_workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,7 @@ import (
var _ = e2e.DescribeCChain("[Interchain Workflow]", func() {
require := require.New(ginkgo.GinkgoT())

const (
txAmount = 10 * units.Avax // Arbitrary amount to send and transfer
gasLimit = uint64(21000) // Standard gas limit
)
const txAmount = 10 * units.Avax // Arbitrary amount to send and transfer

ginkgo.It("should ensure that funds can be transferred from the C-Chain to the X-Chain and the P-Chain", func() {
ginkgo.By("initializing a new eth client")
Expand All @@ -56,7 +53,7 @@ var _ = e2e.DescribeCChain("[Interchain Workflow]", func() {
acceptedNonce,
recipientEthAddress,
big.NewInt(int64(txAmount)),
gasLimit,
e2e.DefaultGasLimit,
gasPrice,
nil,
)
Expand Down
55 changes: 55 additions & 0 deletions tests/e2e/e2e.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"math/big"
"math/rand"
"os"
"path/filepath"
"strings"
"time"

Expand All @@ -29,6 +30,7 @@ import (
"github.com/ava-labs/avalanchego/tests/fixture/testnet"
"github.com/ava-labs/avalanchego/tests/fixture/testnet/local"
"github.com/ava-labs/avalanchego/utils/crypto/secp256k1"
"github.com/ava-labs/avalanchego/utils/perms"
"github.com/ava-labs/avalanchego/vms/platformvm/txs/executor"
"github.com/ava-labs/avalanchego/vms/secp256k1fx"
"github.com/ava-labs/avalanchego/wallet/subnet/primary"
Expand All @@ -54,6 +56,16 @@ const (
// current time for validator addition to succeed, and adding 20
// seconds provides a buffer in case of any delay in processing.
DefaultValidatorStartTimeDiff = executor.SyncBound + 20*time.Second

DefaultGasLimit = uint64(21000) // Standard gas limit
marun marked this conversation as resolved.
Show resolved Hide resolved

// An empty string prompts the use of the default path which ensures a
// predictable target for github's upload-artifact action.
DefaultNetworkDir = ""

// Directory used to store private networks (specific to a single test)
// under the shared network dir.
PrivateNetworksDirName = "private_networks"
)

// Env is used to access shared test fixture. Intended to be
Expand Down Expand Up @@ -116,6 +128,7 @@ func (te *TestEnvironment) NewKeychain(count int) *secp256k1fx.Keychain {
}

// Create a new wallet for the provided keychain against the specified node URI.
// TODO(marun) Make this a regular function.
func (te *TestEnvironment) NewWallet(keychain *secp256k1fx.Keychain, nodeURI testnet.NodeURI) primary.Wallet {
tests.Outf("{{blue}} initializing a new wallet for node %s with URI: %s {{/}}\n", nodeURI.NodeID, nodeURI.URI)
baseWallet, err := primary.MakeWallet(DefaultContext(), &primary.WalletConfig{
Expand All @@ -135,6 +148,7 @@ func (te *TestEnvironment) NewWallet(keychain *secp256k1fx.Keychain, nodeURI tes
}

// Create a new eth client targeting the specified node URI.
// TODO(marun) Make this a regular function.
func (te *TestEnvironment) NewEthClient(nodeURI testnet.NodeURI) ethclient.Client {
tests.Outf("{{blue}} initializing a new eth client for node %s with URI: %s {{/}}\n", nodeURI.NodeID, nodeURI.URI)
nodeAddress := strings.Split(nodeURI.URI, "//")[1]
Expand All @@ -144,6 +158,20 @@ func (te *TestEnvironment) NewEthClient(nodeURI testnet.NodeURI) ethclient.Clien
return client
}

// Create a new private network that is not shared with other tests.
func (te *TestEnvironment) NewPrivateNetwork() testnet.Network {
// Load the shared network to retrieve its path and exec path
sharedNetwork, err := local.ReadNetwork(te.NetworkDir)
te.require.NoError(err)

// The private networks dir is under the shared network dir to ensure it
// will be included in the artifact uploaded in CI.
privateNetworksDir := filepath.Join(sharedNetwork.Dir, PrivateNetworksDirName)
te.require.NoError(os.MkdirAll(privateNetworksDir, perms.ReadWriteExecute))
hexfusion marked this conversation as resolved.
Show resolved Hide resolved

return StartLocalNetwork(sharedNetwork.ExecPath, privateNetworksDir)
}

// Helper simplifying use of a timed context by canceling the context on ginkgo teardown.
func ContextWithTimeout(duration time.Duration) context.Context {
ctx, cancel := context.WithTimeout(context.Background(), duration)
Expand Down Expand Up @@ -259,3 +287,30 @@ func CheckBootstrapIsPossible(network testnet.Network) {
node := AddEphemeralNode(network, testnet.FlagsMap{})
WaitForHealthy(node)
}

// Start a local test-managed network with the provided avalanchego binary.
func StartLocalNetwork(avalancheGoExecPath string, networkDir string) *local.LocalNetwork {
require := require.New(ginkgo.GinkgoT())

network, err := local.StartNetwork(
DefaultContext(),
ginkgo.GinkgoWriter,
networkDir,
&local.LocalNetwork{
LocalConfig: local.LocalConfig{
ExecPath: avalancheGoExecPath,
},
},
testnet.DefaultNodeCount,
testnet.DefaultFundedKeyCount,
)
require.NoError(err)
ginkgo.DeferCleanup(func() {
tests.Outf("Shutting down network\n")
require.NoError(network.Stop())
})
hexfusion marked this conversation as resolved.
Show resolved Hide resolved

tests.Outf("{{green}}Successfully started network{{/}}\n")

return network
}
25 changes: 1 addition & 24 deletions tests/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
package e2e_test

import (
"context"
"encoding/json"
"flag"
"fmt"
Expand All @@ -20,7 +19,6 @@ import (
"github.com/ava-labs/avalanchego/tests"
"github.com/ava-labs/avalanchego/tests/e2e"
"github.com/ava-labs/avalanchego/tests/fixture"
"github.com/ava-labs/avalanchego/tests/fixture/testnet"
"github.com/ava-labs/avalanchego/tests/fixture/testnet/local"

// ensure test packages are scanned by ginkgo
Expand Down Expand Up @@ -83,28 +81,7 @@ var _ = ginkgo.SynchronizedBeforeSuite(func() []byte {
network, err = local.ReadNetwork(persistentNetworkDir)
require.NoError(err)
} else {
ctx, cancel := context.WithTimeout(context.Background(), local.DefaultNetworkStartTimeout)
defer cancel()
var err error
network, err = local.StartNetwork(
ctx,
ginkgo.GinkgoWriter,
"", // Use the default path to ensure a predictable target for github's upload-artifact action
&local.LocalNetwork{
LocalConfig: local.LocalConfig{
ExecPath: avalancheGoExecPath,
},
},
testnet.DefaultNodeCount,
testnet.DefaultFundedKeyCount,
)
require.NoError(err)
ginkgo.DeferCleanup(func() {
tests.Outf("Shutting down network\n")
require.NoError(network.Stop())
})

tests.Outf("{{green}}Successfully started network{{/}}\n")
network = e2e.StartLocalNetwork(avalancheGoExecPath, e2e.DefaultNetworkDir)
}

uris := network.GetURIs()
Expand Down
Loading