From e50bd4001f841ab267755937bc4f7a7a47aabebc Mon Sep 17 00:00:00 2001 From: clabby Date: Fri, 28 Jun 2024 12:25:01 -0400 Subject: [PATCH] feat(client): `ecrecover` accelerated precompile ## Overview Adds a host-accelerated precompile for `ecrecover` with the FPVM backend. --- Cargo.lock | 1 + bin/client/Cargo.toml | 2 + bin/client/src/kona.rs | 8 ++-- bin/client/src/l2/mod.rs | 3 ++ bin/client/src/l2/precompiles/ecrecover.rs | 52 ++++++++++++++++++++++ bin/client/src/l2/precompiles/mod.rs | 51 +++++++++++++++++++++ 6 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 bin/client/src/l2/precompiles/ecrecover.rs create mode 100644 bin/client/src/l2/precompiles/mod.rs diff --git a/Cargo.lock b/Cargo.lock index e3211a5ec..057b499a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1929,6 +1929,7 @@ dependencies = [ "kona-primitives", "lru", "op-alloy-consensus", + "revm", "serde", "serde_json", "spin", diff --git a/bin/client/Cargo.toml b/bin/client/Cargo.toml index 4d683066d..04ccf8ad6 100644 --- a/bin/client/Cargo.toml +++ b/bin/client/Cargo.toml @@ -21,6 +21,7 @@ lru.workspace = true spin.workspace = true async-trait.workspace = true tracing.workspace = true +revm.workspace = true # local kona-common = { path = "../../crates/common", version = "0.0.2" } @@ -31,6 +32,7 @@ kona-mpt = { path = "../../crates/mpt", version = "0.0.2" } kona-derive = { path = "../../crates/derive", default-features = false, version = "0.0.2" } kona-executor = { path = "../../crates/executor", version = "0.0.1" } +# external tracing-subscriber = { version = "0.3.18", optional = true } [dev-dependencies] diff --git a/bin/client/src/kona.rs b/bin/client/src/kona.rs index 0fc0dcb89..d905ebf00 100644 --- a/bin/client/src/kona.rs +++ b/bin/client/src/kona.rs @@ -9,11 +9,11 @@ use alloc::sync::Arc; use alloy_consensus::Header; use kona_client::{ l1::{DerivationDriver, OracleBlobProvider, OracleL1ChainProvider}, - l2::{OracleL2ChainProvider, TrieDBHintWriter}, + l2::{FPVMPrecompileOverride, OracleL2ChainProvider, TrieDBHintWriter}, BootInfo, CachingOracle, }; use kona_common_proc::client_entry; -use kona_executor::{NoPrecompileOverride, StatelessL2BlockExecutor}; +use kona_executor::StatelessL2BlockExecutor; use kona_primitives::L2AttributesWithParent; extern crate alloc; @@ -57,11 +57,13 @@ fn main() -> Result<()> { .await?; let L2AttributesWithParent { attributes, .. } = driver.produce_disputed_payload().await?; + let precompile_overrides = + FPVMPrecompileOverride::::default(); let mut executor = StatelessL2BlockExecutor::builder(&boot.rollup_config) .with_parent_header(driver.take_l2_safe_head_header()) .with_fetcher(l2_provider) .with_hinter(TrieDBHintWriter) - .with_precompile_overrides(NoPrecompileOverride) + .with_precompile_overrides(precompile_overrides) .build()?; let Header { number, .. } = *executor.execute_payload(attributes)?; let output_root = executor.compute_output_root()?; diff --git a/bin/client/src/l2/mod.rs b/bin/client/src/l2/mod.rs index 31ceb9323..908208abc 100644 --- a/bin/client/src/l2/mod.rs +++ b/bin/client/src/l2/mod.rs @@ -5,3 +5,6 @@ pub use trie_hinter::TrieDBHintWriter; mod chain_provider; pub use chain_provider::OracleL2ChainProvider; + +mod precompiles; +pub use precompiles::FPVMPrecompileOverride; diff --git a/bin/client/src/l2/precompiles/ecrecover.rs b/bin/client/src/l2/precompiles/ecrecover.rs new file mode 100644 index 000000000..ed069fc1a --- /dev/null +++ b/bin/client/src/l2/precompiles/ecrecover.rs @@ -0,0 +1,52 @@ +//! Contains the accelerated version of the `ecrecover` precompile. + +use alloc::{string::ToString, vec::Vec}; +use alloy_primitives::{keccak256, Address, Bytes}; +use anyhow::ensure; +use kona_preimage::{HintWriterClient, PreimageKey, PreimageKeyType, PreimageOracleClient}; +use revm::{ + precompile::{u64_to_address, Error as PrecompileError, PrecompileWithAddress}, + primitives::{Precompile, PrecompileOutput, PrecompileResult}, +}; + +use crate::{HintType, HINT_WRITER, ORACLE_READER}; + +const ECRECOVER_ADDRESS: Address = u64_to_address(1); + +pub(crate) const FPVM_ECRECOVER: PrecompileWithAddress = + PrecompileWithAddress(ECRECOVER_ADDRESS, Precompile::Standard(fpvm_ecrecover)); + +/// Performs an FPVM-accelerated `ecrecover` precompile. +fn fpvm_ecrecover(input: &Bytes, gas_limit: u64) -> PrecompileResult { + const ECRECOVER_BASE: u64 = 3_000; + + if ECRECOVER_BASE > gas_limit { + return Err(PrecompileError::OutOfGas.into()); + } + + let result_data = kona_common::block_on(async move { + // Write the hint for the ecrecover precompile run. + let hint_data = &[ECRECOVER_ADDRESS.as_ref(), input.as_ref()]; + HINT_WRITER.write(&HintType::L1Precompile.encode_with(hint_data)).await?; + + // Construct the key hash for the ecrecover precompile run. + let raw_key_data = hint_data.iter().copied().flatten().copied().collect::>(); + let key_hash = keccak256(&raw_key_data); + + // Fetch the result of the ecrecover precompile run from the host. + let result_data = + ORACLE_READER.get(PreimageKey::new(*key_hash, PreimageKeyType::Precompile)).await?; + + // Ensure we've received valid result data. + ensure!(!result_data.is_empty(), "Invalid result data"); + + // Ensure we've not received an error from the host. + ensure!(result_data[0] != 0, "Error executing ecrecover precompile in host"); + + // Return the result data. + Ok(result_data[1..].to_vec()) + }) + .map_err(|e| PrecompileError::Other(e.to_string()))?; + + Ok(PrecompileOutput::new(ECRECOVER_BASE, result_data.into())) +} diff --git a/bin/client/src/l2/precompiles/mod.rs b/bin/client/src/l2/precompiles/mod.rs new file mode 100644 index 000000000..7fa7aad06 --- /dev/null +++ b/bin/client/src/l2/precompiles/mod.rs @@ -0,0 +1,51 @@ +//! Contains the [PrecompileOverride] trait implementation for the FPVM-accelerated precompiles. + +use alloc::sync::Arc; +use kona_executor::PrecompileOverride; +use kona_mpt::{TrieDB, TrieDBFetcher, TrieDBHinter}; +use revm::{ + handler::register::EvmHandler, precompile::PrecompileSpecId, ContextPrecompiles, State, +}; + +mod ecrecover; + +/// The [PrecompileOverride] implementation for the FPVM-accelerated precompiles. +#[derive(Debug)] +pub struct FPVMPrecompileOverride +where + F: TrieDBFetcher, + H: TrieDBHinter, +{ + _phantom: core::marker::PhantomData<(F, H)>, +} + +impl Default for FPVMPrecompileOverride +where + F: TrieDBFetcher, + H: TrieDBHinter, +{ + fn default() -> Self { + Self { _phantom: core::marker::PhantomData::<(F, H)> } + } +} + +impl PrecompileOverride for FPVMPrecompileOverride +where + F: TrieDBFetcher, + H: TrieDBHinter, +{ + fn set_precompiles(handler: &mut EvmHandler<'_, (), &mut State>>) { + let spec_id = handler.cfg.spec_id; + + handler.pre_execution.load_precompiles = Arc::new(move || { + let mut ctx_precompiles = + ContextPrecompiles::new(PrecompileSpecId::from_spec_id(spec_id)).clone(); + + // Extend with FPVM-accelerated precompiles + let override_precompiles = [ecrecover::FPVM_ECRECOVER]; + ctx_precompiles.extend(override_precompiles); + + ctx_precompiles + }); + } +}