11import { TypeTag } from './avm_memory_types.js' ;
2+ import { InstructionExecutionError } from './errors.js' ;
23import { Addressing , AddressingMode } from './opcodes/addressing_mode.js' ;
34import { Opcode } from './serialization/instruction_serialization.js' ;
45
@@ -20,7 +21,7 @@ export function gasLeftToGas(gasLeft: { l1GasLeft: number; l2GasLeft: number; da
2021}
2122
2223/** Creates a new instance with all values set to zero except the ones set. */
23- export function makeGasCost ( gasCost : Partial < Gas > ) {
24+ export function makeGas ( gasCost : Partial < Gas > ) {
2425 return { ...EmptyGas , ...gasCost } ;
2526}
2627
@@ -36,6 +37,11 @@ export function sumGas(...gases: Partial<Gas>[]) {
3637 ) ;
3738}
3839
40+ /** Multiplies a gas instance by a scalar. */
41+ export function mulGas ( gas : Partial < Gas > , scalar : number ) {
42+ return { l1Gas : ( gas . l1Gas ?? 0 ) * scalar , l2Gas : ( gas . l2Gas ?? 0 ) * scalar , daGas : ( gas . daGas ?? 0 ) * scalar } ;
43+ }
44+
3945/** Zero gas across all gas dimensions. */
4046export const EmptyGas : Gas = {
4147 l1Gas : 0 ,
@@ -52,12 +58,12 @@ export const DynamicGasCost = Symbol('DynamicGasCost');
5258/** Temporary default gas cost. We should eventually remove all usage of this variable in favor of actual gas for each opcode. */
5359const TemporaryDefaultGasCost = { l1Gas : 0 , l2Gas : 10 , daGas : 0 } ;
5460
55- /** Gas costs for each instruction. */
56- export const GasCosts = {
57- [ Opcode . ADD ] : DynamicGasCost ,
58- [ Opcode . SUB ] : DynamicGasCost ,
59- [ Opcode . MUL ] : DynamicGasCost ,
60- [ Opcode . DIV ] : DynamicGasCost ,
61+ /** Base gas costs for each instruction. Additional gas cost may be added on top due to memory or storage accesses, etc . */
62+ export const GasCosts : Record < Opcode , Gas | typeof DynamicGasCost > = {
63+ [ Opcode . ADD ] : TemporaryDefaultGasCost ,
64+ [ Opcode . SUB ] : TemporaryDefaultGasCost ,
65+ [ Opcode . MUL ] : TemporaryDefaultGasCost ,
66+ [ Opcode . DIV ] : TemporaryDefaultGasCost ,
6167 [ Opcode . FDIV ] : TemporaryDefaultGasCost ,
6268 [ Opcode . EQ ] : TemporaryDefaultGasCost ,
6369 [ Opcode . LT ] : TemporaryDefaultGasCost ,
@@ -87,7 +93,7 @@ export const GasCosts = {
8793 [ Opcode . BLOCKL1GASLIMIT ] : TemporaryDefaultGasCost ,
8894 [ Opcode . BLOCKL2GASLIMIT ] : TemporaryDefaultGasCost ,
8995 [ Opcode . BLOCKDAGASLIMIT ] : TemporaryDefaultGasCost ,
90- [ Opcode . CALLDATACOPY ] : DynamicGasCost ,
96+ [ Opcode . CALLDATACOPY ] : TemporaryDefaultGasCost ,
9197 // Gas
9298 [ Opcode . L1GASLEFT ] : TemporaryDefaultGasCost ,
9399 [ Opcode . L2GASLEFT ] : TemporaryDefaultGasCost ,
@@ -98,7 +104,7 @@ export const GasCosts = {
98104 [ Opcode . INTERNALCALL ] : TemporaryDefaultGasCost ,
99105 [ Opcode . INTERNALRETURN ] : TemporaryDefaultGasCost ,
100106 // Memory
101- [ Opcode . SET ] : DynamicGasCost ,
107+ [ Opcode . SET ] : TemporaryDefaultGasCost ,
102108 [ Opcode . MOV ] : TemporaryDefaultGasCost ,
103109 [ Opcode . CMOV ] : TemporaryDefaultGasCost ,
104110 // World state
@@ -123,35 +129,42 @@ export const GasCosts = {
123129 [ Opcode . POSEIDON ] : TemporaryDefaultGasCost ,
124130 [ Opcode . SHA256 ] : TemporaryDefaultGasCost , // temp - may be removed, but alot of contracts rely on i: TemporaryDefaultGasCost,
125131 [ Opcode . PEDERSEN ] : TemporaryDefaultGasCost , // temp - may be removed, but alot of contracts rely on i: TemporaryDefaultGasCost,t
126- } as const ;
132+ } ;
127133
128- /** Returns the fixed gas cost for a given opcode, or throws if set to dynamic. */
129- export function getFixedGasCost ( opcode : Opcode ) : Gas {
134+ /** Returns the fixed base gas cost for a given opcode, or throws if set to dynamic. */
135+ export function getBaseGasCost ( opcode : Opcode ) : Gas {
130136 const cost = GasCosts [ opcode ] ;
131137 if ( cost === DynamicGasCost ) {
132138 throw new Error ( `Opcode ${ Opcode [ opcode ] } has dynamic gas cost` ) ;
133139 }
134140 return cost ;
135141}
136142
137- /** Returns the additional cost from indirect accesses to memory. */
138- export function getCostFromIndirectAccess ( indirect : number ) : Partial < Gas > {
139- const indirectCount = Addressing . fromWire ( indirect ) . modePerOperand . filter (
140- mode => mode === AddressingMode . INDIRECT ,
141- ) . length ;
142- return { l2Gas : indirectCount * GasCostConstants . COST_PER_INDIRECT_ACCESS } ;
143+ /** Returns the gas cost associated with the memory operations performed. */
144+ export function getMemoryGasCost ( args : { reads ?: number ; writes ?: number ; indirect ?: number } ) {
145+ const { reads, writes, indirect } = args ;
146+ const indirectCount = Addressing . fromWire ( indirect ?? 0 ) . count ( AddressingMode . INDIRECT ) ;
147+ const l2MemoryGasCost =
148+ ( reads ?? 0 ) * GasCostConstants . MEMORY_READ +
149+ ( writes ?? 0 ) * GasCostConstants . MEMORY_WRITE +
150+ indirectCount * GasCostConstants . MEMORY_INDIRECT_READ_PENALTY ;
151+ return makeGas ( { l2Gas : l2MemoryGasCost } ) ;
143152}
144153
145154/** Constants used in base cost calculations. */
146155export const GasCostConstants = {
147- SET_COST_PER_BYTE : 100 ,
148- CALLDATACOPY_COST_PER_BYTE : 10 ,
149- ARITHMETIC_COST_PER_BYTE : 10 ,
150- COST_PER_INDIRECT_ACCESS : 5 ,
156+ MEMORY_READ : 10 ,
157+ MEMORY_INDIRECT_READ_PENALTY : 10 ,
158+ MEMORY_WRITE : 100 ,
151159} ;
152160
161+ /** Returns gas cost for an operation on a given type tag based on the base cost per byte. */
162+ export function getGasCostForTypeTag ( tag : TypeTag , baseCost : Gas ) {
163+ return mulGas ( baseCost , getGasCostMultiplierFromTypeTag ( tag ) ) ;
164+ }
165+
153166/** Returns a multiplier based on the size of the type represented by the tag. Throws on uninitialized or invalid. */
154- export function getGasCostMultiplierFromTypeTag ( tag : TypeTag ) {
167+ function getGasCostMultiplierFromTypeTag ( tag : TypeTag ) {
155168 switch ( tag ) {
156169 case TypeTag . UINT8 :
157170 return 1 ;
@@ -167,6 +180,6 @@ export function getGasCostMultiplierFromTypeTag(tag: TypeTag) {
167180 return 32 ;
168181 case TypeTag . INVALID :
169182 case TypeTag . UNINITIALIZED :
170- throw new Error ( `Invalid tag type for gas cost multiplier: ${ TypeTag [ tag ] } ` ) ;
183+ throw new InstructionExecutionError ( `Invalid tag type for gas cost multiplier: ${ TypeTag [ tag ] } ` ) ;
171184 }
172185}
0 commit comments