Skip to content

Commit

Permalink
core/vm: implement opcode REVERT - eip#140
Browse files Browse the repository at this point in the history
  • Loading branch information
obscuren committed Mar 6, 2017
1 parent 725c264 commit 12be105
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 28 deletions.
20 changes: 13 additions & 7 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in homestead this also counts for code storage gas errors.
if err != nil {
contract.UseGas(contract.Gas)

if err != errRevert {
contract.UseGas(contract.Gas)
}
evm.StateDB.RevertToSnapshot(snapshot)
}
return ret, contract.Gas, err
Expand Down Expand Up @@ -177,7 +178,9 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,

ret, err = evm.interpreter.Run(contract, input)
if err != nil {
contract.UseGas(contract.Gas)
if err != errRevert {
contract.UseGas(contract.Gas)
}

evm.StateDB.RevertToSnapshot(snapshot)
}
Expand Down Expand Up @@ -212,7 +215,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by

ret, err = evm.interpreter.Run(contract, input)
if err != nil {
contract.UseGas(contract.Gas)
if err != errRevert {
contract.UseGas(contract.Gas)
}

evm.StateDB.RevertToSnapshot(snapshot)
}
Expand Down Expand Up @@ -275,10 +280,11 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
// when we're in homestead this also counts for code storage gas errors.
if maxCodeSizeExceeded ||
(err != nil && (evm.ChainConfig().IsHomestead(evm.BlockNumber) || err != ErrCodeStoreOutOfGas)) {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errRevert {
contract.UseGas(contract.Gas)
}

// Nothing should be returned when an error is thrown.
return nil, contractAddr, 0, err
evm.StateDB.RevertToSnapshot(snapshot)
}
// 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
Expand Down
20 changes: 19 additions & 1 deletion core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package vm

import (
"errors"
"fmt"
"math/big"

Expand All @@ -27,7 +28,10 @@ import (
"github.com/ethereum/go-ethereum/params"
)

var bigZero = new(big.Int)
var (
bigZero = new(big.Int)
errRevert = errors.New("standard throw")
)

func opAdd(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := stack.pop(), stack.pop()
Expand Down Expand Up @@ -669,6 +673,20 @@ func opReturn(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S
return ret, nil
}

func opRevert(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
// if not metropolis return an error. REVERT is not supported
// during pre-metropolis.
if !evm.ChainConfig().IsMetropolis(evm.BlockNumber) {
return nil, fmt.Errorf("invalid opcode %x", REVERT)
}

offset, size := stack.pop(), stack.pop()
ret := memory.GetPtr(offset.Int64(), size.Int64())

evm.interpreter.intPool.put(offset, size)
return ret, errRevert
}

func opStop(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
return nil, nil
}
Expand Down
8 changes: 8 additions & 0 deletions core/vm/jump_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,14 @@ func NewJumpTable() [256]operation {
validateStack: makeStackFunc(0, 1),
valid: true,
},
// TODO:
// * Determine cost
REVERT: {
execute: opRevert,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(2, 0),
valid: true,
},
JUMPDEST: {
execute: opJumpdest,
gasCost: constGasFunc(params.JumpdestGas),
Expand Down
3 changes: 3 additions & 0 deletions core/vm/opcodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ const (
RETURN
DELEGATECALL

REVERT = 0xfd
SELFDESTRUCT = 0xff
)

Expand Down Expand Up @@ -353,6 +354,7 @@ var opCodeToString = map[OpCode]string{
CREATE: "CREATE",
CALL: "CALL",
RETURN: "RETURN",
REVERT: "REVERT",
CALLCODE: "CALLCODE",
DELEGATECALL: "DELEGATECALL",
SELFDESTRUCT: "SELFDESTRUCT",
Expand Down Expand Up @@ -500,6 +502,7 @@ var stringToOp = map[string]OpCode{
"CREATE": CREATE,
"CALL": CALL,
"RETURN": RETURN,
"REVERT": REVERT,
"CALLCODE": CALLCODE,
"SELFDESTRUCT": SELFDESTRUCT,
}
Expand Down
54 changes: 34 additions & 20 deletions params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,28 @@ import (

// MainnetChainConfig is the chain parameters to run a node on the main network.
var MainnetChainConfig = &ChainConfig{
ChainId: MainNetChainID,
HomesteadBlock: MainNetHomesteadBlock,
DAOForkBlock: MainNetDAOForkBlock,
DAOForkSupport: true,
EIP150Block: MainNetHomesteadGasRepriceBlock,
EIP150Hash: MainNetHomesteadGasRepriceHash,
EIP155Block: MainNetSpuriousDragon,
EIP158Block: MainNetSpuriousDragon,
ChainId: MainNetChainID,
HomesteadBlock: MainNetHomesteadBlock,
DAOForkBlock: MainNetDAOForkBlock,
DAOForkSupport: true,
EIP150Block: MainNetHomesteadGasRepriceBlock,
EIP150Hash: MainNetHomesteadGasRepriceHash,
EIP155Block: MainNetSpuriousDragon,
EIP158Block: MainNetSpuriousDragon,
MetropolisBlock: MainNetMetropolisBlock,
}

// TestnetChainConfig is the chain parameters to run a node on the test network.
var TestnetChainConfig = &ChainConfig{
ChainId: big.NewInt(3),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: nil,
DAOForkSupport: true,
EIP150Block: big.NewInt(0),
EIP150Hash: common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"),
EIP155Block: big.NewInt(10),
EIP158Block: big.NewInt(10),
ChainId: big.NewInt(3),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: nil,
DAOForkSupport: true,
EIP150Block: big.NewInt(0),
EIP150Hash: common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"),
EIP155Block: big.NewInt(10),
EIP158Block: big.NewInt(10),
MetropolisBlock: big.NewInt(0),
}

// AllProtocolChanges contains every protocol change (EIPs)
Expand All @@ -55,7 +57,7 @@ var TestnetChainConfig = &ChainConfig{
// means that all fields must be set at all times. This forces
// anyone adding flags to the config to also have to set these
// fields.
var AllProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0)}
var AllProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0)}

// ChainConfig is the core config which determines the blockchain settings.
//
Expand All @@ -75,23 +77,26 @@ type ChainConfig struct {

EIP155Block *big.Int `json:"eip155Block"` // EIP155 HF block
EIP158Block *big.Int `json:"eip158Block"` // EIP158 HF block

MetropolisBlock *big.Int `json:"metropolisBlock"` // Metropolis switch block (nil = no fork, 0 = alraedy on homestead)
}

// String implements the Stringer interface.
func (c *ChainConfig) String() string {
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v}",
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Metropolis: %v}",
c.ChainId,
c.HomesteadBlock,
c.DAOForkBlock,
c.DAOForkSupport,
c.EIP150Block,
c.EIP155Block,
c.EIP158Block,
c.MetropolisBlock,
)
}

