Skip to content

Commit cd88d03

Browse files
committed
Finish gas functions
1 parent 9d87011 commit cd88d03

File tree

4 files changed

+102
-10
lines changed

4 files changed

+102
-10
lines changed

packages/evm/src/evmmax/fieldAllocations.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export class FieldAllocs {
5050
}
5151

5252
getActive(): FieldContext {
53-
if (this.active == null) throw new Error('Active not set')
53+
if (this.active == null) throw new Error('No active mod set')
5454
return this.active
5555
}
5656

packages/evm/src/evmmax/fieldContext.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ export class FieldContext {
121121
return Number(this.elemSize)
122122
}
123123

124+
getNumElems(): bigint {
125+
return this.scratchSpaceElemCount
126+
}
127+
124128
store(dst: number, count: number, from: Uint8Array) {
125129
const elemSize = this.modulus.length
126130

packages/evm/src/opcodes/gas.ts

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
divCeil,
2525
evmmaxMemoryGasCost,
2626
isPowerOfTwo,
27+
makeEVMMAXArithGasFunc,
2728
maxCallGas,
2829
setLengthLeftStorage,
2930
subMemUsage,
@@ -33,7 +34,13 @@ import {
3334

3435
import type { Common } from '@ethereumjs/common'
3536
import type { Address } from '@ethereumjs/util'
36-
import { MAX_ALLOC_SIZE, SETMODX_ODD_MODULUS_COST } from '../evmmax/constants.js'
37+
import {
38+
ADD_OR_SUB_COST,
39+
MAX_ALLOC_SIZE,
40+
MULMODX_COST,
41+
SETMODX_ODD_MODULUS_COST,
42+
} from '../evmmax/index.ts'
43+
import { add64, mul64 } from '../evmmax/util.ts'
3744
import type { RunState } from '../interpreter.ts'
3845

3946
const EXTCALL_TARGET_MAX = BigInt(2) ** BigInt(8 * 20) - BigInt(1)
@@ -787,7 +794,7 @@ export const dynamicGasHandlers: Map<number, AsyncDynamicGasHandler | SyncDynami
787794
trap('one or more parameters overflows 64 bits')
788795
}
789796
if (runState.evmmaxState.getAlloced().get(Number(modId)) !== undefined) {
790-
return 0n
797+
return gas
791798
}
792799
if (modSize > 96n) {
793800
trap('modulus cannot exceed 768 bits in width')
@@ -809,44 +816,104 @@ export const dynamicGasHandlers: Map<number, AsyncDynamicGasHandler | SyncDynami
809816
const memCost = evmmaxMemoryGasCost(runState, common, allocSize, 0n, 0n) // TODO should I be setting length and offset to 0?
810817
const modBytes = runState.memory.read(Number(modOffset), Number(modSize))
811818
if (!isPowerOfTwo(bytesToBigInt(modBytes))) {
812-
return BigInt(precompCost) + memCost
819+
return (gas += BigInt(precompCost) + memCost)
813820
}
814-
return memCost
821+
return gas + memCost
815822
},
816823
],
817824
[
818825
/* LOADX */
819826
0xc1,
820827
async function (runState, gas, common): Promise<bigint> {
821-
return 0n
828+
const [dst, src, count] = runState.stack.peek(3)
829+
830+
if (!isUint64(src) || src >= runState.evmmaxState.getActive().getNumElems()) {
831+
trap('src index out of bounds')
832+
}
833+
if (!isUint64(count) || count >= runState.evmmaxState.getActive().getNumElems()) {
834+
trap('count must be less than number of field elements in the active space')
835+
}
836+
const [last1, overflow1] = add64(src, count, 0n)
837+
if (overflow1 !== 0n || last1 > runState.evmmaxState.getActive().getNumElems()) {
838+
trap('out of bounds copy source')
839+
}
840+
if (!isUint64(dst)) {
841+
trap('destination of copy out of bounds')
842+
}
843+
844+
const [loadSize, overflow2] = mul64(
845+
count,
846+
BigInt(runState.evmmaxState.getActive().getElemSize()),
847+
)
848+
if (overflow2 !== 0n) {
849+
trap('overflow')
850+
}
851+
const [last2, overflow3] = add64(dst, loadSize, 0n)
852+
if (overflow3 !== 0n || last2 > runState.memoryWordCount) {
853+
trap('out of bounds destination')
854+
}
855+
856+
if (runState.evmmaxState.getActive().isModulusBinary) {
857+
return gas + loadSize * common.param('copyGas') // TODO check if this translates from go: toWordSize(storeSize) * params.copyGas
858+
} else {
859+
return (
860+
gas +
861+
count *
862+
BigInt(MULMODX_COST[Number(runState.evmmaxState.getActive().getElemSize() / 8) - 1])
863+
)
864+
}
822865
},
823866
],
824867
[
825868
/* STOREX */
826869
0xc2,
827870
async function (runState, gas, common): Promise<bigint> {
828-
return 0n
871+
const [dst, src, count] = runState.stack.peek(3)
872+
873+
if (!isUint64(src) || src >= runState.memoryWordCount) {
874+
trap('src index out of bounds')
875+
}
876+
if (!isUint64(dst) || dst >= runState.evmmaxState.getActive().getNumElems()) {
877+
trap('destination of copy out of bounds')
878+
}
879+
if (!isUint64(count) || count >= runState.evmmaxState.getActive().getNumElems()) {
880+
trap('count must be less than number of field elements in the active space')
881+
}
882+
const storeSize = count * runState.evmmaxState.getActive().getNumElems()
883+
if (src + storeSize > runState.memoryWordCount) {
884+
trap('source of copy out of bounds of EVM memory')
885+
}
886+
887+
if (runState.evmmaxState.getActive().isModulusBinary) {
888+
return gas + storeSize * common.param('copyGas') // TODO check if this translates from go: toWordSize(storeSize) * params.copyGas
889+
} else {
890+
return (
891+
gas +
892+
count *
893+
BigInt(MULMODX_COST[Number(runState.evmmaxState.getActive().getElemSize() / 8) - 1])
894+
)
895+
}
829896
},
830897
],
831898
[
832899
/* ADDMODX */
833900
0xc3,
834901
async function (runState, gas, common): Promise<bigint> {
835-
return 0n
902+
return makeEVMMAXArithGasFunc(ADD_OR_SUB_COST)(runState, gas, common)
836903
},
837904
],
838905
[
839906
/* SUBMODX */
840907
0xc4,
841908
async function (runState, gas, common): Promise<bigint> {
842-
return 0n
909+
return makeEVMMAXArithGasFunc(ADD_OR_SUB_COST)(runState, gas, common)
843910
},
844911
],
845912
[
846913
/* MULMODX */
847914
0xc5,
848915
async function (runState, gas, common): Promise<bigint> {
849-
return 0n
916+
return makeEVMMAXArithGasFunc(MULMODX_COST)(runState, gas, common)
850917
},
851918
],
852919
/* EXTCALL */

