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(client): providers generic over oracles #336

Merged
merged 8 commits into from
Jul 23, 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
497 changes: 310 additions & 187 deletions Cargo.lock

Large diffs are not rendered by default.

11 changes: 9 additions & 2 deletions bin/client/src/comms/caching_oracle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
//!
//! [OracleReader]: kona_preimage::OracleReader

use crate::ORACLE_READER;
use crate::{HINT_WRITER, ORACLE_READER};
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use anyhow::Result;
use async_trait::async_trait;
use core::num::NonZeroUsize;
use kona_preimage::{PreimageKey, PreimageOracleClient};
use kona_preimage::{HintWriterClient, PreimageKey, PreimageOracleClient};
use lru::LruCache;
use spin::Mutex;

Expand Down Expand Up @@ -63,3 +63,10 @@ impl PreimageOracleClient for CachingOracle {
}
}
}

#[async_trait]
impl HintWriterClient for CachingOracle {
async fn write(&self, hint: &str) -> Result<()> {
HINT_WRITER.write(hint).await
}
}
12 changes: 7 additions & 5 deletions bin/client/src/kona.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use alloc::sync::Arc;
use alloy_consensus::Header;
use kona_client::{
l1::{DerivationDriver, OracleBlobProvider, OracleL1ChainProvider},
l2::{FPVMPrecompileOverride, OracleL2ChainProvider, TrieDBHintWriter},
l2::{FPVMPrecompileOverride, OracleL2ChainProvider},
BootInfo, CachingOracle,
};
use kona_common_proc::client_entry;
Expand Down Expand Up @@ -57,12 +57,14 @@ fn main() -> Result<()> {
.await?;
let L2AttributesWithParent { attributes, .. } = driver.produce_disputed_payload().await?;

let precompile_overrides =
FPVMPrecompileOverride::<OracleL2ChainProvider, TrieDBHintWriter>::default();
let precompile_overrides = FPVMPrecompileOverride::<
OracleL2ChainProvider<CachingOracle>,
OracleL2ChainProvider<CachingOracle>,
>::default();
Comment on lines +60 to +63
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@clabby I understand the logic for keeping a separate Fetcher and Hinter in the library crates, as we want to keep flexibility for future users to implement these separately.

But if we are going to have the L2 Provider fulfill both of these roles in kona-client, do you think it makes sense to refactor FPVMPrecompileOverride to just be generic over a single type that implements both traits?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this simplifies the ux and isn't clear who will need the split logic, I support doing this refactor in a follow-on pr. The FPVM Precompile Override can always be made even more abstract by encapsulating it's internal logic in a trait, so not too worried about increasing the abstractions here.

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_fetcher(l2_provider.clone())
.with_hinter(l2_provider)
.with_precompile_overrides(precompile_overrides)
.build()?;
let Header { number, .. } = *executor.execute_payload(attributes)?;
Expand Down
16 changes: 8 additions & 8 deletions bin/client/src/l1/blob_provider.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Contains the concrete implementation of the [BlobProvider] trait for the client program.

use crate::{CachingOracle, HintType, HINT_WRITER};
use crate::HintType;
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use alloy_consensus::Blob;
use alloy_eips::eip4844::FIELD_ELEMENTS_PER_BLOB;
Expand All @@ -10,18 +10,18 @@ use kona_derive::{
traits::BlobProvider,
types::{BlobProviderError, IndexedBlobHash},
};
use kona_preimage::{HintWriterClient, PreimageKey, PreimageKeyType, PreimageOracleClient};
use kona_preimage::{CommsClient, PreimageKey, PreimageKeyType};
use kona_primitives::BlockInfo;

/// An oracle-backed blob provider.
#[derive(Debug, Clone)]
pub struct OracleBlobProvider {
oracle: Arc<CachingOracle>,
pub struct OracleBlobProvider<T: CommsClient> {
oracle: Arc<T>,
}

impl OracleBlobProvider {
impl<T: CommsClient> OracleBlobProvider<T> {
/// Constructs a new `OracleBlobProvider`.
pub fn new(oracle: Arc<CachingOracle>) -> Self {
pub fn new(oracle: Arc<T>) -> Self {
Self { oracle }
}

Expand All @@ -45,7 +45,7 @@ impl OracleBlobProvider {
blob_req_meta[40..48].copy_from_slice(block_ref.timestamp.to_be_bytes().as_ref());

// Send a hint for the blob commitment and field elements.
HINT_WRITER.write(&HintType::L1Blob.encode_with(&[blob_req_meta.as_ref()])).await?;
self.oracle.write(&HintType::L1Blob.encode_with(&[blob_req_meta.as_ref()])).await?;

// Fetch the blob commitment.
let mut commitment = [0u8; 48];
Expand Down Expand Up @@ -77,7 +77,7 @@ impl OracleBlobProvider {
}

#[async_trait]
impl BlobProvider for OracleBlobProvider {
impl<T: CommsClient + Sync + Send> BlobProvider for OracleBlobProvider<T> {
async fn get_blobs(
&mut self,
block_ref: &BlockInfo,
Expand Down
22 changes: 11 additions & 11 deletions bin/client/src/l1/chain_provider.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Contains the concrete implementation of the [ChainProvider] trait for the client program.

use crate::{BootInfo, CachingOracle, HintType, HINT_WRITER};
use crate::{BootInfo, HintType};
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use alloy_consensus::{Header, Receipt, ReceiptEnvelope, TxEnvelope};
use alloy_eips::eip2718::Decodable2718;
Expand All @@ -10,30 +10,30 @@ use anyhow::{anyhow, Result};
use async_trait::async_trait;
use kona_derive::traits::ChainProvider;
use kona_mpt::{OrderedListWalker, TrieDBFetcher};
use kona_preimage::{HintWriterClient, PreimageKey, PreimageKeyType, PreimageOracleClient};
use kona_preimage::{CommsClient, PreimageKey, PreimageKeyType};
use kona_primitives::BlockInfo;

/// The oracle-backed L1 chain provider for the client program.
#[derive(Debug, Clone)]
pub struct OracleL1ChainProvider {
pub struct OracleL1ChainProvider<T: CommsClient> {
/// The boot information
boot_info: Arc<BootInfo>,
/// The preimage oracle client.
oracle: Arc<CachingOracle>,
pub oracle: Arc<T>,
}

impl OracleL1ChainProvider {
impl<T: CommsClient> OracleL1ChainProvider<T> {
/// Creates a new [OracleL1ChainProvider] with the given boot information and oracle client.
pub fn new(boot_info: Arc<BootInfo>, oracle: Arc<CachingOracle>) -> Self {
pub fn new(boot_info: Arc<BootInfo>, oracle: Arc<T>) -> Self {
Self { boot_info, oracle }
}
}

#[async_trait]
impl ChainProvider for OracleL1ChainProvider {
impl<T: CommsClient + Sync + Send> ChainProvider for OracleL1ChainProvider<T> {
async fn header_by_hash(&mut self, hash: B256) -> Result<Header> {
// Send a hint for the block header.
HINT_WRITER.write(&HintType::L1BlockHeader.encode_with(&[hash.as_ref()])).await?;
self.oracle.write(&HintType::L1BlockHeader.encode_with(&[hash.as_ref()])).await?;

// Fetch the header RLP from the oracle.
let header_rlp =
Expand Down Expand Up @@ -72,7 +72,7 @@ impl ChainProvider for OracleL1ChainProvider {

// Send a hint for the block's receipts, and walk through the receipts trie in the header to
// verify them.
HINT_WRITER.write(&HintType::L1Receipts.encode_with(&[hash.as_ref()])).await?;
self.oracle.write(&HintType::L1Receipts.encode_with(&[hash.as_ref()])).await?;
let trie_walker = OrderedListWalker::try_new_hydrated(header.receipts_root, self)?;

// Decode the receipts within the transactions trie.
Expand Down Expand Up @@ -103,7 +103,7 @@ impl ChainProvider for OracleL1ChainProvider {

// Send a hint for the block's transactions, and walk through the transactions trie in the
// header to verify them.
HINT_WRITER.write(&HintType::L1Transactions.encode_with(&[hash.as_ref()])).await?;
self.oracle.write(&HintType::L1Transactions.encode_with(&[hash.as_ref()])).await?;
let trie_walker = OrderedListWalker::try_new_hydrated(header.transactions_root, self)?;

// Decode the transactions within the transactions trie.
Expand All @@ -119,7 +119,7 @@ impl ChainProvider for OracleL1ChainProvider {
}
}

impl TrieDBFetcher for OracleL1ChainProvider {
impl<T: CommsClient> TrieDBFetcher for OracleL1ChainProvider<T> {
fn trie_node_preimage(&self, key: B256) -> Result<Bytes> {
// On L1, trie node preimages are stored as keccak preimage types in the oracle. We assume
// that a hint for these preimages has already been sent, prior to this call.
Expand Down
45 changes: 23 additions & 22 deletions bin/client/src/l1/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! [L2PayloadAttributes]: kona_derive::types::L2PayloadAttributes

use super::{OracleBlobProvider, OracleL1ChainProvider};
use crate::{l2::OracleL2ChainProvider, BootInfo, CachingOracle, HintType, HINT_WRITER};
use crate::{l2::OracleL2ChainProvider, BootInfo, HintType};
use alloc::sync::Arc;
use alloy_consensus::{Header, Sealed};
use anyhow::{anyhow, Result};
Expand All @@ -19,31 +19,32 @@ use kona_derive::{
traits::{ChainProvider, L2ChainProvider},
};
use kona_mpt::TrieDBFetcher;
use kona_preimage::{HintWriterClient, PreimageKey, PreimageKeyType, PreimageOracleClient};
use kona_preimage::{CommsClient, PreimageKey, PreimageKeyType};
use kona_primitives::{BlockInfo, L2AttributesWithParent, L2BlockInfo};
use tracing::{info, warn};

/// An oracle-backed derivation pipeline.
pub type OraclePipeline =
DerivationPipeline<OracleAttributesQueue<OracleDataProvider>, OracleL2ChainProvider>;
pub type OraclePipeline<O> =
DerivationPipeline<OracleAttributesQueue<OracleDataProvider<O>, O>, OracleL2ChainProvider<O>>;

/// An oracle-backed Ethereum data source.
pub type OracleDataProvider = EthereumDataSource<OracleL1ChainProvider, OracleBlobProvider>;
pub type OracleDataProvider<O> =
EthereumDataSource<OracleL1ChainProvider<O>, OracleBlobProvider<O>>;

/// An oracle-backed payload attributes builder for the `AttributesQueue` stage of the derivation
/// pipeline.
pub type OracleAttributesBuilder =
StatefulAttributesBuilder<OracleL1ChainProvider, OracleL2ChainProvider>;
pub type OracleAttributesBuilder<O> =
StatefulAttributesBuilder<OracleL1ChainProvider<O>, OracleL2ChainProvider<O>>;

/// An oracle-backed attributes queue for the derivation pipeline.
pub type OracleAttributesQueue<DAP> = AttributesQueue<
pub type OracleAttributesQueue<DAP, O> = AttributesQueue<
BatchQueue<
ChannelReader<
ChannelBank<FrameQueue<L1Retrieval<DAP, L1Traversal<OracleL1ChainProvider>>>>,
ChannelBank<FrameQueue<L1Retrieval<DAP, L1Traversal<OracleL1ChainProvider<O>>>>>,
>,
OracleL2ChainProvider,
OracleL2ChainProvider<O>,
>,
OracleAttributesBuilder,
OracleAttributesBuilder<O>,
>;

/// The [DerivationDriver] struct is responsible for handling the [L2PayloadAttributes] derivation
Expand All @@ -54,16 +55,16 @@ pub type OracleAttributesQueue<DAP> = AttributesQueue<
///
/// [L2PayloadAttributes]: kona_derive::types::L2PayloadAttributes
#[derive(Debug)]
pub struct DerivationDriver {
pub struct DerivationDriver<O: CommsClient + Send + Sync + Debug> {
/// The current L2 safe head.
l2_safe_head: L2BlockInfo,
/// The header of the L2 safe head.
l2_safe_head_header: Sealed<Header>,
/// The inner pipeline.
pipeline: OraclePipeline,
pipeline: OraclePipeline<O>,
}

impl DerivationDriver {
impl<O: CommsClient + Send + Sync + Debug> DerivationDriver<O> {
/// Returns the current L2 safe head [L2BlockInfo].
pub fn l2_safe_head(&self) -> &L2BlockInfo {
&self.l2_safe_head
Expand Down Expand Up @@ -92,10 +93,10 @@ impl DerivationDriver {
/// - A new [DerivationDriver] instance.
pub async fn new(
boot_info: &BootInfo,
caching_oracle: &CachingOracle,
blob_provider: OracleBlobProvider,
mut chain_provider: OracleL1ChainProvider,
mut l2_chain_provider: OracleL2ChainProvider,
caching_oracle: &O,
blob_provider: OracleBlobProvider<O>,
mut chain_provider: OracleL1ChainProvider<O>,
mut l2_chain_provider: OracleL2ChainProvider<O>,
) -> Result<Self> {
let cfg = Arc::new(boot_info.rollup_config.clone());

Expand Down Expand Up @@ -159,13 +160,13 @@ impl DerivationDriver {
/// ## Returns
/// - A tuple containing the L1 origin block information and the L2 safe head information.
async fn find_startup_info(
caching_oracle: &CachingOracle,
caching_oracle: &O,
boot_info: &BootInfo,
chain_provider: &mut OracleL1ChainProvider,
l2_chain_provider: &mut OracleL2ChainProvider,
chain_provider: &mut OracleL1ChainProvider<O>,
l2_chain_provider: &mut OracleL2ChainProvider<O>,
) -> Result<(BlockInfo, L2BlockInfo, Sealed<Header>)> {
// Find the initial safe head, based off of the starting L2 block number in the boot info.
HINT_WRITER
caching_oracle
.write(&HintType::StartingL2Output.encode_with(&[boot_info.l2_output_root.as_ref()]))
.await?;
let mut output_preimage = [0u8; 128];
Expand Down
Loading
Loading