github.com/MetalBlockchain/metalgo@v1.11.9/tests/e2e/c/dynamic_fees.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package c 5 6 import ( 7 "math/big" 8 "strings" 9 10 "github.com/MetalBlockchain/coreth/core/types" 11 "github.com/MetalBlockchain/coreth/params" 12 "github.com/MetalBlockchain/coreth/plugin/evm" 13 "github.com/ethereum/go-ethereum/accounts/abi" 14 "github.com/ethereum/go-ethereum/common" 15 "github.com/stretchr/testify/require" 16 17 "github.com/MetalBlockchain/metalgo/tests" 18 "github.com/MetalBlockchain/metalgo/tests/fixture/e2e" 19 "github.com/MetalBlockchain/metalgo/tests/fixture/tmpnet" 20 "github.com/MetalBlockchain/metalgo/utils/crypto/secp256k1" 21 22 ginkgo "github.com/onsi/ginkgo/v2" 23 ) 24 25 // This test uses the compiled bin for `hashing.sol` as 26 // well as its ABI contained in `hashing_contract.go`. 27 28 var _ = e2e.DescribeCChain("[Dynamic Fees]", func() { 29 require := require.New(ginkgo.GinkgoT()) 30 31 // Need a gas limit much larger than the standard 21_000 to enable 32 // the contract to induce a gas price increase 33 const largeGasLimit = uint64(8_000_000) 34 35 // TODO(marun) What is the significance of this value? 36 gasTip := big.NewInt(1000 * params.GWei) 37 38 ginkgo.It("should ensure that the gas price is affected by load", func() { 39 ginkgo.By("creating a new private network to ensure isolation from other tests") 40 privateNetwork := tmpnet.NewDefaultNetwork("avalanchego-e2e-dynamic-fees") 41 e2e.Env.StartPrivateNetwork(privateNetwork) 42 43 ginkgo.By("allocating a pre-funded key") 44 key := privateNetwork.PreFundedKeys[0] 45 ethAddress := evm.GetEthAddress(key) 46 47 ginkgo.By("initializing a coreth client") 48 node := privateNetwork.Nodes[0] 49 nodeURI := tmpnet.NodeURI{ 50 NodeID: node.NodeID, 51 URI: node.URI, 52 } 53 ethClient := e2e.NewEthClient(nodeURI) 54 55 ginkgo.By("initializing a transaction signer") 56 cChainID, err := ethClient.ChainID(e2e.DefaultContext()) 57 require.NoError(err) 58 signer := types.NewEIP155Signer(cChainID) 59 ecdsaKey := key.ToECDSA() 60 sign := func(tx *types.Transaction) *types.Transaction { 61 signedTx, err := types.SignTx(tx, signer, ecdsaKey) 62 require.NoError(err) 63 return signedTx 64 } 65 66 var contractAddress common.Address 67 ginkgo.By("deploying an expensive contract", func() { 68 // Create transaction 69 nonce, err := ethClient.AcceptedNonceAt(e2e.DefaultContext(), ethAddress) 70 require.NoError(err) 71 compiledContract := common.Hex2Bytes(hashingCompiledContract) 72 tx := types.NewTx(&types.LegacyTx{ 73 Nonce: nonce, 74 GasPrice: gasTip, 75 Gas: largeGasLimit, 76 Value: common.Big0, 77 Data: compiledContract, 78 }) 79 80 // Send the transaction and wait for acceptance 81 signedTx := sign(tx) 82 receipt := e2e.SendEthTransaction(ethClient, signedTx) 83 84 contractAddress = receipt.ContractAddress 85 }) 86 87 var gasPrice *big.Int 88 ginkgo.By("calling the expensive contract repeatedly until a gas price increase is detected", func() { 89 // Evaluate the bytes representation of the contract 90 hashingABI, err := abi.JSON(strings.NewReader(hashingABIJson)) 91 require.NoError(err) 92 contractData, err := hashingABI.Pack("hashIt") 93 require.NoError(err) 94 95 var initialGasPrice *big.Int 96 e2e.Eventually(func() bool { 97 // Check the gas price 98 var err error 99 gasPrice, err = ethClient.SuggestGasPrice(e2e.DefaultContext()) 100 require.NoError(err) 101 if initialGasPrice == nil { 102 initialGasPrice = gasPrice 103 tests.Outf("{{blue}}initial gas price is %v{{/}}\n", initialGasPrice) 104 } else if gasPrice.Cmp(initialGasPrice) > 0 { 105 // Gas price has increased 106 tests.Outf("{{blue}}gas price has increased to %v{{/}}\n", gasPrice) 107 return true 108 } 109 110 // Create the transaction 111 nonce, err := ethClient.AcceptedNonceAt(e2e.DefaultContext(), ethAddress) 112 require.NoError(err) 113 tx := types.NewTx(&types.LegacyTx{ 114 Nonce: nonce, 115 GasPrice: gasTip, 116 Gas: largeGasLimit, 117 To: &contractAddress, 118 Value: common.Big0, 119 Data: contractData, 120 }) 121 122 // Send the transaction and wait for acceptance 123 signedTx := sign(tx) 124 _ = e2e.SendEthTransaction(ethClient, signedTx) 125 126 // The gas price will be checked at the start of the next iteration 127 return false 128 }, e2e.DefaultTimeout, e2e.DefaultPollingInterval, "failed to see gas price increase before timeout") 129 }) 130 131 ginkgo.By("waiting for the gas price to decrease...", func() { 132 initialGasPrice := gasPrice 133 e2e.Eventually(func() bool { 134 var err error 135 gasPrice, err = ethClient.SuggestGasPrice(e2e.DefaultContext()) 136 require.NoError(err) 137 tests.Outf("{{blue}}.{{/}}") 138 return initialGasPrice.Cmp(gasPrice) > 0 139 }, e2e.DefaultTimeout, e2e.DefaultPollingInterval, "failed to see gas price decrease before timeout") 140 tests.Outf("\n{{blue}}gas price has decreased to %v{{/}}\n", gasPrice) 141 }) 142 143 ginkgo.By("sending funds at the current gas price", func() { 144 // Create a recipient address 145 recipientKey, err := secp256k1.NewPrivateKey() 146 require.NoError(err) 147 recipientEthAddress := evm.GetEthAddress(recipientKey) 148 149 // Create transaction 150 nonce, err := ethClient.AcceptedNonceAt(e2e.DefaultContext(), ethAddress) 151 require.NoError(err) 152 tx := types.NewTx(&types.LegacyTx{ 153 Nonce: nonce, 154 GasPrice: gasPrice, 155 Gas: e2e.DefaultGasLimit, 156 To: &recipientEthAddress, 157 Value: common.Big0, 158 }) 159 160 // Send the transaction and wait for acceptance 161 signedTx := sign(tx) 162 _ = e2e.SendEthTransaction(ethClient, signedTx) 163 }) 164 165 e2e.CheckBootstrapIsPossible(privateNetwork) 166 }) 167 })