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"))
|
sender := statedb.CreateAccount(common.StringToAddress("sender"))
|
||||||
|
|
||||||
logger := vm.NewStructLogger(nil)
|
logger := vm.NewStructLogger(nil)
|
||||||
|
vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), common.Big(ctx.GlobalString(PriceFlag.Name)), vm.Config{
|
||||||
vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), vm.Config{
|
|
||||||
Debug: ctx.GlobalBool(DebugFlag.Name),
|
Debug: ctx.GlobalBool(DebugFlag.Name),
|
||||||
ForceJit: ctx.GlobalBool(ForceJitFlag.Name),
|
ForceJit: ctx.GlobalBool(ForceJitFlag.Name),
|
||||||
EnableJit: !ctx.GlobalBool(DisableJitFlag.Name),
|
EnableJit: !ctx.GlobalBool(DisableJitFlag.Name),
|
||||||
|
|
@ -141,7 +140,6 @@ func run(ctx *cli.Context) error {
|
||||||
sender,
|
sender,
|
||||||
input,
|
input,
|
||||||
common.Big(ctx.GlobalString(GasFlag.Name)),
|
common.Big(ctx.GlobalString(GasFlag.Name)),
|
||||||
common.Big(ctx.GlobalString(PriceFlag.Name)),
|
|
||||||
common.Big(ctx.GlobalString(ValueFlag.Name)),
|
common.Big(ctx.GlobalString(ValueFlag.Name)),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -152,7 +150,6 @@ func run(ctx *cli.Context) error {
|
||||||
receiver.Address(),
|
receiver.Address(),
|
||||||
common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)),
|
common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)),
|
||||||
common.Big(ctx.GlobalString(GasFlag.Name)),
|
common.Big(ctx.GlobalString(GasFlag.Name)),
|
||||||
common.Big(ctx.GlobalString(PriceFlag.Name)),
|
|
||||||
common.Big(ctx.GlobalString(ValueFlag.Name)),
|
common.Big(ctx.GlobalString(ValueFlag.Name)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -199,19 +196,20 @@ type VMEnv struct {
|
||||||
transactor *common.Address
|
transactor *common.Address
|
||||||
value *big.Int
|
value *big.Int
|
||||||
|
|
||||||
depth int
|
depth int
|
||||||
Gas *big.Int
|
Gas, price *big.Int
|
||||||
time *big.Int
|
time *big.Int
|
||||||
logs []vm.StructLog
|
logs []vm.StructLog
|
||||||
|
|
||||||
evm *vm.EVM
|
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{
|
env := &VMEnv{
|
||||||
state: state,
|
state: state,
|
||||||
transactor: &transactor,
|
transactor: &transactor,
|
||||||
value: value,
|
value: value,
|
||||||
|
price: price,
|
||||||
time: big.NewInt(time.Now().Unix()),
|
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) BlockHash() []byte { return make([]byte, 32) }
|
||||||
func (self *VMEnv) Value() *big.Int { return self.value }
|
func (self *VMEnv) Value() *big.Int { return self.value }
|
||||||
func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) }
|
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) VmType() vm.Type { return vm.StdVmTy }
|
||||||
func (self *VMEnv) Depth() int { return 0 }
|
func (self *VMEnv) Depth() int { return 0 }
|
||||||
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
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)
|
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
|
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) {
|
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, price, value)
|
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) {
|
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, price)
|
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) {
|
func (self *VMEnv) Create(caller vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
|
||||||
return core.Create(self, caller, data, gas, price, value)
|
return core.Create(self, caller, data, gas, value)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,10 @@
|
||||||
|
|
||||||
package common
|
package common
|
||||||
|
|
||||||
import "math/big"
|
import (
|
||||||
|
"math"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
// Common big integers often used
|
// Common big integers often used
|
||||||
var (
|
var (
|
||||||
|
|
@ -33,6 +36,7 @@ var (
|
||||||
Big256 = big.NewInt(0xff)
|
Big256 = big.NewInt(0xff)
|
||||||
Big257 = big.NewInt(257)
|
Big257 = big.NewInt(257)
|
||||||
MaxBig = String2Big("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
|
MaxBig = String2Big("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
|
||||||
|
Max64Bit = new(big.Int).SetUint64(math.MaxUint64)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Big pow
|
// 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
|
// 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) {
|
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, gasPrice, value)
|
ret, _, err = exec(env, caller, &addr, &addr, input, env.Db().GetCode(addr), gas, value)
|
||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CallCode executes the given address' code as the given contract address
|
// 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()
|
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
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DelegateCall is equivalent to CallCode except that sender and value propagates from parent scope to child scope
|
// 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()
|
callerAddr := caller.Address()
|
||||||
originAddr := env.Origin()
|
originAddr := env.Origin()
|
||||||
callerValue := caller.Value()
|
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
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates a new contract with the given code
|
// 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) {
|
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, gasPrice, value)
|
ret, address, err = exec(env, caller, nil, nil, nil, code, gas, value)
|
||||||
// Here we get an error if we run into maximum stack depth,
|
// Here we get an error if we run into maximum stack depth,
|
||||||
// See: https://github.com/ethereum/yellowpaper/pull/131
|
// See: https://github.com/ethereum/yellowpaper/pull/131
|
||||||
// and YP definitions for CREATE instruction
|
// 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
|
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()
|
evm := env.Vm()
|
||||||
// Depth check execution. Fail if we're trying to execute above the
|
// Depth check execution. Fail if we're trying to execute above the
|
||||||
// limit.
|
// limit.
|
||||||
if env.Depth() > int(params.CallCreateDepth.Int64()) {
|
if env.Depth() > int(params.CallCreateDepth.Int64()) {
|
||||||
caller.ReturnGas(gas, gasPrice)
|
caller.ReturnGas(gas.Uint64())
|
||||||
|
|
||||||
return nil, common.Address{}, vm.DepthError
|
return nil, common.Address{}, vm.DepthError
|
||||||
}
|
}
|
||||||
|
|
||||||
if !env.CanTransfer(caller.Address(), value) {
|
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()))
|
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
|
// 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
|
// EVM. The contract is a scoped environment for this execution context
|
||||||
// only.
|
// only.
|
||||||
contract := vm.NewContract(caller, to, value, gas, gasPrice)
|
contract := vm.NewContract(caller, to, value, gas)
|
||||||
contract.SetCallCode(codeAddr, code)
|
contract.SetCallCode(codeAddr, code)
|
||||||
defer contract.Finalise()
|
defer contract.Finalise()
|
||||||
|
|
||||||
|
|
@ -116,7 +116,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
|
||||||
if err == nil && createAccount {
|
if err == nil && createAccount {
|
||||||
dataGas := big.NewInt(int64(len(ret)))
|
dataGas := big.NewInt(int64(len(ret)))
|
||||||
dataGas.Mul(dataGas, params.CreateDataGas)
|
dataGas.Mul(dataGas, params.CreateDataGas)
|
||||||
if contract.UseGas(dataGas) {
|
if contract.UseGas(dataGas.Uint64()) {
|
||||||
env.Db().SetCode(*address, ret)
|
env.Db().SetCode(*address, ret)
|
||||||
} else {
|
} else {
|
||||||
err = vm.CodeStoreOutOfGasError
|
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
|
// 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.
|
// when we're in homestead this also counts for code storage gas errors.
|
||||||
if err != nil && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) {
|
if err != nil && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) {
|
||||||
contract.UseGas(contract.Gas)
|
contract.UseGas(contract.Gas())
|
||||||
|
|
||||||
env.SetSnapshot(snapshotPreTransfer)
|
env.SetSnapshot(snapshotPreTransfer)
|
||||||
}
|
}
|
||||||
|
|
@ -135,12 +135,12 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
|
||||||
return ret, addr, err
|
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()
|
evm := env.Vm()
|
||||||
// Depth check execution. Fail if we're trying to execute above the
|
// Depth check execution. Fail if we're trying to execute above the
|
||||||
// limit.
|
// limit.
|
||||||
if env.Depth() > int(params.CallCreateDepth.Int64()) {
|
if env.Depth() > int(params.CallCreateDepth.Int64()) {
|
||||||
caller.ReturnGas(gas, gasPrice)
|
caller.ReturnGas(gas.Uint64())
|
||||||
return nil, common.Address{}, vm.DepthError
|
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
|
// 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)
|
contract.SetCallCode(codeAddr, code)
|
||||||
defer contract.Finalise()
|
defer contract.Finalise()
|
||||||
|
|
||||||
ret, err = evm.Run(contract, input)
|
ret, err = evm.Run(contract, input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
contract.UseGas(contract.Gas)
|
contract.UseGas(contract.Gas())
|
||||||
|
|
||||||
env.SetSnapshot(snapshot)
|
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
|
// 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 {
|
func (self *StateObject) Copy() *StateObject {
|
||||||
stateObject := NewStateObject(self.Address(), self.db)
|
stateObject := NewStateObject(self.Address(), self.db)
|
||||||
|
|
|
||||||
|
|
@ -244,7 +244,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
|
||||||
vmenv := self.env
|
vmenv := self.env
|
||||||
//var addr common.Address
|
//var addr common.Address
|
||||||
if contractCreation {
|
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 {
|
if homestead && err == vm.CodeStoreOutOfGasError {
|
||||||
self.gas = Big0
|
self.gas = Big0
|
||||||
}
|
}
|
||||||
|
|
@ -256,7 +256,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
|
||||||
} else {
|
} else {
|
||||||
// Increment the nonce for the next transaction
|
// Increment the nonce for the next transaction
|
||||||
self.state.SetNonce(sender.Address(), self.state.GetNonce(sender.Address())+1)
|
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 {
|
if err != nil {
|
||||||
glog.V(logger.Core).Infoln("VM call err:", err)
|
glog.V(logger.Core).Infoln("VM call err:", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Type is the VM type accepted by **NewVm**
|
// Type is the VM type accepted by **NewVm**
|
||||||
|
|
@ -45,41 +44,6 @@ var (
|
||||||
max = big.NewInt(math.MaxInt64) // Maximum 64 bit integer
|
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
|
// Simple helper
|
||||||
func u256(n int64) *big.Int {
|
func u256(n int64) *big.Int {
|
||||||
return big.NewInt(n)
|
return big.NewInt(n)
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import (
|
||||||
|
|
||||||
// ContractRef is a reference to the contract's backing object
|
// ContractRef is a reference to the contract's backing object
|
||||||
type ContractRef interface {
|
type ContractRef interface {
|
||||||
ReturnGas(*big.Int, *big.Int)
|
ReturnGas(uint64)
|
||||||
Address() common.Address
|
Address() common.Address
|
||||||
Value() *big.Int
|
Value() *big.Int
|
||||||
SetCode([]byte)
|
SetCode([]byte)
|
||||||
|
|
@ -47,7 +47,8 @@ type Contract struct {
|
||||||
Input []byte
|
Input []byte
|
||||||
CodeAddr *common.Address
|
CodeAddr *common.Address
|
||||||
|
|
||||||
value, Gas, UsedGas, Price *big.Int
|
value, gas *big.Int
|
||||||
|
gas64 uint64
|
||||||
|
|
||||||
Args []byte
|
Args []byte
|
||||||
|
|
||||||
|
|
@ -55,7 +56,7 @@ type Contract struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContract returns a new contract environment for the execution of EVM.
|
// 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}
|
c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil}
|
||||||
|
|
||||||
if parent, ok := caller.(*Contract); ok {
|
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
|
// Gas should be a pointer so it can safely be reduced through the run
|
||||||
// This pointer will be off the state transition
|
// 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)
|
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
|
return c
|
||||||
}
|
}
|
||||||
|
|
@ -101,6 +99,11 @@ func (c *Contract) GetByte(n uint64) byte {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gas returns the current gas left
|
||||||
|
func (c *Contract) Gas() uint64 {
|
||||||
|
return c.gas64
|
||||||
|
}
|
||||||
|
|
||||||
// Caller returns the caller of the contract.
|
// Caller returns the caller of the contract.
|
||||||
//
|
//
|
||||||
// Caller will recursively call caller when the contract is a delegate
|
// 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
|
// Finalise finalises the contract and returning any remaining gas to the original
|
||||||
// caller.
|
// caller.
|
||||||
func (c *Contract) Finalise() {
|
func (c *Contract) Finalise() {
|
||||||
|
c.gas.SetUint64(c.gas64)
|
||||||
// Return the remaining gas to the caller
|
// 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
|
// UseGas attempts the use gas and subtracts it and returns true on success
|
||||||
func (c *Contract) UseGas(gas *big.Int) (ok bool) {
|
func (c *Contract) UseGas(gas uint64) (ok bool) {
|
||||||
ok = useGas(c.Gas, gas)
|
if c.gas64 < gas {
|
||||||
if ok {
|
return false
|
||||||
c.UsedGas.Add(c.UsedGas, gas)
|
|
||||||
}
|
}
|
||||||
return
|
c.gas64 -= gas
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReturnGas adds the given gas back to itself.
|
// 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
|
// Return the gas to the context
|
||||||
c.Gas.Add(c.Gas, gas)
|
c.gas64 += gas
|
||||||
c.UsedGas.Sub(c.UsedGas, gas)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Address returns the contracts address
|
// Address returns the contracts address
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,8 @@ type Environment interface {
|
||||||
Difficulty() *big.Int
|
Difficulty() *big.Int
|
||||||
// The gas limit of the block
|
// The gas limit of the block
|
||||||
GasLimit() *big.Int
|
GasLimit() *big.Int
|
||||||
|
// The gas price of the current call
|
||||||
|
GasPrice() *big.Int
|
||||||
// Determines whether it's possible to transact
|
// Determines whether it's possible to transact
|
||||||
CanTransfer(from common.Address, balance *big.Int) bool
|
CanTransfer(from common.Address, balance *big.Int) bool
|
||||||
// Transfers amount from one account to the other
|
// Transfers amount from one account to the other
|
||||||
|
|
@ -66,13 +68,13 @@ type Environment interface {
|
||||||
// Set the current calling depth
|
// Set the current calling depth
|
||||||
SetDepth(i int)
|
SetDepth(i int)
|
||||||
// Call another contract
|
// 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
|
// 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
|
// 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 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.
|
// Vm is the basic interface for an implementation of the EVM.
|
||||||
|
|
@ -116,7 +118,7 @@ type Account interface {
|
||||||
SetNonce(uint64)
|
SetNonce(uint64)
|
||||||
Balance() *big.Int
|
Balance() *big.Int
|
||||||
Address() common.Address
|
Address() common.Address
|
||||||
ReturnGas(*big.Int, *big.Int)
|
ReturnGas(uint64)
|
||||||
SetCode([]byte)
|
SetCode([]byte)
|
||||||
ForEachStorage(cb func(key, value common.Hash) bool)
|
ForEachStorage(cb func(key, value common.Hash) bool)
|
||||||
Value() *big.Int
|
Value() *big.Int
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -72,6 +73,41 @@ func toWordSize(size *big.Int) *big.Int {
|
||||||
return tmp
|
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 {
|
type req struct {
|
||||||
stackPop int
|
stackPop int
|
||||||
gas *big.Int
|
gas *big.Int
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ type instruction struct {
|
||||||
fn instrFn
|
fn instrFn
|
||||||
data *big.Int
|
data *big.Int
|
||||||
|
|
||||||
gas *big.Int
|
gas uint64
|
||||||
spop int
|
spop int
|
||||||
spush int
|
spush int
|
||||||
|
|
||||||
|
|
@ -71,7 +71,7 @@ func (instr instruction) do(program *Program, pc *uint64, env Environment, contr
|
||||||
return nil, OutOfGasError
|
return nil, OutOfGasError
|
||||||
}
|
}
|
||||||
// Resize the memory calculated previously
|
// Resize the memory calculated previously
|
||||||
memory.Resize(newMemSize.Uint64())
|
memory.Resize(newMemSize)
|
||||||
|
|
||||||
// These opcodes return an argument and are therefor handled
|
// These opcodes return an argument and are therefor handled
|
||||||
// differently from the rest of the opcodes
|
// 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) {
|
func opSha3(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
offset, size := stack.pop(), stack.pop()
|
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))
|
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) {
|
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) {
|
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) {
|
func opMload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
offset := stack.pop()
|
offset := stack.pop()
|
||||||
val := common.BigD(memory.Get(offset.Int64(), 32))
|
val := common.BigD(memory.GetPtr(offset.Int64(), 32))
|
||||||
stack.push(val)
|
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) {
|
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) {
|
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()
|
value = stack.pop()
|
||||||
offset, size = stack.pop(), stack.pop()
|
offset, size = stack.pop(), stack.pop()
|
||||||
input = memory.Get(offset.Int64(), size.Int64())
|
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)
|
contract.UseGas(contract.gas64)
|
||||||
_, addr, suberr := env.Create(contract, input, gas, contract.Price, value)
|
_, addr, suberr := env.Create(contract, input, gas, value)
|
||||||
// Push item on the stack based on the returned error. If the ruleset is
|
// Push item on the stack based on the returned error. If the ruleset is
|
||||||
// homestead we must check for CodeStoreOutOfGasError (homestead only
|
// homestead we must check for CodeStoreOutOfGasError (homestead only
|
||||||
// rule) and treat as an error, if the ruleset is frontier we must
|
// rule) and treat as an error, if the ruleset is frontier we must
|
||||||
|
|
@ -548,7 +548,7 @@ func opCall(instr instruction, pc *uint64, env Environment, contract *Contract,
|
||||||
gas.Add(gas, params.CallStipend)
|
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 {
|
if err != nil {
|
||||||
stack.push(new(big.Int))
|
stack.push(new(big.Int))
|
||||||
|
|
@ -579,7 +579,7 @@ func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contra
|
||||||
gas.Add(gas, params.CallStipend)
|
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 {
|
if err != nil {
|
||||||
stack.push(new(big.Int))
|
stack.push(new(big.Int))
|
||||||
|
|
@ -596,7 +596,7 @@ func opDelegateCall(instr instruction, pc *uint64, env Environment, contract *Co
|
||||||
|
|
||||||
toAddr := common.BigToAddress(to)
|
toAddr := common.BigToAddress(to)
|
||||||
args := memory.Get(inOffset.Int64(), inSize.Int64())
|
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 {
|
if err != nil {
|
||||||
stack.push(new(big.Int))
|
stack.push(new(big.Int))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
224
core/vm/jit.go
224
core/vm/jit.go
|
|
@ -18,11 +18,13 @@ package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
gmath "math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"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 {
|
if op >= DUP1 && op <= DUP16 {
|
||||||
baseOp = DUP1
|
baseOp = DUP1
|
||||||
}
|
}
|
||||||
base := _baseCheck[baseOp]
|
base := _baseCheck64[baseOp]
|
||||||
|
|
||||||
returns := op == RETURN || op == SUICIDE || op == STOP
|
returns := op == RETURN || op == SUICIDE || op == STOP
|
||||||
instr := instruction{op, pc, fn, data, base.gas, base.stackPop, base.stackPush, returns}
|
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
|
// 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.
|
// 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 (
|
var (
|
||||||
gas = new(big.Int)
|
newMemSize uint64
|
||||||
newMemSize *big.Int = new(big.Int)
|
sizeFault bool
|
||||||
)
|
)
|
||||||
err := jitBaseCheck(instr, stack, gas)
|
|
||||||
|
gas, err := jitBaseCalc(instr, stack)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// stack Check, memory resize & gas phase
|
// stack Check, memory resize & gas phase
|
||||||
|
|
@ -373,40 +376,57 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
|
||||||
n := int(op - SWAP1 + 2)
|
n := int(op - SWAP1 + 2)
|
||||||
err := stack.require(n)
|
err := stack.require(n)
|
||||||
if err != nil {
|
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:
|
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
|
||||||
n := int(op - DUP1 + 1)
|
n := int(op - DUP1 + 1)
|
||||||
err := stack.require(n)
|
err := stack.require(n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
gas.Set(GasFastestStep)
|
gas = GasFastestStep64
|
||||||
case LOG0, LOG1, LOG2, LOG3, LOG4:
|
case LOG0, LOG1, LOG2, LOG3, LOG4:
|
||||||
n := int(op - LOG0)
|
n := int(op - LOG0)
|
||||||
err := stack.require(n + 2)
|
err := stack.require(n + 2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
mSize, mStart := stack.data[stack.len()-2], stack.data[stack.len()-1]
|
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 = (gas + LogGas64) + (uint64(n) * LogTopicGas64)
|
||||||
gas.Add(gas, params.LogGas)
|
if !math.IsMulSafe(msize64, LogDataGas64) {
|
||||||
gas.Add(gas, add.Mul(big.NewInt(int64(n)), params.LogTopicGas))
|
return 0, 0, OutOfGasError
|
||||||
gas.Add(gas, add.Mul(mSize, params.LogDataGas))
|
}
|
||||||
|
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:
|
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:
|
case SSTORE:
|
||||||
err := stack.require(2)
|
err := stack.require(2)
|
||||||
if err != nil {
|
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]
|
y, x := stack.data[stack.len()-2], stack.data[stack.len()-1]
|
||||||
val := statedb.GetState(contract.Address(), common.BigToHash(x))
|
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)
|
// 2. From a non-zero value address to a zero-value address (DELETE)
|
||||||
// 3. From a non-zero to a non-zero (CHANGE)
|
// 3. From a non-zero to a non-zero (CHANGE)
|
||||||
if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) {
|
if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) {
|
||||||
g = params.SstoreSetGas
|
gas = SstoreSetGas64
|
||||||
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
|
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
|
||||||
statedb.AddRefund(params.SstoreRefundGas)
|
statedb.AddRefund(params.SstoreRefundGas)
|
||||||
|
|
||||||
g = params.SstoreClearGas
|
gas = SstoreClearGas64
|
||||||
} else {
|
} else {
|
||||||
g = params.SstoreResetGas
|
gas = SstoreResetGas64
|
||||||
}
|
}
|
||||||
gas.Set(g)
|
|
||||||
case SUICIDE:
|
case SUICIDE:
|
||||||
if !statedb.IsDeleted(contract.Address()) {
|
if !statedb.IsDeleted(contract.Address()) {
|
||||||
statedb.AddRefund(params.SuicideRefundGas)
|
statedb.AddRefund(params.SuicideRefundGas)
|
||||||
}
|
}
|
||||||
case MLOAD:
|
case MLOAD:
|
||||||
newMemSize = calcMemSize(stack.peek(), u256(32))
|
newMemSize, sizeFault = calcMemSize64(stack.peek(), u256(32))
|
||||||
case MSTORE8:
|
case MSTORE8:
|
||||||
newMemSize = calcMemSize(stack.peek(), u256(1))
|
newMemSize, sizeFault = calcMemSize64(stack.peek(), u256(1))
|
||||||
case MSTORE:
|
case MSTORE:
|
||||||
newMemSize = calcMemSize(stack.peek(), u256(32))
|
newMemSize, sizeFault = calcMemSize64(stack.peek(), u256(32))
|
||||||
case RETURN:
|
case RETURN:
|
||||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
|
newMemSize, sizeFault = calcMemSize64(stack.peek(), stack.data[stack.len()-2])
|
||||||
case SHA3:
|
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])
|
if stack.data[stack.len()-2].BitLen() > 64 {
|
||||||
gas.Add(gas, words.Mul(words, params.Sha3WordGas))
|
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:
|
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])
|
words := toWordSize64(stack.data[stack.len()-3].Uint64())
|
||||||
gas.Add(gas, words.Mul(words, params.CopyGas))
|
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:
|
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])
|
words := toWordSize64(stack.data[stack.len()-3].Uint64())
|
||||||
gas.Add(gas, words.Mul(words, params.CopyGas))
|
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:
|
case EXTCODECOPY:
|
||||||
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4])
|
newMemSize, sizeFault = calcMemSize64(stack.data[stack.len()-2], stack.data[stack.len()-4])
|
||||||
|
if sizeFault {
|
||||||
words := toWordSize(stack.data[stack.len()-4])
|
return 0, 0, OutOfGasError
|
||||||
gas.Add(gas, words.Mul(words, params.CopyGas))
|
}
|
||||||
|
|
||||||
|
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:
|
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:
|
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 op == CALL {
|
||||||
if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) {
|
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 {
|
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])
|
x, xSizeFault := calcMemSize64(stack.data[stack.len()-6], stack.data[stack.len()-7])
|
||||||
y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5])
|
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:
|
case DELEGATECALL:
|
||||||
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()
|
||||||
|
|
||||||
x := calcMemSize(stack.data[stack.len()-5], stack.data[stack.len()-6])
|
x, xSizeFault := calcMemSize64(stack.data[stack.len()-5], stack.data[stack.len()-6])
|
||||||
y := calcMemSize(stack.data[stack.len()-3], stack.data[stack.len()-4])
|
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 = common.BigMax(x, y)
|
newMemSize = uint64(gmath.Max(float64(x), float64(y)))
|
||||||
|
}
|
||||||
|
if sizeFault {
|
||||||
|
return 0, 0, OutOfGasError
|
||||||
}
|
}
|
||||||
quadMemGas(mem, newMemSize, gas)
|
|
||||||
|
|
||||||
return newMemSize, gas, nil
|
memGas, _ := calcQuadMemGas64(mem, newMemSize)
|
||||||
}
|
if !math.IsAddSafe(gas, memGas) {
|
||||||
|
return 0, 0, OutOfGasError
|
||||||
// jitBaseCheck is the same as baseCheck except it doesn't do the look up in the
|
}
|
||||||
// gas table. This is done during compilation instead.
|
return toWordSize64(newMemSize) * 32, gas + memGas, nil
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,15 +30,16 @@ func optimiseProgram(program *Program) {
|
||||||
var load []instruction
|
var load []instruction
|
||||||
|
|
||||||
var (
|
var (
|
||||||
statsJump = 0
|
statsJump = 0
|
||||||
statsPush = 0
|
statsOldPush = 0
|
||||||
|
statsNewPush = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
if glog.V(logger.Debug) {
|
if glog.V(logger.Debug) {
|
||||||
glog.Infof("optimising %x\n", program.Id[:4])
|
glog.Infof("optimising %x\n", program.Id[:4])
|
||||||
tstart := time.Now()
|
tstart := time.Now()
|
||||||
defer func() {
|
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 {
|
switch {
|
||||||
case instr.op.IsPush():
|
case instr.op.IsPush():
|
||||||
load = append(load, instr)
|
load = append(load, instr)
|
||||||
|
statsOldPush++
|
||||||
case instr.op.IsStaticJump():
|
case instr.op.IsStaticJump():
|
||||||
if len(load) == 0 {
|
if len(load) == 0 {
|
||||||
continue
|
continue
|
||||||
|
|
@ -74,7 +76,7 @@ func optimiseProgram(program *Program) {
|
||||||
if len(load) > 2 {
|
if len(load) > 2 {
|
||||||
seg, size := makePushSeg(load[:len(load)-1])
|
seg, size := makePushSeg(load[:len(load)-1])
|
||||||
program.instructions[i-size-1] = seg
|
program.instructions[i-size-1] = seg
|
||||||
statsPush++
|
statsNewPush++
|
||||||
}
|
}
|
||||||
// create a segment consisting of a pre determined
|
// create a segment consisting of a pre determined
|
||||||
// jump, destination and validity.
|
// jump, destination and validity.
|
||||||
|
|
@ -88,7 +90,7 @@ func optimiseProgram(program *Program) {
|
||||||
if len(load) > 1 {
|
if len(load) > 1 {
|
||||||
seg, size := makePushSeg(load)
|
seg, size := makePushSeg(load)
|
||||||
program.instructions[i-size] = seg
|
program.instructions[i-size] = seg
|
||||||
statsPush++
|
statsNewPush++
|
||||||
}
|
}
|
||||||
load = nil
|
load = nil
|
||||||
}
|
}
|
||||||
|
|
@ -99,12 +101,12 @@ func optimiseProgram(program *Program) {
|
||||||
func makePushSeg(instrs []instruction) (pushSeg, int) {
|
func makePushSeg(instrs []instruction) (pushSeg, int) {
|
||||||
var (
|
var (
|
||||||
data []*big.Int
|
data []*big.Int
|
||||||
gas = new(big.Int)
|
gas uint64
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, instr := range instrs {
|
for _, instr := range instrs {
|
||||||
data = append(data, instr.data)
|
data = append(data, instr.data)
|
||||||
gas.Add(gas, instr.gas)
|
gas += instr.gas
|
||||||
}
|
}
|
||||||
|
|
||||||
return pushSeg{data, gas}, len(instrs)
|
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
|
// makeStaticJumpSeg creates a new static jump segment from a predefined
|
||||||
// destination (PUSH, JUMP).
|
// destination (PUSH, JUMP).
|
||||||
func makeStaticJumpSeg(to *big.Int, program *Program) jumpSeg {
|
func makeStaticJumpSeg(to *big.Int, program *Program) jumpSeg {
|
||||||
gas := new(big.Int)
|
gas := _baseCheck64[PUSH1].gas + _baseCheck64[JUMP].gas
|
||||||
gas.Add(gas, _baseCheck[PUSH1].gas)
|
|
||||||
gas.Add(gas, _baseCheck[JUMP].gas)
|
|
||||||
|
|
||||||
contract := &Contract{Code: program.code}
|
contract := &Contract{Code: program.code}
|
||||||
pos, err := jump(program.mapping, program.destinations, contract, to)
|
pos, err := jump(program.mapping, program.destinations, contract, to)
|
||||||
|
|
|
||||||
|
|
@ -29,10 +29,7 @@ const maxRun = 1000
|
||||||
|
|
||||||
func TestSegmenting(t *testing.T) {
|
func TestSegmenting(t *testing.T) {
|
||||||
prog := NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, 0x0})
|
prog := NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, 0x0})
|
||||||
err := CompileProgram(prog)
|
CompileProgram(prog)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if instr, ok := prog.instructions[0].(pushSeg); ok {
|
if instr, ok := prog.instructions[0].(pushSeg); ok {
|
||||||
if len(instr.data) != 2 {
|
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)})
|
prog = NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(JUMP)})
|
||||||
err = CompileProgram(prog)
|
CompileProgram(prog)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if _, ok := prog.instructions[1].(jumpSeg); ok {
|
if _, ok := prog.instructions[1].(jumpSeg); ok {
|
||||||
} else {
|
} else {
|
||||||
t.Errorf("expected instr[1] to be jumpSeg, got %T", prog.instructions[1])
|
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)})
|
prog = NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(JUMP)})
|
||||||
err = CompileProgram(prog)
|
CompileProgram(prog)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if instr, ok := prog.instructions[0].(pushSeg); ok {
|
if instr, ok := prog.instructions[0].(pushSeg); ok {
|
||||||
if len(instr.data) != 2 {
|
if len(instr.data) != 2 {
|
||||||
t.Error("expected 2 element width pushSegment, got", len(instr.data))
|
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) {
|
func TestCompiling(t *testing.T) {
|
||||||
prog := NewProgram([]byte{0x60, 0x10})
|
prog := NewProgram([]byte{0x60, 0x10})
|
||||||
err := CompileProgram(prog)
|
CompileProgram(prog)
|
||||||
if err != nil {
|
|
||||||
t.Error("didn't expect compile error")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(prog.instructions) != 1 {
|
if len(prog.instructions) != 1 {
|
||||||
t.Error("expected 1 compiled instruction, got", len(prog.instructions))
|
t.Error("expected 1 compiled instruction, got", len(prog.instructions))
|
||||||
|
|
@ -85,8 +75,8 @@ func TestCompiling(t *testing.T) {
|
||||||
func TestResetInput(t *testing.T) {
|
func TestResetInput(t *testing.T) {
|
||||||
var sender account
|
var sender account
|
||||||
|
|
||||||
env := NewEnv(&Config{EnableJit: true, ForceJit: true})
|
env := NewEnv(true, true)
|
||||||
contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0))
|
contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000))
|
||||||
contract.CodeAddr = &common.Address{}
|
contract.CodeAddr = &common.Address{}
|
||||||
|
|
||||||
program := NewProgram([]byte{})
|
program := NewProgram([]byte{})
|
||||||
|
|
@ -134,7 +124,7 @@ func (account) SetBalance(*big.Int) {}
|
||||||
func (account) SetNonce(uint64) {}
|
func (account) SetNonce(uint64) {}
|
||||||
func (account) Balance() *big.Int { return nil }
|
func (account) Balance() *big.Int { return nil }
|
||||||
func (account) Address() common.Address { return common.Address{} }
|
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) SetCode([]byte) {}
|
||||||
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
|
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
|
||||||
|
|
||||||
|
|
@ -149,7 +139,7 @@ func runVmBench(test vmBench, b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
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.Code = test.code
|
||||||
context.CodeAddr = &common.Address{}
|
context.CodeAddr = &common.Address{}
|
||||||
_, err := env.Vm().Run(context, test.input)
|
_, 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) RuleSet() RuleSet { return ruleSet{new(big.Int)} }
|
||||||
func (self *Env) Vm() Vm { return self.evm }
|
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) Origin() common.Address { return common.Address{} }
|
||||||
func (self *Env) BlockNumber() *big.Int { return big.NewInt(0) }
|
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
|
return true
|
||||||
}
|
}
|
||||||
func (self *Env) Transfer(from, to Account, amount *big.Int) {}
|
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
|
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
|
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
|
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
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,10 @@ type dummyContractRef struct {
|
||||||
calledForEach bool
|
calledForEach bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dummyContractRef) ReturnGas(*big.Int, *big.Int) {}
|
func (dummyContractRef) ReturnGas(uint64) {}
|
||||||
func (dummyContractRef) Address() common.Address { return common.Address{} }
|
func (dummyContractRef) Address() common.Address { return common.Address{} }
|
||||||
func (dummyContractRef) Value() *big.Int { return new(big.Int) }
|
func (dummyContractRef) Value() *big.Int { return new(big.Int) }
|
||||||
func (dummyContractRef) SetCode([]byte) {}
|
func (dummyContractRef) SetCode([]byte) {}
|
||||||
func (d *dummyContractRef) ForEachStorage(callback func(key, value common.Hash) bool) {
|
func (d *dummyContractRef) ForEachStorage(callback func(key, value common.Hash) bool) {
|
||||||
d.calledForEach = true
|
d.calledForEach = true
|
||||||
}
|
}
|
||||||
|
|
@ -61,7 +61,7 @@ func TestStoreCapture(t *testing.T) {
|
||||||
logger = NewStructLogger(nil)
|
logger = NewStructLogger(nil)
|
||||||
mem = NewMemory()
|
mem = NewMemory()
|
||||||
stack = newstack()
|
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(1))
|
||||||
stack.push(big.NewInt(0))
|
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")
|
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 (
|
var (
|
||||||
ref = &dummyContractRef{}
|
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)
|
env = newDummyEnv(ref)
|
||||||
logger = NewStructLogger(nil)
|
logger = NewStructLogger(nil)
|
||||||
mem = NewMemory()
|
mem = NewMemory()
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,11 @@ import "fmt"
|
||||||
// Memory implements a simple memory model for the ethereum virtual machine.
|
// Memory implements a simple memory model for the ethereum virtual machine.
|
||||||
type Memory struct {
|
type Memory struct {
|
||||||
store []byte
|
store []byte
|
||||||
|
cost uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMemory() *Memory {
|
func NewMemory() *Memory {
|
||||||
return &Memory{nil}
|
return &Memory{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set sets offset + size to value
|
// Set sets offset + size to value
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ type Env struct {
|
||||||
time *big.Int
|
time *big.Int
|
||||||
difficulty *big.Int
|
difficulty *big.Int
|
||||||
gasLimit *big.Int
|
gasLimit *big.Int
|
||||||
|
gasPrice *big.Int
|
||||||
|
|
||||||
getHashFn func(uint64) common.Hash
|
getHashFn func(uint64) common.Hash
|
||||||
|
|
||||||
|
|
@ -55,6 +56,7 @@ func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
|
||||||
time: cfg.Time,
|
time: cfg.Time,
|
||||||
difficulty: cfg.Difficulty,
|
difficulty: cfg.Difficulty,
|
||||||
gasLimit: cfg.GasLimit,
|
gasLimit: cfg.GasLimit,
|
||||||
|
gasPrice: cfg.GasPrice,
|
||||||
}
|
}
|
||||||
env.evm = vm.New(env, vm.Config{
|
env.evm = vm.New(env, vm.Config{
|
||||||
Debug: cfg.Debug,
|
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) 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) Vm() vm.Vm { return self.evm }
|
||||||
func (self *Env) Origin() common.Address { return self.origin }
|
func (self *Env) Origin() common.Address { return self.origin }
|
||||||
func (self *Env) BlockNumber() *big.Int { return self.number }
|
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)
|
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) {
|
||||||
return core.Call(self, caller, addr, data, gas, price, value)
|
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) {
|
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, price, value)
|
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) {
|
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, price)
|
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) {
|
func (self *Env) Create(caller vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
|
||||||
return core.Create(self, caller, data, gas, price, value)
|
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(),
|
receiver.Address(),
|
||||||
input,
|
input,
|
||||||
cfg.GasLimit,
|
cfg.GasLimit,
|
||||||
cfg.GasPrice,
|
|
||||||
cfg.Value,
|
cfg.Value,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -136,7 +135,6 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, error) {
|
||||||
address,
|
address,
|
||||||
input,
|
input,
|
||||||
cfg.GasLimit,
|
cfg.GasLimit,
|
||||||
cfg.GasPrice,
|
|
||||||
cfg.Value,
|
cfg.Value,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import "math/big"
|
||||||
type jumpSeg struct {
|
type jumpSeg struct {
|
||||||
pos uint64
|
pos uint64
|
||||||
err error
|
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) {
|
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 {
|
type pushSeg struct {
|
||||||
data []*big.Int
|
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) {
|
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.
|
// User defer pattern to check for an error and, based on the error being nil or not, use all gas and return.
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil && evm.cfg.Debug {
|
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])
|
glog.Infof("running byte VM %x\n", codehash[:4])
|
||||||
tstart := time.Now()
|
tstart := time.Now()
|
||||||
defer func() {
|
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++ {
|
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
|
// Get the memory location of pc
|
||||||
op = contract.GetOp(pc)
|
op = contract.GetOp(pc)
|
||||||
// calculate the new memory size and gas price for the current executing opcode
|
// 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
|
// Use the calculated gas. When insufficient gas is present, use all gas and return an
|
||||||
// Out Of Gas error
|
// Out Of Gas error
|
||||||
if !contract.UseGas(cost) {
|
if !contract.UseGas(cost.Uint64()) {
|
||||||
return nil, OutOfGasError
|
return nil, OutOfGasError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -183,7 +173,8 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
||||||
mem.Resize(newMemSize.Uint64())
|
mem.Resize(newMemSize.Uint64())
|
||||||
// Add a log message
|
// Add a log message
|
||||||
if evm.cfg.Debug {
|
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 {
|
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
|
// 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) {
|
func (evm *EVM) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Contract) (ret []byte, err error) {
|
||||||
gas := p.Gas(len(input))
|
gas := p.Gas(len(input))
|
||||||
if contract.UseGas(gas) {
|
if contract.UseGas(gas.Uint64()) {
|
||||||
ret = p.Call(input)
|
ret = p.Call(input)
|
||||||
|
|
||||||
return ret, nil
|
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) Time() *big.Int { return self.header.Time }
|
||||||
func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty }
|
func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty }
|
||||||
func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit }
|
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) Value() *big.Int { return self.msg.Value() }
|
||||||
func (self *VMEnv) Db() vm.Database { return self.state }
|
func (self *VMEnv) Db() vm.Database { return self.state }
|
||||||
func (self *VMEnv) Depth() int { return self.depth }
|
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)
|
Transfer(from, to, amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *VMEnv) Call(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
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, price, value)
|
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) {
|
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, price, value)
|
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) {
|
func (self *VMEnv) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
|
||||||
return DelegateCall(self, me, addr, data, gas, price)
|
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) {
|
func (self *VMEnv) Create(me vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
|
||||||
return Create(self, me, data, gas, price, value)
|
return Create(self, me, data, gas, value)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -115,7 +116,7 @@ func runStateTests(ruleSet RuleSet, tests map[string]VmTest, skipTests []string)
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, test := range tests {
|
for name, test := range tests {
|
||||||
if skipTest[name] /*|| name != "callcodecallcode_11" */ {
|
if skipTest[name] /*|| name != "TestInputLimitsLight"*/ {
|
||||||
glog.Infoln("Skipping state test", name)
|
glog.Infoln("Skipping state test", name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -162,6 +163,10 @@ func runStateTest(ruleSet RuleSet, test VmTest) error {
|
||||||
// err error
|
// err error
|
||||||
logs vm.Logs
|
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)
|
ret, logs, _, _ = RunState(ruleSet, statedb, env, test.Transaction)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -150,12 +150,12 @@ func (r RuleSet) IsHomestead(n *big.Int) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Env struct {
|
type Env struct {
|
||||||
ruleSet RuleSet
|
ruleSet RuleSet
|
||||||
depth int
|
depth int
|
||||||
state *state.StateDB
|
state *state.StateDB
|
||||||
skipTransfer bool
|
skipTransfer bool
|
||||||
initial bool
|
initial bool
|
||||||
Gas *big.Int
|
Gas, gasPrice *big.Int
|
||||||
|
|
||||||
origin common.Address
|
origin common.Address
|
||||||
parent common.Hash
|
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.difficulty = common.Big(envValues["currentDifficulty"])
|
||||||
env.gasLimit = common.Big(envValues["currentGasLimit"])
|
env.gasLimit = common.Big(envValues["currentGasLimit"])
|
||||||
env.Gas = new(big.Int)
|
env.Gas = new(big.Int)
|
||||||
|
env.gasPrice = common.Big(exeValues["gasPrice"])
|
||||||
|
|
||||||
env.evm = vm.New(env, vm.Config{
|
env.evm = vm.New(env, vm.Config{
|
||||||
EnableJit: EnableJit,
|
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) Difficulty() *big.Int { return self.difficulty }
|
||||||
func (self *Env) Db() vm.Database { return self.state }
|
func (self *Env) Db() vm.Database { return self.state }
|
||||||
func (self *Env) GasLimit() *big.Int { return self.gasLimit }
|
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) VmType() vm.Type { return vm.StdVmTy }
|
||||||
func (self *Env) GetHash(n uint64) common.Hash {
|
func (self *Env) GetHash(n uint64) common.Hash {
|
||||||
return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String())))
|
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)
|
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 {
|
if self.vmTest && self.depth > 0 {
|
||||||
caller.ReturnGas(gas, price)
|
caller.ReturnGas(gas.Uint64())
|
||||||
|
|
||||||
return nil, nil
|
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
|
self.Gas = gas
|
||||||
|
|
||||||
return ret, err
|
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 {
|
if self.vmTest && self.depth > 0 {
|
||||||
caller.ReturnGas(gas, price)
|
caller.ReturnGas(gas.Uint64())
|
||||||
|
|
||||||
return nil, nil
|
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 {
|
if self.vmTest && self.depth > 0 {
|
||||||
caller.ReturnGas(gas, price)
|
caller.ReturnGas(gas.Uint64())
|
||||||
|
|
||||||
return nil, nil
|
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 {
|
if self.vmTest {
|
||||||
caller.ReturnGas(gas, price)
|
caller.ReturnGas(gas.Uint64())
|
||||||
|
|
||||||
nonce := self.state.GetNonce(caller.Address())
|
nonce := self.state.GetNonce(caller.Address())
|
||||||
obj := self.state.GetOrNewStateObject(crypto.CreateAddress(caller.Address(), nonce))
|
obj := self.state.GetOrNewStateObject(crypto.CreateAddress(caller.Address(), nonce))
|
||||||
|
|
||||||
return nil, obj.Address(), nil
|
return nil, obj.Address(), nil
|
||||||
} else {
|
} 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 {
|
for name, test := range tests {
|
||||||
if skipTest[name] {
|
if skipTest[name] /*|| name != "57a9a2fbfe21a848e8d83379562a8513417c73d127495cf4abc5a44a5fe08e92"*/ {
|
||||||
glog.Infoln("Skipping VM test", name)
|
glog.Infoln("Skipping VM test", name)
|
||||||
return nil
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := runVmTest(test); err != nil {
|
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"])
|
from = common.HexToAddress(exec["caller"])
|
||||||
data = common.FromHex(exec["data"])
|
data = common.FromHex(exec["data"])
|
||||||
gas = common.Big(exec["gas"])
|
gas = common.Big(exec["gas"])
|
||||||
price = common.Big(exec["gasPrice"])
|
|
||||||
value = common.Big(exec["value"])
|
value = common.Big(exec["value"])
|
||||||
)
|
)
|
||||||
// Reset the pre-compiled contracts for VM tests.
|
// 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.vmTest = true
|
||||||
vmenv.skipTransfer = true
|
vmenv.skipTransfer = true
|
||||||
vmenv.initial = 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
|
return ret, vmenv.state.Logs(), vmenv.Gas, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue