Skip to content

Commit

Permalink
feat(host): More hint routes
Browse files Browse the repository at this point in the history
Implements several more hint routes in the host program
  • Loading branch information
clabby committed Jun 14, 2024
1 parent 5d327d2 commit 983ce1d
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 14 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.

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

0 comments on commit 983ce1d

Please sign in to comment.