diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 858d99e0d1..15f6e35dba 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -607,16 +607,24 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM // Execute the call. msg := callMsg{call} - txContext := core.NewEVMTxContext(msg) - evmContext := core.NewEVMBlockContext(block.Header(), b.blockchain, nil) // Create a new environment which holds all relevant information // about the transaction and calling mechanisms. + txContext := core.NewEVMTxContext(msg) + evmContext := core.NewEVMBlockContext(block.Header(), b.blockchain, nil) vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true}) gasPool := new(core.GasPool).AddGas(math.MaxUint64) - vmRunner := b.blockchain.NewEVMRunner(block.Header(), stateDB) - - return core.NewStateTransition(vmEnv, msg, gasPool, vmRunner).TransitionDb() + var sysCtx *core.SysContractCallCtx + if b.config.IsEHardfork(block.Number()) { + parent := b.blockchain.GetBlockByNumber(block.NumberU64() - 1) + sysStateDB, err := b.blockchain.StateAt(parent.Root()) + if err != nil { + return nil, err + } + sysVmRunner := b.blockchain.NewEVMRunner(block.Header(), sysStateDB) + sysCtx = core.NewSysContractCallCtx(sysVmRunner) + } + return core.NewStateTransition(vmEnv, msg, gasPool, vmRunner, sysCtx).TransitionDb() } // SendTransaction updates the pending block to include the given transaction. diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 99699684d5..6c0c4e8547 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -151,7 +151,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, // FIXME this is broken // (ret []byte, usedGas uint64, failed bool, err error) - msgResult, err := core.ApplyMessage(evm, msg, gaspool, nil) + msgResult, err := core.ApplyMessage(evm, msg, gaspool, nil, &core.SysContractCallCtx{}) if err != nil { statedb.RevertToSnapshot(snapshot) log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From(), "error", err) diff --git a/contracts/gasprice_minimum/gasprice_minimum.go b/contracts/gasprice_minimum/gasprice_minimum.go index a0d65b5855..88aaff96e9 100644 --- a/contracts/gasprice_minimum/gasprice_minimum.go +++ b/contracts/gasprice_minimum/gasprice_minimum.go @@ -27,6 +27,8 @@ import ( "github.com/celo-org/celo-blockchain/params" ) +// Gas price minimum serves as baseFee(EIP1559) after Espresso HF. + var ( FallbackGasPriceMinimum *big.Int = big.NewInt(0) // gas price minimum to return if unable to fetch from contract suggestionMultiplier *big.Int = big.NewInt(5) // The multiplier that we apply to the minimum when suggesting gas price diff --git a/core/block_context.go b/core/block_context.go deleted file mode 100644 index 1f94587497..0000000000 --- a/core/block_context.go +++ /dev/null @@ -1,53 +0,0 @@ -package core - -import ( - "github.com/celo-org/celo-blockchain/common" - "github.com/celo-org/celo-blockchain/contracts/blockchain_parameters" - "github.com/celo-org/celo-blockchain/contracts/currency" - "github.com/celo-org/celo-blockchain/core/vm" -) - -// BlockContext represents contextual information about the blockchain state -// for a given block -type BlockContext struct { - whitelistedCurrencies map[common.Address]struct{} - gasForAlternativeCurrency uint64 -} - -// NewBlockContext creates a block context for a given block (represented by the -// header & state). -// state MUST be pointing to header's stateRoot -func NewBlockContext(vmRunner vm.EVMRunner) BlockContext { - gasForAlternativeCurrency := blockchain_parameters.GetIntrinsicGasForAlternativeFeeCurrencyOrDefault(vmRunner) - - whitelistedCurrenciesArr, err := currency.CurrencyWhitelist(vmRunner) - if err != nil { - whitelistedCurrenciesArr = []common.Address{} - } - - whitelistedCurrencies := make(map[common.Address]struct{}, len(whitelistedCurrenciesArr)) - for _, currency := range whitelistedCurrenciesArr { - whitelistedCurrencies[currency] = struct{}{} - } - - return BlockContext{ - whitelistedCurrencies: whitelistedCurrencies, - gasForAlternativeCurrency: gasForAlternativeCurrency, - } -} - -// GetIntrinsicGasForAlternativeFeeCurrency retrieves intrisic gas to be paid for -// any tx with a non native fee currency -func (bc *BlockContext) GetIntrinsicGasForAlternativeFeeCurrency() uint64 { - return bc.gasForAlternativeCurrency -} - -// IsWhitelisted indicates if the currency is whitelisted as a fee currency -func (bc *BlockContext) IsWhitelisted(feeCurrency *common.Address) bool { - if feeCurrency == nil { - return true - } - - _, ok := bc.whitelistedCurrencies[*feeCurrency] - return ok -} diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 8acf31e59b..128b4049e0 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -2952,8 +2952,9 @@ func TestEIP2718Transition(t *testing.T) { }, }, } - genesis = gspec.MustCommit(db) ) + gspec.Config.Faker = true + genesis := gspec.MustCommit(db) blocks, _ := GenerateChain(gspec.Config, genesis, engine, db, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) @@ -2961,11 +2962,11 @@ func TestEIP2718Transition(t *testing.T) { // One transaction to 0xAAAA signer := types.LatestSigner(gspec.Config) tx, _ := types.SignNewTx(key, signer, &types.AccessListTx{ - ChainID: gspec.Config.ChainID, - Nonce: 0, - To: &aa, - Gas: 30000, - // GasPrice: b.header.BaseFee, + ChainID: gspec.Config.ChainID, + Nonce: 0, + To: &aa, + Gas: 30000, + GasPrice: MockSysContractCallCtx().GetGasPriceMinimum(nil), AccessList: types.AccessList{{ Address: aa, StorageKeys: []common.Hash{{0}}, @@ -3007,7 +3008,11 @@ func TestEIP2718Transition(t *testing.T) { // gasFeeCap - gasTipCap < baseFee. // 6. Legacy transaction behave as expected (e.g. gasPrice = gasFeeCap = gasTipCap). func TestEIP1559Transition(t *testing.T) { - t.Skip("1559 not enabled") + // When executing the transaction, we expect to send base fee to governance contract. + // Hence, we mock registryMock, registry and register governance without implementation. + RegistryProxyOpcodes := common.FromHex("0x60806040526004361061004a5760003560e01c806303386ba3146101e757806342404e0714610280578063bb913f41146102d7578063d29d44ee14610328578063f7e6af8014610379575b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050600081549050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610136576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f20496d706c656d656e746174696f6e20736574000000000000000000000081525060200191505060405180910390fd5b61013f816103d0565b6101b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b60405136810160405236600082376000803683855af43d604051818101604052816000823e82600081146101e3578282f35b8282fd5b61027e600480360360408110156101fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561023a57600080fd5b82018360208201111561024c57600080fd5b8035906020019184600183028401116401000000008311171561026e57600080fd5b909192939192939050505061041b565b005b34801561028c57600080fd5b506102956105c1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102e357600080fd5b50610326600480360360208110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061060d565b005b34801561033457600080fd5b506103776004803603602081101561034b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107bd565b005b34801561038557600080fd5b5061038e610871565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561041257506000801b8214155b92505050919050565b610423610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6104cc8361060d565b600060608473ffffffffffffffffffffffffffffffffffffffff168484604051808383808284378083019250505092505050600060405180830381855af49150503d8060008114610539576040519150601f19603f3d011682016040523d82523d6000602084013e61053e565b606091505b508092508193505050816105ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f696e697469616c697a6174696f6e2063616c6c6261636b206661696c6564000081525060200191505060405180910390fd5b5050505050565b600080600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050805491505090565b610615610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e696d706c656d656e746174696f6e00000000815250601c019050604051809103902060001c0360001b9050610701826103d0565b610773576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f496e76616c696420636f6e74726163742061646472657373000000000000000081525060200191505060405180910390fd5b8181558173ffffffffffffffffffffffffffffffffffffffff167fab64f92ab780ecbf4f3866f57cee465ff36c89450dcce20237ca7a8d81fb7d1360405160405180910390a25050565b6107c5610871565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610865576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f73656e64657220776173206e6f74206f776e657200000000000000000000000081525060200191505060405180910390fd5b61086e816108bd565b50565b600080600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b9050805491505090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6f776e65722063616e6e6f74206265203000000000000000000000000000000081525060200191505060405180910390fd5b6000600160405180807f656970313936372e70726f78792e61646d696e000000000000000000000000008152506013019050604051809103902060001c0360001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe260405160405180910390a2505056fea165627a7a72305820f4f741dbef8c566cb1690ae708b8ef1113bdb503225629cc1f9e86bd47efd1a40029") + RegistryOpcodes := common.FromHex("0x608060405234801561001057600080fd5b50600436106100cf5760003560e01c80638932cbf41161008c578063c586579311610066578063c586579314610407578063dcf0aaed146104a0578063dd9272331461050e578063f2fde38b1461057c576100cf565b80638932cbf4146102e25780638da5cb5b1461039b5780638f32d59b146103e5576100cf565b8063158ef93e146100d457806317c50818146100f6578063715018a6146101a75780637ef50298146101b15780638129fc1c1461021f578063853db32314610229575b600080fd5b6100dc6105c0565b604051808215151515815260200191505060405180910390f35b61018d6004803603604081101561010c57600080fd5b810190808035906020019064010000000081111561012957600080fd5b82018360208201111561013b57600080fd5b8035906020019184602083028401116401000000008311171561015d57600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506105d3565b604051808215151515815260200191505060405180910390f35b6101af610691565b005b6101dd600480360360208110156101c757600080fd5b81019080803590602001909291905050506107ca565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102276107fd565b005b6102a06004803603602081101561023f57600080fd5b810190808035906020019064010000000081111561025c57600080fd5b82018360208201111561026e57600080fd5b8035906020019184600183028401116401000000008311171561029057600080fd5b90919293919293905050506108a6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610359600480360360208110156102f857600080fd5b810190808035906020019064010000000081111561031557600080fd5b82018360208201111561032757600080fd5b8035906020019184600183028401116401000000008311171561034957600080fd5b9091929391929390505050610918565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103a3610a60565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103ed610a89565b604051808215151515815260200191505060405180910390f35b61049e6004803603604081101561041d57600080fd5b810190808035906020019064010000000081111561043a57600080fd5b82018360208201111561044c57600080fd5b8035906020019184600183028401116401000000008311171561046e57600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ae7565b005b6104cc600480360360208110156104b657600080fd5b8101908080359060200190929190505050610c68565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61053a6004803603602081101561052457600080fd5b8101908080359060200190929190505050610d7a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6105be6004803603602081101561059257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610db7565b005b600060149054906101000a900460ff1681565b600080600090505b84849050811015610684578273ffffffffffffffffffffffffffffffffffffffff166001600087878581811061060d57fe5b90506020020135815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561066957600191505061068a565b61067d600182610e3d90919063ffffffff16565b90506105db565b50600090505b9392505050565b610699610a89565b61070b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b60016020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060149054906101000a900460ff1615610880576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600060146101000a81548160ff0219169083151502179055506108a433610ec5565b565b60008083836040516020018083838082843780830192505050925050506040516020818303038152906040528051906020012090506001600082815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691505092915050565b6000808383604051602001808383808284378083019250505092505050604051602081830303815290604052805190602001209050600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610a23576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f6964656e74696669657220686173206e6f20726567697374727920656e74727981525060200191505060405180910390fd5b6001600082815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691505092915050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16610acb611009565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b610aef610a89565b610b61576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b60008383604051602001808383808284378083019250505092505050604051602081830303815290604052805190602001209050816001600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff16817f4166d073a7a5e704ce0db7113320f88da2457f872d46dc020c805c562c1582a0868660405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a350505050565b60008073ffffffffffffffffffffffffffffffffffffffff166001600084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610d3f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f6964656e74696669657220686173206e6f20726567697374727920656e74727981525060200191505060405180910390fd5b6001600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b60006001600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b610dbf610a89565b610e31576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b610e3a81610ec5565b50565b600080828401905083811015610ebb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610f4b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806110126026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60003390509056fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373a265627a7a7231582021f804e21a59bd673149265b250bf50ab40b86b057ee8f9aeab3d3c904a82cdc64736f6c634300050d0032") + var ( aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa") @@ -3037,12 +3042,23 @@ func TestEIP1559Transition(t *testing.T) { Nonce: 0, Balance: big.NewInt(0), }, + common.HexToAddress("0xce10"): { // Registry Proxy + Code: RegistryProxyOpcodes, + Storage: map[common.Hash]common.Hash{ + common.HexToHash("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"): common.HexToHash("0xce11"), // Registry Implementation + common.HexToHash("0x91646b8507bf2e54d7c3de9155442ba111546b81af1cbdd1f68eeb6926b98d58"): common.HexToHash("0xd023"), // Governance Proxy + }, + Balance: big.NewInt(0), + }, + common.HexToAddress("0xce11"): { // Registry Implementation + Code: RegistryOpcodes, + Balance: big.NewInt(0), + }, }, } ) - gspec.Config.DonutBlock = common.Big0 - gspec.Config.EBlock = common.Big0 + gspec.Config.Faker = true genesis := gspec.MustCommit(db) signer := types.LatestSigner(gspec.Config) txFeeRecipient := common.Address{10} @@ -3076,6 +3092,7 @@ func TestEIP1559Transition(t *testing.T) { gspec.MustCommit(diskdb) chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil) + defer chain.Stop() if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -3096,18 +3113,18 @@ func TestEIP1559Transition(t *testing.T) { // 3: Ensure that miner received only the tx's tip. actual := state.GetBalance(txFeeRecipient) - expected := new(big.Int).SetUint64(block.GasUsed() * block.Transactions()[0].GasTipCap().Uint64()) + expected := new(big.Int).SetUint64(block.GasUsed()*block.Transactions()[0].GasTipCap().Uint64() + 1) // 1 is added by accumulateRewards in consensustest.MockEngine, and will break blockchain_repair_test.go if set 0 if actual.Cmp(expected) != 0 { t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual) } // 4: Ensure the tx sender paid for the gasUsed * (tip + block baseFee). - baseFee := uint64(0) // TODO: figure out how to implement this + baseFee := MockSysContractCallCtx().GetGasPriceMinimum(nil).Uint64() actual = new(big.Int).Sub(funds, state.GetBalance(addr1)) expected = new(big.Int).SetUint64(block.GasUsed() * (block.Transactions()[0].GasTipCap().Uint64() + baseFee)) if actual.Cmp(expected) != 0 { - t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual) + t.Fatalf("sender paid fee incorrect: expected %d, got %d", expected, actual) } blocks, _ = GenerateChain(gspec.Config, block, engine, db, 1, func(i int, b *BlockGen) { @@ -3135,7 +3152,7 @@ func TestEIP1559Transition(t *testing.T) { // 6+5: Ensure that miner received only the tx's effective tip. actual = state.GetBalance(block.Coinbase()) - expected = new(big.Int).SetUint64(block.GasUsed() * effectiveTip) + expected = new(big.Int).SetUint64(block.GasUsed()*effectiveTip + 1) // 1 is added by accumulateRewards in consensustest.MockEngine, and will break blockchain_repair_test.go if set 0 if actual.Cmp(expected) != 0 { t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual) } @@ -3144,6 +3161,6 @@ func TestEIP1559Transition(t *testing.T) { actual = new(big.Int).Sub(funds, state.GetBalance(addr2)) expected = new(big.Int).SetUint64(block.GasUsed() * (effectiveTip + baseFee)) if actual.Cmp(expected) != 0 { - t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual) + t.Fatalf("sender paid fee incorrect: expected %d, got %d", expected, actual) } } diff --git a/core/chain_makers.go b/core/chain_makers.go index c07f3d72ae..1476523f8c 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -99,7 +99,8 @@ func (b *BlockGen) AddTxWithChain(bc ChainContext, tx *types.Transaction) { b.statedb.Prepare(tx.Hash(), len(b.txs)) celoMock := testutil.NewCeloMock() - receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{}, celoMock.Runner) + + receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{}, celoMock.Runner, MockSysContractCallCtx()) if err != nil { panic(err) } @@ -107,6 +108,14 @@ func (b *BlockGen) AddTxWithChain(bc ChainContext, tx *types.Transaction) { b.receipts = append(b.receipts, receipt) } +// MockSysContractCallCtx returns a SysContractCallCtx mock. +func MockSysContractCallCtx() *SysContractCallCtx { + return &SysContractCallCtx{ + // Set common.ZeroAddress to non-zero value to test on proper base fee distribution + gasPriceMinimums: map[common.Address]*big.Int{common.ZeroAddress: common.Big3}, + } +} + // GetBalance returns the balance of the given address at the generated block. func (b *BlockGen) GetBalance(addr common.Address) *big.Int { return b.statedb.GetBalance(addr) diff --git a/core/error.go b/core/error.go index 2a5a4ef9d0..9389efc982 100644 --- a/core/error.go +++ b/core/error.go @@ -70,10 +70,6 @@ var ( // minimum specified by the GasPriceMinimum contract. ErrGasPriceDoesNotExceedMinimum = errors.New("gasprice is less than gas price minimum") - // ErrInsufficientFundsForFees is returned if the account does have enough funds (in the - // fee currency used for the transaction) to pay for the gas. - ErrInsufficientFundsForFees = errors.New("insufficient funds to pay for fees") - // ErrNonWhitelistedFeeCurrency is returned if the currency specified to use for the fees // isn't one of the currencies whitelisted for that purpose. ErrNonWhitelistedFeeCurrency = errors.New("non-whitelisted fee currency address") diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index 78dc409b7c..e3d000e582 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -55,10 +55,15 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c header = block.Header() vmRunner = p.bc.NewEVMRunner(header, statedb) gaspool = new(GasPool).AddGas(blockchain_parameters.GetBlockGasLimitOrDefault(vmRunner)) - baseFee *big.Int // TODO: gas price minimum + baseFee *big.Int + sysCtx *SysContractCallCtx ) // Iterate over and process the individual transactions byzantium := p.config.IsByzantium(block.Number()) + espresso := p.bc.chainConfig.IsEHardfork(block.Number()) + if espresso { + sysCtx = NewSysContractCallCtx(p.bc.NewEVMRunner(header, statedb)) + } for i, tx := range block.Transactions() { // If block precaching was interrupted, abort if interrupt != nil && atomic.LoadUint32(interrupt) == 1 { @@ -66,6 +71,9 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c } // Block precaching permitted to continue, execute the transaction statedb.Prepare(tx.Hash(), i) + if espresso { + baseFee = sysCtx.GetGasPriceMinimum(tx.FeeCurrency()) + } if err := precacheTransaction(p.config, p.bc, nil, gaspool, statedb, header, tx, cfg, baseFee); err != nil { return // Ugh, something went horribly wrong, bail out } @@ -94,6 +102,11 @@ func precacheTransaction(config *params.ChainConfig, bc *BlockChain, author *com txContext := NewEVMTxContext(msg) vm := vm.NewEVM(context, txContext, statedb, config, cfg) - _, err = ApplyMessage(vm, msg, gaspool, bc.NewEVMRunner(header, statedb)) + var sysCtx *SysContractCallCtx + if config.IsEHardfork(header.Number) { + sysVmRunner := bc.NewEVMRunner(header, statedb) + sysCtx = NewSysContractCallCtx(sysVmRunner) + } + _, err = ApplyMessage(vm, msg, gaspool, bc.NewEVMRunner(header, statedb), sysCtx) return err } diff --git a/core/state_processor.go b/core/state_processor.go index e899125259..82376fdcac 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -81,17 +81,30 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg statedb.IntermediateRoot(true) } + var ( + baseFee *big.Int + sysCtx *SysContractCallCtx + ) + if p.bc.Config().IsEHardfork(blockNumber) { + sysVmRunner := p.bc.NewEVMRunner(header, statedb) + sysCtx = NewSysContractCallCtx(sysVmRunner) + if p.bc.Config().Faker { + sysCtx = MockSysContractCallCtx() + } + } blockContext := NewEVMBlockContext(header, p.bc, nil) - var gpm *big.Int // collect basefee from blockContext or somewhere else vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg) // Iterate over and process the individual transactions for i, tx := range block.Transactions() { - msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number), gpm) + if p.bc.chainConfig.IsEHardfork(header.Number) { + baseFee = sysCtx.GetGasPriceMinimum(tx.FeeCurrency()) + } + msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number), baseFee) if err != nil { return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } statedb.Prepare(tx.Hash(), i) - receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv, vmRunner) + receipt, err := applyTransaction(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv, vmRunner, sysCtx) if err != nil { return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } @@ -107,7 +120,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg return receipts, allLogs, *usedGas, nil } -func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, txFeeRecipient *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM, vmRunner vm.EVMRunner) (*types.Receipt, error) { +func applyTransaction(msg types.Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM, vmRunner vm.EVMRunner, sysCtx *SysContractCallCtx) (*types.Receipt, error) { if config.IsDonut(blockNumber) && !tx.Protected() { return nil, ErrUnprotectedTransaction } @@ -117,7 +130,7 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon evm.Reset(txContext, statedb) // Apply the transaction to the current state (included in the env). - result, err := ApplyMessage(evm, msg, gp, vmRunner) + result, err := ApplyMessage(evm, msg, gp, vmRunner, sysCtx) if err != nil { return nil, err } @@ -160,14 +173,17 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon // and uses the input parameters for its environment. It returns the receipt // for the transaction, gas used and an error if the transaction failed, // indicating the block was invalid. -func ApplyTransaction(config *params.ChainConfig, bc ChainContext, txFeeRecipient *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config, vmRunner vm.EVMRunner) (*types.Receipt, error) { - // TODO(Joshua): Set appropriate GPM/basefee here - msg, err := tx.AsMessage(types.MakeSigner(config, header.Number), nil) +func ApplyTransaction(config *params.ChainConfig, bc ChainContext, txFeeRecipient *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config, vmRunner vm.EVMRunner, sysCtx *SysContractCallCtx) (*types.Receipt, error) { + var baseFee *big.Int + if config.IsEHardfork(header.Number) { + baseFee = sysCtx.GetGasPriceMinimum(tx.FeeCurrency()) + } + msg, err := tx.AsMessage(types.MakeSigner(config, header.Number), baseFee) if err != nil { return nil, err } // Create a new context to be used in the EVM environment blockContext := NewEVMBlockContext(header, bc, txFeeRecipient) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg) - return applyTransaction(msg, config, bc, txFeeRecipient, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv, vmRunner) + return applyTransaction(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv, vmRunner, sysCtx) } diff --git a/core/state_processor_test.go b/core/state_processor_test.go index b2b1552285..3abb1ae7bf 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -51,12 +51,23 @@ func TestStateProcessorErrors(t *testing.T) { ChurritoBlock: big.NewInt(0), DonutBlock: big.NewInt(0), EBlock: big.NewInt(0), + Faker: true, } signer = types.LatestSigner(config) testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") ) var makeTx = func(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, feeCurrency *common.Address, gatewayFeeRecipient *common.Address, gatewayFee *big.Int, data []byte) *types.Transaction { - tx, _ := types.SignTx(types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, feeCurrency, gatewayFeeRecipient, gatewayFee, data), signer, testKey) + tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: nonce, + GasPrice: gasPrice, + Gas: gasLimit, + FeeCurrency: feeCurrency, + GatewayFeeRecipient: gatewayFeeRecipient, + GatewayFee: gatewayFee, + To: &to, + Value: amount, + Data: data, + }), signer, testKey) return tx } @@ -78,7 +89,7 @@ func TestStateProcessorErrors(t *testing.T) { Config: config, Alloc: GenesisAlloc{ common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{ - Balance: big.NewInt(1000000000000000000), // 1 ether + Balance: big.NewInt(params.Ether), Nonce: 0, }, }, @@ -91,101 +102,110 @@ func TestStateProcessorErrors(t *testing.T) { tooBigNumber := new(big.Int).Set(bigNumber) tooBigNumber.Add(tooBigNumber, common.Big1) for i, tt := range []struct { + name string txs []*types.Transaction want string }{ - { // ErrNonceTooLow + { + name: "ErrNonceTooLow", txs: []*types.Transaction{ makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil, nil, nil, nil), makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil, nil, nil, nil), }, want: "could not apply tx 1 [0x007b02f957123ca016999a9e826e6dfd4f7b36d2406f0057526aeb9a6202adfa]: nonce too low: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tx: 0 state: 1", }, - { // ErrNonceTooHigh + { + name: "ErrNonceTooHigh", txs: []*types.Transaction{ makeTx(100, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil, nil, nil, nil), }, want: "could not apply tx 0 [0xa0f823b5e4c81b1b0528531f25f06514b8d8c766de99747ad3137577987ff0b7]: nonce too high: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tx: 100 state: 0", }, - { // ErrGasLimitReached + { + name: "ErrGasLimitReached", txs: []*types.Transaction{ makeTx(0, common.Address{}, big.NewInt(0), 21000000, big.NewInt(875000000), nil, nil, nil, nil), }, want: "could not apply tx 0 [0xbd4180e51b03a814b5c96016cc47575fdf777288fc165dc5ad3f9dd3e0e8dcee]: gas limit reached", }, - { // ErrInsufficientFundsForTransfer + { + name: "ErrInsufficientFundsForTransfer", txs: []*types.Transaction{ makeTx(0, common.Address{}, big.NewInt(1000000000000000000), params.TxGas, big.NewInt(875000000), nil, nil, nil, nil), }, - want: "could not apply tx 0 [0x4610b4dc2b2508d3555499e34eed7315ad3b5215ec8dbc1cb22bda45e1667cf6]: insufficient funds to pay for fees", - // want: "could not apply tx 0 [0x4610b4dc2b2508d3555499e34eed7315ad3b5215ec8dbc1cb22bda45e1667cf6]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 1000018375000000000", + want: "could not apply tx 0 [0x4610b4dc2b2508d3555499e34eed7315ad3b5215ec8dbc1cb22bda45e1667cf6]: insufficient funds for gas * price + value + gatewayFee: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 1000018375000000000", }, - { // ErrInsufficientFunds + { + name: "ErrInsufficientFunds", txs: []*types.Transaction{ makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(900000000000000000), nil, nil, nil, nil), }, - want: "could not apply tx 0 [0x99113426d253b063416fc51259f7f78c832d49a9bf09f2748fe50fd3130e7162]: insufficient funds to pay for fees", - // want: "could not apply tx 0 [0x99113426d253b063416fc51259f7f78c832d49a9bf09f2748fe50fd3130e7162]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 18900000000000000000000", + want: "could not apply tx 0 [0x99113426d253b063416fc51259f7f78c832d49a9bf09f2748fe50fd3130e7162]: insufficient funds for gas * price + value + gatewayFee: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 18900000000000000000000", }, // ErrGasUintOverflow // One missing 'core' error is ErrGasUintOverflow: "gas uint64 overflow", // In order to trigger that one, we'd have to allocate a _huge_ chunk of data, such that the // multiplication len(data) +gas_per_byte overflows uint64. Not testable at the moment - { // ErrIntrinsicGas + { + name: "ErrIntrinsicGas", txs: []*types.Transaction{ makeTx(0, common.Address{}, big.NewInt(0), params.TxGas-1000, big.NewInt(875000000), nil, nil, nil, nil), }, want: "could not apply tx 0 [0x436f2e580794590a8627d8155b638c4ed1b41375283ba19f13a5a8b76ed51fdc]: intrinsic gas too low: have 20000, want 21000", }, - { // ErrGasLimitReached + { + name: "ErrGasLimitReached", txs: []*types.Transaction{ makeTx(0, common.Address{}, big.NewInt(0), params.TxGas*1000, big.NewInt(875000000), nil, nil, nil, nil), }, want: "could not apply tx 0 [0xbd4180e51b03a814b5c96016cc47575fdf777288fc165dc5ad3f9dd3e0e8dcee]: gas limit reached", }, - // TODO(Joshua): This test only partially works until 1559 support is merged - // { // ErrFeeCapTooLow - // txs: []*types.Transaction{ - // mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(0), big.NewInt(0)), - // }, - // want: "could not apply tx 0 [0xc4ab868fef0c82ae0387b742aee87907f2d0fc528fc6ea0a021459fb0fc4a4a8]: max fee per gas less than block base fee: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas: 0 baseFee: 875000000", - // }, - { // ErrTipVeryHigh + { + name: "ErrFeeCapTooLow", + txs: []*types.Transaction{ + mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(0), big.NewInt(0)), + }, + want: "could not apply tx 0 [0xc4ab868fef0c82ae0387b742aee87907f2d0fc528fc6ea0a021459fb0fc4a4a8]: max fee per gas less than block base fee: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas: 0 baseFee: 3", // MockSysContractCallCtx expects baseFee to be 3 + }, + { + name: "ErrTipVeryHigh", txs: []*types.Transaction{ mkDynamicTx(0, common.Address{}, params.TxGas, tooBigNumber, big.NewInt(1)), }, want: "could not apply tx 0 [0x15b8391b9981f266b32f3ab7da564bbeb3d6c21628364ea9b32a21139f89f712]: max priority fee per gas higher than 2^256-1: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxPriorityFeePerGas bit length: 257", }, - { // ErrFeeCapVeryHigh + { + name: "ErrFeeCapVeryHigh", txs: []*types.Transaction{ mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(1), tooBigNumber), }, want: "could not apply tx 0 [0x48bc299b83fdb345c57478f239e89814bb3063eb4e4b49f3b6057a69255c16bd]: max fee per gas higher than 2^256-1: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas bit length: 257", }, - { // ErrTipAboveFeeCap + { + name: "ErrTipAboveFeeCap", txs: []*types.Transaction{ mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(2), big.NewInt(1)), }, want: "could not apply tx 0 [0xf987a31ff0c71895780a7612f965a0c8b056deb54e020bb44fa478092f14c9b4]: max priority fee per gas higher than max fee per gas: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxPriorityFeePerGas: 2, maxFeePerGas: 1", }, - { // ErrInsufficientFunds + { // Available balance: 1000000000000000000 - // Effective cost: 18375000021000 + // Effective cost: 84000 // FeeCap * gas: 1050000000000000000 // This test is designed to have the effective cost be covered by the balance, but // the extended requirement on FeeCap*gas < balance to fail + name: "ErrInsufficientFunds", txs: []*types.Transaction{ - mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(1), big.NewInt(50000000000000)), + mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(1), big.NewInt(50_000_000_000_000)), }, - want: "could not apply tx 0 [0x413603cd096a87f41b1660d3ed3e27d62e1da78eac138961c0a1314ed43bd129]: insufficient funds to pay for fees", - // want: "could not apply tx 0 [0x413603cd096a87f41b1660d3ed3e27d62e1da78eac138961c0a1314ed43bd129]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 1050000000000000000", + want: "could not apply tx 0 [0x413603cd096a87f41b1660d3ed3e27d62e1da78eac138961c0a1314ed43bd129]: insufficient funds for gas * price + value + gatewayFee: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 1050000000000000000", }, { // Another ErrInsufficientFunds, this one to ensure that feecap/tip of max u256 is allowed + name: "Another ErrInsufficientFunds", txs: []*types.Transaction{ mkDynamicTx(0, common.Address{}, params.TxGas, bigNumber, bigNumber), }, - want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds to pay for fees", - // want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 2431633873983640103894990685182446064918669677978451844828609264166175722438635000", + want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value + gatewayFee: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 2431633873983640103894990685182446064918669677978451844828609264166175722438635000", }, } { block := GenerateBadBlock(genesis, mockEngine.NewFaker(), tt.txs, gspec.Config) @@ -194,7 +214,7 @@ func TestStateProcessorErrors(t *testing.T) { t.Fatal("block imported without errors") } if have, want := err.Error(), tt.want; have != want { - t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want) + t.Errorf("test %d %v:\nhave \"%v\"\nwant \"%v\"\n", i, tt.name, have, want) } } } diff --git a/core/state_transition.go b/core/state_transition.go index 303a70f006..de97ba17a9 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -38,8 +38,6 @@ import ( // Used for EOA Check // var emptyCodeHash = crypto.Keccak256Hash(nil) -var is1559 = false - /* The State Transitioning Model @@ -71,6 +69,7 @@ type StateTransition struct { evm *vm.EVM vmRunner vm.EVMRunner gasPriceMinimum *big.Int + sysCtx *SysContractCallCtx } // Message represents a message sent to a contract. @@ -207,8 +206,13 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b } // NewStateTransition initialises and returns a new state transition object. -func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool, vmRunner vm.EVMRunner) *StateTransition { - gasPriceMinimum, _ := gpm.GetGasPriceMinimum(vmRunner, msg.FeeCurrency()) +func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool, vmRunner vm.EVMRunner, sysCtx *SysContractCallCtx) *StateTransition { + var gasPriceMinimum *big.Int + if evm.ChainConfig().IsEHardfork(evm.Context.BlockNumber) { + gasPriceMinimum = sysCtx.GetGasPriceMinimum(msg.FeeCurrency()) + } else { + gasPriceMinimum, _ = gpm.GetGasPriceMinimum(vmRunner, msg.FeeCurrency()) + } return &StateTransition{ gp: gp, @@ -222,6 +226,7 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool, vmRunner vm.EVMRu data: msg.Data(), state: evm.StateDB, gasPriceMinimum: gasPriceMinimum, + sysCtx: sysCtx, } } @@ -232,18 +237,18 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool, vmRunner vm.EVMRu // the gas used (which includes gas refunds) and an error if it failed. An error always // indicates a core error meaning that the message would always fail for that particular // state and would never be accepted within a block. -func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool, vmRunner vm.EVMRunner) (*ExecutionResult, error) { +func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool, vmRunner vm.EVMRunner, sysCtx *SysContractCallCtx) (*ExecutionResult, error) { log.Trace("Applying state transition message", "from", msg.From(), "nonce", msg.Nonce(), "to", msg.To(), "gas price", msg.GasPrice(), "fee currency", msg.FeeCurrency(), "gateway fee recipient", msg.GatewayFeeRecipient(), "gateway fee", msg.GatewayFee(), "gas", msg.Gas(), "value", msg.Value(), "data", msg.Data()) - return NewStateTransition(evm, msg, gp, vmRunner).TransitionDb() + return NewStateTransition(evm, msg, gp, vmRunner, sysCtx).TransitionDb() } // ApplyMessageWithoutGasPriceMinimum applies the given message with the gas price minimum // set to zero. It's only for use in eth_call and eth_estimateGas, so that they can be used // with gas price set to zero if the sender doesn't have funds to pay for gas. // Returns the gas used (which does not include gas refunds) and an error if it failed. -func ApplyMessageWithoutGasPriceMinimum(evm *vm.EVM, msg Message, gp *GasPool, vmRunner vm.EVMRunner) (*ExecutionResult, error) { +func ApplyMessageWithoutGasPriceMinimum(evm *vm.EVM, msg Message, gp *GasPool, vmRunner vm.EVMRunner, sysCtx *SysContractCallCtx) (*ExecutionResult, error) { log.Trace("Applying state transition message without gas price minimum", "from", msg.From(), "nonce", msg.Nonce(), "to", msg.To(), "fee currency", msg.FeeCurrency(), "gateway fee recipient", msg.GatewayFeeRecipient(), "gateway fee", msg.GatewayFee(), "gas limit", msg.Gas(), "value", msg.Value(), "data", msg.Data()) - st := NewStateTransition(evm, msg, gp, vmRunner) + st := NewStateTransition(evm, msg, gp, vmRunner, sysCtx) st.gasPriceMinimum = common.Big0 return st.TransitionDb() } @@ -258,20 +263,19 @@ func (st *StateTransition) to() common.Address { // payFees deducts gas and gateway fees from sender balance and adds the purchased amount of gas to the state. func (st *StateTransition) payFees(eHardFork bool) error { - if !currency.IsWhitelisted(st.vmRunner, st.msg.FeeCurrency()) { + var isWhiteListed bool + if eHardFork { + isWhiteListed = st.sysCtx.IsWhitelisted(st.msg.FeeCurrency()) + } else { + isWhiteListed = currency.IsWhitelisted(st.vmRunner, st.msg.FeeCurrency()) + } + if !isWhiteListed { log.Trace("Fee currency not whitelisted", "fee currency address", st.msg.FeeCurrency()) return ErrNonWhitelistedFeeCurrency } - feeVal := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice) - - // If GatewayFeeRecipient is unspecified, the gateway fee value is ignore and the sender is not charged. - if st.msg.GatewayFeeRecipient() != nil { - feeVal.Add(feeVal, st.msg.GatewayFee()) - } - - if !st.canPayFee(st.msg.From(), feeVal, st.msg.FeeCurrency(), eHardFork) { - return ErrInsufficientFundsForFees + if err := st.canPayFee(st.msg.From(), st.msg.FeeCurrency(), eHardFork); err != nil { + return err } if err := st.gp.SubGas(st.msg.Gas()); err != nil { return err @@ -279,29 +283,72 @@ func (st *StateTransition) payFees(eHardFork bool) error { st.initialGas = st.msg.Gas() st.gas += st.msg.Gas() - err := st.debitFee(st.msg.From(), feeVal, st.msg.FeeCurrency()) + err := st.debitFee(st.msg.From(), st.msg.FeeCurrency()) return err } -func (st *StateTransition) canPayFee(accountOwner common.Address, fee *big.Int, feeCurrency *common.Address, eHardfork bool) bool { +// canPayFee checks whether accountOwner's balance can cover transaction fee. +// +// For native token(CELO) as feeCurrency: +// - Pre-Espresso: it ensures balance >= GasPrice * gas + gatewayFee (1) +// - Post-Espresso: it ensures balance >= GasFeeCap * gas + value + gatewayFee (2) +// For non-native tokens(cUSD, cEUR, ...) as feeCurrency: +// - Pre-Espresso: it ensures balance > GasPrice * gas + gatewayFee (3) +// - Post-Espresso: it ensures balance >= GasFeeCap * gas + gatewayFee (4) +func (st *StateTransition) canPayFee(accountOwner common.Address, feeCurrency *common.Address, espresso bool) error { if feeCurrency == nil { - // 1559 Check that value + gas price >= account balance - if eHardfork { - fee = fee.Add(fee, st.msg.Value()) - } - return st.state.GetBalance(accountOwner).Cmp(fee) >= 0 - } + balance := st.state.GetBalance(st.msg.From()) + if espresso { + // feeGap = GasFeeCap * gas + value + gatewayFee, as in (2) + feeGap := new(big.Int).SetUint64(st.msg.Gas()) + feeGap.Mul(feeGap, st.gasFeeCap) + feeGap.Add(feeGap, st.value) + if st.msg.GatewayFeeRecipient() != nil { + feeGap.Add(feeGap, st.msg.GatewayFee()) + } - balance, err := currency.GetBalanceOf(st.vmRunner, accountOwner, *feeCurrency) - if err != nil { - return false - } - // The logic in ValidateTransactorBalanceCoversTx tx_pool.go should match to this. - if eHardfork { - return balance.Cmp(fee) >= 0 + if balance.Cmp(feeGap) < 0 { + return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), balance, feeGap) + } + } else { + // effectiveFee = GasPrice * gas + gatewayFee, as in (1) + effectiveFee := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice) + if st.msg.GatewayFeeRecipient() != nil { + effectiveFee.Add(effectiveFee, st.msg.GatewayFee()) + } + + if balance.Cmp(effectiveFee) < 0 { + return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), balance, effectiveFee) + } + } + return nil } else { - return balance.Cmp(fee) > 0 + balance, err := currency.GetBalanceOf(st.vmRunner, accountOwner, *feeCurrency) + if err != nil { + return err + } + if espresso { + // feeGap = GasFeeCap * gas + gatewayFee, as in (4) + feeGap := new(big.Int).SetUint64(st.msg.Gas()) + feeGap.Mul(feeGap, st.gasFeeCap) + if st.msg.GatewayFeeRecipient() != nil { + feeGap.Add(feeGap, st.msg.GatewayFee()) + } + if balance.Cmp(feeGap) < 0 { + return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), balance, feeGap) + } + } else { + // effectiveFee = GasPrice * gas + gatewayFee, as in (3) + effectiveFee := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice) + if st.msg.GatewayFeeRecipient() != nil { + effectiveFee.Add(effectiveFee, st.msg.GatewayFee()) + } + if balance.Cmp(effectiveFee) <= 0 { + return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), balance, effectiveFee) + } + } } + return nil } func (st *StateTransition) debitGas(address common.Address, amount *big.Int, feeCurrency *common.Address) error { @@ -360,14 +407,19 @@ func (st *StateTransition) creditGasFees( return err } -func (st *StateTransition) debitFee(from common.Address, amount *big.Int, feeCurrency *common.Address) (err error) { - log.Trace("Debiting fee", "from", from, "amount", amount, "feeCurrency", feeCurrency) +func (st *StateTransition) debitFee(from common.Address, feeCurrency *common.Address) (err error) { + effectiveFee := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice) + // If GatewayFeeRecipient is unspecified, the gateway fee value is ignore and the sender is not charged. + if st.msg.GatewayFeeRecipient() != nil { + effectiveFee.Add(effectiveFee, st.msg.GatewayFee()) + } + log.Trace("Debiting fee", "from", from, "amount", effectiveFee, "feeCurrency", feeCurrency) // native currency if feeCurrency == nil { - st.state.SubBalance(from, amount) + st.state.SubBalance(from, effectiveFee) return nil } else { - return st.debitGas(from, amount, feeCurrency) + return st.debitGas(from, effectiveFee, feeCurrency) } } @@ -384,12 +436,7 @@ func (st *StateTransition) preCheck() error { } } - // Make sure this transaction's gas price is valid. - if st.gasPrice.Cmp(st.gasPriceMinimum) < 0 { - log.Debug("Tx gas price is less than minimum", "minimum", st.gasPriceMinimum, "price", st.gasPrice) - return ErrGasPriceDoesNotExceedMinimum - } - // Make sure that transaction gasFeeCap is greater than the baseFee (post E) + // Make sure that transaction gasFeeCap >= baseFee (post Espresso) if st.evm.ChainConfig().IsEHardfork(st.evm.Context.BlockNumber) { // Skip the checks if gas fields are zero and baseFee was explicitly disabled (eth_call) if !st.evm.Config.NoBaseFee || st.gasFeeCap.BitLen() > 0 || st.gasTipCap.BitLen() > 0 { @@ -405,13 +452,16 @@ func (st *StateTransition) preCheck() error { return fmt.Errorf("%w: address %v, maxPriorityFeePerGas: %s, maxFeePerGas: %s", ErrTipAboveFeeCap, st.msg.From().Hex(), st.gasTipCap, st.gasFeeCap) } - // This will panic if baseFee is nil, but basefee presence is verified - // as part of header validation. if st.gasFeeCap.Cmp(st.gasPriceMinimum) < 0 { return fmt.Errorf("%w: address %v, maxFeePerGas: %s baseFee: %s", ErrFeeCapTooLow, st.msg.From().Hex(), st.gasFeeCap, st.gasPriceMinimum) } } + } else { // Make sure this transaction's gas price >= baseFee (pre Espresso) + if st.gasPrice.Cmp(st.gasPriceMinimum) < 0 { + log.Debug("Tx gas price is less than minimum", "minimum", st.gasPriceMinimum, "price", st.gasPrice) + return ErrGasPriceDoesNotExceedMinimum + } } // // Make sure the sender is an EOA @@ -473,7 +523,11 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { gasForAlternativeCurrency := uint64(0) // If the fee currency is nil, do not retrieve the intrinsic gas adjustment from the chain state, as it will not be used. if msg.FeeCurrency() != nil { - gasForAlternativeCurrency = blockchain_parameters.GetIntrinsicGasForAlternativeFeeCurrencyOrDefault(st.vmRunner) + if eHardfork { + gasForAlternativeCurrency = st.sysCtx.GetIntrinsicGasForAlternativeFeeCurrency() + } else { + gasForAlternativeCurrency = blockchain_parameters.GetIntrinsicGasForAlternativeFeeCurrencyOrDefault(st.vmRunner) + } } gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, msg.FeeCurrency(), gasForAlternativeCurrency, istanbul) if err != nil { @@ -521,7 +575,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { st.refundGas(params.RefundQuotientEIP3529) } - err = st.distributeTxFees(eHardfork) + err = st.distributeTxFees() if err != nil { return nil, err } @@ -533,7 +587,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } // distributeTxFees calculates the amounts and recipients of transaction fees and credits the accounts. -func (st *StateTransition) distributeTxFees(eHardfork bool) error { +func (st *StateTransition) distributeTxFees() error { // Run only primary evm.Call() with tracer if st.evm.GetDebug() { st.evm.SetDebug(false) @@ -548,13 +602,8 @@ func (st *StateTransition) distributeTxFees(eHardfork bool) error { // Divide the transaction into a base (the minimum transaction fee) and tip (any extra, or min(max tip, feecap - GPM) if ehardfork). baseTxFee := new(big.Int).Mul(gasUsed, st.gasPriceMinimum) + // No need to do effectiveTip calculation, because st.gasPrice == effectiveGasPrice, and effectiveTip = effectiveGasPrice - baseTxFee tipTxFee := new(big.Int).Sub(totalTxFee, baseTxFee) - // nolint will fix in 1559 activation - if is1559 && eHardfork { - // effectiveTip := cmath.BigMin(st.gasTipCap, new(big.Int).Sub(st.gasFeeCap, st.gasPriceMinimum)) - // tipTxFee = tipTxFee.Mul(effectiveTip, gasUsed) - // refund.Add(refund, (new(big.Int)).Sub()) - } feeCurrency := st.msg.FeeCurrency() diff --git a/core/sys_context.go b/core/sys_context.go new file mode 100644 index 0000000000..cf6c819f16 --- /dev/null +++ b/core/sys_context.go @@ -0,0 +1,80 @@ +package core + +import ( + "math/big" + + "github.com/celo-org/celo-blockchain/common" + "github.com/celo-org/celo-blockchain/contracts/blockchain_parameters" + "github.com/celo-org/celo-blockchain/contracts/currency" + "github.com/celo-org/celo-blockchain/contracts/gasprice_minimum" + "github.com/celo-org/celo-blockchain/core/vm" +) + +// SysContractCallCtx represents a system contract call context for a given block (represented by vm.EVMRunner). +// It MUST sit on the header.Root of a block, which is parent of the block we intend to deal with. +type SysContractCallCtx struct { + whitelistedCurrencies map[common.Address]struct{} + gasForAlternativeCurrency uint64 + // gasPriceMinimums stores values for whitelisted currencies keyed by their contract address + // Note that native token(CELO) is keyed by common.ZeroAddress + gasPriceMinimums map[common.Address]*big.Int +} + +// NewSysContractCallCtx creates the SysContractCallCtx object and makes the contract calls. +func NewSysContractCallCtx(vmRunner vm.EVMRunner) (sc *SysContractCallCtx) { + sc = &SysContractCallCtx{ + whitelistedCurrencies: make(map[common.Address]struct{}), + gasPriceMinimums: make(map[common.Address]*big.Int), + } + // intrinsic gas + sc.gasForAlternativeCurrency = blockchain_parameters.GetIntrinsicGasForAlternativeFeeCurrencyOrDefault(vmRunner) + // whitelist + whiteListedArr, err := currency.CurrencyWhitelist(vmRunner) + if err != nil { + whiteListedArr = []common.Address{} + } + for _, feeCurrency := range whiteListedArr { + sc.whitelistedCurrencies[feeCurrency] = struct{}{} + } + // gas price minimum + celoGPM, _ := gasprice_minimum.GetGasPriceMinimum(vmRunner, nil) + sc.gasPriceMinimums[common.ZeroAddress] = celoGPM + + for feeCurrency := range sc.whitelistedCurrencies { + gasPriceMinimum, _ := gasprice_minimum.GetGasPriceMinimum(vmRunner, &feeCurrency) + sc.gasPriceMinimums[feeCurrency] = gasPriceMinimum + } + + return +} + +// GetIntrinsicGasForAlternativeFeeCurrency retrieves intrinsic gas for non-native fee currencies. +func (sc *SysContractCallCtx) GetIntrinsicGasForAlternativeFeeCurrency() uint64 { + return sc.gasForAlternativeCurrency +} + +// IsWhitelisted indicates if the fee currency is whitelisted, or it's native token(CELO). +func (sc *SysContractCallCtx) IsWhitelisted(feeCurrency *common.Address) bool { + if feeCurrency == nil { + return true + } + _, ok := sc.whitelistedCurrencies[*feeCurrency] + return ok +} + +// GetGasPriceMinimum retrieves gas price minimum for given fee currency address. +func (sc *SysContractCallCtx) GetGasPriceMinimum(feeCurrency *common.Address) *big.Int { + // feeCurrency for native token(CELO) is nil, so we bind common.ZeroAddress as key + var key common.Address + if feeCurrency == nil { + key = common.ZeroAddress + } else { + key = *feeCurrency + } + + gasPriceMinimum, ok := sc.gasPriceMinimums[key] + if !ok { + return gasprice_minimum.FallbackGasPriceMinimum + } + return gasPriceMinimum +} diff --git a/core/tx_pool.go b/core/tx_pool.go index 2d5d6a4992..8277e9853e 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -245,7 +245,7 @@ func (config *TxPoolConfig) sanitize() TxPoolConfig { } type txPoolContext struct { - BlockContext + *SysContractCallCtx *currency.CurrencyManager } @@ -1369,7 +1369,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { pool.currentMaxGas = blockchain_parameters.GetBlockGasLimitOrDefault(pool.currentVMRunner) // atomic store of the new txPoolContext newCtx := txPoolContext{ - NewBlockContext(pool.currentVMRunner), + NewSysContractCallCtx(pool.currentVMRunner), currency.NewManager(pool.currentVMRunner), } pool.currentCtx.Store(newCtx) @@ -1662,38 +1662,67 @@ func (pool *TxPool) demoteUnexecutables() { } } -// ValidateTransactorBalanceCoversTx validates transactor has enough funds to cover transaction cost: V + GP * GL. -func ValidateTransactorBalanceCoversTx(tx *types.Transaction, from common.Address, currentState *state.StateDB, currentVMRunner vm.EVMRunner, eHardfork bool) error { - if tx.FeeCurrency() == nil && currentState.GetBalance(from).Cmp(tx.Cost()) < 0 { - log.Debug("Insufficient funds", - "from", from, "Transaction cost", tx.Cost(), "to", tx.To(), - "gas", tx.Gas(), "gas price", tx.GasPrice(), "nonce", tx.Nonce(), - "value", tx.Value(), "fee currency", tx.FeeCurrency(), "balance", currentState.GetBalance(from)) - return ErrInsufficientFunds - } else if tx.FeeCurrency() != nil { - feeCurrencyBalance, err := currency.GetBalanceOf(currentVMRunner, from, *tx.FeeCurrency()) - - if err != nil { - log.Debug("validateTx error in getting fee currency balance", "feeCurrency", tx.FeeCurrency(), "error", err) - return err +// ValidateTransactorBalanceCoversTx validates transactor has enough funds to cover transaction cost, the rules are consistent with state_transition. +// +// For native token(CELO) as feeCurrency: +// - Pre-Espresso: it ensures balance >= GasPrice * gas + value + gatewayFee (1) +// - Post-Espresso: it ensures balance >= GasFeeCap * gas + value + gatewayFee (2) +// For non-native tokens(cUSD, cEUR, ...) as feeCurrency: +// - Pre-Espresso: it ensures balance > GasPrice * gas + gatewayFee (3) +// - Post-Espresso: it ensures balance >= GasFeeCap * gas + gatewayFee (4) +func ValidateTransactorBalanceCoversTx(tx *types.Transaction, from common.Address, currentState *state.StateDB, currentVMRunner vm.EVMRunner, espresso bool) error { + if tx.FeeCurrency() == nil { + balance := currentState.GetBalance(from) + var cost *big.Int + + if espresso { + // cost = GasFeeCap * gas + value + gatewayFee, as in (2) + cost = new(big.Int).SetUint64(tx.Gas()) + cost.Mul(cost, tx.GasFeeCap()) + cost.Add(cost, tx.Value()) + if tx.GatewayFeeRecipient() != nil { + cost.Add(cost, tx.GatewayFee()) + } + } else { + // cost = GasPrice * gas + value + gatewayFee, as in (1) + cost = tx.Cost() } - // This is required to match the logic in canPayFee() state_transition.go - // - Prior to E hardfork: we require the balance to be strictly greater than the fee, - // which means we reject the transaction if balance <= fee - // - After E hardfork: we require the balance to be greater than or equal to the fee, - // which means we reject the transaction if balance < fee - fee := tx.Fee() - if (eHardfork && feeCurrencyBalance.Cmp(fee) < 0) || (!eHardfork && feeCurrencyBalance.Cmp(fee) <= 0) { - log.Debug("validateTx insufficient fee currency", "feeCurrency", tx.FeeCurrency(), "feeCurrencyBalance", feeCurrencyBalance) + if balance.Cmp(cost) < 0 { + log.Debug("ValidateTransactorBalanceCoversTx: insufficient CELO funds", + "from", from, "Transaction cost", cost, "to", tx.To(), + "gas", tx.Gas(), "gas price", tx.GasPrice(), "nonce", tx.Nonce(), + "value", tx.Value(), "fee currency", tx.FeeCurrency(), "balance", balance) return ErrInsufficientFunds } + } else { + balance, err := currency.GetBalanceOf(currentVMRunner, from, *tx.FeeCurrency()) + if err != nil { + log.Debug("ValidateTransactorBalanceCoversTx: error in getting fee currency balance", "feeCurrency", tx.FeeCurrency()) + return err + } - if currentState.GetBalance(from).Cmp(tx.Value()) < 0 { - log.Debug("validateTx insufficient funds", "balance", currentState.GetBalance(from).String()) - return ErrInsufficientFunds + if espresso { + // cost = GasFeeCap * gas + gatewayFee, as in (4) + cost := new(big.Int).SetUint64(tx.Gas()) + cost.Mul(cost, tx.GasFeeCap()) + if tx.GatewayFeeRecipient() != nil { + cost.Add(cost, tx.GatewayFee()) + } + if balance.Cmp(cost) < 0 { + log.Debug("ValidateTransactorBalanceCoversTx: insufficient funds", "feeCurrency", tx.FeeCurrency(), "balance", balance) + return ErrInsufficientFunds + } + } else { + // cost = GasPrice * gas + gatewayFee, as in (3) + cost := tx.Fee() + if balance.Cmp(cost) <= 0 { + log.Debug("ValidateTransactorBalanceCoversTx: insufficient funds", "feeCurrency", tx.FeeCurrency(), "balance", balance) + return ErrInsufficientFunds + } } } + return nil } diff --git a/core/types/transaction.go b/core/types/transaction.go index c1b48307b4..202e0d49f3 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -334,7 +334,7 @@ func (tx *Transaction) To() *common.Address { return &cpy } -// Cost returns amount + gasprice * gaslimit + gatewayfee. +// Cost returns value + gasprice * gaslimit + gatewayfee. func (tx *Transaction) Cost() *big.Int { total := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas())) total.Add(total, tx.Value()) diff --git a/eth/state_accessor.go b/eth/state_accessor.go index b8b4dd3b3c..61a52af83f 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -19,6 +19,7 @@ package eth import ( "errors" "fmt" + "math/big" "time" "github.com/celo-org/celo-blockchain/common" @@ -158,11 +159,21 @@ func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec if txIndex == 0 && len(block.Transactions()) == 0 { return nil, vm.BlockContext{}, nil, statedb, nil } + // Get the SysContractCallCtx + var sysCtx *core.SysContractCallCtx + espresso := eth.blockchain.Config().IsEHardfork(block.Number()) + if espresso { + sysCtx = core.NewSysContractCallCtx(eth.blockchain.NewEVMRunner(block.Header(), statedb)) + } // Recompute transactions up to the target index. signer := types.MakeSigner(eth.blockchain.Config(), block.Number()) for idx, tx := range block.Transactions() { // Assemble the transaction call message and return if the requested offset - msg, _ := tx.AsMessage(signer, nil) + var baseFee *big.Int + if espresso { + baseFee = sysCtx.GetGasPriceMinimum(tx.FeeCurrency()) + } + msg, _ := tx.AsMessage(signer, baseFee) txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil) vmRunner := eth.blockchain.NewEVMRunner(block.Header(), statedb) @@ -172,7 +183,7 @@ func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec // Not yet the searched for transaction, execute on top of the current state vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{}) statedb.Prepare(tx.Hash(), idx) - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), vmRunner); err != nil { + if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), vmRunner, sysCtx); err != nil { return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) } // Ensure any modifications are committed to the state diff --git a/eth/tracers/api.go b/eth/tracers/api.go index edc0ce9ee5..f17f6b91b1 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -275,6 +275,11 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config for task := range tasks { signer := types.MakeSigner(api.backend.ChainConfig(), task.block.Number()) blockCtx := core.NewEVMBlockContext(task.block.Header(), api.chainContext(localctx), nil) + var sysCtx *core.SysContractCallCtx + if api.backend.ChainConfig().IsEHardfork(blockCtx.BlockNumber) { + sysVmRunner := api.backend.VmRunnerAtHeader(task.block.Header(), task.statedb) + sysCtx = core.NewSysContractCallCtx(sysVmRunner) + } // Trace all the transactions contained within for i, tx := range task.block.Transactions() { msg, _ := tx.AsMessage(signer, nil) @@ -284,7 +289,7 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config TxHash: tx.Hash(), } vmRunner := api.backend.VmRunnerAtHeader(task.block.Header(), task.statedb) - res, err := api.traceTx(localctx, msg, txctx, blockCtx, vmRunner, task.statedb, config) + res, err := api.traceTx(localctx, msg, txctx, blockCtx, vmRunner, task.statedb, sysCtx, config) if err != nil { task.results[i] = &txTraceResult{Error: err.Error()} log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err) @@ -524,6 +529,11 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac } blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) blockHash := block.Hash() + var sysCtx *core.SysContractCallCtx + if api.backend.ChainConfig().IsEHardfork(block.Number()) { + sysVmRunner := api.backend.VmRunnerAtHeader(block.Header(), statedb) + sysCtx = core.NewSysContractCallCtx(sysVmRunner) + } for th := 0; th < threads; th++ { pend.Add(1) go func() { @@ -537,7 +547,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac TxIndex: task.index, TxHash: txs[task.index].Hash(), } - res, err := api.traceTx(ctx, msg, txctx, blockCtx, vmRunner, task.statedb, config) + res, err := api.traceTx(ctx, msg, txctx, blockCtx, vmRunner, task.statedb, sysCtx, config) if err != nil { results[task.index] = &txTraceResult{Error: err.Error()} continue @@ -557,7 +567,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac statedb.Prepare(tx.Hash(), i) vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{}) vmRunner := api.backend.VmRunnerAtHeader(block.Header(), statedb) - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), vmRunner); err != nil { + if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), vmRunner, sysCtx); err != nil { failed = err break } @@ -635,6 +645,11 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block canon = false } } + var sysCtx *core.SysContractCallCtx + if api.backend.ChainConfig().IsEHardfork(block.Number()) { + sysVmRunner := api.backend.VmRunnerAtHeader(block.Header(), statedb) + sysCtx = core.NewSysContractCallCtx(sysVmRunner) + } for i, tx := range block.Transactions() { // Prepare the trasaction for un-traced execution var ( @@ -670,7 +685,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf) statedb.Prepare(tx.Hash(), i) vmRunner := api.backend.VmRunnerAtHeader(block.Header(), statedb) - _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), vmRunner) + _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), vmRunner, sysCtx) if writer != nil { writer.Flush() } @@ -723,6 +738,21 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config * if err != nil { return nil, err } + + var sysCtx *core.SysContractCallCtx + if api.backend.ChainConfig().IsEHardfork(block.Number()) { + parent, err := api.blockByNumber(ctx, rpc.BlockNumber(blockNumber-1)) + if err != nil { + return nil, err + } + sysStateDB, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true) + if err != nil { + return nil, err + } + sysVmRunner := api.backend.VmRunnerAtHeader(block.Header(), sysStateDB) + sysCtx = core.NewSysContractCallCtx(sysVmRunner) + } + msg, vmctx, vmRunner, statedb, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec) if err != nil { return nil, err @@ -732,7 +762,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config * TxIndex: int(index), TxHash: hash, } - return api.traceTx(ctx, msg, txctx, vmctx, vmRunner, statedb, config) + return api.traceTx(ctx, msg, txctx, vmctx, vmRunner, statedb, sysCtx, config) } // TraceCall lets you trace a given eth_call. It collects the structured logs @@ -777,7 +807,11 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc } vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) vmRunner := api.backend.VmRunnerAtHeader(block.Header(), statedb) - + var sysCtx *core.SysContractCallCtx + if api.backend.ChainConfig().IsEHardfork(block.Number()) { + sysVmRunner := api.backend.VmRunnerAtHeader(block.Header(), statedb) + sysCtx = core.NewSysContractCallCtx(sysVmRunner) + } var traceConfig *TraceConfig if config != nil { traceConfig = &TraceConfig{ @@ -787,13 +821,13 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc Reexec: config.Reexec, } } - return api.traceTx(ctx, msg, new(Context), vmctx, vmRunner, statedb, traceConfig) + return api.traceTx(ctx, msg, new(Context), vmctx, vmRunner, statedb, sysCtx, traceConfig) } // traceTx configures a new tracer according to the provided configuration, and // executes the given message in the provided environment. The return value will // be tracer dependent. -func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Context, vmctx vm.BlockContext, vmRunner vm.EVMRunner, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { +func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Context, vmctx vm.BlockContext, vmRunner vm.EVMRunner, statedb *state.StateDB, sysCtx *core.SysContractCallCtx, config *TraceConfig) (interface{}, error) { // Assemble the structured logger or the JavaScript tracer var ( tracer vm.Tracer @@ -835,7 +869,7 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex // Call Prepare to clear out the statedb access list statedb.Prepare(txctx.TxHash, txctx.TxIndex) - result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), vmRunner) + result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), vmRunner, sysCtx) if err != nil { return nil, fmt.Errorf("tracing failed: %w", err) } diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 6411a6992e..105911a649 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -169,7 +169,7 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block return msg, context, vmRunner, statedb, nil } vmenv := vm.NewEVM(context, txContext, statedb, b.chainConfig, vm.Config{}) - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), vmRunner); err != nil { + if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), vmRunner, nil); err != nil { return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) } statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 3021a451bf..aa5e4d3723 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -180,7 +180,7 @@ func TestPrestateTracerCreate2(t *testing.T) { t.Fatalf("failed to prepare transaction for tracing: %v", err) } - st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()), celoMock.Runner) + st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()), celoMock.Runner, nil) if _, err = st.TransitionDb(); err != nil { t.Fatalf("failed to execute transaction: %v", err) } @@ -368,7 +368,7 @@ func BenchmarkTransactionTrace(b *testing.B) { for i := 0; i < b.N; i++ { snap := statedb.Snapshot() - st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()), celoMock.Runner) + st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()), celoMock.Runner, nil) _, err = st.TransitionDb() if err != nil { b.Fatal(err) diff --git a/graphql/graphql_test.go b/graphql/graphql_test.go index 8537a87503..d14a2f6c39 100644 --- a/graphql/graphql_test.go +++ b/graphql/graphql_test.go @@ -176,7 +176,7 @@ func TestGraphQLBlockSerializationEIP2718(t *testing.T) { }{ { body: `{"query": "{block {number transactions { from { address } to { address } value hash type accessList { address storageKeys } index}}}"}`, - want: `{"data":{"block":{"number":1,"transactions":[{"from":{"address":"0x71562b71999873db5b286df957af199ec94617f7"},"to":{"address":"0x0000000000000000000000000000000000000dad"},"value":"0x64","hash":"0xa863609020c7651e840465da231bcfd1c853c295d62dae6551624f800c118e5a","type":0,"accessList":[],"index":0},{"from":{"address":"0x71562b71999873db5b286df957af199ec94617f7"},"to":{"address":"0x0000000000000000000000000000000000000dad"},"value":"0x32","hash":"0x0f32fec26e145116d7927ce74dfa64334682747459481246cde86e68d3091679","type":1,"accessList":[{"address":"0x0000000000000000000000000000000000000dad","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000000"]}],"index":1}]}}}`, + want: `{"data":{"block":{"number":1,"transactions":[{"from":{"address":"0x71562b71999873db5b286df957af199ec94617f7"},"to":{"address":"0x0000000000000000000000000000000000000dad"},"value":"0x64","hash":"0x46933b8a43e70320bb41910f015c4b2aded1caaba32b55b56054e4d1811d06d6","type":0,"accessList":[],"index":0},{"from":{"address":"0x71562b71999873db5b286df957af199ec94617f7"},"to":{"address":"0x0000000000000000000000000000000000000dad"},"value":"0x32","hash":"0x03682ed7cc9cf9e9fa3bb6f58a62d6a350c09ec4d22b71b884503ae469e8640b","type":1,"accessList":[{"address":"0x0000000000000000000000000000000000000dad","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000000"]}],"index":1}]}}}`, code: 200, }, } { @@ -309,19 +309,20 @@ func createGQLServiceWithTransactions(t *testing.T, stack *node.Node) { } signer := types.LatestSigner(ethConf.Genesis.Config) + gp := core.MockSysContractCallCtx().GetGasPriceMinimum(nil) legacyTx, _ := types.SignNewTx(key, signer, &types.LegacyTx{ Nonce: uint64(0), To: &dad, Value: big.NewInt(100), Gas: 50000, - GasPrice: new(big.Int), + GasPrice: gp, }) envelopTx, _ := types.SignNewTx(key, signer, &types.AccessListTx{ ChainID: ethConf.Genesis.Config.ChainID, Nonce: uint64(1), To: &dad, Gas: 30000, - GasPrice: new(big.Int), + GasPrice: gp, Value: big.NewInt(50), AccessList: types.AccessList{{ Address: dad, diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 2c2d386a87..7a6622b172 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -31,7 +31,6 @@ import ( "github.com/celo-org/celo-blockchain/common/hexutil" "github.com/celo-org/celo-blockchain/common/math" "github.com/celo-org/celo-blockchain/contracts/currency" - gpm "github.com/celo-org/celo-blockchain/contracts/gasprice_minimum" "github.com/celo-org/celo-blockchain/core" "github.com/celo-org/celo-blockchain/core/state" "github.com/celo-org/celo-blockchain/core/types" @@ -842,17 +841,18 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash // this makes sure resources are cleaned up. defer cancel() - vmRunner := b.NewEVMRunner(header, state) - if err != nil { - return nil, err + // Create SysContractCallCtx + var sysCtx *core.SysContractCallCtx + if b.ChainConfig().IsEHardfork(header.Number) { + vmRunner := b.NewEVMRunner(header, state) + if err != nil { + return nil, err + } + sysCtx = core.NewSysContractCallCtx(vmRunner) } - gasPriceMinimum, err := gpm.GetGasPriceMinimum(vmRunner, args.FeeCurrency) - if err != nil { - return nil, err - } // Get a new instance of the EVM. - msg, err := args.ToMessage(globalGasCap, gasPriceMinimum) // TODO check this baseFee -> gasPriceMinimum + msg, err := args.ToMessage(globalGasCap, common.Big0) // core.ApplyMessageWithoutGasPriceMinimum below will eventually set basefee to 0 if err != nil { return nil, err } @@ -869,8 +869,7 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash // Execute the message. gp := new(core.GasPool).AddGas(math.MaxUint64) - - result, err := core.ApplyMessageWithoutGasPriceMinimum(evm, msg, gp, b.NewEVMRunner(header, state)) + result, err := core.ApplyMessageWithoutGasPriceMinimum(evm, msg, gp, b.NewEVMRunner(header, state), sysCtx) if err := vmError(); err != nil { return nil, err } @@ -1388,7 +1387,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH return nil, 0, nil, err } vmRunner := b.NewEVMRunner(header, statedb) - res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), vmRunner) + res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), vmRunner, nil) if err != nil { return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err) } diff --git a/les/odr_test.go b/les/odr_test.go index d43ef592da..24d7b2cbfe 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -147,7 +147,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai vmenv := vm.NewEVM(context, txContext, statedb, config, vm.Config{NoBaseFee: true}) gp := new(core.GasPool).AddGas(math.MaxUint64) - result, _ := core.ApplyMessage(vmenv, msg, gp, celoMock.Runner) + result, _ := core.ApplyMessage(vmenv, msg, gp, celoMock.Runner, nil) res = append(res, result.Return()...) } } else { @@ -159,7 +159,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai txContext := core.NewEVMTxContext(msg) vmenv := vm.NewEVM(context, txContext, state, config, vm.Config{NoBaseFee: true}) gp := new(core.GasPool).AddGas(math.MaxUint64) - result, _ := core.ApplyMessage(vmenv, msg, gp, celoMock.Runner) + result, _ := core.ApplyMessage(vmenv, msg, gp, celoMock.Runner, nil) if state.Error() == nil { res = append(res, result.Return()...) } diff --git a/les/state_accessor.go b/les/state_accessor.go index c84074968f..d49c5d5550 100644 --- a/les/state_accessor.go +++ b/les/state_accessor.go @@ -25,6 +25,7 @@ import ( "github.com/celo-org/celo-blockchain/core/state" "github.com/celo-org/celo-blockchain/core/types" "github.com/celo-org/celo-blockchain/core/vm" + "github.com/celo-org/celo-blockchain/core/vm/vmcontext" "github.com/celo-org/celo-blockchain/light" ) @@ -51,11 +52,17 @@ func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types. if txIndex == 0 && len(block.Transactions()) == 0 { return nil, vm.BlockContext{}, nil, statedb, nil } + // Create SysContractCallCtx + var sysCtx *core.SysContractCallCtx + if leth.chainConfig.IsEHardfork(block.Number()) { + vmRunner := vmcontext.NewEVMRunner(leth.blockchain, block.Header(), statedb.Copy()) + sysCtx = core.NewSysContractCallCtx(vmRunner) + } // Recompute transactions up to the target index. signer := types.MakeSigner(leth.blockchain.Config(), block.Number()) for idx, tx := range block.Transactions() { // Assemble the transaction call message and return if the requested offset - msg, _ := tx.AsMessage(signer, nil) // TODO check this nil + msg, _ := tx.AsMessage(signer, sysCtx.GetGasPriceMinimum(tx.FeeCurrency())) txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(block.Header(), leth.blockchain, nil) statedb.Prepare(tx.Hash(), idx) @@ -65,7 +72,7 @@ func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types. } // Not yet the searched for transaction, execute on top of the current state vmenv := vm.NewEVM(context, txContext, statedb, leth.blockchain.Config(), vm.Config{}) - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), vmRunner); err != nil { + if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), vmRunner, sysCtx); err != nil { return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) } // Ensure any modifications are committed to the state diff --git a/light/odr_test.go b/light/odr_test.go index f44cfba942..0437bc2a16 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -250,7 +250,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain gp := new(core.GasPool).AddGas(math.MaxUint64) celoMock := testutil.NewCeloMock() - result, _ := core.ApplyMessage(vmenv, msg, gp, celoMock.Runner) + result, _ := core.ApplyMessage(vmenv, msg, gp, celoMock.Runner, nil) res = append(res, result.Return()...) if st.Error() != nil { return res, st.Error() diff --git a/miner/block.go b/miner/block.go index d813b6b3f2..69ef855341 100644 --- a/miner/block.go +++ b/miner/block.go @@ -44,6 +44,7 @@ type blockState struct { tcount int // tx count in cycle gasPool *core.GasPool // available gas used to pack transactions gasLimit uint64 + sysCtx *core.SysContractCallCtx header *types.Header txs []*types.Transaction @@ -105,6 +106,7 @@ func prepareBlock(w *worker) (*blockState, error) { gasLimit: blockchain_parameters.GetBlockGasLimitOrDefault(vmRunner), header: header, txFeeRecipient: txFeeRecipient, + sysCtx: core.NewSysContractCallCtx(w.chain.NewEVMRunner(header, state.Copy())), } b.gasPool = new(core.GasPool).AddGas(b.gasLimit) @@ -302,7 +304,7 @@ func (b *blockState) commitTransaction(w *worker, tx *types.Transaction, txFeeRe snap := b.state.Snapshot() vmRunner := w.chain.NewEVMRunner(b.header, b.state) - receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &txFeeRecipient, b.gasPool, b.state, b.header, tx, &b.header.GasUsed, *w.chain.GetVMConfig(), vmRunner) + receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &txFeeRecipient, b.gasPool, b.state, b.header, tx, &b.header.GasUsed, *w.chain.GetVMConfig(), vmRunner, b.sysCtx) if err != nil { b.state.RevertToSnapshot(snap) return nil, err diff --git a/tests/state_test_util.go b/tests/state_test_util.go index c6eb814b22..af06924706 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -222,7 +222,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh gaspool := new(core.GasPool) gaspool.AddGas(params.DefaultGasLimit) vmRunner := vmcontext.NewEVMRunner(nil, block.Header(), statedb) - if _, err := core.ApplyMessage(evm, msg, gaspool, vmRunner); err != nil { + if _, err := core.ApplyMessage(evm, msg, gaspool, vmRunner, nil); err != nil { statedb.RevertToSnapshot(snapshot) } diff --git a/tests/testdata b/tests/testdata index 5d534e37b8..7497b116a0 160000 --- a/tests/testdata +++ b/tests/testdata @@ -1 +1 @@ -Subproject commit 5d534e37b80e9310e8c7751f805ca481a451123e +Subproject commit 7497b116a019beb26215cbea4028df068dea06be