Sitelet https://github.com/r55-eth/r55/commit/7e92cb690f1f024946fd43b1352f0008798ff8df
Skip to content

Commit 7e92cb6

Browse files
authored
Crude gas metering (#19)
1 parent c48a4f2 commit 7e92cb6

3 files changed

Lines changed: 75 additions & 10 deletions

File tree

‎eth-riscv-interpreter/Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ license.workspace = true
66
repository.workspace = true
77

88
[dependencies]
9-
rvemu = { git = "https://github.com/lvella/rvemu.git" }
9+
rvemu = { git="https://github.com/r55-eth/rvemu.git"}
1010

1111
thiserror.workspace = true
1212

‎r55/Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ eth-riscv-interpreter.workspace = true
1010
eth-riscv-syscalls.workspace = true
1111

1212
revm = { version = "9.0.0", features = ["std"] }
13-
rvemu = { git = "https://github.com/lvella/rvemu.git" }
13+
rvemu = { git="https://github.com/r55-eth/rvemu.git" }
1414

1515
alloy-core = "0.7.4"
1616
alloy-sol-types = "0.7.4"

‎r55/src/exec.rs‎

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use revm::{
1212
Database, Evm, Frame, FrameOrResult, InMemoryDB,
1313
};
1414
use rvemu::{emulator::Emulator, exception::Exception};
15-
use std::{rc::Rc, sync::Arc};
15+
use std::{collections::BTreeMap, rc::Rc, sync::Arc};
1616

1717
use 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

Comments
 (0)