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

feat(host): More hint routes #232

Merged
merged 1 commit into from
Jun 14, 2024
Merged
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
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.

1 change: 1 addition & 0 deletions bin/host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ alloy-primitives = { workspace = true, features = ["serde"] }
revm = { workspace = true, features = ["std", "c-kzg", "secp256k1", "portable", "blst"] }
alloy-eips.workspace = true
alloy-consensus.workspace = true
alloy-rlp.workspace = true

# local
kona-client = { path = "../programs/client", version = "0.1.0" }
Expand Down
8 changes: 4 additions & 4 deletions bin/host/src/fetcher/hint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ pub enum HintType {
L2Transactions,
/// 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,
/// A hint that specifies the preimage of the starting L2 output root on layer 2.
StartingL2Output,
/// A hint that specifies the state node in the L2 state trie.
L2StateNode,
/// A hint that specifies the proof on the path to an account in the L2 state trie.
Expand All @@ -45,7 +45,7 @@ impl TryFrom<&str> for HintType {
"l2-block-header" => Ok(HintType::L2BlockHeader),
"l2-transactions" => Ok(HintType::L2Transactions),
"l2-code" => Ok(HintType::L2Code),
"l2-output" => Ok(HintType::L2Output),
"l2-output" => Ok(HintType::StartingL2Output),
"l2-state-node" => Ok(HintType::L2StateNode),
"l2-account-proof" => Ok(HintType::L2AccountProof),
"l2-account-storage-proof" => Ok(HintType::L2AccountStorageProof),
Expand All @@ -65,7 +65,7 @@ impl From<HintType> for &str {
HintType::L2BlockHeader => "l2-block-header",
HintType::L2Transactions => "l2-transactions",
HintType::L2Code => "l2-code",
HintType::L2Output => "l2-output",
HintType::StartingL2Output => "starting-l2-output",
HintType::L2StateNode => "l2-state-node",
HintType::L2AccountProof => "l2-account-proof",
HintType::L2AccountStorageProof => "l2-account-storage-proof",
Expand Down
137 changes: 129 additions & 8 deletions bin/host/src/fetcher/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
//! remote source.

use crate::{kv::KeyValueStore, util};
use alloy_consensus::TxEnvelope;
use alloy_consensus::{Header, TxEnvelope};
use alloy_eips::eip2718::Encodable2718;
use alloy_primitives::{keccak256, Address, Bytes, B256};
use alloy_primitives::{address, keccak256, Address, Bytes, B256};
use alloy_provider::{Provider, ReqwestProvider};
use alloy_rlp::Decodable;
use alloy_rpc_types::{Block, BlockTransactions};
use anyhow::{anyhow, Result};
use kona_preimage::{PreimageKey, PreimageKeyType};
Expand All @@ -29,8 +30,9 @@ where
l1_provider: ReqwestProvider,
/// L2 chain provider.
/// TODO: OP provider, N = Optimism
#[allow(unused)]
l2_provider: ReqwestProvider,
/// L2 head
l2_head: B256,
/// The last hint that was received. [None] if no hint has been received yet.
last_hint: Option<String>,
}
Expand All @@ -44,8 +46,9 @@ where
kv_store: Arc<RwLock<KV>>,
l1_provider: ReqwestProvider,
l2_provider: ReqwestProvider,
l2_head: B256,
) -> Self {
Self { kv_store, l1_provider, l2_provider, last_hint: None }
Self { kv_store, l1_provider, l2_provider, l2_head, last_hint: None }
}

/// Set the last hint to be received.
Expand Down Expand Up @@ -182,11 +185,129 @@ where
kv_lock
.set(PreimageKey::new(*input_hash, PreimageKeyType::Precompile).into(), result);
}
HintType::L2BlockHeader => todo!(),
HintType::L2BlockHeader => {
// 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
.l2_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::L2Transactions => todo!(),
HintType::L2Code => todo!(),
HintType::L2Output => todo!(),
HintType::L2StateNode => todo!(),
HintType::L2Code => {
// geth hashdb scheme code hash key prefix
const CODE_PREFIX: u8 = b'c';

if hint_data.len() != 32 {
anyhow::bail!("Invalid hint data length: {}", hint_data.len());
}

let hash: B256 = hint_data
.as_ref()
.try_into()
.map_err(|e| anyhow!("Failed to convert bytes to B256: {e}"))?;

// Attempt to fetch the code from the L2 chain provider.
let code_hash = [&[CODE_PREFIX], hash.as_slice()].concat();
let code = self
.l2_provider
.client()
.request::<&[Bytes; 1], Bytes>("debug_dbGet", &[code_hash.into()])
.await;

// Check if the first attempt to fetch the code failed. If it did, try fetching the
// code hash preimage without the geth hashdb scheme prefix.
let code = match code {
Ok(code) => code,
Err(_) => self
.l2_provider
.client()
.request::<&[B256; 1], Bytes>("debug_dbGet", &[hash])
.await
.map_err(|e| anyhow!("Error fetching code hash preimage: {e}"))?,
};

let mut kv_write_lock = self.kv_store.write().await;
kv_write_lock.set(hash, code.into());
}
HintType::StartingL2Output => {
const OUTPUT_ROOT_VERSION: u8 = 0;
const L2_TO_L1_MESSAGE_PASSER_ADDRESS: Address =
address!("4200000000000000000000000000000000000016");

if !hint_data.is_empty() {
anyhow::bail!("Invalid hint data length: {}", hint_data.len());
}

// Fetch the header for the L2 head block.
let raw_header: Bytes = self
.l2_provider
.client()
.request("debug_getRawHeader", &[self.l2_head])
.await
.map_err(|e| anyhow!("Failed to fetch header RLP: {e}"))?;
let header = Header::decode(&mut raw_header.as_ref())
.map_err(|e| anyhow!("Failed to decode header: {e}"))?;

// Fetch the storage root for the L2 head block.
let l2_to_l1_message_passer = self
.l2_provider
.get_proof(
L2_TO_L1_MESSAGE_PASSER_ADDRESS,
Default::default(),
self.l2_head.into(),
)
.await
.map_err(|e| anyhow!("Failed to fetch account proof: {e}"))?;

let mut raw_output = [0u8; 97];
raw_output[0] = OUTPUT_ROOT_VERSION;
raw_output[1..33].copy_from_slice(header.state_root.as_ref());
raw_output[33..65].copy_from_slice(l2_to_l1_message_passer.storage_hash.as_ref());
raw_output[65..97].copy_from_slice(self.l2_head.as_ref());
let output_root = keccak256(raw_output);

let mut kv_write_lock = self.kv_store.write().await;
kv_write_lock.set(output_root, raw_output.into());
}
HintType::L2StateNode => {
if hint_data.len() != 32 {
anyhow::bail!("Invalid hint data length: {}", hint_data.len());
}

let hash: B256 = hint_data
.as_ref()
.try_into()
.map_err(|e| anyhow!("Failed to convert bytes to B256: {e}"))?;

// Fetch the preimage from the L2 chain provider.
let preimage: Bytes = self
.l2_provider
.client()
.request("debug_dbGet", &[hash])
.await
.map_err(|e| anyhow!("Failed to fetch preimage: {e}"))?;

let mut kv_write_lock = self.kv_store.write().await;
kv_write_lock.set(hash, preimage.into());
}
HintType::L2AccountProof => {
if hint_data.len() != 8 + 20 {
anyhow::bail!("Invalid hint data length: {}", hint_data.len());
Expand Down
4 changes: 2 additions & 2 deletions bin/host/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ async fn start_server(cfg: HostCli) -> Result<()> {
let fetcher = (!cfg.is_offline()).then(|| {
let l1_provider = util::http_provider(&cfg.l1_node_address.expect("Provider must be set"));
let l2_provider = util::http_provider(&cfg.l2_node_address.expect("Provider must be set"));
Arc::new(RwLock::new(Fetcher::new(kv_store.clone(), l1_provider, l2_provider)))
Arc::new(RwLock::new(Fetcher::new(kv_store.clone(), l1_provider, l2_provider, cfg.l2_head)))
});

// Start the server and wait for it to complete.
Expand All @@ -85,7 +85,7 @@ async fn start_server_and_native_client(cfg: HostCli) -> Result<()> {
util::http_provider(cfg.l1_node_address.as_ref().expect("Provider must be set"));
let l2_provider =
util::http_provider(cfg.l2_node_address.as_ref().expect("Provider must be set"));
Arc::new(RwLock::new(Fetcher::new(kv_store.clone(), l1_provider, l2_provider)))
Arc::new(RwLock::new(Fetcher::new(kv_store.clone(), l1_provider, l2_provider, cfg.l2_head)))
});

// Create the server and start it.
Expand Down
Loading