This commit is contained in:
gary rong 2017-06-16 17:13:54 +00:00 committed by GitHub
commit 1e9a6cfefd
19 changed files with 227 additions and 57 deletions

View file

@ -253,7 +253,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
// about the transaction and calling mechanisms. // about the transaction and calling mechanisms.
vmenv := vm.NewEVM(evmContext, statedb, b.config, vm.Config{}) vmenv := vm.NewEVM(evmContext, statedb, b.config, vm.Config{})
gaspool := new(core.GasPool).AddGas(math.MaxBig256) gaspool := new(core.GasPool).AddGas(math.MaxBig256)
ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb() ret, gasUsed, _, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
return ret, gasUsed, err return ret, gasUsed, err
} }

View file

@ -174,12 +174,12 @@ func runCmd(ctx *cli.Context) error {
var leftOverGas uint64 var leftOverGas uint64
if ctx.GlobalBool(CreateFlag.Name) { if ctx.GlobalBool(CreateFlag.Name) {
input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...) input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...)
ret, _, leftOverGas, err = runtime.Create(input, &runtimeConfig) ret, _, leftOverGas, _, err = runtime.Create(input, &runtimeConfig)
} else { } else {
receiver := common.StringToAddress("receiver") receiver := common.StringToAddress("receiver")
statedb.SetCode(receiver, code) statedb.SetCode(receiver, code)
ret, leftOverGas, err = runtime.Call(receiver, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtimeConfig) ret, leftOverGas, _, err = runtime.Call(receiver, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtimeConfig)
} }
execTime := time.Since(tstart) execTime := time.Since(tstart)

View file

@ -98,7 +98,7 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common
// about the transaction and calling mechanisms. // about the transaction and calling mechanisms.
vmenv := vm.NewEVM(context, statedb, config, cfg) vmenv := vm.NewEVM(context, statedb, config, cfg)
// Apply the transaction to the current state (included in the env) // Apply the transaction to the current state (included in the env)
_, gas, err := ApplyMessage(vmenv, msg, gp) _, gas, reverted, err := ApplyMessage(vmenv, msg, gp)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -111,6 +111,7 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common
receipt := types.NewReceipt(root.Bytes(), usedGas) receipt := types.NewReceipt(root.Bytes(), usedGas)
receipt.TxHash = tx.Hash() receipt.TxHash = tx.Hash()
receipt.GasUsed = new(big.Int).Set(gas) receipt.GasUsed = new(big.Int).Set(gas)
receipt.Reverted = reverted
// if the transaction created a contract, store the creation address in the receipt. // if the transaction created a contract, store the creation address in the receipt.
if msg.To() == nil { if msg.To() == nil {
receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce()) receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())

View file