packages/evm/src/opcodes/util.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { EvmError } from '../exceptions.ts'
2020

2121
import type { Common } from '@ethereumjs/common'
2222
import type { Address } from '@ethereumjs/util'
23+
import { extractEVMMAXImmediateInputs } from '../evmmax/util.ts'
2324
import type { ERROR } from '../exceptions.ts'
2425
import type { RunState } from '../interpreter.ts'
2526

@@ -208,6 +209,26 @@ export function isPowerOfTwo(val: bigint): boolean {
208209
return cleared === 0n
209210
}
210211

212+
export function makeEVMMAXArithGasFunc(opCosts: number[]): Function {
213+
return function (runState, gas, common) {
214+
const [out, outStride, x, xStride, y, yStride, count] = extractEVMMAXImmediateInputs(
215+
runState.programCounter,
216+
runState.code,
217+
)
218+
const values = [x + xStride * count, y + yStride * count, out + outStride * count]
219+
const maxOffset = values.reduce((max, current) => (current > max ? current : max), 0)
220+
if (
221+
count === 0 ||
222+
outStride === 0 ||
223+
maxOffset > runState.evmmaxState.getActive().getNumElems()
224+
) {
225+
trap('bad parameters')
226+
}
227+
228+
return gas + count * opCosts[runState.evmmax.getActive().modulus.length - 1]
229+
}
230+
}
231+
211232
export function evmmaxMemoryGasCost(
212233
runState: RunState,
213234
common: Common,

0 commit comments

Comments
 (0)