@@ -12,7 +12,7 @@ use revm::{
1212 Database , Evm , Frame , FrameOrResult , InMemoryDB ,
1313} ;
1414use rvemu:: { emulator:: Emulator , exception:: Exception } ;
15- use std:: { rc:: Rc , sync:: Arc } ;
15+ use std:: { collections :: BTreeMap , rc:: Rc , sync:: Arc } ;
1616
1717use super :: error:: { Error , Result , TxResult } ;
1818
@@ -154,13 +154,15 @@ fn execute_riscv(
154154 host : & mut dyn Host ,
155155) -> Result < InterpreterAction > {
156156 let emu = & mut rvemu. emu ;
157+ emu. cpu . is_count = true ;
157158 let returned_data_destiny = & mut rvemu. returned_data_destiny ;
158159 if let Some ( destiny) = std:: mem:: take ( returned_data_destiny) {
159160 let data = emu. cpu . bus . get_dram_slice ( destiny) ?;
160161 data. copy_from_slice ( shared_memory. slice ( 0 , data. len ( ) ) )
161162 }
162163
163- let return_revert = |interpreter : & mut Interpreter | {
164+ let return_revert = |interpreter : & mut Interpreter , gas_used : u64 | {
165+ let _ = interpreter. gas . record_cost ( gas_used) ;
164166 Ok ( InterpreterAction :: Return {
165167 result : InterpreterResult {
166168 result : InstructionResult :: Revert ,
@@ -170,7 +172,8 @@ fn execute_riscv(
170172 } ,
171173 } )
172174 } ;
173-
175+ // Tracks gas usage across EVM host calls
176+ let mut evm_gas = 0 ;
174177 // Run emulator and capture ecalls
175178 loop {
176179 let run_result = emu. start ( ) ;
@@ -180,15 +183,33 @@ fn execute_riscv(
180183
181184 let Ok ( syscall) = Syscall :: try_from ( t0 as u8 ) else {
182185 println ! ( "Unhandled syscall: {:?}" , t0) ;
183- return return_revert ( interpreter) ;
186+ return return_revert ( interpreter, evm_gas ) ;
184187 } ;
185188
186189 match syscall {
187190 Syscall :: Return => {
188191 let ret_offset: u64 = emu. cpu . xregs . read ( 10 ) ;
189192 let ret_size: u64 = emu. cpu . xregs . read ( 11 ) ;
193+
194+ let r55_gas = r55_gas_used ( & emu. cpu . inst_counter ) ;
190195 let data_bytes = dram_slice ( emu, ret_offset, ret_size) ?;
191196
197+ let total_cost = r55_gas + evm_gas;
198+ println ! (
199+ "evm gas: {}, r55 gas: {}, total cost: {}" ,
200+ evm_gas, r55_gas, total_cost
201+ ) ;
202+ let in_limit = interpreter. gas . record_cost ( total_cost) ;
203+ if !in_limit {
204+ return Ok ( InterpreterAction :: Return {
205+ result : InterpreterResult {
206+ result : InstructionResult :: OutOfGas ,
207+ output : Bytes :: new ( ) ,
208+ gas : interpreter. gas ,
209+ } ,
210+ } ) ;
211+ }
212+
192213 return Ok ( InterpreterAction :: Return {
193214 result : InterpreterResult {
194215 result : InstructionResult :: Return ,
@@ -200,15 +221,20 @@ fn execute_riscv(
200221 Syscall :: SLoad => {
201222 let key: u64 = emu. cpu . xregs . read ( 10 ) ;
202223 match host. sload ( interpreter. contract . target_address , U256 :: from ( key) ) {
203- Some ( ( value, _is_cold ) ) => {
224+ Some ( ( value, is_cold ) ) => {
204225 let limbs = value. as_limbs ( ) ;
205226 emu. cpu . xregs . write ( 10 , limbs[ 0 ] ) ;
206227 emu. cpu . xregs . write ( 11 , limbs[ 1 ] ) ;
207228 emu. cpu . xregs . write ( 12 , limbs[ 2 ] ) ;
208229 emu. cpu . xregs . write ( 13 , limbs[ 3 ] ) ;
230+ if is_cold {
231+ evm_gas += 2100
232+ } else {
233+ evm_gas += 100
234+ }
209235 }
210236 _ => {
211- return return_revert ( interpreter) ;
237+ return return_revert ( interpreter, evm_gas ) ;
212238 }
213239 }
214240 }
@@ -218,11 +244,18 @@ fn execute_riscv(
218244 let second: u64 = emu. cpu . xregs . read ( 12 ) ;
219245 let third: u64 = emu. cpu . xregs . read ( 13 ) ;
220246 let fourth: u64 = emu. cpu . xregs . read ( 14 ) ;
221- host. sstore (
247+ let result = host. sstore (
222248 interpreter. contract . target_address ,
223249 U256 :: from ( key) ,
224250 U256 :: from_limbs ( [ first, second, third, fourth] ) ,
225251 ) ;
252+ if let Some ( result) = result {
253+ if result. is_cold {
254+ evm_gas += 2200
255+ } else {
256+ evm_gas += 100
257+ }
258+ }
226259 }
227260 Syscall :: Call => {
228261 let a0: u64 = emu. cpu . xregs . read ( 10 ) ;
@@ -408,7 +441,8 @@ fn execute_riscv(
408441 }
409442 }
410443 _ => {
411- return return_revert ( interpreter) ;
444+ let total_cost = r55_gas_used ( & emu. cpu . inst_counter ) + evm_gas;
445+ return return_revert ( interpreter, total_cost) ;
412446 }
413447 }
414448 }
@@ -425,3 +459,34 @@ fn dram_slice(emu: &mut Emulator, ret_offset: u64, ret_size: u64) -> Result<&mut
425459 Ok ( & mut [ ] )
426460 }
427461}
462+
463+ fn r55_gas_used ( inst_count : & BTreeMap < String , u64 > ) -> u64 {
464+ let total_cost = inst_count
465+ . iter ( )
466+ . map ( |( inst_name, count) |
467+ // Gas cost = number of instructions * cycles per instruction
468+ match inst_name. as_str ( ) {
469+ // Gas map to approximate cost of each instruction
470+ // References:
471+ // http://ithare.com/infographics-operation-costs-in-cpu-clock-cycles/
472+ // https://www.evm.codes/?fork=cancun#54
473+ // Division and remainder
474+ s if s. starts_with ( "div" ) || s. starts_with ( "rem" ) => count * 25 ,
475+ // Multiplications
476+ s if s. starts_with ( "mul" ) => count * 5 ,
477+ // Loads
478+ "lb" | "lh" | "lw" | "ld" | "lbu" | "lhu" | "lwu" => count * 3 , // Cost analagous to `MLOAD`
479+ // Stores
480+ "sb" | "sh" | "sw" | "sd" | "sc.w" | "sc.d" => count * 3 , // Cost analagous to `MSTORE`
481+ // Branching
482+ "beq" | "bne" | "blt" | "bge" | "bltu" | "bgeu" | "jal" | "jalr" => count * 3 ,
483+ _ => * count, // All other instructions including `add` and `sub`
484+ } )
485+ . sum :: < u64 > ( ) ;
486+
487+ // This is the minimum 'gas used' to ABI decode 'empty' calldata into Rust type arguments. Real calldata will take more gas.
488+ // Internalising this would focus gas metering more on the function logic
489+ let abi_decode_cost = 9_175_538 ;
490+
491+ total_cost - abi_decode_cost
492+ }
0 commit comments