@ -127,11 +127,11 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition
// the gas used (which includes gas refunds) and an error if it failed. An error always // 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 // indicates a core error meaning that the message would always fail for that particular
// state and would never be accepted within a block. // state and would never be accepted within a block.
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, error) { func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, bool, error) {
st := NewStateTransition(evm, msg, gp) st := NewStateTransition(evm, msg, gp)
ret, _, gasUsed, err := st.TransitionDb() ret, _, gasUsed, reverted, err := st.TransitionDb()
return ret, gasUsed, err return ret, gasUsed, reverted, err
} }
func (st *StateTransition) from() vm.AccountRef { func (st *StateTransition) from() vm.AccountRef {
@ -208,7 +208,7 @@ func (st *StateTransition) preCheck() error {
// TransitionDb will transition the state by applying the current message and returning the result // TransitionDb will transition the state by applying the current message and returning the result
// including the required gas for the operation as well as the used gas. It returns an error if it // including the required gas for the operation as well as the used gas. It returns an error if it
// failed. An error indicates a consensus issue. // failed. An error indicates a consensus issue.
func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big.Int, err error) { func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big.Int, reverted bool, err error) {
if err = st.preCheck(); err != nil { if err = st.preCheck(); err != nil {
return return
} }
@ -222,10 +222,10 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big
// TODO convert to uint64 // TODO convert to uint64
intrinsicGas := IntrinsicGas(st.data, contractCreation, homestead) intrinsicGas := IntrinsicGas(st.data, contractCreation, homestead)
if intrinsicGas.BitLen() > 64 { if intrinsicGas.BitLen() > 64 {
return nil, nil, nil, vm.ErrOutOfGas return nil, nil, nil, false, vm.ErrOutOfGas
} }
if err = st.useGas(intrinsicGas.Uint64()); err != nil { if err = st.useGas(intrinsicGas.Uint64()); err != nil {
return nil, nil, nil, err return nil, nil, nil, false, err
} }
var ( var (
@ -236,11 +236,11 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big
vmerr error vmerr error
) )
if contractCreation { if contractCreation {
ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value) ret, _, st.gas, reverted, vmerr = evm.Create(sender, st.data, st.gas, st.value)
} else { } else {
// Increment the nonce for the next transaction // Increment the nonce for the next transaction
st.state.SetNonce(sender.Address(), st.state.GetNonce(sender.Address())+1) st.state.SetNonce(sender.Address(), st.state.GetNonce(sender.Address())+1)
ret, st.gas, vmerr = evm.Call(sender, st.to().Address(), st.data, st.gas, st.value) ret, st.gas, reverted, vmerr = evm.Call(sender, st.to().Address(), st.data, st.gas, st.value)
} }
if vmerr != nil { if vmerr != nil {
log.Debug("VM returned with error", "err", vmerr) log.Debug("VM returned with error", "err", vmerr)
@ -248,7 +248,7 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big
// sufficient balance to make the transfer happen. The first // sufficient balance to make the transfer happen. The first
// balance transfer may never fail. // balance transfer may never fail.
if vmerr == vm.ErrInsufficientBalance { if vmerr == vm.ErrInsufficientBalance {
return nil, nil, nil, vmerr return nil, nil, nil, false, vmerr
} }
} }
requiredGas = new(big.Int).Set(st.gasUsed()) requiredGas = new(big.Int).Set(st.gasUsed())
@ -256,7 +256,7 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big
st.refundGas() st.refundGas()
st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(st.gasUsed(), st.gasPrice)) st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(st.gasUsed(), st.gasPrice))
return ret, requiredGas, st.gasUsed(), err return ret, requiredGas, st.gasUsed(), reverted, err
} }
func (st *StateTransition) refundGas() { func (st *StateTransition) refundGas() {

View file

@ -20,6 +20,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
TxHash common.Hash `json:"transactionHash" gencodec:"required"` TxHash common.Hash `json:"transactionHash" gencodec:"required"`
ContractAddress common.Address `json:"contractAddress"` ContractAddress common.Address `json:"contractAddress"`
GasUsed *hexutil.Big `json:"gasUsed" gencodec:"required"` GasUsed *hexutil.Big `json:"gasUsed" gencodec:"required"`
Reverted bool `json:"reverted" gencode:"required"`
} }
var enc Receipt var enc Receipt
enc.PostState = r.PostState enc.PostState = r.PostState
@ -29,6 +30,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
enc.TxHash = r.TxHash enc.TxHash = r.TxHash
enc.ContractAddress = r.ContractAddress enc.ContractAddress = r.ContractAddress
enc.GasUsed = (*hexutil.Big)(r.GasUsed) enc.GasUsed = (*hexutil.Big)(r.GasUsed)
enc.Reverted = r.Reverted
return json.Marshal(&enc) return json.Marshal(&enc)
} }
@ -41,6 +43,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
TxHash *common.Hash `json:"transactionHash" gencodec:"required"` TxHash *common.Hash `json:"transactionHash" gencodec:"required"`
ContractAddress *common.Address `json:"contractAddress"` ContractAddress *common.Address `json:"contractAddress"`
GasUsed *hexutil.Big `json:"gasUsed" gencodec:"required"` GasUsed *hexutil.Big `json:"gasUsed" gencodec:"required"`
Reverted *bool `json:"reverted" gencode:"required"`
} }
var dec Receipt var dec Receipt
if err := json.Unmarshal(input, &dec); err != nil { if err := json.Unmarshal(input, &dec); err != nil {
@ -73,5 +76,8 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
return errors.New("missing required field 'gasUsed' for Receipt") return errors.New("missing required field 'gasUsed' for Receipt")
} }
r.GasUsed = (*big.Int)(dec.GasUsed) r.GasUsed = (*big.Int)(dec.GasUsed)
if dec.Reverted != nil {
r.Reverted = *dec.Reverted
}
return nil return nil
} }

View file