var (
TestChainConfig = &ChainConfig{big.NewInt(1), new(big.Int), new(big.Int), true, new(big.Int), common.Hash{}, new(big.Int), new(big.Int)}
TestChainConfig = &ChainConfig{big.NewInt(1), new(big.Int), new(big.Int), true, new(big.Int), common.Hash{}, new(big.Int), new(big.Int), new(big.Int)}
TestRules = TestChainConfig.Rules(new(big.Int))
)

Expand Down Expand Up @@ -145,6 +150,14 @@ func (c *ChainConfig) IsEIP158(num *big.Int) bool {

}

func (c *ChainConfig) IsMetropolis(num *big.Int) bool {
if c.MetropolisBlock == nil || num == nil {
return false
}
return num.Cmp(c.MetropolisBlock) >= 0

}

// Rules wraps ChainConfig and is merely syntatic sugar or can be used for functions
// that do not have or require information about the block.
//
Expand All @@ -153,8 +166,9 @@ func (c *ChainConfig) IsEIP158(num *big.Int) bool {
type Rules struct {
ChainId *big.Int
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
IsMetropolis bool
}

func (c *ChainConfig) Rules(num *big.Int) Rules {
return Rules{ChainId: new(big.Int).Set(c.ChainId), IsHomestead: c.IsHomestead(num), IsEIP150: c.IsEIP150(num), IsEIP155: c.IsEIP155(num), IsEIP158: c.IsEIP158(num)}
return Rules{ChainId: new(big.Int).Set(c.ChainId), IsHomestead: c.IsHomestead(num), IsEIP150: c.IsEIP150(num), IsEIP155: c.IsEIP155(num), IsEIP158: c.IsEIP158(num), IsMetropolis: c.IsMetropolis(num)}
}
2 changes: 2 additions & 0 deletions params/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ var (
TestNetSpuriousDragon = big.NewInt(10)
MainNetSpuriousDragon = big.NewInt(2675000)

MainNetMetropolisBlock = big.NewInt(5000000)

TestNetChainID = big.NewInt(3) // Test net default chain ID
MainNetChainID = big.NewInt(1) // main net default chain ID
)

0 comments on commit 12be105

Please sign in to comment.