diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3982b92a03..ec4a28d44f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,9 @@ env: FORC_VERSION: 0.48.0 FORC_PATCH_BRANCH: "" FORC_PATCH_REVISION: "" + TEST_WALLET_SECRET_KEY_1: ${{ secrets.TEST_WALLET_SECRET_KEY_1 }} + TEST_WALLET_SECRET_KEY_2: ${{ secrets.TEST_WALLET_SECRET_KEY_2 }} + TEST_WALLET_SECRET_KEY_3: ${{ secrets.TEST_WALLET_SECRET_KEY_3 }} jobs: setup-test-projects: @@ -172,6 +175,12 @@ jobs: args: run --all-targets --workspace download_sway_artifacts: sway-examples install_fuel_core: true + - command: nextest # test some functions against live nodes + # the live node features requires using one thread only to avoid transaction collision + args: run --features "test-against-live-node" -j 1 --workspace + download_sway_artifacts: sway-examples + # not all tests use the live nodes so fuel-core binary remains necessary + install_fuel_core: true - cargo_command: test args: --doc --workspace - cargo_command: machete diff --git a/examples/providers/src/lib.rs b/examples/providers/src/lib.rs index 850914b0ff..5fd1ae417e 100644 --- a/examples/providers/src/lib.rs +++ b/examples/providers/src/lib.rs @@ -14,7 +14,7 @@ mod tests { // Create a provider pointing to the testnet. // This example will not work as the testnet does not support the new version of fuel-core // yet - let provider = Provider::connect("beta-5.fuel.network").await.unwrap(); + let provider = Provider::connect(TESTNET_NODE_URL).await.unwrap(); // Setup a private key let secret = diff --git a/packages/fuels-core/src/utils/constants.rs b/packages/fuels-core/src/utils/constants.rs index 8596475903..94907f7080 100644 --- a/packages/fuels-core/src/utils/constants.rs +++ b/packages/fuels-core/src/utils/constants.rs @@ -16,3 +16,4 @@ pub const DEFAULT_GAS_ESTIMATION_TOLERANCE: f64 = 0.2; pub const WITNESS_STATIC_SIZE: usize = 8; const SIGNATURE_SIZE: usize = 64; pub const SIGNATURE_WITNESS_SIZE: usize = WITNESS_STATIC_SIZE + SIGNATURE_SIZE; +pub const TESTNET_NODE_URL: &str = "beta-5.fuel.network"; diff --git a/packages/fuels-macros/src/setup_program_test/code_gen.rs b/packages/fuels-macros/src/setup_program_test/code_gen.rs index 9b528e238d..f22012b33a 100644 --- a/packages/fuels-macros/src/setup_program_test/code_gen.rs +++ b/packages/fuels-macros/src/setup_program_test/code_gen.rs @@ -25,7 +25,7 @@ pub(crate) fn generate_setup_program_test_code( let project_lookup = generate_project_lookup(&generate_bindings)?; let abigen_code = abigen_code(&project_lookup); - let wallet_code = wallet_initialization_code(initialize_wallets); + let wallet_code = wallet_initialization_code(initialize_wallets)?; let deploy_code = contract_deploying_code(&deploy_contract, &project_lookup); let script_code = script_loading_code(&load_scripts, &project_lookup); @@ -66,21 +66,22 @@ fn generate_abigen_targets(project_lookup: &HashMap) -> Vec) -> TokenStream { +fn wallet_initialization_code( + maybe_command: Option, +) -> syn::Result { let command = if let Some(command) = maybe_command { command } else { - return Default::default(); + return Ok(Default::default()); }; let wallet_names = extract_wallet_names(&command); - if wallet_names.is_empty() { - return Default::default(); + return Ok(Default::default()); } - let num_wallets = wallet_names.len(); - quote! { + + Ok(quote! { let [#(#wallet_names),*]: [_; #num_wallets] = launch_custom_provider_and_get_wallets( WalletsConfig::new(Some(#num_wallets as u64), None, None), None, @@ -90,7 +91,7 @@ fn wallet_initialization_code(maybe_command: Option) -> .expect("Error while trying to fetch wallets from the custom provider") .try_into() .expect("Should have the exact number of wallets"); - } + }) } fn extract_wallet_names(command: &InitializeWalletCommand) -> Vec { diff --git a/packages/fuels-macros/src/setup_program_test/parsing/commands.rs b/packages/fuels-macros/src/setup_program_test/parsing/commands.rs index 7c1eae6dad..0653ea77b5 100644 --- a/packages/fuels-macros/src/setup_program_test/parsing/commands.rs +++ b/packages/fuels-macros/src/setup_program_test/parsing/commands.rs @@ -35,7 +35,7 @@ command_parser!( Wallets -> InitializeWalletCommand, Abigen -> AbigenCommand, Deploy -> DeployContractCommand, - LoadScript -> LoadScriptCommand + LoadScript -> LoadScriptCommand, ); impl Parse for TestProgramCommands { diff --git a/packages/fuels-test-helpers/Cargo.toml b/packages/fuels-test-helpers/Cargo.toml index b3e3bcee13..3f03a9f064 100644 --- a/packages/fuels-test-helpers/Cargo.toml +++ b/packages/fuels-test-helpers/Cargo.toml @@ -35,3 +35,4 @@ which = { workspace = true, default-features = false } default = ["fuels-accounts", "std"] std = ["fuels-accounts?/std", "fuels-core/std"] fuel-core-lib = ["fuel-core"] +test-against-live-node = [] diff --git a/packages/fuels-test-helpers/src/accounts.rs b/packages/fuels-test-helpers/src/accounts.rs index a565739222..29543de386 100644 --- a/packages/fuels-test-helpers/src/accounts.rs +++ b/packages/fuels-test-helpers/src/accounts.rs @@ -1,8 +1,9 @@ +use fuel_crypto::SecretKey; use std::mem::size_of; +use std::str::FromStr; -use fuel_crypto::SecretKey; -use fuels_accounts::wallet::WalletUnlocked; -use fuels_core::types::errors::Result; +use fuels_accounts::{provider::Provider, wallet::WalletUnlocked}; +use fuels_core::{constants::TESTNET_NODE_URL, error, types::errors::Result}; use crate::{ node_types::{ChainConfig, Config}, @@ -85,6 +86,44 @@ pub async fn launch_custom_provider_and_get_wallets( Ok(wallets) } +pub async fn connect_to_testnet_node_and_get_wallets( + num_wallets: usize, +) -> Result> { + if num_wallets > 3 { + error!( + InvalidData, + "Trying to get more than 3 wallets from beta node" + ); + } + let provider = Provider::connect(TESTNET_NODE_URL) + .await + .expect("Should be able to connect to {TESTNET_NODE_URL}"); + let wallets = (1..=num_wallets) + .map(|wallet_counter| { + let private_key_string = + std::env::var(format!("TEST_WALLET_SECRET_KEY_{wallet_counter}")) + .expect("Should find private key in ENV"); + let private_key = SecretKey::from_str(private_key_string.as_str()) + .expect("Should be able to transform into private key"); + WalletUnlocked::new_from_private_key(private_key, Some(provider.clone())) + }) + .collect::>(); + Ok(wallets) +} + +pub async fn maybe_live_wallet(num_wallets: usize) -> Result> { + if cfg!(feature = "test-against-live-node") { + connect_to_testnet_node_and_get_wallets(num_wallets).await + } else { + launch_custom_provider_and_get_wallets( + WalletsConfig::new(Some(num_wallets as u64), None, None), + None, + None, + ) + .await + } +} + #[cfg(test)] mod tests { use fuel_core_chain_config::ChainConfig; diff --git a/packages/fuels/Cargo.toml b/packages/fuels/Cargo.toml index 4a213e9a64..0a7d697be9 100644 --- a/packages/fuels/Cargo.toml +++ b/packages/fuels/Cargo.toml @@ -55,5 +55,6 @@ std = [ ] # TODO: To be removed once https://github.com/FuelLabs/fuels-rs/issues/881 is unblocked. test-type-paths = [] +test-against-live-node = [] fuel-core-lib = ["fuels-test-helpers?/fuel-core-lib", "dep:fuel-core"] rocksdb = ["fuel-core?/rocksdb"] diff --git a/packages/fuels/tests/contracts.rs b/packages/fuels/tests/contracts.rs index 48644a5e7d..4bb0efdd2d 100644 --- a/packages/fuels/tests/contracts.rs +++ b/packages/fuels/tests/contracts.rs @@ -1,5 +1,7 @@ #[allow(unused_imports)] use std::future::Future; +use std::thread::sleep; +use std::time::Duration; use std::vec; use fuels::{ @@ -43,8 +45,11 @@ async fn test_multiple_args() -> Result<()> { #[tokio::test] async fn test_contract_calling_contract() -> Result<()> { // Tests a contract call that calls another contract (FooCaller calls FooContract underneath) + let [wallet]: [WalletUnlocked; 1] = maybe_live_wallet(1) + .await? + .try_into() + .expect("Vec can be converted to an array"); setup_program_test!( - Wallets("wallet"), Abigen( Contract( name = "LibContract", @@ -75,6 +80,9 @@ async fn test_contract_calling_contract() -> Result<()> { let lib_contract_id2 = lib_contract_instance2.contract_id(); // Call the contract directly. It increments the given value. + if cfg!(feature = "test-against-live-node") { + sleep(Duration::from_secs(10)); + } let response = lib_contract_instance.methods().increment(42).call().await?; assert_eq!(43, response.value); @@ -144,8 +152,11 @@ async fn test_reverting_transaction() -> Result<()> { #[tokio::test] async fn test_multiple_read_calls() -> Result<()> { + let [wallet]: [WalletUnlocked; 1] = maybe_live_wallet(1) + .await? + .try_into() + .expect("Vec can be converted to an array"); setup_program_test!( - Wallets("wallet"), Abigen(Contract( name = "MultiReadContract", project = "packages/fuels/tests/contracts/multiple_read_calls" @@ -175,8 +186,11 @@ async fn test_multiple_read_calls() -> Result<()> { #[tokio::test] async fn test_multi_call_beginner() -> Result<()> { + let [wallet]: [WalletUnlocked; 1] = maybe_live_wallet(1) + .await? + .try_into() + .expect("Vec can be converted to an array"); setup_program_test!( - Wallets("wallet"), Abigen(Contract( name = "TestContract", project = "packages/fuels/tests/contracts/contract_test" @@ -198,6 +212,10 @@ async fn test_multi_call_beginner() -> Result<()> { .add_call(call_handler_1) .add_call(call_handler_2); + if cfg!(feature = "test-against-live-node") { + sleep(Duration::from_secs(10)); + } + let (val_1, val_2): (u64, u64) = multi_call_handler.call().await?.value; assert_eq!(val_1, 7); @@ -208,8 +226,11 @@ async fn test_multi_call_beginner() -> Result<()> { #[tokio::test] async fn test_multi_call_pro() -> Result<()> { + let [wallet]: [WalletUnlocked; 1] = maybe_live_wallet(1) + .await? + .try_into() + .expect("Vec can be converted to an array"); setup_program_test!( - Wallets("wallet"), Abigen(Contract( name = "TestContract", project = "packages/fuels/tests/contracts/contract_test" @@ -263,8 +284,11 @@ async fn test_multi_call_pro() -> Result<()> { #[tokio::test] async fn test_contract_call_fee_estimation() -> Result<()> { + let [wallet]: [WalletUnlocked; 1] = maybe_live_wallet(1) + .await? + .try_into() + .expect("Vec can be converted to an array"); setup_program_test!( - Wallets("wallet"), Abigen(Contract( name = "TestContract", project = "packages/fuels/tests/contracts/contract_test" @@ -276,14 +300,22 @@ async fn test_contract_call_fee_estimation() -> Result<()> { ), ); - let gas_price = 100_000_000; + let gas_price = if cfg!(feature = "test-against-live-node") { + 1000 + } else { + 100_000_000 + }; let gas_limit = 800; let tolerance = 0.2; - let expected_min_gas_price = 0; // This is the default min_gas_price from the ConsensusParameters - let expected_gas_used = 675; let expected_metered_bytes_size = 792; - let expected_total_fee = 898; + let (expected_min_gas_price, expected_gas_used, expected_total_fee) = + if cfg!(feature = "test-against-live-node") { + (1, 960, 1_074_685) + } else { + // 0 is the default min_gas_price from the ConsensusParameters + (0, 675, 898) + }; let estimated_transaction_cost = contract_instance .methods() @@ -312,8 +344,11 @@ async fn test_contract_call_fee_estimation() -> Result<()> { #[tokio::test] async fn contract_call_has_same_estimated_and_used_gas() -> Result<()> { + let [wallet]: [WalletUnlocked; 1] = maybe_live_wallet(1) + .await? + .try_into() + .expect("Vec can be converted to an array"); setup_program_test!( - Wallets("wallet"), Abigen(Contract( name = "TestContract", project = "packages/fuels/tests/contracts/contract_test" @@ -345,8 +380,11 @@ async fn contract_call_has_same_estimated_and_used_gas() -> Result<()> { #[tokio::test] async fn mult_call_has_same_estimated_and_used_gas() -> Result<()> { + let [wallet]: [WalletUnlocked; 1] = maybe_live_wallet(1) + .await? + .try_into() + .expect("Vec can be converted to an array"); setup_program_test!( - Wallets("wallet"), Abigen(Contract( name = "TestContract", project = "packages/fuels/tests/contracts/contract_test" @@ -1503,8 +1541,11 @@ async fn can_configure_decoding_of_contract_return() -> Result<()> { #[tokio::test] async fn test_contract_submit_and_response() -> Result<()> { + let [wallet]: [WalletUnlocked; 1] = maybe_live_wallet(1) + .await? + .try_into() + .expect("Vec can be converted to an array"); setup_program_test!( - Wallets("wallet"), Abigen(Contract( name = "TestContract", project = "packages/fuels/tests/contracts/contract_test" @@ -1519,6 +1560,9 @@ async fn test_contract_submit_and_response() -> Result<()> { let contract_methods = contract_instance.methods(); let submitted_tx = contract_methods.get(1, 2).submit().await?; + if cfg!(feature = "test-against-live-node") { + sleep(Duration::from_secs(10)); + } let value = submitted_tx.response().await?.value; assert_eq!(value, 3); @@ -1534,6 +1578,9 @@ async fn test_contract_submit_and_response() -> Result<()> { .add_call(call_handler_2); let handle = multi_call_handler.submit().await?; + if cfg!(feature = "test-against-live-node") { + sleep(Duration::from_secs(10)); + } let (val_1, val_2): (u64, u64) = handle.response().await?.value; assert_eq!(val_1, 7); @@ -1670,8 +1717,11 @@ async fn heap_types_correctly_offset_in_create_transactions_w_storage_slots() -> #[tokio::test] async fn test_arguments_with_gas_forwarded() -> Result<()> { + let [wallet]: [WalletUnlocked; 1] = maybe_live_wallet(1) + .await? + .try_into() + .expect("Vec can be converted to an array"); setup_program_test!( - Wallets("wallet"), Abigen( Contract( name = "TestContract", @@ -1710,7 +1760,8 @@ async fn test_arguments_with_gas_forwarded() -> Result<()> { contract_instance_2 .methods() .u32_vec(vec_input.clone()) - .call_params(CallParameters::default().with_gas_forwarded(1024))? + // from tests, it seems that it uses ~2500gas + .call_params(CallParameters::default().with_gas_forwarded(3000))? .call() .await?; } diff --git a/packages/fuels/tests/providers.rs b/packages/fuels/tests/providers.rs index dd66d81135..02a72d2d89 100644 --- a/packages/fuels/tests/providers.rs +++ b/packages/fuels/tests/providers.rs @@ -587,7 +587,7 @@ async fn testnet_hello_world() -> Result<()> { )); // Create a provider pointing to the testnet. - let provider = Provider::connect("beta-5.fuel.network").await.unwrap(); + let provider = Provider::connect(TESTNET_NODE_URL).await.unwrap(); // Setup the private key. let secret = diff --git a/packages/fuels/tests/scripts.rs b/packages/fuels/tests/scripts.rs index ea66d7f92f..24b3e52d77 100644 --- a/packages/fuels/tests/scripts.rs +++ b/packages/fuels/tests/scripts.rs @@ -1,5 +1,7 @@ use fuels::{prelude::*, types::Bits256}; use fuels_core::codec::DecoderConfig; +use std::thread::sleep; +use std::time::Duration; #[tokio::test] async fn test_transaction_script_workflow() -> Result<()> { @@ -85,8 +87,11 @@ async fn main_function_arguments() -> Result<()> { #[tokio::test] async fn script_call_has_same_estimated_and_used_gas() -> Result<()> { + let [wallet]: [WalletUnlocked; 1] = maybe_live_wallet(1) + .await? + .try_into() + .expect("Vec can be converted to an array"); setup_program_test!( - Wallets("wallet"), Abigen(Script( name = "MyScript", project = "packages/fuels/tests/scripts/basic_script" @@ -116,8 +121,11 @@ async fn script_call_has_same_estimated_and_used_gas() -> Result<()> { #[tokio::test] async fn test_basic_script_with_tx_policies() -> Result<()> { + let [wallet]: [WalletUnlocked; 1] = maybe_live_wallet(1) + .await? + .try_into() + .expect("Vec can be converted to an array"); setup_program_test!( - Wallets("wallet"), Abigen(Script( name = "bimbam_script", project = "packages/fuels/tests/scripts/basic_script" @@ -378,8 +386,11 @@ async fn can_configure_decoder_on_script_call() -> Result<()> { #[tokio::test] async fn test_script_submit_and_response() -> Result<()> { + let [wallet]: [WalletUnlocked; 1] = maybe_live_wallet(1) + .await? + .try_into() + .expect("Vec can be converted to an array"); setup_program_test!( - Wallets("wallet"), Abigen(Script( name = "MyScript", project = "packages/fuels/tests/scripts/script_struct" @@ -398,6 +409,10 @@ async fn test_script_submit_and_response() -> Result<()> { // ANCHOR: submit_response_script let submitted_tx = script_instance.main(my_struct).submit().await?; + if cfg!(feature = "test-against-live-node") { + // add delay when testing against live node to make sure the receipts propagate + sleep(Duration::from_secs(10)); + } let value = submitted_tx.response().await?.value; // ANCHOR_END: submit_response_script