EVM Changed added

This commit is contained in:
AnilChinchawale 2019-03-22 17:39:27 +05:30
parent 1e77239ede
commit 2f02ac8043
32 changed files with 2145 additions and 1213 deletions

View file

@ -44,7 +44,7 @@ func disasmCmd(ctx *cli.Context) error {
return err
}
code := strings.TrimSpace(string(in))
code := strings.TrimSpace(string(in[:]))
fmt.Printf("%v\n", code)
return asm.PrintDisassembled(code)
}

View file

@ -25,7 +25,7 @@ import (
func Compile(fn string, src []byte, debug bool) (string, error) {
compiler := asm.NewCompiler(debug)
compiler.Feed(asm.Lex(src, debug))
compiler.Feed(asm.Lex(fn, src, debug))
bin, compileErrors := compiler.Compile()
if len(compileErrors) > 0 {

View file

@ -110,11 +110,6 @@ var (
Name: "nostack",
Usage: "disable stack output",
}
EVMInterpreterFlag = cli.StringFlag{
Name: "vm.evm",
Usage: "External EVM configuration (default = built-in interpreter)",
Value: "",
}
)
func init() {
@ -138,7 +133,6 @@ func init() {
ReceiverFlag,
DisableMemoryFlag,
DisableStackFlag,
EVMInterpreterFlag,
}
app.Commands = []cli.Command{
compileCommand,

View file

@ -21,20 +21,20 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"math/big"
"os"
goruntime "runtime"
"runtime/pprof"
"time"
goruntime "runtime"
"github.com/ethereum/go-ethereum/cmd/evm/internal/compiler"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/core/vm/runtime"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
cli "gopkg.in/urfave/cli.v1"
@ -76,20 +76,18 @@ func runCmd(ctx *cli.Context) error {
logconfig := &vm.LogConfig{
DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
Debug: ctx.GlobalBool(DebugFlag.Name),
}
var (
tracer vm.Tracer
debugLogger *vm.StructLogger
statedb *state.StateDB
chainConfig *params.ChainConfig
sender = common.BytesToAddress([]byte("sender"))
receiver = common.BytesToAddress([]byte("receiver"))
genesisConfig *core.Genesis
tracer vm.Tracer
debugLogger *vm.StructLogger
statedb *state.StateDB
chainConfig *params.ChainConfig
sender = common.StringToAddress("sender")
receiver = common.StringToAddress("receiver")
)
if ctx.GlobalBool(MachineFlag.Name) {
tracer = vm.NewJSONLogger(logconfig, os.Stdout)
tracer = NewJSONLogger(logconfig, os.Stdout)
} else if ctx.GlobalBool(DebugFlag.Name) {
debugLogger = vm.NewStructLogger(logconfig)
tracer = debugLogger
@ -98,14 +96,13 @@ func runCmd(ctx *cli.Context) error {
}
if ctx.GlobalString(GenesisFlag.Name) != "" {
gen := readGenesis(ctx.GlobalString(GenesisFlag.Name))
genesisConfig = gen
db := rawdb.NewMemoryDatabase()
db, _ := ethdb.NewMemDatabase()
genesis := gen.ToBlock(db)
statedb, _ = state.New(genesis.Root(), state.NewDatabase(db))
chainConfig = gen.Config
} else {
statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
genesisConfig = new(core.Genesis)
db, _ := ethdb.NewMemDatabase()
statedb, _ = state.New(common.Hash{}, state.NewDatabase(db))
}
if ctx.GlobalString(SenderFlag.Name) != "" {
sender = common.HexToAddress(ctx.GlobalString(SenderFlag.Name))
@ -157,23 +154,15 @@ func runCmd(ctx *cli.Context) error {
}
initialGas := ctx.GlobalUint64(GasFlag.Name)
if genesisConfig.GasLimit != 0 {
initialGas = genesisConfig.GasLimit
}
runtimeConfig := runtime.Config{
Origin: sender,
State: statedb,
GasLimit: initialGas,
GasPrice: utils.GlobalBig(ctx, PriceFlag.Name),
Value: utils.GlobalBig(ctx, ValueFlag.Name),
Difficulty: genesisConfig.Difficulty,
Time: new(big.Int).SetUint64(genesisConfig.Timestamp),
Coinbase: genesisConfig.Coinbase,
BlockNumber: new(big.Int).SetUint64(genesisConfig.Number),
Origin: sender,
State: statedb,
GasLimit: initialGas,
GasPrice: utils.GlobalBig(ctx, PriceFlag.Name),
Value: utils.GlobalBig(ctx, ValueFlag.Name),
EVMConfig: vm.Config{
Tracer: tracer,
Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),
EVMInterpreter: ctx.GlobalString(EVMInterpreterFlag.Name),
Tracer: tracer,
Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),
},
}
@ -207,7 +196,6 @@ func runCmd(ctx *cli.Context) error {
execTime := time.Since(tstart)
if ctx.GlobalBool(DumpFlag.Name) {
statedb.Commit(true)
statedb.IntermediateRoot(true)
fmt.Println(string(statedb.Dump()))
}
@ -246,7 +234,9 @@ Gas used: %d
`, execTime, mem.HeapObjects, mem.Alloc, mem.TotalAlloc, mem.NumGC, initialGas-leftOverGas)
}
if tracer == nil {
if tracer != nil {
tracer.CaptureEnd(ret, initialGas-leftOverGas, execTime, err)
} else {
fmt.Printf("0x%x\n", ret)
if err != nil {
fmt.Printf(" error: %v\n", err)

View file

@ -38,8 +38,6 @@ var stateTestCommand = cli.Command{
ArgsUsage: "<file>",
}
// StatetestResult contains the execution status after running a state test, any
// error that might have occurred and a dump of the final state if requested.
type StatetestResult struct {
Name string `json:"name"`
Pass bool `json:"pass"`
@ -68,7 +66,7 @@ func stateTestCmd(ctx *cli.Context) error {
)
switch {
case ctx.GlobalBool(MachineFlag.Name):
tracer = vm.NewJSONLogger(config, os.Stderr)
tracer = NewJSONLogger(config, os.Stderr)
case ctx.GlobalBool(DebugFlag.Name):
debugger = vm.NewStructLogger(config)
@ -97,10 +95,6 @@ func stateTestCmd(ctx *cli.Context) error {
// Run the test and aggregate the result
result := &StatetestResult{Name: key, Fork: st.Fork, Pass: true}
state, err := test.Run(st, cfg)
// print state root for evmlab tracing
if ctx.GlobalBool(MachineFlag.Name) && state != nil {
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%x\"}\n", state.IntermediateRoot(false))
}
if err != nil {
// Test failed, mark as so and dump any state to aid debugging
result.Pass, result.Error = false, err.Error()
@ -109,6 +103,10 @@ func stateTestCmd(ctx *cli.Context) error {
result.State = &dump
}
}
// print state root for evmlab tracing (already committed above, so no need to delete objects again
if ctx.GlobalBool(MachineFlag.Name) && state != nil {
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%x\"}\n", state.IntermediateRoot(false))
}
results = append(results, *result)

View file

@ -16,34 +16,6 @@
package vm
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
)
// destinations stores one map per contract (keyed by hash of code).
// The maps contain an entry for each location of a JUMPDEST
// instruction.
type destinations map[common.Hash]bitvec
// has checks whether code has a JUMPDEST at dest.
func (d destinations) has(codehash common.Hash, code []byte, dest *big.Int) bool {
// PC cannot go beyond len(code) and certainly can't be bigger than 63bits.
// Don't bother checking for JUMPDEST in that case.
udest := dest.Uint64()
if dest.BitLen() >= 63 || udest >= uint64(len(code)) {
return false
}
m, analysed := d[codehash]
if !analysed {
m = codeBitmap(code)
d[codehash] = m
}
return OpCode(code[udest]) == JUMPDEST && m.codeSegment(udest)
}
// bitvec is a bit vector which maps bytes in a program.
// An unset bit means the byte is an opcode, a set bit means
// it's data (i.e. argument of PUSHxx).

View file

@ -16,7 +16,11 @@
package vm
import "testing"
import (
"testing"
"github.com/ethereum/go-ethereum/crypto"
)
func TestJumpDestAnalysis(t *testing.T) {
tests := []struct {
@ -49,5 +53,23 @@ func TestJumpDestAnalysis(t *testing.T) {
t.Fatalf("expected %x, got %02x", test.exp, ret[test.which])
}
}
}
func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) {
// 1.4 ms
code := make([]byte, 1200000)
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
codeBitmap(code)
}
bench.StopTimer()
}
func BenchmarkJumpdestHashing_1200k(bench *testing.B) {
// 4 ms
code := make([]byte, 1200000)
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
crypto.Keccak256Hash(code)
}
bench.StopTimer()
}

View file

@ -23,13 +23,31 @@ import (
"github.com/ethereum/go-ethereum/common/math"
)
// calculates the memory size required for a step
func calcMemSize(off, l *big.Int) *big.Int {
if l.Sign() == 0 {
return common.Big0
// calcMemSize64 calculates the required memory size, and returns
// the size and whether the result overflowed uint64
func calcMemSize64(off, l *big.Int) (uint64, bool) {
if !l.IsUint64() {
return 0, true
}
return calcMemSize64WithUint(off, l.Uint64())
}
return new(big.Int).Add(off, l)
// calcMemSize64WithUint calculates the required memory size, and returns
// the size and whether the result overflowed uint64
// Identical to calcMemSize64, but length is a uint64
func calcMemSize64WithUint(off *big.Int, length64 uint64) (uint64, bool) {
// if length is zero, memsize is always zero, regardless of offset
if length64 == 0 {
return 0, false
}
// Check that offset doesn't overflow
if !off.IsUint64() {
return 0, true
}
offset64 := off.Uint64()
val := offset64 + length64
// if value < either of it's parts, then it overflowed
return val, val < offset64
}
// getData returns a slice from the data based on the start and size and pads
@ -59,7 +77,7 @@ func getDataBig(data []byte, start *big.Int, size *big.Int) []byte {
// bigUint64 returns the integer casted to a uint64 and returns whether it
// overflowed in the process.
func bigUint64(v *big.Int) (uint64, bool) {
return v.Uint64(), v.BitLen() > 64
return v.Uint64(), !v.IsUint64()
}
// toWordSize returns the ceiled word size required for memory expansion.

View file

@ -40,7 +40,7 @@ type AccountRef common.Address
func (ar AccountRef) Address() common.Address { return (common.Address)(ar) }
// Contract represents an ethereum contract in the state database. It contains
// the the contract code, calling arguments. Contract implements ContractRef
// the contract code, calling arguments. Contract implements ContractRef
type Contract struct {
// CallerAddress is the result of the caller which initialised this
// contract. However when the "call method" is delegated this value
@ -49,7 +49,8 @@ type Contract struct {
caller ContractRef
self ContractRef
jumpdests destinations // result of JUMPDEST analysis.
jumpdests map[common.Hash]bitvec // Aggregated result of JUMPDEST analysis.
analysis bitvec // Locally cached result of JUMPDEST analysis
Code []byte
CodeHash common.Hash
@ -58,21 +59,17 @@ type Contract struct {
Gas uint64
value *big.Int
Args []byte
DelegateCall bool
}
// NewContract returns a new contract environment for the execution of EVM.
func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uint64) *Contract {
c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil}
c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object}
if parent, ok := caller.(*Contract); ok {
// Reuse JUMPDEST analysis from parent context if available.
c.jumpdests = parent.jumpdests
} else {
c.jumpdests = make(destinations)
c.jumpdests = make(map[common.Hash]bitvec)
}
// Gas should be a pointer so it can safely be reduced through the run
@ -84,10 +81,42 @@ func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uin
return c
}
func (c *Contract) validJumpdest(dest *big.Int) bool {
udest := dest.Uint64()
// PC cannot go beyond len(code) and certainly can't be bigger than 63bits.
// Don't bother checking for JUMPDEST in that case.
if dest.BitLen() >= 63 || udest >= uint64(len(c.Code)) {
return false
}
// Only JUMPDESTs allowed for destinations
if OpCode(c.Code[udest]) != JUMPDEST {
return false
}
// Do we have a contract hash already?
if c.CodeHash != (common.Hash{}) {
// Does parent context have the analysis?
analysis, exist := c.jumpdests[c.CodeHash]
if !exist {
// Do the analysis and save in parent context
// We do not need to store it in c.analysis
analysis = codeBitmap(c.Code)
c.jumpdests[c.CodeHash] = analysis
}
return analysis.codeSegment(udest)
}
// We don't have the code hash, most likely a piece of initcode not already
// in state trie. In that case, we do an analysis, and save it locally, so
// we don't have to recalculate it for every JUMP instruction in the execution
// However, we don't save it within the parent context
if c.analysis == nil {
c.analysis = codeBitmap(c.Code)
}
return c.analysis.codeSegment(udest)
}
// AsDelegate sets the contract to be a delegate call and returns the current
// contract (for chaining calls)
func (c *Contract) AsDelegate() *Contract {
c.DelegateCall = true
// NOTE: caller must, at all times be a contract. It should never happen
// that caller is something other than a Contract.
parent := c.caller.(*Contract)
@ -138,16 +167,18 @@ func (c *Contract) Value() *big.Int {
return c.value
}
// SetCode sets the code to the contract
func (self *Contract) SetCode(hash common.Hash, code []byte) {
self.Code = code
self.CodeHash = hash
}
// SetCallCode sets the code of the contract and address of the backing data
// object
func (self *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) {
self.Code = code
self.CodeHash = hash
self.CodeAddr = addr
func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) {
c.Code = code
c.CodeHash = hash
c.CodeAddr = addr
}
// SetCodeOptionalHash can be used to provide code, but it's optional to provide hash.
// In case hash is not provided, the jumpdest analysis will not be saved to the parent context
func (c *Contract) SetCodeOptionalHash(addr *common.Address, codeAndHash *codeAndHash) {
c.Code = codeAndHash.code
c.CodeHash = codeAndHash.hash
c.CodeAddr = addr
}

View file

@ -116,7 +116,7 @@ func (c *sha256hash) Run(input []byte) ([]byte, error) {
return h[:], nil
}
// RIPMED160 implemented as a native contract.
// RIPEMD160 implemented as a native contract.
type ripemd160hash struct{}
// RequiredGas returns the gas required to execute the pre-compiled contract.

View file

@ -18,6 +18,7 @@ package vm
import "errors"
// List execution errors
var (
ErrOutOfGas = errors.New("out of gas")
ErrCodeStoreOutOfGas = errors.New("contract creation code storage out of gas")
@ -25,4 +26,5 @@ var (
ErrTraceLimitReached = errors.New("the number of logs reached the specified limit")
ErrInsufficientBalance = errors.New("insufficient balance for transfer")
ErrContractAddressCollision = errors.New("contract address collision")
ErrNoCompatibleInterpreter = errors.New("no compatible interpreter")
)

View file

@ -31,15 +31,17 @@ import (
var emptyCodeHash = crypto.Keccak256Hash(nil)
type (
// CanTransferFunc is the signature of a transfer guard function
CanTransferFunc func(StateDB, common.Address, *big.Int) bool
TransferFunc func(StateDB, common.Address, common.Address, *big.Int)
// TransferFunc is the signature of a transfer function
TransferFunc func(StateDB, common.Address, common.Address, *big.Int)
// GetHashFunc returns the nth block hash in the blockchain
// and is used by the BLOCKHASH EVM op code.
GetHashFunc func(uint64) common.Hash
)
// run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
func run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) {
if contract.CodeAddr != nil {
precompiles := PrecompiledContractsHomestead
if evm.ChainConfig().IsByzantium(evm.BlockNumber) {
@ -49,7 +51,20 @@ func run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
return RunPrecompiledContract(p, input, contract)
}
}
return evm.interpreter.Run(contract, input)
for _, interpreter := range evm.interpreters {
if interpreter.CanRun(contract.Code) {
if evm.interpreter != interpreter {
// Ensure that the interpreter pointer is set back
// to its current value upon return.
defer func(i Interpreter) {
evm.interpreter = i
}(evm.interpreter)
evm.interpreter = interpreter
}
return interpreter.Run(contract, input, readOnly)
}
}
return nil, ErrNoCompatibleInterpreter
}
// Context provides the EVM with auxiliary information. Once provided
@ -101,7 +116,8 @@ type EVM struct {
vmConfig Config
// global (to this context) ethereum virtual machine
// used throughout the execution of the tx.
interpreter *Interpreter
interpreters []Interpreter
interpreter Interpreter
// abort is used to abort the EVM calling operations
// NOTE: must be set atomically
abort int32
@ -115,14 +131,35 @@ type EVM struct {
// only ever be used *once*.
func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM {
evm := &EVM{
Context: ctx,
StateDB: statedb,
vmConfig: vmConfig,
chainConfig: chainConfig,
chainRules: chainConfig.Rules(ctx.BlockNumber),
Context: ctx,
StateDB: statedb,
vmConfig: vmConfig,
chainConfig: chainConfig,
chainRules: chainConfig.Rules(ctx.BlockNumber),
interpreters: make([]Interpreter, 0, 1),
}
evm.interpreter = NewInterpreter(evm, vmConfig)
if chainConfig.IsEWASM(ctx.BlockNumber) {
// to be implemented by EVM-C and Wagon PRs.
// if vmConfig.EWASMInterpreter != "" {
// extIntOpts := strings.Split(vmConfig.EWASMInterpreter, ":")
// path := extIntOpts[0]
// options := []string{}
// if len(extIntOpts) > 1 {
// options = extIntOpts[1..]
// }
// evm.interpreters = append(evm.interpreters, NewEVMVCInterpreter(evm, vmConfig, options))
// } else {
// evm.interpreters = append(evm.interpreters, NewEWASMInterpreter(evm, vmConfig))
// }
panic("No supported ewasm interpreter yet.")
}
// vmConfig.EVMInterpreter will be used by EVM-C, it won't be checked here
// as we always want to have the built-in EVM as the failover option.
evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, vmConfig))
evm.interpreter = evm.interpreters[0]
return evm
}
@ -132,6 +169,11 @@ func (evm *EVM) Cancel() {
atomic.StoreInt32(&evm.abort, 1)
}
// Interpreter returns the current interpreter
func (evm *EVM) Interpreter() Interpreter {
return evm.interpreter
}
// Call executes the contract associated with the addr with the given input as
// parameters. It also handles any necessary value transfer required and takes
// the necessary steps to create accounts and reverses the state in case of an
@ -160,17 +202,22 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
precompiles = PrecompiledContractsByzantium
}
if precompiles[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 {
// Calling a non existing account, don't do anything, but ping the tracer
if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil)
}
return nil, gas, nil
}
evm.StateDB.CreateAccount(addr)
}
evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
// Even if the account has no code, we need to continue because it might be a precompile
start := time.Now()
// Capture the tracer start/end events in debug mode
@ -181,7 +228,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
}()
}
ret, err = run(evm, contract, input)
ret, err = run(evm, contract, input, false)
// 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
@ -220,13 +267,12 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
snapshot = evm.StateDB.Snapshot()
to = AccountRef(caller.Address())
)
// initialise a new contract and set the code that is to be used by the
// EVM. The contract is a scoped environment for this execution context
// only.
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
ret, err = run(evm, contract, input)
ret, err = run(evm, contract, input, false)
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted {
@ -259,7 +305,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
contract := NewContract(caller, to, nil, gas).AsDelegate()
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
ret, err = run(evm, contract, input)
ret, err = run(evm, contract, input, false)
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted {
@ -281,28 +327,26 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
}
// Make sure the readonly is only set if we aren't in readonly yet
// this makes also sure that the readonly flag isn't removed for
// child calls.
if !evm.interpreter.readOnly {
evm.interpreter.readOnly = true
defer func() { evm.interpreter.readOnly = false }()
}
var (
to = AccountRef(addr)
snapshot = evm.StateDB.Snapshot()
)
// Initialise a new contract and set the code that is to be used by the
// EVM. The contract is a scoped environment for this execution context
// only.
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, to, new(big.Int), gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
// We do an AddBalance of zero here, just in order to trigger a touch.
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
// but is the correct thing to do and matters on other networks, in tests, and potential
// future scenarios
evm.StateDB.AddBalance(addr, bigZero)
// 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.
ret, err = run(evm, contract, input)
ret, err = run(evm, contract, input, true)
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted {
@ -312,9 +356,20 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
return ret, contract.Gas, err
}
// Create creates a new contract using code as deployment code.
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
type codeAndHash struct {
code []byte
hash common.Hash
}
func (c *codeAndHash) Hash() common.Hash {
if c.hash == (common.Hash{}) {
c.hash = crypto.Keccak256Hash(c.code)
}
return c.hash
}
// create creates a new contract using code as deployment code.
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address) ([]byte, common.Address, uint64, error) {
// Depth check execution. Fail if we're trying to execute above the
// limit.
if evm.depth > int(params.CallCreateDepth) {
@ -323,39 +378,37 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, common.Address{}, gas, ErrInsufficientBalance
}
// Ensure there's no existing contract already at the designated address
nonce := evm.StateDB.GetNonce(caller.Address())
evm.StateDB.SetNonce(caller.Address(), nonce+1)
contractAddr = crypto.CreateAddress(caller.Address(), nonce)
contractHash := evm.StateDB.GetCodeHash(contractAddr)
if evm.StateDB.GetNonce(contractAddr) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) {
// Ensure there's no existing contract already at the designated address
contractHash := evm.StateDB.GetCodeHash(address)
if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) {
return nil, common.Address{}, 0, ErrContractAddressCollision
}
// Create a new account on the state
snapshot := evm.StateDB.Snapshot()
evm.StateDB.CreateAccount(contractAddr)
evm.StateDB.CreateAccount(address)
if evm.ChainConfig().IsEIP158(evm.BlockNumber) {
evm.StateDB.SetNonce(contractAddr, 1)
evm.StateDB.SetNonce(address, 1)
}
evm.Transfer(evm.StateDB, caller.Address(), contractAddr, value)
evm.Transfer(evm.StateDB, caller.Address(), address, value)
// initialise a new contract and set the code that is to be used by the
// EVM. The contract is a scoped environment for this execution context
// only.
contract := NewContract(caller, AccountRef(contractAddr), value, gas)
contract.SetCallCode(&contractAddr, crypto.Keccak256Hash(code), code)
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, AccountRef(address), value, gas)
contract.SetCodeOptionalHash(&address, codeAndHash)
if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, contractAddr, gas, nil
return nil, address, gas, nil
}
if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(caller.Address(), contractAddr, true, code, gas, value)
evm.vmConfig.Tracer.CaptureStart(caller.Address(), address, true, codeAndHash.code, gas, value)
}
start := time.Now()
ret, err = run(evm, contract, nil)
ret, err := run(evm, contract, nil, false)
// check whether the max code size has been exceeded
maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize
@ -366,7 +419,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
if err == nil && !maxCodeSizeExceeded {
createDataGas := uint64(len(ret)) * params.CreateDataGas
if contract.UseGas(createDataGas) {
evm.StateDB.SetCode(contractAddr, ret)
evm.StateDB.SetCode(address, ret)
} else {
err = ErrCodeStoreOutOfGas
}
@ -388,11 +441,25 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
}
return ret, contractAddr, contract.Gas, err
return ret, address, contract.Gas, err
}
// Create creates a new contract using code as deployment code.
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr)
}
// Create2 creates a new contract using code as deployment code.
//
// The different between Create2 with Create is Create2 uses sha3(0xff ++ msg.sender ++ salt ++ sha3(init_code))[12:]
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
codeAndHash := &codeAndHash{code: code}
contractAddr = crypto.CreateAddress2(caller.Address(), common.BigToHash(salt), codeAndHash.Hash().Bytes())
return evm.create(caller, codeAndHash, gas, endowment, contractAddr)
}
// ChainConfig returns the environment's chain configuration
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
// Interpreter returns the EVM interpreter
func (evm *EVM) Interpreter() *Interpreter { return evm.interpreter }

View file

@ -22,6 +22,7 @@ import (
"github.com/ethereum/go-ethereum/params"
)
// Gas costs
const (
GasQuickStep uint64 = 2
GasFastestStep uint64 = 3
@ -29,10 +30,6 @@ const (
GasMidStep uint64 = 8
GasSlowStep uint64 = 10
GasExtStep uint64 = 20
GasReturn uint64 = 0
GasStop uint64 = 0
GasContractByte uint64 = 200
)
// calcGas returns the actual gas cost of the call.
@ -46,11 +43,11 @@ func callGas(gasTable params.GasTable, availableGas, base uint64, callCost *big.
// If the bit length exceeds 64 bit we know that the newly calculated "gas" for EIP150
// is smaller than the requested amount. Therefor we return the new gas instead
// of returning an error.
if callCost.BitLen() > 64 || gas < callCost.Uint64() {
if !callCost.IsUint64() || gas < callCost.Uint64() {
return gas, nil
}
}
if callCost.BitLen() > 64 {
if !callCost.IsUint64() {
return 0, errGasUintOverflow
}

View file

@ -22,7 +22,7 @@ import (
"github.com/ethereum/go-ethereum/params"
)
// memoryGasCosts calculates the quadratic gas for memory expansion. It does so
// memoryGasCost calculates the quadratic gas for memory expansion. It does so
// only for the memory region that is expanded, not the total memory.
func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
@ -57,12 +57,6 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
return 0, nil
}
func constGasFunc(gas uint64) gasFunc {
return func(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return gas, nil
}
}
func gasCallDataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
@ -117,24 +111,71 @@ func gasReturnDataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *
func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
y, x = stack.Back(1), stack.Back(0)
val = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
y, x = stack.Back(1), stack.Back(0)
current = evm.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 params.SstoreSetGas, nil
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
evm.StateDB.AddRefund(params.SstoreRefundGas)
return params.SstoreClearGas, nil
} else {
// non 0 => non 0 (or 0 => 0)
return params.SstoreResetGas, nil
// The legacy gas metering only takes into consideration the current state
// Legacy rules should be applied if we are in Petersburg (removal of EIP-1283)
// OR Constantinople is not active
if evm.chainRules.IsPetersburg || !evm.chainRules.IsConstantinople {
// 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)
switch {
case current == (common.Hash{}) && y.Sign() != 0: // 0 => non 0
return params.SstoreSetGas, nil
case current != (common.Hash{}) && y.Sign() == 0: // non 0 => 0
evm.StateDB.AddRefund(params.SstoreRefundGas)
return params.SstoreClearGas, nil
default: // non 0 => non 0 (or 0 => 0)
return params.SstoreResetGas, nil
}
}
// The new gas metering is based on net gas costs (EIP-1283):
//
// 1. If current value equals new value (this is a no-op), 200 gas is deducted.
// 2. If current value does not equal new value
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context)
// 2.1.1. If original value is 0, 20000 gas is deducted.
// 2.1.2. Otherwise, 5000 gas is deducted. If new value is 0, add 15000 gas to refund counter.
// 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses.
// 2.2.1. If original value is not 0
// 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0.
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter.
// 2.2.2. If original value equals new value (this storage slot is reset)
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
// 2.2.2.2. Otherwise, add 4800 gas to refund counter.
value := common.BigToHash(y)
if current == value { // noop (1)
return params.NetSstoreNoopGas, nil
}
original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x))
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return params.NetSstoreInitGas, nil
}
if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(params.NetSstoreClearRefund)
}
return params.NetSstoreCleanGas, nil // write existing slot (2.1.2)
}
if original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
evm.StateDB.SubRefund(params.NetSstoreClearRefund)
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
evm.StateDB.AddRefund(params.NetSstoreClearRefund)
}
}
if original == value {
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
evm.StateDB.AddRefund(params.NetSstoreResetClearRefund)
} else { // reset to original existing slot (2.2.2.2)
evm.StateDB.AddRefund(params.NetSstoreResetRefund)
}
}
return params.NetSstoreDirtyGas, nil
}
func makeGasLog(n uint64) gasFunc {
@ -241,6 +282,10 @@ func gasExtCodeCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Sta
return gas, nil
}
func gasExtCodeHash(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return gt.ExtcodeHash, nil
}
func gasMLoad(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var overflow bool
gas, err := memoryGasCost(mem, memorySize)
@ -289,6 +334,29 @@ func gasCreate(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, m
return gas, nil
}
func gasCreate2(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var overflow bool
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
}
if gas, overflow = math.SafeAdd(gas, params.Create2Gas); overflow {
return 0, errGasUintOverflow
}
wordGas, overflow := bigUint64(stack.Back(2))
if overflow {
return 0, errGasUintOverflow
}
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow {
return 0, errGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
return 0, errGasUintOverflow
}
return gas, nil
}
func gasBalance(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return gt.Balance, nil
}
@ -308,7 +376,7 @@ func gasExp(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem
gas = expByteLen * gt.ExpByte // no overflow check required. Max is 256 * ExpByte gas
overflow bool
)
if gas, overflow = math.SafeAdd(gas, GasSlowStep); overflow {
if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
return 0, errGasUintOverflow
}
return gas, nil
@ -447,15 +515,3 @@ func gasStaticCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stac
}
return gas, nil
}
func gasPush(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return GasFastestStep, nil
}
func gasSwap(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return GasFastestStep, nil
}
func gasDup(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return GasFastestStep, nil
}

View file

@ -13,20 +13,22 @@ import (
var _ = (*structLogMarshaling)(nil)
// MarshalJSON marshals as JSON.
func (s StructLog) MarshalJSON() ([]byte, error) {
type StructLog struct {
Pc uint64 `json:"pc"`
Op OpCode `json:"op"`
Gas math.HexOrDecimal64 `json:"gas"`
GasCost math.HexOrDecimal64 `json:"gasCost"`
Memory hexutil.Bytes `json:"memory"`
MemorySize int `json:"memSize"`
Stack []*math.HexOrDecimal256 `json:"stack"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth int `json:"depth"`
Err error `json:"-"`
OpName string `json:"opName"`
ErrorString string `json:"error"`
Pc uint64 `json:"pc"`
Op OpCode `json:"op"`
Gas math.HexOrDecimal64 `json:"gas"`
GasCost math.HexOrDecimal64 `json:"gasCost"`
Memory hexutil.Bytes `json:"memory"`
MemorySize int `json:"memSize"`
Stack []*math.HexOrDecimal256 `json:"stack"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth int `json:"depth"`
RefundCounter uint64 `json:"refund"`
Err error `json:"-"`
OpName string `json:"opName"`
ErrorString string `json:"error"`
}
var enc StructLog
enc.Pc = s.Pc
@ -43,24 +45,27 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
}
enc.Storage = s.Storage
enc.Depth = s.Depth
enc.RefundCounter = s.RefundCounter
enc.Err = s.Err
enc.OpName = s.OpName()
enc.ErrorString = s.ErrorString()
return json.Marshal(&enc)
}
// UnmarshalJSON unmarshals from JSON.
func (s *StructLog) UnmarshalJSON(input []byte) error {
type StructLog struct {
Pc *uint64 `json:"pc"`
Op *OpCode `json:"op"`
Gas *math.HexOrDecimal64 `json:"gas"`
GasCost *math.HexOrDecimal64 `json:"gasCost"`
Memory *hexutil.Bytes `json:"memory"`
MemorySize *int `json:"memSize"`
Stack []*math.HexOrDecimal256 `json:"stack"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth *int `json:"depth"`
Err error `json:"-"`
Pc *uint64 `json:"pc"`
Op *OpCode `json:"op"`
Gas *math.HexOrDecimal64 `json:"gas"`
GasCost *math.HexOrDecimal64 `json:"gasCost"`
Memory *hexutil.Bytes `json:"memory"`
MemorySize *int `json:"memSize"`
Stack []*math.HexOrDecimal256 `json:"stack"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth *int `json:"depth"`
RefundCounter *uint64 `json:"refund"`
Err error `json:"-"`
}
var dec StructLog
if err := json.Unmarshal(input, &dec); err != nil {
@ -96,6 +101,9 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
if dec.Depth != nil {
s.Depth = *dec.Depth
}
if dec.RefundCounter != nil {
s.RefundCounter = *dec.RefundCounter
}
if dec.Err != nil {
s.Err = dec.Err
}

File diff suppressed because it is too large Load diff

View file

@ -17,10 +17,12 @@
package vm
import (
"bytes"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)
@ -30,19 +32,23 @@ type twoOperandTest struct {
expected string
}
func testTwoOperandOp(t *testing.T, tests []twoOperandTest, opFn func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)) {
func testTwoOperandOp(t *testing.T, tests []twoOperandTest, opFn func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)) {
var (
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
stack = newstack()
pc = uint64(0)
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
stack = newstack()
pc = uint64(0)
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
)
env.interpreter = evmInterpreter
evmInterpreter.intPool = poolOfIntPools.get()
for i, test := range tests {
x := new(big.Int).SetBytes(common.Hex2Bytes(test.x))
shift := new(big.Int).SetBytes(common.Hex2Bytes(test.y))
expected := new(big.Int).SetBytes(common.Hex2Bytes(test.expected))
stack.push(x)
stack.push(shift)
opFn(&pc, env, nil, nil, stack)
opFn(&pc, evmInterpreter, nil, nil, stack)
actual := stack.pop()
if actual.Cmp(expected) != 0 {
t.Errorf("Testcase %d, expected %v, got %v", i, expected, actual)
@ -50,13 +56,13 @@ func testTwoOperandOp(t *testing.T, tests []twoOperandTest, opFn func(pc *uint64
// Check pool usage
// 1.pool is not allowed to contain anything on the stack
// 2.pool is not allowed to contain the same pointers twice
if env.interpreter.intPool.pool.len() > 0 {
if evmInterpreter.intPool.pool.len() > 0 {
poolvals := make(map[*big.Int]struct{})
poolvals[actual] = struct{}{}
for env.interpreter.intPool.pool.len() > 0 {
key := env.interpreter.intPool.get()
for evmInterpreter.intPool.pool.len() > 0 {
key := evmInterpreter.intPool.get()
if _, exist := poolvals[key]; exist {
t.Errorf("Testcase %d, pool contains double-entry", i)
}
@ -64,13 +70,18 @@ func testTwoOperandOp(t *testing.T, tests []twoOperandTest, opFn func(pc *uint64
}
}
}
poolOfIntPools.put(evmInterpreter.intPool)
}
func TestByteOp(t *testing.T) {
var (
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
stack = newstack()
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
stack = newstack()
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
)
env.interpreter = evmInterpreter
evmInterpreter.intPool = poolOfIntPools.get()
tests := []struct {
v string
th uint64
@ -91,12 +102,13 @@ func TestByteOp(t *testing.T) {
th := new(big.Int).SetUint64(test.th)
stack.push(val)
stack.push(th)
opByte(&pc, env, nil, nil, stack)
opByte(&pc, evmInterpreter, nil, nil, stack)
actual := stack.pop()
if actual.Cmp(test.expected) != 0 {
t.Fatalf("Expected [%v] %v:th byte to be %v, was %v.", test.v, test.th, test.expected, actual)
}
}
poolOfIntPools.put(evmInterpreter.intPool)
}
func TestSHL(t *testing.T) {
@ -196,11 +208,15 @@ func TestSLT(t *testing.T) {
testTwoOperandOp(t, tests, opSlt)
}
func opBenchmark(bench *testing.B, op func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error), args ...string) {
func opBenchmark(bench *testing.B, op func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error), args ...string) {
var (
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
stack = newstack()
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
stack = newstack()
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
)
env.interpreter = evmInterpreter
evmInterpreter.intPool = poolOfIntPools.get()
// convert args
byteArgs := make([][]byte, len(args))
for i, arg := range args {
@ -213,9 +229,10 @@ func opBenchmark(bench *testing.B, op func(pc *uint64, evm *EVM, contract *Contr
a := new(big.Int).SetBytes(arg)
stack.push(a)
}
op(&pc, env, nil, nil, stack)
op(&pc, evmInterpreter, nil, nil, stack)
stack.pop()
}
poolOfIntPools.put(evmInterpreter.intPool)
}
func BenchmarkOpAdd64(b *testing.B) {
@ -425,3 +442,148 @@ func BenchmarkOpIsZero(b *testing.B) {
x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff"
opBenchmark(b, opIszero, x)
}
func TestOpMstore(t *testing.T) {
var (
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
)
env.interpreter = evmInterpreter
evmInterpreter.intPool = poolOfIntPools.get()
mem.Resize(64)
pc := uint64(0)
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
stack.pushN(new(big.Int).SetBytes(common.Hex2Bytes(v)), big.NewInt(0))
opMstore(&pc, evmInterpreter, nil, mem, stack)
if got := common.Bytes2Hex(mem.Get(0, 32)); got != v {
t.Fatalf("Mstore fail, got %v, expected %v", got, v)
}
stack.pushN(big.NewInt(0x1), big.NewInt(0))
opMstore(&pc, evmInterpreter, nil, mem, stack)
if common.Bytes2Hex(mem.Get(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
t.Fatalf("Mstore failed to overwrite previous value")
}
poolOfIntPools.put(evmInterpreter.intPool)
}
func BenchmarkOpMstore(bench *testing.B) {
var (
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
)
env.interpreter = evmInterpreter
evmInterpreter.intPool = poolOfIntPools.get()
mem.Resize(64)
pc := uint64(0)
memStart := big.NewInt(0)
value := big.NewInt(0x1337)
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
stack.pushN(value, memStart)
opMstore(&pc, evmInterpreter, nil, mem, stack)
}
poolOfIntPools.put(evmInterpreter.intPool)
}
func BenchmarkOpSHA3(bench *testing.B) {
var (
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
)
env.interpreter = evmInterpreter
evmInterpreter.intPool = poolOfIntPools.get()
mem.Resize(32)
pc := uint64(0)
start := big.NewInt(0)
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
stack.pushN(big.NewInt(32), start)
opSha3(&pc, evmInterpreter, nil, mem, stack)
}
poolOfIntPools.put(evmInterpreter.intPool)
}
func TestCreate2Addreses(t *testing.T) {
type testcase struct {
origin string
salt string
code string
expected string
}
for i, tt := range []testcase{
{
origin: "0x0000000000000000000000000000000000000000",
salt: "0x0000000000000000000000000000000000000000",
code: "0x00",
expected: "0x4d1a2e2bb4f88f0250f26ffff098b0b30b26bf38",
},
{
origin: "0xdeadbeef00000000000000000000000000000000",
salt: "0x0000000000000000000000000000000000000000",
code: "0x00",
expected: "0xB928f69Bb1D91Cd65274e3c79d8986362984fDA3",
},
{
origin: "0xdeadbeef00000000000000000000000000000000",
salt: "0xfeed000000000000000000000000000000000000",
code: "0x00",
expected: "0xD04116cDd17beBE565EB2422F2497E06cC1C9833",
},
{
origin: "0x0000000000000000000000000000000000000000",
salt: "0x0000000000000000000000000000000000000000",
code: "0xdeadbeef",
expected: "0x70f2b2914A2a4b783FaEFb75f459A580616Fcb5e",
},
{
origin: "0x00000000000000000000000000000000deadbeef",
salt: "0xcafebabe",
code: "0xdeadbeef",
expected: "0x60f3f640a8508fC6a86d45DF051962668E1e8AC7",
},
{
origin: "0x00000000000000000000000000000000deadbeef",
salt: "0xcafebabe",
code: "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
expected: "0x1d8bfDC5D46DC4f61D6b6115972536eBE6A8854C",
},
{
origin: "0x0000000000000000000000000000000000000000",
salt: "0x0000000000000000000000000000000000000000",
code: "0x",
expected: "0xE33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0",
},
} {
origin := common.BytesToAddress(common.FromHex(tt.origin))
salt := common.BytesToHash(common.FromHex(tt.salt))
code := common.FromHex(tt.code)
codeHash := crypto.Keccak256(code)
address := crypto.CreateAddress2(origin, salt, codeHash)
/*
stack := newstack()
// salt, but we don't need that for this test
stack.push(big.NewInt(int64(len(code)))) //size
stack.push(big.NewInt(0)) // memstart
stack.push(big.NewInt(0)) // value
gas, _ := gasCreate2(params.GasTable{}, nil, nil, stack, nil, 0)
fmt.Printf("Example %d\n* address `0x%x`\n* salt `0x%x`\n* init_code `0x%x`\n* gas (assuming no mem expansion): `%v`\n* result: `%s`\n\n", i,origin, salt, code, gas, address.String())
*/
expected := common.BytesToAddress(common.FromHex(tt.expected))
if !bytes.Equal(expected.Bytes(), address.Bytes()) {
t.Errorf("test %d: expected %s, got %s", i, expected.String(), address.String())
}
}
}

View file

@ -40,8 +40,10 @@ type StateDB interface {
GetCodeSize(common.Address) int
AddRefund(uint64)
SubRefund(uint64)
GetRefund() uint64
GetCommittedState(common.Address, common.Hash) common.Hash
GetState(common.Address, common.Hash) common.Hash
SetState(common.Address, common.Hash, common.Hash)
@ -64,7 +66,7 @@ type StateDB interface {
ForEachStorage(common.Address, func(common.Hash, common.Hash) bool)
}
// CallContext provides a basic interface for the EVM calling conventions. The EVM EVM
// CallContext provides a basic interface for the EVM calling conventions. The EVM
// depends on this context being implemented for doing subcalls and initialising new EVM contracts.
type CallContext interface {
// Call another contract

View file

@ -18,8 +18,10 @@ package vm
import (
"fmt"
"hash"
"sync/atomic"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/params"
)
@ -39,24 +41,60 @@ type Config struct {
// may be left uninitialised and will be set to the default
// table.
JumpTable [256]operation
// Type of the EWASM interpreter
EWASMInterpreter string
// Type of the EVM interpreter
EVMInterpreter string
}
// Interpreter is used to run Ethereum based contracts and will utilise the
// passed environment to query external sources for state information.
// The Interpreter will run the byte code VM based on the passed
// configuration.
type Interpreter struct {
type Interpreter interface {
// Run loops and evaluates the contract's code with the given input data and returns
// the return byte-slice and an error if one occurred.
Run(contract *Contract, input []byte, static bool) ([]byte, error)
// CanRun tells if the contract, passed as an argument, can be
// run by the current interpreter. This is meant so that the
// caller can do something like:
//
// ```golang
// for _, interpreter := range interpreters {
// if interpreter.CanRun(contract.code) {
// interpreter.Run(contract.code, input)
// }
// }
// ```
CanRun([]byte) bool
}
// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports
// Read to get a variable amount of data from the hash state. Read is faster than Sum
// because it doesn't copy the internal state, but also modifies the internal state.
type keccakState interface {
hash.Hash
Read([]byte) (int, error)
}
// EVMInterpreter represents an EVM interpreter
type EVMInterpreter struct {
evm *EVM
cfg Config
gasTable params.GasTable
intPool *intPool
intPool *intPool
hasher keccakState // Keccak256 hasher instance shared across opcodes
hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes
readOnly bool // Whether to throw on stateful modifications
returnData []byte // Last CALL's return data for subsequent reuse
}
// NewInterpreter returns a new instance of the Interpreter.
func NewInterpreter(evm *EVM, cfg Config) *Interpreter {
// NewEVMInterpreter returns a new instance of the Interpreter.
func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
// We use the STOP instruction whether to see
// the jump table was initialised. If it was not
// we'll set the default jump table.
@ -73,41 +111,39 @@ func NewInterpreter(evm *EVM, cfg Config) *Interpreter {
}
}
return &Interpreter{
return &EVMInterpreter{
evm: evm,
cfg: cfg,
gasTable: evm.ChainConfig().GasTable(evm.BlockNumber),
intPool: newIntPool(),
}
}
func (in *Interpreter) enforceRestrictions(op OpCode, operation operation, stack *Stack) error {
if in.evm.chainRules.IsByzantium {
if in.readOnly {
// If the interpreter is operating in readonly mode, make sure no
// state-modifying operation is performed. The 3rd stack item
// for a call operation is the value. Transferring value from one
// account to the others means the state is modified and should also
// return with an error.
if operation.writes || (op == CALL && stack.Back(2).BitLen() > 0) {
return errWriteProtection
}
}
}
return nil
}
// Run loops and evaluates the contract's code with the given input data and returns
// the return byte-slice and an error if one occurred.
//
// It's important to note that any errors returned by the interpreter should be
// considered a revert-and-consume-all-gas operation except for
// errExecutionReverted which means revert-and-keep-gas-left.
func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err error) {
func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
if in.intPool == nil {
in.intPool = poolOfIntPools.get()
defer func() {
poolOfIntPools.put(in.intPool)
in.intPool = nil
}()
}
// Increment the call depth which is restricted to 1024
in.evm.depth++
defer func() { in.evm.depth-- }()
// Make sure the readOnly is only set if we aren't in readOnly yet.
// This makes also sure that the readOnly flag isn't removed for child calls.
if readOnly && !in.readOnly {
in.readOnly = true
defer func() { in.readOnly = false }()
}
// Reset the previous call's return data. It's unimportant to preserve the old buffer
// as every returning call will return new data anyway.
in.returnData = nil
@ -130,9 +166,13 @@ func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err er
pcCopy uint64 // needed for the deferred Tracer
gasCopy uint64 // for Tracer to log gas remaining before execution
logged bool // deferred Tracer should ignore already logged steps
res []byte // result of the opcode execution function
)
contract.Input = input
// Reclaim the stack as an int pool when the execution stops
defer func() { in.intPool.put(stack.data...) }()
if in.cfg.Debug {
defer func() {
if err != nil {
@ -161,19 +201,35 @@ func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err er
if !operation.valid {
return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
}
if err := operation.validateStack(stack); err != nil {
return nil, err
// Validate stack
if sLen := stack.len(); sLen < operation.minStack {
return nil, fmt.Errorf("stack underflow (%d <=> %d)", sLen, operation.minStack)
} else if sLen > operation.maxStack {
return nil, fmt.Errorf("stack limit reached %d (%d)", sLen, operation.maxStack)
}
// If the operation is valid, enforce and write restrictions
if err := in.enforceRestrictions(op, operation, stack); err != nil {
return nil, err
if in.readOnly && in.evm.chainRules.IsByzantium {
// If the interpreter is operating in readonly mode, make sure no
// state-modifying operation is performed. The 3rd stack item
// for a call operation is the value. Transferring value from one
// account to the others means the state is modified and should also
// return with an error.
if operation.writes || (op == CALL && stack.Back(2).Sign() != 0) {
return nil, errWriteProtection
}
}
// Static portion of gas
if !contract.UseGas(operation.constantGas) {
return nil, ErrOutOfGas
}
var memorySize uint64
// calculate the new memory size and expand the memory to fit
// the operation
// Memory check needs to be done prior to evaluating the dynamic gas portion,
// to detect calculation overflows
if operation.memorySize != nil {
memSize, overflow := bigUint64(operation.memorySize(stack))
memSize, overflow := operation.memorySize(stack)
if overflow {
return nil, errGasUintOverflow
}
@ -183,11 +239,14 @@ func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err er
return nil, errGasUintOverflow
}
}
// Dynamic portion of gas
// consume the gas and return an error if not enough gas is available.
// cost is explicitly set so that the capture state defer method can get the proper cost
cost, err = operation.gasCost(in.gasTable, in.evm, contract, stack, mem, memorySize)
if err != nil || !contract.UseGas(cost) {
return nil, ErrOutOfGas
if operation.dynamicGas != nil {
cost, err = operation.dynamicGas(in.gasTable, in.evm, contract, stack, mem, memorySize)
if err != nil || !contract.UseGas(cost) {
return nil, ErrOutOfGas
}
}
if memorySize > 0 {
mem.Resize(memorySize)
@ -199,7 +258,7 @@ func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err er
}
// execute the operation
res, err := operation.execute(&pc, in.evm, contract, mem, stack)
res, err = operation.execute(&pc, in, contract, mem, stack)
// verifyPool is a build flag. Pool verification makes sure the integrity
// of the integer pool by comparing values to a default value.
if verifyPool {
@ -224,3 +283,9 @@ func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err er
}
return nil, nil
}
// CanRun tells if the contract, passed as an argument, can be
// run by the current interpreter.
func (in *EVMInterpreter) CanRun(code []byte) bool {
return true
}

View file

@ -16,7 +16,10 @@
package vm
import "math/big"
import (
"math/big"
"sync"
)
var checkVal = big.NewInt(-42)
@ -65,3 +68,39 @@ func (p *intPool) put(is ...*big.Int) {
p.pool.push(i)
}
}
// The intPool pool's default capacity
const poolDefaultCap = 25
// intPoolPool manages a pool of intPools.
type intPoolPool struct {
pools []*intPool
lock sync.Mutex
}
var poolOfIntPools = &intPoolPool{
pools: make([]*intPool, 0, poolDefaultCap),
}
// get is looking for an available pool to return.
func (ipp *intPoolPool) get() *intPool {
ipp.lock.Lock()
defer ipp.lock.Unlock()
if len(poolOfIntPools.pools) > 0 {
ip := ipp.pools[len(ipp.pools)-1]
ipp.pools = ipp.pools[:len(ipp.pools)-1]
return ip
}
return newIntPool()
}
// put a pool that has been allocated with get.
func (ipp *intPoolPool) put(ip *intPool) {
ipp.lock.Lock()
defer ipp.lock.Unlock()
if len(ipp.pools) < cap(ipp.pools) {
ipp.pools = append(ipp.pools, ip)
}
}

55
core/vm/intpool_test.go Normal file
View file

@ -0,0 +1,55 @@
// Copyright 2018 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"
)
func TestIntPoolPoolGet(t *testing.T) {
poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap)
nip := poolOfIntPools.get()
if nip == nil {
t.Fatalf("Invalid pool allocation")
}
}
func TestIntPoolPoolPut(t *testing.T) {
poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap)
nip := poolOfIntPools.get()
if len(poolOfIntPools.pools) != 0 {
t.Fatalf("Pool got added to list when none should have been")
}
poolOfIntPools.put(nip)
if len(poolOfIntPools.pools) == 0 {
t.Fatalf("Pool did not get added to list when one should have been")
}
}
func TestIntPoolPoolReUse(t *testing.T) {
poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap)
nip := poolOfIntPools.get()
poolOfIntPools.put(nip)
poolOfIntPools.get()
if len(poolOfIntPools.pools) != 0 {
t.Fatalf("Invalid number of pools. Got %d, expected %d", len(poolOfIntPools.pools), 0)
}
}

File diff suppressed because it is too large Load diff

View file

@ -29,11 +29,13 @@ import (
"github.com/ethereum/go-ethereum/core/types"
)
// Storage represents a contract's storage.
type Storage map[common.Hash]common.Hash
func (self Storage) Copy() Storage {
// Copy duplicates the current storage.
func (s Storage) Copy() Storage {
cpy := make(Storage)
for key, value := range self {
for key, value := range s {
cpy[key] = value
}
@ -45,6 +47,7 @@ type LogConfig struct {
DisableMemory bool // disable memory capture
DisableStack bool // disable stack capture
DisableStorage bool // disable storage capture
Debug bool // print output during capture end
Limit int // maximum length of output, but zero means unlimited
}
@ -53,16 +56,17 @@ type LogConfig struct {
// 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 `json:"pc"`
Op OpCode `json:"op"`
Gas uint64 `json:"gas"`
GasCost uint64 `json:"gasCost"`
Memory []byte `json:"memory"`
MemorySize int `json:"memSize"`
Stack []*big.Int `json:"stack"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth int `json:"depth"`
Err error `json:"-"`
Pc uint64 `json:"pc"`
Op OpCode `json:"op"`
Gas uint64 `json:"gas"`
GasCost uint64 `json:"gasCost"`
Memory []byte `json:"memory"`
MemorySize int `json:"memSize"`
Stack []*big.Int `json:"stack"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth int `json:"depth"`
RefundCounter uint64 `json:"refund"`
Err error `json:"-"`
}
// overrides for gencodec
@ -75,10 +79,12 @@ type structLogMarshaling struct {
ErrorString string `json:"error"` // adds call to ErrorString() in MarshalJSON
}
// OpName formats the operand name in a human-readable format.
func (s *StructLog) OpName() string {
return s.Op.String()
}
// ErrorString formats the log's error as a string.
func (s *StructLog) ErrorString() string {
if s.Err != nil {
return s.Err.Error()
@ -123,6 +129,7 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
return logger
}
// CaptureStart implements the Tracer interface to initialize the tracing operation.
func (l *StructLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
return nil
}
@ -171,19 +178,28 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
storage = l.changedValues[contract.Address()].Copy()
}
// create a new snaptshot of the EVM.
log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, err}
log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, env.StateDB.GetRefund(), err}
l.logs = append(l.logs, log)
return nil
}
// CaptureFault implements the Tracer interface to trace an execution fault
// while running an opcode.
func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
return nil
}
// CaptureEnd is called after the call finishes to finalize the tracing.
func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
l.output = output
l.err = err
if l.cfg.Debug {
fmt.Printf("0x%x\n", output)
if err != nil {
fmt.Printf(" error: %v\n", err)
}
}
return nil
}

87
core/vm/logger_json.go Normal file
View file

@ -0,0 +1,87 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package vm
import (
"encoding/json"
"io"
"math/big"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
)
type JSONLogger struct {
encoder *json.Encoder
cfg *LogConfig
}
// NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects
// into the provided stream.
func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger {
l := &JSONLogger{json.NewEncoder(writer), cfg}
if l.cfg == nil {
l.cfg = &LogConfig{}
}
return l
}
func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
return nil
}
// CaptureState outputs state information on the logger.
func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
log := StructLog{
Pc: pc,
Op: op,
Gas: gas,
GasCost: cost,
MemorySize: memory.Len(),
Storage: nil,
Depth: depth,
RefundCounter: env.StateDB.GetRefund(),
Err: err,
}
if !l.cfg.DisableMemory {
log.Memory = memory.Data()
}
if !l.cfg.DisableStack {
log.Stack = stack.Data()
}
return l.encoder.Encode(log)
}
// CaptureFault outputs state information on the logger.
func (l *JSONLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
return nil
}
// CaptureEnd is triggered at end of execution.
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
type endLog struct {
Output string `json:"output"`
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
Time time.Duration `json:"time"`
Err string `json:"error,omitempty"`
}
if err != nil {
return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, err.Error()})
}
return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, ""})
}

