diff --git a/core/state_transition.go b/core/state_transition.go index 60ac7d8cb5..c8160424b9 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -104,8 +104,9 @@ func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int { return igas } -func ApplyMessage(env vm.Environment, msg Message, gp *GasPool) ([]byte, *big.Int, error) { - var st = StateTransition{ +// NewStateTransition initialises and returns a new state transition object. +func NewStateTransition(env vm.Environment, msg Message, gp *GasPool) *StateTransition { + return &StateTransition{ gp: gp, env: env, msg: msg, @@ -116,7 +117,20 @@ func ApplyMessage(env vm.Environment, msg Message, gp *GasPool) ([]byte, *big.In data: msg.Data(), state: env.Db(), } - return st.transitionDb() +} + +// ApplyMessage computes the new state by applying the given message +// against the old state within the environment. +// +// ApplyMessage returns the bytes returned by any EVM execution (if it took place), +// 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(env vm.Environment, msg Message, gp *GasPool) ([]byte, *big.Int, error) { + st := NewStateTransition(env, msg, gp) + + ret, _, gasUsed, err := st.TransitionDb() + return ret, gasUsed, err } func (self *StateTransition) from() (vm.Account, error) { @@ -209,7 +223,8 @@ func (self *StateTransition) preCheck() (err error) { return nil } -func (self *StateTransition) transitionDb() (ret []byte, usedGas *big.Int, err error) { +// TransitionDb will move the state by applying the message against the given environment. +func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big.Int, err error) { if err = self.preCheck(); err != nil { return } @@ -220,7 +235,7 @@ func (self *StateTransition) transitionDb() (ret []byte, usedGas *big.Int, err e contractCreation := MessageCreatesContract(msg) // Pay intrinsic gas if err = self.useGas(IntrinsicGas(self.data, contractCreation, homestead)); err != nil { - return nil, nil, InvalidTxError(err) + return nil, nil, nil, InvalidTxError(err) } vmenv := self.env @@ -245,7 +260,7 @@ func (self *StateTransition) transitionDb() (ret []byte, usedGas *big.Int, err e } if err != nil && IsValueTransferErr(err) { - return nil, nil, InvalidTxError(err) + return nil, nil, nil, InvalidTxError(err) } // We aren't interested in errors here. Errors returned by the VM are non-consensus errors and therefor shouldn't bubble up @@ -253,10 +268,12 @@ func (self *StateTransition) transitionDb() (ret []byte, usedGas *big.Int, err e err = nil } + requiredGas = new(big.Int).Set(self.gasUsed()) + self.refundGas() self.state.AddBalance(self.env.Coinbase(), new(big.Int).Mul(self.gasUsed(), self.gasPrice)) - return ret, self.gasUsed(), err + return ret, requiredGas, self.gasUsed(), err } func (self *StateTransition) refundGas() { diff --git a/eth/api.go b/eth/api.go index 06c10daa91..af03c096dc 100644 --- a/eth/api.go +++ b/eth/api.go @@ -674,11 +674,11 @@ func (s *PublicBlockChainAPI) doCall(args CallArgs, blockNr rpc.BlockNumber) (st vmenv := core.NewEnv(stateDb, s.config, s.bc, msg, block.Header(), s.config.VmConfig) gp := new(core.GasPool).AddGas(common.MaxBig) - res, gas, err := core.ApplyMessage(vmenv, msg, gp) + res, requiredGas, _, err := core.NewStateTransition(vmenv, msg, gp).TransitionDb() if len(res) == 0 { // backwards compatibility - return "0x", gas, err + return "0x", requiredGas, err } - return common.ToHex(res), gas, err + return common.ToHex(res), requiredGas, err } // Call executes the given transaction on the state for the given block number.