瀏覽代碼

resolve issue with local faucet overlapping ports in tests / new api (#7909)

* introduce run_local_faucet_for_tests / run_local_Faucet_with_config

* migrate to use new run_local_faucet_for_tests

* migrate to new run_local_faucet_for_tests

* migrate tests to run_local_faucet_for_tests / non-overlapping ports

* introduce helper run_local_faucet

* migrate address_lookup_table tests to local faucet / non-overlapping ports

* migrate cluster_query tests to local faucet / non-overlapping ports

* migrate nonce tests to local faucet / non-overlapping ports

* migrate program tests to local cluster / non-overlapping ports

* migrate request_airdrop to local_cluster / non-overlapping ports

* migrate stake tests to local faucet / non-overlapping ports

* migrate transfer tests to local faucet / non-overlapping ports

* migrate validator_info tests to local faucet / non-overlapping ports

* migrate vote tests to local cluster / non-overlapping ports

* migrate local faucet test to new api

* change name of run_local_faucet to be more explicit / extend docs

* move helper function from cli/test_utils to faucet / DCOU
puhtaytow 2 月之前
父節點
當前提交
cb0d00e9d8

+ 2 - 1
Cargo.lock

@@ -7735,7 +7735,6 @@ dependencies = [
  "solana-address-lookup-table-interface",
  "solana-borsh",
  "solana-clap-utils",
- "solana-cli",
  "solana-cli-config",
  "solana-cli-output",
  "solana-client",
@@ -8586,12 +8585,14 @@ dependencies = [
  "solana-clap-utils",
  "solana-cli-config",
  "solana-cli-output",
+ "solana-faucet",
  "solana-hash",
  "solana-instruction",
  "solana-keypair",
  "solana-logger",
  "solana-message",
  "solana-metrics",
+ "solana-net-utils",
  "solana-packet",
  "solana-pubkey",
  "solana-signer",

+ 1 - 1
accounts-cluster-bench/Cargo.toml

@@ -53,7 +53,7 @@ jemallocator = { workspace = true }
 [dev-dependencies]
 solana-accounts-db = { workspace = true }
 solana-core = { workspace = true, features = ["dev-context-only-utils"] }
-solana-faucet = { workspace = true }
+solana-faucet = { workspace = true, features = ["dev-context-only-utils"] }
 solana-local-cluster = { workspace = true, features = ["dev-context-only-utils"] }
 solana-native-token = { workspace = true }
 solana-poh-config = { workspace = true }

+ 6 - 2
accounts-cluster-bench/src/main.rs

@@ -1399,7 +1399,7 @@ pub mod test {
         super::*,
         solana_accounts_db::accounts_index::{AccountIndex, AccountSecondaryIndexes},
         solana_core::validator::ValidatorConfig,
-        solana_faucet::faucet::run_local_faucet,
+        solana_faucet::faucet::run_local_faucet_for_tests,
         solana_local_cluster::{
             local_cluster::{ClusterConfig, LocalCluster},
             validator_configs::make_identical_validator_configs,
@@ -1533,7 +1533,11 @@ pub mod test {
         solana_logger::setup();
         let mint_keypair = Keypair::new();
         let mint_pubkey = mint_keypair.pubkey();
-        let faucet_addr = run_local_faucet(mint_keypair, None);
+        let faucet_addr = run_local_faucet_for_tests(
+            mint_keypair,
+            None, /* per_time_cap */
+            0,    /* port */
+        );
         let test_validator = TestValidator::with_custom_fees(
             mint_pubkey,
             1,

+ 1 - 1
bench-tps/Cargo.toml

@@ -35,7 +35,6 @@ solana-commitment-config = { workspace = true }
 solana-compute-budget-interface = { workspace = true }
 solana-connection-cache = { workspace = true }
 solana-core = { workspace = true, features = ["dev-context-only-utils"] }
-solana-faucet = { workspace = true }
 solana-fee-calculator = { workspace = true }
 solana-genesis = { workspace = true }
 solana-genesis-config = { workspace = true }
@@ -76,6 +75,7 @@ jemallocator = { workspace = true }
 [dev-dependencies]
 agave-feature-set = { workspace = true }
 serial_test = { workspace = true }
+solana-faucet = { workspace = true, features = ["dev-context-only-utils"] }
 solana-local-cluster = { workspace = true }
 solana-rent = { workspace = true }
 solana-runtime = { workspace = true, features = ["dev-context-only-utils"] }

+ 11 - 4
bench-tps/tests/bench_tps.rs

@@ -11,7 +11,7 @@ use {
     solana_commitment_config::CommitmentConfig,
     solana_connection_cache::connection_cache::NewConnectionConfig,
     solana_core::validator::ValidatorConfig,
-    solana_faucet::faucet::run_local_faucet,
+    solana_faucet::faucet::run_local_faucet_for_tests,
     solana_fee_calculator::FeeRateGovernor,
     solana_keypair::Keypair,
     solana_local_cluster::{
@@ -51,7 +51,11 @@ fn test_bench_tps_local_cluster(config: Config) {
 
     let faucet_keypair = Keypair::new();
     let faucet_pubkey = faucet_keypair.pubkey();
-    let faucet_addr = run_local_faucet(faucet_keypair, None);
+    let faucet_addr = run_local_faucet_for_tests(
+        faucet_keypair,
+        None, /* per_time_cap */
+        0,    /* port */
+    );
 
     const NUM_NODES: usize = 1;
     let cluster = LocalCluster::new(
@@ -109,8 +113,11 @@ fn test_bench_tps_test_validator(config: Config) {
 
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_for_tests(
+        mint_keypair,
+        None, /* per_time_cap */
+        0,    /* port */
+    );
 
     let test_validator = TestValidatorGenesis::default()
         .fee_rate_governor(FeeRateGovernor::new(0, 0))

+ 2 - 3
cli/Cargo.toml

@@ -17,7 +17,7 @@ name = "solana"
 path = "src/main.rs"
 
 [features]
-dev-context-only-utils = []
+dev-context-only-utils = ["solana-faucet/dev-context-only-utils"]
 
 [dependencies]
 agave-feature-set = { workspace = true }
@@ -103,9 +103,8 @@ tiny-bip39 = { workspace = true }
 
 [dev-dependencies]
 assert_matches = { workspace = true }
-solana-cli = { path = ".", features = ["dev-context-only-utils"] }
 solana-client = { workspace = true, features = ["dev-context-only-utils"] }
-solana-faucet = { workspace = true }
+solana-faucet = { workspace = true, features = ["dev-context-only-utils"] }
 solana-nonce-account = { workspace = true }
 solana-presigner = { workspace = true }
 solana-rpc = { workspace = true }

+ 3 - 3
cli/tests/address_lookup_table.rs

@@ -7,7 +7,7 @@ use {
         cli::{process_command, CliCommand, CliConfig},
     },
     solana_cli_output::{CliAddressLookupTable, CliAddressLookupTableCreated, OutputFormat},
-    solana_faucet::faucet::run_local_faucet,
+    solana_faucet::faucet::run_local_faucet_with_unique_port_for_tests,
     solana_keypair::Keypair,
     solana_native_token::LAMPORTS_PER_SOL,
     solana_pubkey::Pubkey,
@@ -21,7 +21,7 @@ use {
 fn test_cli_create_extend_and_freeze_address_lookup_table() {
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator =
         TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
 
@@ -135,7 +135,7 @@ fn test_cli_create_extend_and_freeze_address_lookup_table() {
 fn test_cli_create_and_deactivate_address_lookup_table() {
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator =
         TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
 

+ 2 - 2
cli/tests/cluster_query.rs

@@ -5,7 +5,7 @@ use {
         test_utils::check_ready,
     },
     solana_commitment_config::CommitmentConfig,
-    solana_faucet::faucet::run_local_faucet,
+    solana_faucet::faucet::run_local_faucet_with_unique_port_for_tests,
     solana_fee_structure::FeeStructure,
     solana_keypair::Keypair,
     solana_native_token::LAMPORTS_PER_SOL,
@@ -24,7 +24,7 @@ fn test_ping(compute_unit_price: Option<u64>) {
     let fee = FeeStructure::default().get_max_fee(1, 0);
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator = TestValidator::with_custom_fees(
         mint_pubkey,
         fee,

+ 3 - 3
cli/tests/nonce.rs

@@ -8,7 +8,7 @@ use {
     },
     solana_cli_output::{parse_sign_only_reply_string, OutputFormat},
     solana_commitment_config::CommitmentConfig,
-    solana_faucet::faucet::run_local_faucet,
+    solana_faucet::faucet::run_local_faucet_with_unique_port_for_tests,
     solana_hash::Hash,
     solana_keypair::{keypair_from_seed, Keypair},
     solana_native_token::LAMPORTS_PER_SOL,
@@ -29,7 +29,7 @@ use {
 fn test_nonce(seed: Option<String>, use_nonce_authority: bool, compute_unit_price: Option<u64>) {
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator =
         TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
 
@@ -218,7 +218,7 @@ fn test_create_account_with_seed() {
     solana_logger::setup();
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator = TestValidator::with_custom_fees(
         mint_pubkey,
         ONE_SIG_FEE,

+ 5 - 3
cli/tests/program.rs

@@ -18,7 +18,7 @@ use {
     solana_client::rpc_config::RpcSendTransactionConfig,
     solana_commitment_config::CommitmentConfig,
     solana_compute_budget_interface::ComputeBudgetInstruction,
-    solana_faucet::faucet::run_local_faucet,
+    solana_faucet::faucet::run_local_faucet_with_unique_port_for_tests,
     solana_fee_calculator::FeeRateGovernor,
     solana_keypair::Keypair,
     solana_loader_v3_interface::state::UpgradeableLoaderState,
@@ -56,7 +56,9 @@ fn test_validator_genesis(mint_keypair: Keypair) -> TestValidatorGenesis {
             exemption_threshold: 1.0,
             ..Rent::default()
         })
-        .faucet_addr(Some(run_local_faucet(mint_keypair, None)));
+        .faucet_addr(Some(run_local_faucet_with_unique_port_for_tests(
+            mint_keypair,
+        )));
     genesis
 }
 
@@ -2929,7 +2931,7 @@ fn test_cli_program_deploy_with_args(compute_unit_price: Option<u64>, use_rpc: b
 
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator = TestValidatorGenesis::default()
         .fee_rate_governor(FeeRateGovernor::new(0, 0))
         .rent(Rent {

+ 2 - 2
cli/tests/request_airdrop.rs

@@ -2,7 +2,7 @@
 use {
     solana_cli::cli::{process_command, CliCommand, CliConfig},
     solana_commitment_config::CommitmentConfig,
-    solana_faucet::faucet::run_local_faucet,
+    solana_faucet::faucet::run_local_faucet_with_unique_port_for_tests,
     solana_keypair::Keypair,
     solana_native_token::LAMPORTS_PER_SOL,
     solana_rpc_client::rpc_client::RpcClient,
@@ -15,7 +15,7 @@ use {
 fn test_cli_request_airdrop() {
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator =
         TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
 

+ 14 - 14
cli/tests/stake.rs

@@ -12,7 +12,7 @@ use {
     solana_cli_output::{parse_sign_only_reply_string, OutputFormat},
     solana_commitment_config::CommitmentConfig,
     solana_epoch_schedule::EpochSchedule,
-    solana_faucet::faucet::run_local_faucet,
+    solana_faucet::faucet::run_local_faucet_with_unique_port_for_tests,
     solana_fee_calculator::FeeRateGovernor,
     solana_fee_structure::FeeStructure,
     solana_keypair::{keypair_from_seed, Keypair},
@@ -39,7 +39,7 @@ fn test_stake_delegation_force() {
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
     let authorized_withdrawer = Keypair::new().pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let slots_per_epoch = 32;
     let test_validator = TestValidatorGenesis::default()
         .fee_rate_governor(FeeRateGovernor::new(0, 0))
@@ -204,7 +204,7 @@ fn test_seed_stake_delegation_and_deactivation(compute_unit_price: Option<u64>)
 
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator =
         TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
 
@@ -299,7 +299,7 @@ fn test_stake_delegation_and_withdraw_available() {
 
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator =
         TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
 
@@ -468,7 +468,7 @@ fn test_stake_delegation_and_withdraw_all() {
 
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator =
         TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
 
@@ -633,7 +633,7 @@ fn test_stake_delegation_and_deactivation(compute_unit_price: Option<u64>) {
 
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator =
         TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
 
@@ -725,7 +725,7 @@ fn test_offline_stake_delegation_and_deactivation(compute_unit_price: Option<u64
 
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator =
         TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
 
@@ -885,7 +885,7 @@ fn test_nonced_stake_delegation_and_deactivation(compute_unit_price: Option<u64>
 
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator =
         TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
 
@@ -1014,7 +1014,7 @@ fn test_stake_authorize(compute_unit_price: Option<u64>) {
 
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator =
         TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
 
@@ -1344,7 +1344,7 @@ fn test_stake_authorize_with_fee_payer() {
 
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator = TestValidator::with_custom_fees(
         mint_pubkey,
         fee_one_sig,
@@ -1524,7 +1524,7 @@ fn test_stake_split(compute_unit_price: Option<u64>) {
 
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator = TestValidator::with_custom_fees(
         mint_pubkey,
         1,
@@ -1685,7 +1685,7 @@ fn test_stake_set_lockup(compute_unit_price: Option<u64>) {
 
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator = TestValidator::with_custom_fees(
         mint_pubkey,
         1,
@@ -1973,7 +1973,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw(compute_unit_price: Opt
 
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator =
         TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
 
@@ -2216,7 +2216,7 @@ fn test_stake_checked_instructions() {
 
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator =
         TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
 

+ 6 - 6
cli/tests/transfer.rs

@@ -9,7 +9,7 @@ use {
     solana_cli_output::{parse_sign_only_reply_string, OutputFormat},
     solana_commitment_config::CommitmentConfig,
     solana_compute_budget_interface::ComputeBudgetInstruction,
-    solana_faucet::faucet::run_local_faucet,
+    solana_faucet::faucet::run_local_faucet_with_unique_port_for_tests,
     solana_fee_structure::FeeStructure,
     solana_keypair::{keypair_from_seed, Keypair},
     solana_message::Message,
@@ -34,7 +34,7 @@ fn test_transfer(skip_preflight: bool) {
     let fee_two_sig = FeeStructure::default().get_max_fee(2, 0);
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator = TestValidator::with_custom_fees(
         mint_pubkey,
         fee_one_sig,
@@ -332,7 +332,7 @@ fn test_transfer_multisession_signing() {
     let fee_two_sig = FeeStructure::default().get_max_fee(2, 0);
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator = TestValidator::with_custom_fees(
         mint_pubkey,
         fee_one_sig,
@@ -481,7 +481,7 @@ fn test_transfer_all(compute_unit_price: Option<u64>) {
     let lamports_per_signature = FeeStructure::default().get_max_fee(1, 0);
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator = TestValidator::with_custom_fees(
         mint_pubkey,
         lamports_per_signature,
@@ -558,7 +558,7 @@ fn test_transfer_unfunded_recipient() {
     solana_logger::setup();
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator = TestValidator::with_custom_fees(
         mint_pubkey,
         1,
@@ -614,7 +614,7 @@ fn test_transfer_with_seed() {
     let fee = FeeStructure::default().get_max_fee(1, 0);
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator = TestValidator::with_custom_fees(
         mint_pubkey,
         fee,

+ 2 - 2
cli/tests/validator_info.rs

@@ -5,7 +5,7 @@ use {
         cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
     },
     solana_commitment_config::CommitmentConfig,
-    solana_faucet::faucet::run_local_faucet,
+    solana_faucet::faucet::run_local_faucet_with_unique_port_for_tests,
     solana_keypair::{keypair_from_seed, Keypair},
     solana_rpc_client::rpc_client::RpcClient,
     solana_signer::Signer,
@@ -21,7 +21,7 @@ fn test_publish(compute_unit_price: Option<u64>) {
 
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator =
         TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
 

+ 3 - 3
cli/tests/vote.rs

@@ -8,7 +8,7 @@ use {
     },
     solana_cli_output::{parse_sign_only_reply_string, OutputFormat},
     solana_commitment_config::CommitmentConfig,
-    solana_faucet::faucet::run_local_faucet,
+    solana_faucet::faucet::run_local_faucet_with_unique_port_for_tests,
     solana_keypair::Keypair,
     solana_rpc_client::rpc_client::RpcClient,
     solana_rpc_client_nonce_utils::blockhash_query::{self, BlockhashQuery},
@@ -24,7 +24,7 @@ use {
 fn test_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator =
         TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
 
@@ -229,7 +229,7 @@ fn test_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
 fn test_offline_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet(mint_keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
     let test_validator =
         TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
 

+ 1 - 1
dos/Cargo.toml

@@ -27,7 +27,6 @@ solana-bench-tps = { workspace = true }
 solana-client = { workspace = true }
 solana-connection-cache = { workspace = true }
 solana-core = { workspace = true }
-solana-faucet = { workspace = true }
 solana-gossip = { workspace = true }
 solana-hash = { workspace = true }
 solana-instruction = { workspace = true }
@@ -54,5 +53,6 @@ solana-version = { workspace = true }
 
 [dev-dependencies]
 solana-core = { workspace = true, features = ["dev-context-only-utils"] }
+solana-faucet = { workspace = true, features = ["dev-context-only-utils"] }
 solana-local-cluster = { workspace = true }
 solana-runtime = { workspace = true, features = ["dev-context-only-utils"] }

+ 2 - 2
dos/src/main.rs

@@ -831,7 +831,7 @@ pub mod test {
     use {
         super::*,
         solana_core::validator::ValidatorConfig,
-        solana_faucet::faucet::run_local_faucet,
+        solana_faucet::faucet::run_local_faucet_with_unique_port_for_tests,
         solana_local_cluster::{
             cluster::Cluster,
             local_cluster::{ClusterConfig, LocalCluster},
@@ -1086,7 +1086,7 @@ pub mod test {
         // 1. Create faucet thread
         let faucet_keypair = Keypair::new();
         let faucet_pubkey = faucet_keypair.pubkey();
-        let faucet_addr = run_local_faucet(faucet_keypair, None);
+        let faucet_addr = run_local_faucet_with_unique_port_for_tests(faucet_keypair);
         let mut validator_config = ValidatorConfig::default_for_test();
         validator_config.rpc_config = JsonRpcConfig {
             faucet_addr: Some(faucet_addr),

+ 7 - 0
faucet/Cargo.toml

@@ -20,6 +20,9 @@ name = "solana_faucet"
 name = "solana-faucet"
 path = "src/bin/faucet.rs"
 
+[features]
+dev-context-only-utils = []
+
 [dependencies]
 bincode = { workspace = true }
 clap = { workspace = true }
@@ -36,6 +39,7 @@ solana-keypair = "=3.0.1"
 solana-logger = "=3.0.0"
 solana-message = "=3.0.1"
 solana-metrics = { workspace = true }
+solana-net-utils = { workspace = true }
 solana-packet = "=3.0.0"
 solana-pubkey = { version = "=3.0.0", features = ["rand"] }
 solana-signer = "=3.0.0"
@@ -46,3 +50,6 @@ solana-version = { workspace = true }
 spl-memo-interface = { version = "=2.0.0" }
 thiserror = { workspace = true }
 tokio = { workspace = true, features = ["full"] }
+
+[dev-dependencies]
+solana-faucet = { path = ".", features = ["dev-context-only-utils"] }

+ 73 - 0
faucet/src/faucet.rs

@@ -337,6 +337,79 @@ pub fn run_local_faucet_with_port(
     });
 }
 
+/// Configuration for running a local faucet server.
+#[cfg(feature = "dev-context-only-utils")]
+pub struct LocalFaucetConfig {
+    pub keypair: Keypair,
+    pub address: Ipv4Addr,
+    pub port: u16,
+    pub time_input: Option<u64>,
+    pub per_time_cap: Option<u64>,
+    pub per_request_cap: Option<u64>,
+}
+
+/// Runs a local faucet server with the specified configuration.
+///
+/// # Arguments
+/// * `sender` - Channel to report the bound socket address or startup errors
+/// * `config` - Faucet configuration (use `config.port = 0` for random open port)
+#[cfg(feature = "dev-context-only-utils")]
+pub fn run_local_faucet_with_config(
+    sender: Sender<Result<SocketAddr, String>>,
+    config: LocalFaucetConfig,
+) {
+    thread::spawn(move || {
+        let faucet_addr = socketaddr!(config.address, config.port);
+        let faucet = Arc::new(Mutex::new(Faucet::new(
+            config.keypair,
+            config.time_input,
+            config.per_time_cap,
+            config.per_request_cap,
+        )));
+        let runtime = Runtime::new().unwrap();
+        runtime.block_on(run_faucet(faucet, faucet_addr, Some(sender)));
+    });
+}
+
+/// For integration tests and benchmarks.
+///
+/// Listens on `LOCALHOST` with a random open port (unless port is provided) and reports to Sender.
+#[cfg(feature = "dev-context-only-utils")]
+pub fn run_local_faucet_for_tests(
+    keypair: Keypair,
+    per_time_cap: Option<u64>,
+    port: u16,
+) -> SocketAddr {
+    let (sender, receiver) = unbounded();
+    run_local_faucet_with_config(
+        sender,
+        LocalFaucetConfig {
+            keypair,
+            address: Ipv4Addr::LOCALHOST,
+            port,
+            time_input: None,
+            per_time_cap,
+            per_request_cap: None,
+        },
+    );
+    receiver
+        .recv()
+        .expect("run_local_faucet_for_tests")
+        .expect("faucet_addr")
+}
+
+/// For tests only.
+///
+/// Listens on `LOCALHOST` with unique, non-overlapping port and reports to Sender.
+#[cfg(feature = "dev-context-only-utils")]
+pub fn run_local_faucet_with_unique_port_for_tests(keypair: Keypair) -> SocketAddr {
+    run_local_faucet_for_tests(
+        keypair,
+        None, /* per_time_cap */
+        solana_net_utils::sockets::unique_port_range_for_tests(1).start,
+    )
+}
+
 // For integration tests. Listens on random open port and reports port to Sender.
 pub fn run_local_faucet(faucet_keypair: Keypair, per_time_cap: Option<u64>) -> SocketAddr {
     let (sender, receiver) = unbounded();

+ 4 - 3
faucet/tests/local-faucet.rs

@@ -1,5 +1,7 @@
 use {
-    solana_faucet::faucet::{request_airdrop_transaction, run_local_faucet},
+    solana_faucet::faucet::{
+        request_airdrop_transaction, run_local_faucet_with_unique_port_for_tests,
+    },
     solana_hash::Hash,
     solana_keypair::Keypair,
     solana_message::Message,
@@ -17,8 +19,7 @@ fn test_local_faucet() {
     let create_instruction = transfer(&keypair.pubkey(), &to, lamports);
     let message = Message::new(&[create_instruction], Some(&keypair.pubkey()));
     let expected_tx = Transaction::new(&[&keypair], message, blockhash);
-
-    let faucet_addr = run_local_faucet(keypair, None);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(keypair);
 
     let result = request_airdrop_transaction(&faucet_addr, &to, lamports, blockhash);
     assert_eq!(expected_tx, result.unwrap());

+ 1 - 0
programs/sbf/Cargo.lock

@@ -6784,6 +6784,7 @@ dependencies = [
  "solana-logger",
  "solana-message",
  "solana-metrics",
+ "solana-net-utils",
  "solana-packet",
  "solana-pubkey",
  "solana-signer",