Skip to content

Commit

Permalink
feat(l1_provider): add L1ProviderContent
Browse files Browse the repository at this point in the history
Follows the same pattern and API style as `MempoolContent`.
  • Loading branch information
Gilad Chase committed Nov 20, 2024
1 parent 66bd6f0 commit cd9ed57
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 28 deletions.
3 changes: 3 additions & 0 deletions crates/starknet_l1_provider/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,8 @@ assert_matches.workspace = true
pretty_assertions.workspace = true
starknet_api = { workspace = true, features = ["testing"] }

[features]
testing = []

[lints]
workspace = true
41 changes: 13 additions & 28 deletions crates/starknet_l1_provider/src/l1_provider_tests.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
use assert_matches::assert_matches;
use indexmap::IndexMap;
use pretty_assertions::assert_eq;
use starknet_api::executable_transaction::L1HandlerTransaction;
use starknet_api::hash::StarkHash;
use starknet_api::l1_handler_tx_args;
use starknet_api::test_utils::l1_handler::executable_l1_handler_tx;
use starknet_api::transaction::TransactionHash;

use crate::errors::L1ProviderError;
use crate::test_utils::L1ProviderContentBuilder;
use crate::L1Provider;
use crate::ProviderState::{Propose, Validate};
use crate::{L1Provider, TransactionManager};

