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:
		
				
					committed by
					
						
						Péter Szilágyi
					
				
			
			
				
	
			
			
			
						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",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user