Skip to content

Commit

Permalink
chore(starknet_l1_provider): Getting things ready for the client
Browse files Browse the repository at this point in the history
Adding provider-side boilerplate for provider-batcher communication
  • Loading branch information
Gilad Chase committed Dec 12, 2024
1 parent 203fb99 commit 235c792
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 12 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

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

8 changes: 8 additions & 0 deletions crates/starknet_l1_provider/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,20 @@ edition.workspace = true
repository.workspace = true
license.workspace = true

[features]
testing = []

[dependencies]
async-trait.workspace = true
indexmap.workspace = true
papyrus_base_layer.workspace = true
serde.workspace = true
starknet_api.workspace = true
starknet_l1_provider_types.workspace = true
starknet_sequencer_infra.workspace = true
thiserror.workspace = true
tracing.workspace = true
validator.workspace = true

[dev-dependencies]
assert_matches.workspace = true
Expand Down
31 changes: 31 additions & 0 deletions crates/starknet_l1_provider/src/communication.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use async_trait::async_trait;
use starknet_l1_provider_types::{L1ProviderRequest, L1ProviderResponse};
use starknet_sequencer_infra::component_client::{LocalComponentClient, RemoteComponentClient};
use starknet_sequencer_infra::component_definitions::{
ComponentRequestAndResponseSender,
ComponentRequestHandler,
};
use starknet_sequencer_infra::component_server::{LocalComponentServer, RemoteComponentServer};
use tracing::instrument;

use crate::L1Provider;

pub type LocalL1ProviderServer =
LocalComponentServer<L1Provider, L1ProviderRequest, L1ProviderResponse>;
pub type RemoteL1ProviderServer = RemoteComponentServer<L1ProviderRequest, L1ProviderResponse>;
pub type L1ProviderRequestAndResponseSender =
ComponentRequestAndResponseSender<L1ProviderRequest, L1ProviderResponse>;
pub type LocalL1ProviderClient = LocalComponentClient<L1ProviderRequest, L1ProviderResponse>;
pub type RemoteL1ProviderClient = RemoteComponentClient<L1ProviderRequest, L1ProviderResponse>;

#[async_trait]
impl ComponentRequestHandler<L1ProviderRequest, L1ProviderResponse> for L1Provider {
#[instrument(skip(self))]
async fn handle_request(&mut self, request: L1ProviderRequest) -> L1ProviderResponse {
match request {
L1ProviderRequest::GetTransactions(n_txs) => {
L1ProviderResponse::GetTransactions(self.get_txs(n_txs))
}
}
}
}
30 changes: 19 additions & 11 deletions crates/starknet_l1_provider/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
pub mod communication;
pub mod errors;

#[cfg(test)]
pub mod test_utils;

use indexmap::{IndexMap, IndexSet};
use serde::{Deserialize, Serialize};
use starknet_api::executable_transaction::L1HandlerTransaction;
use starknet_api::transaction::TransactionHash;
use starknet_l1_provider_types::errors::L1ProviderError;
use starknet_l1_provider_types::{L1ProviderResult, ValidationStatus};
use starknet_sequencer_infra::component_definitions::ComponentStarter;
use validator::Validate;

#[cfg(test)]
#[path = "l1_provider_tests.rs"]
Expand All @@ -24,11 +28,8 @@ pub struct L1Provider {
}

