Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

R55 implementation #82

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
members = [
"bin/odyssey/",
"crates/node",
"crates/risc-v-handler",
"crates/e2e-tests",
"crates/wallet",
"crates/walltime",
Expand Down Expand Up @@ -135,6 +136,7 @@ strip = false
[workspace.dependencies]
# odyssey
odyssey-node = { path = "crates/node" }
odyssey-risc-v-handler = { path = "crates/risc-v-handler" }
odyssey-wallet = { path = "crates/wallet" }
odyssey-walltime = { path = "crates/walltime" }

Expand Down Expand Up @@ -188,7 +190,12 @@ reth-transaction-pool = { git = "https://github.com/paradigmxyz/reth.git", rev =
reth-network = { git = "https://github.com/paradigmxyz/reth.git", rev = "e98a050" }
reth-network-types = { git = "https://github.com/paradigmxyz/reth.git", rev = "e98a050" }
reth-chain-state = { git = "https://github.com/paradigmxyz/reth.git", rev = "e98a050" }
revm-precompile ={version= "14.0.0",features=["secp256r1"]}
revm-precompile = { version = "14.0.0", features = ["secp256r1"] }

# risc-v
eth-riscv-interpreter = { git = "https://github.com/leonardoalt/r55.git" }
eth-riscv-syscalls = { git = "https://github.com/leonardoalt/r55.git" }
rvemu = { git = "https://github.com/lvella/rvemu.git" }

# metrics
metrics = "0.23.0"
Expand Down
2 changes: 2 additions & 0 deletions crates/node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ keywords.workspace = true
categories.workspace = true

[dependencies]
odyssey-risc-v-handler.workspace = true

revm-precompile.workspace = true
reth-cli.workspace = true
reth-node-api.workspace = true
Expand Down
2 changes: 2 additions & 0 deletions crates/node/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//! precompiles defined by [`revm_precompile`].

use alloy_primitives::{Address, Bytes, TxKind, U256};
use odyssey_risc_v_handler::risc_v_handle_register;
use reth_chainspec::{ChainSpec, EthereumHardfork, Head};
use reth_node_api::{ConfigureEvm, ConfigureEvmEnv, NextBlockEnvAttributes};
use reth_optimism_chainspec::OpChainSpec;
Expand Down Expand Up @@ -244,6 +245,7 @@ impl ConfigureEvm for OdysseyEvmConfig {
// add additional precompiles
.append_handler_register(Self::set_precompiles)
.append_handler_register(inspector_handle_register)
.append_handler_register(risc_v_handle_register)
.build()
}

Expand Down
27 changes: 27 additions & 0 deletions crates/risc-v-handler/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "odyssey-risc-v-handler"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
authors.workspace = true
license.workspace = true
repository.workspace = true
keywords.workspace = true
categories.workspace = true

[dependencies]
# reth
reth-revm.workspace = true

# risc-v
eth-riscv-interpreter.workspace = true
eth-riscv-syscalls.workspace = true
rvemu.workspace = true

thiserror.workspace = true
tracing.workspace = true

[dev-dependencies]

[lints]
workspace = true
27 changes: 27 additions & 0 deletions crates/risc-v-handler/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use reth_revm::primitives::EVMError;
use rvemu::exception::Exception;

/// Errors returned during the RISC-V context execution
#[derive(Debug, thiserror::Error)]
pub(crate) enum RiscVError {
/// The exception kind on RISC-V [`emulator`](`Emulator`)
#[error("Got RISC-V emulator exception: {0:?}")]
RvEmuException(Exception),
/// Unhandled system call
#[error("Unhandled syscall: {0}")]
UnhandledSyscall(u32),
}

impl<E> From<RiscVError> for EVMError<E> {
#[inline]
fn from(err: RiscVError) -> Self {
Self::Custom(err.to_string())
}
}

impl From<Exception> for RiscVError {
#[inline]
fn from(exception: Exception) -> Self {
Self::RvEmuException(exception)
}
}
96 changes: 96 additions & 0 deletions crates/risc-v-handler/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
//! Odyssey's RISC-V EVM handler
#![cfg_attr(not(test), warn(unused_crate_dependencies))]

use std::{cell::RefCell, rc::Rc, sync::Arc};

use eth_riscv_interpreter::setup_from_elf;
use reth_revm::{
handler::register::EvmHandler,
interpreter::{Host, Interpreter, InterpreterAction, SharedMemory},
Database, Frame, FrameOrResult,
};

mod error;
use error::RiscVError;

mod rvemu;
use rvemu::RVEmu;

/// RISC-V magic bytes
const RISC_V_MAGIC: &[u8] = &[0xFF];

/// RISC-V EVM handler register
pub fn risc_v_handle_register<EXT, DB: Database>(handler: &mut EvmHandler<'_, EXT, DB>) {
let call_stack = Rc::<RefCell<Vec<_>>>::new(RefCell::new(Vec::new()));

// create a riscv context on call frame.
let call_stack_inner = call_stack.clone();
let old_handle = handler.execution.call.clone();
handler.execution.call = Arc::new(move |ctx, inputs| {
let result = old_handle(ctx, inputs);
if let Ok(FrameOrResult::Frame(frame)) = &result {
call_stack_inner.borrow_mut().push(riscv_context(frame));
}
result
});

// create a riscv context on create frame.
let call_stack_inner = call_stack.clone();
let old_handle = handler.execution.create.clone();
handler.execution.create = Arc::new(move |ctx, inputs| {
let result = old_handle(ctx, inputs);
if let Ok(FrameOrResult::Frame(frame)) = &result {
call_stack_inner.borrow_mut().push(riscv_context(frame));
}
result
});

// execute riscv context or old logic.
let old_handle = handler.execution.execute_frame.clone();
handler.execution.execute_frame = Arc::new(move |frame, memory, instraction_table, ctx| {
let result = if let Some(Some(riscv_context)) = call_stack.borrow_mut().first_mut() {
execute_riscv(riscv_context, frame.interpreter_mut(), memory, ctx)?
} else {
old_handle(frame, memory, instraction_table, ctx)?
};

// if it is return pop the stack.
if result.is_return() {
call_stack.borrow_mut().pop();
}
Ok(result)
});
}

/// Setup RISC-V execution context if bytecode starts with [`RISC_V_MAGIC`].
///
/// Load and parse the ELF (Electronic Linker Format), then
/// - allocates contract input size and data to emulator's CPU memory.
/// - allocates currently run bytecode to emulator's CPU memory
///
/// # Note:
/// By default it preallocated 1Mb for RISC-V DRAM data
fn riscv_context(frame: &Frame) -> Option<RVEmu> {
let interpreter = frame.interpreter();

let Some((RISC_V_MAGIC, bytecode)) = interpreter.bytecode.split_at_checked(RISC_V_MAGIC.len())
else {
return None;
};

let emu = setup_from_elf(bytecode, &interpreter.contract.input);
Some(RVEmu::new(emu))
}

/// Executes frame in the RISC-V context
///
/// FIXME: gas is not correct on interpreter return.
fn execute_riscv(
rvemu: &mut RVEmu,
interpreter: &mut Interpreter,
shared_memory: &mut SharedMemory,
host: &mut dyn Host,
) -> Result<InterpreterAction, RiscVError> {
rvemu.handle_shared_memory(shared_memory)?;
rvemu.handle_syscall(interpreter, host)
}
Loading
Loading