@ -40,6 +40,10 @@ type Receipt struct {
TxHash common.Hash `json:"transactionHash" gencodec:"required"` TxHash common.Hash `json:"transactionHash" gencodec:"required"`
ContractAddress common.Address `json:"contractAddress"` ContractAddress common.Address `json:"contractAddress"`
GasUsed *big.Int `json:"gasUsed" gencodec:"required"` GasUsed *big.Int `json:"gasUsed" gencodec:"required"`
// The Reverted field is true if transaction is reverted during state transition,
// As the transaction revert will cause all the remaining gas to be consumed,
// This field could help to gas estimation.
Reverted bool `json:"reverted" gencode:"required"`
} }
type receiptMarshaling struct { type receiptMarshaling struct {
@ -91,7 +95,7 @@ func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
for i, log := range r.Logs { for i, log := range r.Logs {
logs[i] = (*LogForStorage)(log) logs[i] = (*LogForStorage)(log)
} }
return rlp.Encode(w, []interface{}{r.PostState, r.CumulativeGasUsed, r.Bloom, r.TxHash, r.ContractAddress, logs, r.GasUsed}) return rlp.Encode(w, []interface{}{r.PostState, r.CumulativeGasUsed, r.Bloom, r.TxHash, r.ContractAddress, logs, r.GasUsed, r.Reverted})
} }
// DecodeRLP implements rlp.Decoder, and loads both consensus and implementation // DecodeRLP implements rlp.Decoder, and loads both consensus and implementation
@ -105,6 +109,7 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
ContractAddress common.Address ContractAddress common.Address
Logs []*LogForStorage Logs []*LogForStorage
GasUsed *big.Int GasUsed *big.Int
Reverted bool
} }
if err := s.Decode(&receipt); err != nil { if err := s.Decode(&receipt); err != nil {
return err return err
@ -116,7 +121,7 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
r.Logs[i] = (*Log)(log) r.Logs[i] = (*Log)(log)
} }
// Assign the implementation fields // Assign the implementation fields
r.TxHash, r.ContractAddress, r.GasUsed = receipt.TxHash, receipt.ContractAddress, receipt.GasUsed r.TxHash, r.ContractAddress, r.GasUsed, r.Reverted = receipt.TxHash, receipt.ContractAddress, receipt.GasUsed, receipt.Reverted
return nil return nil
} }

View file

@ -62,6 +62,8 @@ type Contract struct {
Args []byte Args []byte
DelegateCall bool DelegateCall bool
Reverted bool // use to represent whether transaction is reverted during state transition
// child's reverted field changed will also affect parent.
} }
// NewContract returns a new contract environment for the execution of EVM. // NewContract returns a new contract environment for the execution of EVM.
@ -151,3 +153,12 @@ func (self *Contract) SetCallCode(addr *common.Address, hash common.Hash, code [
self.CodeHash = hash self.CodeHash = hash
self.CodeAddr = addr self.CodeAddr = addr
} }
// MarkReverted mark current transaction's execution has meet revert.
func (self *Contract) MarkReverted() {
self.Reverted = true
// affect parent recursively
if parent, ok := self.caller.(*Contract); ok {
parent.MarkReverted()
}
}

View file