View file

@ -21,6 +21,7 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/params"
)
@ -41,14 +42,15 @@ func (d *dummyContractRef) SetBalance(*big.Int) {}
func (d *dummyContractRef) SetNonce(uint64) {}
func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) }
type dummyStateDB struct {
NoopStateDB
ref *dummyContractRef
type dummyStatedb struct {
state.StateDB
}
func (*dummyStatedb) GetRefund() uint64 { return 1337 }
func TestStoreCapture(t *testing.T) {
var (
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
env = NewEVM(Context{}, &dummyStatedb{}, params.TestChainConfig, Config{})
logger = NewStructLogger(nil)
mem = NewMemory()
stack = newstack()
@ -56,9 +58,7 @@ func TestStoreCapture(t *testing.T) {
)
stack.push(big.NewInt(1))
stack.push(big.NewInt(0))
var index common.Hash
logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, contract, 0, nil)
if len(logger.changedValues[contract.Address()]) == 0 {
t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.changedValues[contract.Address()]))

View file

@ -16,7 +16,12 @@
package vm
import "fmt"
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common/math"
)
// Memory implements a simple memory model for the ethereum virtual machine.
type Memory struct {
@ -24,25 +29,39 @@ type Memory struct {
lastGasCost uint64
}
// NewMemory returns a new memory model.
func NewMemory() *Memory {
return &Memory{}
}
// Set sets offset + size to value
func (m *Memory) Set(offset, size uint64, value []byte) {
// length of store may never be less than offset + size.
// The store should be resized PRIOR to setting the memory
if size > uint64(len(m.store)) {
panic("INVALID memory: store empty")
}
// It's possible the offset is greater than 0 and size equals 0. This is because
// the calcMemSize (common.go) could potentially return 0 when size is zero (NO-OP)
if size > 0 {
// length of store may never be less than offset + size.
// The store should be resized PRIOR to setting the memory
if offset+size > uint64(len(m.store)) {
panic("invalid memory: store empty")
}
copy(m.store[offset:offset+size], value)
}
}
// Set32 sets the 32 bytes starting at offset to the value of val, left-padded with zeroes to
// 32 bytes.
func (m *Memory) Set32(offset uint64, val *big.Int) {
// length of store may never be less than offset + size.
// The store should be resized PRIOR to setting the memory
if offset+32 > uint64(len(m.store)) {
panic("invalid memory: store empty")
}
// Zero the memory area
copy(m.store[offset:offset+32], []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
// Fill in relevant bits
math.ReadBits(val, m.store[offset:offset+32])
}
// Resize resizes the memory to size
func (m *Memory) Resize(size uint64) {
if uint64(m.Len()) < size {
@ -51,14 +70,14 @@ func (m *Memory) Resize(size uint64) {
}
// Get returns offset + size as a new slice
func (self *Memory) Get(offset, size int64) (cpy []byte) {
func (m *Memory) Get(offset, size int64) (cpy []byte) {
if size == 0 {
return nil
}
if len(self.store) > int(offset) {
if len(m.store) > int(offset) {
cpy = make([]byte, size)
copy(cpy, self.store[offset:offset+size])
copy(cpy, m.store[offset:offset+size])
return
}
@ -67,13 +86,13 @@ func (self *Memory) Get(offset, size int64) (cpy []byte) {
}
// GetPtr returns the offset + size
func (self *Memory) GetPtr(offset, size int64) []byte {
func (m *Memory) GetPtr(offset, size int64) []byte {
if size == 0 {
return nil
}
if len(self.store) > int(offset) {
return self.store[offset : offset+size]
if len(m.store) > int(offset) {
return m.store[offset : offset+size]
}
return nil
@ -89,6 +108,7 @@ func (m *Memory) Data() []byte {
return m.store
}
// Print dumps the content of the memory.
func (m *Memory) Print() {
fmt.Printf("### mem %d bytes ###\n", len(m.store))
if len(m.store) > 0 {

View file

@ -16,84 +16,98 @@
package vm
import (
"math/big"
"github.com/ethereum/go-ethereum/common/math"
)
func memorySha3(stack *Stack) *big.Int {
return calcMemSize(stack.Back(0), stack.Back(1))
func memorySha3(stack *Stack) (uint64, bool) {
return calcMemSize64(stack.Back(0), stack.Back(1))
}
func memoryCallDataCopy(stack *Stack) *big.Int {
return calcMemSize(stack.Back(0), stack.Back(2))
func memoryCallDataCopy(stack *Stack) (uint64, bool) {
return calcMemSize64(stack.Back(0), stack.Back(2))
}
func memoryReturnDataCopy(stack *Stack) *big.Int {
return calcMemSize(stack.Back(0), stack.Back(2))
func memoryReturnDataCopy(stack *Stack) (uint64, bool) {
return calcMemSize64(stack.Back(0), stack.Back(2))
}
func memoryCodeCopy(stack *Stack) *big.Int {
return calcMemSize(stack.Back(0), stack.Back(2))
func memoryCodeCopy(stack *Stack) (uint64, bool) {
return calcMemSize64(stack.Back(0), stack.Back(2))
}
func memoryExtCodeCopy(stack *Stack) *big.Int {
return calcMemSize(stack.Back(1), stack.Back(3))
func memoryExtCodeCopy(stack *Stack) (uint64, bool) {
return calcMemSize64(stack.Back(1), stack.Back(3))
}
func memoryMLoad(stack *Stack) *big.Int {
return calcMemSize(stack.Back(0), big.NewInt(32))
func memoryMLoad(stack *Stack) (uint64, bool) {
return calcMemSize64WithUint(stack.Back(0), 32)
}
func memoryMStore8(stack *Stack) *big.Int {
return calcMemSize(stack.Back(0), big.NewInt(1))
func memoryMStore8(stack *Stack) (uint64, bool) {
return calcMemSize64WithUint(stack.Back(0), 1)
}
func memoryMStore(stack *Stack) *big.Int {
return calcMemSize(stack.Back(0), big.NewInt(32))
func memoryMStore(stack *Stack) (uint64, bool) {
return calcMemSize64WithUint(stack.Back(0), 32)
}
func memoryCreate(stack *Stack) *big.Int {
return calcMemSize(stack.Back(1), stack.Back(2))
func memoryCreate(stack *Stack) (uint64, bool) {
return calcMemSize64(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 math.BigMax(x, y)
func memoryCreate2(stack *Stack) (uint64, bool) {
return calcMemSize64(stack.Back(1), stack.Back(2))
}
func memoryCallCode(stack *Stack) *big.Int {
x := calcMemSize(stack.Back(5), stack.Back(6))
y := calcMemSize(stack.Back(3), stack.Back(4))
return math.BigMax(x, y)
func memoryCall(stack *Stack) (uint64, bool) {
x, overflow := calcMemSize64(stack.Back(5), stack.Back(6))
if overflow {
return 0, true
}
y, overflow := calcMemSize64(stack.Back(3), stack.Back(4))
if overflow {
return 0, true
}
if x > y {
return x, false
}
return y, false
}
func memoryDelegateCall(stack *Stack) *big.Int {
x := calcMemSize(stack.Back(4), stack.Back(5))
y := calcMemSize(stack.Back(2), stack.Back(3))
return math.BigMax(x, y)
func memoryDelegateCall(stack *Stack) (uint64, bool) {
x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
if overflow {
return 0, true
}
y, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
if overflow {
return 0, true
}
if x > y {
return x, false
}
return y, false
}
func memoryStaticCall(stack *Stack) *big.Int {
x := calcMemSize(stack.Back(4), stack.Back(5))
y := calcMemSize(stack.Back(2), stack.Back(3))
return math.BigMax(x, y)
func memoryStaticCall(stack *Stack) (uint64, bool) {
x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
if overflow {
return 0, true
}
y, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
if overflow {
return 0, true
}
if x > y {
return x, false
}
return y, false
}
func memoryReturn(stack *Stack) *big.Int {
return calcMemSize(stack.Back(0), stack.Back(1))
func memoryReturn(stack *Stack) (uint64, bool) {
return calcMemSize64(stack.Back(0), stack.Back(1))
}
func memoryRevert(stack *Stack) *big.Int {
return calcMemSize(stack.Back(0), stack.Back(1))
func memoryRevert(stack *Stack) (uint64, bool) {
return calcMemSize64(stack.Back(0), stack.Back(1))
}
func memoryLog(stack *Stack) *big.Int {
mSize, mStart := stack.Back(1), stack.Back(0)
return calcMemSize(mStart, mSize)
func memoryLog(stack *Stack) (uint64, bool) {
return calcMemSize64(stack.Back(0), stack.Back(1))
}

View file

@ -23,6 +23,7 @@ import (
// OpCode is an EVM opcode
type OpCode byte
// IsPush specifies if an opcode is a PUSH opcode.
func (op OpCode) IsPush() bool {
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:
@ -31,12 +32,13 @@ func (op OpCode) IsPush() bool {
return false
}
// IsStaticJump specifies if an opcode is JUMP.
func (op OpCode) IsStaticJump() bool {
return op == JUMP
}
// 0x0 range - arithmetic ops.
const (
// 0x0 range - arithmetic ops
STOP OpCode = iota
ADD
MUL
@ -51,6 +53,7 @@ const (
SIGNEXTEND
)
// 0x10 range - comparison ops.
const (
LT OpCode = iota + 0x10
GT
@ -70,8 +73,8 @@ const (
SHA3 = 0x20
)
// 0x30 range - closure state.
const (
// 0x30 range - closure state
ADDRESS OpCode = 0x30 + iota
BALANCE
ORIGIN
@ -87,10 +90,11 @@ const (
EXTCODECOPY
RETURNDATASIZE
RETURNDATACOPY
EXTCODEHASH
)
// 0x40 range - block operations.
const (
// 0x40 range - block operations
BLOCKHASH OpCode = 0x40 + iota
COINBASE
TIMESTAMP
@ -99,8 +103,8 @@ const (
GASLIMIT
)
// 0x50 range - 'storage' and execution.
const (
// 0x50 range - 'storage' and execution
POP OpCode = 0x50 + iota
MLOAD
MSTORE
@ -115,8 +119,8 @@ const (
JUMPDEST
)
// 0x60 range.
const (
// 0x60 range
PUSH1 OpCode = 0x60 + iota
PUSH2
PUSH3
@ -183,6 +187,7 @@ const (
SWAP16
)
// 0xa0 range - logging ops.
const (
LOG0 OpCode = 0xa0 + iota
LOG1
@ -191,29 +196,30 @@ const (
LOG4
)
// unofficial opcodes used for parsing
// unofficial opcodes used for parsing.
const (
PUSH OpCode = 0xb0 + iota
DUP
SWAP
)
// 0xf0 range - closures.
const (
// 0xf0 range - closures
CREATE OpCode = 0xf0 + iota
CALL
CALLCODE
RETURN
DELEGATECALL
CREATE2
STATICCALL = 0xfa
REVERT = 0xfd
SELFDESTRUCT = 0xff
)
// Since the opcodes aren't all in order we can't use a regular slice
// Since the opcodes aren't all in order we can't use a regular slice.
var opCodeToString = map[OpCode]string{
// 0x0 range - arithmetic ops
// 0x0 range - arithmetic ops.
STOP: "STOP",
ADD: "ADD",
MUL: "MUL",
@ -232,7 +238,7 @@ var opCodeToString = map[OpCode]string{
ISZERO: "ISZERO",
SIGNEXTEND: "SIGNEXTEND",
// 0x10 range - bit ops
// 0x10 range - bit ops.
AND: "AND",
OR: "OR",
XOR: "XOR",
@ -243,10 +249,10 @@ var opCodeToString = map[OpCode]string{
ADDMOD: "ADDMOD",
MULMOD: "MULMOD",
// 0x20 range - crypto
// 0x20 range - crypto.
SHA3: "SHA3",
// 0x30 range - closure state
// 0x30 range - closure state.
ADDRESS: "ADDRESS",
BALANCE: "BALANCE",
ORIGIN: "ORIGIN",
@ -262,8 +268,9 @@ var opCodeToString = map[OpCode]string{
EXTCODECOPY: "EXTCODECOPY",
RETURNDATASIZE: "RETURNDATASIZE",
RETURNDATACOPY: "RETURNDATACOPY",
EXTCODEHASH: "EXTCODEHASH",
// 0x40 range - block operations
// 0x40 range - block operations.
BLOCKHASH: "BLOCKHASH",
COINBASE: "COINBASE",
TIMESTAMP: "TIMESTAMP",
@ -271,7 +278,7 @@ var opCodeToString = map[OpCode]string{
DIFFICULTY: "DIFFICULTY",
GASLIMIT: "GASLIMIT",
// 0x50 range - 'storage' and execution
// 0x50 range - 'storage' and execution.
POP: "POP",
//DUP: "DUP",
//SWAP: "SWAP",
@ -287,7 +294,7 @@ var opCodeToString = map[OpCode]string{
GAS: "GAS",
JUMPDEST: "JUMPDEST",
// 0x60 range - push
// 0x60 range - push.
PUSH1: "PUSH1",
PUSH2: "PUSH2",
PUSH3: "PUSH3",
@ -360,12 +367,13 @@ var opCodeToString = map[OpCode]string{
LOG3: "LOG3",
LOG4: "LOG4",
// 0xf0 range
// 0xf0 range.
CREATE: "CREATE",
CALL: "CALL",
RETURN: "RETURN",
CALLCODE: "CALLCODE",
DELEGATECALL: "DELEGATECALL",
CREATE2: "CREATE2",
STATICCALL: "STATICCALL",
REVERT: "REVERT",
SELFDESTRUCT: "SELFDESTRUCT",
@ -375,10 +383,10 @@ var opCodeToString = map[OpCode]string{
SWAP: "SWAP",
}
func (o OpCode) String() string {
str := opCodeToString[o]
func (op OpCode) String() string {
str := opCodeToString[op]
if len(str) == 0 {
return fmt.Sprintf("Missing opcode 0x%x", int(o))
return fmt.Sprintf("Missing opcode 0x%x", int(op))
}
return str
@ -429,6 +437,7 @@ var stringToOp = map[string]OpCode{
"EXTCODECOPY": EXTCODECOPY,
"RETURNDATASIZE": RETURNDATASIZE,
"RETURNDATACOPY": RETURNDATACOPY,
"EXTCODEHASH": EXTCODEHASH,
"BLOCKHASH": BLOCKHASH,
"COINBASE": COINBASE,
"TIMESTAMP": TIMESTAMP,
@ -517,6 +526,7 @@ var stringToOp = map[string]OpCode{
"LOG3": LOG3,
"LOG4": LOG4,
"CREATE": CREATE,
"CREATE2": CREATE2,
"CALL": CALL,
"RETURN": RETURN,
"CALLCODE": CALLCODE,
@ -524,6 +534,7 @@ var stringToOp = map[string]OpCode{
"SELFDESTRUCT": SELFDESTRUCT,
}
// StringToOp finds the opcode whose name is stored in `str`.
func StringToOp(str string) OpCode {
return stringToOp[str]
}

View file

@ -22,10 +22,10 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
)
@ -52,7 +52,7 @@ type Config struct {
func setDefaults(cfg *Config) {
if cfg.ChainConfig == nil {
cfg.ChainConfig = &params.ChainConfig{
ChainId: big.NewInt(1),
ChainID: big.NewInt(1),
HomesteadBlock: new(big.Int),
DAOForkBlock: new(big.Int),
DAOForkSupport: false,
@ -99,11 +99,10 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
setDefaults(cfg)
if cfg.State == nil {
db, _ := ethdb.NewMemDatabase()
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(db))
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
}
var (
address = common.StringToAddress("contract")
address = common.BytesToAddress([]byte("contract"))
vmenv = NewEnv(cfg)
sender = vm.AccountRef(cfg.Origin)
)
@ -113,7 +112,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
// Call the code with the given configuration.
ret, _, err := vmenv.Call(
sender,
common.StringToAddress("contract"),
common.BytesToAddress([]byte("contract")),
input,
cfg.GasLimit,
cfg.Value,
@ -130,8 +129,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
setDefaults(cfg)
if cfg.State == nil {
db, _ := ethdb.NewMemDatabase()
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(db))
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
}
var (
vmenv = NewEnv(cfg)

View file

@ -23,9 +23,10 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
)
func TestDefaults(t *testing.T) {
@ -94,8 +95,7 @@ func TestExecute(t *testing.T) {
}
func TestCall(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
state, _ := state.New(common.Hash{}, state.NewDatabase(db))
state, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
address := common.HexToAddress("0x0a")
state.SetCode(address, []byte{
byte(vm.PUSH1), 10,
@ -149,3 +149,57 @@ func BenchmarkCall(b *testing.B) {
}
}
}
func benchmarkEVM_Create(bench *testing.B, code string) {
var (
statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
sender = common.BytesToAddress([]byte("sender"))
receiver = common.BytesToAddress([]byte("receiver"))
)
statedb.CreateAccount(sender)
statedb.SetCode(receiver, common.FromHex(code))
runtimeConfig := Config{
Origin: sender,
State: statedb,
GasLimit: 10000000,
Difficulty: big.NewInt(0x200000),
Time: new(big.Int).SetUint64(0),
Coinbase: common.Address{},
BlockNumber: new(big.Int).SetUint64(1),
ChainConfig: &params.ChainConfig{
ChainID: big.NewInt(1),
HomesteadBlock: new(big.Int),
ByzantiumBlock: new(big.Int),
ConstantinopleBlock: new(big.Int),
DAOForkBlock: new(big.Int),
DAOForkSupport: false,
EIP150Block: new(big.Int),
EIP155Block: new(big.Int),
EIP158Block: new(big.Int),
},
EVMConfig: vm.Config{},
}
// Warm up the intpools and stuff
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
Call(receiver, []byte{}, &runtimeConfig)
}
bench.StopTimer()
}
func BenchmarkEVM_CREATE_500(bench *testing.B) {
// initcode size 500K, repeatedly calls CREATE and then modifies the mem contents
benchmarkEVM_Create(bench, "5b6207a120600080f0600152600056")
}
func BenchmarkEVM_CREATE2_500(bench *testing.B) {
// initcode size 500K, repeatedly calls CREATE2 and then modifies the mem contents
benchmarkEVM_Create(bench, "5b586207a120600080f5600152600056")
}
func BenchmarkEVM_CREATE_1200(bench *testing.B) {
// initcode size 1200K, repeatedly calls CREATE and then modifies the mem contents
benchmarkEVM_Create(bench, "5b62124f80600080f0600152600056")
}
func BenchmarkEVM_CREATE2_1200(bench *testing.B) {
// initcode size 1200K, repeatedly calls CREATE2 and then modifies the mem contents
benchmarkEVM_Create(bench, "5b5862124f80600080f5600152600056")
}

View file

@ -21,7 +21,7 @@ import (
"math/big"
)
// stack is an object for basic stack operations. Items popped to the stack are
// Stack is an object for basic stack operations. Items popped to the stack are
// expected to be changed and modified. stack does not take care of adding newly
// initialised objects.
type Stack struct {
@ -32,6 +32,7 @@ func newstack() *Stack {
return &Stack{data: make([]*big.Int, 0, 1024)}
}
// Data returns the underlying big.Int array.
func (st *Stack) Data() []*big.Int {
return st.data
}
@ -80,6 +81,7 @@ func (st *Stack) require(n int) error {
return nil
}
// Print dumps the content of the stack
func (st *Stack) Print() {
fmt.Println("### stack ###")
if len(st.data) > 0 {

View file

@ -17,28 +17,26 @@
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 stack.len()+push-pop > int(params.StackLimit) {
return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit)
}
return nil
}
func minSwapStack(n int) int {
return minStack(n, n)
}
func maxSwapStack(n int) int {
return maxStack(n, n)
}
func makeDupStackFunc(n int) stackValidationFunc {
return makeStackFunc(n, n+1)
func minDupStack(n int) int {
return minStack(n, n+1)
}
func maxDupStack(n int) int {
return maxStack(n, n+1)
}
func makeSwapStackFunc(n int) stackValidationFunc {
return makeStackFunc(n, n)
func maxStack(pop, push int) int {
return int(params.StackLimit) + pop - push
}
func minStack(pops, push int) int {
return pops
}