diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index d2f40d118c..ab98eb7256 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -530,7 +530,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM vmRunner := b.blockchain.NewEVMRunner(block.Header(), statedb) - return core.NewStateTransition(vmenv, msg, gaspool, vmRunner).TransitionDb() + return core.NewStateTransition(vmenv, msg, gaspool, vmRunner, core.NewBlockContext(vmRunner)).TransitionDb() } // SendTransaction updates the pending block to include the given transaction. diff --git a/cmd/geth/retesteth.go b/cmd/geth/retesteth.go index 28bb64f708..185c77ba65 100644 --- a/cmd/geth/retesteth.go +++ b/cmd/geth/retesteth.go @@ -503,6 +503,7 @@ func (api *RetestethAPI) mineBlock() error { &header.GasUsed, *api.blockchain.GetVMConfig(), vmRunner, + core.NewBlockContext(vmRunner), ) if err != nil { statedb.RevertToSnapshot(snap) @@ -659,7 +660,7 @@ func (api *RetestethAPI) AccountRange(ctx context.Context, // Not yet the searched for transaction, execute on top of the current state vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{}) vmRunner := api.blockchain.NewEVMRunner(block.Header(), statedb) - 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, core.NewBlockContext(vmRunner)); err != nil { return AccountRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) } // Ensure any modifications are committed to the state @@ -770,7 +771,7 @@ func (api *RetestethAPI) StorageRangeAt(ctx context.Context, // Not yet the searched for transaction, execute on top of the current state vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{}) vmRunner := api.blockchain.NewEVMRunner(block.Header(), statedb) - 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, core.NewBlockContext(vmRunner)); err != nil { return StorageRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) } // Ensure any modifications are committed to the state diff --git a/core/block_context.go b/core/block_context.go index 1f94587497..e3abf3c1ef 100644 --- a/core/block_context.go +++ b/core/block_context.go @@ -1,9 +1,12 @@ 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" + gpm "github.com/celo-org/celo-blockchain/contracts/gasprice_minimum" "github.com/celo-org/celo-blockchain/core/vm" ) @@ -12,14 +15,20 @@ import ( type BlockContext struct { whitelistedCurrencies map[common.Address]struct{} gasForAlternativeCurrency uint64 + + // gasPriceMinimums stores values for whitelist currency under their currency addresses + // Note that native currency(CELO) is under common.ZeroAddress + gasPriceMinimums map[common.Address]*big.Int } // 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 { +func NewBlockContext(vmRunner vm.EVMRunner) *BlockContext { + // gasForAlternativeCurrency gasForAlternativeCurrency := blockchain_parameters.GetIntrinsicGasForAlternativeFeeCurrencyOrDefault(vmRunner) + // whitelistedCurrencies whitelistedCurrenciesArr, err := currency.CurrencyWhitelist(vmRunner) if err != nil { whitelistedCurrenciesArr = []common.Address{} @@ -30,9 +39,20 @@ func NewBlockContext(vmRunner vm.EVMRunner) BlockContext { whitelistedCurrencies[currency] = struct{}{} } - return BlockContext{ + // gasPriceMinimums + gasPriceMinimums := make(map[common.Address]*big.Int) + + nativeTokenGpm, _ := gpm.GetGasPriceMinimum(vmRunner, nil) + gasPriceMinimums[common.ZeroAddress] = nativeTokenGpm + + for currency := range whitelistedCurrencies { + gasPriceMinimums[currency], _ = gpm.GetGasPriceMinimum(vmRunner, ¤cy) + } + + return &BlockContext{ whitelistedCurrencies: whitelistedCurrencies, gasForAlternativeCurrency: gasForAlternativeCurrency, + gasPriceMinimums: gasPriceMinimums, } } @@ -51,3 +71,24 @@ func (bc *BlockContext) IsWhitelisted(feeCurrency *common.Address) bool { _, ok := bc.whitelistedCurrencies[*feeCurrency] return ok } + +// GetGasPriceMinimum gets the gasPriceMinimum given the address +// - if the input address is nil or common.ZeroAddress, return value for native currency(CELO) +// - otherwise it returns value based on address +func (bc *BlockContext) GetGasPriceMinimum(address *common.Address) (gasPriceMinimum *big.Int) { + if address == nil || *address == common.ZeroAddress { + return bc.gasPriceMinimums[common.ZeroAddress] + } + return bc.gasPriceMinimums[*address] +} + +// WithZeroGasPriceMinimum returns a new BlockContext with GasPriceMinimums set to zero +// One use case is core.ApplyMessageWithoutGasPriceMinimum +func (bc *BlockContext) WithZeroGasPriceMinimum() *BlockContext{ + cpy := *bc + for address := range cpy.gasPriceMinimums { + bc.gasPriceMinimums[address] = common.Big0 + } + + return &cpy +} diff --git a/core/chain_makers.go b/core/chain_makers.go index 4ac2f14c73..a7e39f5f80 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -97,7 +97,7 @@ func (b *BlockGen) AddTxWithChain(bc ChainContext, tx *types.Transaction) { b.statedb.Prepare(tx.Hash(), common.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, NewBlockContext(celoMock.Runner)) if err != nil { panic(err) } diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index 8c333ebc1f..46b29fcb3d 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -91,6 +91,7 @@ func precacheTransaction(config *params.ChainConfig, bc *BlockChain, author *com ctx := NewEVMContext(msg, header, bc, author) vm := vm.NewEVM(ctx, statedb, config, cfg) - _, err = ApplyMessage(vm, msg, gaspool, bc.NewEVMRunner(header, statedb)) + evmRunner := bc.NewEVMRunner(header, statedb) + _, err = ApplyMessage(vm, msg, gaspool, evmRunner, NewBlockContext(evmRunner)) return err } diff --git a/core/state_processor.go b/core/state_processor.go index 03f76ba1ab..67362e56df 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -17,6 +17,8 @@ package core import ( + "fmt" + "github.com/celo-org/celo-blockchain/common" "github.com/celo-org/celo-blockchain/consensus" "github.com/celo-org/celo-blockchain/consensus/misc" @@ -81,10 +83,22 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // always true (EIP158) statedb.IntermediateRoot(true) } + + // After E hard fork, create BlockContext based on parent block + var blockContext *BlockContext + if p.bc.chainConfig.IsEHardfork(block.Number()) { + parentHeader := p.bc.GetHeaderByNumber(block.NumberU64() - 1) + parentState, err := p.bc.StateAt(parentHeader.Root) + if err != nil { + return nil, nil, 0, fmt.Errorf("failed to get the parent state: %w", err) + } + blockContext = NewBlockContext(p.bc.NewEVMRunner(parentHeader, parentState)) + } + // Iterate over and process the individual transactions for i, tx := range block.Transactions() { statedb.Prepare(tx.Hash(), block.Hash(), i) - receipt, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg, vmRunner) + receipt, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg, vmRunner, blockContext) if err != nil { return nil, nil, 0, err } @@ -104,7 +118,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // 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) { +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, blockContext *BlockContext) (*types.Receipt, error) { if config.IsDonut(header.Number) && !tx.Protected() { return nil, ErrUnprotectedTransaction } @@ -120,7 +134,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, txFeeRecipien vmenv := vm.NewEVM(ctx, statedb, config, cfg) // Apply the transaction to the current state (included in the env) - result, err := ApplyMessage(vmenv, msg, gp, vmRunner) + result, err := ApplyMessage(vmenv, msg, gp, vmRunner, blockContext) if err != nil { return nil, err } diff --git a/core/state_transition.go b/core/state_transition.go index a9a56cf99c..7c1d60ceb3 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -62,6 +62,7 @@ type StateTransition struct { evm *vm.EVM vmRunner vm.EVMRunner gasPriceMinimum *big.Int + blockContext *BlockContext } // Message represents a message sent to a contract. @@ -190,8 +191,13 @@ func IntrinsicGas(data []byte, contractCreation bool, feeCurrency *common.Addres } // 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, blockContext *BlockContext) *StateTransition { + // - Prior to E hard fork, it does a contract call to get gasPriceMinimum + // - After E hard fork, it uses BlockContext + var gasPriceMinimum *big.Int + if !evm.ChainConfig().IsEHardfork(evm.BlockNumber) { + gasPriceMinimum, _ = gpm.GetGasPriceMinimum(vmRunner, msg.FeeCurrency()) + } return &StateTransition{ gp: gp, @@ -203,6 +209,7 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool, vmRunner vm.EVMRu data: msg.Data(), state: evm.StateDB, gasPriceMinimum: gasPriceMinimum, + blockContext: blockContext, } } @@ -213,18 +220,21 @@ 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, blockContext *BlockContext) (*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, blockContext).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, blockContext *BlockContext) (*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) + if evm.ChainConfig().IsEHardfork(evm.BlockNumber) { + blockContext = blockContext.WithZeroGasPriceMinimum() + } + st := NewStateTransition(evm, msg, gp, vmRunner, blockContext) st.gasPriceMinimum = common.Big0 return st.TransitionDb() } @@ -361,8 +371,14 @@ 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) + var gpm *big.Int + if !st.evm.ChainConfig().IsEHardfork(st.evm.BlockNumber) { + gpm = st.gasPriceMinimum + } else { + gpm = st.blockContext.GetGasPriceMinimum(st.msg.FeeCurrency()) + } + if st.gasPrice.Cmp(gpm) < 0 { + log.Debug("Tx gas price is less than minimum", "minimum", gpm, "price", st.gasPrice) return ErrGasPriceDoesNotExceedMinimum } @@ -417,7 +433,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 !st.evm.ChainConfig().IsEHardfork(st.evm.BlockNumber) { + gasForAlternativeCurrency = blockchain_parameters.GetIntrinsicGasForAlternativeFeeCurrencyOrDefault(st.vmRunner) + } else { + gasForAlternativeCurrency = st.blockContext.GetIntrinsicGasForAlternativeFeeCurrency() + } } gas, err := IntrinsicGas(st.data, contractCreation, msg.FeeCurrency(), gasForAlternativeCurrency, istanbul) if err != nil { @@ -481,9 +501,15 @@ func (st *StateTransition) distributeTxFees() error { from := st.msg.From() // Divide the transaction into a base (the minimum transaction fee) and tip (any extra). - baseTxFee := new(big.Int).Mul(gasUsed, st.gasPriceMinimum) - tipTxFee := new(big.Int).Sub(totalTxFee, baseTxFee) feeCurrency := st.msg.FeeCurrency() + var gpm *big.Int + if !st.evm.ChainConfig().IsEHardfork(st.evm.BlockNumber) { + gpm = st.gasPriceMinimum + } else { + gpm = st.blockContext.GetGasPriceMinimum(feeCurrency) + } + baseTxFee := new(big.Int).Mul(gasUsed, gpm) + tipTxFee := new(big.Int).Sub(totalTxFee, baseTxFee) gatewayFeeRecipient := st.msg.GatewayFeeRecipient() if gatewayFeeRecipient == nil { @@ -502,7 +528,7 @@ func (st *StateTransition) distributeTxFees() error { baseTxFee = new(big.Int) } - log.Trace("distributeTxFees", "from", from, "refund", refund, "feeCurrency", st.msg.FeeCurrency(), + log.Trace("distributeTxFees", "from", from, "refund", refund, "feeCurrency", feeCurrency, "gatewayFeeRecipient", *gatewayFeeRecipient, "gatewayFee", st.msg.GatewayFee(), "coinbaseFeeRecipient", st.evm.Coinbase, "coinbaseFee", tipTxFee, "comunityFundRecipient", governanceAddress, "communityFundFee", baseTxFee) diff --git a/core/tx_pool.go b/core/tx_pool.go index e4cd3b3fe1..b213f4dae9 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -238,7 +238,7 @@ func (config *TxPoolConfig) sanitize() TxPoolConfig { } type txPoolContext struct { - BlockContext + *BlockContext *currency.CurrencyManager } diff --git a/eth/api_tracer.go b/eth/api_tracer.go index ef9cd1787e..be14443f37 100644 --- a/eth/api_tracer.go +++ b/eth/api_tracer.go @@ -35,12 +35,14 @@ 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/eth/tracers" "github.com/celo-org/celo-blockchain/internal/ethapi" "github.com/celo-org/celo-blockchain/log" "github.com/celo-org/celo-blockchain/rlp" "github.com/celo-org/celo-blockchain/rpc" "github.com/celo-org/celo-blockchain/trie" + "github.com/docker/docker/daemon/config" ) const ( @@ -204,12 +206,13 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl signer := types.MakeSigner(api.eth.blockchain.Config(), task.block.Number()) // Trace all the transactions contained within + blockCtx := core.NewBlockContext(api.eth.blockchain.NewEVMRunner(task.block.Header(), task.statedb)) for i, tx := range task.block.Transactions() { msg, _ := tx.AsMessage(signer) vmctx := core.NewEVMContext(msg, task.block.Header(), api.eth.blockchain, nil) vmRunner := api.eth.blockchain.NewEVMRunner(task.block.Header(), statedb) - res, err := api.traceTx(ctx, msg, vmctx, vmRunner, task.statedb, config) + res, err := api.traceTx(ctx, msg, vmctx, vmRunner, task.statedb, blockCtx, config) if err != nil { task.results[i] = &txTraceResult{Error: err.Error()} log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err) @@ -462,6 +465,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, // Execute all the transaction contained within the block concurrently var ( signer = types.MakeSigner(api.eth.blockchain.Config(), block.Number()) + blockCtx = core.NewBlockContext(api.eth.blockchain.NewEVMRunner(block.Header(), statedb)) txs = block.Transactions() results = make([]*txTraceResult, len(txs)) @@ -484,7 +488,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil) vmRunner := api.eth.blockchain.NewEVMRunner(block.Header(), statedb) - res, err := api.traceTx(ctx, msg, vmctx, vmRunner, task.statedb, config) + res, err := api.traceTx(ctx, msg, vmctx, vmRunner, task.statedb, blockCtx, config) if err != nil { results[task.index] = &txTraceResult{Error: err.Error()} continue @@ -505,7 +509,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, vmenv := vm.NewEVM(vmctx, statedb, api.eth.blockchain.Config(), vm.Config{}) vmRunner := api.eth.blockchain.NewEVMRunner(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, core.NewBlockContext(vmRunner)); err != nil { failed = err break } @@ -526,7 +530,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, // standardTraceBlockToFile configures a new tracer which uses standard JSON output, // and traces either a full block or an individual transaction. The return value will // be one filename per transaction traced. -func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block *types.Block, config *StdTraceConfig) ([]string, error) { +func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block *types.Block, blockCtx *core.BlockContext, config *StdTraceConfig) ([]string, error) { // If we're tracing a single transaction, make sure it's present if config != nil && config.TxHash != (common.Hash{}) { if !containsTx(block, config.TxHash) { @@ -600,7 +604,7 @@ func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block // Execute the transaction and flush any traces to disk vmenv := vm.NewEVM(vmctx, statedb, api.eth.blockchain.Config(), vmConf) vmRunner := api.eth.blockchain.NewEVMRunner(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, blockCtx) if writer != nil { writer.Flush() } @@ -726,7 +730,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Ha // 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 *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, vmctx vm.Context, vmRunner vm.EVMRunner, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { +func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, vmctx vm.Context, vmRunner vm.EVMRunner, statedb *state.StateDB, blockCtx *core.BlockContext, config *TraceConfig) (interface{}, error) { // Assemble the structured logger or the JavaScript tracer var ( tracer vm.Tracer @@ -761,7 +765,7 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, v } // Run the transaction with tracing enabled. vmenv := vm.NewEVM(vmctx, statedb, api.eth.blockchain.Config(), vm.Config{Debug: true, Tracer: tracer}) - 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, blockCtx) if err != nil { return nil, fmt.Errorf("tracing failed: %v", err) } @@ -789,23 +793,23 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, v } // computeTxEnv returns the execution environment of a certain transaction. -func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, reexec uint64) (core.Message, vm.Context, vm.EVMRunner, *state.StateDB, error) { +func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, reexec uint64) (core.Message, vm.Context, vm.EVMRunner, *state.StateDB, *core.BlockContext, error) { // Create the parent state database block := api.eth.blockchain.GetBlockByHash(blockHash) if block == nil { - return nil, vm.Context{}, nil, nil, fmt.Errorf("block %#x not found", blockHash) + return nil, vm.Context{}, nil, nil, nil, fmt.Errorf("block %#x not found", blockHash) } parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) if parent == nil { - return nil, vm.Context{}, nil, nil, fmt.Errorf("parent %#x not found", block.ParentHash()) + return nil, vm.Context{}, nil, nil, nil, fmt.Errorf("parent %#x not found", block.ParentHash()) } statedb, err := api.computeStateDB(parent, reexec) if err != nil { - return nil, vm.Context{}, nil, nil, err + return nil, vm.Context{}, nil, nil, nil, err } if txIndex == 0 && len(block.Transactions()) == 0 { - return nil, vm.Context{}, nil, statedb, nil + return nil, vm.Context{}, nil, statedb, nil, nil } // Recompute transactions up to the target index. @@ -818,15 +822,15 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree vmenv := vm.NewEVM(context, statedb, api.eth.blockchain.Config(), vm.Config{}) vmRunner := api.eth.blockchain.NewEVMRunner(block.Header(), statedb) if idx == txIndex { - return msg, context, vmRunner, statedb, nil + return msg, context, vmRunner, statedb, nil, nil } // Not yet the searched for transaction, execute on top of the current state - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), vmRunner); err != nil { - return nil, vm.Context{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) + if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), vmRunner, core.NewBlockContext(); err != nil { + return nil, vm.Context{}, nil, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) } // Ensure any modifications are committed to the state // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) } - return nil, vm.Context{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, blockHash) + return nil, vm.Context{}, nil, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, blockHash) } diff --git a/miner/block.go b/miner/block.go index 1b253b63be..6b3d1e1c7b 100644 --- a/miner/block.go +++ b/miner/block.go @@ -38,7 +38,8 @@ import ( // blockState is the collection of modified state that is used to assemble a block type blockState struct { - signer types.Signer + signer types.Signer + blockContext *core.BlockContext state *state.StateDB // apply state changes here tcount int // tx count in cycle @@ -52,7 +53,7 @@ type blockState struct { txFeeRecipient common.Address } -// prepareBlock intializes a new blockState that is ready to have transaction included to. +// prepareBlock initializes a new blockState that is ready to have transaction included to. func prepareBlock(w *worker) (*blockState, error) { w.mu.RLock() defer w.mu.RUnlock() @@ -98,8 +99,10 @@ func prepareBlock(w *worker) (*blockState, error) { } vmRunner := w.chain.NewEVMRunner(header, state) + blockContext := core.NewBlockContext(vmRunner) b := &blockState{ signer: types.NewEIP155Signer(w.chainConfig.ChainID), + blockContext: blockContext, state: state, tcount: 0, gasLimit: blockchain_parameters.GetBlockGasLimitOrDefault(vmRunner), @@ -301,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.blockContext) if err != nil { b.state.RevertToSnapshot(snap) return nil, err