add costs from eip. check for modOffset + modSize overflow in setupx gas cost calculation

This commit is contained in:
Jared Wasinger 2024-11-29 21:01:12 +07:00
parent ce284116bc
commit 08bd16adff
3 changed files with 37 additions and 28 deletions

View file

@ -19,6 +19,7 @@ package vm
import (
"errors"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
@ -540,28 +541,40 @@ func gasEOFCreate(pc uint64, evm *EVM, scope *ScopeContext, memorySize uint64) (
panic("not implemented")
}
func isPowerOfTwo(val *big.Int) bool {
if val.Bit(val.BitLen()-1) == 1 && new(big.Int).SetBit(val, val.BitLen()-1, 0).Cmp(big.NewInt(0)) == 0 {
return true
}
return false
}
func gasSetupx(pc uint64, evm *EVM, scope *ScopeContext, memorySize uint64) (uint64, error) {
if !scope.Stack.Back(0).IsUint64() || !scope.Stack.Back(2).IsUint64() || !scope.Stack.Back(3).IsUint64() {
return 0, errors.New("one or more parameters overflows 64 bits")
}
modId := uint(scope.Stack.Back(0).Uint64())
if scope.modExtState.alloced[modId] != nil {
return 0, nil
}
modOffset := scope.Stack.Back(1).Uint64()
modSize := scope.Stack.Back(2).Uint64()
if modSize > 96 {
// TODO: ensure returning error here consumes all evm call context gas
return 0, fmt.Errorf("modulus cannot exceed 768 bits in width")
}
_, overflow := math.SafeAdd(modOffset, modSize)
if overflow {
return 0, fmt.Errorf("modulus offset + size overflows uint64.")
}
feAllocCount := scope.Stack.Back(3).Uint64()
if feAllocCount > 256 {
return 0, fmt.Errorf("cannot allocate more than 256 field elements per modulus id")
}
paddedModSize := (modSize + 7) / 8
precompCost := uint64(params.SetupxPrecompCost[paddedModSize])
precompCost := params.SetmodxOddModulusCost[paddedModSize]
// the size in bytes of the field element heap that this call to SETUPX is
// allocating.
@ -576,7 +589,11 @@ func gasSetupx(pc uint64, evm *EVM, scope *ScopeContext, memorySize uint64) (uin
// and the maximum call-context allocatable memory + reasonable evm memory limit
// will not overflow a uint64.
memCost, _ := evmmaxMemoryGasCost(pc, scope, memorySize, allocSize)
modBytes := scope.Memory.GetPtr(modOffset, modSize)
if !isPowerOfTwo(new(big.Int).SetBytes(modBytes)) {
return precompCost + memCost, nil
}
return memCost, nil
}
func gasStorex(pc uint64, evm *EVM, scope *ScopeContext, memorySize uint64) (uint64, error) {
@ -642,24 +659,16 @@ func gasLoadx(pc uint64, evm *EVM, scope *ScopeContext, memorySize uint64) (uint
}
}
func gasEVMMAXArithOp(pc uint64, evm *EVM, scope *ScopeContext, memorySize uint64) (uint64, error) {
func makeEVMMAXArithGasFunc(opCosts []uint64) gasFunc {
return func(pc uint64, evm *EVM, scope *ScopeContext, memorySize uint64) (uint64, error) {
if scope.modExtState.active == nil {
return 0, errors.New("no active mod state")
return 0, errors.New("no active field context")
}
_ = scope.Contract.Code[pc+7]
out := uint(scope.Contract.Code[pc+1])
out_stride := uint(scope.Contract.Code[pc+2])
x := uint(scope.Contract.Code[pc+3])
x_stride := uint(scope.Contract.Code[pc+4])
y := uint(scope.Contract.Code[pc+5])
y_stride := uint(scope.Contract.Code[pc+6])
count := uint(scope.Contract.Code[pc+7])
maxOffset := max(x+x_stride*count, y+y_stride*count, out+out_stride*count)
// TODO: might not need to assert count == 0 ?
if count == 0 || out_stride == 0 || maxOffset > scope.modExtState.active.NumElems() {
out, outStride, x, xStride, y, yStride, count := extractEVMMAXImmediateInputs(pc, scope.Contract.Code)
maxOffset := max(x+xStride*count, y+yStride*count, out+outStride*count)
if count == 0 || outStride == 0 || maxOffset > scope.modExtState.active.NumElems() {
return 0, errors.New("bad parameters")
}
// TODO: fill in gas costs with table lookup multiplied by count...
return 1, nil
return uint64(count) * opCosts[len(scope.modExtState.active.Modulus)-1], nil
}
}

View file

@ -126,19 +126,19 @@ func newEVMMAXInstructionSet() JumpTable {
}
instructionSet[ADDMODX] = &operation{
execute: opAddmodx,
dynamicGas: gasEVMMAXArithOp,
dynamicGas: makeEVMMAXArithGasFunc(params.AddOrSubCost),
minStack: minStack(0, 0),
maxStack: maxStack(0, 0),
}
instructionSet[SUBMODX] = &operation{
execute: opSubmodx,
dynamicGas: gasEVMMAXArithOp,
dynamicGas: makeEVMMAXArithGasFunc(params.AddOrSubCost),
minStack: minStack(0, 0),
maxStack: maxStack(0, 0),
}
instructionSet[MULMODX] = &operation{
execute: opMulmodx,
dynamicGas: gasEVMMAXArithOp,
dynamicGas: makeEVMMAXArithGasFunc(params.MulmodxCost),
minStack: minStack(0, 0),
maxStack: maxStack(0, 0),
}

View file

@ -213,11 +213,11 @@ var (
// EVMMAX constants
var (
SetupxPrecompCost = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
SetmodxOddModulusCost = []uint64{23, 26, 29, 32, 36, 39, 42, 45, 48, 51, 54, 58}
// mulmodx cost lookup table
MulmodxCost = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
MulmodxCost = []uint64{1, 1, 1, 2, 2, 3, 4, 5, 7, 8, 10, 12}
// addmodx/submodx cost lookup table
AddOrSubCost = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
AddOrSubCost = []uint64{1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2}
// the maximum size (in bytes) that can be allocated by all field contexts
// per EVM call frame
MaxFEAllocSize = 96 * 256