core/vm: improved EVM run loop & instruction calling (#3378)
The run loop, which previously contained custom opcode executes have been removed and has been simplified to a few checks. Each operation consists of 4 elements: execution function, gas cost function, stack validation function and memory size function. The execution function implements the operation's runtime behaviour, the gas cost function implements the operation gas costs function and greatly depends on the memory and stack, the stack validation function validates the stack and makes sure that enough items can be popped off and pushed on and the memory size function calculates the memory required for the operation and returns it. This commit also allows the EVM to go unmetered. This is helpful for offline operations such as contract calls.
This commit is contained in:
parent
2126d81488
commit
bbc4ea4ae8
@ -229,7 +229,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
|
||||
evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain)
|
||||
// Create a new environment which holds all relevant information
|
||||
// about the transaction and calling mechanisms.
|
||||
vmenv := vm.NewEnvironment(evmContext, statedb, chainConfig, vm.Config{})
|
||||
vmenv := vm.NewEVM(evmContext, statedb, chainConfig, vm.Config{})
|
||||
gaspool := new(core.GasPool).AddGas(common.MaxBig)
|
||||
ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
|
||||
return ret, gasUsed, err
|
||||
|
@ -44,14 +44,6 @@ var (
|
||||
Name: "debug",
|
||||
Usage: "output full trace logs",
|
||||
}
|
||||
ForceJitFlag = cli.BoolFlag{
|
||||
Name: "forcejit",
|
||||
Usage: "forces jit compilation",
|
||||
}
|
||||
DisableJitFlag = cli.BoolFlag{
|
||||
Name: "nojit",
|
||||
Usage: "disabled jit compilation",
|
||||
}
|
||||
CodeFlag = cli.StringFlag{
|
||||
Name: "code",
|
||||
Usage: "EVM code",
|
||||
@ -95,6 +87,10 @@ var (
|
||||
Name: "create",
|
||||
Usage: "indicates the action should be create rather than call",
|
||||
}
|
||||
DisableGasMeteringFlag = cli.BoolFlag{
|
||||
Name: "nogasmetering",
|
||||
Usage: "disable gas metering",
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -102,8 +98,6 @@ func init() {
|
||||
CreateFlag,
|
||||
DebugFlag,
|
||||
VerbosityFlag,
|
||||
ForceJitFlag,
|
||||
DisableJitFlag,
|
||||
SysStatFlag,
|
||||
CodeFlag,
|
||||
CodeFileFlag,
|
||||
@ -112,6 +106,7 @@ func init() {
|
||||
ValueFlag,
|
||||
DumpFlag,
|
||||
InputFlag,
|
||||
DisableGasMeteringFlag,
|
||||
}
|
||||
app.Action = run
|
||||
}
|
||||
@ -165,7 +160,8 @@ func run(ctx *cli.Context) error {
|
||||
GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)),
|
||||
Value: common.Big(ctx.GlobalString(ValueFlag.Name)),
|
||||
EVMConfig: vm.Config{
|
||||
Tracer: logger,
|
||||
Tracer: logger,
|
||||
DisableGasMetering: ctx.GlobalBool(DisableGasMeteringFlag.Name),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
@ -179,7 +175,8 @@ func run(ctx *cli.Context) error {
|
||||
GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)),
|
||||
Value: common.Big(ctx.GlobalString(ValueFlag.Name)),
|
||||
EVMConfig: vm.Config{
|
||||
Tracer: logger,
|
||||
Tracer: logger,
|
||||
DisableGasMetering: ctx.GlobalBool(DisableGasMeteringFlag.Name),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
|
||||
b.SetCoinbase(common.Address{})
|
||||
}
|
||||
b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs))
|
||||
receipt, _, _, err := ApplyTransaction(b.config, nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{})
|
||||
receipt, _, err := ApplyTransaction(b.config, nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -74,12 +74,12 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||
for i, tx := range block.Transactions() {
|
||||
//fmt.Println("tx:", i)
|
||||
statedb.StartRecord(tx.Hash(), block.Hash(), i)
|
||||
receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
|
||||
receipt, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
receipts = append(receipts, receipt)
|
||||
allLogs = append(allLogs, logs...)
|
||||
allLogs = append(allLogs, receipt.Logs...)
|
||||
}
|
||||
AccumulateRewards(statedb, header, block.Uncles())
|
||||
|
||||
@ -87,24 +87,23 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||
}
|
||||
|
||||
// ApplyTransaction attempts to apply a transaction to the given state database
|
||||
// and uses the input parameters for its environment.
|
||||
//
|
||||
// ApplyTransactions returns the generated receipts and vm logs during the
|
||||
// execution of the state transition phase.
|
||||
func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) {
|
||||
// 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 *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, *big.Int, error) {
|
||||
msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
// Create a new context to be used in the EVM environment
|
||||
context := NewEVMContext(msg, header, bc)
|
||||
// Create a new environment which holds all relevant information
|
||||
// about the transaction and calling mechanisms.
|
||||
vmenv := vm.NewEnvironment(context, statedb, config, vm.Config{})
|
||||
vmenv := vm.NewEVM(context, statedb, config, vm.Config{})
|
||||
// Apply the transaction to the current state (included in the env)
|
||||
_, gas, err := ApplyMessage(vmenv, msg, gp)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Update the state with pending changes
|
||||
@ -125,7 +124,7 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, s
|
||||
|
||||
glog.V(logger.Debug).Infoln(receipt)
|
||||
|
||||
return receipt, receipt.Logs, gas, err
|
||||
return receipt, gas, err
|
||||
}
|
||||
|
||||
// AccumulateRewards credits the coinbase of the given block with the
|
||||
|
@ -57,7 +57,7 @@ type StateTransition struct {
|
||||
data []byte
|
||||
state vm.StateDB
|
||||
|
||||
env *vm.Environment
|
||||
env *vm.EVM
|
||||
}
|
||||
|
||||
// Message represents a message sent to a contract.
|
||||
@ -106,7 +106,7 @@ func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int {
|
||||
}
|
||||
|
||||
// NewStateTransition initialises and returns a new state transition object.
|
||||
func NewStateTransition(env *vm.Environment, msg Message, gp *GasPool) *StateTransition {
|
||||
func NewStateTransition(env *vm.EVM, msg Message, gp *GasPool) *StateTransition {
|
||||
return &StateTransition{
|
||||
gp: gp,
|
||||
env: env,
|
||||
@ -127,7 +127,7 @@ func NewStateTransition(env *vm.Environment, msg Message, gp *GasPool) *StateTra
|
||||
// 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) {
|
||||
func ApplyMessage(env *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, error) {
|
||||
st := NewStateTransition(env, msg, gp)
|
||||
|
||||
ret, _, gasUsed, err := st.TransitionDb()
|
||||
@ -159,7 +159,7 @@ func (self *StateTransition) to() vm.Account {
|
||||
|
||||
func (self *StateTransition) useGas(amount *big.Int) error {
|
||||
if self.gas.Cmp(amount) < 0 {
|
||||
return vm.OutOfGasError
|
||||
return vm.ErrOutOfGas
|
||||
}
|
||||
self.gas.Sub(self.gas, amount)
|
||||
|
||||
@ -233,7 +233,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
|
||||
)
|
||||
if contractCreation {
|
||||
ret, _, vmerr = vmenv.Create(sender, self.data, self.gas, self.value)
|
||||
if homestead && err == vm.CodeStoreOutOfGasError {
|
||||
if homestead && err == vm.ErrCodeStoreOutOfGas {
|
||||
self.gas = Big0
|
||||
}
|
||||
} else {
|
||||
|
@ -26,64 +26,45 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// PrecompiledAccount represents a native ethereum contract
|
||||
type PrecompiledAccount struct {
|
||||
Gas func(l int) *big.Int
|
||||
fn func(in []byte) []byte
|
||||
}
|
||||
|
||||
// Call calls the native function
|
||||
func (self PrecompiledAccount) Call(in []byte) []byte {
|
||||
return self.fn(in)
|
||||
// Precompiled contract is the basic interface for native Go contracts. The implementation
|
||||
// requires a deterministic gas count based on the input size of the Run method of the
|
||||
// contract.
|
||||
type PrecompiledContract interface {
|
||||
RequiredGas(inputSize int) *big.Int // RequiredPrice calculates the contract gas use
|
||||
Run(input []byte) []byte // Run runs the precompiled contract
|
||||
}
|
||||
|
||||
// Precompiled contains the default set of ethereum contracts
|
||||
var Precompiled = PrecompiledContracts()
|
||||
var PrecompiledContracts = map[common.Address]PrecompiledContract{
|
||||
common.BytesToAddress([]byte{1}): &ecrecover{},
|
||||
common.BytesToAddress([]byte{2}): &sha256{},
|
||||
common.BytesToAddress([]byte{3}): &ripemd160{},
|
||||
common.BytesToAddress([]byte{4}): &dataCopy{},
|
||||
}
|
||||
|
||||
// PrecompiledContracts returns the default set of precompiled ethereum
|
||||
// contracts defined by the ethereum yellow paper.
|
||||
func PrecompiledContracts() map[string]*PrecompiledAccount {
|
||||
return map[string]*PrecompiledAccount{
|
||||
// ECRECOVER
|
||||
string(common.LeftPadBytes([]byte{1}, 20)): &PrecompiledAccount{func(l int) *big.Int {
|
||||
return params.EcrecoverGas
|
||||
}, ecrecoverFunc},
|
||||
// RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go
|
||||
func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) {
|
||||
gas := p.RequiredGas(len(input))
|
||||
if contract.UseGas(gas) {
|
||||
ret = p.Run(input)
|
||||
|
||||
// SHA256
|
||||
string(common.LeftPadBytes([]byte{2}, 20)): &PrecompiledAccount{func(l int) *big.Int {
|
||||
n := big.NewInt(int64(l+31) / 32)
|
||||
n.Mul(n, params.Sha256WordGas)
|
||||
return n.Add(n, params.Sha256Gas)
|
||||
}, sha256Func},
|
||||
|
||||
// RIPEMD160
|
||||
string(common.LeftPadBytes([]byte{3}, 20)): &PrecompiledAccount{func(l int) *big.Int {
|
||||
n := big.NewInt(int64(l+31) / 32)
|
||||
n.Mul(n, params.Ripemd160WordGas)
|
||||
return n.Add(n, params.Ripemd160Gas)
|
||||
}, ripemd160Func},
|
||||
|
||||
string(common.LeftPadBytes([]byte{4}, 20)): &PrecompiledAccount{func(l int) *big.Int {
|
||||
n := big.NewInt(int64(l+31) / 32)
|
||||
n.Mul(n, params.IdentityWordGas)
|
||||
|
||||
return n.Add(n, params.IdentityGas)
|
||||
}, memCpy},
|
||||
return ret, nil
|
||||
} else {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
|
||||
func sha256Func(in []byte) []byte {
|
||||
return crypto.Sha256(in)
|
||||
// ECRECOVER implemented as a native contract
|
||||
type ecrecover struct{}
|
||||
|
||||
func (c *ecrecover) RequiredGas(inputSize int) *big.Int {
|
||||
return params.EcrecoverGas
|
||||
}
|
||||
|
||||
func ripemd160Func(in []byte) []byte {
|
||||
return common.LeftPadBytes(crypto.Ripemd160(in), 32)
|
||||
}
|
||||
func (c *ecrecover) Run(in []byte) []byte {
|
||||
const ecRecoverInputLength = 128
|
||||
|
||||
const ecRecoverInputLength = 128
|
||||
|
||||
func ecrecoverFunc(in []byte) []byte {
|
||||
in = common.RightPadBytes(in, 128)
|
||||
in = common.RightPadBytes(in, ecRecoverInputLength)
|
||||
// "in" is (hash, v, r, s), each 32 bytes
|
||||
// but for ecrecover we want (r, s, v)
|
||||
|
||||
@ -108,6 +89,39 @@ func ecrecoverFunc(in []byte) []byte {
|
||||
return common.LeftPadBytes(crypto.Keccak256(pubKey[1:])[12:], 32)
|
||||
}
|
||||
|
||||
func memCpy(in []byte) []byte {
|
||||
// SHA256 implemented as a native contract
|
||||
type sha256 struct{}
|
||||
|
||||
func (c *sha256) RequiredGas(inputSize int) *big.Int {
|
||||
n := big.NewInt(int64(inputSize+31) / 32)
|
||||
n.Mul(n, params.Sha256WordGas)
|
||||
return n.Add(n, params.Sha256Gas)
|
||||
}
|
||||
func (c *sha256) Run(in []byte) []byte {
|
||||
return crypto.Sha256(in)
|
||||
}
|
||||
|
||||
// RIPMED160 implemented as a native contract
|
||||
type ripemd160 struct{}
|
||||
|
||||
func (c *ripemd160) RequiredGas(inputSize int) *big.Int {
|
||||
n := big.NewInt(int64(inputSize+31) / 32)
|
||||
n.Mul(n, params.Ripemd160WordGas)
|
||||
return n.Add(n, params.Ripemd160Gas)
|
||||
}
|
||||
func (c *ripemd160) Run(in []byte) []byte {
|
||||
return common.LeftPadBytes(crypto.Ripemd160(in), 32)
|
||||
}
|
||||
|
||||
// data copy implemented as a native contract
|
||||
type dataCopy struct{}
|
||||
|
||||
func (c *dataCopy) RequiredGas(inputSize int) *big.Int {
|
||||
n := big.NewInt(int64(inputSize+31) / 32)
|
||||
n.Mul(n, params.IdentityWordGas)
|
||||
|
||||
return n.Add(n, params.IdentityGas)
|
||||
}
|
||||
func (c *dataCopy) Run(in []byte) []byte {
|
||||
return in
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ package vm
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
@ -55,41 +56,54 @@ type Context struct {
|
||||
Difficulty *big.Int // Provides information for DIFFICULTY
|
||||
}
|
||||
|
||||
// Environment provides information about external sources for the EVM
|
||||
// EVM provides information about external sources for the EVM
|
||||
//
|
||||
// The Environment should never be reused and is not thread safe.
|
||||
type Environment struct {
|
||||
// The EVM should never be reused and is not thread safe.
|
||||
type EVM struct {
|
||||
// Context provides auxiliary blockchain related information
|
||||
Context
|
||||
// StateDB gives access to the underlying state
|
||||
StateDB StateDB
|
||||
// Depth is the current call stack
|
||||
Depth int
|
||||
depth int
|
||||
|
||||
// evm is the ethereum virtual machine
|
||||
evm Vm
|
||||
// chainConfig contains information about the current chain
|
||||
chainConfig *params.ChainConfig
|
||||
vmConfig Config
|
||||
// virtual machine configuration options used to initialise the
|
||||
// evm.
|
||||
vmConfig Config
|
||||
// global (to this context) ethereum virtual machine
|
||||
// used throughout the execution of the tx.
|
||||
interpreter *Interpreter
|
||||
// abort is used to abort the EVM calling operations
|
||||
// NOTE: must be set atomically
|
||||
abort int32
|
||||
}
|
||||
|
||||
// NewEnvironment retutrns a new EVM environment.
|
||||
func NewEnvironment(context Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *Environment {
|
||||
env := &Environment{
|
||||
Context: context,
|
||||
// NewEVM retutrns a new EVM evmironment.
|
||||
func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM {
|
||||
evm := &EVM{
|
||||
Context: ctx,
|
||||
StateDB: statedb,
|
||||
vmConfig: vmConfig,
|
||||
chainConfig: chainConfig,
|
||||
}
|
||||
env.evm = New(env, vmConfig)
|
||||
return env
|
||||
|
||||
evm.interpreter = NewInterpreter(evm, vmConfig)
|
||||
return evm
|
||||
}
|
||||
|
||||
// Cancel cancels any running EVM operation. This may be called concurrently and it's safe to be
|
||||
// called multiple times.
|
||||
func (evm *EVM) Cancel() {
|
||||
atomic.StoreInt32(&evm.abort, 1)
|
||||
}
|
||||
|
||||
// Call executes the contract associated with the addr with the given input as paramaters. It also handles any
|
||||
// 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.
|
||||
func (env *Environment) Call(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) ([]byte, error) {
|
||||
if env.vmConfig.NoRecursion && env.Depth > 0 {
|
||||
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) {
|
||||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, nil
|
||||
@ -97,48 +111,48 @@ func (env *Environment) Call(caller ContractRef, addr common.Address, input []by
|
||||
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
if env.Depth > int(params.CallCreateDepth.Int64()) {
|
||||
if evm.depth > int(params.CallCreateDepth.Int64()) {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, DepthError
|
||||
return nil, ErrDepth
|
||||
}
|
||||
if !env.Context.CanTransfer(env.StateDB, caller.Address(), value) {
|
||||
if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, ErrInsufficientBalance
|
||||
}
|
||||
|
||||
var (
|
||||
to Account
|
||||
snapshotPreTransfer = env.StateDB.Snapshot()
|
||||
to Account
|
||||
snapshot = evm.StateDB.Snapshot()
|
||||
)
|
||||
if !env.StateDB.Exist(addr) {
|
||||
if Precompiled[addr.Str()] == nil && env.ChainConfig().IsEIP158(env.BlockNumber) && value.BitLen() == 0 {
|
||||
if !evm.StateDB.Exist(addr) {
|
||||
if PrecompiledContracts[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.BitLen() == 0 {
|
||||
caller.ReturnGas(gas)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
to = env.StateDB.CreateAccount(addr)
|
||||
to = evm.StateDB.CreateAccount(addr)
|
||||
} else {
|
||||
to = env.StateDB.GetAccount(addr)
|
||||
to = evm.StateDB.GetAccount(addr)
|
||||
}
|
||||
env.Transfer(env.StateDB, caller.Address(), to.Address(), value)
|
||||
evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
|
||||
|
||||
// initialise a new contract and set the code that is to be used by the
|
||||
// E The contract is a scoped environment for this execution context
|
||||
// E The contract is a scoped evmironment for this execution context
|
||||
// only.
|
||||
contract := NewContract(caller, to, value, gas)
|
||||
contract.SetCallCode(&addr, env.StateDB.GetCodeHash(addr), env.StateDB.GetCode(addr))
|
||||
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
|
||||
defer contract.Finalise()
|
||||
|
||||
ret, err := env.EVM().Run(contract, input)
|
||||
ret, err = evm.interpreter.Run(contract, input)
|
||||
// When an error was returned by the EVM or when setting the creation code
|
||||
// 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)
|
||||
|
||||
env.StateDB.RevertToSnapshot(snapshotPreTransfer)
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
}
|
||||
return ret, err
|
||||
}
|
||||
@ -148,8 +162,8 @@ func (env *Environment) Call(caller ContractRef, addr common.Address, input []by
|
||||
// case of an execution error or failed value transfer.
|
||||
//
|
||||
// CallCode differs from Call in the sense that it executes the given address' code with the caller as context.
|
||||
func (env *Environment) CallCode(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) ([]byte, error) {
|
||||
if env.vmConfig.NoRecursion && env.Depth > 0 {
|
||||
func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) {
|
||||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, nil
|
||||
@ -157,33 +171,33 @@ func (env *Environment) CallCode(caller ContractRef, addr common.Address, input
|
||||
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
if env.Depth > int(params.CallCreateDepth.Int64()) {
|
||||
if evm.depth > int(params.CallCreateDepth.Int64()) {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, DepthError
|
||||
return nil, ErrDepth
|
||||
}
|
||||
if !env.CanTransfer(env.StateDB, caller.Address(), value) {
|
||||
if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, fmt.Errorf("insufficient funds to transfer value. Req %v, has %v", value, env.StateDB.GetBalance(caller.Address()))
|
||||
return nil, fmt.Errorf("insufficient funds to transfer value. Req %v, has %v", value, evm.StateDB.GetBalance(caller.Address()))
|
||||
}
|
||||
|
||||
var (
|
||||
snapshotPreTransfer = env.StateDB.Snapshot()
|
||||
to = env.StateDB.GetAccount(caller.Address())
|
||||
snapshot = evm.StateDB.Snapshot()
|
||||
to = evm.StateDB.GetAccount(caller.Address())
|
||||
)
|
||||
// initialise a new contract and set the code that is to be used by the
|
||||
// E The contract is a scoped environment for this execution context
|
||||
// E The contract is a scoped evmironment for this execution context
|
||||
// only.
|
||||
contract := NewContract(caller, to, value, gas)
|
||||
contract.SetCallCode(&addr, env.StateDB.GetCodeHash(addr), env.StateDB.GetCode(addr))
|
||||
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
|
||||
defer contract.Finalise()
|
||||
|
||||
ret, err := env.EVM().Run(contract, input)
|
||||
ret, err = evm.interpreter.Run(contract, input)
|
||||
if err != nil {
|
||||
contract.UseGas(contract.Gas)
|
||||
|
||||
env.StateDB.RevertToSnapshot(snapshotPreTransfer)
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
}
|
||||
|
||||
return ret, err
|
||||
@ -194,8 +208,8 @@ func (env *Environment) CallCode(caller ContractRef, addr common.Address, input
|
||||
//
|
||||
// DelegateCall differs from CallCode in the sense that it executes the given address' code with the caller as context
|
||||
// and the caller is set to the caller of the caller.
|
||||
func (env *Environment) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas *big.Int) ([]byte, error) {
|
||||
if env.vmConfig.NoRecursion && env.Depth > 0 {
|
||||
func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas *big.Int) (ret []byte, err error) {
|
||||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, nil
|
||||
@ -203,34 +217,34 @@ func (env *Environment) DelegateCall(caller ContractRef, addr common.Address, in
|
||||
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
if env.Depth > int(params.CallCreateDepth.Int64()) {
|
||||
if evm.depth > int(params.CallCreateDepth.Int64()) {
|
||||
caller.ReturnGas(gas)
|
||||
return nil, DepthError
|
||||
return nil, ErrDepth
|
||||
}
|
||||
|
||||
var (
|
||||
snapshot = env.StateDB.Snapshot()
|
||||
to = env.StateDB.GetAccount(caller.Address())
|
||||
snapshot = evm.StateDB.Snapshot()
|
||||
to = evm.StateDB.GetAccount(caller.Address())
|
||||
)
|
||||
|
||||
// Iinitialise a new contract and make initialise the delegate values
|
||||
contract := NewContract(caller, to, caller.Value(), gas).AsDelegate()
|
||||
contract.SetCallCode(&addr, env.StateDB.GetCodeHash(addr), env.StateDB.GetCode(addr))
|
||||
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
|
||||
defer contract.Finalise()
|
||||
|
||||
ret, err := env.EVM().Run(contract, input)
|
||||
ret, err = evm.interpreter.Run(contract, input)
|
||||
if err != nil {
|
||||
contract.UseGas(contract.Gas)
|
||||
|
||||
env.StateDB.RevertToSnapshot(snapshot)
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
}
|
||||
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// Create creates a new contract using code as deployment code.
|
||||
func (env *Environment) Create(caller ContractRef, code []byte, gas, value *big.Int) ([]byte, common.Address, error) {
|
||||
if env.vmConfig.NoRecursion && env.Depth > 0 {
|
||||
func (evm *EVM) Create(caller ContractRef, code []byte, gas, value *big.Int) (ret []byte, contractAddr common.Address, err error) {
|
||||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, common.Address{}, nil
|
||||
@ -238,39 +252,38 @@ func (env *Environment) Create(caller ContractRef, code []byte, gas, value *big.
|
||||
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
if env.Depth > int(params.CallCreateDepth.Int64()) {
|
||||
if evm.depth > int(params.CallCreateDepth.Int64()) {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, common.Address{}, DepthError
|
||||
return nil, common.Address{}, ErrDepth
|
||||
}
|
||||
if !env.CanTransfer(env.StateDB, caller.Address(), value) {
|
||||
if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, common.Address{}, ErrInsufficientBalance
|
||||
}
|
||||
|
||||
// Create a new account on the state
|
||||
nonce := env.StateDB.GetNonce(caller.Address())
|
||||
env.StateDB.SetNonce(caller.Address(), nonce+1)
|
||||
nonce := evm.StateDB.GetNonce(caller.Address())
|
||||
evm.StateDB.SetNonce(caller.Address(), nonce+1)
|
||||
|
||||
snapshotPreTransfer := env.StateDB.Snapshot()
|
||||
var (
|
||||
addr = crypto.CreateAddress(caller.Address(), nonce)
|
||||
to = env.StateDB.CreateAccount(addr)
|
||||
)
|
||||
if env.ChainConfig().IsEIP158(env.BlockNumber) {
|
||||
env.StateDB.SetNonce(addr, 1)
|
||||
snapshot := evm.StateDB.Snapshot()
|
||||
contractAddr = crypto.CreateAddress(caller.Address(), nonce)
|
||||
to := evm.StateDB.CreateAccount(contractAddr)
|
||||
if evm.ChainConfig().IsEIP158(evm.BlockNumber) {
|
||||
evm.StateDB.SetNonce(contractAddr, 1)
|
||||
}
|
||||
env.Transfer(env.StateDB, caller.Address(), to.Address(), value)
|
||||
evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
|
||||
|
||||
// initialise a new contract and set the code that is to be used by the
|
||||
// E The contract is a scoped environment for this execution context
|
||||
// E The contract is a scoped evmironment for this execution context
|
||||
// only.
|
||||
contract := NewContract(caller, to, value, gas)
|
||||
contract.SetCallCode(&addr, crypto.Keccak256Hash(code), code)
|
||||
contract.SetCallCode(&contractAddr, crypto.Keccak256Hash(code), code)
|
||||
defer contract.Finalise()
|
||||
|
||||
ret, err := env.EVM().Run(contract, nil)
|
||||
ret, err = evm.interpreter.Run(contract, nil)
|
||||
|
||||
// check whether the max code size has been exceeded
|
||||
maxCodeSizeExceeded := len(ret) > params.MaxCodeSize
|
||||
// if the contract creation ran successfully and no errors were returned
|
||||
@ -281,9 +294,9 @@ func (env *Environment) Create(caller ContractRef, code []byte, gas, value *big.
|
||||
dataGas := big.NewInt(int64(len(ret)))
|
||||
dataGas.Mul(dataGas, params.CreateDataGas)
|
||||
if contract.UseGas(dataGas) {
|
||||
env.StateDB.SetCode(addr, ret)
|
||||
evm.StateDB.SetCode(contractAddr, ret)
|
||||
} else {
|
||||
err = CodeStoreOutOfGasError
|
||||
err = ErrCodeStoreOutOfGas
|
||||
}
|
||||
}
|
||||
|
||||
@ -291,12 +304,12 @@ func (env *Environment) Create(caller ContractRef, code []byte, gas, value *big.
|
||||
// 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 maxCodeSizeExceeded ||
|
||||
(err != nil && (env.ChainConfig().IsHomestead(env.BlockNumber) || err != CodeStoreOutOfGasError)) {
|
||||
(err != nil && (evm.ChainConfig().IsHomestead(evm.BlockNumber) || err != ErrCodeStoreOutOfGas)) {
|
||||
contract.UseGas(contract.Gas)
|
||||
env.StateDB.RevertToSnapshot(snapshotPreTransfer)
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
|
||||
// Nothing should be returned when an error is thrown.
|
||||
return nil, addr, err
|
||||
return nil, contractAddr, err
|
||||
}
|
||||
// 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
|
||||
@ -305,11 +318,11 @@ func (env *Environment) Create(caller ContractRef, code []byte, gas, value *big.
|
||||
ret = nil
|
||||
}
|
||||
|
||||
return ret, addr, err
|
||||
return ret, contractAddr, err
|
||||
}
|
||||
|
||||
// ChainConfig returns the environment's chain configuration
|
||||
func (env *Environment) ChainConfig() *params.ChainConfig { return env.chainConfig }
|
||||
// ChainConfig returns the evmironment's chain configuration
|
||||
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
|
||||
|
||||
// EVM returns the environments EVM
|
||||
func (env *Environment) EVM() Vm { return env.evm }
|
||||
// Interpreter returns the EVM interpreter
|
||||
func (evm *EVM) Interpreter() *Interpreter { return evm.interpreter }
|
||||
|
@ -16,17 +16,12 @@
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
OutOfGasError = errors.New("Out of gas")
|
||||
CodeStoreOutOfGasError = errors.New("Contract creation code storage out of gas")
|
||||
DepthError = fmt.Errorf("Max call depth exceeded (%d)", params.CallCreateDepth)
|
||||
TraceLimitReachedError = errors.New("The number of logs reached the specified limit")
|
||||
ErrOutOfGas = errors.New("out of gas")
|
||||
ErrCodeStoreOutOfGas = errors.New("contract creation code storage out of gas")
|
||||
ErrDepth = errors.New("max call depth exceeded")
|
||||
ErrTraceLimitReached = errors.New("the number of logs reached the specified limit")
|
||||
ErrInsufficientBalance = errors.New("insufficient balance for transfer")
|
||||
)
|
||||
|
246
core/vm/gas_table.go
Normal file
246
core/vm/gas_table.go
Normal file
@ -0,0 +1,246 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func memoryGasCost(mem *Memory, newMemSize *big.Int) *big.Int {
|
||||
gas := new(big.Int)
|
||||
if newMemSize.Cmp(common.Big0) > 0 {
|
||||
newMemSizeWords := toWordSize(newMemSize)
|
||||
|
||||
if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 {
|
||||
// be careful reusing variables here when changing.
|
||||
// The order has been optimised to reduce allocation
|
||||
oldSize := toWordSize(big.NewInt(int64(mem.Len())))
|
||||
pow := new(big.Int).Exp(oldSize, common.Big2, Zero)
|
||||
linCoef := oldSize.Mul(oldSize, params.MemoryGas)
|
||||
quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv)
|
||||
oldTotalFee := new(big.Int).Add(linCoef, quadCoef)
|
||||
|
||||
pow.Exp(newMemSizeWords, common.Big2, Zero)
|
||||
linCoef = linCoef.Mul(newMemSizeWords, params.MemoryGas)
|
||||
quadCoef = quadCoef.Div(pow, params.QuadCoeffDiv)
|
||||
newTotalFee := linCoef.Add(linCoef, quadCoef)
|
||||
|
||||
fee := newTotalFee.Sub(newTotalFee, oldTotalFee)
|
||||
gas.Add(gas, fee)
|
||||
}
|
||||
}
|
||||
return gas
|
||||
}
|
||||
|
||||
func constGasFunc(gas *big.Int) gasFunc {
|
||||
return func(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return gas
|
||||
}
|
||||
}
|
||||
|
||||
func gasCalldataCopy(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
gas := memoryGasCost(mem, memorySize)
|
||||
gas.Add(gas, GasFastestStep)
|
||||
words := toWordSize(stack.Back(2))
|
||||
|
||||
return gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||
}
|
||||
|
||||
func gasSStore(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
var (
|
||||
y, x = stack.Back(1), stack.Back(0)
|
||||
val = env.StateDB.GetState(contract.Address(), common.BigToHash(x))
|
||||
)
|
||||
// This checks for 3 scenario's and calculates gas accordingly
|
||||
// 1. From a zero-value address to a non-zero value (NEW VALUE)
|
||||
// 2. From a non-zero value address to a zero-value address (DELETE)
|
||||
// 3. From a non-zero to a non-zero (CHANGE)
|
||||
if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) {
|
||||
// 0 => non 0
|
||||
return new(big.Int).Set(params.SstoreSetGas)
|
||||
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
|
||||
env.StateDB.AddRefund(params.SstoreRefundGas)
|
||||
|
||||
return new(big.Int).Set(params.SstoreClearGas)
|
||||
} else {
|
||||
// non 0 => non 0 (or 0 => 0)
|
||||
return new(big.Int).Set(params.SstoreResetGas)
|
||||
}
|
||||
}
|
||||
|
||||
func makeGasLog(n uint) gasFunc {
|
||||
return func(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
mSize := stack.Back(1)
|
||||
|
||||
gas := new(big.Int).Add(memoryGasCost(mem, memorySize), params.LogGas)
|
||||
gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(n)), params.LogTopicGas))
|
||||
gas.Add(gas, new(big.Int).Mul(mSize, params.LogDataGas))
|
||||
return gas
|
||||
}
|
||||
}
|
||||
|
||||
func gasSha3(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
gas := memoryGasCost(mem, memorySize)
|
||||
gas.Add(gas, params.Sha3Gas)
|
||||
words := toWordSize(stack.Back(1))
|
||||
return gas.Add(gas, words.Mul(words, params.Sha3WordGas))
|
||||
}
|
||||
|
||||
func gasCodeCopy(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
gas := memoryGasCost(mem, memorySize)
|
||||
gas.Add(gas, GasFastestStep)
|
||||
words := toWordSize(stack.Back(2))
|
||||
|
||||
return gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||
}
|
||||
|
||||
func gasExtCodeCopy(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
gas := memoryGasCost(mem, memorySize)
|
||||
gas.Add(gas, gt.ExtcodeCopy)
|
||||
words := toWordSize(stack.Back(3))
|
||||
|
||||
return gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||
}
|
||||
|
||||
func gasMLoad(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return new(big.Int).Add(GasFastestStep, memoryGasCost(mem, memorySize))
|
||||
}
|
||||
|
||||
func gasMStore8(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return new(big.Int).Add(GasFastestStep, memoryGasCost(mem, memorySize))
|
||||
}
|
||||
|
||||
func gasMStore(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return new(big.Int).Add(GasFastestStep, memoryGasCost(mem, memorySize))
|
||||
}
|
||||
|
||||
func gasCreate(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return new(big.Int).Add(params.CreateGas, memoryGasCost(mem, memorySize))
|
||||
}
|
||||
|
||||
func gasBalance(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return gt.Balance
|
||||
}
|
||||
|
||||
func gasExtCodeSize(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return gt.ExtcodeSize
|
||||
}
|
||||
|
||||
func gasSLoad(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return gt.SLoad
|
||||
}
|
||||
|
||||
func gasExp(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
expByteLen := int64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
||||
gas := big.NewInt(expByteLen)
|
||||
gas.Mul(gas, gt.ExpByte)
|
||||
return gas.Add(gas, GasSlowStep)
|
||||
}
|
||||
|
||||
func gasCall(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
gas := new(big.Int).Set(gt.Calls)
|
||||
|
||||
transfersValue := stack.Back(2).BitLen() > 0
|
||||
var (
|
||||
address = common.BigToAddress(stack.Back(1))
|
||||
eip158 = env.ChainConfig().IsEIP158(env.BlockNumber)
|
||||
)
|
||||
if eip158 {
|
||||
if env.StateDB.Empty(address) && transfersValue {
|
||||
gas.Add(gas, params.CallNewAccountGas)
|
||||
}
|
||||
} else if !env.StateDB.Exist(address) {
|
||||
gas.Add(gas, params.CallNewAccountGas)
|
||||
}
|
||||
if transfersValue {
|
||||
gas.Add(gas, params.CallValueTransferGas)
|
||||
}
|
||||
gas.Add(gas, memoryGasCost(mem, memorySize))
|
||||
|
||||
cg := callGas(gt, contract.Gas, gas, stack.data[stack.len()-1])
|
||||
// Replace the stack item with the new gas calculation. This means that
|
||||
// either the original item is left on the stack or the item is replaced by:
|
||||
// (availableGas - gas) * 63 / 64
|
||||
// We replace the stack item so that it's available when the opCall instruction is
|
||||
// called. This information is otherwise lost due to the dependency on *current*
|
||||
// available gas.
|
||||
stack.data[stack.len()-1] = cg
|
||||
|
||||
return gas.Add(gas, cg)
|
||||
}
|
||||
|
||||
func gasCallCode(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
gas := new(big.Int).Set(gt.Calls)
|
||||
if stack.Back(2).BitLen() > 0 {
|
||||
gas.Add(gas, params.CallValueTransferGas)
|
||||
}
|
||||
gas.Add(gas, memoryGasCost(mem, memorySize))
|
||||
|
||||
cg := callGas(gt, contract.Gas, gas, stack.data[stack.len()-1])
|
||||
// Replace the stack item with the new gas calculation. This means that
|
||||
// either the original item is left on the stack or the item is replaced by:
|
||||
// (availableGas - gas) * 63 / 64
|
||||
// We replace the stack item so that it's available when the opCall instruction is
|
||||
// called. This information is otherwise lost due to the dependency on *current*
|
||||
// available gas.
|
||||
stack.data[stack.len()-1] = cg
|
||||
|
||||
return gas.Add(gas, cg)
|
||||
}
|
||||
|
||||
func gasReturn(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return memoryGasCost(mem, memorySize)
|
||||
}
|
||||
|
||||
func gasSuicide(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
gas := new(big.Int)
|
||||
// EIP150 homestead gas reprice fork:
|
||||
if env.ChainConfig().IsEIP150(env.BlockNumber) {
|
||||
gas.Set(gt.Suicide)
|
||||
var (
|
||||
address = common.BigToAddress(stack.Back(0))
|
||||
eip158 = env.ChainConfig().IsEIP158(env.BlockNumber)
|
||||
)
|
||||
|
||||
if eip158 {
|
||||
// if empty and transfers value
|
||||
if env.StateDB.Empty(address) && env.StateDB.GetBalance(contract.Address()).BitLen() > 0 {
|
||||
gas.Add(gas, gt.CreateBySuicide)
|
||||
}
|
||||
} else if !env.StateDB.Exist(address) {
|
||||
gas.Add(gas, gt.CreateBySuicide)
|
||||
}
|
||||
}
|
||||
|
||||
if !env.StateDB.HasSuicided(contract.Address()) {
|
||||
env.StateDB.AddRefund(params.SuicideRefundGas)
|
||||
}
|
||||
return gas
|
||||
}
|
||||
|
||||
func gasDelegateCall(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
gas := new(big.Int).Add(gt.Calls, memoryGasCost(mem, memorySize))
|
||||
|
||||
cg := callGas(gt, contract.Gas, gas, stack.data[stack.len()-1])
|
||||
// Replace the stack item with the new gas calculation. This means that
|
||||
// either the original item is left on the stack or the item is replaced by:
|
||||
// (availableGas - gas) * 63 / 64
|
||||
// We replace the stack item so that it's available when the opCall instruction is
|
||||
// called.
|
||||
stack.data[stack.len()-1] = cg
|
||||
|
||||
return gas.Add(gas, cg)
|
||||
}
|
||||
|
||||
func gasPush(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return GasFastestStep
|
||||
}
|
||||
|
||||
func gasSwap(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return GasFastestStep
|
||||
}
|
||||
|
||||
func gasDup(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return GasFastestStep
|
||||
}
|
@ -26,128 +26,39 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
type programInstruction interface {
|
||||
// executes the program instruction and allows the instruction to modify the state of the program
|
||||
do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)
|
||||
// returns whether the program instruction halts the execution of the JIT
|
||||
halts() bool
|
||||
// Returns the current op code (debugging purposes)
|
||||
Op() OpCode
|
||||
}
|
||||
|
||||
type instrFn func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack)
|
||||
|
||||
type instruction struct {
|
||||
op OpCode
|
||||
pc uint64
|
||||
fn instrFn
|
||||
data *big.Int
|
||||
|
||||
gas *big.Int
|
||||
spop int
|
||||
spush int
|
||||
|
||||
returns bool
|
||||
}
|
||||
|
||||
func jump(mapping map[uint64]uint64, destinations map[uint64]struct{}, contract *Contract, to *big.Int) (uint64, error) {
|
||||
if !validDest(destinations, to) {
|
||||
nop := contract.GetOp(to.Uint64())
|
||||
return 0, fmt.Errorf("invalid jump destination (%v) %v", nop, to)
|
||||
}
|
||||
|
||||
return mapping[to.Uint64()], nil
|
||||
}
|
||||
|
||||
func (instr instruction) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
// calculate the new memory size and gas price for the current executing opcode
|
||||
newMemSize, cost, err := jitCalculateGasAndSize(env, contract, instr, memory, stack)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Use the calculated gas. When insufficient gas is present, use all gas and return an
|
||||
// Out Of Gas error
|
||||
if !contract.UseGas(cost) {
|
||||
return nil, OutOfGasError
|
||||
}
|
||||
// Resize the memory calculated previously
|
||||
memory.Resize(newMemSize.Uint64())
|
||||
|
||||
// These opcodes return an argument and are therefor handled
|
||||
// differently from the rest of the opcodes
|
||||
switch instr.op {
|
||||
case JUMP:
|
||||
if pos, err := jump(program.mapping, program.destinations, contract, stack.pop()); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
*pc = pos
|
||||
return nil, nil
|
||||
}
|
||||
case JUMPI:
|
||||
pos, cond := stack.pop(), stack.pop()
|
||||
if cond.Cmp(common.BigTrue) >= 0 {
|
||||
if pos, err := jump(program.mapping, program.destinations, contract, pos); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
*pc = pos
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
case RETURN:
|
||||
offset, size := stack.pop(), stack.pop()
|
||||
return memory.GetPtr(offset.Int64(), size.Int64()), nil
|
||||
default:
|
||||
if instr.fn == nil {
|
||||
return nil, fmt.Errorf("Invalid opcode 0x%x", instr.op)
|
||||
}
|
||||
instr.fn(instr, pc, env, contract, memory, stack)
|
||||
}
|
||||
*pc++
|
||||
func opAdd(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(U256(x.Add(x, y)))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (instr instruction) halts() bool {
|
||||
return instr.returns
|
||||
}
|
||||
|
||||
func (instr instruction) Op() OpCode {
|
||||
return instr.op
|
||||
}
|
||||
|
||||
func opStaticJump(instr instruction, pc *uint64, ret *big.Int, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
ret.Set(instr.data)
|
||||
}
|
||||
|
||||
func opAdd(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(U256(x.Add(x, y)))
|
||||
}
|
||||
|
||||
func opSub(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opSub(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(U256(x.Sub(x, y)))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMul(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opMul(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(U256(x.Mul(x, y)))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opDiv(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opDiv(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
if y.Cmp(common.Big0) != 0 {
|
||||
stack.push(U256(x.Div(x, y)))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSdiv(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opSdiv(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := S256(stack.pop()), S256(stack.pop())
|
||||
if y.Cmp(common.Big0) == 0 {
|
||||
stack.push(new(big.Int))
|
||||
return
|
||||
return nil, nil
|
||||
} else {
|
||||
n := new(big.Int)
|
||||
if new(big.Int).Mul(x, y).Cmp(common.Big0) < 0 {
|
||||
@ -161,18 +72,20 @@ func opSdiv(instr instruction, pc *uint64, env *Environment, contract *Contract,
|
||||
|
||||
stack.push(U256(res))
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMod(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opMod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
if y.Cmp(common.Big0) == 0 {
|
||||
stack.push(new(big.Int))
|
||||
} else {
|
||||
stack.push(U256(x.Mod(x, y)))
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSmod(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opSmod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := S256(stack.pop()), S256(stack.pop())
|
||||
|
||||
if y.Cmp(common.Big0) == 0 {
|
||||
@ -190,14 +103,16 @@ func opSmod(instr instruction, pc *uint64, env *Environment, contract *Contract,
|
||||
|
||||
stack.push(U256(res))
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opExp(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opExp(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
base, exponent := stack.pop(), stack.pop()
|
||||
stack.push(math.Exp(base, exponent))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSignExtend(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opSignExtend(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
back := stack.pop()
|
||||
if back.Cmp(big.NewInt(31)) < 0 {
|
||||
bit := uint(back.Uint64()*8 + 7)
|
||||
@ -212,80 +127,91 @@ func opSignExtend(instr instruction, pc *uint64, env *Environment, contract *Con
|
||||
|
||||
stack.push(U256(num))
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opNot(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opNot(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x := stack.pop()
|
||||
stack.push(U256(x.Not(x)))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opLt(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opLt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
if x.Cmp(y) < 0 {
|
||||
stack.push(big.NewInt(1))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opGt(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opGt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
if x.Cmp(y) > 0 {
|
||||
stack.push(big.NewInt(1))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSlt(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opSlt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := S256(stack.pop()), S256(stack.pop())
|
||||
if x.Cmp(S256(y)) < 0 {
|
||||
stack.push(big.NewInt(1))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSgt(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opSgt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := S256(stack.pop()), S256(stack.pop())
|
||||
if x.Cmp(y) > 0 {
|
||||
stack.push(big.NewInt(1))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opEq(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opEq(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
if x.Cmp(y) == 0 {
|
||||
stack.push(big.NewInt(1))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opIszero(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opIszero(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x := stack.pop()
|
||||
if x.Cmp(common.Big0) > 0 {
|
||||
stack.push(new(big.Int))
|
||||
} else {
|
||||
stack.push(big.NewInt(1))
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opAnd(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opAnd(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(x.And(x, y))
|
||||
return nil, nil
|
||||
}
|
||||
func opOr(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opOr(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(x.Or(x, y))
|
||||
return nil, nil
|
||||
}
|
||||
func opXor(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opXor(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(x.Xor(x, y))
|
||||
return nil, nil
|
||||
}
|
||||
func opByte(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opByte(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
th, val := stack.pop(), stack.pop()
|
||||
if th.Cmp(big.NewInt(32)) < 0 {
|
||||
byte := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()]))
|
||||
@ -293,8 +219,9 @@ func opByte(instr instruction, pc *uint64, env *Environment, contract *Contract,
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
func opAddmod(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opAddmod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y, z := stack.pop(), stack.pop(), stack.pop()
|
||||
if z.Cmp(Zero) > 0 {
|
||||
add := x.Add(x, y)
|
||||
@ -303,8 +230,9 @@ func opAddmod(instr instruction, pc *uint64, env *Environment, contract *Contrac
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
func opMulmod(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opMulmod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y, z := stack.pop(), stack.pop(), stack.pop()
|
||||
if z.Cmp(Zero) > 0 {
|
||||
mul := x.Mul(x, y)
|
||||
@ -313,67 +241,79 @@ func opMulmod(instr instruction, pc *uint64, env *Environment, contract *Contrac
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSha3(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opSha3(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
offset, size := stack.pop(), stack.pop()
|
||||
hash := crypto.Keccak256(memory.Get(offset.Int64(), size.Int64()))
|
||||
|
||||
stack.push(common.BytesToBig(hash))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opAddress(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opAddress(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(common.Bytes2Big(contract.Address().Bytes()))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opBalance(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opBalance(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
addr := common.BigToAddress(stack.pop())
|
||||
balance := env.StateDB.GetBalance(addr)
|
||||
|
||||
stack.push(new(big.Int).Set(balance))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opOrigin(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opOrigin(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(env.Origin.Big())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCaller(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opCaller(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(contract.Caller().Big())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCallValue(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opCallValue(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(new(big.Int).Set(contract.value))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCalldataLoad(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opCalldataLoad(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(common.Bytes2Big(getData(contract.Input, stack.pop(), common.Big32)))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCalldataSize(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opCalldataSize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(big.NewInt(int64(len(contract.Input))))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCalldataCopy(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opCalldataCopy(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
var (
|
||||
mOff = stack.pop()
|
||||
cOff = stack.pop()
|
||||
l = stack.pop()
|
||||
)
|
||||
memory.Set(mOff.Uint64(), l.Uint64(), getData(contract.Input, cOff, l))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opExtCodeSize(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opExtCodeSize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
addr := common.BigToAddress(stack.pop())
|
||||
l := big.NewInt(int64(env.StateDB.GetCodeSize(addr)))
|
||||
stack.push(l)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCodeSize(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opCodeSize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
l := big.NewInt(int64(len(contract.Code)))
|
||||
stack.push(l)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCodeCopy(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opCodeCopy(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
var (
|
||||
mOff = stack.pop()
|
||||
cOff = stack.pop()
|
||||
@ -382,9 +322,10 @@ func opCodeCopy(instr instruction, pc *uint64, env *Environment, contract *Contr
|
||||
codeCopy := getData(contract.Code, cOff, l)
|
||||
|
||||
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opExtCodeCopy(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opExtCodeCopy(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
var (
|
||||
addr = common.BigToAddress(stack.pop())
|
||||
mOff = stack.pop()
|
||||
@ -394,13 +335,15 @@ func opExtCodeCopy(instr instruction, pc *uint64, env *Environment, contract *Co
|
||||
codeCopy := getData(env.StateDB.GetCode(addr), cOff, l)
|
||||
|
||||
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opGasprice(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opGasprice(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(new(big.Int).Set(env.GasPrice))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opBlockhash(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opBlockhash(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
num := stack.pop()
|
||||
|
||||
n := new(big.Int).Sub(env.BlockNumber, common.Big257)
|
||||
@ -409,106 +352,115 @@ func opBlockhash(instr instruction, pc *uint64, env *Environment, contract *Cont
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCoinbase(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opCoinbase(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(env.Coinbase.Big())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opTimestamp(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opTimestamp(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(U256(new(big.Int).Set(env.Time)))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opNumber(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opNumber(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(U256(new(big.Int).Set(env.BlockNumber)))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opDifficulty(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opDifficulty(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(U256(new(big.Int).Set(env.Difficulty)))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opGasLimit(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opGasLimit(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(U256(new(big.Int).Set(env.GasLimit)))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opPop(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opPop(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.pop()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opPush(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(new(big.Int).Set(instr.data))
|
||||
}
|
||||
|
||||
func opDup(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.dup(int(instr.data.Int64()))
|
||||
}
|
||||
|
||||
func opSwap(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.swap(int(instr.data.Int64()))
|
||||
}
|
||||
|
||||
func opLog(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
n := int(instr.data.Int64())
|
||||
topics := make([]common.Hash, n)
|
||||
mStart, mSize := stack.pop(), stack.pop()
|
||||
for i := 0; i < n; i++ {
|
||||
topics[i] = common.BigToHash(stack.pop())
|
||||
}
|
||||
|
||||
d := memory.Get(mStart.Int64(), mSize.Int64())
|
||||
log := NewLog(contract.Address(), topics, d, env.BlockNumber.Uint64())
|
||||
env.StateDB.AddLog(log)
|
||||
}
|
||||
|
||||
func opMload(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opMload(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
offset := stack.pop()
|
||||
val := common.BigD(memory.Get(offset.Int64(), 32))
|
||||
stack.push(val)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMstore(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opMstore(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
// pop value of the stack
|
||||
mStart, val := stack.pop(), stack.pop()
|
||||
memory.Set(mStart.Uint64(), 32, common.BigToBytes(val, 256))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMstore8(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opMstore8(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
off, val := stack.pop().Int64(), stack.pop().Int64()
|
||||
memory.store[off] = byte(val & 0xff)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSload(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opSload(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
loc := common.BigToHash(stack.pop())
|
||||
val := env.StateDB.GetState(contract.Address(), loc).Big()
|
||||
stack.push(val)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSstore(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opSstore(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
loc := common.BigToHash(stack.pop())
|
||||
val := stack.pop()
|
||||
env.StateDB.SetState(contract.Address(), loc, common.BigToHash(val))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opJump(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opJump(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
pos := stack.pop()
|
||||
if !contract.jumpdests.has(contract.CodeHash, contract.Code, pos) {
|
||||
nop := contract.GetOp(pos.Uint64())
|
||||
return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos)
|
||||
}
|
||||
*pc = pos.Uint64()
|
||||
return nil, nil
|
||||
}
|
||||
func opJumpi(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opJumpi(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
pos, cond := stack.pop(), stack.pop()
|
||||
if cond.Cmp(common.BigTrue) >= 0 {
|
||||
if !contract.jumpdests.has(contract.CodeHash, contract.Code, pos) {
|
||||
nop := contract.GetOp(pos.Uint64())
|
||||
return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos)
|
||||
}
|
||||
*pc = pos.Uint64()
|
||||
} else {
|
||||
*pc++
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
func opJumpdest(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opJumpdest(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opPc(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(new(big.Int).Set(instr.data))
|
||||
func opPc(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(new(big.Int).SetUint64(*pc))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMsize(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opMsize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(big.NewInt(int64(memory.Len())))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opGas(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opGas(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(new(big.Int).Set(contract.Gas))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCreate(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opCreate(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
var (
|
||||
value = stack.pop()
|
||||
offset, size = stack.pop(), stack.pop()
|
||||
@ -526,16 +478,17 @@ func opCreate(instr instruction, pc *uint64, env *Environment, contract *Contrac
|
||||
// homestead we must check for CodeStoreOutOfGasError (homestead only
|
||||
// rule) and treat as an error, if the ruleset is frontier we must
|
||||
// ignore this error and pretend the operation was successful.
|
||||
if env.ChainConfig().IsHomestead(env.BlockNumber) && suberr == CodeStoreOutOfGasError {
|
||||
if env.ChainConfig().IsHomestead(env.BlockNumber) && suberr == ErrCodeStoreOutOfGas {
|
||||
stack.push(new(big.Int))
|
||||
} else if suberr != nil && suberr != CodeStoreOutOfGasError {
|
||||
} else if suberr != nil && suberr != ErrCodeStoreOutOfGas {
|
||||
stack.push(new(big.Int))
|
||||
} else {
|
||||
stack.push(addr.Big())
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCall(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opCall(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
gas := stack.pop()
|
||||
// pop gas and value of the stack.
|
||||
addr, value := stack.pop(), stack.pop()
|
||||
@ -564,9 +517,10 @@ func opCall(instr instruction, pc *uint64, env *Environment, contract *Contract,
|
||||
|
||||
memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCallCode(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opCallCode(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
gas := stack.pop()
|
||||
// pop gas and value of the stack.
|
||||
addr, value := stack.pop(), stack.pop()
|
||||
@ -595,9 +549,16 @@ func opCallCode(instr instruction, pc *uint64, env *Environment, contract *Contr
|
||||
|
||||
memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opDelegateCall(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opDelegateCall(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
// if not homestead return an error. DELEGATECALL is not supported
|
||||
// during pre-homestead.
|
||||
if !env.ChainConfig().IsHomestead(env.BlockNumber) {
|
||||
return nil, fmt.Errorf("invalid opcode %x", DELEGATECALL)
|
||||
}
|
||||
|
||||
gas, to, inOffset, inSize, outOffset, outSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||
|
||||
toAddr := common.BigToAddress(to)
|
||||
@ -609,25 +570,34 @@ func opDelegateCall(instr instruction, pc *uint64, env *Environment, contract *C
|
||||
stack.push(big.NewInt(1))
|
||||
memory.Set(outOffset.Uint64(), outSize.Uint64(), ret)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opReturn(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
}
|
||||
func opStop(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opReturn(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
offset, size := stack.pop(), stack.pop()
|
||||
ret := memory.GetPtr(offset.Int64(), size.Int64())
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func opSuicide(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opStop(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSuicide(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
balance := env.StateDB.GetBalance(contract.Address())
|
||||
env.StateDB.AddBalance(common.BigToAddress(stack.pop()), balance)
|
||||
|
||||
env.StateDB.Suicide(contract.Address())
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// following functions are used by the instruction jump table
|
||||
|
||||
// make log instruction function
|
||||
func makeLog(size int) instrFn {
|
||||
return func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func makeLog(size int) executionFunc {
|
||||
return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
topics := make([]common.Hash, size)
|
||||
mStart, mSize := stack.pop(), stack.pop()
|
||||
for i := 0; i < size; i++ {
|
||||
@ -637,30 +607,34 @@ func makeLog(size int) instrFn {
|
||||
d := memory.Get(mStart.Int64(), mSize.Int64())
|
||||
log := NewLog(contract.Address(), topics, d, env.BlockNumber.Uint64())
|
||||
env.StateDB.AddLog(log)
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// make push instruction function
|
||||
func makePush(size uint64, bsize *big.Int) instrFn {
|
||||
return func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func makePush(size uint64, bsize *big.Int) executionFunc {
|
||||
return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
byts := getData(contract.Code, new(big.Int).SetUint64(*pc+1), bsize)
|
||||
stack.push(common.Bytes2Big(byts))
|
||||
*pc += size
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// make push instruction function
|
||||
func makeDup(size int64) instrFn {
|
||||
return func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func makeDup(size int64) executionFunc {
|
||||
return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.dup(int(size))
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// make swap instruction function
|
||||
func makeSwap(size int64) instrFn {
|
||||
func makeSwap(size int64) executionFunc {
|
||||
// switch n + 1 otherwise n would be swapped with n
|
||||
size += 1
|
||||
return func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.swap(int(size))
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
@ -22,14 +22,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
// Vm is the basic interface for an implementation of the EVM.
|
||||
type Vm interface {
|
||||
// Run should execute the given contract with the input given in in
|
||||
// and return the contract execution return bytes or an error if it
|
||||
// failed.
|
||||
Run(c *Contract, in []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
// StateDB is an EVM database for full state querying.
|
||||
type StateDB interface {
|
||||
GetAccount(common.Address) Account
|
||||
@ -83,15 +75,15 @@ type Account interface {
|
||||
Value() *big.Int
|
||||
}
|
||||
|
||||
// CallContext provides a basic interface for the EVM calling conventions. The EVM Environment
|
||||
// CallContext provides a basic interface for the EVM calling conventions. The EVM EVM
|
||||
// depends on this context being implemented for doing subcalls and initialising new EVM contracts.
|
||||
type CallContext interface {
|
||||
// Call another contract
|
||||
Call(env *Environment, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
|
||||
Call(env *EVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
|
||||
// Take another's contract code and execute within our own context
|
||||
CallCode(env *Environment, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
|
||||
CallCode(env *EVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
|
||||
// Same as CallCode except sender and value is propagated from parent to child scope
|
||||
DelegateCall(env *Environment, me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error)
|
||||
DelegateCall(env *EVM, me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error)
|
||||
// Create a new contract
|
||||
Create(env *Environment, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error)
|
||||
Create(env *EVM, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error)
|
||||
}
|
||||
|
512
core/vm/jit.go
512
core/vm/jit.go
@ -1,512 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
// progStatus is the type for the JIT program status.
|
||||
type progStatus int32
|
||||
|
||||
const (
|
||||
progUnknown progStatus = iota // unknown status
|
||||
progCompile // compile status
|
||||
progReady // ready for use status
|
||||
progError // error status (usually caused during compilation)
|
||||
|
||||
defaultJitMaxCache int = 64 // maximum amount of jit cached programs
|
||||
)
|
||||
|
||||
var MaxProgSize int // Max cache size for JIT programs
|
||||
|
||||
var programs *lru.Cache // lru cache for the JIT programs.
|
||||
|
||||
func init() {
|
||||
SetJITCacheSize(defaultJitMaxCache)
|
||||
}
|
||||
|
||||
// SetJITCacheSize recreates the program cache with the max given size. Setting
|
||||
// a new cache is **not** thread safe. Use with caution.
|
||||
func SetJITCacheSize(size int) {
|
||||
programs, _ = lru.New(size)
|
||||
}
|
||||
|
||||
// GetProgram returns the program by id or nil when non-existent
|
||||
func GetProgram(id common.Hash) *Program {
|
||||
if p, ok := programs.Get(id); ok {
|
||||
return p.(*Program)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenProgramStatus returns the status of the given program id
|
||||
func GetProgramStatus(id common.Hash) progStatus {
|
||||
program := GetProgram(id)
|
||||
if program != nil {
|
||||
return progStatus(atomic.LoadInt32(&program.status))
|
||||
}
|
||||
|
||||
return progUnknown
|
||||
}
|
||||
|
||||
// Program is a compiled program for the JIT VM and holds all required for
|
||||
// running a compiled JIT program.
|
||||
type Program struct {
|
||||
Id common.Hash // Id of the program
|
||||
status int32 // status should be accessed atomically
|
||||
|
||||
contract *Contract
|
||||
|
||||
instructions []programInstruction // instruction set
|
||||
mapping map[uint64]uint64 // real PC mapping to array indices
|
||||
destinations map[uint64]struct{} // cached jump destinations
|
||||
|
||||
code []byte
|
||||
}
|
||||
|
||||
// NewProgram returns a new JIT program
|
||||
func NewProgram(code []byte) *Program {
|
||||
program := &Program{
|
||||
Id: crypto.Keccak256Hash(code),
|
||||
mapping: make(map[uint64]uint64),
|
||||
destinations: make(map[uint64]struct{}),
|
||||
code: code,
|
||||
}
|
||||
|
||||
programs.Add(program.Id, program)
|
||||
return program
|
||||
}
|
||||
|
||||
func (p *Program) addInstr(op OpCode, pc uint64, fn instrFn, data *big.Int) {
|
||||
// PUSH and DUP are a bit special. They all cost the same but we do want to have checking on stack push limit
|
||||
// PUSH is also allowed to calculate the same price for all PUSHes
|
||||
// DUP requirements are handled elsewhere (except for the stack limit check)
|
||||
baseOp := op
|
||||
if op >= PUSH1 && op <= PUSH32 {
|
||||
baseOp = PUSH1
|
||||
}
|
||||
if op >= DUP1 && op <= DUP16 {
|
||||
baseOp = DUP1
|
||||
}
|
||||
base := _baseCheck[baseOp]
|
||||
|
||||
returns := op == RETURN || op == SUICIDE || op == STOP
|
||||
instr := instruction{op, pc, fn, data, base.gas, base.stackPop, base.stackPush, returns}
|
||||
|
||||
p.instructions = append(p.instructions, instr)
|
||||
p.mapping[pc] = uint64(len(p.instructions) - 1)
|
||||
}
|
||||
|
||||
// CompileProgram compiles the given program and return an error when it fails
|
||||
func CompileProgram(program *Program) (err error) {
|
||||
if progStatus(atomic.LoadInt32(&program.status)) == progCompile {
|
||||
return nil
|
||||
}
|
||||
atomic.StoreInt32(&program.status, int32(progCompile))
|
||||
defer func() {
|
||||
if err != nil {
|
||||
atomic.StoreInt32(&program.status, int32(progError))
|
||||
} else {
|
||||
atomic.StoreInt32(&program.status, int32(progReady))
|
||||
}
|
||||
}()
|
||||
if glog.V(logger.Debug) {
|
||||
glog.Infof("compiling %x\n", program.Id[:4])
|
||||
tstart := time.Now()
|
||||
defer func() {
|
||||
glog.Infof("compiled %x instrc: %d time: %v\n", program.Id[:4], len(program.instructions), time.Since(tstart))
|
||||
}()
|
||||
}
|
||||
|
||||
// loop thru the opcodes and "compile" in to instructions
|
||||
for pc := uint64(0); pc < uint64(len(program.code)); pc++ {
|
||||
switch op := OpCode(program.code[pc]); op {
|
||||
case ADD:
|
||||
program.addInstr(op, pc, opAdd, nil)
|
||||
case SUB:
|
||||
program.addInstr(op, pc, opSub, nil)
|
||||
case MUL:
|
||||
program.addInstr(op, pc, opMul, nil)
|
||||
case DIV:
|
||||
program.addInstr(op, pc, opDiv, nil)
|
||||
case SDIV:
|
||||
program.addInstr(op, pc, opSdiv, nil)
|
||||
case MOD:
|
||||
program.addInstr(op, pc, opMod, nil)
|
||||
case SMOD:
|
||||
program.addInstr(op, pc, opSmod, nil)
|
||||
case EXP:
|
||||
program.addInstr(op, pc, opExp, nil)
|
||||
case SIGNEXTEND:
|
||||
program.addInstr(op, pc, opSignExtend, nil)
|
||||
case NOT:
|
||||
program.addInstr(op, pc, opNot, nil)
|
||||
case LT:
|
||||
program.addInstr(op, pc, opLt, nil)
|
||||
case GT:
|
||||
program.addInstr(op, pc, opGt, nil)
|
||||
case SLT:
|
||||
program.addInstr(op, pc, opSlt, nil)
|
||||
case SGT:
|
||||
program.addInstr(op, pc, opSgt, nil)
|
||||
case EQ:
|
||||
program.addInstr(op, pc, opEq, nil)
|
||||
case ISZERO:
|
||||
program.addInstr(op, pc, opIszero, nil)
|
||||
case AND:
|
||||
program.addInstr(op, pc, opAnd, nil)
|
||||
case OR:
|
||||
program.addInstr(op, pc, opOr, nil)
|
||||
case XOR:
|
||||
program.addInstr(op, pc, opXor, nil)
|
||||
case BYTE:
|
||||
program.addInstr(op, pc, opByte, nil)
|
||||
case ADDMOD:
|
||||
program.addInstr(op, pc, opAddmod, nil)
|
||||
case MULMOD:
|
||||
program.addInstr(op, pc, opMulmod, nil)
|
||||
case SHA3:
|
||||
program.addInstr(op, pc, opSha3, nil)
|
||||
case ADDRESS:
|
||||
program.addInstr(op, pc, opAddress, nil)
|
||||
case BALANCE:
|
||||
program.addInstr(op, pc, opBalance, nil)
|
||||
case ORIGIN:
|
||||
program.addInstr(op, pc, opOrigin, nil)
|
||||
case CALLER:
|
||||
program.addInstr(op, pc, opCaller, nil)
|
||||
case CALLVALUE:
|
||||
program.addInstr(op, pc, opCallValue, nil)
|
||||
case CALLDATALOAD:
|
||||
program.addInstr(op, pc, opCalldataLoad, nil)
|
||||
case CALLDATASIZE:
|
||||
program.addInstr(op, pc, opCalldataSize, nil)
|
||||
case CALLDATACOPY:
|
||||
program.addInstr(op, pc, opCalldataCopy, nil)
|
||||
case CODESIZE:
|
||||
program.addInstr(op, pc, opCodeSize, nil)
|
||||
case EXTCODESIZE:
|
||||
program.addInstr(op, pc, opExtCodeSize, nil)
|
||||
case CODECOPY:
|
||||
program.addInstr(op, pc, opCodeCopy, nil)
|
||||
case EXTCODECOPY:
|
||||
program.addInstr(op, pc, opExtCodeCopy, nil)
|
||||
case GASPRICE:
|
||||
program.addInstr(op, pc, opGasprice, nil)
|
||||
case BLOCKHASH:
|
||||
program.addInstr(op, pc, opBlockhash, nil)
|
||||
case COINBASE:
|
||||
program.addInstr(op, pc, opCoinbase, nil)
|
||||
case TIMESTAMP:
|
||||
program.addInstr(op, pc, opTimestamp, nil)
|
||||
case NUMBER:
|
||||
program.addInstr(op, pc, opNumber, nil)
|
||||
case DIFFICULTY:
|
||||
program.addInstr(op, pc, opDifficulty, nil)
|
||||
case GASLIMIT:
|
||||
program.addInstr(op, pc, opGasLimit, nil)
|
||||
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
|
||||
size := uint64(op - PUSH1 + 1)
|
||||
bytes := getData([]byte(program.code), new(big.Int).SetUint64(pc+1), new(big.Int).SetUint64(size))
|
||||
|
||||
program.addInstr(op, pc, opPush, common.Bytes2Big(bytes))
|
||||
|
||||
pc += size
|
||||
|
||||
case POP:
|
||||
program.addInstr(op, pc, opPop, nil)
|
||||
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
|
||||
program.addInstr(op, pc, opDup, big.NewInt(int64(op-DUP1+1)))
|
||||
case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
|
||||
program.addInstr(op, pc, opSwap, big.NewInt(int64(op-SWAP1+2)))
|
||||
case LOG0, LOG1, LOG2, LOG3, LOG4:
|
||||
program.addInstr(op, pc, opLog, big.NewInt(int64(op-LOG0)))
|
||||
case MLOAD:
|
||||
program.addInstr(op, pc, opMload, nil)
|
||||
case MSTORE:
|
||||
program.addInstr(op, pc, opMstore, nil)
|
||||
case MSTORE8:
|
||||
program.addInstr(op, pc, opMstore8, nil)
|
||||
case SLOAD:
|
||||
program.addInstr(op, pc, opSload, nil)
|
||||
case SSTORE:
|
||||
program.addInstr(op, pc, opSstore, nil)
|
||||
case JUMP:
|
||||
program.addInstr(op, pc, opJump, nil)
|
||||
case JUMPI:
|
||||
program.addInstr(op, pc, opJumpi, nil)
|
||||
case JUMPDEST:
|
||||
program.addInstr(op, pc, opJumpdest, nil)
|
||||
program.destinations[pc] = struct{}{}
|
||||
case PC:
|
||||
program.addInstr(op, pc, opPc, big.NewInt(int64(pc)))
|
||||
case MSIZE:
|
||||
program.addInstr(op, pc, opMsize, nil)
|
||||
case GAS:
|
||||
program.addInstr(op, pc, opGas, nil)
|
||||
case CREATE:
|
||||
program.addInstr(op, pc, opCreate, nil)
|
||||
case DELEGATECALL:
|
||||
// Instruction added regardless of homestead phase.
|
||||
// Homestead (and execution of the opcode) is checked during
|
||||
// runtime.
|
||||
program.addInstr(op, pc, opDelegateCall, nil)
|
||||
case CALL:
|
||||
program.addInstr(op, pc, opCall, nil)
|
||||
case CALLCODE:
|
||||
program.addInstr(op, pc, opCallCode, nil)
|
||||
case RETURN:
|
||||
program.addInstr(op, pc, opReturn, nil)
|
||||
case SUICIDE:
|
||||
program.addInstr(op, pc, opSuicide, nil)
|
||||
case STOP: // Stop the contract
|
||||
program.addInstr(op, pc, opStop, nil)
|
||||
default:
|
||||
program.addInstr(op, pc, nil, nil)
|
||||
}
|
||||
}
|
||||
|
||||
optimiseProgram(program)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunProgram runs the program given the environment and contract and returns an
|
||||
// error if the execution failed (non-consensus)
|
||||
func RunProgram(program *Program, env *Environment, contract *Contract, input []byte) ([]byte, error) {
|
||||
return runProgram(program, 0, NewMemory(), newstack(), env, contract, input)
|
||||
}
|
||||
|
||||
func runProgram(program *Program, pcstart uint64, mem *Memory, stack *Stack, env *Environment, contract *Contract, input []byte) ([]byte, error) {
|
||||
contract.Input = input
|
||||
|
||||
var (
|
||||
pc uint64 = program.mapping[pcstart]
|
||||
instrCount = 0
|
||||
)
|
||||
|
||||
if glog.V(logger.Debug) {
|
||||
glog.Infof("running JIT program %x\n", program.Id[:4])
|
||||
tstart := time.Now()
|
||||
defer func() {
|
||||
glog.Infof("JIT program %x done. time: %v instrc: %v\n", program.Id[:4], time.Since(tstart), instrCount)
|
||||
}()
|
||||
}
|
||||
|
||||
homestead := env.ChainConfig().IsHomestead(env.BlockNumber)
|
||||
for pc < uint64(len(program.instructions)) {
|
||||
instrCount++
|
||||
|
||||
instr := program.instructions[pc]
|
||||
if instr.Op() == DELEGATECALL && !homestead {
|
||||
return nil, fmt.Errorf("Invalid opcode 0x%x", instr.Op())
|
||||
}
|
||||
|
||||
ret, err := instr.do(program, &pc, env, contract, mem, stack)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if instr.halts() {
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
contract.Input = nil
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// validDest checks if the given destination is a valid one given the
|
||||
// destination table of the program
|
||||
func validDest(dests map[uint64]struct{}, dest *big.Int) bool {
|
||||
// PC cannot go beyond len(code) and certainly can't be bigger than 64bits.
|
||||
// Don't bother checking for JUMPDEST in that case.
|
||||
if dest.Cmp(bigMaxUint64) > 0 {
|
||||
return false
|
||||
}
|
||||
_, ok := dests[dest.Uint64()]
|
||||
return ok
|
||||
}
|
||||
|
||||
// jitCalculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for
|
||||
// the operation. This does not reduce gas or resizes the memory.
|
||||
func jitCalculateGasAndSize(env *Environment, contract *Contract, instr instruction, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) {
|
||||
var (
|
||||
gas = new(big.Int)
|
||||
newMemSize *big.Int = new(big.Int)
|
||||
)
|
||||
err := jitBaseCheck(instr, stack, gas)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// stack Check, memory resize & gas phase
|
||||
switch op := instr.op; op {
|
||||
case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
|
||||
n := int(op - SWAP1 + 2)
|
||||
err := stack.require(n)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
gas.Set(GasFastestStep)
|
||||
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
|
||||
n := int(op - DUP1 + 1)
|
||||
err := stack.require(n)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
gas.Set(GasFastestStep)
|
||||
case LOG0, LOG1, LOG2, LOG3, LOG4:
|
||||
n := int(op - LOG0)
|
||||
err := stack.require(n + 2)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
mSize, mStart := stack.data[stack.len()-2], stack.data[stack.len()-1]
|
||||
|
||||
add := new(big.Int)
|
||||
gas.Add(gas, params.LogGas)
|
||||
gas.Add(gas, add.Mul(big.NewInt(int64(n)), params.LogTopicGas))
|
||||
gas.Add(gas, add.Mul(mSize, params.LogDataGas))
|
||||
|
||||
newMemSize = calcMemSize(mStart, mSize)
|
||||
case EXP:
|
||||
gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), params.ExpByteGas))
|
||||
case SSTORE:
|
||||
err := stack.require(2)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var g *big.Int
|
||||
y, x := stack.data[stack.len()-2], stack.data[stack.len()-1]
|
||||
val := env.StateDB.GetState(contract.Address(), common.BigToHash(x))
|
||||
|
||||
// This checks for 3 scenario's and calculates gas accordingly
|
||||
// 1. From a zero-value address to a non-zero value (NEW VALUE)
|
||||
// 2. From a non-zero value address to a zero-value address (DELETE)
|
||||
// 3. From a non-zero to a non-zero (CHANGE)
|
||||
if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) {
|
||||
g = params.SstoreSetGas
|
||||
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
|
||||
env.StateDB.AddRefund(params.SstoreRefundGas)
|
||||
|
||||
g = params.SstoreClearGas
|
||||
} else {
|
||||
g = params.SstoreResetGas
|
||||
}
|
||||
gas.Set(g)
|
||||
case SUICIDE:
|
||||
if !env.StateDB.HasSuicided(contract.Address()) {
|
||||
env.StateDB.AddRefund(params.SuicideRefundGas)
|
||||
}
|
||||
case MLOAD:
|
||||
newMemSize = calcMemSize(stack.peek(), u256(32))
|
||||
case MSTORE8:
|
||||
newMemSize = calcMemSize(stack.peek(), u256(1))
|
||||
case MSTORE:
|
||||
newMemSize = calcMemSize(stack.peek(), u256(32))
|
||||
case RETURN:
|
||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
|
||||
case SHA3:
|
||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
|
||||
|
||||
words := toWordSize(stack.data[stack.len()-2])
|
||||
gas.Add(gas, words.Mul(words, params.Sha3WordGas))
|
||||
case CALLDATACOPY:
|
||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
|
||||
|
||||
words := toWordSize(stack.data[stack.len()-3])
|
||||
gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||
case CODECOPY:
|
||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
|
||||
|
||||
words := toWordSize(stack.data[stack.len()-3])
|
||||
gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||
case EXTCODECOPY:
|
||||
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4])
|
||||
|
||||
words := toWordSize(stack.data[stack.len()-4])
|
||||
gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||
|
||||
case CREATE:
|
||||
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3])
|
||||
case CALL, CALLCODE:
|
||||
gas.Add(gas, stack.data[stack.len()-1])
|
||||
|
||||
if op == CALL {
|
||||
if !env.StateDB.Exist(common.BigToAddress(stack.data[stack.len()-2])) {
|
||||
gas.Add(gas, params.CallNewAccountGas)
|
||||
}
|
||||
}
|
||||
|
||||
if len(stack.data[stack.len()-3].Bytes()) > 0 {
|
||||
gas.Add(gas, params.CallValueTransferGas)
|
||||
}
|
||||
|
||||
x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7])
|
||||
y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5])
|
||||
|
||||
newMemSize = common.BigMax(x, y)
|
||||
case DELEGATECALL:
|
||||
gas.Add(gas, stack.data[stack.len()-1])
|
||||
|
||||
x := calcMemSize(stack.data[stack.len()-5], stack.data[stack.len()-6])
|
||||
y := calcMemSize(stack.data[stack.len()-3], stack.data[stack.len()-4])
|
||||
|
||||
newMemSize = common.BigMax(x, y)
|
||||
}
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
|
||||
return newMemSize, gas, nil
|
||||
}
|
||||
|
||||
// jitBaseCheck is the same as baseCheck except it doesn't do the look up in the
|
||||
// gas table. This is done during compilation instead.
|
||||
func jitBaseCheck(instr instruction, stack *Stack, gas *big.Int) error {
|
||||
err := stack.require(instr.spop)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if instr.spush > 0 && stack.len()-instr.spop+instr.spush > int(params.StackLimit.Int64()) {
|
||||
return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit.Int64())
|
||||
}
|
||||
|
||||
// nil on gas means no base calculation
|
||||
if instr.gas == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
gas.Add(gas, instr.gas)
|
||||
|
||||
return nil
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
)
|
||||
|
||||
// optimeProgram optimises a JIT program creating segments out of program
|
||||
// instructions. Currently covered are multi-pushes and static jumps
|
||||
func optimiseProgram(program *Program) {
|
||||
var load []instruction
|
||||
|
||||
var (
|
||||
statsJump = 0
|
||||
statsPush = 0
|
||||
)
|
||||
|
||||
if glog.V(logger.Debug) {
|
||||
glog.Infof("optimising %x\n", program.Id[:4])
|
||||
tstart := time.Now()
|
||||
defer func() {
|
||||
glog.Infof("optimised %x done in %v with JMP: %d PSH: %d\n", program.Id[:4], time.Since(tstart), statsJump, statsPush)
|
||||
}()
|
||||
}
|
||||
|
||||
/*
|
||||
code := Parse(program.code)
|
||||
for _, test := range [][]OpCode{
|
||||
[]OpCode{PUSH, PUSH, ADD},
|
||||
[]OpCode{PUSH, PUSH, SUB},
|
||||
[]OpCode{PUSH, PUSH, MUL},
|
||||
[]OpCode{PUSH, PUSH, DIV},
|
||||
} {
|
||||
matchCount := 0
|
||||
MatchFn(code, test, func(i int) bool {
|
||||
matchCount++
|
||||
return true
|
||||
})
|
||||
fmt.Printf("found %d match count on: %v\n", matchCount, test)
|
||||
}
|
||||
*/
|
||||
|
||||
for i := 0; i < len(program.instructions); i++ {
|
||||
instr := program.instructions[i].(instruction)
|
||||
|
||||
switch {
|
||||
case instr.op.IsPush():
|
||||
load = append(load, instr)
|
||||
case instr.op.IsStaticJump():
|
||||
if len(load) == 0 {
|
||||
continue
|
||||
}
|
||||
// if the push load is greater than 1, finalise that
|
||||
// segment first
|
||||
if len(load) > 2 {
|
||||
seg, size := makePushSeg(load[:len(load)-1])
|
||||
program.instructions[i-size-1] = seg
|
||||
statsPush++
|
||||
}
|
||||
// create a segment consisting of a pre determined
|
||||
// jump, destination and validity.
|
||||
seg := makeStaticJumpSeg(load[len(load)-1].data, program)
|
||||
program.instructions[i-1] = seg
|
||||
statsJump++
|
||||
|
||||
load = nil
|
||||
default:
|
||||
// create a new N pushes segment
|
||||
if len(load) > 1 {
|
||||
seg, size := makePushSeg(load)
|
||||
program.instructions[i-size] = seg
|
||||
statsPush++
|
||||
}
|
||||
load = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// makePushSeg creates a new push segment from N amount of push instructions
|
||||
func makePushSeg(instrs []instruction) (pushSeg, int) {
|
||||
var (
|
||||
data []*big.Int
|
||||
gas = new(big.Int)
|
||||
)
|
||||
|
||||
for _, instr := range instrs {
|
||||
data = append(data, instr.data)
|
||||
gas.Add(gas, instr.gas)
|
||||
}
|
||||
|
||||
return pushSeg{data, gas}, len(instrs)
|
||||
}
|
||||
|
||||
// makeStaticJumpSeg creates a new static jump segment from a predefined
|
||||
// destination (PUSH, JUMP).
|
||||
func makeStaticJumpSeg(to *big.Int, program *Program) jumpSeg {
|
||||
gas := new(big.Int)
|
||||
gas.Add(gas, _baseCheck[PUSH1].gas)
|
||||
gas.Add(gas, _baseCheck[JUMP].gas)
|
||||
|
||||
contract := &Contract{Code: program.code}
|
||||
pos, err := jump(program.mapping, program.destinations, contract, to)
|
||||
return jumpSeg{pos, err, gas}
|
||||
}
|
@ -1,160 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
const maxRun = 1000
|
||||
|
||||
func TestSegmenting(t *testing.T) {
|
||||
prog := NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, 0x0})
|
||||
err := CompileProgram(prog)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if instr, ok := prog.instructions[0].(pushSeg); ok {
|
||||
if len(instr.data) != 2 {
|
||||
t.Error("expected 2 element width pushSegment, got", len(instr.data))
|
||||
}
|
||||
} else {
|
||||
t.Errorf("expected instr[0] to be a pushSeg, got %T", prog.instructions[0])
|
||||
}
|
||||
|
||||
prog = NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(JUMP)})
|
||||
err = CompileProgram(prog)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, ok := prog.instructions[1].(jumpSeg); ok {
|
||||
} else {
|
||||
t.Errorf("expected instr[1] to be jumpSeg, got %T", prog.instructions[1])
|
||||
}
|
||||
|
||||
prog = NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(JUMP)})
|
||||
err = CompileProgram(prog)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if instr, ok := prog.instructions[0].(pushSeg); ok {
|
||||
if len(instr.data) != 2 {
|
||||
t.Error("expected 2 element width pushSegment, got", len(instr.data))
|
||||
}
|
||||
} else {
|
||||
t.Errorf("expected instr[0] to be a pushSeg, got %T", prog.instructions[0])
|
||||
}
|
||||
if _, ok := prog.instructions[2].(jumpSeg); ok {
|
||||
} else {
|
||||
t.Errorf("expected instr[1] to be jumpSeg, got %T", prog.instructions[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompiling(t *testing.T) {
|
||||
prog := NewProgram([]byte{0x60, 0x10})
|
||||
err := CompileProgram(prog)
|
||||
if err != nil {
|
||||
t.Error("didn't expect compile error")
|
||||
}
|
||||
|
||||
if len(prog.instructions) != 1 {
|
||||
t.Error("expected 1 compiled instruction, got", len(prog.instructions))
|
||||
}
|
||||
}
|
||||
|
||||
func TestResetInput(t *testing.T) {
|
||||
var sender account
|
||||
|
||||
env := NewEnvironment(Context{}, nil, params.TestChainConfig, Config{})
|
||||
contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000))
|
||||
contract.CodeAddr = &common.Address{}
|
||||
|
||||
program := NewProgram([]byte{})
|
||||
RunProgram(program, env, contract, []byte{0xbe, 0xef})
|
||||
if contract.Input != nil {
|
||||
t.Errorf("expected input to be nil, got %x", contract.Input)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPcMappingToInstruction(t *testing.T) {
|
||||
program := NewProgram([]byte{byte(PUSH2), 0xbe, 0xef, byte(ADD)})
|
||||
CompileProgram(program)
|
||||
if program.mapping[3] != 1 {
|
||||
t.Error("expected mapping PC 4 to me instr no. 2, got", program.mapping[4])
|
||||
}
|
||||
}
|
||||
|
||||
var benchmarks = map[string]vmBench{
|
||||
"pushes": vmBench{
|
||||
false, false, false,
|
||||
common.Hex2Bytes("600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01"), nil,
|
||||
},
|
||||
}
|
||||
|
||||
func BenchmarkPushes(b *testing.B) {
|
||||
runVmBench(benchmarks["pushes"], b)
|
||||
}
|
||||
|
||||
type vmBench struct {
|
||||
precompile bool // compile prior to executing
|
||||
nojit bool // ignore jit (sets DisbaleJit = true
|
||||
forcejit bool // forces the jit, precompile is ignored
|
||||
|
||||
code []byte
|
||||
input []byte
|
||||
}
|
||||
|
||||
type account struct{}
|
||||
|
||||
func (account) SubBalance(amount *big.Int) {}
|
||||
func (account) AddBalance(amount *big.Int) {}
|
||||
func (account) SetAddress(common.Address) {}
|
||||
func (account) Value() *big.Int { return nil }
|
||||
func (account) SetBalance(*big.Int) {}
|
||||
func (account) SetNonce(uint64) {}
|
||||
func (account) Balance() *big.Int { return nil }
|
||||
func (account) Address() common.Address { return common.Address{} }
|
||||
func (account) ReturnGas(*big.Int) {}
|
||||
func (account) SetCode(common.Hash, []byte) {}
|
||||
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
|
||||
|
||||
func runVmBench(test vmBench, b *testing.B) {
|
||||
var sender account
|
||||
|
||||
if test.precompile && !test.forcejit {
|
||||
NewProgram(test.code)
|
||||
}
|
||||
env := NewEnvironment(Context{}, nil, params.TestChainConfig, Config{EnableJit: !test.nojit, ForceJit: test.forcejit})
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
context := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000))
|
||||
context.Code = test.code
|
||||
context.CodeAddr = &common.Address{}
|
||||
_, err := env.EVM().Run(context, test.input)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
b.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
// Parse parses all opcodes from the given code byte slice. This function
|
||||
// performs no error checking and may return non-existing opcodes.
|
||||
func Parse(code []byte) (opcodes []OpCode) {
|
||||
for pc := uint64(0); pc < uint64(len(code)); pc++ {
|
||||
op := OpCode(code[pc])
|
||||
|
||||
switch op {
|
||||
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
|
||||
a := uint64(op) - uint64(PUSH1) + 1
|
||||
pc += a
|
||||
opcodes = append(opcodes, PUSH)
|
||||
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
|
||||
opcodes = append(opcodes, DUP)
|
||||
case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
|
||||
opcodes = append(opcodes, SWAP)
|
||||
default:
|
||||
opcodes = append(opcodes, op)
|
||||
}
|
||||
}
|
||||
|
||||
return opcodes
|
||||
}
|
||||
|
||||
// MatchFn searcher for match in the given input and calls matcheFn if it finds
|
||||
// an appropriate match. matcherFn yields the starting position in the input.
|
||||
// MatchFn will continue to search for a match until it reaches the end of the
|
||||
// buffer or if matcherFn return false.
|
||||
func MatchFn(input, match []OpCode, matcherFn func(int) bool) {
|
||||
// short circuit if either input or match is empty or if the match is
|
||||
// greater than the input
|
||||
if len(input) == 0 || len(match) == 0 || len(match) > len(input) {
|
||||
return
|
||||
}
|
||||
|
||||
main:
|
||||
for i, op := range input[:len(input)+1-len(match)] {
|
||||
// match first opcode and continue search
|
||||
if op == match[0] {
|
||||
for j := 1; j < len(match); j++ {
|
||||
if input[i+j] != match[j] {
|
||||
continue main
|
||||
}
|
||||
}
|
||||
// check for abort instruction
|
||||
if !matcherFn(i) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
import "testing"
|
||||
|
||||
type matchTest struct {
|
||||
input []OpCode
|
||||
match []OpCode
|
||||
matches int
|
||||
}
|
||||
|
||||
func TestMatchFn(t *testing.T) {
|
||||
tests := []matchTest{
|
||||
matchTest{
|
||||
[]OpCode{PUSH1, PUSH1, MSTORE, JUMP},
|
||||
[]OpCode{PUSH1, MSTORE},
|
||||
1,
|
||||
},
|
||||
matchTest{
|
||||
[]OpCode{PUSH1, PUSH1, MSTORE, JUMP},
|
||||
[]OpCode{PUSH1, MSTORE, PUSH1},
|
||||
0,
|
||||
},
|
||||
matchTest{
|
||||
[]OpCode{},
|
||||
[]OpCode{PUSH1},
|
||||
0,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
var matchCount int
|
||||
MatchFn(test.input, test.match, func(i int) bool {
|
||||
matchCount++
|
||||
return true
|
||||
})
|
||||
if matchCount != test.matches {
|
||||
t.Errorf("match count failed on test[%d]: expected %d matches, got %d", i, test.matches, matchCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type parseTest struct {
|
||||
base OpCode
|
||||
size int
|
||||
output OpCode
|
||||
}
|
||||
|
||||
func TestParser(t *testing.T) {
|
||||
tests := []parseTest{
|
||||
parseTest{PUSH1, 32, PUSH},
|
||||
parseTest{DUP1, 16, DUP},
|
||||
parseTest{SWAP1, 16, SWAP},
|
||||
parseTest{MSTORE, 1, MSTORE},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
for i := 0; i < test.size; i++ {
|
||||
code := append([]byte{byte(byte(test.base) + byte(i))}, make([]byte, i+1)...)
|
||||
output := Parse(code)
|
||||
if len(output) == 0 {
|
||||
t.Fatal("empty output")
|
||||
}
|
||||
if output[0] != test.output {
|
||||
t.Errorf("%v failed: expected %v but got %v", test.base+OpCode(i), test.output, output[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -22,152 +22,838 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
type jumpPtr struct {
|
||||
fn instrFn
|
||||
type (
|
||||
executionFunc func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)
|
||||
gasFunc func(params.GasTable, *EVM, *Contract, *Stack, *Memory, *big.Int) *big.Int
|
||||
stackValidationFunc func(*Stack) error
|
||||
memorySizeFunc func(*Stack) *big.Int
|
||||
)
|
||||
|
||||
type operation struct {
|
||||
// op is the operation function
|
||||
execute executionFunc
|
||||
// gasCost is the gas function and returns the gas required for execution
|
||||
gasCost gasFunc
|
||||
// validateStack validates the stack (size) for the operation
|
||||
validateStack stackValidationFunc
|
||||
// memorySize returns the memory size required for the operation
|
||||
memorySize memorySizeFunc
|
||||
// halts indicates whether the operation shoult halt further execution
|
||||
// and return
|
||||
halts bool
|
||||
// jumps indicates whether operation made a jump. This prevents the program
|
||||
// counter from further incrementing.
|
||||
jumps bool
|
||||
// valid is used to check whether the retrieved operation is valid and known
|
||||
valid bool
|
||||
}
|
||||
|
||||
type vmJumpTable [256]jumpPtr
|
||||
var defaultJumpTable = NewJumpTable()
|
||||
|
||||
func newJumpTable(ruleset *params.ChainConfig, blockNumber *big.Int) vmJumpTable {
|
||||
var jumpTable vmJumpTable
|
||||
func NewJumpTable() [256]operation {
|
||||
return [256]operation{
|
||||
ADD: operation{
|
||||
execute: opAdd,
|
||||
gasCost: constGasFunc(GasFastestStep),
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
},
|
||||
SUB: operation{
|
||||
execute: opSub,
|
||||
gasCost: constGasFunc(GasFastestStep),
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
},
|
||||
MUL: operation{
|
||||
execute: opMul,
|
||||
gasCost: constGasFunc(GasFastStep),
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
},
|
||||
DIV: operation{
|
||||
execute: opDiv,
|
||||
gasCost: constGasFunc(GasFastStep),
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
},
|
||||
SDIV: operation{
|
||||
execute: opSdiv,
|
||||
gasCost: constGasFunc(GasFastStep),
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
},
|
||||
MOD: operation{
|
||||
execute: opMod,
|
||||
gasCost: constGasFunc(GasFastStep),
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
},
|
||||
SMOD: operation{
|
||||
execute: opSmod,
|
||||
gasCost: constGasFunc(GasFastStep),
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
},
|
||||
EXP: operation{
|
||||
execute: opExp,
|
||||
gasCost: gasExp,
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
},
|
||||
SIGNEXTEND: operation{
|
||||
execute: opSignExtend,
|
||||
gasCost: constGasFunc(GasFastStep),
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
},
|
||||
NOT: operation{
|
||||
execute: opNot,
|
||||
gasCost: constGasFunc(GasFastestStep),
|
||||
validateStack: makeStackFunc(1, 1),
|
||||
valid: true,
|
||||
},
|
||||
LT: operation{
|
||||
execute: opLt,
|
||||
gasCost: constGasFunc(GasFastestStep),
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
},
|
||||
GT: operation{
|
||||
execute: opGt,
|
||||
gasCost: constGasFunc(GasFastestStep),
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
},
|
||||
SLT: operation{
|
||||
execute: opSlt,
|
||||
gasCost: constGasFunc(GasFastestStep),
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
},
|
||||
SGT: operation{
|
||||
execute: opSgt,
|
||||
gasCost: constGasFunc(GasFastestStep),
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
},
|
||||
EQ: operation{
|
||||
execute: opEq,
|
||||
gasCost: constGasFunc(GasFastestStep),
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
},
|
||||
ISZERO: operation{
|
||||
execute: opIszero,
|
||||
gasCost: constGasFunc(GasFastestStep),
|
||||
validateStack: makeStackFunc(1, 1),
|
||||
valid: true,
|
||||
},
|
||||
AND: operation{
|
||||
execute: opAnd,
|
||||
gasCost: constGasFunc(GasFastestStep),
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
},
|
||||
OR: operation{
|
||||
execute: opOr,
|
||||
gasCost: constGasFunc(GasFastestStep),
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
},
|
||||
XOR: operation{
|
||||
execute: opXor,
|
||||
gasCost: constGasFunc(GasFastestStep),
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
},
|
||||
BYTE: operation{
|
||||
execute: opByte,
|
||||
gasCost: constGasFunc(GasFastestStep),
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
},
|
||||
ADDMOD: operation{
|
||||
execute: opAddmod,
|
||||
gasCost: constGasFunc(GasMidStep),
|
||||
validateStack: makeStackFunc(3, 1),
|
||||
valid: true,
|
||||
},
|
||||
MULMOD: operation{
|
||||
execute: opMulmod,
|
||||
gasCost: constGasFunc(GasMidStep),
|
||||
validateStack: makeStackFunc(3, 1),
|
||||
valid: true,
|
||||
},
|
||||
SHA3: operation{
|
||||
execute: opSha3,
|
||||
gasCost: gasSha3,
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
memorySize: memorySha3,
|
||||
valid: true,
|
||||
},
|
||||
ADDRESS: operation{
|
||||
execute: opAddress,
|
||||
gasCost: constGasFunc(GasQuickStep),
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
BALANCE: operation{
|
||||
execute: opBalance,
|
||||
gasCost: gasBalance,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
ORIGIN: operation{
|
||||
execute: opOrigin,
|
||||
gasCost: constGasFunc(GasQuickStep),
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
CALLER: operation{
|
||||
execute: opCaller,
|
||||
gasCost: constGasFunc(GasQuickStep),
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
CALLVALUE: operation{
|
||||
execute: opCallValue,
|
||||
gasCost: constGasFunc(GasQuickStep),
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
CALLDATALOAD: operation{
|
||||
execute: opCalldataLoad,
|
||||
gasCost: constGasFunc(GasFastestStep),
|
||||
validateStack: makeStackFunc(1, 1),
|
||||
valid: true,
|
||||
},
|
||||
CALLDATASIZE: operation{
|
||||
execute: opCalldataSize,
|
||||
gasCost: constGasFunc(GasQuickStep),
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
CALLDATACOPY: operation{
|
||||
execute: opCalldataCopy,
|
||||
gasCost: gasCalldataCopy,
|
||||
validateStack: makeStackFunc(3, 1),
|
||||
memorySize: memoryCalldataCopy,
|
||||
valid: true,
|
||||
},
|
||||
CODESIZE: operation{
|
||||
execute: opCodeSize,
|
||||
gasCost: constGasFunc(GasQuickStep),
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
EXTCODESIZE: operation{
|
||||
execute: opExtCodeSize,
|
||||
gasCost: gasExtCodeSize,
|
||||
validateStack: makeStackFunc(1, 1),
|
||||
valid: true,
|
||||
},
|
||||
CODECOPY: operation{
|
||||
execute: opCodeCopy,
|
||||
gasCost: gasCodeCopy,
|
||||
validateStack: makeStackFunc(3, 0),
|
||||
memorySize: memoryCodeCopy,
|
||||
valid: true,
|
||||
},
|
||||
EXTCODECOPY: operation{
|
||||
execute: opExtCodeCopy,
|
||||
gasCost: gasExtCodeCopy,
|
||||
validateStack: makeStackFunc(4, 0),
|
||||
memorySize: memoryExtCodeCopy,
|
||||
valid: true,
|
||||
},
|
||||
GASPRICE: operation{
|
||||
execute: opGasprice,
|
||||
gasCost: constGasFunc(GasQuickStep),
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
BLOCKHASH: operation{
|
||||
execute: opBlockhash,
|
||||
gasCost: constGasFunc(GasExtStep),
|
||||
validateStack: makeStackFunc(1, 1),
|
||||
valid: true,
|
||||
},
|
||||
COINBASE: operation{
|
||||
execute: opCoinbase,
|
||||
gasCost: constGasFunc(GasQuickStep),
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
TIMESTAMP: operation{
|
||||
execute: opTimestamp,
|
||||
gasCost: constGasFunc(GasQuickStep),
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
NUMBER: operation{
|
||||
execute: opNumber,
|
||||
gasCost: constGasFunc(GasQuickStep),
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
DIFFICULTY: operation{
|
||||
execute: opDifficulty,
|
||||
gasCost: constGasFunc(GasQuickStep),
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
GASLIMIT: operation{
|
||||
execute: opGasLimit,
|
||||
gasCost: constGasFunc(GasQuickStep),
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
POP: operation{
|
||||
execute: opPop,
|
||||
gasCost: constGasFunc(GasQuickStep),
|
||||
validateStack: makeStackFunc(1, 0),
|
||||
valid: true,
|
||||
},
|
||||
MLOAD: operation{
|
||||
execute: opMload,
|
||||
gasCost: gasMLoad,
|
||||
validateStack: makeStackFunc(1, 1),
|
||||
memorySize: memoryMLoad,
|
||||
valid: true,
|
||||
},
|
||||
MSTORE: operation{
|
||||
execute: opMstore,
|
||||
gasCost: gasMStore,
|
||||
validateStack: makeStackFunc(2, 0),
|
||||
memorySize: memoryMStore,
|
||||
valid: true,
|
||||
},
|
||||
MSTORE8: operation{
|
||||
execute: opMstore8,
|
||||
gasCost: gasMStore8,
|
||||
memorySize: memoryMStore8,
|
||||
validateStack: makeStackFunc(2, 0),
|
||||
|
||||
// when initialising a new VM execution we must first check the homestead
|
||||
// changes.
|
||||
if ruleset.IsHomestead(blockNumber) {
|
||||
jumpTable[DELEGATECALL] = jumpPtr{opDelegateCall, true}
|
||||
valid: true,
|
||||
},
|
||||
SLOAD: operation{
|
||||
execute: opSload,
|
||||
gasCost: gasSLoad,
|
||||
validateStack: makeStackFunc(1, 1),
|
||||
valid: true,
|
||||
},
|
||||
SSTORE: operation{
|
||||
execute: opSstore,
|
||||
gasCost: gasSStore,
|
||||
validateStack: makeStackFunc(2, 0),
|
||||
valid: true,
|
||||
},
|
||||
JUMPDEST: operation{
|
||||
execute: opJumpdest,
|
||||
gasCost: constGasFunc(params.JumpdestGas),
|
||||
validateStack: makeStackFunc(0, 0),
|
||||
valid: true,
|
||||
},
|
||||
PC: operation{
|
||||
execute: opPc,
|
||||
gasCost: constGasFunc(GasQuickStep),
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
MSIZE: operation{
|
||||
execute: opMsize,
|
||||
gasCost: constGasFunc(GasQuickStep),
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
GAS: operation{
|
||||
execute: opGas,
|
||||
gasCost: constGasFunc(GasQuickStep),
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
CREATE: operation{
|
||||
execute: opCreate,
|
||||
gasCost: gasCreate,
|
||||
validateStack: makeStackFunc(3, 1),
|
||||
memorySize: memoryCreate,
|
||||
valid: true,
|
||||
},
|
||||
CALL: operation{
|
||||
execute: opCall,
|
||||
gasCost: gasCall,
|
||||
validateStack: makeStackFunc(7, 1),
|
||||
memorySize: memoryCall,
|
||||
valid: true,
|
||||
},
|
||||
CALLCODE: operation{
|
||||
execute: opCallCode,
|
||||
gasCost: gasCallCode,
|
||||
validateStack: makeStackFunc(7, 1),
|
||||
memorySize: memoryCall,
|
||||
valid: true,
|
||||
},
|
||||
DELEGATECALL: operation{
|
||||
execute: opDelegateCall,
|
||||
gasCost: gasDelegateCall,
|
||||
validateStack: makeStackFunc(6, 1),
|
||||
memorySize: memoryDelegateCall,
|
||||
valid: true,
|
||||
},
|
||||
RETURN: operation{
|
||||
execute: opReturn,
|
||||
gasCost: gasReturn,
|
||||
validateStack: makeStackFunc(2, 0),
|
||||
memorySize: memoryReturn,
|
||||
halts: true,
|
||||
valid: true,
|
||||
},
|
||||
SUICIDE: operation{
|
||||
execute: opSuicide,
|
||||
gasCost: gasSuicide,
|
||||
validateStack: makeStackFunc(1, 0),
|
||||
halts: true,
|
||||
valid: true,
|
||||
},
|
||||
JUMP: operation{
|
||||
execute: opJump,
|
||||
gasCost: constGasFunc(GasMidStep),
|
||||
validateStack: makeStackFunc(1, 0),
|
||||
jumps: true,
|
||||
valid: true,
|
||||
},
|
||||
JUMPI: operation{
|
||||
execute: opJumpi,
|
||||
gasCost: constGasFunc(GasSlowStep),
|
||||
validateStack: makeStackFunc(2, 0),
|
||||
jumps: true,
|
||||
valid: true,
|
||||
},
|
||||
STOP: operation{
|
||||
execute: opStop,
|
||||
gasCost: constGasFunc(Zero),
|
||||
validateStack: makeStackFunc(0, 0),
|
||||
halts: true,
|
||||
valid: true,
|
||||
},
|
||||
LOG0: operation{
|
||||
execute: makeLog(0),
|
||||
gasCost: makeGasLog(0),
|
||||
validateStack: makeStackFunc(2, 0),
|
||||
memorySize: memoryLog,
|
||||
valid: true,
|
||||
},
|
||||
LOG1: operation{
|
||||
execute: makeLog(1),
|
||||
gasCost: makeGasLog(1),
|
||||
validateStack: makeStackFunc(3, 0),
|
||||
memorySize: memoryLog,
|
||||
valid: true,
|
||||
},
|
||||
LOG2: operation{
|
||||
execute: makeLog(2),
|
||||
gasCost: makeGasLog(2),
|
||||
validateStack: makeStackFunc(4, 0),
|
||||
memorySize: memoryLog,
|
||||
valid: true,
|
||||
},
|
||||
LOG3: operation{
|
||||
execute: makeLog(3),
|
||||
gasCost: makeGasLog(3),
|
||||
validateStack: makeStackFunc(5, 0),
|
||||
memorySize: memoryLog,
|
||||
valid: true,
|
||||
},
|
||||
LOG4: operation{
|
||||
execute: makeLog(4),
|
||||
gasCost: makeGasLog(4),
|
||||
validateStack: makeStackFunc(6, 0),
|
||||
memorySize: memoryLog,
|
||||
valid: true,
|
||||
},
|
||||
SWAP1: operation{
|
||||
execute: makeSwap(1),
|
||||
gasCost: gasSwap,
|
||||
validateStack: makeStackFunc(2, 0),
|
||||
valid: true,
|
||||
},
|
||||
SWAP2: operation{
|
||||
execute: makeSwap(2),
|
||||
gasCost: gasSwap,
|
||||
validateStack: makeStackFunc(3, 0),
|
||||
valid: true,
|
||||
},
|
||||
SWAP3: operation{
|
||||
execute: makeSwap(3),
|
||||
gasCost: gasSwap,
|
||||
validateStack: makeStackFunc(4, 0),
|
||||
valid: true,
|
||||
},
|
||||
SWAP4: operation{
|
||||
execute: makeSwap(4),
|
||||
gasCost: gasSwap,
|
||||
validateStack: makeStackFunc(5, 0),
|
||||
valid: true,
|
||||
},
|
||||
SWAP5: operation{
|
||||
execute: makeSwap(5),
|
||||
gasCost: gasSwap,
|
||||
validateStack: makeStackFunc(6, 0),
|
||||
valid: true,
|
||||
},
|
||||
SWAP6: operation{
|
||||
execute: makeSwap(6),
|
||||
gasCost: gasSwap,
|
||||
validateStack: makeStackFunc(7, 0),
|
||||
valid: true,
|
||||
},
|
||||
SWAP7: operation{
|
||||
execute: makeSwap(7),
|
||||
gasCost: gasSwap,
|
||||
validateStack: makeStackFunc(8, 0),
|
||||
valid: true,
|
||||
},
|
||||
SWAP8: operation{
|
||||
execute: makeSwap(8),
|
||||
gasCost: gasSwap,
|
||||
validateStack: makeStackFunc(9, 0),
|
||||
valid: true,
|
||||
},
|
||||
SWAP9: operation{
|
||||
execute: makeSwap(9),
|
||||
gasCost: gasSwap,
|
||||
validateStack: makeStackFunc(10, 0),
|
||||
valid: true,
|
||||
},
|
||||
SWAP10: operation{
|
||||
execute: makeSwap(10),
|
||||
gasCost: gasSwap,
|
||||
validateStack: makeStackFunc(11, 0),
|
||||
valid: true,
|
||||
},
|
||||
SWAP11: operation{
|
||||
execute: makeSwap(11),
|
||||
gasCost: gasSwap,
|
||||
validateStack: makeStackFunc(12, 0),
|
||||
valid: true,
|
||||
},
|
||||
SWAP12: operation{
|
||||
execute: makeSwap(12),
|
||||
gasCost: gasSwap,
|
||||
validateStack: makeStackFunc(13, 0),
|
||||
valid: true,
|
||||
},
|
||||
SWAP13: operation{
|
||||
execute: makeSwap(13),
|
||||
gasCost: gasSwap,
|
||||
validateStack: makeStackFunc(14, 0),
|
||||
valid: true,
|
||||
},
|
||||
SWAP14: operation{
|
||||
execute: makeSwap(14),
|
||||
gasCost: gasSwap,
|
||||
validateStack: makeStackFunc(15, 0),
|
||||
valid: true,
|
||||
},
|
||||
SWAP15: operation{
|
||||
execute: makeSwap(15),
|
||||
gasCost: gasSwap,
|
||||
validateStack: makeStackFunc(16, 0),
|
||||
valid: true,
|
||||
},
|
||||
SWAP16: operation{
|
||||
execute: makeSwap(16),
|
||||
gasCost: gasSwap,
|
||||
validateStack: makeStackFunc(17, 0),
|
||||
valid: true,
|
||||
},
|
||||
PUSH1: operation{
|
||||
execute: makePush(1, big.NewInt(1)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH2: operation{
|
||||
execute: makePush(2, big.NewInt(2)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH3: operation{
|
||||
execute: makePush(3, big.NewInt(3)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH4: operation{
|
||||
execute: makePush(4, big.NewInt(4)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH5: operation{
|
||||
execute: makePush(5, big.NewInt(5)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH6: operation{
|
||||
execute: makePush(6, big.NewInt(6)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH7: operation{
|
||||
execute: makePush(7, big.NewInt(7)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH8: operation{
|
||||
execute: makePush(8, big.NewInt(8)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH9: operation{
|
||||
execute: makePush(9, big.NewInt(9)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH10: operation{
|
||||
execute: makePush(10, big.NewInt(10)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH11: operation{
|
||||
execute: makePush(11, big.NewInt(11)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH12: operation{
|
||||
execute: makePush(12, big.NewInt(12)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH13: operation{
|
||||
execute: makePush(13, big.NewInt(13)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH14: operation{
|
||||
execute: makePush(14, big.NewInt(14)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH15: operation{
|
||||
execute: makePush(15, big.NewInt(15)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH16: operation{
|
||||
execute: makePush(16, big.NewInt(16)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH17: operation{
|
||||
execute: makePush(17, big.NewInt(17)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH18: operation{
|
||||
execute: makePush(18, big.NewInt(18)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH19: operation{
|
||||
execute: makePush(19, big.NewInt(19)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH20: operation{
|
||||
execute: makePush(20, big.NewInt(20)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH21: operation{
|
||||
execute: makePush(21, big.NewInt(21)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH22: operation{
|
||||
execute: makePush(22, big.NewInt(22)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH23: operation{
|
||||
execute: makePush(23, big.NewInt(23)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH24: operation{
|
||||
execute: makePush(24, big.NewInt(24)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH25: operation{
|
||||
execute: makePush(25, big.NewInt(25)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH26: operation{
|
||||
execute: makePush(26, big.NewInt(26)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH27: operation{
|
||||
execute: makePush(27, big.NewInt(27)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH28: operation{
|
||||
execute: makePush(28, big.NewInt(28)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH29: operation{
|
||||
execute: makePush(29, big.NewInt(29)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH30: operation{
|
||||
execute: makePush(30, big.NewInt(30)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH31: operation{
|
||||
execute: makePush(31, big.NewInt(31)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
PUSH32: operation{
|
||||
execute: makePush(32, big.NewInt(32)),
|
||||
gasCost: gasPush,
|
||||
validateStack: makeStackFunc(0, 1),
|
||||
valid: true,
|
||||
},
|
||||
DUP1: operation{
|
||||
execute: makeDup(1),
|
||||
gasCost: gasDup,
|
||||
validateStack: makeStackFunc(1, 1),
|
||||
valid: true,
|
||||
},
|
||||
DUP2: operation{
|
||||
execute: makeDup(2),
|
||||
gasCost: gasDup,
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
},
|
||||
DUP3: operation{
|
||||
execute: makeDup(3),
|
||||
gasCost: gasDup,
|
||||
validateStack: makeStackFunc(3, 1),
|
||||
valid: true,
|
||||
},
|
||||
DUP4: operation{
|
||||
execute: makeDup(4),
|
||||
gasCost: gasDup,
|
||||
validateStack: makeStackFunc(4, 1),
|
||||
valid: true,
|
||||
},
|
||||
DUP5: operation{
|
||||
execute: makeDup(5),
|
||||
gasCost: gasDup,
|
||||
validateStack: makeStackFunc(5, 1),
|
||||
valid: true,
|
||||
},
|
||||
DUP6: operation{
|
||||
execute: makeDup(6),
|
||||
gasCost: gasDup,
|
||||
validateStack: makeStackFunc(6, 1),
|
||||
valid: true,
|
||||
},
|
||||
DUP7: operation{
|
||||
execute: makeDup(7),
|
||||
gasCost: gasDup,
|
||||
validateStack: makeStackFunc(7, 1),
|
||||
valid: true,
|
||||
},
|
||||
DUP8: operation{
|
||||
execute: makeDup(8),
|
||||
gasCost: gasDup,
|
||||
validateStack: makeStackFunc(8, 1),
|
||||
valid: true,
|
||||
},
|
||||
DUP9: operation{
|
||||
execute: makeDup(9),
|
||||
gasCost: gasDup,
|
||||
validateStack: makeStackFunc(9, 1),
|
||||
valid: true,
|
||||
},
|
||||
DUP10: operation{
|
||||
execute: makeDup(10),
|
||||
gasCost: gasDup,
|
||||
validateStack: makeStackFunc(10, 1),
|
||||
valid: true,
|
||||
},
|
||||
DUP11: operation{
|
||||
execute: makeDup(11),
|
||||
gasCost: gasDup,
|
||||
validateStack: makeStackFunc(11, 1),
|
||||
valid: true,
|
||||
},
|
||||
DUP12: operation{
|
||||
execute: makeDup(12),
|
||||
gasCost: gasDup,
|
||||
validateStack: makeStackFunc(12, 1),
|
||||
valid: true,
|
||||
},
|
||||
DUP13: operation{
|
||||
execute: makeDup(13),
|
||||
gasCost: gasDup,
|
||||
validateStack: makeStackFunc(13, 1),
|
||||
valid: true,
|
||||
},
|
||||
DUP14: operation{
|
||||
execute: makeDup(14),
|
||||
gasCost: gasDup,
|
||||
validateStack: makeStackFunc(14, 1),
|
||||
valid: true,
|
||||
},
|
||||
DUP15: operation{
|
||||
execute: makeDup(15),
|
||||
gasCost: gasDup,
|
||||
validateStack: makeStackFunc(15, 1),
|
||||
valid: true,
|
||||
},
|
||||
DUP16: operation{
|
||||
execute: makeDup(16),
|
||||
gasCost: gasDup,
|
||||
validateStack: makeStackFunc(16, 1),
|
||||
valid: true,
|
||||
},
|
||||
}
|
||||
|
||||
jumpTable[ADD] = jumpPtr{opAdd, true}
|
||||
jumpTable[SUB] = jumpPtr{opSub, true}
|
||||
jumpTable[MUL] = jumpPtr{opMul, true}
|
||||
jumpTable[DIV] = jumpPtr{opDiv, true}
|
||||
jumpTable[SDIV] = jumpPtr{opSdiv, true}
|
||||
jumpTable[MOD] = jumpPtr{opMod, true}
|
||||
jumpTable[SMOD] = jumpPtr{opSmod, true}
|
||||
jumpTable[EXP] = jumpPtr{opExp, true}
|
||||
jumpTable[SIGNEXTEND] = jumpPtr{opSignExtend, true}
|
||||
jumpTable[NOT] = jumpPtr{opNot, true}
|
||||
jumpTable[LT] = jumpPtr{opLt, true}
|
||||
jumpTable[GT] = jumpPtr{opGt, true}
|
||||
jumpTable[SLT] = jumpPtr{opSlt, true}
|
||||
jumpTable[SGT] = jumpPtr{opSgt, true}
|
||||
jumpTable[EQ] = jumpPtr{opEq, true}
|
||||
jumpTable[ISZERO] = jumpPtr{opIszero, true}
|
||||
jumpTable[AND] = jumpPtr{opAnd, true}
|
||||
jumpTable[OR] = jumpPtr{opOr, true}
|
||||
jumpTable[XOR] = jumpPtr{opXor, true}
|
||||
jumpTable[BYTE] = jumpPtr{opByte, true}
|
||||
jumpTable[ADDMOD] = jumpPtr{opAddmod, true}
|
||||
jumpTable[MULMOD] = jumpPtr{opMulmod, true}
|
||||
jumpTable[SHA3] = jumpPtr{opSha3, true}
|
||||
jumpTable[ADDRESS] = jumpPtr{opAddress, true}
|
||||
jumpTable[BALANCE] = jumpPtr{opBalance, true}
|
||||
jumpTable[ORIGIN] = jumpPtr{opOrigin, true}
|
||||
jumpTable[CALLER] = jumpPtr{opCaller, true}
|
||||
jumpTable[CALLVALUE] = jumpPtr{opCallValue, true}
|
||||
jumpTable[CALLDATALOAD] = jumpPtr{opCalldataLoad, true}
|
||||
jumpTable[CALLDATASIZE] = jumpPtr{opCalldataSize, true}
|
||||
jumpTable[CALLDATACOPY] = jumpPtr{opCalldataCopy, true}
|
||||
jumpTable[CODESIZE] = jumpPtr{opCodeSize, true}
|
||||
jumpTable[EXTCODESIZE] = jumpPtr{opExtCodeSize, true}
|
||||
jumpTable[CODECOPY] = jumpPtr{opCodeCopy, true}
|
||||
jumpTable[EXTCODECOPY] = jumpPtr{opExtCodeCopy, true}
|
||||
jumpTable[GASPRICE] = jumpPtr{opGasprice, true}
|
||||
jumpTable[BLOCKHASH] = jumpPtr{opBlockhash, true}
|
||||
jumpTable[COINBASE] = jumpPtr{opCoinbase, true}
|
||||
jumpTable[TIMESTAMP] = jumpPtr{opTimestamp, true}
|
||||
jumpTable[NUMBER] = jumpPtr{opNumber, true}
|
||||
jumpTable[DIFFICULTY] = jumpPtr{opDifficulty, true}
|
||||
jumpTable[GASLIMIT] = jumpPtr{opGasLimit, true}
|
||||
jumpTable[POP] = jumpPtr{opPop, true}
|
||||
jumpTable[MLOAD] = jumpPtr{opMload, true}
|
||||
jumpTable[MSTORE] = jumpPtr{opMstore, true}
|
||||
jumpTable[MSTORE8] = jumpPtr{opMstore8, true}
|
||||
jumpTable[SLOAD] = jumpPtr{opSload, true}
|
||||
jumpTable[SSTORE] = jumpPtr{opSstore, true}
|
||||
jumpTable[JUMPDEST] = jumpPtr{opJumpdest, true}
|
||||
jumpTable[PC] = jumpPtr{nil, true}
|
||||
jumpTable[MSIZE] = jumpPtr{opMsize, true}
|
||||
jumpTable[GAS] = jumpPtr{opGas, true}
|
||||
jumpTable[CREATE] = jumpPtr{opCreate, true}
|
||||
jumpTable[CALL] = jumpPtr{opCall, true}
|
||||
jumpTable[CALLCODE] = jumpPtr{opCallCode, true}
|
||||
jumpTable[LOG0] = jumpPtr{makeLog(0), true}
|
||||
jumpTable[LOG1] = jumpPtr{makeLog(1), true}
|
||||
jumpTable[LOG2] = jumpPtr{makeLog(2), true}
|
||||
jumpTable[LOG3] = jumpPtr{makeLog(3), true}
|
||||
jumpTable[LOG4] = jumpPtr{makeLog(4), true}
|
||||
jumpTable[SWAP1] = jumpPtr{makeSwap(1), true}
|
||||
jumpTable[SWAP2] = jumpPtr{makeSwap(2), true}
|
||||
jumpTable[SWAP3] = jumpPtr{makeSwap(3), true}
|
||||
jumpTable[SWAP4] = jumpPtr{makeSwap(4), true}
|
||||
jumpTable[SWAP5] = jumpPtr{makeSwap(5), true}
|
||||
jumpTable[SWAP6] = jumpPtr{makeSwap(6), true}
|
||||
jumpTable[SWAP7] = jumpPtr{makeSwap(7), true}
|
||||
jumpTable[SWAP8] = jumpPtr{makeSwap(8), true}
|
||||
jumpTable[SWAP9] = jumpPtr{makeSwap(9), true}
|
||||
jumpTable[SWAP10] = jumpPtr{makeSwap(10), true}
|
||||
jumpTable[SWAP11] = jumpPtr{makeSwap(11), true}
|
||||
jumpTable[SWAP12] = jumpPtr{makeSwap(12), true}
|
||||
jumpTable[SWAP13] = jumpPtr{makeSwap(13), true}
|
||||
jumpTable[SWAP14] = jumpPtr{makeSwap(14), true}
|
||||
jumpTable[SWAP15] = jumpPtr{makeSwap(15), true}
|
||||
jumpTable[SWAP16] = jumpPtr{makeSwap(16), true}
|
||||
jumpTable[PUSH1] = jumpPtr{makePush(1, big.NewInt(1)), true}
|
||||
jumpTable[PUSH2] = jumpPtr{makePush(2, big.NewInt(2)), true}
|
||||
jumpTable[PUSH3] = jumpPtr{makePush(3, big.NewInt(3)), true}
|
||||
jumpTable[PUSH4] = jumpPtr{makePush(4, big.NewInt(4)), true}
|
||||
jumpTable[PUSH5] = jumpPtr{makePush(5, big.NewInt(5)), true}
|
||||
jumpTable[PUSH6] = jumpPtr{makePush(6, big.NewInt(6)), true}
|
||||
jumpTable[PUSH7] = jumpPtr{makePush(7, big.NewInt(7)), true}
|
||||
jumpTable[PUSH8] = jumpPtr{makePush(8, big.NewInt(8)), true}
|
||||
jumpTable[PUSH9] = jumpPtr{makePush(9, big.NewInt(9)), true}
|
||||
jumpTable[PUSH10] = jumpPtr{makePush(10, big.NewInt(10)), true}
|
||||
jumpTable[PUSH11] = jumpPtr{makePush(11, big.NewInt(11)), true}
|
||||
jumpTable[PUSH12] = jumpPtr{makePush(12, big.NewInt(12)), true}
|
||||
jumpTable[PUSH13] = jumpPtr{makePush(13, big.NewInt(13)), true}
|
||||
jumpTable[PUSH14] = jumpPtr{makePush(14, big.NewInt(14)), true}
|
||||
jumpTable[PUSH15] = jumpPtr{makePush(15, big.NewInt(15)), true}
|
||||
jumpTable[PUSH16] = jumpPtr{makePush(16, big.NewInt(16)), true}
|
||||
jumpTable[PUSH17] = jumpPtr{makePush(17, big.NewInt(17)), true}
|
||||
jumpTable[PUSH18] = jumpPtr{makePush(18, big.NewInt(18)), true}
|
||||
jumpTable[PUSH19] = jumpPtr{makePush(19, big.NewInt(19)), true}
|
||||
jumpTable[PUSH20] = jumpPtr{makePush(20, big.NewInt(20)), true}
|
||||
jumpTable[PUSH21] = jumpPtr{makePush(21, big.NewInt(21)), true}
|
||||
jumpTable[PUSH22] = jumpPtr{makePush(22, big.NewInt(22)), true}
|
||||
jumpTable[PUSH23] = jumpPtr{makePush(23, big.NewInt(23)), true}
|
||||
jumpTable[PUSH24] = jumpPtr{makePush(24, big.NewInt(24)), true}
|
||||
jumpTable[PUSH25] = jumpPtr{makePush(25, big.NewInt(25)), true}
|
||||
jumpTable[PUSH26] = jumpPtr{makePush(26, big.NewInt(26)), true}
|
||||
jumpTable[PUSH27] = jumpPtr{makePush(27, big.NewInt(27)), true}
|
||||
jumpTable[PUSH28] = jumpPtr{makePush(28, big.NewInt(28)), true}
|
||||
jumpTable[PUSH29] = jumpPtr{makePush(29, big.NewInt(29)), true}
|
||||
jumpTable[PUSH30] = jumpPtr{makePush(30, big.NewInt(30)), true}
|
||||
jumpTable[PUSH31] = jumpPtr{makePush(31, big.NewInt(31)), true}
|
||||
jumpTable[PUSH32] = jumpPtr{makePush(32, big.NewInt(32)), true}
|
||||
jumpTable[DUP1] = jumpPtr{makeDup(1), true}
|
||||
jumpTable[DUP2] = jumpPtr{makeDup(2), true}
|
||||
jumpTable[DUP3] = jumpPtr{makeDup(3), true}
|
||||
jumpTable[DUP4] = jumpPtr{makeDup(4), true}
|
||||
jumpTable[DUP5] = jumpPtr{makeDup(5), true}
|
||||
jumpTable[DUP6] = jumpPtr{makeDup(6), true}
|
||||
jumpTable[DUP7] = jumpPtr{makeDup(7), true}
|
||||
jumpTable[DUP8] = jumpPtr{makeDup(8), true}
|
||||
jumpTable[DUP9] = jumpPtr{makeDup(9), true}
|
||||
jumpTable[DUP10] = jumpPtr{makeDup(10), true}
|
||||
jumpTable[DUP11] = jumpPtr{makeDup(11), true}
|
||||
jumpTable[DUP12] = jumpPtr{makeDup(12), true}
|
||||
jumpTable[DUP13] = jumpPtr{makeDup(13), true}
|
||||
jumpTable[DUP14] = jumpPtr{makeDup(14), true}
|
||||
jumpTable[DUP15] = jumpPtr{makeDup(15), true}
|
||||
jumpTable[DUP16] = jumpPtr{makeDup(16), true}
|
||||
|
||||
jumpTable[RETURN] = jumpPtr{nil, true}
|
||||
jumpTable[SUICIDE] = jumpPtr{nil, true}
|
||||
jumpTable[JUMP] = jumpPtr{nil, true}
|
||||
jumpTable[JUMPI] = jumpPtr{nil, true}
|
||||
jumpTable[STOP] = jumpPtr{nil, true}
|
||||
|
||||
return jumpTable
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
jumpTable := newJumpTable(¶ms.ChainConfig{HomesteadBlock: big.NewInt(1)}, big.NewInt(0))
|
||||
if jumpTable[DELEGATECALL].valid {
|
||||
t.Error("Expected DELEGATECALL not to be present")
|
||||
}
|
||||
|
||||
for _, n := range []int64{1, 2, 100} {
|
||||
jumpTable := newJumpTable(¶ms.ChainConfig{HomesteadBlock: big.NewInt(1)}, big.NewInt(n))
|
||||
if !jumpTable[DELEGATECALL].valid {
|
||||
t.Error("Expected DELEGATECALL to be present for block", n)
|
||||
}
|
||||
}
|
||||
}
|
@ -45,7 +45,7 @@ type LogConfig struct {
|
||||
Limit int // maximum length of output, but zero means unlimited
|
||||
}
|
||||
|
||||
// StructLog is emitted to the Environment each cycle and lists information about the current internal state
|
||||
// StructLog is emitted to the EVM each cycle and lists information about the current internal state
|
||||
// prior to the execution of the statement.
|
||||
type StructLog struct {
|
||||
Pc uint64
|
||||
@ -65,7 +65,7 @@ type StructLog struct {
|
||||
// Note that reference types are actual VM data structures; make copies
|
||||
// if you need to retain them beyond the current call.
|
||||
type Tracer interface {
|
||||
CaptureState(env *Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
|
||||
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
|
||||
}
|
||||
|
||||
// StructLogger is an EVM state logger and implements Tracer.
|
||||
@ -94,10 +94,10 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
|
||||
// captureState logs a new structured log message and pushes it out to the environment
|
||||
//
|
||||
// captureState also tracks SSTORE ops to track dirty values.
|
||||
func (l *StructLogger) CaptureState(env *Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
|
||||
func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
|
||||
// check if already accumulated the specified number of logs
|
||||
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
|
||||
return TraceLimitReachedError
|
||||
return ErrTraceLimitReached
|
||||
}
|
||||
|
||||
// initialise new changed values storage container for this contract
|
||||
@ -155,7 +155,7 @@ func (l *StructLogger) CaptureState(env *Environment, pc uint64, op OpCode, gas,
|
||||
}
|
||||
}
|
||||
// create a new snaptshot of the EVM.
|
||||
log := StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, env.Depth, err}
|
||||
log := StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, env.depth, err}
|
||||
|
||||
l.logs = append(l.logs, log)
|
||||
return nil
|
||||
|
@ -52,7 +52,7 @@ func (d dummyStateDB) GetAccount(common.Address) Account {
|
||||
|
||||
func TestStoreCapture(t *testing.T) {
|
||||
var (
|
||||
env = NewEnvironment(Context{}, nil, params.TestChainConfig, Config{EnableJit: false, ForceJit: false})
|
||||
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{EnableJit: false, ForceJit: false})
|
||||
logger = NewStructLogger(nil)
|
||||
mem = NewMemory()
|
||||
stack = newstack()
|
||||
@ -79,7 +79,7 @@ func TestStorageCapture(t *testing.T) {
|
||||
var (
|
||||
ref = &dummyContractRef{}
|
||||
contract = NewContract(ref, ref, new(big.Int), new(big.Int))
|
||||
env = NewEnvironment(Context{}, dummyStateDB{ref: ref}, params.TestChainConfig, Config{EnableJit: false, ForceJit: false})
|
||||
env = NewEVM(Context{}, dummyStateDB{ref: ref}, params.TestChainConfig, Config{EnableJit: false, ForceJit: false})
|
||||
logger = NewStructLogger(nil)
|
||||
mem = NewMemory()
|
||||
stack = newstack()
|
||||
|
68
core/vm/memory_table.go
Normal file
68
core/vm/memory_table.go
Normal file
@ -0,0 +1,68 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
func memorySha3(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), stack.Back(1))
|
||||
}
|
||||
|
||||
func memoryCalldataCopy(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), stack.Back(2))
|
||||
}
|
||||
|
||||
func memoryCodeCopy(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), stack.Back(2))
|
||||
}
|
||||
|
||||
func memoryExtCodeCopy(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(1), stack.Back(3))
|
||||
}
|
||||
|
||||
func memoryMLoad(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), big.NewInt(32))
|
||||
}
|
||||
|
||||
func memoryMStore8(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), big.NewInt(1))
|
||||
}
|
||||
|
||||
func memoryMStore(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), big.NewInt(32))
|
||||
}
|
||||
|
||||
func memoryCreate(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(1), stack.Back(2))
|
||||
}
|
||||
|
||||
func memoryCall(stack *Stack) *big.Int {
|
||||
x := calcMemSize(stack.Back(5), stack.Back(6))
|
||||
y := calcMemSize(stack.Back(3), stack.Back(4))
|
||||
|
||||
return common.BigMax(x, y)
|
||||
}
|
||||
|
||||
func memoryCallCode(stack *Stack) *big.Int {
|
||||
x := calcMemSize(stack.Back(5), stack.Back(6))
|
||||
y := calcMemSize(stack.Back(3), stack.Back(4))
|
||||
|
||||
return common.BigMax(x, y)
|
||||
}
|
||||
func memoryDelegateCall(stack *Stack) *big.Int {
|
||||
x := calcMemSize(stack.Back(4), stack.Back(5))
|
||||
y := calcMemSize(stack.Back(2), stack.Back(3))
|
||||
|
||||
return common.BigMax(x, y)
|
||||
}
|
||||
|
||||
func memoryReturn(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), stack.Back(1))
|
||||
}
|
||||
|
||||
func memoryLog(stack *Stack) *big.Int {
|
||||
mSize, mStart := stack.Back(1), stack.Back(0)
|
||||
return calcMemSize(mStart, mSize)
|
||||
}
|
@ -25,7 +25,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
)
|
||||
|
||||
func NewEnv(cfg *Config, state *state.StateDB) *vm.Environment {
|
||||
func NewEnv(cfg *Config, state *state.StateDB) *vm.EVM {
|
||||
context := vm.Context{
|
||||
CanTransfer: core.CanTransfer,
|
||||
Transfer: core.Transfer,
|
||||
@ -40,5 +40,5 @@ func NewEnv(cfg *Config, state *state.StateDB) *vm.Environment {
|
||||
GasPrice: new(big.Int),
|
||||
}
|
||||
|
||||
return vm.NewEnvironment(context, cfg.State, cfg.ChainConfig, cfg.EVMConfig)
|
||||
return vm.NewEVM(context, cfg.State, cfg.ChainConfig, cfg.EVMConfig)
|
||||
}
|
||||
|
@ -28,14 +28,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// The default, always homestead, rule set for the vm env
|
||||
type ruleSet struct{}
|
||||
|
||||
func (ruleSet) IsHomestead(*big.Int) bool { return true }
|
||||
func (ruleSet) GasTable(*big.Int) params.GasTable {
|
||||
return params.GasTableHomesteadGasRepriceFork
|
||||
}
|
||||
|
||||
// Config is a basic type specifying certain configuration flags for running
|
||||
// the EVM.
|
||||
type Config struct {
|
||||
|
@ -56,7 +56,7 @@ func TestDefaults(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvironment(t *testing.T) {
|
||||
func TestEVM(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Fatalf("crashed with: %v", r)
|
||||
|
@ -1,60 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
import "math/big"
|
||||
|
||||
type jumpSeg struct {
|
||||
pos uint64
|
||||
err error
|
||||
gas *big.Int
|
||||
}
|
||||
|
||||
func (j jumpSeg) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
if !contract.UseGas(j.gas) {
|
||||
return nil, OutOfGasError
|
||||
}
|
||||
if j.err != nil {
|
||||
return nil, j.err
|
||||
}
|
||||
*pc = j.pos
|
||||
return nil, nil
|
||||
}
|
||||
func (s jumpSeg) halts() bool { return false }
|
||||
func (s jumpSeg) Op() OpCode { return 0 }
|
||||
|
||||
type pushSeg struct {
|
||||
data []*big.Int
|
||||
gas *big.Int
|
||||
}
|
||||
|
||||
func (s pushSeg) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
// Use the calculated gas. When insufficient gas is present, use all gas and return an
|
||||
// Out Of Gas error
|
||||
if !contract.UseGas(s.gas) {
|
||||
return nil, OutOfGasError
|
||||
}
|
||||
|
||||
for _, d := range s.data {
|
||||
stack.push(new(big.Int).Set(d))
|
||||
}
|
||||
*pc += uint64(len(s.data))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s pushSeg) halts() bool { return false }
|
||||
func (s pushSeg) Op() OpCode { return 0 }
|
@ -68,6 +68,11 @@ func (st *Stack) peek() *big.Int {
|
||||
return st.data[st.len()-1]
|
||||
}
|
||||
|
||||
// Back returns the n'th item in stack
|
||||
func (st *Stack) Back(n int) *big.Int {
|
||||
return st.data[st.len()-n-1]
|
||||
}
|
||||
|
||||
func (st *Stack) require(n int) error {
|
||||
if st.len() < n {
|
||||
return fmt.Errorf("stack underflow (%d <=> %d)", len(st.data), n)
|
||||
|
20
core/vm/stack_table.go
Normal file
20
core/vm/stack_table.go
Normal file
@ -0,0 +1,20 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func makeStackFunc(pop, push int) stackValidationFunc {
|
||||
return func(stack *Stack) error {
|
||||
if err := stack.require(pop); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if push > 0 && int64(stack.len()-pop+push) > params.StackLimit.Int64() {
|
||||
return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit.Int64())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
455
core/vm/vm.go
455
core/vm/vm.go
@ -19,6 +19,7 @@ package vm
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@ -28,9 +29,9 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// Config are the configuration options for the EVM
|
||||
// Config are the configuration options for the Interpreter
|
||||
type Config struct {
|
||||
// Debug enabled debugging EVM options
|
||||
// Debug enabled debugging Interpreter options
|
||||
Debug bool
|
||||
// EnableJit enabled the JIT VM
|
||||
EnableJit bool
|
||||
@ -38,40 +39,51 @@ type Config struct {
|
||||
ForceJit bool
|
||||
// Tracer is the op code logger
|
||||
Tracer Tracer
|
||||
// NoRecursion disabled EVM call, callcode,
|
||||
// NoRecursion disabled Interpreter call, callcode,
|
||||
// delegate call and create.
|
||||
NoRecursion bool
|
||||
// Disable gas metering
|
||||
DisableGasMetering bool
|
||||
// JumpTable contains the EVM instruction table. This
|
||||
// may me left uninitialised and will be set the default
|
||||
// table.
|
||||
JumpTable [256]operation
|
||||
}
|
||||
|
||||
// EVM is used to run Ethereum based contracts and will utilise the
|
||||
// Interpreter is used to run Ethereum based contracts and will utilise the
|
||||
// passed environment to query external sources for state information.
|
||||
// The EVM will run the byte code VM or JIT VM based on the passed
|
||||
// The Interpreter will run the byte code VM or JIT VM based on the passed
|
||||
// configuration.
|
||||
type EVM struct {
|
||||
env *Environment
|
||||
jumpTable vmJumpTable
|
||||
cfg Config
|
||||
gasTable params.GasTable
|
||||
type Interpreter struct {
|
||||
env *EVM
|
||||
cfg Config
|
||||
gasTable params.GasTable
|
||||
}
|
||||
|
||||
// New returns a new instance of the EVM.
|
||||
func New(env *Environment, cfg Config) *EVM {
|
||||
return &EVM{
|
||||
env: env,
|
||||
jumpTable: newJumpTable(env.ChainConfig(), env.BlockNumber),
|
||||
cfg: cfg,
|
||||
gasTable: env.ChainConfig().GasTable(env.BlockNumber),
|
||||
// NewInterpreter returns a new instance of the Interpreter.
|
||||
func NewInterpreter(env *EVM, cfg Config) *Interpreter {
|
||||
// We use the STOP instruction whether to see
|
||||
// the jump table was initialised. If it was not
|
||||
// we'll set the default jump table.
|
||||
if !cfg.JumpTable[STOP].valid {
|
||||
cfg.JumpTable = defaultJumpTable
|
||||
}
|
||||
|
||||
return &Interpreter{
|
||||
env: env,
|
||||
cfg: cfg,
|
||||
gasTable: env.ChainConfig().GasTable(env.BlockNumber),
|
||||
}
|
||||
}
|
||||
|
||||
// Run loops and evaluates the contract's code with the given input data
|
||||
func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
||||
evm.env.Depth++
|
||||
defer func() { evm.env.Depth-- }()
|
||||
func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
||||
evm.env.depth++
|
||||
defer func() { evm.env.depth-- }()
|
||||
|
||||
if contract.CodeAddr != nil {
|
||||
if p := Precompiled[contract.CodeAddr.Str()]; p != nil {
|
||||
return evm.RunPrecompiled(p, input, contract)
|
||||
if p := PrecompiledContracts[*contract.CodeAddr]; p != nil {
|
||||
return RunPrecompiledContract(p, input, contract)
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,384 +96,91 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
||||
if codehash == (common.Hash{}) {
|
||||
codehash = crypto.Keccak256Hash(contract.Code)
|
||||
}
|
||||
var program *Program
|
||||
if false {
|
||||
// JIT disabled due to JIT not being Homestead gas reprice ready.
|
||||
|
||||
// If the JIT is enabled check the status of the JIT program,
|
||||
// if it doesn't exist compile a new program in a separate
|
||||
// goroutine or wait for compilation to finish if the JIT is
|
||||
// forced.
|
||||
switch GetProgramStatus(codehash) {
|
||||
case progReady:
|
||||
return RunProgram(GetProgram(codehash), evm.env, contract, input)
|
||||
case progUnknown:
|
||||
if evm.cfg.ForceJit {
|
||||
// Create and compile program
|
||||
program = NewProgram(contract.Code)
|
||||
perr := CompileProgram(program)
|
||||
if perr == nil {
|
||||
return RunProgram(program, evm.env, contract, input)
|
||||
}
|
||||
glog.V(logger.Info).Infoln("error compiling program", err)
|
||||
} else {
|
||||
// create and compile the program. Compilation
|
||||
// is done in a separate goroutine
|
||||
program = NewProgram(contract.Code)
|
||||
go func() {
|
||||
err := CompileProgram(program)
|
||||
if err != nil {
|
||||
glog.V(logger.Info).Infoln("error compiling program", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
caller = contract.caller
|
||||
code = contract.Code
|
||||
instrCount = 0
|
||||
|
||||
op OpCode // current opcode
|
||||
mem = NewMemory() // bound memory
|
||||
stack = newstack() // local stack
|
||||
// For optimisation reason we're using uint64 as the program counter.
|
||||
// It's theoretically possible to go above 2^64. The YP defines the PC to be uint256. Practically much less so feasible.
|
||||
pc = uint64(0) // program counter
|
||||
|
||||
// jump evaluates and checks whether the given jump destination is a valid one
|
||||
// if valid move the `pc` otherwise return an error.
|
||||
jump = func(from uint64, to *big.Int) error {
|
||||
if !contract.jumpdests.has(codehash, code, to) {
|
||||
nop := contract.GetOp(to.Uint64())
|
||||
return fmt.Errorf("invalid jump destination (%v) %v", nop, to)
|
||||
}
|
||||
|
||||
pc = to.Uint64()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
newMemSize *big.Int
|
||||
cost *big.Int
|
||||
pc = uint64(0) // program counter
|
||||
cost *big.Int
|
||||
)
|
||||
contract.Input = input
|
||||
|
||||
// User defer pattern to check for an error and, based on the error being nil or not, use all gas and return.
|
||||
defer func() {
|
||||
if err != nil && evm.cfg.Debug {
|
||||
evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth, err)
|
||||
evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.depth, err)
|
||||
}
|
||||
}()
|
||||
|
||||
if glog.V(logger.Debug) {
|
||||
glog.Infof("running byte VM %x\n", codehash[:4])
|
||||
glog.Infof("evm running: %x\n", codehash[:4])
|
||||
tstart := time.Now()
|
||||
defer func() {
|
||||
glog.Infof("byte VM %x done. time: %v instrc: %v\n", codehash[:4], time.Since(tstart), instrCount)
|
||||
glog.Infof("evm done: %x. time: %v\n", codehash[:4], time.Since(tstart))
|
||||
}()
|
||||
}
|
||||
|
||||
for ; ; instrCount++ {
|
||||
/*
|
||||
if EnableJit && it%100 == 0 {
|
||||
if program != nil && progStatus(atomic.LoadInt32(&program.status)) == progReady {
|
||||
// move execution
|
||||
fmt.Println("moved", it)
|
||||
glog.V(logger.Info).Infoln("Moved execution to JIT")
|
||||
return runProgram(program, pc, mem, stack, evm.env, contract, input)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// The Interpreter main run loop (contextual). This loop runs until either an
|
||||
// explicit STOP, RETURN or SUICIDE is executed, an error accured during
|
||||
// the execution of one of the operations or until the evm.done is set by
|
||||
// the parent context.Context.
|
||||
for atomic.LoadInt32(&evm.env.abort) == 0 {
|
||||
// Get the memory location of pc
|
||||
op = contract.GetOp(pc)
|
||||
//fmt.Printf("OP %d %v\n", op, op)
|
||||
// calculate the new memory size and gas price for the current executing opcode
|
||||
newMemSize, cost, err = calculateGasAndSize(evm.gasTable, evm.env, contract, caller, op, mem, stack)
|
||||
if err != nil {
|
||||
|
||||
// get the operation from the jump table matching the opcode
|
||||
operation := evm.cfg.JumpTable[op]
|
||||
|
||||
// if the op is invalid abort the process and return an error
|
||||
if !operation.valid {
|
||||
return nil, fmt.Errorf("invalid opcode %x", op)
|
||||
}
|
||||
|
||||
// validate the stack and make sure there enough stack items available
|
||||
// to perform the operation
|
||||
if err := operation.validateStack(stack); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Use the calculated gas. When insufficient gas is present, use all gas and return an
|
||||
// Out Of Gas error
|
||||
if !contract.UseGas(cost) {
|
||||
return nil, OutOfGasError
|
||||
var memorySize *big.Int
|
||||
// calculate the new memory size and expand the memory to fit
|
||||
// the operation
|
||||
if operation.memorySize != nil {
|
||||
memorySize = operation.memorySize(stack)
|
||||
// memory is expanded in words of 32 bytes. Gas
|
||||
// is also calculated in words.
|
||||
memorySize.Mul(toWordSize(memorySize), big.NewInt(32))
|
||||
}
|
||||
|
||||
if !evm.cfg.DisableGasMetering {
|
||||
// consume the gas and return an error if not enough gas is available.
|
||||
// cost is explicitly set so that the capture state defer method cas get the proper cost
|
||||
cost = operation.gasCost(evm.gasTable, evm.env, contract, stack, mem, memorySize)
|
||||
if !contract.UseGas(cost) {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
if memorySize != nil {
|
||||
mem.Resize(memorySize.Uint64())
|
||||
}
|
||||
|
||||
// Resize the memory calculated previously
|
||||
mem.Resize(newMemSize.Uint64())
|
||||
// Add a log message
|
||||
if evm.cfg.Debug {
|
||||
err = evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.depth, err)
|
||||
}
|
||||
|
||||
if opPtr := evm.jumpTable[op]; opPtr.valid {
|
||||
if opPtr.fn != nil {
|
||||
opPtr.fn(instruction{}, &pc, evm.env, contract, mem, stack)
|
||||
} else {
|
||||
switch op {
|
||||
case PC:
|
||||
opPc(instruction{data: new(big.Int).SetUint64(pc)}, &pc, evm.env, contract, mem, stack)
|
||||
case JUMP:
|
||||
if err := jump(pc, stack.pop()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
continue
|
||||
case JUMPI:
|
||||
pos, cond := stack.pop(), stack.pop()
|
||||
|
||||
if cond.Cmp(common.BigTrue) >= 0 {
|
||||
if err := jump(pc, pos); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
case RETURN:
|
||||
offset, size := stack.pop(), stack.pop()
|
||||
ret := mem.GetPtr(offset.Int64(), size.Int64())
|
||||
|
||||
return ret, nil
|
||||
case SUICIDE:
|
||||
opSuicide(instruction{}, nil, evm.env, contract, mem, stack)
|
||||
|
||||
fallthrough
|
||||
case STOP: // Stop the contract
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("Invalid opcode %x", op)
|
||||
// execute the operation
|
||||
res, err := operation.execute(&pc, evm.env, contract, mem, stack)
|
||||
switch {
|
||||
case err != nil:
|
||||
return nil, err
|
||||
case operation.halts:
|
||||
return res, nil
|
||||
case !operation.jumps:
|
||||
pc++
|
||||
}
|
||||
|
||||
pc++
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// calculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for
|
||||
// the operation. This does not reduce gas or resizes the memory.
|
||||
func calculateGasAndSize(gasTable params.GasTable, env *Environment, contract *Contract, caller ContractRef, op OpCode, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) {
|
||||
var (
|
||||
gas = new(big.Int)
|
||||
newMemSize *big.Int = new(big.Int)
|
||||
)
|
||||
err := baseCheck(op, stack, gas)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// stack Check, memory resize & gas phase
|
||||
switch op {
|
||||
case SUICIDE:
|
||||
// EIP150 homestead gas reprice fork:
|
||||
if gasTable.CreateBySuicide != nil {
|
||||
gas.Set(gasTable.Suicide)
|
||||
var (
|
||||
address = common.BigToAddress(stack.data[len(stack.data)-1])
|
||||
eip158 = env.ChainConfig().IsEIP158(env.BlockNumber)
|
||||
)
|
||||
|
||||
if eip158 {
|
||||
// if empty and transfers value
|
||||
if env.StateDB.Empty(address) && env.StateDB.GetBalance(contract.Address()).BitLen() > 0 {
|
||||
gas.Add(gas, gasTable.CreateBySuicide)
|
||||
}
|
||||
} else if !env.StateDB.Exist(address) {
|
||||
gas.Add(gas, gasTable.CreateBySuicide)
|
||||
}
|
||||
}
|
||||
|
||||
if !env.StateDB.HasSuicided(contract.Address()) {
|
||||
env.StateDB.AddRefund(params.SuicideRefundGas)
|
||||
}
|
||||
case EXTCODESIZE:
|
||||
gas.Set(gasTable.ExtcodeSize)
|
||||
case BALANCE:
|
||||
gas.Set(gasTable.Balance)
|
||||
case SLOAD:
|
||||
gas.Set(gasTable.SLoad)
|
||||
case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
|
||||
n := int(op - SWAP1 + 2)
|
||||
err := stack.require(n)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
gas.Set(GasFastestStep)
|
||||
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
|
||||
n := int(op - DUP1 + 1)
|
||||
err := stack.require(n)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
gas.Set(GasFastestStep)
|
||||
case LOG0, LOG1, LOG2, LOG3, LOG4:
|
||||
n := int(op - LOG0)
|
||||
err := stack.require(n + 2)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
mSize, mStart := stack.data[stack.len()-2], stack.data[stack.len()-1]
|
||||
|
||||
gas.Add(gas, params.LogGas)
|
||||
gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(n)), params.LogTopicGas))
|
||||
gas.Add(gas, new(big.Int).Mul(mSize, params.LogDataGas))
|
||||
|
||||
newMemSize = calcMemSize(mStart, mSize)
|
||||
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
case EXP:
|
||||
expByteLen := int64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
||||
gas.Add(gas, new(big.Int).Mul(big.NewInt(expByteLen), gasTable.ExpByte))
|
||||
case SSTORE:
|
||||
err := stack.require(2)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var g *big.Int
|
||||
y, x := stack.data[stack.len()-2], stack.data[stack.len()-1]
|
||||
val := env.StateDB.GetState(contract.Address(), common.BigToHash(x))
|
||||
|
||||
// This checks for 3 scenario's and calculates gas accordingly
|
||||
// 1. From a zero-value address to a non-zero value (NEW VALUE)
|
||||
// 2. From a non-zero value address to a zero-value address (DELETE)
|
||||
// 3. From a non-zero to a non-zero (CHANGE)
|
||||
if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) {
|
||||
// 0 => non 0
|
||||
g = params.SstoreSetGas
|
||||
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
|
||||
env.StateDB.AddRefund(params.SstoreRefundGas)
|
||||
|
||||
g = params.SstoreClearGas
|
||||
} else {
|
||||
// non 0 => non 0 (or 0 => 0)
|
||||
g = params.SstoreResetGas
|
||||
}
|
||||
gas.Set(g)
|
||||
case MLOAD:
|
||||
newMemSize = calcMemSize(stack.peek(), u256(32))
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
case MSTORE8:
|
||||
newMemSize = calcMemSize(stack.peek(), u256(1))
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
case MSTORE:
|
||||
newMemSize = calcMemSize(stack.peek(), u256(32))
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
case RETURN:
|
||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
case SHA3:
|
||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
|
||||
|
||||
words := toWordSize(stack.data[stack.len()-2])
|
||||
gas.Add(gas, words.Mul(words, params.Sha3WordGas))
|
||||
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
case CALLDATACOPY:
|
||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
|
||||
|
||||
words := toWordSize(stack.data[stack.len()-3])
|
||||
gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
case CODECOPY:
|
||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
|
||||
|
||||
words := toWordSize(stack.data[stack.len()-3])
|
||||
gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
case EXTCODECOPY:
|
||||
gas.Set(gasTable.ExtcodeCopy)
|
||||
|
||||
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4])
|
||||
|
||||
words := toWordSize(stack.data[stack.len()-4])
|
||||
gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
case CREATE:
|
||||
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3])
|
||||
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
case CALL, CALLCODE:
|
||||
gas.Set(gasTable.Calls)
|
||||
|
||||
transfersValue := stack.data[len(stack.data)-3].BitLen() > 0
|
||||
if op == CALL {
|
||||
var (
|
||||
address = common.BigToAddress(stack.data[len(stack.data)-2])
|
||||
eip158 = env.ChainConfig().IsEIP158(env.BlockNumber)
|
||||
)
|
||||
if eip158 {
|
||||
if env.StateDB.Empty(address) && transfersValue {
|
||||
gas.Add(gas, params.CallNewAccountGas)
|
||||
}
|
||||
} else if !env.StateDB.Exist(address) {
|
||||
gas.Add(gas, params.CallNewAccountGas)
|
||||
}
|
||||
}
|
||||
if transfersValue {
|
||||
gas.Add(gas, params.CallValueTransferGas)
|
||||
}
|
||||
x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7])
|
||||
y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5])
|
||||
|
||||
newMemSize = common.BigMax(x, y)
|
||||
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
|
||||
cg := callGas(gasTable, contract.Gas, gas, stack.data[stack.len()-1])
|
||||
// Replace the stack item with the new gas calculation. This means that
|
||||
// either the original item is left on the stack or the item is replaced by:
|
||||
// (availableGas - gas) * 63 / 64
|
||||
// We replace the stack item so that it's available when the opCall instruction is
|
||||
// called. This information is otherwise lost due to the dependency on *current*
|
||||
// available gas.
|
||||
stack.data[stack.len()-1] = cg
|
||||
gas.Add(gas, cg)
|
||||
|
||||
case DELEGATECALL:
|
||||
gas.Set(gasTable.Calls)
|
||||
|
||||
x := calcMemSize(stack.data[stack.len()-5], stack.data[stack.len()-6])
|
||||
y := calcMemSize(stack.data[stack.len()-3], stack.data[stack.len()-4])
|
||||
|
||||
newMemSize = common.BigMax(x, y)
|
||||
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
|
||||
cg := callGas(gasTable, contract.Gas, gas, stack.data[stack.len()-1])
|
||||
// Replace the stack item with the new gas calculation. This means that
|
||||
// either the original item is left on the stack or the item is replaced by:
|
||||
// (availableGas - gas) * 63 / 64
|
||||
// We replace the stack item so that it's available when the opCall instruction is
|
||||
// called.
|
||||
stack.data[stack.len()-1] = cg
|
||||
gas.Add(gas, cg)
|
||||
|
||||
}
|
||||
|
||||
return newMemSize, gas, nil
|
||||
}
|
||||
|
||||
// RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go
|
||||
func (evm *EVM) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Contract) (ret []byte, err error) {
|
||||
gas := p.Gas(len(input))
|
||||
if contract.UseGas(gas) {
|
||||
ret = p.Call(input)
|
||||
|
||||
return ret, nil
|
||||
} else {
|
||||
return nil, OutOfGasError
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ import (
|
||||
)
|
||||
|
||||
type JitVm struct {
|
||||
env Environment
|
||||
env EVM
|
||||
me ContextRef
|
||||
callerAddr []byte
|
||||
price *big.Int
|
||||
@ -161,7 +161,7 @@ func assert(condition bool, message string) {
|
||||
}
|
||||
}
|
||||
|
||||
func NewJitVm(env Environment) *JitVm {
|
||||
func NewJitVm(env EVM) *JitVm {
|
||||
return &JitVm{env: env}
|
||||
}
|
||||
|
||||
@ -235,7 +235,7 @@ func (self *JitVm) Endl() VirtualMachine {
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *JitVm) Env() Environment {
|
||||
func (self *JitVm) Env() EVM {
|
||||
return self.env
|
||||
}
|
||||
|
||||
|
@ -1,17 +0,0 @@
|
||||
// Copyright 2014 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
@ -532,7 +532,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
|
||||
|
||||
// Mutate the state if we haven't reached the tracing transaction yet
|
||||
if uint64(idx) < txIndex {
|
||||
vmenv := vm.NewEnvironment(context, stateDb, api.config, vm.Config{})
|
||||
vmenv := vm.NewEVM(context, stateDb, api.config, vm.Config{})
|
||||
_, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("mutation failed: %v", err)
|
||||
@ -541,7 +541,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
|
||||
continue
|
||||
}
|
||||
|
||||
vmenv := vm.NewEnvironment(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()))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tracing failed: %v", err)
|
||||
|
@ -106,14 +106,14 @@ func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int {
|
||||
return b.eth.blockchain.GetTdByHash(blockHash)
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (*vm.Environment, func() error, error) {
|
||||
func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (*vm.EVM, func() error, error) {
|
||||
statedb := state.(EthApiState).state
|
||||
from := statedb.GetOrNewStateObject(msg.From())
|
||||
from.SetBalance(common.MaxBig)
|
||||
vmError := func() error { return nil }
|
||||
|
||||
context := core.NewEVMContext(msg, header, b.eth.BlockChain())
|
||||
return vm.NewEnvironment(context, statedb, b.eth.chainConfig, vm.Config{}), vmError, nil
|
||||
return vm.NewEVM(context, statedb, b.eth.chainConfig, vm.Config{}), vmError, nil
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
||||
|
@ -51,7 +51,7 @@ type Backend interface {
|
||||
GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error)
|
||||
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
|
||||
GetTd(blockHash common.Hash) *big.Int
|
||||
GetVMEnv(ctx context.Context, msg core.Message, state State, header *types.Header) (*vm.Environment, func() error, error)
|
||||
GetVMEnv(ctx context.Context, msg core.Message, state State, header *types.Header) (*vm.EVM, func() error, error)
|
||||
// TxPool API
|
||||
SendTx(ctx context.Context, signedTx *types.Transaction) error
|
||||
RemoveTx(txHash common.Hash)
|
||||
|
@ -278,7 +278,7 @@ func wrapError(context string, err error) error {
|
||||
}
|
||||
|
||||
// CaptureState implements the Tracer interface to trace a single step of VM execution
|
||||
func (jst *JavascriptTracer) CaptureState(env *vm.Environment, pc uint64, op vm.OpCode, gas, cost *big.Int, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
|
||||
func (jst *JavascriptTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost *big.Int, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
|
||||
if jst.err == nil {
|
||||
jst.memory.memory = memory
|
||||
jst.stack.stack = stack
|
||||
|
@ -43,12 +43,12 @@ func (account) SetCode(common.Hash, []byte) {}
|
||||
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
|
||||
|
||||
func runTrace(tracer *JavascriptTracer) (interface{}, error) {
|
||||
env := vm.NewEnvironment(vm.Context{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
|
||||
env := vm.NewEVM(vm.Context{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
|
||||
|
||||
contract := vm.NewContract(account{}, account{}, big.NewInt(0), big.NewInt(10000))
|
||||
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
|
||||
|
||||
_, err := env.EVM().Run(contract, []byte{})
|
||||
_, err := env.Interpreter().Run(contract, []byte{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -133,7 +133,7 @@ func TestHaltBetweenSteps(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
env := vm.NewEnvironment(vm.Context{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
|
||||
env := vm.NewEVM(vm.Context{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
|
||||
contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), big.NewInt(0))
|
||||
|
||||
tracer.CaptureState(env, 0, 0, big.NewInt(0), big.NewInt(0), nil, nil, contract, 0, nil)
|
||||
|
@ -88,7 +88,7 @@ func (b *LesApiBackend) GetTd(blockHash common.Hash) *big.Int {
|
||||
return b.eth.blockchain.GetTdByHash(blockHash)
|
||||
}
|
||||
|
||||
func (b *LesApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (*vm.Environment, func() error, error) {
|
||||
func (b *LesApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (*vm.EVM, func() error, error) {
|
||||
stateDb := state.(*light.LightState).Copy()
|
||||
addr := msg.From()
|
||||
from, err := stateDb.GetOrNewStateObject(ctx, addr)
|
||||
@ -99,7 +99,7 @@ func (b *LesApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state et
|
||||
|
||||
vmstate := light.NewVMState(ctx, stateDb)
|
||||
context := core.NewEVMContext(msg, header, b.eth.blockchain)
|
||||
return vm.NewEnvironment(context, vmstate, b.eth.chainConfig, vm.Config{}), vmstate.Error, nil
|
||||
return vm.NewEVM(context, vmstate, b.eth.chainConfig, vm.Config{}), vmstate.Error, nil
|
||||
}
|
||||
|
||||
func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
||||
|
@ -123,7 +123,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
|
||||
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(100000), new(big.Int), data, false)}
|
||||
|
||||
context := core.NewEVMContext(msg, header, bc)
|
||||
vmenv := vm.NewEnvironment(context, statedb, config, vm.Config{})
|
||||
vmenv := vm.NewEVM(context, statedb, config, vm.Config{})
|
||||
|
||||
//vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{})
|
||||
gp := new(core.GasPool).AddGas(common.MaxBig)
|
||||
@ -141,7 +141,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
|
||||
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(100000), new(big.Int), data, false)}
|
||||
|
||||
context := core.NewEVMContext(msg, header, lc)
|
||||
vmenv := vm.NewEnvironment(context, vmstate, config, vm.Config{})
|
||||
vmenv := vm.NewEVM(context, vmstate, config, vm.Config{})
|
||||
|
||||
//vmenv := light.NewEnv(ctx, state, config, lc, msg, header, vm.Config{})
|
||||
gp := new(core.GasPool).AddGas(common.MaxBig)
|
||||
|
@ -172,7 +172,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
|
||||
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(1000000), new(big.Int), data, false)}
|
||||
|
||||
context := core.NewEVMContext(msg, header, bc)
|
||||
vmenv := vm.NewEnvironment(context, statedb, config, vm.Config{})
|
||||
vmenv := vm.NewEVM(context, statedb, config, vm.Config{})
|
||||
|
||||
gp := new(core.GasPool).AddGas(common.MaxBig)
|
||||
ret, _, _ := core.ApplyMessage(vmenv, msg, gp)
|
||||
@ -188,7 +188,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
|
||||
|
||||
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(1000000), new(big.Int), data, false)}
|
||||
context := core.NewEVMContext(msg, header, lc)
|
||||
vmenv := vm.NewEnvironment(context, vmstate, config, vm.Config{})
|
||||
vmenv := vm.NewEVM(context, vmstate, config, vm.Config{})
|
||||
gp := new(core.GasPool).AddGas(common.MaxBig)
|
||||
ret, _, _ := core.ApplyMessage(vmenv, msg, gp)
|
||||
if vmstate.Error() == nil {
|
||||
|
@ -616,7 +616,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
|
||||
func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) (error, vm.Logs) {
|
||||
snap := env.state.Snapshot()
|
||||
|
||||
receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, vm.Config{})
|
||||
receipt, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, vm.Config{})
|
||||
if err != nil {
|
||||
env.state.RevertToSnapshot(snap)
|
||||
return err, nil
|
||||
@ -624,7 +624,7 @@ func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, g
|
||||
env.txs = append(env.txs, tx)
|
||||
env.receipts = append(env.receipts, receipt)
|
||||
|
||||
return nil, logs
|
||||
return nil, receipt.Logs
|
||||
}
|
||||
|
||||
// TODO: remove or use
|
||||
|
@ -237,6 +237,7 @@ func TestWallet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStateTestsRandom(t *testing.T) {
|
||||
t.Skip()
|
||||
chainConfig := ¶ms.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(1150000),
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ func runStateTests(chainConfig *params.ChainConfig, tests map[string]VmTest, ski
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
if skipTest[name] /*|| name != "EXP_Empty"*/ {
|
||||
if skipTest[name] || name != "loop_stacklimit_1021" {
|
||||
glog.Infoln("Skipping state test", name)
|
||||
continue
|
||||
}
|
||||
@ -205,8 +205,6 @@ func runStateTest(chainConfig *params.ChainConfig, test VmTest) error {
|
||||
|
||||
func RunState(chainConfig *params.ChainConfig, statedb *state.StateDB, env, tx map[string]string) ([]byte, vm.Logs, *big.Int, error) {
|
||||
environment, msg := NewEVMEnvironment(false, chainConfig, statedb, env, tx)
|
||||
// Set pre compiled contracts
|
||||
vm.Precompiled = vm.PrecompiledContracts()
|
||||
gaspool := new(core.GasPool).AddGas(common.Big(env["currentGasLimit"]))
|
||||
|
||||
root, _ := statedb.Commit(false)
|
||||
|
@ -149,7 +149,7 @@ type VmTest struct {
|
||||
PostStateRoot string
|
||||
}
|
||||
|
||||
func NewEVMEnvironment(vmTest bool, chainConfig *params.ChainConfig, statedb *state.StateDB, envValues map[string]string, tx map[string]string) (*vm.Environment, core.Message) {
|
||||
func NewEVMEnvironment(vmTest bool, chainConfig *params.ChainConfig, statedb *state.StateDB, envValues map[string]string, tx map[string]string) (*vm.EVM, core.Message) {
|
||||
var (
|
||||
data = common.FromHex(tx["data"])
|
||||
gas = common.Big(tx["gasLimit"])
|
||||
@ -207,5 +207,5 @@ func NewEVMEnvironment(vmTest bool, chainConfig *params.ChainConfig, statedb *st
|
||||
if context.GasPrice == nil {
|
||||
context.GasPrice = new(big.Int)
|
||||
}
|
||||
return vm.NewEnvironment(context, statedb, chainConfig, vm.Config{NoRecursion: vmTest}), msg
|
||||
return vm.NewEVM(context, statedb, chainConfig, vm.Config{NoRecursion: vmTest}), msg
|
||||
}
|
||||
|
@ -37,63 +37,63 @@ func BenchmarkVmFibonacci16Tests(b *testing.B) {
|
||||
}
|
||||
|
||||
// I've created a new function for each tests so it's easier to identify where the problem lies if any of them fail.
|
||||
func TestVMArithmetic(t *testing.T) {
|
||||
func TestVmVMArithmetic(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmArithmeticTest.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBitwiseLogicOperation(t *testing.T) {
|
||||
func TestVmBitwiseLogicOperation(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmBitwiseLogicOperationTest.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockInfo(t *testing.T) {
|
||||
func TestVmBlockInfo(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmBlockInfoTest.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvironmentalInfo(t *testing.T) {
|
||||
func TestVmEnvironmentalInfo(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmEnvironmentalInfoTest.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlowOperation(t *testing.T) {
|
||||
func TestVmFlowOperation(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmIOandFlowOperationsTest.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogTest(t *testing.T) {
|
||||
func TestVmLogTest(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmLogTest.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPerformance(t *testing.T) {
|
||||
func TestVmPerformance(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmPerformanceTest.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPushDupSwap(t *testing.T) {
|
||||
func TestVmPushDupSwap(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmPushDupSwapTest.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMSha3(t *testing.T) {
|
||||
func TestVmVMSha3(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmSha3Test.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
@ -114,21 +114,21 @@ func TestVmLog(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestInputLimits(t *testing.T) {
|
||||
func TestVmInputLimits(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmInputLimits.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInputLimitsLight(t *testing.T) {
|
||||
func TestVmInputLimitsLight(t *testing.T) {
|
||||
fn := filepath.Join(vmTestDir, "vmInputLimitsLight.json")
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMRandom(t *testing.T) {
|
||||
func TestVmVMRandom(t *testing.T) {
|
||||
fns, _ := filepath.Glob(filepath.Join(baseDir, "RandomTests", "*"))
|
||||
for _, fn := range fns {
|
||||
if err := RunVmTest(fn, VmSkipTests); err != nil {
|
||||
|
@ -128,9 +128,9 @@ func runVmTests(tests map[string]VmTest, skipTests []string) error {
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
if skipTest[name] {
|
||||
if skipTest[name] /*|| name != "loop_stacklimit_1021"*/ {
|
||||
glog.Infoln("Skipping VM test", name)
|
||||
return nil
|
||||
continue
|
||||
}
|
||||
|
||||
if err := runVmTest(test); err != nil {
|
||||
@ -225,7 +225,7 @@ func RunVm(statedb *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs
|
||||
value = common.Big(exec["value"])
|
||||
)
|
||||
caller := statedb.GetOrNewStateObject(from)
|
||||
vm.Precompiled = make(map[string]*vm.PrecompiledAccount)
|
||||
vm.PrecompiledContracts = make(map[common.Address]vm.PrecompiledContract)
|
||||
|
||||
environment, _ := NewEVMEnvironment(true, chainConfig, statedb, env, exec)
|
||||
ret, err := environment.Call(caller, to, data, gas, value)
|
||||
|
Loading…
Reference in New Issue
Block a user