Skip to content

Commit

Permalink
test(sync): deduplicate fake blocks generation
Browse files Browse the repository at this point in the history
  • Loading branch information
CHr15F0x committed Nov 21, 2024
1 parent cacfa75 commit fbb66a0
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 78 deletions.
1 change: 1 addition & 0 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 crates/pathfinder/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,6 @@ rstest = { workspace = true }
serde_with = { workspace = true }
starknet-gateway-test-fixtures = { path = "../gateway-test-fixtures" }
starknet_api = { workspace = true }
test-log = { workspace = true, features = ["trace"] }
tokio = { workspace = true, features = ["test-util"] }
warp = { workspace = true }
46 changes: 36 additions & 10 deletions crates/pathfinder/src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ impl LatestStream {

#[cfg(test)]
mod tests {
use fake::{Fake, Faker};
use futures::stream;
use p2p::client::types::{
ClassDefinition,
Expand All @@ -316,6 +317,8 @@ mod tests {
use pathfinder_common::state_update::StateUpdateData;
use pathfinder_common::transaction::Transaction;
use pathfinder_common::{BlockHeader, BlockId, ClassHash, SignedBlockHeader, TransactionHash};
use pathfinder_crypto::signature::ecdsa_sign;
use pathfinder_crypto::Felt;
use pathfinder_ethereum::EthereumClient;
use pathfinder_storage::fake::{generate, Block, Config};
use pathfinder_storage::StorageBuilder;
Expand All @@ -331,39 +334,62 @@ mod tests {
};
use crate::state::update_starknet_state;

#[test]
fn checkpoint_restarts_after_recoverable_error() {
/// Generate a fake chain of blocks as in
/// [`pathfinder_storage::fake::generate`] but with additional
/// guarantees:
/// - all commitments computed correctly
/// - all block hashes computed correctly
/// - all blocks signed with the same private key
///
/// Returns: public key, generated blocks.
pub fn generate_fake_blocks(num_blocks: usize) -> (PublicKey, Vec<Block>) {
let private_key = Faker.fake();
let public_key = PublicKey(pathfinder_crypto::signature::get_pk(private_key).unwrap());
let blocks = generate::with_config(
20,
num_blocks,
Config {
calculate_block_hash: Box::new(|header: &BlockHeader| {
compute_final_hash(&BlockHeaderData::from_header(header))
}),
sign_block_hash: Box::new(move |block_hash| ecdsa_sign(private_key, block_hash.0)),
calculate_transaction_commitment: Box::new(calculate_transaction_commitment),
calculate_receipt_commitment: Box::new(calculate_receipt_commitment),
calculate_event_commitment: Box::new(calculate_event_commitment),
update_tries: Box::new(update_starknet_state),
},
);
let s = Sync {
(public_key, blocks)
}

#[test_log::test(tokio::test)]
async fn checkpoint_restarts_after_recoverable_error() {
let (public_key, blocks) = generate_fake_blocks(20);
let last_header = &blocks.last().unwrap().header.header;
let mid_header = &blocks[9].header.header;
let sync = Sync {
storage: StorageBuilder::in_tempdir().unwrap(),
p2p: todo!(),
eth_client: EthereumClient::new("unused").unwrap(),
p2p: FakeP2PClient {
blocks: blocks.clone(),
},
// We use `l1_checkpoint_override` instead
eth_client: EthereumClient::new("https://unused.com").unwrap(),
eth_address: H160::zero(), // Unused
fgw_client: FakeFgw {
head: (BlockNumber::new_or_panic(10), BlockHash::ZERO),
head: (last_header.number, last_header.hash),
},
chain: Chain::SepoliaTestnet,
chain_id: ChainId::SEPOLIA_TESTNET,
public_key: PublicKey::ZERO, // TODO
l1_checkpoint_override: Some(EthereumStateUpdate {
state_root: todo!(),
block_number: BlockNumber::new_or_panic(9),
block_hash: todo!(),
state_root: mid_header.state_commitment,
block_number: mid_header.number,
block_hash: mid_header.hash,
}),
verify_tree_hashes: true,
};

sync.run().await.unwrap();

// TODO
// 2 cases here:
// - recoverable error
Expand Down
103 changes: 65 additions & 38 deletions crates/pathfinder/src/sync/checkpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -708,10 +708,7 @@ mod tests {

mod handle_header_stream {
use assert_matches::assert_matches;
use fake::{Dummy, Fake, Faker};
use futures::stream;
use p2p::libp2p::PeerId;
use p2p_proto::header;
use pathfinder_common::{
public_key,
BlockCommitmentSignature,
Expand All @@ -731,20 +728,21 @@ mod tests {
StorageCommitment,
TransactionCommitment,
};
use pathfinder_storage::fake::{self as fake_storage, Block};
use pathfinder_storage::StorageBuilder;
use rstest::rstest;
use serde::Deserialize;
use serde_with::{serde_as, DisplayFromStr};

use super::super::handle_header_stream;
use super::*;
use crate::sync::tests::generate_fake_blocks;

struct Setup {
pub streamed_headers: Vec<PeerData<SignedBlockHeader>>,
pub expected_headers: Vec<SignedBlockHeader>,
pub storage: Storage,
pub head: (BlockNumber, BlockHash),
pub public_key: PublicKey,
pub block_hash_db: Option<pathfinder_block_hashes::BlockHashDb>,
}

#[serde_as]
Expand Down Expand Up @@ -805,7 +803,7 @@ mod tests {
}
}

async fn setup() -> Setup {
fn setup_from_fixture() -> Setup {
let expected_headers =
serde_json::from_str::<Vec<Fixture>>(include_str!("fixtures/sepolia_headers.json"))
.unwrap()
Expand All @@ -832,28 +830,55 @@ mod tests {
public_key: public_key!(
"0x1252b6bce1351844c677869c6327e80eae1535755b611c66b8f46e595b40eea"
),
block_hash_db: Some(pathfinder_block_hashes::BlockHashDb::new(
Chain::SepoliaTestnet,
)),
}
}

#[tokio::test]
async fn happy_path() {
fn setup_from_fake(num_blocks: usize) -> Setup {
let (public_key, blocks) = generate_fake_blocks(num_blocks);
let expected_headers = blocks.into_iter().map(|b| b.header).collect::<Vec<_>>();
let hdr = &expected_headers.last().unwrap().header;

Setup {
head: (hdr.number, hdr.hash),
streamed_headers: expected_headers
.iter()
.rev()
.cloned()
.map(PeerData::for_tests)
.collect::<Vec<_>>(),
expected_headers,
storage: StorageBuilder::in_tempdir().unwrap(),
public_key,
block_hash_db: None,
}
}

// These two cases are an implicit verification that [`storage::fake::generate`]
// is just good enough for tests.
#[rstest]
#[case::from_fixture(setup_from_fixture())]
#[case::from_fake(setup_from_fake(10))]
#[test_log::test(tokio::test)]
async fn happy_path(#[case] setup: Setup) {
let Setup {
streamed_headers,
expected_headers,
storage,
head,
public_key,
} = setup().await;
block_hash_db,
} = setup;

handle_header_stream(
stream::iter(streamed_headers),
head,
Chain::SepoliaTestnet,
ChainId::SEPOLIA_TESTNET,
public_key,
Some(pathfinder_block_hashes::BlockHashDb::new(
Chain::SepoliaTestnet,
)),
block_hash_db,
storage.clone(),
)
.await
Expand All @@ -878,6 +903,7 @@ mod tests {

pretty_assertions_sorted::assert_eq!(expected_headers, actual_headers);
}

#[tokio::test]
async fn discontinuity() {
let Setup {
Expand All @@ -886,7 +912,7 @@ mod tests {
head,
public_key,
..
} = setup().await;
} = setup_from_fixture();

streamed_headers.last_mut().unwrap().data.header.number = BlockNumber::new_or_panic(3);

Expand Down Expand Up @@ -915,7 +941,7 @@ mod tests {
head,
public_key,
..
} = setup().await;
} = setup_from_fixture();

assert_matches!(
handle_header_stream(
Expand All @@ -933,29 +959,30 @@ mod tests {
);
}

// TODO readd once the signature verification is enabled
// #[tokio::test]
// async fn bad_signature() {
// let Setup {
// streamed_headers,
// storage,
// head,
// ..
// } = setup().await;

// assert_matches!(
// handle_header_stream(
// stream::iter(streamed_headers),
// head,
// Chain::SepoliaTestnet,
// ChainId::SEPOLIA_TESTNET,
// PublicKey::ZERO, // Invalid public key
// storage.clone(),
// )
// .await,
// Err(SyncError::BadHeaderSignature(_))
// );
// }
#[tokio::test]
async fn bad_signature() {
let Setup {
streamed_headers,
storage,
head,
block_hash_db,
..
} = setup_from_fixture();

assert_matches!(
handle_header_stream(
stream::iter(streamed_headers),
head,
Chain::SepoliaTestnet,
ChainId::SEPOLIA_TESTNET,
PublicKey::ZERO, // Invalid public key
block_hash_db,
storage.clone(),
)
.await,
Err(SyncError::BadHeaderSignature(_))
);
}

#[tokio::test]
async fn db_failure() {
Expand All @@ -965,7 +992,7 @@ mod tests {
head,
public_key,
..
} = setup().await;
} = setup_from_fixture();

let mut db = storage.connection().unwrap();
let db = db.transaction().unwrap();
Expand Down
33 changes: 3 additions & 30 deletions crates/pathfinder/src/sync/track.rs
Original file line number Diff line number Diff line change
Expand Up @@ -891,10 +891,6 @@ impl ProcessStage for StoreBlock {

#[cfg(test)]
mod tests {
use std::num::NonZeroU32;
use std::path::PathBuf;
use std::sync::Arc;

use futures::{stream, Stream, StreamExt};
use p2p::client::types::{
ClassDefinition,
Expand All @@ -903,38 +899,15 @@ mod tests {
Receipt as P2PReceipt,
StateDiffsError,
};
use p2p::libp2p::PeerId;
use p2p::PeerData;
use p2p_proto::common::Hash;
use pathfinder_common::{BlockHeader, ReceiptCommitment, SignedBlockHeader};
use pathfinder_storage::fake::{self, Block, Config};
use pathfinder_storage::StorageBuilder;
use starknet_gateway_types::error::SequencerError;

use super::*;
use crate::state::block_hash::{
calculate_event_commitment,
calculate_receipt_commitment,
calculate_transaction_commitment,
compute_final_hash,
BlockHeaderData,
};
use crate::sync::tests::generate_fake_blocks;

#[tokio::test]
async fn happy_path() {
const N: usize = 10;
let blocks = fake::generate::with_config(
N,
Config {
calculate_block_hash: Box::new(|header: &BlockHeader| {
compute_final_hash(&BlockHeaderData::from_header(header))
}),
calculate_transaction_commitment: Box::new(calculate_transaction_commitment),
calculate_receipt_commitment: Box::new(calculate_receipt_commitment),
calculate_event_commitment: Box::new(calculate_event_commitment),
update_tries: Box::new(update_starknet_state),
},
);
let (public_key, blocks) = generate_fake_blocks(10);

let BlockHeader { hash, number, .. } = blocks.last().unwrap().header.header;
let latest = (number, hash);
Expand All @@ -951,7 +924,7 @@ mod tests {
storage: storage.clone(),
chain: Chain::SepoliaTestnet,
chain_id: ChainId::SEPOLIA_TESTNET,
public_key: PublicKey::default(),
public_key,
block_hash_db: None,
verify_tree_hashes: true,
};
Expand Down

0 comments on commit fbb66a0

Please sign in to comment.