diff --git a/Cargo.lock b/Cargo.lock index d3bea91a1f..8b7dd9f744 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10328,6 +10328,7 @@ dependencies = [ name = "starknet_l1_provider" version = "0.0.0" dependencies = [ + "assert_matches", "papyrus_base_layer", "starknet_api", "thiserror", diff --git a/crates/starknet_l1_provider/Cargo.toml b/crates/starknet_l1_provider/Cargo.toml index f9ac23e999..2340234fcb 100644 --- a/crates/starknet_l1_provider/Cargo.toml +++ b/crates/starknet_l1_provider/Cargo.toml @@ -10,5 +10,8 @@ papyrus_base_layer.workspace = true starknet_api.workspace = true thiserror.workspace = true +[dev-dependencies] +assert_matches.workspace = true + [lints] workspace = true diff --git a/crates/starknet_l1_provider/src/errors.rs b/crates/starknet_l1_provider/src/errors.rs index 487dc62696..22dc68d22b 100644 --- a/crates/starknet_l1_provider/src/errors.rs +++ b/crates/starknet_l1_provider/src/errors.rs @@ -1,6 +1,8 @@ use papyrus_base_layer::ethereum_base_layer_contract::EthereumBaseLayerError; use thiserror::Error; +use crate::ProviderState; + #[derive(Error, Debug)] pub enum L1ProviderError { #[error(transparent)] @@ -11,8 +13,8 @@ pub enum L1ProviderError { GetTransactionsInPendingState, #[error("`get_txs` while in validate state")] GetTransactionConsensusBug, - #[error("Can not set state: {0}")] - UnexpectedState(String), + #[error("Cannot transition from {from} to {to}")] + UnexpectedProviderStateTransition { from: ProviderState, to: ProviderState }, #[error("`validate_tx` called while in proposal state")] ValidateTransactionConsensusBug, } diff --git a/crates/starknet_l1_provider/src/l1_provider_tests.rs b/crates/starknet_l1_provider/src/l1_provider_tests.rs new file mode 100644 index 0000000000..be8dcbc608 --- /dev/null +++ b/crates/starknet_l1_provider/src/l1_provider_tests.rs @@ -0,0 +1,20 @@ +use assert_matches::assert_matches; + +use crate::errors::L1ProviderError::UnexpectedProviderStateTransition; +use crate::{L1Provider, ProviderState}; + +#[test] +fn proposal_start_errors() { + // Setup. + let mut l1_provider = L1Provider::default(); + + // Test. + l1_provider.proposal_start().unwrap(); + assert_matches!( + l1_provider.proposal_start().unwrap_err(), + UnexpectedProviderStateTransition { + from: ProviderState::Propose, + to: ProviderState::Propose + } + ); +} diff --git a/crates/starknet_l1_provider/src/lib.rs b/crates/starknet_l1_provider/src/lib.rs index 10fda6bf7c..557402c48e 100644 --- a/crates/starknet_l1_provider/src/lib.rs +++ b/crates/starknet_l1_provider/src/lib.rs @@ -1,5 +1,7 @@ pub mod errors; +use std::fmt::Display; + use starknet_api::executable_transaction::L1HandlerTransaction; use starknet_api::transaction::TransactionHash; @@ -7,8 +9,13 @@ use crate::errors::L1ProviderError; type L1ProviderResult = Result; +#[cfg(test)] +#[path = "l1_provider_tests.rs"] +pub mod l1_provider_tests; + // TODO: optimistic proposer support, will add later to keep things simple, but the design here // is compatible with it. +#[derive(Debug, Default)] pub struct L1Provider { unconsumed_l1_not_in_l2_block_txs: PendingMessagesFromL1, state: ProviderState, @@ -53,8 +60,9 @@ impl L1Provider { todo!("Sets internal state as validate, returns error if state is Pending.") } - pub fn proposal_start(&mut self) { - todo!("Similar to validation_start.") + pub fn proposal_start(&mut self) -> L1ProviderResult<()> { + self.state = self.state.transition_to_propose()?; + Ok(()) } /// Simple recovery from L1 and L2 reorgs by reseting the service, which rewinds L1 and L2 @@ -81,6 +89,7 @@ impl L1Provider { } } +#[derive(Debug, Default)] struct PendingMessagesFromL1; impl PendingMessagesFromL1 { @@ -90,7 +99,7 @@ impl PendingMessagesFromL1 { } /// Current state of the provider, where pending means: idle, between proposal/validation cycles. -#[derive(Debug, Default)] +#[derive(Clone, Copy, Debug, Default)] pub enum ProviderState { #[default] Pending, @@ -99,8 +108,14 @@ pub enum ProviderState { } impl ProviderState { - fn _transition_to_propose(self) -> L1ProviderResult { - todo!() + fn transition_to_propose(self) -> L1ProviderResult { + match self { + ProviderState::Pending => Ok(ProviderState::Propose), + _ => Err(L1ProviderError::UnexpectedProviderStateTransition { + from: self, + to: ProviderState::Propose, + }), + } } fn _transition_to_validate(self) -> L1ProviderResult { @@ -110,6 +125,21 @@ impl ProviderState { fn _transition_to_pending(self) -> L1ProviderResult { todo!() } + + pub fn as_str(&self) -> &str { + match self { + ProviderState::Pending => "Pending", + ProviderState::Propose => "Propose", + ProviderState::Validate => "Validate", + } + } +} + +impl Display for ProviderState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.as_str()) + } } +#[derive(Debug)] pub struct L1ProviderConfig; diff --git a/crates/starknet_mempool/src/mempool.rs b/crates/starknet_mempool/src/mempool.rs index 22758b1b02..13899acd45 100644 --- a/crates/starknet_mempool/src/mempool.rs +++ b/crates/starknet_mempool/src/mempool.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::fmt::Display; use starknet_api::block::GasPrice; use starknet_api::core::{ContractAddress, Nonce}; @@ -355,7 +356,7 @@ impl TransactionReference { } } -impl std::fmt::Display for TransactionReference { +impl Display for TransactionReference { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let TransactionReference { address, nonce, tx_hash, tip, max_l2_gas_price } = self; write!(