macro_rules! tx {
(tx_hash: $tx_hash:expr) => {{
Expand All @@ -21,41 +20,27 @@ macro_rules! tx {
}};
}

// TODO: change to something more robust once we have more tests.
fn l1_provider(txs: Vec<L1HandlerTransaction>) -> L1Provider {
let n_txs = txs.len();
let awaiting_l2_inclusion: IndexMap<_, _> =
txs.into_iter().map(|tx| (tx.tx_hash, tx)).collect();
assert_eq!(
awaiting_l2_inclusion.len(),
n_txs,
"Transactions given to this constructor should have unique hashes."
);

L1Provider {
tx_manager: TransactionManager { txs: awaiting_l2_inclusion, ..Default::default() },
..Default::default()
}
}

#[test]
fn get_txs_happy_flow() {
// Setup.
let txs = vec![tx!(tx_hash: 0), tx!(tx_hash: 1), tx!(tx_hash: 2)];
let mut l1_provider = l1_provider(txs.clone());
l1_provider.proposal_start().unwrap();
let txs = [tx!(tx_hash: 0), tx!(tx_hash: 1), tx!(tx_hash: 2)];
let mut l1_provider = L1ProviderContentBuilder::new()
.with_txs(txs.clone())
.with_state(Propose)
.build_into_l1_provider();

// Test.
assert_eq!(l1_provider.get_txs(0).unwrap(), vec![]);
assert_eq!(l1_provider.get_txs(1).unwrap(), vec![txs[0].clone()]);
assert_eq!(l1_provider.get_txs(3).unwrap(), txs[1..=2].to_vec());
assert_eq!(l1_provider.get_txs(1).unwrap(), vec![]);
assert_eq!(l1_provider.get_txs(0).unwrap(), []);
assert_eq!(l1_provider.get_txs(1).unwrap(), [txs[0].clone()]);
assert_eq!(l1_provider.get_txs(3).unwrap(), txs[1..=2]);
assert_eq!(l1_provider.get_txs(1).unwrap(), []);
}

#[test]
fn pending_state_errors() {
// Setup.
let mut l1_provider = l1_provider(vec![tx!(tx_hash: 0)]);
let mut l1_provider =
L1ProviderContentBuilder::new().with_txs([tx!(tx_hash: 1)]).build_into_l1_provider();

// Test.
assert_matches!(
Expand Down
3 changes: 3 additions & 0 deletions crates/starknet_l1_provider/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
pub mod errors;

#[cfg(any(feature = "testing", test))]
pub mod test_utils;

use indexmap::{IndexMap, IndexSet};
use starknet_api::executable_transaction::L1HandlerTransaction;
use starknet_api::transaction::TransactionHash;
Expand Down
121 changes: 121 additions & 0 deletions crates/starknet_l1_provider/src/test_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
use indexmap::{IndexMap, IndexSet};
use starknet_api::executable_transaction::L1HandlerTransaction;
use starknet_api::transaction::TransactionHash;

use crate::{L1Provider, ProviderState, TransactionManager};

// Represents the internal content of the L1 provider for testing.
// Enables customized (and potentially inconsistent) creation for unit testing.
#[derive(Debug, Default)]
pub struct L1ProviderContent {
tx_manager_content: Option<TransactionManagerContent>,
state: Option<ProviderState>,
}

impl From<L1ProviderContent> for L1Provider {
fn from(content: L1ProviderContent) -> L1Provider {
L1Provider {
tx_manager: content
.tx_manager_content
.map(|tm_content| tm_content.complete_to_tx_manager())
.unwrap_or_default(),
state: content.state.unwrap_or_default(),
}
}
}

#[derive(Debug, Default)]
pub struct L1ProviderContentBuilder {
tx_manager_content_builder: TransactionManagerContentBuilder,
state: Option<ProviderState>,
}

impl L1ProviderContentBuilder {
pub fn new() -> Self {
Self::default()
}

pub fn with_state(mut self, state: ProviderState) -> Self {
self.state = Some(state);
self
}

pub fn with_txs(mut self, txs: impl IntoIterator<Item = L1HandlerTransaction>) -> Self {
self.tx_manager_content_builder = self.tx_manager_content_builder.with_txs(txs);
self
}

pub fn with_on_l2_awaiting_l1_consumption(
mut self,
tx_hashes: impl IntoIterator<Item = TransactionHash>,
) -> Self {
self.tx_manager_content_builder =
self.tx_manager_content_builder.with_on_l2_awaiting_l1_consumption(tx_hashes);
self
}

pub fn build(self) -> L1ProviderContent {
L1ProviderContent {
tx_manager_content: self.tx_manager_content_builder.build(),
state: self.state,
}
}

pub fn build_into_l1_provider(self) -> L1Provider {
self.build().into()
}
}

// Represents the internal content of the TransactionManager.
// Enables customized (and potentially inconsistent) creation for unit testing.
#[derive(Debug, Default)]
struct TransactionManagerContent {
txs: Option<IndexMap<TransactionHash, L1HandlerTransaction>>,
on_l2_awaiting_l1_consumption: Option<IndexSet<TransactionHash>>,
}

impl TransactionManagerContent {
fn complete_to_tx_manager(self) -> TransactionManager {
TransactionManager {
txs: self.txs.unwrap_or_default(),
_on_l2_awaiting_l1_consumption: self.on_l2_awaiting_l1_consumption.unwrap_or_default(),
..Default::default()
}
}
}

#[derive(Debug, Default)]
struct TransactionManagerContentBuilder {
txs: Option<IndexMap<TransactionHash, L1HandlerTransaction>>,
on_l2_awaiting_l1_consumption: Option<IndexSet<TransactionHash>>,
}

impl TransactionManagerContentBuilder {
fn with_txs(mut self, txs: impl IntoIterator<Item = L1HandlerTransaction>) -> Self {
self.txs = Some(txs.into_iter().map(|tx| (tx.tx_hash, tx)).collect());
self
}

fn with_on_l2_awaiting_l1_consumption(
mut self,
tx_hashes: impl IntoIterator<Item = TransactionHash>,
) -> Self {
self.on_l2_awaiting_l1_consumption = Some(tx_hashes.into_iter().collect());
self
}

fn build(self) -> Option<TransactionManagerContent> {
if self.is_default() {
return None;
}

Some(TransactionManagerContent {
txs: self.txs,
on_l2_awaiting_l1_consumption: self.on_l2_awaiting_l1_consumption,
})
}

fn is_default(&self) -> bool {
self.txs.is_none() && self.on_l2_awaiting_l1_consumption.is_none()
}
}

0 comments on commit cd9ed57

Please sign in to comment.