Skip to content

Commit

Permalink
chore(starknet_l1_provider): add l1 scraper test
Browse files Browse the repository at this point in the history
  • Loading branch information
Gilad Chase committed Dec 24, 2024
1 parent ef44633 commit f750116
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 5 deletions.
5 changes: 0 additions & 5 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ license-file = "LICENSE"
alloy-contract = "0.3.5"
alloy-dyn-abi = "0.8.3"
alloy-json-rpc = "0.3.5"
alloy-node-bindings = "0.8.3"
alloy-primitives = "0.8.3"
alloy-provider = "0.3.5"
alloy-rpc-types-eth = "0.3.5"
Expand Down
3 changes: 3 additions & 0 deletions crates/starknet_l1_provider/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ tracing.workspace = true
validator.workspace = true

[dev-dependencies]
alloy-node-bindings.workspace = true
alloy-primitives.workspace = true
assert_matches.workspace = true
pretty_assertions.workspace = true
rstest.workspace = true
starknet_api = { workspace = true, features = ["testing"] }

[lints]
Expand Down
152 changes: 152 additions & 0 deletions crates/starknet_l1_provider/src/l1_scraper_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
use std::sync::Arc;

use alloy_node_bindings::{Anvil, AnvilInstance};
use alloy_primitives::U256;
use papyrus_base_layer::ethereum_base_layer_contract::{
EthereumBaseLayerConfig,
EthereumBaseLayerContract,
Starknet,
};
use rstest::{fixture, rstest};
use starknet_api::core::{EntryPointSelector, Nonce};
use starknet_api::executable_transaction::L1HandlerTransaction as ExecutableL1HandlerTransaction;
use starknet_api::hash::StarkHash;
use starknet_api::transaction::fields::Fee;
use starknet_api::transaction::{L1HandlerTransaction, TransactionHasher, TransactionVersion};
use starknet_api::{calldata, contract_address, felt};
use starknet_l1_provider_types::Event;

use crate::event_identifiers_to_track;
use crate::l1_scraper::L1Scraper;
use crate::test_utils::FakeL1ProviderClient;

// TODO: move to global test_utils crate and use everywhere instead of relying on the
// confusing `#[ignore]` api to mark slow tests.
// fn in_ci() -> bool {
// std::env::var("CI").is_ok()
// }

// Default funded account, there are more fixed funded accounts,
// see https://github.com/foundry-rs/foundry/tree/master/crates/anvil.
const DEFAULT_ANVIL_ACCOUNT_ADDRESS: &str = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";
const DEFAULT_ANVIL_DEPLOY_ADDRESS: &str = "0x5fbdb2315678afecb367f032d93f642f64180aa3";

#[fixture]
// Spin up Anvil instance, a local Ethereum node, dies when dropped.
fn anvil() -> AnvilInstance {
Anvil::new().spawn()
}

// TODO: Replace EthereumBaseLayerContract with a mock that has a provider initialized with
// `with_recommended_fillers`, in order to be able to create txs from non-default users.
async fn scraper(
anvil: &AnvilInstance,
) -> (L1Scraper<EthereumBaseLayerContract>, Arc<FakeL1ProviderClient>) {
let fake_client = Arc::new(FakeL1ProviderClient::default());
let config = EthereumBaseLayerConfig {
node_url: anvil.endpoint_url(),
starknet_contract_address: DEFAULT_ANVIL_DEPLOY_ADDRESS.parse().unwrap(),
};
let base_layer = EthereumBaseLayerContract::new(config);

// Deploy fresh starknet contract on anvil from the bytecode in the json file.
let contract = Starknet::deploy(base_layer.contract.provider().clone()).await.unwrap();
println!("Deployed contract at: {}", contract.address());

(
L1Scraper::new(
Default::default(),
fake_client.clone(),
base_layer,
event_identifiers_to_track(),
),
fake_client,
)
}

#[rstest]
#[tokio::test]
// TODO: extract setup stuff into test helpers once more tests are added and patterns emerge.
async fn txs_happy_flow(anvil: AnvilInstance) {
// Setup.
let (mut scraper, fake_client) = scraper(&anvil).await;

// Test.
// Scrape multiple events.
let l2_contract_address = "0x12";
let l2_entry_point = "0x34";
let send_message_to_l2 = scraper.base_layer.contract.sendMessageToL2(
l2_contract_address.parse().unwrap(),
l2_entry_point.parse().unwrap(),
vec![U256::from(1_u8), U256::from(2_u8)],
);
let send_message_to_l2_2 = scraper.base_layer.contract.sendMessageToL2(
l2_contract_address.parse().unwrap(),
l2_entry_point.parse().unwrap(),
vec![U256::from(3_u8), U256::from(4_u8)],
);

// Send the txs.
send_message_to_l2.send().await.unwrap().get_receipt().await.unwrap();
send_message_to_l2_2.send().await.unwrap().get_receipt().await.unwrap();

let expected_version = TransactionVersion(StarkHash::ZERO);
let expected_internal_l1_tx = L1HandlerTransaction {
version: expected_version,
nonce: Nonce(StarkHash::ZERO),
contract_address: contract_address!(l2_contract_address),
entry_point_selector: EntryPointSelector(felt!(l2_entry_point)),
calldata: calldata![
StarkHash::from_hex_unchecked(DEFAULT_ANVIL_ACCOUNT_ADDRESS),
felt!(1_u8),
felt!(2_u8)
],
};
let expected_chain_id = &scraper.config.chain_id;
let tx = ExecutableL1HandlerTransaction {
tx_hash: expected_internal_l1_tx
.calculate_transaction_hash(expected_chain_id, &expected_version)
.unwrap(),
tx: expected_internal_l1_tx,
paid_fee_on_l1: Fee(0),
};
let first_expected_log = Event::L1HandlerTransaction(tx.clone());
let expected_internal_l1_tx_2 = L1HandlerTransaction {
nonce: Nonce(StarkHash::ONE),
calldata: calldata![
StarkHash::from_hex_unchecked(DEFAULT_ANVIL_ACCOUNT_ADDRESS),
felt!(3_u8),
felt!(4_u8)
],
..tx.tx
};
let second_expected_log = Event::L1HandlerTransaction(ExecutableL1HandlerTransaction {
tx_hash: expected_internal_l1_tx_2
.calculate_transaction_hash(expected_chain_id, &expected_version)
.unwrap(),
tx: expected_internal_l1_tx_2,
..tx
});

// Assert.
scraper.fetch_events().await.unwrap();
fake_client.assert_add_events_received_with(&[first_expected_log, second_expected_log]);

// Previous events had been scraped, should no longer appear.
scraper.fetch_events().await.unwrap();
fake_client.assert_add_events_received_with(&[]);
}

#[tokio::test]
#[ignore = "Not yet implemented: generate an l1 and an cancel event for that tx, also check an \
abort for a different tx"]
async fn cancel_l1_handlers() {}

#[tokio::test]
#[ignore = "Not yet implemented: check that when the scraper resets all txs from the last T time
are processed"]
async fn reset() {}

#[tokio::test]
#[ignore = "Not yet implemented: check successful consume."]
async fn consume() {}

0 comments on commit f750116

Please sign in to comment.