Skip to content

Commit

Permalink
feat(client): ecrecover accelerated precompile
Browse files Browse the repository at this point in the history
## Overview

Adds a host-accelerated precompile for `ecrecover` with the FPVM
backend.
  • Loading branch information
clabby committed Jun 28, 2024
1 parent 9816543 commit 1d84d0d
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 3 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions bin/client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand All @@ -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]
Expand Down
8 changes: 5 additions & 3 deletions bin/client/src/kona.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -57,11 +57,13 @@ fn main() -> Result<()> {
.await?;
let L2AttributesWithParent { attributes, .. } = driver.produce_disputed_payload().await?;

let precompile_overrides =
FPVMPrecompileOverride::<OracleL2ChainProvider, TrieDBHintWriter>::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()?;
Expand Down
3 changes: 3 additions & 0 deletions bin/client/src/l2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ pub use trie_hinter::TrieDBHintWriter;

mod chain_provider;
pub use chain_provider::OracleL2ChainProvider;

mod precompiles;
pub use precompiles::FPVMPrecompileOverride;
52 changes: 52 additions & 0 deletions bin/client/src/l2/precompiles/ecrecover.rs
Original file line number Diff line number Diff line change
@@ -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::<Vec<u8>>();
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()))
}
51 changes: 51 additions & 0 deletions bin/client/src/l2/precompiles/mod.rs
Original file line number Diff line number Diff line change
@@ -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<F, H>
where
F: TrieDBFetcher,
H: TrieDBHinter,
{
_phantom: core::marker::PhantomData<(F, H)>,
}

impl<F, H> Default for FPVMPrecompileOverride<F, H>
where
F: TrieDBFetcher,
H: TrieDBHinter,
{
fn default() -> Self {
Self { _phantom: core::marker::PhantomData::<(F, H)> }
}
}

impl<F, H> PrecompileOverride<F, H> for FPVMPrecompileOverride<F, H>
where
F: TrieDBFetcher,
H: TrieDBHinter,
{
fn set_precompiles(handler: &mut EvmHandler<'_, (), &mut State<TrieDB<F, H>>>) {
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
});
}
}

0 comments on commit 1d84d0d

Please sign in to comment.