@ -124,18 +124,18 @@ func (evm *EVM) Cancel() {
// Call executes the contract associated with the addr with the given input as parameters. It also handles any // Call executes the contract associated with the addr with the given input as parameters. It also handles any
// necessary value transfer required and takes the necessary steps to create accounts and reverses the state in // necessary value transfer required and takes the necessary steps to create accounts and reverses the state in
// case of an execution error or failed value transfer. // case of an execution error or failed value transfer.
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, reverted bool, err error) {
if evm.vmConfig.NoRecursion && evm.depth > 0 { if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, gas, nil return nil, gas, false, nil
} }
// Depth check execution. Fail if we're trying to execute above the // Depth check execution. Fail if we're trying to execute above the
// limit. // limit.
if evm.depth > int(params.CallCreateDepth) { if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth return nil, gas, false, ErrDepth
} }
if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, gas, ErrInsufficientBalance return nil, gas, false, ErrInsufficientBalance
} }
var ( var (
@ -144,7 +144,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
) )
if !evm.StateDB.Exist(addr) { if !evm.StateDB.Exist(addr) {
if PrecompiledContracts[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 { if PrecompiledContracts[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 {
return nil, gas, nil return nil, gas, false, nil
} }
evm.StateDB.CreateAccount(addr) evm.StateDB.CreateAccount(addr)
@ -163,9 +163,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
// when we're in homestead this also counts for code storage gas errors. // when we're in homestead this also counts for code storage gas errors.
if err != nil { if err != nil {
contract.UseGas(contract.Gas) contract.UseGas(contract.Gas)
evm.StateDB.RevertToSnapshot(snapshot) evm.Revert(snapshot, contract)
} }
return ret, contract.Gas, err return ret, contract.Gas, contract.Reverted, err
} }
// CallCode executes the contract associated with the addr with the given input as parameters. It also handles any // CallCode executes the contract associated with the addr with the given input as parameters. It also handles any
@ -200,7 +200,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
ret, err = run(evm, snapshot, contract, input) ret, err = run(evm, snapshot, contract, input)
if err != nil { if err != nil {
contract.UseGas(contract.Gas) contract.UseGas(contract.Gas)
evm.StateDB.RevertToSnapshot(snapshot) evm.Revert(snapshot, contract)
} }
return ret, contract.Gas, err return ret, contract.Gas, err
@ -234,25 +234,25 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
ret, err = run(evm, snapshot, contract, input) ret, err = run(evm, snapshot, contract, input)
if err != nil { if err != nil {
contract.UseGas(contract.Gas) contract.UseGas(contract.Gas)
evm.StateDB.RevertToSnapshot(snapshot) evm.Revert(snapshot, contract)
} }
return ret, contract.Gas, err return ret, contract.Gas, err
} }
// Create creates a new contract using code as deployment code. // Create creates a new contract using code as deployment code.
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, reverted bool, err error) {
if evm.vmConfig.NoRecursion && evm.depth > 0 { if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, common.Address{}, gas, nil return nil, common.Address{}, gas, false, nil
} }
// Depth check execution. Fail if we're trying to execute above the // Depth check execution. Fail if we're trying to execute above the
// limit. // limit.
if evm.depth > int(params.CallCreateDepth) { if evm.depth > int(params.CallCreateDepth) {
return nil, common.Address{}, gas, ErrDepth return nil, common.Address{}, gas, false, ErrDepth
} }
if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, common.Address{}, gas, ErrInsufficientBalance return nil, common.Address{}, gas, false, ErrInsufficientBalance
} }
// Create a new account on the state // Create a new account on the state
@ -295,7 +295,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
if maxCodeSizeExceeded || if maxCodeSizeExceeded ||
(err != nil && (evm.ChainConfig().IsHomestead(evm.BlockNumber) || err != ErrCodeStoreOutOfGas)) { (err != nil && (evm.ChainConfig().IsHomestead(evm.BlockNumber) || err != ErrCodeStoreOutOfGas)) {
contract.UseGas(contract.Gas) contract.UseGas(contract.Gas)
evm.StateDB.RevertToSnapshot(snapshot) evm.Revert(snapshot, contract)
} }
// If the vm returned with an error the return value should be set to nil. // If the vm returned with an error the return value should be set to nil.
// This isn't consensus critical but merely to for behaviour reasons such as // This isn't consensus critical but merely to for behaviour reasons such as
@ -304,7 +304,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
ret = nil ret = nil
} }
return ret, contractAddr, contract.Gas, err return ret, contractAddr, contract.Gas, contract.Reverted, err
} }
// ChainConfig returns the evmironment's chain configuration // ChainConfig returns the evmironment's chain configuration
@ -312,3 +312,10 @@ func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
// Interpreter returns the EVM interpreter // Interpreter returns the EVM interpreter
func (evm *EVM) Interpreter() *Interpreter { return evm.interpreter } func (evm *EVM) Interpreter() *Interpreter { return evm.interpreter }
// Revert revert all state changes made since the given revision.
// set `reverted` flag recursively.
func (evm *EVM) Revert(snapshot int, contract *Contract) {
evm.StateDB.RevertToSnapshot(snapshot)
contract.MarkReverted()
}

View file

