From f83701fbb40e0433255c2ffaf93bf05f89f9f62c Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 5 Nov 2024 14:24:06 +0100 Subject: [PATCH] core/vm: cleanup stack usage --- core/vm/eips.go | 29 ++-- core/vm/instructions.go | 285 ++++++++++++++++++----------------- core/vm/instructions_test.go | 58 +++---- core/vm/operations_acl.go | 10 +- core/vm/operations_verkle.go | 8 +- core/vm/stack.go | 39 ++++- core/vm/stack_test.go | 16 ++ 7 files changed, 246 insertions(+), 199 deletions(-) create mode 100644 core/vm/stack_test.go diff --git a/core/vm/eips.go b/core/vm/eips.go index 6159eade7e..b30ad51a79 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -90,7 +90,7 @@ func enable1884(jt *JumpTable) { func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) - scope.Stack.push(balance) + scope.Stack.push(*balance) return nil, nil } @@ -109,7 +109,7 @@ func enable1344(jt *JumpTable) { // opChainID implements CHAINID opcode func opChainID(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { chainId, _ := uint256.FromBig(interpreter.evm.chainConfig.ChainID) - scope.Stack.push(chainId) + scope.Stack.push(*chainId) return nil, nil } @@ -199,10 +199,10 @@ func enable1153(jt *JumpTable) { // opTload implements TLOAD opcode func opTload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - loc := scope.Stack.peek() + loc := scope.Stack.pop() hash := common.Hash(loc.Bytes32()) val := interpreter.evm.StateDB.GetTransientState(scope.Contract.Address(), hash) - loc.SetBytes(val.Bytes()) + scope.Stack.pushBytes(val.Bytes()) return nil, nil } @@ -220,7 +220,7 @@ func opTstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b // opBaseFee implements BASEFEE opcode func opBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { baseFee, _ := uint256.FromBig(interpreter.evm.Context.BaseFee) - scope.Stack.push(baseFee) + scope.Stack.push(*baseFee) return nil, nil } @@ -237,7 +237,7 @@ func enable3855(jt *JumpTable) { // opPush0 implements the PUSH0 opcode func opPush0(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int)) + scope.Stack.pushU64(0) return nil, nil } @@ -276,12 +276,12 @@ func opMcopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by // opBlobHash implements the BLOBHASH opcode func opBlobHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - index := scope.Stack.peek() + index := scope.Stack.pop() if index.LtUint64(uint64(len(interpreter.evm.TxContext.BlobHashes))) { blobHash := interpreter.evm.TxContext.BlobHashes[index.Uint64()] - index.SetBytes32(blobHash[:]) + scope.Stack.pushBytes(blobHash[:]) } else { - index.Clear() + scope.Stack.pushU64(0) } return nil, nil } @@ -289,7 +289,7 @@ func opBlobHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ // opBlobBaseFee implements BLOBBASEFEE opcode func opBlobBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { blobBaseFee, _ := uint256.FromBig(interpreter.evm.Context.BlobBaseFee) - scope.Stack.push(blobBaseFee) + scope.Stack.push(*blobBaseFee) return nil, nil } @@ -357,11 +357,10 @@ func opExtCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeC func opPush1EIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( codeLen = uint64(len(scope.Contract.Code)) - integer = new(uint256.Int) ) *pc += 1 if *pc < codeLen { - scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc]))) + scope.Stack.pushU64(uint64(scope.Contract.Code[*pc])) if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall && *pc%31 == 0 { // touch next chunk if PUSH1 is at the boundary. if so, *pc has @@ -374,7 +373,7 @@ func opPush1EIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } } } else { - scope.Stack.push(integer.Clear()) + scope.Stack.pushU64(0) } return nil, nil } @@ -386,11 +385,11 @@ func makePushEIP4762(size uint64, pushByteSize int) executionFunc { start = min(codeLen, int(*pc+1)) end = min(codeLen, start+pushByteSize) ) - scope.Stack.push(new(uint256.Int).SetBytes( + scope.Stack.pushBytes( common.RightPadBytes( scope.Contract.Code[start:end], pushByteSize, - )), + ), ) if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall { diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 0b3b1d1569..a6c8cb0246 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -28,158 +28,174 @@ import ( ) func opAdd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - y.Add(&x, y) + x, y := scope.Stack.pop(), scope.Stack.pop() + y.Add(&x, &y) + scope.Stack.push(y) return nil, nil } func opSub(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - y.Sub(&x, y) + x, y := scope.Stack.pop(), scope.Stack.pop() + y.Sub(&x, &y) + scope.Stack.push(y) return nil, nil } func opMul(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - y.Mul(&x, y) + x, y := scope.Stack.pop(), scope.Stack.pop() + y.Mul(&x, &y) + scope.Stack.push(y) return nil, nil } func opDiv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - y.Div(&x, y) + x, y := scope.Stack.pop(), scope.Stack.pop() + y.Div(&x, &y) + scope.Stack.push(y) return nil, nil } func opSdiv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - y.SDiv(&x, y) + x, y := scope.Stack.pop(), scope.Stack.pop() + y.SDiv(&x, &y) + scope.Stack.push(y) return nil, nil } func opMod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - y.Mod(&x, y) + x, y := scope.Stack.pop(), scope.Stack.pop() + y.Mod(&x, &y) + scope.Stack.push(y) return nil, nil } func opSmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - y.SMod(&x, y) + x, y := scope.Stack.pop(), scope.Stack.pop() + y.SMod(&x, &y) + scope.Stack.push(y) return nil, nil } func opExp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - base, exponent := scope.Stack.pop(), scope.Stack.peek() - exponent.Exp(&base, exponent) + base, exponent := scope.Stack.pop(), scope.Stack.pop() + exponent.Exp(&base, &exponent) + scope.Stack.push(exponent) return nil, nil } func opSignExtend(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - back, num := scope.Stack.pop(), scope.Stack.peek() - num.ExtendSign(num, &back) + back, num := scope.Stack.pop(), scope.Stack.pop() + num.ExtendSign(&num, &back) + scope.Stack.push(num) return nil, nil } func opNot(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x := scope.Stack.peek() - x.Not(x) + x := scope.Stack.pop() + x.Not(&x) + scope.Stack.push(x) return nil, nil } func opLt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - if x.Lt(y) { - y.SetOne() + x, y := scope.Stack.pop(), scope.Stack.pop() + if x.Lt(&y) { + scope.Stack.pushU64(1) } else { - y.Clear() + scope.Stack.pushU64(0) } return nil, nil } func opGt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - if x.Gt(y) { - y.SetOne() + x, y := scope.Stack.pop(), scope.Stack.pop() + if x.Gt(&y) { + scope.Stack.pushU64(1) } else { - y.Clear() + scope.Stack.pushU64(0) } return nil, nil } func opSlt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - if x.Slt(y) { - y.SetOne() + x, y := scope.Stack.pop(), scope.Stack.pop() + if x.Slt(&y) { + scope.Stack.pushU64(1) } else { - y.Clear() + scope.Stack.pushU64(0) } return nil, nil } func opSgt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - if x.Sgt(y) { - y.SetOne() + x, y := scope.Stack.pop(), scope.Stack.pop() + if x.Sgt(&y) { + scope.Stack.pushU64(1) } else { - y.Clear() + scope.Stack.pushU64(0) } return nil, nil } func opEq(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - if x.Eq(y) { - y.SetOne() + x, y := scope.Stack.pop(), scope.Stack.pop() + if x.Eq(&y) { + scope.Stack.pushU64(1) } else { - y.Clear() + scope.Stack.pushU64(0) } return nil, nil } func opIszero(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x := scope.Stack.peek() + x := scope.Stack.pop() if x.IsZero() { - x.SetOne() + scope.Stack.pushU64(1) } else { - x.Clear() + scope.Stack.pushU64(0) } return nil, nil } func opAnd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - y.And(&x, y) + x, y := scope.Stack.pop(), scope.Stack.pop() + y.And(&x, &y) + scope.Stack.push(y) return nil, nil } func opOr(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - y.Or(&x, y) + x, y := scope.Stack.pop(), scope.Stack.pop() + y.Or(&x, &y) + scope.Stack.push(y) return nil, nil } func opXor(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - y.Xor(&x, y) + x, y := scope.Stack.pop(), scope.Stack.pop() + y.Xor(&x, &y) + scope.Stack.push(y) return nil, nil } func opByte(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - th, val := scope.Stack.pop(), scope.Stack.peek() + th, val := scope.Stack.pop(), scope.Stack.pop() val.Byte(&th) + scope.Stack.push(val) return nil, nil } func opAddmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek() - z.AddMod(&x, &y, z) + x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.pop() + z.AddMod(&x, &y, &z) + scope.Stack.push(z) return nil, nil } func opMulmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek() - z.MulMod(&x, &y, z) + x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.pop() + z.MulMod(&x, &y, &z) + scope.Stack.push(z) return nil, nil } @@ -188,11 +204,12 @@ func opMulmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b // and pushes on the stack arg2 shifted to the left by arg1 number of bits. func opSHL(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { // Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards - shift, value := scope.Stack.pop(), scope.Stack.peek() + shift, value := scope.Stack.pop(), scope.Stack.pop() if shift.LtUint64(256) { - value.Lsh(value, uint(shift.Uint64())) + value.Lsh(&value, uint(shift.Uint64())) + scope.Stack.push(value) } else { - value.Clear() + scope.Stack.pushU64(0) } return nil, nil } @@ -202,11 +219,12 @@ func opSHL(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte // and pushes on the stack arg2 shifted to the right by arg1 number of bits with zero fill. func opSHR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { // Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards - shift, value := scope.Stack.pop(), scope.Stack.peek() + shift, value := scope.Stack.pop(), scope.Stack.pop() if shift.LtUint64(256) { - value.Rsh(value, uint(shift.Uint64())) + value.Rsh(&value, uint(shift.Uint64())) + scope.Stack.push(value) } else { - value.Clear() + scope.Stack.pushU64(0) } return nil, nil } @@ -215,23 +233,24 @@ func opSHR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte // The SAR instruction (arithmetic shift right) pops 2 values from the stack, first arg1 and then arg2, // and pushes on the stack arg2 shifted to the right by arg1 number of bits with sign extension. func opSAR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - shift, value := scope.Stack.pop(), scope.Stack.peek() + shift, value := scope.Stack.pop(), scope.Stack.pop() if shift.GtUint64(256) { if value.Sign() >= 0 { - value.Clear() + scope.Stack.pushU64(0) } else { // Max negative shift: all bits set value.SetAllOne() + scope.Stack.push(value) } return nil, nil } - n := uint(shift.Uint64()) - value.SRsh(value, n) + value.SRsh(&value, uint(shift.Uint64())) + scope.Stack.push(value) return nil, nil } func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - offset, size := scope.Stack.pop(), scope.Stack.peek() + offset, size := scope.Stack.pop(), scope.Stack.pop() data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64()) if interpreter.hasher == nil { @@ -246,50 +265,50 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( if evm.Config.EnablePreimageRecording { evm.StateDB.AddPreimage(interpreter.hasherBuf, data) } - size.SetBytes(interpreter.hasherBuf[:]) + scope.Stack.pushBytes(interpreter.hasherBuf[:]) return nil, nil } func opAddress(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Address().Bytes())) + scope.Stack.pushBytes(scope.Contract.Address().Bytes()) return nil, nil } func opBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - slot := scope.Stack.peek() + slot := scope.Stack.pop() address := common.Address(slot.Bytes20()) - slot.Set(interpreter.evm.StateDB.GetBalance(address)) + scope.Stack.push(*interpreter.evm.StateDB.GetBalance(address)) return nil, nil } func opOrigin(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes())) + scope.Stack.pushBytes(interpreter.evm.Origin.Bytes()) return nil, nil } func opCaller(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Caller().Bytes())) + scope.Stack.pushBytes(scope.Contract.Caller().Bytes()) return nil, nil } func opCallValue(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(scope.Contract.value) + scope.Stack.push(*scope.Contract.value) return nil, nil } func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x := scope.Stack.peek() + x := scope.Stack.pop() if offset, overflow := x.Uint64WithOverflow(); !overflow { data := getData(scope.Contract.Input, offset, 32) - x.SetBytes(data) + scope.Stack.pushBytes(data) } else { - x.Clear() + scope.Stack.pushU64(0) } return nil, nil } func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Input)))) + scope.Stack.pushU64(uint64(len(scope.Contract.Input))) return nil, nil } @@ -312,7 +331,7 @@ func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData)))) + scope.Stack.pushU64(uint64(len(interpreter.returnData))) return nil, nil } @@ -339,13 +358,13 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte } func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - slot := scope.Stack.peek() - slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20()))) + slot := scope.Stack.pop() + scope.Stack.pushU64(uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20()))) return nil, nil } func opCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Code)))) + scope.Stack.pushU64(uint64(len(scope.Contract.Code))) return nil, nil } @@ -412,27 +431,27 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) // 6. Caller tries to get the code hash for an account which is marked as deleted, this // account should be regarded as a non-existent account and zero should be returned. func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - slot := scope.Stack.peek() + slot := scope.Stack.pop() address := common.Address(slot.Bytes20()) if interpreter.evm.StateDB.Empty(address) { - slot.Clear() + scope.Stack.pushU64(0) } else { - slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes()) + scope.Stack.pushBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes()) } return nil, nil } func opGasprice(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { v, _ := uint256.FromBig(interpreter.evm.GasPrice) - scope.Stack.push(v) + scope.Stack.push(*v) return nil, nil } func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - num := scope.Stack.peek() + num := scope.Stack.pop() num64, overflow := num.Uint64WithOverflow() if overflow { - num.Clear() + scope.Stack.pushU64(0) return nil, nil } @@ -451,43 +470,42 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( if tracer := interpreter.evm.Config.Tracer; tracer != nil && tracer.OnBlockHashRead != nil { tracer.OnBlockHashRead(num64, res) } - num.SetBytes(res[:]) + scope.Stack.pushBytes(res[:]) } else { - num.Clear() + scope.Stack.pushU64(0) } return nil, nil } func opCoinbase(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Context.Coinbase.Bytes())) + scope.Stack.pushBytes(interpreter.evm.Context.Coinbase.Bytes()) return nil, nil } func opTimestamp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.Context.Time)) + scope.Stack.pushU64(interpreter.evm.Context.Time) return nil, nil } func opNumber(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { v, _ := uint256.FromBig(interpreter.evm.Context.BlockNumber) - scope.Stack.push(v) + scope.Stack.push(*v) return nil, nil } func opDifficulty(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { v, _ := uint256.FromBig(interpreter.evm.Context.Difficulty) - scope.Stack.push(v) + scope.Stack.push(*v) return nil, nil } func opRandom(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - v := new(uint256.Int).SetBytes(interpreter.evm.Context.Random.Bytes()) - scope.Stack.push(v) + scope.Stack.pushBytes(interpreter.evm.Context.Random.Bytes()) return nil, nil } func opGasLimit(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.Context.GasLimit)) + scope.Stack.pushU64(interpreter.evm.Context.GasLimit) return nil, nil } @@ -497,9 +515,9 @@ func opPop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte } func opMload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - v := scope.Stack.peek() + v := scope.Stack.pop() offset := v.Uint64() - v.SetBytes(scope.Memory.GetPtr(offset, 32)) + scope.Stack.pushBytes(scope.Memory.GetPtr(offset, 32)) return nil, nil } @@ -516,10 +534,10 @@ func opMstore8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] } func opSload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - loc := scope.Stack.peek() + loc := scope.Stack.pop() hash := common.Hash(loc.Bytes32()) val := interpreter.evm.StateDB.GetState(scope.Contract.Address(), hash) - loc.SetBytes(val.Bytes()) + scope.Stack.pushBytes(val.Bytes()) return nil, nil } @@ -564,17 +582,17 @@ func opJumpdest(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ } func opPc(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(*pc)) + scope.Stack.pushU64(*pc) return nil, nil } func opMsize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(uint64(scope.Memory.Len()))) + scope.Stack.pushU64((uint64(scope.Memory.Len()))) return nil, nil } func opGas(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(scope.Contract.Gas)) + scope.Stack.pushU64(scope.Contract.Gas) return nil, nil } @@ -672,9 +690,6 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b gas -= gas / 64 } - // reuse size int for stackvalue - stackvalue := size - scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, tracing.GasChangeCallContractCreation) res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract.Address(), input, gas, &value) @@ -683,13 +698,12 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b // rule) and treat as an error, if the ruleset is frontier we must // ignore this error and pretend the operation was successful. if interpreter.evm.chainRules.IsHomestead && suberr == ErrCodeStoreOutOfGas { - stackvalue.Clear() + scope.Stack.pushU64(0) } else if suberr != nil && suberr != ErrCodeStoreOutOfGas { - stackvalue.Clear() + scope.Stack.pushU64(0) } else { - stackvalue.SetBytes(addr.Bytes()) + scope.Stack.pushBytes(addr.Bytes()) } - scope.Stack.push(&stackvalue) scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) @@ -717,16 +731,14 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] gas -= gas / 64 scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, tracing.GasChangeCallContractCreation2) // reuse size int for stackvalue - stackvalue := size res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract.Address(), input, gas, &endowment, &salt) // Push item on the stack based on the returned error. if suberr != nil { - stackvalue.Clear() + scope.Stack.pushU64(0) } else { - stackvalue.SetBytes(addr.Bytes()) + scope.Stack.pushBytes(addr.Bytes()) } - scope.Stack.push(&stackvalue) scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) if suberr == ErrExecutionReverted { @@ -741,7 +753,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt stack := scope.Stack // Pop gas. The actual gas in interpreter.evm.callGasTemp. // We can use this as a temporary value - temp := stack.pop() + _ = stack.pop() gas := interpreter.evm.callGasTemp // Pop other call parameters. addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() @@ -758,11 +770,10 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt ret, returnGas, err := interpreter.evm.Call(scope.Contract.Address(), toAddr, args, gas, &value) if err != nil { - temp.Clear() + stack.pushU64(0) } else { - temp.SetOne() + stack.pushU64(1) } - stack.push(&temp) if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } @@ -774,10 +785,9 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt } func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - // Pop gas. The actual gas is in interpreter.evm.callGasTemp. stack := scope.Stack - // We use it as a temporary value - temp := stack.pop() + // Pop gas. The actual gas is in interpreter.evm.callGasTemp. + _ = stack.pop() gas := interpreter.evm.callGasTemp // Pop other call parameters. addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() @@ -791,11 +801,10 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ ret, returnGas, err := interpreter.evm.CallCode(scope.Contract.Address(), toAddr, args, gas, &value) if err != nil { - temp.Clear() + stack.pushU64(0) } else { - temp.SetOne() + stack.pushU64(1) } - stack.push(&temp) if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } @@ -810,7 +819,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext stack := scope.Stack // Pop gas. The actual gas is in interpreter.evm.callGasTemp. // We use it as a temporary value - temp := stack.pop() + _ = stack.pop() gas := interpreter.evm.callGasTemp // Pop other call parameters. addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() @@ -820,11 +829,10 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext ret, returnGas, err := interpreter.evm.DelegateCall(scope.Contract.Caller(), scope.Contract.Address(), toAddr, args, gas, scope.Contract.value) if err != nil { - temp.Clear() + stack.pushU64(0) } else { - temp.SetOne() + stack.pushU64(1) } - stack.push(&temp) if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } @@ -836,10 +844,10 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - // Pop gas. The actual gas is in interpreter.evm.callGasTemp. stack := scope.Stack + // Pop gas. The actual gas is in interpreter.evm.callGasTemp. // We use it as a temporary value - temp := stack.pop() + _ = stack.pop() gas := interpreter.evm.callGasTemp // Pop other call parameters. addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() @@ -849,11 +857,10 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ret, returnGas, err := interpreter.evm.StaticCall(scope.Contract.Address(), toAddr, args, gas) if err != nil { - temp.Clear() + stack.pushU64(0) } else { - temp.SetOne() + stack.pushU64(1) } - stack.push(&temp) if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } @@ -960,13 +967,12 @@ func makeLog(size int) executionFunc { func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( codeLen = uint64(len(scope.Contract.Code)) - integer = new(uint256.Int) ) *pc += 1 if *pc < codeLen { - scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc]))) + scope.Stack.pushU64(uint64(scope.Contract.Code[*pc])) } else { - scope.Stack.push(integer.Clear()) + scope.Stack.pushU64(0) } return nil, nil } @@ -975,14 +981,13 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by func opPush2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( codeLen = uint64(len(scope.Contract.Code)) - integer = new(uint256.Int) ) if *pc+2 < codeLen { - scope.Stack.push(integer.SetBytes2(scope.Contract.Code[*pc+1 : *pc+3])) + scope.Stack.pushBytes(scope.Contract.Code[*pc+1 : *pc+3]) } else if *pc+1 < codeLen { - scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc+1]) << 8)) + scope.Stack.pushU64(uint64(scope.Contract.Code[*pc+1]) << 8) } else { - scope.Stack.push(integer.Clear()) + scope.Stack.pushU64(0) } *pc += 2 return nil, nil @@ -1002,7 +1007,7 @@ func makePush(size uint64, pushByteSize int) executionFunc { if missing := pushByteSize - (end - start); missing > 0 { a.Lsh(a, uint(8*missing)) } - scope.Stack.push(a) + scope.Stack.push(*a) *pc += size return nil, nil } diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index cce88eebb5..94f133a122 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -105,8 +105,8 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu x := new(uint256.Int).SetBytes(common.Hex2Bytes(test.X)) y := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Y)) expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected)) - stack.push(x) - stack.push(y) + stack.push(*x) + stack.push(*y) opFn(&pc, evm.interpreter, &ScopeContext{nil, stack, nil}) if stack.len() != 1 { t.Errorf("Expected one item on stack after %v, got %d: ", name, stack.len()) @@ -218,9 +218,9 @@ func TestAddMod(t *testing.T) { y := new(uint256.Int).SetBytes(common.Hex2Bytes(test.y)) z := new(uint256.Int).SetBytes(common.Hex2Bytes(test.z)) expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.expected)) - stack.push(z) - stack.push(y) - stack.push(x) + stack.push(*z) + stack.push(*y) + stack.push(*x) opAddmod(&pc, evm.interpreter, &ScopeContext{nil, stack, nil}) actual := stack.pop() if actual.Cmp(expected) != 0 { @@ -245,8 +245,8 @@ func TestWriteExpectedValues(t *testing.T) { for i, param := range args { x := new(uint256.Int).SetBytes(common.Hex2Bytes(param.x)) y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y)) - stack.push(x) - stack.push(y) + stack.push(*x) + stack.push(*y) opFn(&pc, evm.interpreter, &ScopeContext{nil, stack, nil}) actual := stack.pop() result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)} @@ -294,7 +294,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) { bench.ResetTimer() for i := 0; i < bench.N; i++ { for _, arg := range intArgs { - stack.push(arg) + stack.push(*arg) } op(&pc, evm.interpreter, scope) stack.pop() @@ -526,14 +526,14 @@ func TestOpMstore(t *testing.T) { mem.Resize(64) pc := uint64(0) v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700" - stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(v))) - stack.push(new(uint256.Int)) + stack.push(*new(uint256.Int).SetBytes(common.Hex2Bytes(v))) + stack.push(*new(uint256.Int)) opMstore(&pc, evm.interpreter, &ScopeContext{mem, stack, nil}) if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v { t.Fatalf("Mstore fail, got %v, expected %v", got, v) } - stack.push(new(uint256.Int).SetUint64(0x1)) - stack.push(new(uint256.Int)) + stack.push(*new(uint256.Int).SetUint64(0x1)) + stack.push(*new(uint256.Int)) opMstore(&pc, evm.interpreter, &ScopeContext{mem, stack, nil}) if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" { t.Fatalf("Mstore failed to overwrite previous value") @@ -553,8 +553,8 @@ func BenchmarkOpMstore(bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { - stack.push(value) - stack.push(memStart) + stack.push(*value) + stack.push(*memStart) opMstore(&pc, evm.interpreter, &ScopeContext{mem, stack, nil}) } } @@ -578,22 +578,22 @@ func TestOpTstore(t *testing.T) { pc := uint64(0) // push the value to the stack - stack.push(new(uint256.Int).SetBytes(value)) + stack.push(*new(uint256.Int).SetBytes(value)) // push the location to the stack - stack.push(new(uint256.Int)) + stack.push(*new(uint256.Int)) opTstore(&pc, evm.interpreter, &scopeContext) // there should be no elements on the stack after TSTORE if stack.len() != 0 { t.Fatal("stack wrong size") } // push the location to the stack - stack.push(new(uint256.Int)) + stack.push(*new(uint256.Int)) opTload(&pc, evm.interpreter, &scopeContext) // there should be one element on the stack after TLOAD if stack.len() != 1 { t.Fatal("stack wrong size") } - val := stack.peek() + val := stack.Back(0) if !bytes.Equal(val.Bytes(), value) { t.Fatal("incorrect element read from transient storage") } @@ -611,8 +611,8 @@ func BenchmarkOpKeccak256(bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { - stack.push(uint256.NewInt(32)) - stack.push(start) + stack.push(*uint256.NewInt(32)) + stack.push(*start) opKeccak256(&pc, evm.interpreter, &ScopeContext{mem, stack, nil}) } } @@ -708,8 +708,8 @@ func TestRandom(t *testing.T) { pc = uint64(0) ) opRandom(&pc, evm.interpreter, &ScopeContext{nil, stack, nil}) - if have, want := stack.len(), 1; have != want { - t.Errorf("test '%v': want %d item(s) on stack, have %d: ", tt.name, have, want) + if stack.len() != 1 { + t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, stack.len()) } actual := stack.pop() expected, overflow := uint256.FromBig(new(big.Int).SetBytes(tt.random.Bytes())) @@ -748,10 +748,10 @@ func TestBlobHash(t *testing.T) { pc = uint64(0) ) evm.SetTxContext(TxContext{BlobHashes: tt.hashes}) - stack.push(uint256.NewInt(tt.idx)) + stack.push(*uint256.NewInt(tt.idx)) opBlobHash(&pc, evm.interpreter, &ScopeContext{nil, stack, nil}) - if have, want := stack.len(), 1; have != want { - t.Errorf("test '%v': want %d item(s) on stack, have %d: ", tt.name, have, want) + if stack.len() != 1 { + t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, stack.len()) } actual := stack.pop() expected, overflow := uint256.FromBig(new(big.Int).SetBytes(tt.expect.Bytes())) @@ -860,9 +860,9 @@ func TestOpMCopy(t *testing.T) { src, _ := uint256.FromHex(tc.src) dst, _ := uint256.FromHex(tc.dst) - stack.push(len) - stack.push(src) - stack.push(dst) + stack.push(*len) + stack.push(*src) + stack.push(*dst) wantErr := (tc.wantGas == 0) // Calc mem expansion var memorySize uint64 @@ -903,7 +903,7 @@ func TestOpMCopy(t *testing.T) { // TestPush sanity-checks how code with immediates are handled when the code size is // smaller than the size of the immediate. -func TestPush(t *testing.T) { +func TestPush2(t *testing.T) { code := common.FromHex("0011223344556677889900aabbccddeeff0102030405060708090a0b0c0d0e0ff1e1d1c1b1a19181716151413121") push32 := makePush(32, 32) diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index ff3875868f..98e2f64348 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -34,7 +34,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc { } // Gas sentry honoured, do the actual gas calculation based on the stored value var ( - y, x = stack.Back(1), stack.peek() + y, x = stack.Back(1), stack.Back(0) slot = common.Hash(x.Bytes32()) current = evm.StateDB.GetState(contract.Address(), slot) cost = uint64(0) @@ -97,7 +97,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc { // charge 2100 gas and add the pair to accessed_storage_keys. // If the pair is already in accessed_storage_keys, charge 100 gas. func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - loc := stack.peek() + loc := stack.Back(0) slot := common.Hash(loc.Bytes32()) // Check slot presence in the access list if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent { @@ -120,7 +120,7 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo if err != nil { return 0, err } - addr := common.Address(stack.peek().Bytes20()) + addr := common.Address(stack.Back(0).Bytes20()) // Check slot presence in the access list if !evm.StateDB.AddressInAccessList(addr) { evm.StateDB.AddAddressToAccessList(addr) @@ -142,7 +142,7 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo // - extcodesize, // - (ext) balance func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - addr := common.Address(stack.peek().Bytes20()) + addr := common.Address(stack.Back(0).Bytes20()) // Check slot presence in the access list if !evm.StateDB.AddressInAccessList(addr) { // If the caller cannot afford the cost, this change will be rolled back @@ -225,7 +225,7 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc { gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var ( gas uint64 - address = common.Address(stack.peek().Bytes20()) + address = common.Address(stack.Back(0).Bytes20()) ) if !evm.StateDB.AddressInAccessList(address) { // If the caller cannot afford the cost, this change will be rolled back diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go index 751761a911..168db7d0a8 100644 --- a/core/vm/operations_verkle.go +++ b/core/vm/operations_verkle.go @@ -25,7 +25,7 @@ import ( ) func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas := evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true) + gas := evm.AccessEvents.SlotGas(contract.Address(), stack.Back(0).Bytes32(), true) if gas == 0 { gas = params.WarmStorageReadCostEIP2929 } @@ -33,7 +33,7 @@ func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo } func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas := evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false) + gas := evm.AccessEvents.SlotGas(contract.Address(), stack.Back(0).Bytes32(), false) if gas == 0 { gas = params.WarmStorageReadCostEIP2929 } @@ -53,7 +53,7 @@ func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem } func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - address := stack.peek().Bytes20() + address := stack.Back(0).Bytes20() if _, isPrecompile := evm.precompile(address); isPrecompile { return 0, nil } @@ -110,7 +110,7 @@ var ( ) func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - beneficiaryAddr := common.Address(stack.peek().Bytes20()) + beneficiaryAddr := common.Address(stack.Back(0).Bytes20()) if _, isPrecompile := evm.precompile(beneficiaryAddr); isPrecompile { return 0, nil } diff --git a/core/vm/stack.go b/core/vm/stack.go index 11b305d20f..188f9be09c 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -28,16 +28,29 @@ type stackArena struct { top int // first free slot } -func (sa *stackArena) push(value *uint256.Int) { +func (sa *stackArena) push(value uint256.Int) { if len(sa.data) <= sa.top { // we need to grow the arena sa.data = slices.Grow(sa.data, 512) sa.data = sa.data[:cap(sa.data)] } - sa.data[sa.top] = *value + sa.data[sa.top] = value sa.top++ } +// weird optimization, adds an element onto the stack +// and returns a pointer to it. Might contain some old data +// so we need to make sure to properly overwrite it. +func (sa *stackArena) peekElement() uint256.Int { + if len(sa.data) <= sa.top { + // we need to grow the arena + sa.data = slices.Grow(sa.data, 512) + sa.data = sa.data[:cap(sa.data)] + } + elem := sa.data[sa.top] + return elem +} + func (sa *stackArena) pop() { sa.top-- } @@ -88,12 +101,26 @@ func (s *Stack) Data() []uint256.Int { return s.inner.data[s.bottom : s.bottom+s.size] } -func (s *Stack) push(d *uint256.Int) { +func (s *Stack) push(d uint256.Int) { // NOTE push limit (1024) is checked in baseCheck s.inner.push(d) s.size++ } +func (s *Stack) pushBytes(d []byte) { + elem := s.inner.peekElement() + elem.SetBytes(d) + s.inner.push(elem) + s.size++ +} + +func (s *Stack) pushU64(d uint64) { + elem := s.inner.peekElement() + elem.SetUint64(d) + s.inner.push(elem) + s.size++ +} + func (s *Stack) pop() uint256.Int { ret := s.inner.data[s.bottom+s.size-1] s.inner.pop() @@ -155,10 +182,10 @@ func (s *Stack) swap16() { } func (s *Stack) dup(n int) { - // TODO: check size of inner - s.inner.data[s.bottom+s.size] = s.inner.data[s.bottom+s.size-n] + elem := s.inner.peekElement() + elem.Set(&s.inner.data[s.bottom+s.size-n]) + s.inner.push(elem) s.size++ - s.inner.top++ } func (s *Stack) peek() *uint256.Int { diff --git a/core/vm/stack_test.go b/core/vm/stack_test.go new file mode 100644 index 0000000000..2fb50b25e4 --- /dev/null +++ b/core/vm/stack_test.go @@ -0,0 +1,16 @@ +package vm + +import ( + "testing" + + "github.com/holiman/uint256" +) + +func BenchmarkStack(b *testing.B) { + stack := newStackForTesting() + for i := 0; i < b.N; i++ { + stack.push(uint256.Int{1}) + stack.pushBytes([]byte{12, 34}) + stack.pushU64(1234) + } +}