impl L1Provider {
pub async fn new(_config: L1ProviderConfig) -> L1ProviderResult<Self> {
todo!(
"init crawler to start next crawl from ~1 hour ago, this can have l1 errors when \
finding the latest block on L1 to 'subtract' 1 hour from."
);
pub fn new(_config: L1ProviderConfig) -> L1ProviderResult<Self> {
todo!("Init crawler in uninitialized_state from config, to initialize call `reset`.");
}

/// Retrieves up to `n_txs` transactions that have yet to be proposed or accepted on L2.
Expand Down Expand Up @@ -75,8 +76,8 @@ impl L1Provider {

/// Simple recovery from L1 and L2 reorgs by reseting the service, which rewinds L1 and L2
/// information.
pub fn handle_reorg(&mut self) -> L1ProviderResult<()> {
self.reset()
pub async fn handle_reorg(&mut self) -> L1ProviderResult<()> {
self.reset().await
}

// TODO: this will likely change during integration with infra team.
Expand All @@ -88,15 +89,18 @@ impl L1Provider {
)
}

fn reset(&mut self) -> L1ProviderResult<()> {
pub async fn reset(&mut self) -> L1ProviderResult<()> {
todo!(
"resets internal buffers and rewinds the internal crawler _pointer_ back for ~1 \
hour,so that the main loop will start collecting from that time gracefully. May hit \
base layer errors."
base layer errors when finding the latest block on l1 to 'subtract' 1 hour from. \
Then, transition to Pending."
);
}
}

impl ComponentStarter for L1Provider {}

#[derive(Debug, Default)]
struct TransactionManager {
txs: IndexMap<TransactionHash, L1HandlerTransaction>,
Expand Down Expand Up @@ -173,8 +177,8 @@ impl ProviderState {
match self {
ProviderState::Pending => "Pending",
ProviderState::Propose => "Propose",
ProviderState::Uninitialized => "Uninitialized",
ProviderState::Validate => "Validate",
ProviderState::Uninitialized => "Validate",
}
}
}
Expand All @@ -185,5 +189,9 @@ impl std::fmt::Display for ProviderState {
}
}

#[derive(Debug)]
#[derive(Clone, Debug, Default, Serialize, Deserialize, Validate, PartialEq)]
pub struct L1ProviderConfig;

pub fn create_l1_provider(_config: L1ProviderConfig) -> L1Provider {
L1Provider { state: ProviderState::Propose, ..Default::default() }
}
12 changes: 12 additions & 0 deletions crates/starknet_l1_provider_types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,21 @@ edition.workspace = true
repository.workspace = true
license.workspace = true

[features]
testing = ["mockall"]

[dependencies]
async-trait.workspace = true
mockall = { workspace = true, optional = true }
papyrus_proc_macros.workspace = true
serde.workspace = true
starknet_api.workspace = true
starknet_sequencer_infra.workspace = true
thiserror.workspace = true
tracing.workspace = true

[dev-dependencies]
mockall.workspace = true

[lints]
workspace = true
9 changes: 9 additions & 0 deletions crates/starknet_l1_provider_types/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::fmt::Debug;

use serde::{Deserialize, Serialize};
use starknet_sequencer_infra::component_client::ClientError;
use thiserror::Error;

#[derive(Clone, Debug, Error, PartialEq, Eq, Serialize, Deserialize)]
Expand All @@ -26,3 +27,11 @@ impl L1ProviderError {
Self::UnexpectedProviderStateTransition { from: from.to_string(), to: to.to_string() }
}
}

#[derive(Clone, Debug, Error)]
pub enum L1ProviderClientError {
#[error(transparent)]
ClientError(#[from] ClientError),
#[error(transparent)]
L1ProviderError(#[from] L1ProviderError),
}
61 changes: 60 additions & 1 deletion crates/starknet_l1_provider_types/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,71 @@
pub mod errors;

use crate::errors::L1ProviderError;
use std::sync::Arc;

use async_trait::async_trait;
#[cfg(any(feature = "testing", test))]
use mockall::automock;
use papyrus_proc_macros::handle_response_variants;
use serde::{Deserialize, Serialize};
use starknet_api::executable_transaction::L1HandlerTransaction;
use starknet_api::transaction::TransactionHash;
use starknet_sequencer_infra::component_client::ClientError;
use starknet_sequencer_infra::component_definitions::ComponentClient;
use tracing::instrument;

use crate::errors::{L1ProviderClientError, L1ProviderError};

pub type L1ProviderResult<T> = Result<T, L1ProviderError>;
pub type L1ProviderClientResult<T> = Result<T, L1ProviderClientError>;
pub type SharedL1ProviderClient = Arc<dyn L1ProviderClient>;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ValidationStatus {
Validated,
AlreadyIncludedOnL2,
ConsumedOnL1OrUnknown,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum L1ProviderRequest {
GetTransactions(usize),
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum L1ProviderResponse {
GetTransactions(L1ProviderResult<Vec<L1HandlerTransaction>>),
}

/// Serves as the mempool's shared interface. Requires `Send + Sync` to allow transferring and
/// sharing resources (inputs, futures) across threads.
#[cfg_attr(any(feature = "testing", test), automock)]
#[async_trait]
pub trait L1ProviderClient: Send + Sync {
async fn get_txs(&self, n_txs: usize) -> L1ProviderClientResult<Vec<L1HandlerTransaction>>;
async fn validate(&self, _tx_hash: TransactionHash)
-> L1ProviderClientResult<ValidationStatus>;
}

#[async_trait]
impl<ComponentClientType> L1ProviderClient for ComponentClientType
where
ComponentClientType: Send + Sync + ComponentClient<L1ProviderRequest, L1ProviderResponse>,
{
#[instrument(skip(self))]
async fn get_txs(&self, n_txs: usize) -> L1ProviderClientResult<Vec<L1HandlerTransaction>> {
let request = L1ProviderRequest::GetTransactions(n_txs);
let response = self.send(request).await;
handle_response_variants!(
L1ProviderResponse,
GetTransactions,
L1ProviderClientError,
L1ProviderError
)
}
async fn validate(
&self,
_tx_hash: TransactionHash,
) -> L1ProviderClientResult<ValidationStatus> {
todo!();
}
}

0 comments on commit 235c792

Please sign in to comment.