core/vm: 64 bit memory and gas calculations (#19210)
* core/vm: remove function call for stack validation from evm runloop * core/vm: separate gas calc into static + dynamic * core/vm: optimize push1 * core/vm: reuse pooled bigints for ADDRESS, ORIGIN and CALLER * core/vm: use generic error message for jump/jumpi, to avoid string interpolation * testdata: fix tests for new error message * core/vm: use 64-bit memory calculations * core/vm: fix error in memory calculation * core/vm: address review concerns * core/vm: avoid unnecessary use of big.Int:BitLen()
This commit is contained in:
parent
da5de012c3
commit
7504dbd6eb
@ -202,9 +202,6 @@ func IsHexAddress(s string) bool {
|
|||||||
// Bytes gets the string representation of the underlying address.
|
// Bytes gets the string representation of the underlying address.
|
||||||
func (a Address) Bytes() []byte { return a[:] }
|
func (a Address) Bytes() []byte { return a[:] }
|
||||||
|
|
||||||
// Big converts an address to a big integer.
|
|
||||||
func (a Address) Big() *big.Int { return new(big.Int).SetBytes(a[:]) }
|
|
||||||
|
|
||||||
// Hash converts an address to a hash by left-padding it with zeros.
|
// Hash converts an address to a hash by left-padding it with zeros.
|
||||||
func (a Address) Hash() Hash { return BytesToHash(a[:]) }
|
func (a Address) Hash() Hash { return BytesToHash(a[:]) }
|
||||||
|
|
||||||
|
@ -114,8 +114,8 @@ func TestAddressUnmarshalJSON(t *testing.T) {
|
|||||||
if test.ShouldErr {
|
if test.ShouldErr {
|
||||||
t.Errorf("test #%d: expected error, got none", i)
|
t.Errorf("test #%d: expected error, got none", i)
|
||||||
}
|
}
|
||||||
if v.Big().Cmp(test.Output) != 0 {
|
if got := new(big.Int).SetBytes(v.Bytes()); got.Cmp(test.Output) != 0 {
|
||||||
t.Errorf("test #%d: address mismatch: have %v, want %v", i, v.Big(), test.Output)
|
t.Errorf("test #%d: address mismatch: have %v, want %v", i, got, test.Output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,8 @@ func makelist(g *core.Genesis) allocList {
|
|||||||
if len(account.Storage) > 0 || len(account.Code) > 0 || account.Nonce != 0 {
|
if len(account.Storage) > 0 || len(account.Code) > 0 || account.Nonce != 0 {
|
||||||
panic(fmt.Sprintf("can't encode account %x", addr))
|
panic(fmt.Sprintf("can't encode account %x", addr))
|
||||||
}
|
}
|
||||||
a = append(a, allocItem{addr.Big(), account.Balance})
|
bigAddr := new(big.Int).SetBytes(addr.Bytes())
|
||||||
|
a = append(a, allocItem{bigAddr, account.Balance})
|
||||||
}
|
}
|
||||||
sort.Sort(a)
|
sort.Sort(a)
|
||||||
return a
|
return a
|
||||||
|
@ -23,13 +23,31 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
)
|
)
|
||||||
|
|
||||||
// calculates the memory size required for a step
|
// calcMemSize64 calculates the required memory size, and returns
|
||||||
func calcMemSize(off, l *big.Int) *big.Int {
|
// the size and whether the result overflowed uint64
|
||||||
if l.Sign() == 0 {
|
func calcMemSize64(off, l *big.Int) (uint64, bool) {
|
||||||
return common.Big0
|
if !l.IsUint64() {
|
||||||
|
return 0, true
|
||||||
}
|
}
|
||||||
|
return calcMemSize64WithUint(off, l.Uint64())
|
||||||
|
}
|
||||||
|
|
||||||
return new(big.Int).Add(off, l)
|
// calcMemSize64WithUint calculates the required memory size, and returns
|
||||||
|
// the size and whether the result overflowed uint64
|
||||||
|
// Identical to calcMemSize64, but length is a uint64
|
||||||
|
func calcMemSize64WithUint(off *big.Int, length64 uint64) (uint64, bool) {
|
||||||
|
// if length is zero, memsize is always zero, regardless of offset
|
||||||
|
if length64 == 0 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
// Check that offset doesn't overflow
|
||||||
|
if !off.IsUint64() {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
offset64 := off.Uint64()
|
||||||
|
val := offset64 + length64
|
||||||
|
// if value < either of it's parts, then it overflowed
|
||||||
|
return val, val < offset64
|
||||||
}
|
}
|
||||||
|
|
||||||
// getData returns a slice from the data based on the start and size and pads
|
// getData returns a slice from the data based on the start and size and pads
|
||||||
@ -59,7 +77,7 @@ func getDataBig(data []byte, start *big.Int, size *big.Int) []byte {
|
|||||||
// bigUint64 returns the integer casted to a uint64 and returns whether it
|
// bigUint64 returns the integer casted to a uint64 and returns whether it
|
||||||
// overflowed in the process.
|
// overflowed in the process.
|
||||||
func bigUint64(v *big.Int) (uint64, bool) {
|
func bigUint64(v *big.Int) (uint64, bool) {
|
||||||
return v.Uint64(), v.BitLen() > 64
|
return v.Uint64(), !v.IsUint64()
|
||||||
}
|
}
|
||||||
|
|
||||||
// toWordSize returns the ceiled word size required for memory expansion.
|
// toWordSize returns the ceiled word size required for memory expansion.
|
||||||
|
@ -43,11 +43,11 @@ func callGas(gasTable params.GasTable, availableGas, base uint64, callCost *big.
|
|||||||
// If the bit length exceeds 64 bit we know that the newly calculated "gas" for EIP150
|
// If the bit length exceeds 64 bit we know that the newly calculated "gas" for EIP150
|
||||||
// is smaller than the requested amount. Therefor we return the new gas instead
|
// is smaller than the requested amount. Therefor we return the new gas instead
|
||||||
// of returning an error.
|
// of returning an error.
|
||||||
if callCost.BitLen() > 64 || gas < callCost.Uint64() {
|
if !callCost.IsUint64() || gas < callCost.Uint64() {
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if callCost.BitLen() > 64 {
|
if !callCost.IsUint64() {
|
||||||
return 0, errGasUintOverflow
|
return 0, errGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,12 +57,6 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
|
|||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func constGasFunc(gas uint64) gasFunc {
|
|
||||||
return func(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
||||||
return gas, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func gasCallDataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasCallDataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
gas, err := memoryGasCost(mem, memorySize)
|
gas, err := memoryGasCost(mem, memorySize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -521,15 +515,3 @@ func gasStaticCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stac
|
|||||||
}
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasPush(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
||||||
return GasFastestStep, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func gasSwap(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
||||||
return GasFastestStep, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func gasDup(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
||||||
return GasFastestStep, nil
|
|
||||||
}
|
|
||||||
|
@ -18,7 +18,6 @@ package vm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
@ -35,6 +34,7 @@ var (
|
|||||||
errReturnDataOutOfBounds = errors.New("evm: return data out of bounds")
|
errReturnDataOutOfBounds = errors.New("evm: return data out of bounds")
|
||||||
errExecutionReverted = errors.New("evm: execution reverted")
|
errExecutionReverted = errors.New("evm: execution reverted")
|
||||||
errMaxCodeSizeExceeded = errors.New("evm: max code size exceeded")
|
errMaxCodeSizeExceeded = errors.New("evm: max code size exceeded")
|
||||||
|
errInvalidJump = errors.New("evm: invalid jump destination")
|
||||||
)
|
)
|
||||||
|
|
||||||
func opAdd(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
func opAdd(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
@ -405,7 +405,7 @@ func opSha3(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory
|
|||||||
}
|
}
|
||||||
|
|
||||||
func opAddress(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
func opAddress(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
stack.push(contract.Address().Big())
|
stack.push(interpreter.intPool.get().SetBytes(contract.Address().Bytes()))
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,12 +416,12 @@ func opBalance(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func opOrigin(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
func opOrigin(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
stack.push(interpreter.evm.Origin.Big())
|
stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Origin.Bytes()))
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func opCaller(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
func opCaller(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
stack.push(contract.Caller().Big())
|
stack.push(interpreter.intPool.get().SetBytes(contract.Caller().Bytes()))
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -467,7 +467,7 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contrac
|
|||||||
)
|
)
|
||||||
defer interpreter.intPool.put(memOffset, dataOffset, length, end)
|
defer interpreter.intPool.put(memOffset, dataOffset, length, end)
|
||||||
|
|
||||||
if end.BitLen() > 64 || uint64(len(interpreter.returnData)) < end.Uint64() {
|
if !end.IsUint64() || uint64(len(interpreter.returnData)) < end.Uint64() {
|
||||||
return nil, errReturnDataOutOfBounds
|
return nil, errReturnDataOutOfBounds
|
||||||
}
|
}
|
||||||
memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[dataOffset.Uint64():end.Uint64()])
|
memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[dataOffset.Uint64():end.Uint64()])
|
||||||
@ -572,7 +572,7 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, contract *Contract, me
|
|||||||
}
|
}
|
||||||
|
|
||||||
func opCoinbase(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
func opCoinbase(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
stack.push(interpreter.evm.Coinbase.Big())
|
stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Coinbase.Bytes()))
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -645,8 +645,7 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memor
|
|||||||
func opJump(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
func opJump(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
pos := stack.pop()
|
pos := stack.pop()
|
||||||
if !contract.validJumpdest(pos) {
|
if !contract.validJumpdest(pos) {
|
||||||
nop := contract.GetOp(pos.Uint64())
|
return nil, errInvalidJump
|
||||||
return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos)
|
|
||||||
}
|
}
|
||||||
*pc = pos.Uint64()
|
*pc = pos.Uint64()
|
||||||
|
|
||||||
@ -658,8 +657,7 @@ func opJumpi(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory
|
|||||||
pos, cond := stack.pop(), stack.pop()
|
pos, cond := stack.pop(), stack.pop()
|
||||||
if cond.Sign() != 0 {
|
if cond.Sign() != 0 {
|
||||||
if !contract.validJumpdest(pos) {
|
if !contract.validJumpdest(pos) {
|
||||||
nop := contract.GetOp(pos.Uint64())
|
return nil, errInvalidJump
|
||||||
return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos)
|
|
||||||
}
|
}
|
||||||
*pc = pos.Uint64()
|
*pc = pos.Uint64()
|
||||||
} else {
|
} else {
|
||||||
@ -711,7 +709,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memor
|
|||||||
} else if suberr != nil && suberr != ErrCodeStoreOutOfGas {
|
} else if suberr != nil && suberr != ErrCodeStoreOutOfGas {
|
||||||
stack.push(interpreter.intPool.getZero())
|
stack.push(interpreter.intPool.getZero())
|
||||||
} else {
|
} else {
|
||||||
stack.push(addr.Big())
|
stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
|
||||||
}
|
}
|
||||||
contract.Gas += returnGas
|
contract.Gas += returnGas
|
||||||
interpreter.intPool.put(value, offset, size)
|
interpreter.intPool.put(value, offset, size)
|
||||||
@ -739,7 +737,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memo
|
|||||||
if suberr != nil {
|
if suberr != nil {
|
||||||
stack.push(interpreter.intPool.getZero())
|
stack.push(interpreter.intPool.getZero())
|
||||||
} else {
|
} else {
|
||||||
stack.push(addr.Big())
|
stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
|
||||||
}
|
}
|
||||||
contract.Gas += returnGas
|
contract.Gas += returnGas
|
||||||
interpreter.intPool.put(endowment, offset, size, salt)
|
interpreter.intPool.put(endowment, offset, size, salt)
|
||||||
@ -912,6 +910,21 @@ func makeLog(size int) executionFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// opPush1 is a specialized version of pushN
|
||||||
|
func opPush1(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
|
var (
|
||||||
|
codeLen = uint64(len(contract.Code))
|
||||||
|
integer = interpreter.intPool.get()
|
||||||
|
)
|
||||||
|
*pc += 1
|
||||||
|
if *pc < codeLen {
|
||||||
|
stack.push(integer.SetUint64(uint64(contract.Code[*pc])))
|
||||||
|
} else {
|
||||||
|
stack.push(integer.SetUint64(0))
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// make push instruction function
|
// make push instruction function
|
||||||
func makePush(size uint64, pushByteSize int) executionFunc {
|
func makePush(size uint64, pushByteSize int) executionFunc {
|
||||||
return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
|
@ -118,22 +118,6 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (in *EVMInterpreter) enforceRestrictions(op OpCode, operation operation, stack *Stack) error {
|
|
||||||
if in.evm.chainRules.IsByzantium {
|
|
||||||
if in.readOnly {
|
|
||||||
// If the interpreter is operating in readonly mode, make sure no
|
|
||||||
// state-modifying operation is performed. The 3rd stack item
|
|
||||||
// for a call operation is the value. Transferring value from one
|
|
||||||
// account to the others means the state is modified and should also
|
|
||||||
// return with an error.
|
|
||||||
if operation.writes || (op == CALL && stack.Back(2).BitLen() > 0) {
|
|
||||||
return errWriteProtection
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run loops and evaluates the contract's code with the given input data and returns
|
// Run loops and evaluates the contract's code with the given input data and returns
|
||||||
// the return byte-slice and an error if one occurred.
|
// the return byte-slice and an error if one occurred.
|
||||||
//
|
//
|
||||||
@ -217,19 +201,35 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||||||
if !operation.valid {
|
if !operation.valid {
|
||||||
return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
|
return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
|
||||||
}
|
}
|
||||||
if err = operation.validateStack(stack); err != nil {
|
// Validate stack
|
||||||
return nil, err
|
if sLen := stack.len(); sLen < operation.minStack {
|
||||||
|
return nil, fmt.Errorf("stack underflow (%d <=> %d)", sLen, operation.minStack)
|
||||||
|
} else if sLen > operation.maxStack {
|
||||||
|
return nil, fmt.Errorf("stack limit reached %d (%d)", sLen, operation.maxStack)
|
||||||
}
|
}
|
||||||
// If the operation is valid, enforce and write restrictions
|
// If the operation is valid, enforce and write restrictions
|
||||||
if err = in.enforceRestrictions(op, operation, stack); err != nil {
|
if in.readOnly && in.evm.chainRules.IsByzantium {
|
||||||
return nil, err
|
// If the interpreter is operating in readonly mode, make sure no
|
||||||
|
// state-modifying operation is performed. The 3rd stack item
|
||||||
|
// for a call operation is the value. Transferring value from one
|
||||||
|
// account to the others means the state is modified and should also
|
||||||
|
// return with an error.
|
||||||
|
if operation.writes || (op == CALL && stack.Back(2).Sign() != 0) {
|
||||||
|
return nil, errWriteProtection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Static portion of gas
|
||||||
|
if !contract.UseGas(operation.constantGas) {
|
||||||
|
return nil, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
|
||||||
var memorySize uint64
|
var memorySize uint64
|
||||||
// calculate the new memory size and expand the memory to fit
|
// calculate the new memory size and expand the memory to fit
|
||||||
// the operation
|
// the operation
|
||||||
|
// Memory check needs to be done prior to evaluating the dynamic gas portion,
|
||||||
|
// to detect calculation overflows
|
||||||
if operation.memorySize != nil {
|
if operation.memorySize != nil {
|
||||||
memSize, overflow := bigUint64(operation.memorySize(stack))
|
memSize, overflow := operation.memorySize(stack)
|
||||||
if overflow {
|
if overflow {
|
||||||
return nil, errGasUintOverflow
|
return nil, errGasUintOverflow
|
||||||
}
|
}
|
||||||
@ -239,11 +239,14 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||||||
return nil, errGasUintOverflow
|
return nil, errGasUintOverflow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Dynamic portion of gas
|
||||||
// consume the gas and return an error if not enough gas is available.
|
// consume the gas and return an error if not enough gas is available.
|
||||||
// cost is explicitly set so that the capture state defer method can get the proper cost
|
// cost is explicitly set so that the capture state defer method can get the proper cost
|
||||||
cost, err = operation.gasCost(in.gasTable, in.evm, contract, stack, mem, memorySize)
|
if operation.dynamicGas != nil {
|
||||||
if err != nil || !contract.UseGas(cost) {
|
cost, err = operation.dynamicGas(in.gasTable, in.evm, contract, stack, mem, memorySize)
|
||||||
return nil, ErrOutOfGas
|
if err != nil || !contract.UseGas(cost) {
|
||||||
|
return nil, ErrOutOfGas
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if memorySize > 0 {
|
if memorySize > 0 {
|
||||||
mem.Resize(memorySize)
|
mem.Resize(memorySize)
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -16,82 +16,98 @@
|
|||||||
|
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
func memorySha3(stack *Stack) (uint64, bool) {
|
||||||
"math/big"
|
return calcMemSize64(stack.Back(0), stack.Back(1))
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
|
||||||
)
|
|
||||||
|
|
||||||
func memorySha3(stack *Stack) *big.Int {
|
|
||||||
return calcMemSize(stack.Back(0), stack.Back(1))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryCallDataCopy(stack *Stack) *big.Int {
|
func memoryCallDataCopy(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(0), stack.Back(2))
|
return calcMemSize64(stack.Back(0), stack.Back(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryReturnDataCopy(stack *Stack) *big.Int {
|
func memoryReturnDataCopy(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(0), stack.Back(2))
|
return calcMemSize64(stack.Back(0), stack.Back(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryCodeCopy(stack *Stack) *big.Int {
|
func memoryCodeCopy(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(0), stack.Back(2))
|
return calcMemSize64(stack.Back(0), stack.Back(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryExtCodeCopy(stack *Stack) *big.Int {
|
func memoryExtCodeCopy(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(1), stack.Back(3))
|
return calcMemSize64(stack.Back(1), stack.Back(3))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryMLoad(stack *Stack) *big.Int {
|
func memoryMLoad(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(0), big.NewInt(32))
|
return calcMemSize64WithUint(stack.Back(0), 32)
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryMStore8(stack *Stack) *big.Int {
|
func memoryMStore8(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(0), big.NewInt(1))
|
return calcMemSize64WithUint(stack.Back(0), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryMStore(stack *Stack) *big.Int {
|
func memoryMStore(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(0), big.NewInt(32))
|
return calcMemSize64WithUint(stack.Back(0), 32)
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryCreate(stack *Stack) *big.Int {
|
func memoryCreate(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(1), stack.Back(2))
|
return calcMemSize64(stack.Back(1), stack.Back(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryCreate2(stack *Stack) *big.Int {
|
func memoryCreate2(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(1), stack.Back(2))
|
return calcMemSize64(stack.Back(1), stack.Back(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryCall(stack *Stack) *big.Int {
|
func memoryCall(stack *Stack) (uint64, bool) {
|
||||||
x := calcMemSize(stack.Back(5), stack.Back(6))
|
x, overflow := calcMemSize64(stack.Back(5), stack.Back(6))
|
||||||
y := calcMemSize(stack.Back(3), stack.Back(4))
|
if overflow {
|
||||||
|
return 0, true
|
||||||
return math.BigMax(x, y)
|
}
|
||||||
|
y, overflow := calcMemSize64(stack.Back(3), stack.Back(4))
|
||||||
|
if overflow {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
if x > y {
|
||||||
|
return x, false
|
||||||
|
}
|
||||||
|
return y, false
|
||||||
|
}
|
||||||
|
func memoryDelegateCall(stack *Stack) (uint64, bool) {
|
||||||
|
x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
|
||||||
|
if overflow {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
y, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
|
||||||
|
if overflow {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
if x > y {
|
||||||
|
return x, false
|
||||||
|
}
|
||||||
|
return y, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryDelegateCall(stack *Stack) *big.Int {
|
func memoryStaticCall(stack *Stack) (uint64, bool) {
|
||||||
x := calcMemSize(stack.Back(4), stack.Back(5))
|
x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
|
||||||
y := calcMemSize(stack.Back(2), stack.Back(3))
|
if overflow {
|
||||||
|
return 0, true
|
||||||
return math.BigMax(x, y)
|
}
|
||||||
|
y, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
|
||||||
|
if overflow {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
if x > y {
|
||||||
|
return x, false
|
||||||
|
}
|
||||||
|
return y, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryStaticCall(stack *Stack) *big.Int {
|
func memoryReturn(stack *Stack) (uint64, bool) {
|
||||||
x := calcMemSize(stack.Back(4), stack.Back(5))
|
return calcMemSize64(stack.Back(0), stack.Back(1))
|
||||||
y := calcMemSize(stack.Back(2), stack.Back(3))
|
|
||||||
|
|
||||||
return math.BigMax(x, y)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryReturn(stack *Stack) *big.Int {
|
func memoryRevert(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(0), stack.Back(1))
|
return calcMemSize64(stack.Back(0), stack.Back(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryRevert(stack *Stack) *big.Int {
|
func memoryLog(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(0), stack.Back(1))
|
return calcMemSize64(stack.Back(0), stack.Back(1))
|
||||||
}
|
|
||||||
|
|
||||||
func memoryLog(stack *Stack) *big.Int {
|
|
||||||
mSize, mStart := stack.Back(1), stack.Back(0)
|
|
||||||
return calcMemSize(mStart, mSize)
|
|
||||||
}
|
}
|
||||||
|
@ -17,28 +17,26 @@
|
|||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeStackFunc(pop, push int) stackValidationFunc {
|
func minSwapStack(n int) int {
|
||||||
return func(stack *Stack) error {
|
return minStack(n, n)
|
||||||
if err := stack.require(pop); err != nil {
|
}
|
||||||
return err
|
func maxSwapStack(n int) int {
|
||||||
}
|
return maxStack(n, n)
|
||||||
|
|
||||||
if stack.len()+push-pop > int(params.StackLimit) {
|
|
||||||
return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeDupStackFunc(n int) stackValidationFunc {
|
func minDupStack(n int) int {
|
||||||
return makeStackFunc(n, n+1)
|
return minStack(n, n+1)
|
||||||
|
}
|
||||||
|
func maxDupStack(n int) int {
|
||||||
|
return maxStack(n, n+1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeSwapStackFunc(n int) stackValidationFunc {
|
func maxStack(pop, push int) int {
|
||||||
return makeStackFunc(n, n)
|
return int(params.StackLimit) + pop - push
|
||||||
|
}
|
||||||
|
func minStack(pops, push int) int {
|
||||||
|
return pops
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@
|
|||||||
"value": "0x0"
|
"value": "0x0"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"error": "invalid jump destination (PUSH1) 0",
|
"error": "evm: invalid jump destination",
|
||||||
"from": "0xe4a13bc304682a903e9472f469c33801dd18d9e8",
|
"from": "0xe4a13bc304682a903e9472f469c33801dd18d9e8",
|
||||||
"gas": "0x435c8",
|
"gas": "0x435c8",
|
||||||
"gasUsed": "0x435c8",
|
"gasUsed": "0x435c8",
|
||||||
|
2
eth/tracers/testdata/call_tracer_throw.json
vendored
2
eth/tracers/testdata/call_tracer_throw.json
vendored
@ -50,7 +50,7 @@
|
|||||||
},
|
},
|
||||||
"input": "0xf88b8206668504a817c8008303d09094c212e03b9e060e36facad5fd8f4435412ca22e6b80a451a34eb8000000000000000000000000000000000000000000000027fad02094277c000029a0692a3b4e7b2842f8dd7832e712c21e09f451f416c8976d5b8d02e8c0c2b4bea9a07645e90fc421b63dd755767fd93d3c03b4ec0c4d8fafa059558d08cf11d59750",
|
"input": "0xf88b8206668504a817c8008303d09094c212e03b9e060e36facad5fd8f4435412ca22e6b80a451a34eb8000000000000000000000000000000000000000000000027fad02094277c000029a0692a3b4e7b2842f8dd7832e712c21e09f451f416c8976d5b8d02e8c0c2b4bea9a07645e90fc421b63dd755767fd93d3c03b4ec0c4d8fafa059558d08cf11d59750",
|
||||||
"result": {
|
"result": {
|
||||||
"error": "invalid jump destination (PUSH1) 2",
|
"error": "evm: invalid jump destination",
|
||||||
"from": "0x70c9217d814985faef62b124420f8dfbddd96433",
|
"from": "0x70c9217d814985faef62b124420f8dfbddd96433",
|
||||||
"gas": "0x37b38",
|
"gas": "0x37b38",
|
||||||
"gasUsed": "0x37b38",
|
"gasUsed": "0x37b38",
|
||||||
|
Loading…
Reference in New Issue
Block a user