@ -551,7 +551,8 @@ func opCreate(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S
} }
contract.UseGas(gas) contract.UseGas(gas)
_, addr, returnGas, suberr := evm.Create(contract, input, gas, value) // ignore reverted here since it is only useful in vm entry.
_, addr, returnGas, _, suberr := evm.Create(contract, input, gas, value)
// Push item on the stack based on the returned error. If the ruleset is // Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only // homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must // rule) and treat as an error, if the ruleset is frontier we must
@ -588,8 +589,8 @@ func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta
if value.Sign() != 0 { if value.Sign() != 0 {
gas += params.CallStipend gas += params.CallStipend
} }
// ignore revert here, same with opCreate.
ret, returnGas, err := evm.Call(contract, address, args, gas, value) ret, returnGas, _, err := evm.Call(contract, address, args, gas, value)
if err != nil { if err != nil {
stack.push(new(big.Int)) stack.push(new(big.Int))
} else { } else {

View file

@ -23,7 +23,7 @@ package runtime
// This returns 1 for valid parsable/runable code, 0 // This returns 1 for valid parsable/runable code, 0
// for invalid opcode. // for invalid opcode.
func Fuzz(input []byte) int { func Fuzz(input []byte) int {
_, _, err := Execute(input, input, &Config{ _, _, _, err := Execute(input, input, &Config{
GasLimit: 3000000, GasLimit: 3000000,
}) })

View file

@ -94,7 +94,7 @@ func setDefaults(cfg *Config) {
// Executes sets up a in memory, temporarily, environment for the execution of // Executes sets up a in memory, temporarily, environment for the execution of
// the given code. It enabled the JIT by default and make sure that it's restored // the given code. It enabled the JIT by default and make sure that it's restored
// to it's original state afterwards. // to it's original state afterwards.
func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, bool, error) {
if cfg == nil { if cfg == nil {
cfg = new(Config) cfg = new(Config)
} }
@ -113,7 +113,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
// set the receiver's (the executing contract) code for execution. // set the receiver's (the executing contract) code for execution.
cfg.State.SetCode(address, code) cfg.State.SetCode(address, code)
// Call the code with the given configuration. // Call the code with the given configuration.
ret, _, err := vmenv.Call( ret, _, reverted, err := vmenv.Call(
sender, sender,
common.StringToAddress("contract"), common.StringToAddress("contract"),
input, input,
@ -121,11 +121,11 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
cfg.Value, cfg.Value,
) )
return ret, cfg.State, err return ret, cfg.State, reverted, err
} }
// Create executes the code using the EVM create method // Create executes the code using the EVM create method
func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, bool, error) {
if cfg == nil { if cfg == nil {
cfg = new(Config) cfg = new(Config)
} }
@ -141,13 +141,13 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
) )
// Call the code with the given configuration. // Call the code with the given configuration.
code, address, leftOverGas, err := vmenv.Create( code, address, leftOverGas, reverted, err := vmenv.Create(
sender, sender,
input, input,
cfg.GasLimit, cfg.GasLimit,
cfg.Value, cfg.Value,
) )
return code, address, leftOverGas, err return code, address, leftOverGas, reverted, err
} }
// Call executes the code given by the contract's address. It will return the // Call executes the code given by the contract's address. It will return the
@ -155,14 +155,14 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
// //
// Call, unlike Execute, requires a config and also requires the State field to // Call, unlike Execute, requires a config and also requires the State field to
// be set. // be set.
func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, error) { func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, bool, error) {
setDefaults(cfg) setDefaults(cfg)
vmenv := NewEnv(cfg, cfg.State) vmenv := NewEnv(cfg, cfg.State)
sender := cfg.State.GetOrNewStateObject(cfg.Origin) sender := cfg.State.GetOrNewStateObject(cfg.Origin)
// Call the code with the given configuration. // Call the code with the given configuration.
ret, leftOverGas, err := vmenv.Call( ret, leftOverGas, reverted, err := vmenv.Call(
sender, sender,
address, address,
input, input,
@ -170,5 +170,5 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
cfg.Value, cfg.Value,
) )
return ret, leftOverGas, err return ret, leftOverGas, reverted, err
} }

View file

@ -24,7 +24,7 @@ import (
) )
func ExampleExecute() { func ExampleExecute() {
ret, _, err := runtime.Execute(common.Hex2Bytes("6060604052600a8060106000396000f360606040526008565b00"), nil, nil) ret, _, _, err := runtime.Execute(common.Hex2Bytes("6060604052600a8060106000396000f360606040526008565b00"), nil, nil)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }

View file

@ -75,7 +75,7 @@ func TestEVM(t *testing.T) {
} }
func TestExecute(t *testing.T) { func TestExecute(t *testing.T) {
ret, _, err := Execute([]byte{ ret, _, _, err := Execute([]byte{
byte(vm.PUSH1), 10, byte(vm.PUSH1), 10,
byte(vm.PUSH1), 0, byte(vm.PUSH1), 0,
byte(vm.MSTORE), byte(vm.MSTORE),
@ -106,7 +106,7 @@ func TestCall(t *testing.T) {
byte(vm.RETURN), byte(vm.RETURN),
}) })
ret, _, err := Call(address, nil, &Config{State: state}) ret, _, _, err := Call(address, nil, &Config{State: state})
if err != nil { if err != nil {
t.Fatal("didn't expect error", err) t.Fatal("didn't expect error", err)
} }
@ -117,6 +117,143 @@ func TestCall(t *testing.T) {
} }
} }
func TestTransactionReverted(t *testing.T) {
/*
Contract Source Code
```
contract Demo {
function Demo() {}
function IllegalDivision() returns(int) {
var dividend = 0;
return 1 / dividend;
}
function LegalDivision() returns(int) {
var dividend = 1;
return 1 / dividend;
}
}
```
*/
var definition = `[{"constant":false,"inputs":[],"name":"LegalDivision","outputs":[{"name":"","type":"int256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"IllegalDivision","outputs":[{"name":"","type":"int256"}],"payable":false,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"}]`
var rawcode = common.Hex2Bytes("6060604052341561000c57fe5b5b5b5b60f88061001d6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632d256662146044578063522de105146067575bfe5b3415604b57fe5b6051608a565b6040518082815260200191505060405180910390f35b3415606e57fe5b607460ab565b6040518082815260200191505060405180910390f35b60006000600190508060ff16600181151560a057fe5b0460ff1691505b5090565b60006000600090508060ff16600181151560c157fe5b0460ff1691505b50905600a165627a7a7230582091585859014c4644d0427bf34abc65433b5874993ddea00cac57bcb87ee4cb6b0029")
abi, err := abi.JSON(strings.NewReader(definition))
if err != nil {
t.Fatal(err)
}
legalDivision, err := abi.Pack("LegalDivision")
if err != nil {
t.Fatal(err)
}
illegalDivision, err := abi.Pack("IllegalDivision")
if err != nil {
t.Fatal(err)
}
var reverted bool
// deploy
cfg := &Config{
Origin: common.HexToAddress("sender"),
}
code, _, _, _, err := Create(rawcode, cfg)
if err != nil {
t.Fatal(err)
}
_, _, reverted, _ = Execute(code, legalDivision, cfg)
if reverted != false {
t.Fatal("Expect false, got true")
}
_, _, reverted, _ = Execute(code, illegalDivision, cfg)
if reverted != true {
t.Fatal("Expect true, got false")
}
}
func TestDelegateReverted(t *testing.T) {
/*
Contract Source Code
```
contract Relay {
address public currentVersion;
address public owner;
modifier onlyOwner() {
if (msg.sender != owner) {
throw;
}
_;
}
function Relay(address _address) {
currentVersion = _address;
owner = msg.sender;
}
function changeContract(address newVersion) public
onlyOwner()
{
currentVersion = newVersion;
}
function() {
if(!currentVersion.delegatecall(msg.data)) throw;
}
}
contract Demo {
function Demo() {
}
function IllegalDivision() returns(int) {
var dividend = 0;
return 1 / dividend;
}
function LegalDivision() returns(int) {
var dividend = 1;
return 1 / dividend;
}
}
```
*/
var definition = `[{"constant":false,"inputs":[],"name":"LegalDivision","outputs":[{"name":"","type":"int256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"IllegalDivision","outputs":[{"name":"","type":"int256"}],"payable":false,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"}]`
var rawcode1 = common.Hex2Bytes("6060604052341561000c57fe5b5b5b5b60f88061001d6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632d256662146044578063522de105146067575bfe5b3415604b57fe5b6051608a565b6040518082815260200191505060405180910390f35b3415606e57fe5b607460ab565b6040518082815260200191505060405180910390f35b60006000600190508060ff16600181151560a057fe5b0460ff1691505b5090565b60006000600090508060ff16600181151560c157fe5b0460ff1691505b50905600a165627a7a7230582091585859014c4644d0427bf34abc65433b5874993ddea00cac57bcb87ee4cb6b0029")
var rawcode2 = common.Hex2Bytes("6060604052341561000c57fe5b60405160208061039c833981016040528080519060200190919050505b80600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b505b6102df806100bd6000396000f30060606040523615610055576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633d71c3af146100ea5780638da5cb5b146101205780639d888e8614610172575b341561005d57fe5b6100e85b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600036600060405160200152604051808383808284378201915050925050506020604051808303818560325a03f415156100d057fe5b50506040518051905015156100e55760006000fd5b5b565b005b34156100f257fe5b61011e600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506101c4565b005b341561012857fe5b610130610267565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561017a57fe5b61018261028d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156102215760006000fd5b80600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b5b50565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16815600a165627a7a723058208496aa0ef6d67e2e0423b76e8fd61b92e0047fe2e20ad20f887caf2b378dfa300029")
abi, err := abi.JSON(strings.NewReader(definition))
if err != nil {
t.Fatal(err)
}
legalDivision, err := abi.Pack("LegalDivision")
if err != nil {
t.Fatal(err)
}
illegalDivision, err := abi.Pack("IllegalDivision")
if err != nil {
t.Fatal(err)
}
var reverted bool
// deploy
cfg := &Config{
Origin: common.HexToAddress("sender"),
}
_, addr, _, _, err := Create(rawcode1, cfg)
if err != nil {
t.Fatal(err)
}
_, addr2, _, _, err := Create(append(rawcode2, common.LeftPadBytes(addr.Bytes(), 32)...), cfg)
if err != nil {
t.Fatal(err)
}
_, _, reverted, _ = Call(addr2, legalDivision, cfg)
if reverted != false {
t.Fatal("Expect false, got true")
}
_, _, reverted, _ = Call(addr2, illegalDivision, cfg)
if reverted != true {
t.Fatal("Expect true, got false")
}
}
func BenchmarkCall(b *testing.B) { func BenchmarkCall(b *testing.B) {
var definition = `[{"constant":true,"inputs":[],"name":"seller","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[],"name":"abort","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"value","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"refund","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"buyer","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[],"name":"confirmReceived","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"state","outputs":[{"name":"","type":"uint8"}],"type":"function"},{"constant":false,"inputs":[],"name":"confirmPurchase","outputs":[],"type":"function"},{"inputs":[],"type":"constructor"},{"anonymous":false,"inputs":[],"name":"Aborted","type":"event"},{"anonymous":false,"inputs":[],"name":"PurchaseConfirmed","type":"event"},{"anonymous":false,"inputs":[],"name":"ItemReceived","type":"event"},{"anonymous":false,"inputs":[],"name":"Refunded","type":"event"}]` var definition = `[{"constant":true,"inputs":[],"name":"seller","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[],"name":"abort","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"value","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"refund","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"buyer","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[],"name":"confirmReceived","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"state","outputs":[{"name":"","type":"uint8"}],"type":"function"},{"constant":false,"inputs":[],"name":"confirmPurchase","outputs":[],"type":"function"},{"inputs":[],"type":"constructor"},{"anonymous":false,"inputs":[],"name":"Aborted","type":"event"},{"anonymous":false,"inputs":[],"name":"PurchaseConfirmed","type":"event"},{"anonymous":false,"inputs":[],"name":"ItemReceived","type":"event"},{"anonymous":false,"inputs":[],"name":"Refunded","type":"event"}]`

View file

@ -543,7 +543,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
// Run the transaction with tracing enabled. // Run the transaction with tracing enabled.
vmenv := vm.NewEVM(context, statedb, api.config, vm.Config{Debug: true, Tracer: tracer}) vmenv := vm.NewEVM(context, statedb, api.config, vm.Config{Debug: true, Tracer: tracer})
ret, gas, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())) ret, gas, reverted, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil { if err != nil {
return nil, fmt.Errorf("tracing failed: %v", err) return nil, fmt.Errorf("tracing failed: %v", err)
} }
@ -553,6 +553,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
Gas: gas, Gas: gas,
ReturnValue: fmt.Sprintf("%x", ret), ReturnValue: fmt.Sprintf("%x", ret),
StructLogs: ethapi.FormatLogs(tracer.StructLogs()), StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
Reverted: reverted,
}, nil }, nil
case *ethapi.JavascriptTracer: case *ethapi.JavascriptTracer:
return tracer.GetResult() return tracer.GetResult()
@ -590,7 +591,7 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int) (co
vmenv := vm.NewEVM(context, statedb, api.config, vm.Config{}) vmenv := vm.NewEVM(context, statedb, api.config, vm.Config{})
gp := new(core.GasPool).AddGas(tx.Gas()) gp := new(core.GasPool).AddGas(tx.Gas())
_, _, err := core.ApplyMessage(vmenv, msg, gp) _, _, _,err := core.ApplyMessage(vmenv, msg, gp)
if err != nil { if err != nil {
return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err) return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
} }

