diff --git a/.github/actions/install_rust/action.yml b/.github/actions/install_rust/action.yml index 6ee9ecd7465..9f35388dae5 100644 --- a/.github/actions/install_rust/action.yml +++ b/.github/actions/install_rust/action.yml @@ -30,4 +30,4 @@ runs: - name: Install Anvil uses: foundry-rs/foundry-toolchain@v1 with: - version: v0.3.0 + version: v1.5.1 diff --git a/.github/workflows/hybrid_system_test.yaml b/.github/workflows/hybrid_system_test.yaml index 2909e82d411..b849da58752 100644 --- a/.github/workflows/hybrid_system_test.yaml +++ b/.github/workflows/hybrid_system_test.yaml @@ -269,7 +269,7 @@ jobs: - name: Install Anvil uses: foundry-rs/foundry-toolchain@v1 with: - version: v0.3.0 + version: v1.5.1 - name: Restore executable permissions run: chmod +x ./target/debug/sequencer_node_setup ./target/debug/sequencer_simulator diff --git a/Cargo.lock b/Cargo.lock index 03a53e4e9fb..6197b218433 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -866,11 +866,13 @@ version = "0.17.0-rc.0" dependencies = [ "alloy", "apollo_config", + "apollo_infra_utils", "async-trait", "colored 3.0.0", "papyrus_base_layer", "starknet_api", "tokio", + "tracing", "url", ] diff --git a/crates/apollo_base_layer_tests/Cargo.toml b/crates/apollo_base_layer_tests/Cargo.toml index 6b4f9784614..8f5ea914571 100644 --- a/crates/apollo_base_layer_tests/Cargo.toml +++ b/crates/apollo_base_layer_tests/Cargo.toml @@ -19,6 +19,8 @@ colored.workspace = true papyrus_base_layer = { workspace = true, features = ["testing"] } starknet_api.workspace = true tokio.workspace = true +tracing.workspace = true url.workspace = true [dev-dependencies] +apollo_infra_utils.workspace = true diff --git a/crates/apollo_base_layer_tests/src/anvil_base_layer.rs b/crates/apollo_base_layer_tests/src/anvil_base_layer.rs index 25e3c75acda..4c47ac60c16 100644 --- a/crates/apollo_base_layer_tests/src/anvil_base_layer.rs +++ b/crates/apollo_base_layer_tests/src/anvil_base_layer.rs @@ -1,5 +1,6 @@ use std::ops::RangeInclusive; use std::process::Command; +use std::time::Duration; use alloy::node_bindings::NodeError as AnvilError; use alloy::primitives::{I256, U256}; @@ -28,6 +29,7 @@ use papyrus_base_layer::{ use starknet_api::block::BlockHashAndNumber; use starknet_api::hash::StarkHash; use starknet_api::transaction::L1HandlerTransaction; +use tracing::info; use url::Url; /// Initialize an anvil instance under the default port and deploy the Starknet contract. @@ -47,7 +49,7 @@ pub struct AnvilBaseLayer { impl AnvilBaseLayer { pub const DEFAULT_ANVIL_L1_ACCOUNT_ADDRESS: StarkHash = StarkHash::from_hex_unchecked("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"); - pub const DEFAULT_ANVIL_PORT: u16 = 8545; + const DEFAULT_ANVIL_PORT: u16 = 8545; const DEFAULT_ANVIL_L1_DEPLOYED_ADDRESS: &str = "0x5fbdb2315678afecb367f032d93f642f64180aa3"; /// Note: if you have port conflicts, you might have a zombie anvil instance @@ -69,7 +71,7 @@ impl AnvilBaseLayer { "Install instructions (for local development):\n Execute from within a directory that's included in PATH, like ~/.local/bin:\n curl -L \ - https://github.com/foundry-rs/foundry/releases/download/v0.3.0/foundry_v0.3.0_linux_amd64.tar.gz \ + https://github.com/foundry-rs/foundry/releases/download/v1.5.1/foundry_v1.5.1_linux_amd64.tar.gz \ | tar -xz --wildcards 'anvil'".yellow() ); @@ -96,8 +98,22 @@ curl -L \ _ => panic!("Failed to spawn Anvil: {}", error.to_string().red()), }); - Starknet::deploy(anvil_client.clone()).await.unwrap(); - + info!("Deploying Starknet contract to Anvil with port: {}", port); + let mut retries = 0; + for _ in 0..100 { + let result = Starknet::deploy(anvil_client.clone()).await; + if result.is_ok() { + break; + } + retries += 1; + tokio::time::sleep(Duration::from_millis(10)).await; + } + Starknet::deploy(anvil_client.clone()).await.unwrap_or_else(|error| { + panic!( + "Failed to deploy Starknet contract to Anvil on port {port} after {retries} \ + retries: {error:?}" + ); + }); let config = Self::config(Self::url_static(port)); let url_iterator = CircularUrlIterator::new(config.ordered_l1_endpoint_urls.clone()); let root_client = anvil_client.root().clone(); diff --git a/crates/apollo_base_layer_tests/tests/anvil_starts_with_no_contract.rs b/crates/apollo_base_layer_tests/tests/anvil_starts_with_no_contract.rs index 3d177d637b0..ac183100ca0 100644 --- a/crates/apollo_base_layer_tests/tests/anvil_starts_with_no_contract.rs +++ b/crates/apollo_base_layer_tests/tests/anvil_starts_with_no_contract.rs @@ -1,4 +1,5 @@ use alloy::node_bindings::Anvil; +use apollo_infra_utils::test_utils::{AvailablePortsGenerator, TestIdentifier}; use papyrus_base_layer::ethereum_base_layer_contract::{ EthereumBaseLayerConfig, EthereumBaseLayerContract, @@ -10,14 +11,20 @@ use papyrus_base_layer::test_utils::{ OTHER_ARBITRARY_ANVIL_L1_ACCOUNT_ADDRESS, }; use papyrus_base_layer::BaseLayerContract; +use tracing::info; #[tokio::test] async fn anvil_starts_with_no_contract() { const NUM_L1_TRANSACTIONS: usize = 10; - // TODO(GuyNir/Shahak): avoid this hard-coded port number, and align port usages throughout the - // anvil instances. + let mut ports_gen = + AvailablePortsGenerator::new(TestIdentifier::AnvilStartsWithNoContractTest.into()); + let mut available_ports = ports_gen + .next() + .expect("Failed to get an AvailablePorts instance for anvil_starts_with_no_contract"); + let port = available_ports.get_next_port(); + info!("Starting Anvil with port: {}", port); let anvil = Anvil::new() - .port(9999_u16) + .port(port) .try_spawn() .expect("Anvil not installed, see anvil base layer for installation instructions."); let base_layer_config = EthereumBaseLayerConfig { diff --git a/crates/apollo_infra_utils/src/test_utils.rs b/crates/apollo_infra_utils/src/test_utils.rs index 451ec483b50..8799b9c76b8 100644 --- a/crates/apollo_infra_utils/src/test_utils.rs +++ b/crates/apollo_infra_utils/src/test_utils.rs @@ -44,6 +44,12 @@ pub enum TestIdentifier { SyncFlowIntegrationTest, StorageReaderServerUnitTests, StorageReaderTypesUnitTests, + L1EventsScraperEndToEndTest, + MockedStarknetStateUpdateTest, + LatestProvedBlockEthereumTest, + EventsFromOtherContractsTest, + L1ProviderUnitTests, + AnvilStartsWithNoContractTest, } #[derive(Debug)] diff --git a/crates/apollo_integration_tests/src/integration_test_manager.rs b/crates/apollo_integration_tests/src/integration_test_manager.rs index 90ccb18f8b5..eec9d499754 100644 --- a/crates/apollo_integration_tests/src/integration_test_manager.rs +++ b/crates/apollo_integration_tests/src/integration_test_manager.rs @@ -31,6 +31,7 @@ use mempool_test_utils::starknet_api_test_utils::{ AccountId, MultiAccountTransactionGenerator, }; +use papyrus_base_layer::ethereum_base_layer_contract::EthereumBaseLayerConfig; use papyrus_base_layer::test_utils::anvil_mine_blocks; use starknet_api::block::BlockNumber; use starknet_api::core::{ChainId, Nonce}; @@ -260,6 +261,18 @@ impl NodeSetup { .clone() .expect("No executable with a set l1 gas price scraper config.") } + + pub fn get_base_layer_config(&self) -> EthereumBaseLayerConfig { + get_executable_by_component( + self.node_type, + &self.executables, + ComponentConfigInService::BaseLayer, + ) + .get_config() + .base_layer_config + .clone() + .expect("No executable with a set base layer config.") + } } pub struct RunningNode { @@ -345,8 +358,26 @@ impl IntegrationTestManager { let l1_gas_price_scraper_config = sequencers_setup.first().unwrap().get_l1_gas_price_scraper_config(); + let anvil_base_layer_config = sequencers_setup.first().unwrap().get_base_layer_config(); + + // TODO(guyn): consider saving the port as a part of the base layer config, not just (or + // instead of) in the url. + let mut anvil_base_layer = AnvilBaseLayer::new( + Some(1), + Some( + anvil_base_layer_config + .ordered_l1_endpoint_urls + .first() + .unwrap() + .peek_secret() + .port() + .unwrap(), + ), + ) + .await; + // Make sure to update the rest of the config to match what comes from the sequencer setup. + anvil_base_layer.ethereum_base_layer.config = anvil_base_layer_config; - let anvil_base_layer = AnvilBaseLayer::new(Some(1), None).await; // Send some transactions to L1 so it has a history of blocks to scrape gas prices from. let num_blocks_needed_on_l1 = l1_gas_price_scraper_config.number_of_blocks_for_mean + l1_gas_price_scraper_config.finality; @@ -1101,12 +1132,7 @@ async fn get_sequencer_setup_configs( .next() .expect("Failed to get an AvailablePorts instance for base layer config"); let base_layer_config = - // TODO(guyn): Need to start using the ports generator for anvil, but we need to make sure - // we pass it from "setup configs" into IntegrationTestManager::new() where we setup an actual Anvil instance. - // Use this commented line: - // AnvilBaseLayer::config(AnvilBaseLayer::url_static(base_layer_ports.get_next_port())); - // TODO(guyn): Also check if DEFAULT_ANVIL_PORT should be pub in AnvilBaseLayer. - AnvilBaseLayer::config(AnvilBaseLayer::url_static(AnvilBaseLayer::DEFAULT_ANVIL_PORT)); + AnvilBaseLayer::config(AnvilBaseLayer::url_static(base_layer_ports.get_next_port())); let mut nodes = Vec::new(); diff --git a/crates/apollo_integration_tests/tests/events_from_other_contracts.rs b/crates/apollo_integration_tests/tests/events_from_other_contracts.rs index dc0cea733aa..4e748ebe45b 100644 --- a/crates/apollo_integration_tests/tests/events_from_other_contracts.rs +++ b/crates/apollo_integration_tests/tests/events_from_other_contracts.rs @@ -1,4 +1,5 @@ use apollo_base_layer_tests::anvil_base_layer::{send_message_to_l2, AnvilBaseLayer}; +use apollo_infra_utils::test_utils::{AvailablePortsGenerator, TestIdentifier}; use assert_matches::assert_matches; use papyrus_base_layer::constants::{EventIdentifier, LOG_MESSAGE_TO_L2_EVENT_IDENTIFIER}; use papyrus_base_layer::ethereum_base_layer_contract::Starknet; @@ -14,7 +15,13 @@ use starknet_api::{calldata, contract_address, felt}; async fn events_from_other_contract() { const EVENT_IDENTIFIERS: &[EventIdentifier] = &[LOG_MESSAGE_TO_L2_EVENT_IDENTIFIER]; - let mut anvil_base_layer = AnvilBaseLayer::new(None, Some(8888)).await; + let mut ports_gen = + AvailablePortsGenerator::new(TestIdentifier::EventsFromOtherContractsTest.into()); + let mut available_ports = ports_gen + .next() + .expect("Failed to get an AvailablePorts instance for events_from_other_contract"); + let mut anvil_base_layer = + AnvilBaseLayer::new(None, Some(available_ports.get_next_port())).await; // Anvil base layer already auto-deployed a starknet contract. let this_contract = &anvil_base_layer.ethereum_base_layer.contract; diff --git a/crates/apollo_integration_tests/tests/l1_events_scraper_end_to_end.rs b/crates/apollo_integration_tests/tests/l1_events_scraper_end_to_end.rs index 6e4a2d51d45..add96a11ee0 100644 --- a/crates/apollo_integration_tests/tests/l1_events_scraper_end_to_end.rs +++ b/crates/apollo_integration_tests/tests/l1_events_scraper_end_to_end.rs @@ -3,6 +3,7 @@ use std::time::Duration; use alloy::primitives::U256; use apollo_base_layer_tests::anvil_base_layer::AnvilBaseLayer; +use apollo_infra_utils::test_utils::{AvailablePortsGenerator, TestIdentifier}; use apollo_l1_provider::event_identifiers_to_track; use apollo_l1_provider::l1_scraper::L1Scraper; use apollo_l1_provider_types::{Event, MockL1ProviderClient}; @@ -22,7 +23,12 @@ use starknet_api::transaction::{L1HandlerTransaction, TransactionHasher, Transac #[tokio::test] async fn scraper_end_to_end() { // Setup. - let mut base_layer = AnvilBaseLayer::new(None, None).await; + let mut ports_gen = + AvailablePortsGenerator::new(TestIdentifier::L1EventsScraperEndToEndTest.into()); + let mut available_ports = ports_gen + .next() + .expect("Failed to get an AvailablePorts instance for l1_events_scraper_end_to_end"); + let mut base_layer = AnvilBaseLayer::new(None, Some(available_ports.get_next_port())).await; let contract = &base_layer.ethereum_base_layer.contract; let mut l1_provider_client = MockL1ProviderClient::default(); diff --git a/crates/apollo_integration_tests/tests/latest_proved_block_ethereum.rs b/crates/apollo_integration_tests/tests/latest_proved_block_ethereum.rs index 2ea347e229e..00e480a9de6 100644 --- a/crates/apollo_integration_tests/tests/latest_proved_block_ethereum.rs +++ b/crates/apollo_integration_tests/tests/latest_proved_block_ethereum.rs @@ -1,5 +1,6 @@ use alloy::providers::Provider; use apollo_base_layer_tests::anvil_base_layer::{AnvilBaseLayer, MockedStateUpdate}; +use apollo_infra_utils::test_utils::{AvailablePortsGenerator, TestIdentifier}; use papyrus_base_layer::BaseLayerContract; use pretty_assertions::assert_eq; use starknet_api::block::{BlockHash, BlockHashAndNumber, BlockNumber}; @@ -49,7 +50,12 @@ async fn latest_proved_block_ethereum() { ))), }; - let mut base_layer = AnvilBaseLayer::new(None, None).await; + let mut ports_gen = + AvailablePortsGenerator::new(TestIdentifier::LatestProvedBlockEthereumTest.into()); + let mut available_ports = ports_gen + .next() + .expect("Failed to get an AvailablePorts instance for latest_proved_block_ethereum"); + let mut base_layer = AnvilBaseLayer::new(None, Some(available_ports.get_next_port())).await; let provider = &base_layer.anvil_provider; let mut current_block = provider.get_block_number().await.expect("Failed to get block number"); diff --git a/crates/apollo_integration_tests/tests/mocked_starknet_state_update_test.rs b/crates/apollo_integration_tests/tests/mocked_starknet_state_update_test.rs index 95c197d5da7..8b516475f37 100644 --- a/crates/apollo_integration_tests/tests/mocked_starknet_state_update_test.rs +++ b/crates/apollo_integration_tests/tests/mocked_starknet_state_update_test.rs @@ -3,6 +3,7 @@ use alloy::providers::Provider; use alloy::rpc::types::eth::Filter as EthEventFilter; use alloy::sol_types::SolEventInterface; use apollo_base_layer_tests::anvil_base_layer::{AnvilBaseLayer, MockedStateUpdate}; +use apollo_infra_utils::test_utils::{AvailablePortsGenerator, TestIdentifier}; use papyrus_base_layer::ethereum_base_layer_contract::Starknet; use papyrus_base_layer::BaseLayerContract; use pretty_assertions::assert_eq; @@ -10,7 +11,12 @@ use starknet_api::block::{BlockHash, BlockHashAndNumber, BlockNumber}; #[tokio::test] async fn test_mocked_starknet_state_update() { - let mut base_layer = AnvilBaseLayer::new(None, None).await; + let mut ports_gen = + AvailablePortsGenerator::new(TestIdentifier::MockedStarknetStateUpdateTest.into()); + let mut available_ports = ports_gen + .next() + .expect("Failed to get an AvailablePorts instance for mocked_starknet_state_update_test"); + let mut base_layer = AnvilBaseLayer::new(None, Some(available_ports.get_next_port())).await; // Check that the contract was initialized (during the construction above). let genesis_block_number = 1; diff --git a/crates/apollo_l1_provider/tests/utils/mod.rs b/crates/apollo_l1_provider/tests/utils/mod.rs index f9a60d9d347..97191f02a96 100644 --- a/crates/apollo_l1_provider/tests/utils/mod.rs +++ b/crates/apollo_l1_provider/tests/utils/mod.rs @@ -17,6 +17,7 @@ use apollo_infra::component_server::{ LocalComponentServer, LocalServerConfig, }; +use apollo_infra_utils::test_utils::{AvailablePortsGenerator, TestIdentifier}; use apollo_l1_provider::l1_provider::L1Provider; use apollo_l1_provider::l1_scraper::L1Scraper; use apollo_l1_provider::metrics::L1_PROVIDER_INFRA_METRICS; @@ -67,7 +68,11 @@ fn convert_call_data_to_u256(call_data: &[u8]) -> Vec> { // Need to allow dead code as this is only used in some of the test crates. #[allow(dead_code)] pub(crate) async fn setup_anvil_base_layer() -> AnvilBaseLayer { - let mut base_layer = AnvilBaseLayer::new(None, None).await; + let mut ports_gen = AvailablePortsGenerator::new(TestIdentifier::L1ProviderUnitTests.into()); + let mut available_ports = ports_gen + .next() + .expect("Failed to get an AvailablePorts instance for L1 provider unit tests"); + let mut base_layer = AnvilBaseLayer::new(None, Some(available_ports.get_next_port())).await; anvil_mine_blocks(base_layer.ethereum_base_layer.config.clone(), NUMBER_OF_BLOCKS_TO_MINE) .await; // We use a really long timeout because in the tests we sometimes advance the fake time by large diff --git a/deployments/anvil/main.py b/deployments/anvil/main.py index 7cd3a7f816f..e0980d90505 100755 --- a/deployments/anvil/main.py +++ b/deployments/anvil/main.py @@ -8,7 +8,7 @@ from imports import k8s SERVICE_PORT = 8545 -IMAGE = "ghcr.io/foundry-rs/foundry:v0.3.0" +IMAGE = "ghcr.io/foundry-rs/foundry:v1.5.1" def argument_parser(): diff --git a/deployments/images/sequencer/node_setup.Dockerfile b/deployments/images/sequencer/node_setup.Dockerfile index 0c53d9976c2..afcdcf518f2 100644 --- a/deployments/images/sequencer/node_setup.Dockerfile +++ b/deployments/images/sequencer/node_setup.Dockerfile @@ -14,7 +14,7 @@ RUN cargo chef prepare --recipe-path recipe.json FROM base AS builder WORKDIR /app -RUN curl -L https://github.com/foundry-rs/foundry/releases/download/v0.3.0/foundry_v0.3.0_linux_amd64.tar.gz | tar -xz --wildcards 'anvil' +RUN curl -L https://github.com/foundry-rs/foundry/releases/download/v1.5.1/foundry_v1.5.1_linux_amd64.tar.gz | tar -xz --wildcards 'anvil' COPY --from=planner /app/recipe.json recipe.json RUN cargo chef cook --recipe-path recipe.json --bin sequencer_node_setup COPY . .