-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduces the scaffolding and initial implementation of the `kona-host` program.
- Loading branch information
Showing
23 changed files
with
688 additions
and
68 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
//! This module contains the [HintType] enum. | ||
use std::fmt::Display; | ||
|
||
/// The [HintType] enum is used to specify the type of hint that was received. | ||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] | ||
pub enum HintType { | ||
/// A hint that specifies the block header of a layer 1 block. | ||
L1BlockHeader, | ||
/// A hint that specifies the transactions of a layer 1 block. | ||
L1Transactions, | ||
/// A hint that specifies the state node of a layer 1 block. | ||
L1Receipts, | ||
/// A hint that specifies a blob in the layer 1 beacon chain. | ||
L1Blob, | ||
/// A hint that specifies a precompile call on layer 1. | ||
L1Precompile, | ||
/// A hint that specifies the block header of a layer 2 block. | ||
L2BlockHeader, | ||
/// A hint that specifies the transactions of a layer 2 block. | ||
L2Transactions, | ||
/// A hint that specifies the state node in the L2 state trie. | ||
L2StateNode, | ||
/// A hint that specifies the code of a contract on layer 2. | ||
L2Code, | ||
/// A hint that specifies the output root of a block on layer 2. | ||
L2Output, | ||
} | ||
|
||
impl TryFrom<&str> for HintType { | ||
type Error = anyhow::Error; | ||
|
||
fn try_from(value: &str) -> Result<Self, Self::Error> { | ||
match value { | ||
"l1-block-header" => Ok(HintType::L1BlockHeader), | ||
"l1-transactions" => Ok(HintType::L1Transactions), | ||
"l1-receipts" => Ok(HintType::L1Receipts), | ||
"l1-blob" => Ok(HintType::L1Blob), | ||
"l1-precompile" => Ok(HintType::L1Precompile), | ||
"l2-block-header" => Ok(HintType::L2BlockHeader), | ||
"l2-transactions" => Ok(HintType::L2Transactions), | ||
"l2-state-node" => Ok(HintType::L2StateNode), | ||
"l2-code" => Ok(HintType::L2Code), | ||
"l2-output" => Ok(HintType::L2Output), | ||
_ => anyhow::bail!("Invalid hint type: {value}"), | ||
} | ||
} | ||
} | ||
|
||
impl From<HintType> for &str { | ||
fn from(value: HintType) -> Self { | ||
match value { | ||
HintType::L1BlockHeader => "l1-block-header", | ||
HintType::L1Transactions => "l1-transactions", | ||
HintType::L1Receipts => "l1-receipts", | ||
HintType::L1Blob => "l1-blob", | ||
HintType::L1Precompile => "l1-precompile", | ||
HintType::L2BlockHeader => "l2-block-header", | ||
HintType::L2Transactions => "l2-transactions", | ||
HintType::L2StateNode => "l2-state-node", | ||
HintType::L2Code => "l2-code", | ||
HintType::L2Output => "l2-output", | ||
} | ||
} | ||
} | ||
|
||
impl Display for HintType { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
let s: &str = (*self).into(); | ||
write!(f, "{}", s) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
//! This module contains the [Fetcher] struct, which is responsible for fetching preimages from a | ||
//! remote source. | ||
use crate::{kv::KeyValueStore, util}; | ||
use alloy_primitives::{Bytes, B256}; | ||
use alloy_provider::{Provider, ReqwestProvider}; | ||
use anyhow::{anyhow, Result}; | ||
use kona_preimage::{PreimageKey, PreimageKeyType}; | ||
use std::sync::Arc; | ||
use tokio::sync::RwLock; | ||
use tracing::debug; | ||
|
||
mod hint; | ||
pub use hint::HintType; | ||
|
||
/// The [Fetcher] struct is responsible for fetching preimages from a remote source. | ||
pub struct Fetcher<KV> | ||
where | ||
KV: KeyValueStore, | ||
{ | ||
/// Key-value store for preimages. | ||
kv_store: Arc<RwLock<KV>>, | ||
/// L1 chain provider. | ||
l1_provider: ReqwestProvider, | ||
/// L2 chain provider. | ||
/// TODO: OP provider, N = Optimism | ||
#[allow(unused)] | ||
l2_provider: ReqwestProvider, | ||
/// The last hint that was received. [None] if no hint has been received yet. | ||
last_hint: Option<String>, | ||
} | ||
|
||
impl<KV> Fetcher<KV> | ||
where | ||
KV: KeyValueStore, | ||
{ | ||
/// Create a new [Fetcher] with the given [KeyValueStore]. | ||
pub fn new( | ||
kv_store: Arc<RwLock<KV>>, | ||
l1_provider: ReqwestProvider, | ||
l2_provider: ReqwestProvider, | ||
) -> Self { | ||
Self { kv_store, l1_provider, l2_provider, last_hint: None } | ||
} | ||
|
||
/// Set the last hint to be received. | ||
pub fn hint(&mut self, hint: &str) { | ||
debug!(target: "fetcher", "Received hint: {hint}"); | ||
self.last_hint = Some(hint.to_string()); | ||
} | ||
|
||
/// Get the preimage for the given key. | ||
pub async fn get_preimage(&self, key: B256) -> Result<Vec<u8>> { | ||
debug!(target: "fetcher", "Pre-image requested. Key: {key}"); | ||
|
||
// Acquire a read lock on the key-value store. | ||
let kv_lock = self.kv_store.read().await; | ||
let mut preimage = kv_lock.get(key).cloned(); | ||
|
||
// Drop the read lock before beginning the loop. | ||
drop(kv_lock); | ||
|
||
// Use a loop to keep retrying the prefetch as long as the key is not found | ||
while preimage.is_none() && self.last_hint.is_some() { | ||
let hint = self.last_hint.as_ref().expect("Cannot be None"); | ||
self.prefetch(hint).await?; | ||
|
||
let kv_lock = self.kv_store.read().await; | ||
preimage = kv_lock.get(key).cloned(); | ||
} | ||
|
||
preimage.ok_or_else(|| anyhow!("Preimage not found.")) | ||
} | ||
|
||
/// Fetch the preimage for the given hint and insert it into the key-value store. | ||
async fn prefetch(&self, hint: &str) -> Result<()> { | ||
let (hint_type, hint_data) = util::parse_hint(hint)?; | ||
debug!(target: "fetcher", "Fetching hint: {hint_type} {hint_data}"); | ||
|
||
match hint_type { | ||
HintType::L1BlockHeader => { | ||
// Validate the hint data length. | ||
if hint_data.len() != 32 { | ||
anyhow::bail!("Invalid hint data length: {}", hint_data.len()); | ||
} | ||
|
||
// Fetch the raw header from the L1 chain provider. | ||
let hash: B256 = hint_data | ||
.as_ref() | ||
.try_into() | ||
.map_err(|e| anyhow!("Failed to convert bytes to B256: {e}"))?; | ||
let raw_header: Bytes = self | ||
.l1_provider | ||
.client() | ||
.request("debug_getRawHeader", [hash]) | ||
.await | ||
.map_err(|e| anyhow!(e))?; | ||
|
||
// Acquire a lock on the key-value store and set the preimage. | ||
let mut kv_lock = self.kv_store.write().await; | ||
kv_lock.set( | ||
PreimageKey::new(*hash, PreimageKeyType::Keccak256).into(), | ||
raw_header.into(), | ||
); | ||
} | ||
HintType::L1Transactions => todo!(), | ||
HintType::L1Receipts => todo!(), | ||
HintType::L1Blob => todo!(), | ||
HintType::L1Precompile => todo!(), | ||
HintType::L2BlockHeader => todo!(), | ||
HintType::L2Transactions => todo!(), | ||
HintType::L2StateNode => todo!(), | ||
HintType::L2Code => todo!(), | ||
HintType::L2Output => todo!(), | ||
} | ||
|
||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
//! Contains a concrete implementation of the [KeyValueStore] trait that stores data in memory. | ||
use alloy_primitives::B256; | ||
|
||
use super::KeyValueStore; | ||
use std::collections::HashMap; | ||
|
||
/// A simple, synchronous key-value store that stores data in memory. This is useful for testing and | ||
/// development purposes. | ||
#[derive(Default, Clone, Debug, Eq, PartialEq)] | ||
pub struct MemoryKeyValueStore { | ||
store: HashMap<B256, Vec<u8>>, | ||
} | ||
|
||
impl MemoryKeyValueStore { | ||
/// Create a new [MemoryKeyValueStore] with an empty store. | ||
pub fn new() -> Self { | ||
Self { store: HashMap::new() } | ||
} | ||
} | ||
|
||
impl KeyValueStore for MemoryKeyValueStore { | ||
fn get(&self, key: B256) -> Option<&Vec<u8>> { | ||
self.store.get(&key) | ||
} | ||
|
||
fn set(&mut self, key: B256, value: Vec<u8>) { | ||
self.store.insert(key, value); | ||
} | ||
} |
Oops, something went wrong.