View file

@ -643,7 +643,7 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
// Setup the gas pool (also for unmetered requests) // Setup the gas pool (also for unmetered requests)
// and apply the message. // and apply the message.
gp := new(core.GasPool).AddGas(math.MaxBig256) gp := new(core.GasPool).AddGas(math.MaxBig256)
res, gas, err := core.ApplyMessage(evm, msg, gp) res, gas, _, err := core.ApplyMessage(evm, msg, gp)
if err := vmError(); err != nil { if err := vmError(); err != nil {
return nil, common.Big0, err return nil, common.Big0, err
} }
@ -696,6 +696,7 @@ type ExecutionResult struct {
Gas *big.Int `json:"gas"` Gas *big.Int `json:"gas"`
ReturnValue string `json:"returnValue"` ReturnValue string `json:"returnValue"`
StructLogs []StructLogRes `json:"structLogs"` StructLogs []StructLogRes `json:"structLogs"`
Reverted bool `json:"reverted"`
} }
// StructLogRes stores a structured log emitted by the EVM while replaying a // StructLogRes stores a structured log emitted by the EVM while replaying a
@ -1089,6 +1090,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(hash common.Hash) (map[
"contractAddress": nil, "contractAddress": nil,
"logs": receipt.Logs, "logs": receipt.Logs,
"logsBloom": receipt.Bloom, "logsBloom": receipt.Bloom,
"reverted": receipt.Reverted,
} }
if receipt.Logs == nil { if receipt.Logs == nil {
fields["logs"] = [][]*types.Log{} fields["logs"] = [][]*types.Log{}

View file

@ -128,7 +128,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
//vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{}) //vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{})
gp := new(core.GasPool).AddGas(math.MaxBig256) gp := new(core.GasPool).AddGas(math.MaxBig256)
ret, _, _ := core.ApplyMessage(vmenv, msg, gp) ret, _, _, _ := core.ApplyMessage(vmenv, msg, gp)
res = append(res, ret...) res = append(res, ret...)
} }
} else { } else {
@ -146,7 +146,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
//vmenv := light.NewEnv(ctx, state, config, lc, msg, header, vm.Config{}) //vmenv := light.NewEnv(ctx, state, config, lc, msg, header, vm.Config{})
gp := new(core.GasPool).AddGas(math.MaxBig256) gp := new(core.GasPool).AddGas(math.MaxBig256)
ret, _, _ := core.ApplyMessage(vmenv, msg, gp) ret, _, _, _ := core.ApplyMessage(vmenv, msg, gp)
if vmstate.Error() == nil { if vmstate.Error() == nil {
res = append(res, ret...) res = append(res, ret...)
} }

View file

@ -179,7 +179,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
vmenv := vm.NewEVM(context, statedb, config, vm.Config{}) vmenv := vm.NewEVM(context, statedb, config, vm.Config{})
gp := new(core.GasPool).AddGas(math.MaxBig256) gp := new(core.GasPool).AddGas(math.MaxBig256)
ret, _, _ := core.ApplyMessage(vmenv, msg, gp) ret, _, _, _ := core.ApplyMessage(vmenv, msg, gp)
res = append(res, ret...) res = append(res, ret...)
} }
} else { } else {
@ -194,7 +194,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
context := core.NewEVMContext(msg, header, lc, nil) context := core.NewEVMContext(msg, header, lc, nil)
vmenv := vm.NewEVM(context, vmstate, config, vm.Config{}) vmenv := vm.NewEVM(context, vmstate, config, vm.Config{})
gp := new(core.GasPool).AddGas(math.MaxBig256) gp := new(core.GasPool).AddGas(math.MaxBig256)
ret, _, _ := core.ApplyMessage(vmenv, msg, gp) ret, _, _, _ := core.ApplyMessage(vmenv, msg, gp)
if vmstate.Error() == nil { if vmstate.Error() == nil {
res = append(res, ret...) res = append(res, ret...)
} }

View file

@ -212,8 +212,7 @@ func RunState(chainConfig *params.ChainConfig, statedb *state.StateDB, env, tx m
statedb.Reset(root) statedb.Reset(root)
snapshot := statedb.Snapshot() snapshot := statedb.Snapshot()
ret, gasUsed, _, err := core.ApplyMessage(environment, msg, gaspool)
ret, gasUsed, err := core.ApplyMessage(environment, msg, gaspool)
if err != nil { if err != nil {
statedb.RevertToSnapshot(snapshot) statedb.RevertToSnapshot(snapshot)
} }

View file

@ -230,6 +230,6 @@ func RunVm(statedb *state.StateDB, env, exec map[string]string) ([]byte, []*type
vm.PrecompiledContracts = make(map[common.Address]vm.PrecompiledContract) vm.PrecompiledContracts = make(map[common.Address]vm.PrecompiledContract)
environment, _ := NewEVMEnvironment(true, chainConfig, statedb, env, exec) environment, _ := NewEVMEnvironment(true, chainConfig, statedb, env, exec)
ret, g, err := environment.Call(caller, to, data, gas.Uint64(), value) ret, g, _, err := environment.Call(caller, to, data, gas.Uint64(), value)
return ret, statedb.Logs(), new(big.Int).SetUint64(g), err return ret, statedb.Logs(), new(big.Int).SetUint64(g), err
} }