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.
|
||||
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.
|
||||
func (a Address) Hash() Hash { return BytesToHash(a[:]) }
|
||||
|
||||
|
@ -114,8 +114,8 @@ func TestAddressUnmarshalJSON(t *testing.T) {
|
||||
if test.ShouldErr {
|
||||
t.Errorf("test #%d: expected error, got none", i)
|
||||
}
|
||||
if v.Big().Cmp(test.Output) != 0 {
|
||||
t.Errorf("test #%d: address mismatch: have %v, want %v", i, v.Big(), test.Output)
|
||||
if got := new(big.Int).SetBytes(v.Bytes()); got.Cmp(test.Output) != 0 {
|
||||
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 {
|
||||
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)
|
||||
return a
|
||||
|
@ -23,13 +23,31 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
)
|
||||
|
||||
// calculates the memory size required for a step
|
||||
func calcMemSize(off, l *big.Int) *big.Int {
|
||||
if l.Sign() == 0 {
|
||||
return common.Big0
|
||||
// calcMemSize64 calculates the required memory size, and returns
|
||||
// the size and whether the result overflowed uint64
|
||||
func calcMemSize64(off, l *big.Int) (uint64, bool) {
|
||||
if !l.IsUint64() {
|
||||
return 0, true
|
||||
}
|
||||
return calcMemSize64WithUint(off, l.Uint64())
|
||||
}
|
||||
|
||||
return new(big.Int).Add(off, l)
|
||||
// calcMemSize64WithUint calculates the required memory size, and returns
|
||||
// the size and whether the result overflowed uint64
|
||||
// Identical to calcMemSize64, but length is a uint64
|
||||
func calcMemSize64WithUint(off *big.Int, length64 uint64) (uint64, bool) {
|
||||
// if length is zero, memsize is always zero, regardless of offset
|
||||
if length64 == 0 {
|
||||
return 0, false
|
||||
}
|
||||
// Check that offset doesn't overflow
|
||||
if !off.IsUint64() {
|
||||
return 0, true
|
||||
}
|
||||
offset64 := off.Uint64()
|
||||
val := offset64 + length64
|
||||
// if value < either of it's parts, then it overflowed
|
||||
return val, val < offset64
|
||||
}
|
||||
|
||||
// getData returns a slice from the data based on the start and size and pads
|
||||
@ -59,7 +77,7 @@ func getDataBig(data []byte, start *big.Int, size *big.Int) []byte {
|
||||
// bigUint64 returns the integer casted to a uint64 and returns whether it
|
||||
// overflowed in the process.
|
||||
func bigUint64(v *big.Int) (uint64, bool) {
|
||||
return v.Uint64(), v.BitLen() > 64
|
||||
return v.Uint64(), !v.IsUint64()
|
||||
}
|
||||
|
||||
// toWordSize returns the ceiled word size required for memory expansion.
|
||||
|
@ -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
|
||||
// is smaller than the requested amount. Therefor we return the new gas instead
|
||||
// of returning an error.
|
||||
if callCost.BitLen() > 64 || gas < callCost.Uint64() {
|
||||
if !callCost.IsUint64() || gas < callCost.Uint64() {
|
||||
return gas, nil
|
||||
}
|
||||
}
|
||||
if callCost.BitLen() > 64 {
|
||||
if !callCost.IsUint64() {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
|
||||
|
@ -57,12 +57,6 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func constGasFunc(gas uint64) gasFunc {
|
||||
return func(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return gas, nil
|
||||
}
|
||||
}
|
||||
|
||||
func gasCallDataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
@ -521,15 +515,3 @@ func gasStaticCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stac
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasPush(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return GasFastestStep, nil
|
||||
}
|
||||
|
||||
func gasSwap(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return GasFastestStep, nil
|
||||
}
|
||||
|
||||
func gasDup(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return GasFastestStep, nil
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ package vm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@ -35,6 +34,7 @@ var (
|
||||
errReturnDataOutOfBounds = errors.New("evm: return data out of bounds")
|
||||
errExecutionReverted = errors.New("evm: execution reverted")
|
||||
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) {
|
||||
@ -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) {
|
||||
stack.push(contract.Address().Big())
|
||||
stack.push(interpreter.intPool.get().SetBytes(contract.Address().Bytes()))
|
||||
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) {
|
||||
stack.push(interpreter.evm.Origin.Big())
|
||||
stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Origin.Bytes()))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@ -467,7 +467,7 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contrac
|
||||
)
|
||||
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
|
||||
}
|
||||
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) {
|
||||
stack.push(interpreter.evm.Coinbase.Big())
|
||||
stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Coinbase.Bytes()))
|
||||
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) {
|
||||
pos := stack.pop()
|
||||
if !contract.validJumpdest(pos) {
|
||||
nop := contract.GetOp(pos.Uint64())
|
||||
return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos)
|
||||
return nil, errInvalidJump
|
||||
}
|
||||
*pc = pos.Uint64()
|
||||
|
||||
@ -658,8 +657,7 @@ func opJumpi(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory
|
||||
pos, cond := stack.pop(), stack.pop()
|
||||
if cond.Sign() != 0 {
|
||||
if !contract.validJumpdest(pos) {
|
||||
nop := contract.GetOp(pos.Uint64())
|
||||
return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos)
|
||||
return nil, errInvalidJump
|
||||
}
|
||||
*pc = pos.Uint64()
|
||||
} else {
|
||||
@ -711,7 +709,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memor
|
||||
} else if suberr != nil && suberr != ErrCodeStoreOutOfGas {
|
||||
stack.push(interpreter.intPool.getZero())
|
||||
} else {
|
||||
stack.push(addr.Big())
|
||||
stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
|
||||
}
|
||||
contract.Gas += returnGas
|
||||
interpreter.intPool.put(value, offset, size)
|
||||
@ -739,7 +737,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memo
|
||||
if suberr != nil {
|
||||
stack.push(interpreter.intPool.getZero())
|
||||
} else {
|
||||
stack.push(addr.Big())
|
||||
stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
|
||||
}
|
||||
contract.Gas += returnGas
|
||||
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
|
||||
func makePush(size uint64, pushByteSize int) executionFunc {
|
||||
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
|
||||
// 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 {
|
||||
return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
|
||||
}
|
||||
if err = operation.validateStack(stack); err != nil {
|
||||
return nil, err
|
||||
// Validate stack
|
||||
if sLen := stack.len(); sLen < operation.minStack {
|
||||
return nil, fmt.Errorf("stack underflow (%d <=> %d)", sLen, operation.minStack)
|
||||
} else if sLen > operation.maxStack {
|
||||
return nil, fmt.Errorf("stack limit reached %d (%d)", sLen, operation.maxStack)
|
||||
}
|
||||
// If the operation is valid, enforce and write restrictions
|
||||
if err = in.enforceRestrictions(op, operation, stack); err != nil {
|
||||
return nil, err
|
||||
if in.readOnly && in.evm.chainRules.IsByzantium {
|
||||
// If the interpreter is operating in readonly mode, make sure no
|
||||
// state-modifying operation is performed. The 3rd stack item
|
||||
// for a call operation is the value. Transferring value from one
|
||||
// account to the others means the state is modified and should also
|
||||
// return with an error.
|
||||
if operation.writes || (op == CALL && stack.Back(2).Sign() != 0) {
|
||||
return nil, errWriteProtection
|
||||
}
|
||||
}
|
||||
// Static portion of gas
|
||||
if !contract.UseGas(operation.constantGas) {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
|
||||
var memorySize uint64
|
||||
// calculate the new memory size and expand the memory to fit
|
||||
// the operation
|
||||
// Memory check needs to be done prior to evaluating the dynamic gas portion,
|
||||
// to detect calculation overflows
|
||||
if operation.memorySize != nil {
|
||||
memSize, overflow := bigUint64(operation.memorySize(stack))
|
||||
memSize, overflow := operation.memorySize(stack)
|
||||
if overflow {
|
||||
return nil, errGasUintOverflow
|
||||
}
|
||||
@ -239,11 +239,14 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||
return nil, errGasUintOverflow
|
||||
}
|
||||
}
|
||||
// Dynamic portion of gas
|
||||
// consume the gas and return an error if not enough gas is available.
|
||||
// cost is explicitly set so that the capture state defer method can get the proper cost
|
||||
cost, err = operation.gasCost(in.gasTable, in.evm, contract, stack, mem, memorySize)
|
||||
if err != nil || !contract.UseGas(cost) {
|
||||
return nil, ErrOutOfGas
|
||||
if operation.dynamicGas != nil {
|
||||
cost, err = operation.dynamicGas(in.gasTable, in.evm, contract, stack, mem, memorySize)
|
||||
if err != nil || !contract.UseGas(cost) {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
if memorySize > 0 {
|
||||
mem.Resize(memorySize)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -16,82 +16,98 @@
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
)
|
||||
|
||||
func memorySha3(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), stack.Back(1))
|
||||
func memorySha3(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(0), stack.Back(1))
|
||||
}
|
||||
|
||||
func memoryCallDataCopy(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), stack.Back(2))
|
||||
func memoryCallDataCopy(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(0), stack.Back(2))
|
||||
}
|
||||
|
||||
func memoryReturnDataCopy(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), stack.Back(2))
|
||||
func memoryReturnDataCopy(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(0), stack.Back(2))
|
||||
}
|
||||
|
||||
func memoryCodeCopy(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), stack.Back(2))
|
||||
func memoryCodeCopy(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(0), stack.Back(2))
|
||||
}
|
||||
|
||||
func memoryExtCodeCopy(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(1), stack.Back(3))
|
||||
func memoryExtCodeCopy(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(1), stack.Back(3))
|
||||
}
|
||||
|
||||
func memoryMLoad(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), big.NewInt(32))
|
||||
func memoryMLoad(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64WithUint(stack.Back(0), 32)
|
||||
}
|
||||
|
||||
func memoryMStore8(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), big.NewInt(1))
|
||||
func memoryMStore8(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64WithUint(stack.Back(0), 1)
|
||||
}
|
||||
|
||||
func memoryMStore(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), big.NewInt(32))
|
||||
func memoryMStore(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64WithUint(stack.Back(0), 32)
|
||||
}
|
||||
|
||||
func memoryCreate(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(1), stack.Back(2))
|
||||
func memoryCreate(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(1), stack.Back(2))
|
||||
}
|
||||
|
||||
func memoryCreate2(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(1), stack.Back(2))
|
||||
func memoryCreate2(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(1), stack.Back(2))
|
||||
}
|
||||
|
||||
func memoryCall(stack *Stack) *big.Int {
|
||||
x := calcMemSize(stack.Back(5), stack.Back(6))
|
||||
y := calcMemSize(stack.Back(3), stack.Back(4))
|
||||
|
||||
return math.BigMax(x, y)
|
||||
func memoryCall(stack *Stack) (uint64, bool) {
|
||||
x, overflow := calcMemSize64(stack.Back(5), stack.Back(6))
|
||||
if overflow {
|
||||
return 0, true
|
||||
}
|
||||
y, overflow := calcMemSize64(stack.Back(3), stack.Back(4))
|
||||
if overflow {
|
||||
return 0, true
|
||||
}
|
||||
if x > y {
|
||||
return x, false
|
||||
}
|
||||
return y, false
|
||||
}
|
||||
func memoryDelegateCall(stack *Stack) (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 {
|
||||
x := calcMemSize(stack.Back(4), stack.Back(5))
|
||||
y := calcMemSize(stack.Back(2), stack.Back(3))
|
||||
|
||||
return math.BigMax(x, y)
|
||||
func memoryStaticCall(stack *Stack) (uint64, bool) {
|
||||
x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
|
||||
if overflow {
|
||||
return 0, true
|
||||
}
|
||||
y, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
|
||||
if overflow {
|
||||
return 0, true
|
||||
}
|
||||
if x > y {
|
||||
return x, false
|
||||
}
|
||||
return y, false
|
||||
}
|
||||
|
||||
func memoryStaticCall(stack *Stack) *big.Int {
|
||||
x := calcMemSize(stack.Back(4), stack.Back(5))
|
||||
y := calcMemSize(stack.Back(2), stack.Back(3))
|
||||
|
||||
return math.BigMax(x, y)
|
||||
func memoryReturn(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(0), stack.Back(1))
|
||||
}
|
||||
|
||||
func memoryReturn(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), stack.Back(1))
|
||||
func memoryRevert(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(0), stack.Back(1))
|
||||
}
|
||||
|
||||
func memoryRevert(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), stack.Back(1))
|
||||
}
|
||||
|
||||
func memoryLog(stack *Stack) *big.Int {
|
||||
mSize, mStart := stack.Back(1), stack.Back(0)
|
||||
return calcMemSize(mStart, mSize)
|
||||
func memoryLog(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(0), stack.Back(1))
|
||||
}
|
||||
|
@ -17,28 +17,26 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func makeStackFunc(pop, push int) stackValidationFunc {
|
||||
return func(stack *Stack) error {
|
||||
if err := stack.require(pop); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if stack.len()+push-pop > int(params.StackLimit) {
|
||||
return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func minSwapStack(n int) int {
|
||||
return minStack(n, n)
|
||||
}
|
||||
func maxSwapStack(n int) int {
|
||||
return maxStack(n, n)
|
||||
}
|
||||
|
||||
func makeDupStackFunc(n int) stackValidationFunc {
|
||||
return makeStackFunc(n, n+1)
|
||||
func minDupStack(n int) int {
|
||||
return minStack(n, n+1)
|
||||
}
|
||||
func maxDupStack(n int) int {
|
||||
return maxStack(n, n+1)
|
||||
}
|
||||
|
||||
func makeSwapStackFunc(n int) stackValidationFunc {
|
||||
return makeStackFunc(n, n)
|
||||
func maxStack(pop, push int) int {
|
||||
return int(params.StackLimit) + pop - push
|
||||
}
|
||||
func minStack(pops, push int) int {
|
||||
return pops
|
||||
}
|
||||
|
@ -65,7 +65,7 @@
|
||||
"value": "0x0"
|
||||
}
|
||||
],
|
||||
"error": "invalid jump destination (PUSH1) 0",
|
||||
"error": "evm: invalid jump destination",
|
||||
"from": "0xe4a13bc304682a903e9472f469c33801dd18d9e8",
|
||||
"gas": "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",
|
||||
"result": {
|
||||
"error": "invalid jump destination (PUSH1) 2",
|
||||
"error": "evm: invalid jump destination",
|
||||
"from": "0x70c9217d814985faef62b124420f8dfbddd96433",
|
||||
"gas": "0x37b38",
|
||||
"gasUsed": "0x37b38",
|
||||
|
Loading…
Reference in New Issue
Block a user