mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 14:16:44 +00:00
common/math, core/vm, core/state: EVM gas calculations to 64bit
This changes the internals of the EVM-JIT by dropping support for blocks (and therefor also transactions) that can exceed a 64^2-1 gas-limit. This means that the EVM-JIT can drop support for abritrary precision (*big.Int) and move to 64bit based gas calculations. For obvious reasons integer overflows have added to make sure that gas calculations such as gas for memory doesn't overflow 64bit. If the overflow check fails it throws an out-of-gas error and will halt the execution of the code.
This commit is contained in:
parent
3778f1bf77
commit
32b5ce9a32
26 changed files with 604 additions and 284 deletions
|
|
@ -120,8 +120,7 @@ func run(ctx *cli.Context) error {
|
|||
sender := statedb.CreateAccount(common.StringToAddress("sender"))
|
||||
|
||||
logger := vm.NewStructLogger(nil)
|
||||
|
||||
vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), vm.Config{
|
||||
vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), common.Big(ctx.GlobalString(PriceFlag.Name)), vm.Config{
|
||||
Debug: ctx.GlobalBool(DebugFlag.Name),
|
||||
ForceJit: ctx.GlobalBool(ForceJitFlag.Name),
|
||||
EnableJit: !ctx.GlobalBool(DisableJitFlag.Name),
|
||||
|
|
@ -141,7 +140,6 @@ func run(ctx *cli.Context) error {
|
|||
sender,
|
||||
input,
|
||||
common.Big(ctx.GlobalString(GasFlag.Name)),
|
||||
common.Big(ctx.GlobalString(PriceFlag.Name)),
|
||||
common.Big(ctx.GlobalString(ValueFlag.Name)),
|
||||
)
|
||||
} else {
|
||||
|
|
@ -152,7 +150,6 @@ func run(ctx *cli.Context) error {
|
|||
receiver.Address(),
|
||||
common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)),
|
||||
common.Big(ctx.GlobalString(GasFlag.Name)),
|
||||
common.Big(ctx.GlobalString(PriceFlag.Name)),
|
||||
common.Big(ctx.GlobalString(ValueFlag.Name)),
|
||||
)
|
||||
}
|
||||
|
|
@ -200,18 +197,19 @@ type VMEnv struct {
|
|||
value *big.Int
|
||||
|
||||
depth int
|
||||
Gas *big.Int
|
||||
Gas, price *big.Int
|
||||
time *big.Int
|
||||
logs []vm.StructLog
|
||||
|
||||
evm *vm.EVM
|
||||
}
|
||||
|
||||
func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg vm.Config) *VMEnv {
|
||||
func NewEnv(state *state.StateDB, transactor common.Address, value, price *big.Int, cfg vm.Config) *VMEnv {
|
||||
env := &VMEnv{
|
||||
state: state,
|
||||
transactor: &transactor,
|
||||
value: value,
|
||||
price: price,
|
||||
time: big.NewInt(time.Now().Unix()),
|
||||
}
|
||||
|
||||
|
|
@ -237,6 +235,7 @@ func (self *VMEnv) Difficulty() *big.Int { return common.Big1 }
|
|||
func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) }
|
||||
func (self *VMEnv) Value() *big.Int { return self.value }
|
||||
func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) }
|
||||
func (self *VMEnv) GasPrice() *big.Int { return big.NewInt(1000000000) }
|
||||
func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy }
|
||||
func (self *VMEnv) Depth() int { return 0 }
|
||||
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
||||
|
|
@ -256,19 +255,19 @@ func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) {
|
|||
core.Transfer(from, to, amount)
|
||||
}
|
||||
|
||||
func (self *VMEnv) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
func (self *VMEnv) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
||||
self.Gas = gas
|
||||
return core.Call(self, caller, addr, data, gas, price, value)
|
||||
return core.Call(self, caller, addr, data, gas, value)
|
||||
}
|
||||
|
||||
func (self *VMEnv) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
return core.CallCode(self, caller, addr, data, gas, price, value)
|
||||
func (self *VMEnv) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
||||
return core.CallCode(self, caller, addr, data, gas, value)
|
||||
}
|
||||
|
||||
func (self *VMEnv) DelegateCall(caller vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
|
||||
return core.DelegateCall(self, caller, addr, data, gas, price)
|
||||
func (self *VMEnv) DelegateCall(caller vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
|
||||
return core.DelegateCall(self, caller, addr, data, gas)
|
||||
}
|
||||
|
||||
func (self *VMEnv) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
|
||||
return core.Create(self, caller, data, gas, price, value)
|
||||
func (self *VMEnv) Create(caller vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
|
||||
return core.Create(self, caller, data, gas, value)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,10 @@
|
|||
|
||||
package common
|
||||
|
||||
import "math/big"
|
||||
import (
|
||||
"math"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// Common big integers often used
|
||||
var (
|
||||
|
|
@ -33,6 +36,7 @@ var (
|
|||
Big256 = big.NewInt(0xff)
|
||||
Big257 = big.NewInt(257)
|
||||
MaxBig = String2Big("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
|
||||
Max64Bit = new(big.Int).SetUint64(math.MaxUint64)
|
||||
)
|
||||
|
||||
// Big pow
|
||||
|
|
|
|||
22
common/math/integer.go
Normal file
22
common/math/integer.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package math
|
||||
|
||||
import gmath "math"
|
||||
|
||||
/*
|
||||
* NOTE: The following methods need to be optimised using either bit checking or asm
|
||||
*/
|
||||
|
||||
// IsMinSafe returns whether the subtraction is safe and does not overflow.
|
||||
func IsSubSafe(x, y uint64) bool {
|
||||
return x >= y
|
||||
}
|
||||
|
||||
// IsAddSafe returns whether the addition is safe and does not overflow.
|
||||
func IsAddSafe(x, y uint64) bool {
|
||||
return y <= gmath.MaxUint64-x
|
||||
}
|
||||
|
||||
// IsAddSafe returns whether the multiplication is safe and does not overflow.
|
||||
func IsMulSafe(x, y uint64) bool {
|
||||
return x == 0 || y == 0 || y <= gmath.MaxUint64/x
|
||||
}
|
||||
50
common/math/integer_test.go
Normal file
50
common/math/integer_test.go
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
package math
|
||||
|
||||
import (
|
||||
gmath "math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type operation byte
|
||||
|
||||
const (
|
||||
sub operation = iota
|
||||
add
|
||||
mul
|
||||
)
|
||||
|
||||
func TestIsAddSafe(t *testing.T) {
|
||||
for i, test := range []struct {
|
||||
x uint64
|
||||
y uint64
|
||||
safe bool
|
||||
op operation
|
||||
}{
|
||||
// add operations
|
||||
{gmath.MaxUint64, 1, false, add},
|
||||
{gmath.MaxUint64 - 1, 1, true, add},
|
||||
|
||||
// sub operations
|
||||
{0, 1, false, sub},
|
||||
{0, 0, true, sub},
|
||||
|
||||
// mul operations
|
||||
{10, 10, true, mul},
|
||||
{gmath.MaxUint64, 2, false, mul},
|
||||
{gmath.MaxUint64, 1, true, mul},
|
||||
} {
|
||||
var isSafe bool
|
||||
switch test.op {
|
||||
case sub:
|
||||
isSafe = IsSubSafe(test.x, test.y)
|
||||
case add:
|
||||
isSafe = IsAddSafe(test.x, test.y)
|
||||
case mul:
|
||||
isSafe = IsMulSafe(test.x, test.y)
|
||||
}
|
||||
|
||||
if test.safe != isSafe {
|
||||
t.Errorf("%d failed. Expected test to be %v, got %v", i, test.safe, isSafe)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -26,30 +26,30 @@ import (
|
|||
)
|
||||
|
||||
// Call executes within the given contract
|
||||
func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
|
||||
ret, _, err = exec(env, caller, &addr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, value)
|
||||
func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) {
|
||||
ret, _, err = exec(env, caller, &addr, &addr, input, env.Db().GetCode(addr), gas, value)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// CallCode executes the given address' code as the given contract address
|
||||
func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
|
||||
func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) {
|
||||
callerAddr := caller.Address()
|
||||
ret, _, err = exec(env, caller, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, value)
|
||||
ret, _, err = exec(env, caller, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, value)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// DelegateCall is equivalent to CallCode except that sender and value propagates from parent scope to child scope
|
||||
func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice *big.Int) (ret []byte, err error) {
|
||||
func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas *big.Int) (ret []byte, err error) {
|
||||
callerAddr := caller.Address()
|
||||
originAddr := env.Origin()
|
||||
callerValue := caller.Value()
|
||||
ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, callerValue)
|
||||
ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, callerValue)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// Create creates a new contract with the given code
|
||||
func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPrice, value *big.Int) (ret []byte, address common.Address, err error) {
|
||||
ret, address, err = exec(env, caller, nil, nil, nil, code, gas, gasPrice, value)
|
||||
func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, value *big.Int) (ret []byte, address common.Address, err error) {
|
||||
ret, address, err = exec(env, caller, nil, nil, nil, code, gas, value)
|
||||
// Here we get an error if we run into maximum stack depth,
|
||||
// See: https://github.com/ethereum/yellowpaper/pull/131
|
||||
// and YP definitions for CREATE instruction
|
||||
|
|
@ -59,18 +59,18 @@ func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPric
|
|||
return ret, address, err
|
||||
}
|
||||
|
||||
func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) {
|
||||
func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, input, code []byte, gas, value *big.Int) (ret []byte, addr common.Address, err error) {
|
||||
evm := env.Vm()
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
if env.Depth() > int(params.CallCreateDepth.Int64()) {
|
||||
caller.ReturnGas(gas, gasPrice)
|
||||
caller.ReturnGas(gas.Uint64())
|
||||
|
||||
return nil, common.Address{}, vm.DepthError
|
||||
}
|
||||
|
||||
if !env.CanTransfer(caller.Address(), value) {
|
||||
caller.ReturnGas(gas, gasPrice)
|
||||
caller.ReturnGas(gas.Uint64())
|
||||
|
||||
return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
|
||||
}
|
||||
|
|
@ -104,7 +104,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
|
|||
// 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 := vm.NewContract(caller, to, value, gas, gasPrice)
|
||||
contract := vm.NewContract(caller, to, value, gas)
|
||||
contract.SetCallCode(codeAddr, code)
|
||||
defer contract.Finalise()
|
||||
|
||||
|
|
@ -116,7 +116,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
|
|||
if err == nil && createAccount {
|
||||
dataGas := big.NewInt(int64(len(ret)))
|
||||
dataGas.Mul(dataGas, params.CreateDataGas)
|
||||
if contract.UseGas(dataGas) {
|
||||
if contract.UseGas(dataGas.Uint64()) {
|
||||
env.Db().SetCode(*address, ret)
|
||||
} else {
|
||||
err = vm.CodeStoreOutOfGasError
|
||||
|
|
@ -127,7 +127,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
|
|||
// 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 && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) {
|
||||
contract.UseGas(contract.Gas)
|
||||
contract.UseGas(contract.Gas())
|
||||
|
||||
env.SetSnapshot(snapshotPreTransfer)
|
||||
}
|
||||
|
|
@ -135,12 +135,12 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
|
|||
return ret, addr, err
|
||||
}
|
||||
|
||||
func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) {
|
||||
func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, input, code []byte, gas, value *big.Int) (ret []byte, addr common.Address, err error) {
|
||||
evm := env.Vm()
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
if env.Depth() > int(params.CallCreateDepth.Int64()) {
|
||||
caller.ReturnGas(gas, gasPrice)
|
||||
caller.ReturnGas(gas.Uint64())
|
||||
return nil, common.Address{}, vm.DepthError
|
||||
}
|
||||
|
||||
|
|
@ -154,13 +154,13 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA
|
|||
}
|
||||
|
||||
// Iinitialise a new contract and make initialise the delegate values
|
||||
contract := vm.NewContract(caller, to, value, gas, gasPrice).AsDelegate()
|
||||
contract := vm.NewContract(caller, to, value, gas).AsDelegate()
|
||||
contract.SetCallCode(codeAddr, code)
|
||||
defer contract.Finalise()
|
||||
|
||||
ret, err = evm.Run(contract, input)
|
||||
if err != nil {
|
||||
contract.UseGas(contract.Gas)
|
||||
contract.UseGas(contract.Gas())
|
||||
|
||||
env.SetSnapshot(snapshot)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ func (c *StateObject) St() Storage {
|
|||
}
|
||||
|
||||
// Return the gas back to the origin. Used by the Virtual machine or Closures
|
||||
func (c *StateObject) ReturnGas(gas, price *big.Int) {}
|
||||
func (c *StateObject) ReturnGas(gas uint64) {}
|
||||
|
||||
func (self *StateObject) Copy() *StateObject {
|
||||
stateObject := NewStateObject(self.Address(), self.db)
|
||||
|
|
|
|||
|
|
@ -244,7 +244,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
|
|||
vmenv := self.env
|
||||
//var addr common.Address
|
||||
if contractCreation {
|
||||
ret, _, err = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value)
|
||||
ret, _, err = vmenv.Create(sender, self.data, self.gas, self.value)
|
||||
if homestead && err == vm.CodeStoreOutOfGasError {
|
||||
self.gas = Big0
|
||||
}
|
||||
|
|
@ -256,7 +256,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
|
|||
} else {
|
||||
// Increment the nonce for the next transaction
|
||||
self.state.SetNonce(sender.Address(), self.state.GetNonce(sender.Address())+1)
|
||||
ret, err = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.gasPrice, self.value)
|
||||
ret, err = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.value)
|
||||
if err != nil {
|
||||
glog.V(logger.Core).Infoln("VM call err:", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import (
|
|||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// Type is the VM type accepted by **NewVm**
|
||||
|
|
@ -45,41 +44,6 @@ var (
|
|||
max = big.NewInt(math.MaxInt64) // Maximum 64 bit integer
|
||||
)
|
||||
|
||||
// calculates the memory size required for a step
|
||||
func calcMemSize(off, l *big.Int) *big.Int {
|
||||
if l.Cmp(common.Big0) == 0 {
|
||||
return common.Big0
|
||||
}
|
||||
|
||||
return new(big.Int).Add(off, l)
|
||||
}
|
||||
|
||||
// calculates the quadratic gas
|
||||
func quadMemGas(mem *Memory, newMemSize, gas *big.Int) {
|
||||
if newMemSize.Cmp(common.Big0) > 0 {
|
||||
newMemSizeWords := toWordSize(newMemSize)
|
||||
newMemSize.Mul(newMemSizeWords, u256(32))
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Simple helper
|
||||
func u256(n int64) *big.Int {
|
||||
return big.NewInt(n)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import (
|
|||
|
||||
// ContractRef is a reference to the contract's backing object
|
||||
type ContractRef interface {
|
||||
ReturnGas(*big.Int, *big.Int)
|
||||
ReturnGas(uint64)
|
||||
Address() common.Address
|
||||
Value() *big.Int
|
||||
SetCode([]byte)
|
||||
|
|
@ -47,7 +47,8 @@ type Contract struct {
|
|||
Input []byte
|
||||
CodeAddr *common.Address
|
||||
|
||||
value, Gas, UsedGas, Price *big.Int
|
||||
value, gas *big.Int
|
||||
gas64 uint64
|
||||
|
||||
Args []byte
|
||||
|
||||
|
|
@ -55,7 +56,7 @@ type Contract struct {
|
|||
}
|
||||
|
||||
// NewContract returns a new contract environment for the execution of EVM.
|
||||
func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.Int) *Contract {
|
||||
func NewContract(caller ContractRef, object ContractRef, value, gas *big.Int) *Contract {
|
||||
c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil}
|
||||
|
||||
if parent, ok := caller.(*Contract); ok {
|
||||
|
|
@ -67,12 +68,9 @@ func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.
|
|||
|
||||
// Gas should be a pointer so it can safely be reduced through the run
|
||||
// This pointer will be off the state transition
|
||||
c.Gas = gas //new(big.Int).Set(gas)
|
||||
c.gas = gas //new(big.Int).Set(gas)
|
||||
c.gas64 = gas.Uint64()
|
||||
c.value = new(big.Int).Set(value)
|
||||
// In most cases price and value are pointers to transaction objects
|
||||
// and we don't want the transaction's values to change.
|
||||
c.Price = new(big.Int).Set(price)
|
||||
c.UsedGas = new(big.Int)
|
||||
|
||||
return c
|
||||
}
|
||||
|
|
@ -101,6 +99,11 @@ func (c *Contract) GetByte(n uint64) byte {
|
|||
return 0
|
||||
}
|
||||
|
||||
// Gas returns the current gas left
|
||||
func (c *Contract) Gas() uint64 {
|
||||
return c.gas64
|
||||
}
|
||||
|
||||
// Caller returns the caller of the contract.
|
||||
//
|
||||
// Caller will recursively call caller when the contract is a delegate
|
||||
|
|
@ -112,24 +115,24 @@ func (c *Contract) Caller() common.Address {
|
|||
// Finalise finalises the contract and returning any remaining gas to the original
|
||||
// caller.
|
||||
func (c *Contract) Finalise() {
|
||||
c.gas.SetUint64(c.gas64)
|
||||
// Return the remaining gas to the caller
|
||||
c.caller.ReturnGas(c.Gas, c.Price)
|
||||
c.caller.ReturnGas(c.gas64)
|
||||
}
|
||||
|
||||
// UseGas attempts the use gas and subtracts it and returns true on success
|
||||
func (c *Contract) UseGas(gas *big.Int) (ok bool) {
|
||||
ok = useGas(c.Gas, gas)
|
||||
if ok {
|
||||
c.UsedGas.Add(c.UsedGas, gas)
|
||||
func (c *Contract) UseGas(gas uint64) (ok bool) {
|
||||
if c.gas64 < gas {
|
||||
return false
|
||||
}
|
||||
return
|
||||
c.gas64 -= gas
|
||||
return true
|
||||
}
|
||||
|
||||
// ReturnGas adds the given gas back to itself.
|
||||
func (c *Contract) ReturnGas(gas, price *big.Int) {
|
||||
func (c *Contract) ReturnGas(gas uint64) {
|
||||
// Return the gas to the context
|
||||
c.Gas.Add(c.Gas, gas)
|
||||
c.UsedGas.Sub(c.UsedGas, gas)
|
||||
c.gas64 += gas
|
||||
}
|
||||
|
||||
// Address returns the contracts address
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ type Environment interface {
|
|||
Difficulty() *big.Int
|
||||
// The gas limit of the block
|
||||
GasLimit() *big.Int
|
||||
// The gas price of the current call
|
||||
GasPrice() *big.Int
|
||||
// Determines whether it's possible to transact
|
||||
CanTransfer(from common.Address, balance *big.Int) bool
|
||||
// Transfers amount from one account to the other
|
||||
|
|
@ -66,13 +68,13 @@ type Environment interface {
|
|||
// Set the current calling depth
|
||||
SetDepth(i int)
|
||||
// Call another contract
|
||||
Call(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error)
|
||||
Call(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(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error)
|
||||
CallCode(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(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error)
|
||||
DelegateCall(me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error)
|
||||
// Create a new contract
|
||||
Create(me ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error)
|
||||
Create(me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error)
|
||||
}
|
||||
|
||||
// Vm is the basic interface for an implementation of the EVM.
|
||||
|
|
@ -116,7 +118,7 @@ type Account interface {
|
|||
SetNonce(uint64)
|
||||
Balance() *big.Int
|
||||
Address() common.Address
|
||||
ReturnGas(*big.Int, *big.Int)
|
||||
ReturnGas(uint64)
|
||||
SetCode([]byte)
|
||||
ForEachStorage(cb func(key, value common.Hash) bool)
|
||||
Value() *big.Int
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
|
|
@ -72,6 +73,41 @@ func toWordSize(size *big.Int) *big.Int {
|
|||
return tmp
|
||||
}
|
||||
|
||||
// calculates the memory size required for a step
|
||||
func calcMemSize(off, l *big.Int) *big.Int {
|
||||
if l.Cmp(common.Big0) == 0 {
|
||||
return common.Big0
|
||||
}
|
||||
|
||||
return new(big.Int).Add(off, l)
|
||||
}
|
||||
|
||||
// calculates the quadratic gas
|
||||
func quadMemGas(mem *Memory, newMemSize, gas *big.Int) {
|
||||
if newMemSize.Cmp(common.Big0) > 0 {
|
||||
newMemSizeWords := toWordSize(newMemSize)
|
||||
newMemSize.Mul(newMemSizeWords, u256(32))
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type req struct {
|
||||
stackPop int
|
||||
gas *big.Int
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ type instruction struct {
|
|||
fn instrFn
|
||||
data *big.Int
|
||||
|
||||
gas *big.Int
|
||||
gas uint64
|
||||
spop int
|
||||
spush int
|
||||
|
||||
|
|
@ -71,7 +71,7 @@ func (instr instruction) do(program *Program, pc *uint64, env Environment, contr
|
|||
return nil, OutOfGasError
|
||||
}
|
||||
// Resize the memory calculated previously
|
||||
memory.Resize(newMemSize.Uint64())
|
||||
memory.Resize(newMemSize)
|
||||
|
||||
// These opcodes return an argument and are therefor handled
|
||||
// differently from the rest of the opcodes
|
||||
|
|
@ -316,7 +316,7 @@ func opMulmod(instr instruction, pc *uint64, env Environment, contract *Contract
|
|||
|
||||
func opSha3(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
offset, size := stack.pop(), stack.pop()
|
||||
hash := crypto.Keccak256(memory.Get(offset.Int64(), size.Int64()))
|
||||
hash := crypto.Keccak256(memory.GetPtr(offset.Int64(), size.Int64()))
|
||||
|
||||
stack.push(common.BytesToBig(hash))
|
||||
}
|
||||
|
|
@ -396,7 +396,7 @@ func opExtCodeCopy(instr instruction, pc *uint64, env Environment, contract *Con
|
|||
}
|
||||
|
||||
func opGasprice(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(new(big.Int).Set(contract.Price))
|
||||
stack.push(new(big.Int).Set(env.GasPrice()))
|
||||
}
|
||||
|
||||
func opBlockhash(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
|
|
@ -461,7 +461,7 @@ func opLog(instr instruction, pc *uint64, env Environment, contract *Contract, m
|
|||
|
||||
func opMload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
offset := stack.pop()
|
||||
val := common.BigD(memory.Get(offset.Int64(), 32))
|
||||
val := common.BigD(memory.GetPtr(offset.Int64(), 32))
|
||||
stack.push(val)
|
||||
}
|
||||
|
||||
|
|
@ -504,7 +504,7 @@ func opMsize(instr instruction, pc *uint64, env Environment, contract *Contract,
|
|||
}
|
||||
|
||||
func opGas(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(new(big.Int).Set(contract.Gas))
|
||||
stack.push(new(big.Int).SetUint64(contract.gas64))
|
||||
}
|
||||
|
||||
func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
|
|
@ -512,10 +512,10 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract
|
|||
value = stack.pop()
|
||||
offset, size = stack.pop(), stack.pop()
|
||||
input = memory.Get(offset.Int64(), size.Int64())
|
||||
gas = new(big.Int).Set(contract.Gas)
|
||||
gas = new(big.Int).SetUint64(contract.gas64)
|
||||
)
|
||||
contract.UseGas(contract.Gas)
|
||||
_, addr, suberr := env.Create(contract, input, gas, contract.Price, value)
|
||||
contract.UseGas(contract.gas64)
|
||||
_, addr, suberr := env.Create(contract, input, gas, value)
|
||||
// Push item on the stack based on the returned error. If the ruleset is
|
||||
// homestead we must check for CodeStoreOutOfGasError (homestead only
|
||||
// rule) and treat as an error, if the ruleset is frontier we must
|
||||
|
|
@ -548,7 +548,7 @@ func opCall(instr instruction, pc *uint64, env Environment, contract *Contract,
|
|||
gas.Add(gas, params.CallStipend)
|
||||
}
|
||||
|
||||
ret, err := env.Call(contract, address, args, gas, contract.Price, value)
|
||||
ret, err := env.Call(contract, address, args, gas, value)
|
||||
|
||||
if err != nil {
|
||||
stack.push(new(big.Int))
|
||||
|
|
@ -579,7 +579,7 @@ func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contra
|
|||
gas.Add(gas, params.CallStipend)
|
||||
}
|
||||
|
||||
ret, err := env.CallCode(contract, address, args, gas, contract.Price, value)
|
||||
ret, err := env.CallCode(contract, address, args, gas, value)
|
||||
|
||||
if err != nil {
|
||||
stack.push(new(big.Int))
|
||||
|
|
@ -596,7 +596,7 @@ func opDelegateCall(instr instruction, pc *uint64, env Environment, contract *Co
|
|||
|
||||
toAddr := common.BigToAddress(to)
|
||||
args := memory.Get(inOffset.Int64(), inSize.Int64())
|
||||
ret, err := env.DelegateCall(contract, toAddr, args, gas, contract.Price)
|
||||
ret, err := env.DelegateCall(contract, toAddr, args, gas)
|
||||
if err != nil {
|
||||
stack.push(new(big.Int))
|
||||
} else {
|
||||
|
|
|
|||
228
core/vm/jit.go
228
core/vm/jit.go
|
|
@ -18,11 +18,13 @@ package vm
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
gmath "math"
|
||||
"math/big"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
|
|
@ -114,7 +116,7 @@ func (p *Program) addInstr(op OpCode, pc uint64, fn instrFn, data *big.Int) {
|
|||
if op >= DUP1 && op <= DUP16 {
|
||||
baseOp = DUP1
|
||||
}
|
||||
base := _baseCheck[baseOp]
|
||||
base := _baseCheck64[baseOp]
|
||||
|
||||
returns := op == RETURN || op == SUICIDE || op == STOP
|
||||
instr := instruction{op, pc, fn, data, base.gas, base.stackPop, base.stackPush, returns}
|
||||
|
|
@ -357,14 +359,15 @@ func validDest(dests map[uint64]struct{}, dest *big.Int) bool {
|
|||
|
||||
// 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, statedb Database, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) {
|
||||
func jitCalculateGasAndSize(env Environment, contract *Contract, instr instruction, statedb Database, mem *Memory, stack *Stack) (uint64, uint64, error) {
|
||||
var (
|
||||
gas = new(big.Int)
|
||||
newMemSize *big.Int = new(big.Int)
|
||||
newMemSize uint64
|
||||
sizeFault bool
|
||||
)
|
||||
err := jitBaseCheck(instr, stack, gas)
|
||||
|
||||
gas, err := jitBaseCalc(instr, stack)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
// stack Check, memory resize & gas phase
|
||||
|
|
@ -373,40 +376,57 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
|
|||
n := int(op - SWAP1 + 2)
|
||||
err := stack.require(n)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return 0, 0, err
|
||||
}
|
||||
gas.Set(GasFastestStep)
|
||||
gas = GasFastestStep64
|
||||
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
|
||||
return 0, 0, err
|
||||
}
|
||||
gas.Set(GasFastestStep)
|
||||
gas = GasFastestStep64
|
||||
case LOG0, LOG1, LOG2, LOG3, LOG4:
|
||||
n := int(op - LOG0)
|
||||
err := stack.require(n + 2)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
mSize, mStart := stack.data[stack.len()-2], stack.data[stack.len()-1]
|
||||
if mSize.BitLen() > 64 {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
msize64 := mSize.Uint64()
|
||||
|
||||
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))
|
||||
gas = (gas + LogGas64) + (uint64(n) * LogTopicGas64)
|
||||
if !math.IsMulSafe(msize64, LogDataGas64) {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
gasLogData := msize64 * LogDataGas64
|
||||
|
||||
newMemSize = calcMemSize(mStart, mSize)
|
||||
if !math.IsAddSafe(gas, gasLogData) {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
gas += gasLogData
|
||||
|
||||
newMemSize, sizeFault = calcMemSize64(mStart, mSize)
|
||||
case EXP:
|
||||
gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), params.ExpByteGas))
|
||||
x := uint64(len(stack.data[stack.len()-2].Bytes()))
|
||||
if !math.IsMulSafe(x, ExpByteGas64) {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
x *= ExpByteGas64
|
||||
if !math.IsAddSafe(gas, x) {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
gas += x
|
||||
case SSTORE:
|
||||
err := stack.require(2)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
var g *big.Int
|
||||
y, x := stack.data[stack.len()-2], stack.data[stack.len()-1]
|
||||
val := statedb.GetState(contract.Address(), common.BigToHash(x))
|
||||
|
||||
|
|
@ -415,98 +435,152 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
|
|||
// 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
|
||||
gas = SstoreSetGas64
|
||||
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
|
||||
statedb.AddRefund(params.SstoreRefundGas)
|
||||
|
||||
g = params.SstoreClearGas
|
||||
gas = SstoreClearGas64
|
||||
} else {
|
||||
g = params.SstoreResetGas
|
||||
gas = SstoreResetGas64
|
||||
}
|
||||
gas.Set(g)
|
||||
case SUICIDE:
|
||||
if !statedb.IsDeleted(contract.Address()) {
|
||||
statedb.AddRefund(params.SuicideRefundGas)
|
||||
}
|
||||
case MLOAD:
|
||||
newMemSize = calcMemSize(stack.peek(), u256(32))
|
||||
newMemSize, sizeFault = calcMemSize64(stack.peek(), u256(32))
|
||||
case MSTORE8:
|
||||
newMemSize = calcMemSize(stack.peek(), u256(1))
|
||||
newMemSize, sizeFault = calcMemSize64(stack.peek(), u256(1))
|
||||
case MSTORE:
|
||||
newMemSize = calcMemSize(stack.peek(), u256(32))
|
||||
newMemSize, sizeFault = calcMemSize64(stack.peek(), u256(32))
|
||||
case RETURN:
|
||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
|
||||
newMemSize, sizeFault = calcMemSize64(stack.peek(), stack.data[stack.len()-2])
|
||||
case SHA3:
|
||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
|
||||
newMemSize, sizeFault = calcMemSize64(stack.peek(), stack.data[stack.len()-2])
|
||||
if sizeFault {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
|
||||
words := toWordSize(stack.data[stack.len()-2])
|
||||
gas.Add(gas, words.Mul(words, params.Sha3WordGas))
|
||||
if stack.data[stack.len()-2].BitLen() > 64 {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
words := toWordSize64(stack.data[stack.len()-2].Uint64())
|
||||
if !math.IsMulSafe(words, KeccakWordGas64) {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
wordsGas := words * KeccakWordGas64
|
||||
if !math.IsAddSafe(gas, wordsGas) {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
gas += wordsGas
|
||||
case CALLDATACOPY:
|
||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
|
||||
newMemSize, sizeFault = calcMemSize64(stack.peek(), stack.data[stack.len()-3])
|
||||
if sizeFault {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
|
||||
words := toWordSize(stack.data[stack.len()-3])
|
||||
gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||
words := toWordSize64(stack.data[stack.len()-3].Uint64())
|
||||
if !math.IsMulSafe(words, CopyGas64) {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
wordsGas := words * CopyGas64
|
||||
if !math.IsAddSafe(gas, wordsGas) {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
gas += wordsGas
|
||||
case CODECOPY:
|
||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
|
||||
newMemSize, sizeFault = calcMemSize64(stack.peek(), stack.data[stack.len()-3])
|
||||
if sizeFault {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
|
||||
words := toWordSize(stack.data[stack.len()-3])
|
||||
gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||
words := toWordSize64(stack.data[stack.len()-3].Uint64())
|
||||
if !math.IsMulSafe(words, CopyGas64) {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
wordsGas := words * CopyGas64
|
||||
if !math.IsAddSafe(gas, wordsGas) {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
gas += wordsGas
|
||||
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))
|
||||
newMemSize, sizeFault = calcMemSize64(stack.data[stack.len()-2], stack.data[stack.len()-4])
|
||||
if sizeFault {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
|
||||
words := toWordSize64(stack.data[stack.len()-4].Uint64())
|
||||
if !math.IsMulSafe(words, CopyGas64) {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
wordsGas := words * CopyGas64
|
||||
if !math.IsAddSafe(gas, wordsGas) {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
gas += wordsGas
|
||||
case CREATE:
|
||||
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3])
|
||||
newMemSize, sizeFault = calcMemSize64(stack.data[stack.len()-2], stack.data[stack.len()-3])
|
||||
case CALL, CALLCODE:
|
||||
gas.Add(gas, stack.data[stack.len()-1])
|
||||
callGas := stack.data[stack.len()-1]
|
||||
if callGas.BitLen() > 64 {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
gas += callGas.Uint64()
|
||||
|
||||
if op == CALL {
|
||||
if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) {
|
||||
gas.Add(gas, params.CallNewAccountGas)
|
||||
if !math.IsAddSafe(gas, CallNewAccountGas64) {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
gas += CallNewAccountGas64
|
||||
}
|
||||
}
|
||||
|
||||
if len(stack.data[stack.len()-3].Bytes()) > 0 {
|
||||
gas.Add(gas, params.CallValueTransferGas)
|
||||
if !math.IsAddSafe(gas, CallValueTransferGas64) {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
gas += CallValueTransferGas64
|
||||
}
|
||||
|
||||
x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7])
|
||||
y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5])
|
||||
x, xSizeFault := calcMemSize64(stack.data[stack.len()-6], stack.data[stack.len()-7])
|
||||
if xSizeFault {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
y, ySizeFault := calcMemSize64(stack.data[stack.len()-4], stack.data[stack.len()-5])
|
||||
if ySizeFault {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
|
||||
newMemSize = common.BigMax(x, y)
|
||||
newMemSize = x
|
||||
if y > newMemSize {
|
||||
newMemSize = 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)
|
||||
callGas := stack.data[stack.len()-1]
|
||||
if callGas.BitLen() > 64 {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
gas += callGas.Uint64()
|
||||
|
||||
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
|
||||
x, xSizeFault := calcMemSize64(stack.data[stack.len()-5], stack.data[stack.len()-6])
|
||||
if xSizeFault {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
y, ySizeFault := calcMemSize64(stack.data[stack.len()-3], stack.data[stack.len()-4])
|
||||
if ySizeFault {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
|
||||
newMemSize = uint64(gmath.Max(float64(x), float64(y)))
|
||||
}
|
||||
if sizeFault {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
|
||||
memGas, _ := calcQuadMemGas64(mem, newMemSize)
|
||||
if !math.IsAddSafe(gas, memGas) {
|
||||
return 0, 0, OutOfGasError
|
||||
}
|
||||
return toWordSize64(newMemSize) * 32, gas + memGas, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,14 +31,15 @@ func optimiseProgram(program *Program) {
|
|||
|
||||
var (
|
||||
statsJump = 0
|
||||
statsPush = 0
|
||||
statsOldPush = 0
|
||||
statsNewPush = 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)
|
||||
glog.Infof("optimised %x done in %v with JMP: %d PSH: %d/%d\n", program.Id[:4], time.Since(tstart), statsJump, statsNewPush, statsOldPush)
|
||||
}()
|
||||
}
|
||||
|
||||
|
|
@ -65,6 +66,7 @@ func optimiseProgram(program *Program) {
|
|||
switch {
|
||||
case instr.op.IsPush():
|
||||
load = append(load, instr)
|
||||
statsOldPush++
|
||||
case instr.op.IsStaticJump():
|
||||
if len(load) == 0 {
|
||||
continue
|
||||
|
|
@ -74,7 +76,7 @@ func optimiseProgram(program *Program) {
|
|||
if len(load) > 2 {
|
||||
seg, size := makePushSeg(load[:len(load)-1])
|
||||
program.instructions[i-size-1] = seg
|
||||
statsPush++
|
||||
statsNewPush++
|
||||
}
|
||||
// create a segment consisting of a pre determined
|
||||
// jump, destination and validity.
|
||||
|
|
@ -88,7 +90,7 @@ func optimiseProgram(program *Program) {
|
|||
if len(load) > 1 {
|
||||
seg, size := makePushSeg(load)
|
||||
program.instructions[i-size] = seg
|
||||
statsPush++
|
||||
statsNewPush++
|
||||
}
|
||||
load = nil
|
||||
}
|
||||
|
|
@ -99,12 +101,12 @@ func optimiseProgram(program *Program) {
|
|||
func makePushSeg(instrs []instruction) (pushSeg, int) {
|
||||
var (
|
||||
data []*big.Int
|
||||
gas = new(big.Int)
|
||||
gas uint64
|
||||
)
|
||||
|
||||
for _, instr := range instrs {
|
||||
data = append(data, instr.data)
|
||||
gas.Add(gas, instr.gas)
|
||||
gas += instr.gas
|
||||
}
|
||||
|
||||
return pushSeg{data, gas}, len(instrs)
|
||||
|
|
@ -113,9 +115,7 @@ func makePushSeg(instrs []instruction) (pushSeg, int) {
|
|||
// 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)
|
||||
gas := _baseCheck64[PUSH1].gas + _baseCheck64[JUMP].gas
|
||||
|
||||
contract := &Contract{Code: program.code}
|
||||
pos, err := jump(program.mapping, program.destinations, contract, to)
|
||||
|
|
|
|||
|
|
@ -29,10 +29,7 @@ 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)
|
||||
}
|
||||
CompileProgram(prog)
|
||||
|
||||
if instr, ok := prog.instructions[0].(pushSeg); ok {
|
||||
if len(instr.data) != 2 {
|
||||
|
|
@ -43,20 +40,16 @@ func TestSegmenting(t *testing.T) {
|
|||
}
|
||||
|
||||
prog = NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(JUMP)})
|
||||
err = CompileProgram(prog)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
CompileProgram(prog)
|
||||
|
||||
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)
|
||||
}
|
||||
CompileProgram(prog)
|
||||
|
||||
if instr, ok := prog.instructions[0].(pushSeg); ok {
|
||||
if len(instr.data) != 2 {
|
||||
t.Error("expected 2 element width pushSegment, got", len(instr.data))
|
||||
|
|
@ -72,10 +65,7 @@ func TestSegmenting(t *testing.T) {
|
|||
|
||||
func TestCompiling(t *testing.T) {
|
||||
prog := NewProgram([]byte{0x60, 0x10})
|
||||
err := CompileProgram(prog)
|
||||
if err != nil {
|
||||
t.Error("didn't expect compile error")
|
||||
}
|
||||
CompileProgram(prog)
|
||||
|
||||
if len(prog.instructions) != 1 {
|
||||
t.Error("expected 1 compiled instruction, got", len(prog.instructions))
|
||||
|
|
@ -85,8 +75,8 @@ func TestCompiling(t *testing.T) {
|
|||
func TestResetInput(t *testing.T) {
|
||||
var sender account
|
||||
|
||||
env := NewEnv(&Config{EnableJit: true, ForceJit: true})
|
||||
contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0))
|
||||
env := NewEnv(true, true)
|
||||
contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000))
|
||||
contract.CodeAddr = &common.Address{}
|
||||
|
||||
program := NewProgram([]byte{})
|
||||
|
|
@ -134,7 +124,7 @@ 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, *big.Int) {}
|
||||
func (account) ReturnGas(uint64) {}
|
||||
func (account) SetCode([]byte) {}
|
||||
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
|
||||
|
||||
|
|
@ -149,7 +139,7 @@ func runVmBench(test vmBench, b *testing.B) {
|
|||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
context := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0))
|
||||
context := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000))
|
||||
context.Code = test.code
|
||||
context.CodeAddr = &common.Address{}
|
||||
_, err := env.Vm().Run(context, test.input)
|
||||
|
|
@ -174,6 +164,7 @@ func NewEnv(config *Config) *Env {
|
|||
|
||||
func (self *Env) RuleSet() RuleSet { return ruleSet{new(big.Int)} }
|
||||
func (self *Env) Vm() Vm { return self.evm }
|
||||
func (self *Env) GasPrice() *big.Int { return new(big.Int) }
|
||||
func (self *Env) Origin() common.Address { return common.Address{} }
|
||||
func (self *Env) BlockNumber() *big.Int { return big.NewInt(0) }
|
||||
|
||||
|
|
@ -197,15 +188,15 @@ func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
|
|||
return true
|
||||
}
|
||||
func (self *Env) Transfer(from, to Account, amount *big.Int) {}
|
||||
func (self *Env) Call(caller ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
func (self *Env) Call(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (self *Env) CallCode(caller ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
func (self *Env) CallCode(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (self *Env) Create(caller ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
|
||||
func (self *Env) Create(caller ContractRef, data []byte, gas, price *big.Int) ([]byte, common.Address, error) {
|
||||
return nil, common.Address{}, nil
|
||||
}
|
||||
func (self *Env) DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
|
||||
func (self *Env) DelegateCall(me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ type dummyContractRef struct {
|
|||
calledForEach bool
|
||||
}
|
||||
|
||||
func (dummyContractRef) ReturnGas(*big.Int, *big.Int) {}
|
||||
func (dummyContractRef) ReturnGas(uint64) {}
|
||||
func (dummyContractRef) Address() common.Address { return common.Address{} }
|
||||
func (dummyContractRef) Value() *big.Int { return new(big.Int) }
|
||||
func (dummyContractRef) SetCode([]byte) {}
|
||||
|
|
@ -61,7 +61,7 @@ func TestStoreCapture(t *testing.T) {
|
|||
logger = NewStructLogger(nil)
|
||||
mem = NewMemory()
|
||||
stack = newstack()
|
||||
contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), new(big.Int), new(big.Int))
|
||||
contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), new(big.Int))
|
||||
)
|
||||
stack.push(big.NewInt(1))
|
||||
stack.push(big.NewInt(0))
|
||||
|
|
@ -83,7 +83,7 @@ func TestStorageCapture(t *testing.T) {
|
|||
t.Skip("implementing this function is difficult. it requires all sort of interfaces to be implemented which isn't trivial. The value (the actual test) isn't worth it")
|
||||
var (
|
||||
ref = &dummyContractRef{}
|
||||
contract = NewContract(ref, ref, new(big.Int), new(big.Int), new(big.Int))
|
||||
contract = NewContract(ref, ref, new(big.Int), new(big.Int))
|
||||
env = newDummyEnv(ref)
|
||||
logger = NewStructLogger(nil)
|
||||
mem = NewMemory()
|
||||
|
|
|
|||
|
|
@ -21,10 +21,11 @@ import "fmt"
|
|||
// Memory implements a simple memory model for the ethereum virtual machine.
|
||||
type Memory struct {
|
||||
store []byte
|
||||
cost uint64
|
||||
}
|
||||
|
||||
func NewMemory() *Memory {
|
||||
return &Memory{nil}
|
||||
return &Memory{}
|
||||
}
|
||||
|
||||
// Set sets offset + size to value
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ type Env struct {
|
|||
time *big.Int
|
||||
difficulty *big.Int
|
||||
gasLimit *big.Int
|
||||
gasPrice *big.Int
|
||||
|
||||
getHashFn func(uint64) common.Hash
|
||||
|
||||
|
|
@ -55,6 +56,7 @@ func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
|
|||
time: cfg.Time,
|
||||
difficulty: cfg.Difficulty,
|
||||
gasLimit: cfg.GasLimit,
|
||||
gasPrice: cfg.GasPrice,
|
||||
}
|
||||
env.evm = vm.New(env, vm.Config{
|
||||
Debug: cfg.Debug,
|
||||
|
|
@ -66,6 +68,7 @@ func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
|
|||
}
|
||||
|
||||
func (self *Env) RuleSet() vm.RuleSet { return self.ruleSet }
|
||||
func (self *Env) GasPrice() *big.Int { return self.gasPrice }
|
||||
func (self *Env) Vm() vm.Vm { return self.evm }
|
||||
func (self *Env) Origin() common.Address { return self.origin }
|
||||
func (self *Env) BlockNumber() *big.Int { return self.number }
|
||||
|
|
@ -97,17 +100,17 @@ func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {
|
|||
core.Transfer(from, to, amount)
|
||||
}
|
||||
|
||||
func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
return core.Call(self, caller, addr, data, gas, price, value)
|
||||
func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
||||
return core.Call(self, caller, addr, data, gas, value)
|
||||
}
|
||||
func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
return core.CallCode(self, caller, addr, data, gas, price, value)
|
||||
func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
||||
return core.CallCode(self, caller, addr, data, gas, value)
|
||||
}
|
||||
|
||||
func (self *Env) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
|
||||
return core.DelegateCall(self, me, addr, data, gas, price)
|
||||
func (self *Env) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
|
||||
return core.DelegateCall(self, me, addr, data, gas)
|
||||
}
|
||||
|
||||
func (self *Env) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
|
||||
return core.Create(self, caller, data, gas, price, value)
|
||||
func (self *Env) Create(caller vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
|
||||
return core.Create(self, caller, data, gas, value)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,6 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
|||
receiver.Address(),
|
||||
input,
|
||||
cfg.GasLimit,
|
||||
cfg.GasPrice,
|
||||
cfg.Value,
|
||||
)
|
||||
|
||||
|
|
@ -136,7 +135,6 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, error) {
|
|||
address,
|
||||
input,
|
||||
cfg.GasLimit,
|
||||
cfg.GasPrice,
|
||||
cfg.Value,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import "math/big"
|
|||
type jumpSeg struct {
|
||||
pos uint64
|
||||
err error
|
||||
gas *big.Int
|
||||
gas uint64
|
||||
}
|
||||
|
||||
func (j jumpSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
|
|
@ -39,7 +39,7 @@ func (s jumpSeg) Op() OpCode { return 0 }
|
|||
|
||||
type pushSeg struct {
|
||||
data []*big.Int
|
||||
gas *big.Int
|
||||
gas uint64
|
||||
}
|
||||
|
||||
func (s pushSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
|
|
|
|||
175
core/vm/uint64.go
Normal file
175
core/vm/uint64.go
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
gmath "math"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var (
|
||||
maxInt63 = new(big.Int).Exp(big.NewInt(2), big.NewInt(63), big.NewInt(0))
|
||||
maxIntCap = new(big.Int).Sub(maxInt63, big.NewInt(1))
|
||||
)
|
||||
|
||||
var (
|
||||
StackLimit64 = params.StackLimit.Uint64()
|
||||
GasQuickStep64 uint64 = 2
|
||||
GasFastestStep64 uint64 = 3
|
||||
GasFastStep64 uint64 = 5
|
||||
GasMidStep64 uint64 = 8
|
||||
GasSlowStep64 uint64 = 10
|
||||
GasExtStep64 uint64 = 20
|
||||
|
||||
GasReturn64 uint64 = 0
|
||||
GasStop64 uint64 = 0
|
||||
|
||||
GasContractByte64 uint64 = 200
|
||||
LogGas64 = params.LogGas.Uint64()
|
||||
LogTopicGas64 = params.LogTopicGas.Uint64()
|
||||
LogDataGas64 = params.LogDataGas.Uint64()
|
||||
ExpByteGas64 = params.ExpByteGas.Uint64()
|
||||
SstoreSetGas64 = params.SstoreSetGas.Uint64()
|
||||
SstoreClearGas64 = params.SstoreClearGas.Uint64()
|
||||
SstoreResetGas64 = params.SstoreResetGas.Uint64()
|
||||
KeccakWordGas64 = params.Sha3WordGas.Uint64()
|
||||
CopyGas64 = params.CopyGas.Uint64()
|
||||
CallNewAccountGas64 = params.CallNewAccountGas.Uint64()
|
||||
CallValueTransferGas64 = params.CallValueTransferGas.Uint64()
|
||||
MemoryGas64 = params.MemoryGas.Uint64()
|
||||
QuadCoeffDiv64 = params.QuadCoeffDiv.Uint64()
|
||||
)
|
||||
|
||||
// casts a arbitrary number to the amount of words (sets of 32 bytes)
|
||||
func toWordSize64(size uint64) uint64 {
|
||||
return (size + 31) / 32
|
||||
}
|
||||
|
||||
// calculates the memory size required for a step
|
||||
func calcMemSize64(off, l *big.Int) (uint64, bool) {
|
||||
if l.Cmp(common.Big0) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
size := new(big.Int).Add(off, l)
|
||||
if size.Cmp(maxIntCap) > 0 {
|
||||
return 0, true
|
||||
}
|
||||
return size.Uint64(), false
|
||||
}
|
||||
|
||||
// calculates the quadratic gas
|
||||
// TODO this function requires guarding of overflows
|
||||
func calcQuadMemGas64(mem *Memory, newMemSize uint64) (uint64, bool) {
|
||||
oldTotalFee := mem.cost
|
||||
if newMemSize > 0 {
|
||||
newMemSizeWords := toWordSize64(newMemSize)
|
||||
newMemSize = newMemSizeWords * 32
|
||||
|
||||
if newMemSize > uint64(mem.Len()) {
|
||||
pow := uint64(gmath.Pow(float64(newMemSizeWords), 2))
|
||||
linCoef := newMemSizeWords * MemoryGas64
|
||||
quadCoef := pow / QuadCoeffDiv64
|
||||
newTotalFee := linCoef + quadCoef
|
||||
|
||||
fee := newTotalFee - oldTotalFee
|
||||
mem.cost = newTotalFee
|
||||
|
||||
return fee, false
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// jitBaseCalc is the same as baseCheck except it doesn't do the look up in the
|
||||
// gas table. This is done during compilation instead.
|
||||
func jitBaseCalc(instr instruction, stack *Stack) (uint64, error) {
|
||||
err := stack.require(instr.spop)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if instr.spush > 0 && stack.len()-instr.spop+instr.spush > int(StackLimit64) {
|
||||
return 0, fmt.Errorf("stack limit reached %d (%d)", stack.len(), StackLimit64)
|
||||
}
|
||||
|
||||
// 0 on gas means no base calculation
|
||||
if instr.gas == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return instr.gas, nil
|
||||
}
|
||||
|
||||
type req64 struct {
|
||||
stackPop int
|
||||
gas uint64
|
||||
stackPush int
|
||||
}
|
||||
|
||||
var _baseCheck64 = map[OpCode]req64{
|
||||
// opcode | stack pop | gas price | stack push
|
||||
ADD: {2, GasFastestStep64, 1},
|
||||
LT: {2, GasFastestStep64, 1},
|
||||
GT: {2, GasFastestStep64, 1},
|
||||
SLT: {2, GasFastestStep64, 1},
|
||||
SGT: {2, GasFastestStep64, 1},
|
||||
EQ: {2, GasFastestStep64, 1},
|
||||
ISZERO: {1, GasFastestStep64, 1},
|
||||
SUB: {2, GasFastestStep64, 1},
|
||||
AND: {2, GasFastestStep64, 1},
|
||||
OR: {2, GasFastestStep64, 1},
|
||||
XOR: {2, GasFastestStep64, 1},
|
||||
NOT: {1, GasFastestStep64, 1},
|
||||
BYTE: {2, GasFastestStep64, 1},
|
||||
CALLDATALOAD: {1, GasFastestStep64, 1},
|
||||
CALLDATACOPY: {3, GasFastestStep64, 1},
|
||||
MLOAD: {1, GasFastestStep64, 1},
|
||||
MSTORE: {2, GasFastestStep64, 0},
|
||||
MSTORE8: {2, GasFastestStep64, 0},
|
||||
CODECOPY: {3, GasFastestStep64, 0},
|
||||
MUL: {2, GasFastStep64, 1},
|
||||
DIV: {2, GasFastStep64, 1},
|
||||
SDIV: {2, GasFastStep64, 1},
|
||||
MOD: {2, GasFastStep64, 1},
|
||||
SMOD: {2, GasFastStep64, 1},
|
||||
SIGNEXTEND: {2, GasFastStep64, 1},
|
||||
ADDMOD: {3, GasMidStep64, 1},
|
||||
MULMOD: {3, GasMidStep64, 1},
|
||||
JUMP: {1, GasMidStep64, 0},
|
||||
JUMPI: {2, GasSlowStep64, 0},
|
||||
EXP: {2, GasSlowStep64, 1},
|
||||
ADDRESS: {0, GasQuickStep64, 1},
|
||||
ORIGIN: {0, GasQuickStep64, 1},
|
||||
CALLER: {0, GasQuickStep64, 1},
|
||||
CALLVALUE: {0, GasQuickStep64, 1},
|
||||
CODESIZE: {0, GasQuickStep64, 1},
|
||||
GASPRICE: {0, GasQuickStep64, 1},
|
||||
COINBASE: {0, GasQuickStep64, 1},
|
||||
TIMESTAMP: {0, GasQuickStep64, 1},
|
||||
NUMBER: {0, GasQuickStep64, 1},
|
||||
CALLDATASIZE: {0, GasQuickStep64, 1},
|
||||
DIFFICULTY: {0, GasQuickStep64, 1},
|
||||
GASLIMIT: {0, GasQuickStep64, 1},
|
||||
POP: {1, GasQuickStep64, 0},
|
||||
PC: {0, GasQuickStep64, 1},
|
||||
MSIZE: {0, GasQuickStep64, 1},
|
||||
GAS: {0, GasQuickStep64, 1},
|
||||
BLOCKHASH: {1, GasExtStep64, 1},
|
||||
BALANCE: {1, GasExtStep64, 1},
|
||||
EXTCODESIZE: {1, GasExtStep64, 1},
|
||||
EXTCODECOPY: {4, GasExtStep64, 0},
|
||||
SLOAD: {1, params.SloadGas.Uint64(), 1},
|
||||
SSTORE: {2, 0, 0},
|
||||
SHA3: {2, params.Sha3Gas.Uint64(), 1},
|
||||
CREATE: {3, params.CreateGas.Uint64(), 1},
|
||||
CALL: {7, params.CallGas.Uint64(), 1},
|
||||
CALLCODE: {7, params.CallGas.Uint64(), 1},
|
||||
DELEGATECALL: {6, params.CallGas.Uint64(), 1},
|
||||
JUMPDEST: {0, params.JumpdestGas.Uint64(), 0},
|
||||
SUICIDE: {1, 0, 0},
|
||||
RETURN: {2, 0, 0},
|
||||
PUSH1: {0, GasFastestStep64, 1},
|
||||
DUP1: {0, 0, 1},
|
||||
}
|
||||
|
|
@ -141,7 +141,8 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
|||
// 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)
|
||||
gas := new(big.Int).SetUint64(contract.gas64)
|
||||
evm.cfg.Tracer.CaptureState(evm.env, pc, op, gas, cost, mem, stack, contract, evm.env.Depth(), err)
|
||||
}
|
||||
}()
|
||||
|
||||
|
|
@ -149,22 +150,11 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
|||
glog.Infof("running byte VM %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("VM %x done. time: %v instrc: %v\n", codehash[:4], time.Since(tstart), instrCount)
|
||||
}()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Get the memory location of pc
|
||||
op = contract.GetOp(pc)
|
||||
// calculate the new memory size and gas price for the current executing opcode
|
||||
|
|
@ -175,7 +165,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
|||
|
||||
// Use the calculated gas. When insufficient gas is present, use all gas and return an
|
||||
// Out Of Gas error
|
||||
if !contract.UseGas(cost) {
|
||||
if !contract.UseGas(cost.Uint64()) {
|
||||
return nil, OutOfGasError
|
||||
}
|
||||
|
||||
|
|
@ -183,7 +173,8 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
|||
mem.Resize(newMemSize.Uint64())
|
||||
// Add a log message
|
||||
if evm.cfg.Debug {
|
||||
evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), nil)
|
||||
gas := new(big.Int).SetUint64(contract.gas64)
|
||||
evm.cfg.Tracer.CaptureState(evm.env, pc, op, gas, cost, mem, stack, contract, evm.env.Depth(), nil)
|
||||
}
|
||||
|
||||
if opPtr := evm.jumpTable[op]; opPtr.valid {
|
||||
|
|
@ -369,7 +360,7 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
|
|||
// 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) {
|
||||
if contract.UseGas(gas.Uint64()) {
|
||||
ret = p.Call(input)
|
||||
|
||||
return ret, nil
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase }
|
|||
func (self *VMEnv) Time() *big.Int { return self.header.Time }
|
||||
func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty }
|
||||
func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit }
|
||||
func (self *VMEnv) GasPrice() *big.Int { return self.msg.GasPrice() }
|
||||
func (self *VMEnv) Value() *big.Int { return self.msg.Value() }
|
||||
func (self *VMEnv) Db() vm.Database { return self.state }
|
||||
func (self *VMEnv) Depth() int { return self.depth }
|
||||
|
|
@ -101,17 +102,17 @@ func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) {
|
|||
Transfer(from, to, amount)
|
||||
}
|
||||
|
||||
func (self *VMEnv) Call(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
return Call(self, me, addr, data, gas, price, value)
|
||||
func (self *VMEnv) Call(me vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
||||
return Call(self, me, addr, data, gas, value)
|
||||
}
|
||||
func (self *VMEnv) CallCode(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
return CallCode(self, me, addr, data, gas, price, value)
|
||||
func (self *VMEnv) CallCode(me vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
||||
return CallCode(self, me, addr, data, gas, value)
|
||||
}
|
||||
|
||||
func (self *VMEnv) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
|
||||
return DelegateCall(self, me, addr, data, gas, price)
|
||||
func (self *VMEnv) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
|
||||
return DelegateCall(self, me, addr, data, gas)
|
||||
}
|
||||
|
||||
func (self *VMEnv) Create(me vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
|
||||
return Create(self, me, data, gas, price, value)
|
||||
func (self *VMEnv) Create(me vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
|
||||
return Create(self, me, data, gas, value)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
|
@ -115,7 +116,7 @@ func runStateTests(ruleSet RuleSet, tests map[string]VmTest, skipTests []string)
|
|||
}
|
||||
|
||||
for name, test := range tests {
|
||||
if skipTest[name] /*|| name != "callcodecallcode_11" */ {
|
||||
if skipTest[name] /*|| name != "TestInputLimitsLight"*/ {
|
||||
glog.Infoln("Skipping state test", name)
|
||||
continue
|
||||
}
|
||||
|
|
@ -162,6 +163,10 @@ func runStateTest(ruleSet RuleSet, test VmTest) error {
|
|||
// err error
|
||||
logs vm.Logs
|
||||
)
|
||||
if common.Big(test.Transaction["gasLimit"]).Cmp(new(big.Int).SetUint64(math.MaxUint64)) > 0 {
|
||||
fmt.Println("skipping a test because of unsupported high gas")
|
||||
return nil
|
||||
}
|
||||
|
||||
ret, logs, _, _ = RunState(ruleSet, statedb, env, test.Transaction)
|
||||
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ type Env struct {
|
|||
state *state.StateDB
|
||||
skipTransfer bool
|
||||
initial bool
|
||||
Gas *big.Int
|
||||
Gas, gasPrice *big.Int
|
||||
|
||||
origin common.Address
|
||||
parent common.Hash
|
||||
|
|
@ -190,6 +190,7 @@ func NewEnvFromMap(ruleSet RuleSet, state *state.StateDB, envValues map[string]s
|
|||
env.difficulty = common.Big(envValues["currentDifficulty"])
|
||||
env.gasLimit = common.Big(envValues["currentGasLimit"])
|
||||
env.Gas = new(big.Int)
|
||||
env.gasPrice = common.Big(exeValues["gasPrice"])
|
||||
|
||||
env.evm = vm.New(env, vm.Config{
|
||||
EnableJit: EnableJit,
|
||||
|
|
@ -208,6 +209,7 @@ func (self *Env) Time() *big.Int { return self.time }
|
|||
func (self *Env) Difficulty() *big.Int { return self.difficulty }
|
||||
func (self *Env) Db() vm.Database { return self.state }
|
||||
func (self *Env) GasLimit() *big.Int { return self.gasLimit }
|
||||
func (self *Env) GasPrice() *big.Int { return self.gasPrice }
|
||||
func (self *Env) VmType() vm.Type { return vm.StdVmTy }
|
||||
func (self *Env) GetHash(n uint64) common.Hash {
|
||||
return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String())))
|
||||
|
|
@ -241,46 +243,46 @@ func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {
|
|||
core.Transfer(from, to, amount)
|
||||
}
|
||||
|
||||
func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
||||
if self.vmTest && self.depth > 0 {
|
||||
caller.ReturnGas(gas, price)
|
||||
caller.ReturnGas(gas.Uint64())
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
ret, err := core.Call(self, caller, addr, data, gas, price, value)
|
||||
ret, err := core.Call(self, caller, addr, data, gas, value)
|
||||
self.Gas = gas
|
||||
|
||||
return ret, err
|
||||
|
||||
}
|
||||
func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
||||
if self.vmTest && self.depth > 0 {
|
||||
caller.ReturnGas(gas, price)
|
||||
caller.ReturnGas(gas.Uint64())
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
return core.CallCode(self, caller, addr, data, gas, price, value)
|
||||
return core.CallCode(self, caller, addr, data, gas, value)
|
||||
}
|
||||
|
||||
func (self *Env) DelegateCall(caller vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
|
||||
func (self *Env) DelegateCall(caller vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
|
||||
if self.vmTest && self.depth > 0 {
|
||||
caller.ReturnGas(gas, price)
|
||||
caller.ReturnGas(gas.Uint64())
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
return core.DelegateCall(self, caller, addr, data, gas, price)
|
||||
return core.DelegateCall(self, caller, addr, data, gas)
|
||||
}
|
||||
|
||||
func (self *Env) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
|
||||
func (self *Env) Create(caller vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
|
||||
if self.vmTest {
|
||||
caller.ReturnGas(gas, price)
|
||||
caller.ReturnGas(gas.Uint64())
|
||||
|
||||
nonce := self.state.GetNonce(caller.Address())
|
||||
obj := self.state.GetOrNewStateObject(crypto.CreateAddress(caller.Address(), nonce))
|
||||
|
||||
return nil, obj.Address(), nil
|
||||
} else {
|
||||
return core.Create(self, caller, data, gas, price, value)
|
||||
return core.Create(self, caller, data, gas, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -135,9 +135,9 @@ func runVmTests(tests map[string]VmTest, skipTests []string) error {
|
|||
}
|
||||
|
||||
for name, test := range tests {
|
||||
if skipTest[name] {
|
||||
if skipTest[name] /*|| name != "57a9a2fbfe21a848e8d83379562a8513417c73d127495cf4abc5a44a5fe08e92"*/ {
|
||||
glog.Infoln("Skipping VM test", name)
|
||||
return nil
|
||||
continue
|
||||
}
|
||||
|
||||
if err := runVmTest(test); err != nil {
|
||||
|
|
@ -233,7 +233,6 @@ func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs,
|
|||
from = common.HexToAddress(exec["caller"])
|
||||
data = common.FromHex(exec["data"])
|
||||
gas = common.Big(exec["gas"])
|
||||
price = common.Big(exec["gasPrice"])
|
||||
value = common.Big(exec["value"])
|
||||
)
|
||||
// Reset the pre-compiled contracts for VM tests.
|
||||
|
|
@ -245,7 +244,7 @@ func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs,
|
|||
vmenv.vmTest = true
|
||||
vmenv.skipTransfer = true
|
||||
vmenv.initial = true
|
||||
ret, err := vmenv.Call(caller, to, data, gas, price, value)
|
||||
ret, err := vmenv.Call(caller, to, data, gas, value)
|
||||
|
||||
return ret, vmenv.state.Logs(), vmenv.Gas, err
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue