From 30913937e2e4ff38269110774043a480d7aeda00 Mon Sep 17 00:00:00 2001 From: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> Date: Fri, 22 Mar 2024 17:39:40 +0100 Subject: [PATCH] Update private tangle tests so they work with 2.0 (#2191) * Update private tangle tests so they work with 2.0 * Fix more tests, don't select inputs for mana when they don't add anything * Use nova branch for ledger nano simulator * Uncomment and update test * Update failing tests * Build ledger simulator with default IOTA coin type * Update sdk/tests/wallet/common/mod.rs Co-authored-by: DaughterOfMars * Review suggestions * Fix implicit_account break * Add and use slots_in_duration() * Make instant dep required * Add and use ProtocolParameters::duration_of_slots() * no_std * comment before attribute, allow_additional_input_selection for consolidation since we need mana * Fix inputs ordering --------- Co-authored-by: DaughterOfMars Co-authored-by: Thibault Martinez --- .github/actions/ledger-nano/action.yml | 4 +- .github/workflows/private-tangle-tests.yml | 5 +- bindings/core/tests/combined.rs | 18 ++- sdk/Cargo.toml | 2 +- .../block_builder/transaction_builder/mod.rs | 7 +- .../transaction_builder/remainder.rs | 2 +- sdk/src/types/block/output/basic.rs | 9 +- sdk/src/types/block/protocol/mod.rs | 14 +++ .../wallet/operations/output_consolidation.rs | 1 - sdk/tests/client/common/constants.rs | 2 +- sdk/tests/client/node_api/core.rs | 21 ++-- sdk/tests/client/node_api/mod.rs | 59 ++++++++-- sdk/tests/wallet/balance.rs | 29 ++--- sdk/tests/wallet/burn_outputs.rs | 44 ++++---- sdk/tests/wallet/claim_outputs.rs | 105 +++++++++++++++--- sdk/tests/wallet/common/constants.rs | 2 +- sdk/tests/wallet/common/mod.rs | 81 ++++++++++++-- sdk/tests/wallet/consolidation.rs | 9 +- sdk/tests/wallet/output_preparation.rs | 102 ++++++++--------- sdk/tests/wallet/transactions.rs | 61 +++++----- 20 files changed, 375 insertions(+), 202 deletions(-) diff --git a/.github/actions/ledger-nano/action.yml b/.github/actions/ledger-nano/action.yml index 36d162f742..f6e0a102d5 100644 --- a/.github/actions/ledger-nano/action.yml +++ b/.github/actions/ledger-nano/action.yml @@ -7,7 +7,7 @@ runs: uses: actions/checkout@v3 with: repository: iotaledger/ledger-iota-app - ref: develop + ref: feat/nova path: ledger-iota-app - name: Update submodules @@ -17,5 +17,5 @@ runs: - name: Run the simulator shell: bash - run: ./build.sh -s -v shimmer -b + run: ./build.sh -s -b working-directory: ledger-iota-app diff --git a/.github/workflows/private-tangle-tests.yml b/.github/workflows/private-tangle-tests.yml index 92a9b8494e..92e7a00a92 100644 --- a/.github/workflows/private-tangle-tests.yml +++ b/.github/workflows/private-tangle-tests.yml @@ -65,10 +65,7 @@ jobs: uses: "./.github/actions/ledger-nano" - name: Run tests - run: | - cargo nextest run test_get_info --all-features --run-ignored ignored-only --profile ci --cargo-profile ci -p iota-sdk -p iota-sdk-bindings-core --no-capture - # TODO: change in the future to run all tests and not only test_get_info - # cargo ci-tangle-test + run: cargo ci-tangle-test - name: Tear down private tangle if: always() diff --git a/bindings/core/tests/combined.rs b/bindings/core/tests/combined.rs index 06e4822fcd..423f68bff4 100644 --- a/bindings/core/tests/combined.rs +++ b/bindings/core/tests/combined.rs @@ -95,17 +95,13 @@ async fn client_from_wallet() -> Result<(), Error> { .build() .await?; - // TODO reenable - // // Send ClientMethod via the client from the wallet - // let response = wallet - // .client() - // .call_method(ClientMethod::GetHealth) - // .await; - - // match response { - // Response::Bool(_) => {} - // _ => panic!("unexpected response {response:?}"), - // } + // Send ClientMethod via the client from the wallet + let response = wallet.client().call_method(ClientMethod::GetProtocolParameters).await; + + match response { + Response::ProtocolParameters(_) => {} + _ => panic!("unexpected response {response:?}"), + } std::fs::remove_dir_all(storage_path).ok(); Ok(()) diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 964df23bc9..fbbad3ba1b 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -176,6 +176,7 @@ std = [ "iota_stronghold?/std", "iota-crypto/std", "once_cell?/std", + "dep:instant" ] storage = ["iota-crypto/chacha", "dep:time", "dep:anymap", "dep:once_cell"] stronghold = [ @@ -199,7 +200,6 @@ client = [ "iota-crypto/slip10", "dep:async-trait", "dep:futures", - "dep:instant", "dep:log", "dep:reqwest", "dep:thiserror", diff --git a/sdk/src/client/api/block_builder/transaction_builder/mod.rs b/sdk/src/client/api/block_builder/transaction_builder/mod.rs index ea26d99a5f..d3f8c7ea73 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/mod.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/mod.rs @@ -449,8 +449,9 @@ impl TransactionBuilder { return Err(TransactionBuilderError::InvalidOutputCount(outputs.len())); } + let inputs_data: Vec = self.selected_inputs.into_sorted_iter().collect(); for output_id in self.mana_rewards.keys() { - if !self.selected_inputs.iter().any(|i| output_id == i.output_id()) { + if !inputs_data.iter().any(|i| output_id == i.output_id()) { return Err(TransactionBuilderError::ExtraManaRewards(*output_id)); } } @@ -463,7 +464,7 @@ impl TransactionBuilder { .chain(self.commitment_context_input.map(ContextInput::from)) .collect::>(); - for (idx, input) in self.selected_inputs.iter().enumerate() { + for (idx, input) in inputs_data.iter().enumerate() { inputs.push(Input::Utxo(UtxoInput::from(*input.output_id()))); if self.reward_context_inputs.contains(input.output_id()) { context_inputs.push(RewardContextInput::new(idx as u16).unwrap().into()); @@ -494,7 +495,7 @@ impl TransactionBuilder { let data = PreparedTransactionData { transaction, - inputs_data: self.selected_inputs.into_sorted_iter().collect(), + inputs_data, remainders: self.remainders.data, mana_rewards: self.mana_rewards.into_iter().collect(), }; diff --git a/sdk/src/client/api/block_builder/transaction_builder/remainder.rs b/sdk/src/client/api/block_builder/transaction_builder/remainder.rs index de55b5ffc7..04fadac54f 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/remainder.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/remainder.rs @@ -293,7 +293,7 @@ impl TransactionBuilder { self.remainders.data.push(RemainderData { output: catchall, chain: remainder_address_chain, - address: remainder_address.clone(), + address: remainder_address, }); Ok(()) diff --git a/sdk/src/types/block/output/basic.rs b/sdk/src/types/block/output/basic.rs index e6761ea619..01e10b7934 100644 --- a/sdk/src/types/block/output/basic.rs +++ b/sdk/src/types/block/output/basic.rs @@ -517,14 +517,9 @@ mod tests { use super::*; use crate::types::block::{ - output::{basic::dto::BasicOutputDto, FoundryId, SimpleTokenScheme, TokenId}, + output::basic::dto::BasicOutputDto, protocol::iota_mainnet_protocol_parameters, - rand::{ - address::rand_account_address, - output::{ - feature::rand_allowed_features, rand_basic_output, unlock_condition::rand_address_unlock_condition, - }, - }, + rand::output::{rand_basic_output, unlock_condition::rand_address_unlock_condition}, }; #[test] diff --git a/sdk/src/types/block/protocol/mod.rs b/sdk/src/types/block/protocol/mod.rs index 92757829bf..94b9054132 100644 --- a/sdk/src/types/block/protocol/mod.rs +++ b/sdk/src/types/block/protocol/mod.rs @@ -10,6 +10,8 @@ use core::borrow::Borrow; use crypto::hashes::{blake2b::Blake2b256, Digest}; use getset::{CopyGetters, Getters}; +#[cfg(feature = "std")] +use instant::Duration; use packable::{prefix::StringPrefix, Packable, PackableExt}; #[cfg(feature = "protocol_parameters_samples")] pub use samples::{iota_mainnet_protocol_parameters, shimmer_mainnet_protocol_parameters}; @@ -173,6 +175,18 @@ impl ProtocolParameters { } } + /// Calculates the number of slots in a duration. + #[cfg(feature = "std")] + pub fn slots_in_duration(&self, duration: Duration) -> u32 { + (duration.as_secs() / self.slot_duration_in_seconds() as u64) as u32 + } + + /// Calculates the [`Duration`] of a number of slots. + #[cfg(feature = "std")] + pub fn duration_of_slots(&self, slots: u32) -> Duration { + Duration::from_secs((slots * self.slot_duration_in_seconds() as u32) as u64) + } + /// Gets the [`EpochIndex`] of a given [`SlotIndex`]. pub fn epoch_index_of(&self, slot_index: impl Into) -> EpochIndex { EpochIndex::from_slot_index(slot_index, self.genesis_slot, self.slots_per_epoch_exponent()) diff --git a/sdk/src/wallet/operations/output_consolidation.rs b/sdk/src/wallet/operations/output_consolidation.rs index 16e8d2332f..3210361b0b 100644 --- a/sdk/src/wallet/operations/output_consolidation.rs +++ b/sdk/src/wallet/operations/output_consolidation.rs @@ -116,7 +116,6 @@ where .map(|bech32| bech32.into_inner()) .unwrap_or_else(|| wallet_address.into_inner()), ), - allow_additional_input_selection: false, ..Default::default() }); diff --git a/sdk/tests/client/common/constants.rs b/sdk/tests/client/common/constants.rs index c06eb0d4e6..9fe5aa7574 100644 --- a/sdk/tests/client/common/constants.rs +++ b/sdk/tests/client/common/constants.rs @@ -3,6 +3,6 @@ pub static NODE_LOCAL: &str = "http://localhost:8050"; -pub static FAUCET_URL: &str = "http://localhost:8091/api/enqueue"; +pub static FAUCET_URL: &str = "http://localhost:8088/api/enqueue"; pub static DEFAULT_MNEMONIC: &str = "inhale gorilla deny three celery song category owner lottery rent author wealth penalty crawl hobby obtain glad warm early rain clutch slab august bleak"; diff --git a/sdk/tests/client/node_api/core.rs b/sdk/tests/client/node_api/core.rs index e276993666..7aade154cb 100644 --- a/sdk/tests/client/node_api/core.rs +++ b/sdk/tests/client/node_api/core.rs @@ -55,8 +55,7 @@ async fn test_get_issuance() { #[ignore] #[tokio::test] async fn test_post_block_with_tagged_data() { - let secret_manager = setup_secret_manager(); - let block_id = setup_tagged_data_block(&secret_manager).await; + let block_id = setup_tagged_data_block().await.unwrap(); println!("{block_id}"); } @@ -72,9 +71,8 @@ async fn test_post_block_with_transaction() { #[tokio::test] async fn test_get_block_data() { let client = setup_client_with_node_health_ignored().await; - let secret_manager = setup_secret_manager(); - let block_id = setup_tagged_data_block(&secret_manager).await; + let block_id = setup_tagged_data_block().await.unwrap(); let r = client.get_block(&block_id).await.unwrap(); println!("{r:#?}"); @@ -83,8 +81,7 @@ async fn test_get_block_data() { #[ignore] #[tokio::test] async fn test_get_block_metadata() { - let secret_manager = setup_secret_manager(); - let block_id = setup_tagged_data_block(&secret_manager).await; + let block_id = setup_tagged_data_block().await.unwrap(); let r = setup_client_with_node_health_ignored() .await @@ -98,8 +95,7 @@ async fn test_get_block_metadata() { #[ignore] #[tokio::test] async fn test_get_block_raw() { - let secret_manager = setup_secret_manager(); - let block_id = setup_tagged_data_block(&secret_manager).await; + let block_id = setup_tagged_data_block().await.unwrap(); let r = setup_client_with_node_health_ignored() .await @@ -201,7 +197,7 @@ async fn test_call_plugin_route() { // we call the "custom" plugin "node info" let plugin_res: NodeInfoResponse = c - .call_plugin_route("api/core/v2/", "GET", "info", vec![], None) + .call_plugin_route("api/core/v3/", "GET", "info", vec![], None) .await .unwrap(); @@ -218,7 +214,7 @@ async fn test_get_routes() { let routes_response = client.get_routes().await.unwrap(); // At at least one route, which is not created by plugin, is available - assert!(routes_response.routes.contains(&"core/v2".to_string())); + assert!(routes_response.routes.contains(&"core/v3".to_string())); println!("{routes_response:#?}"); } @@ -231,7 +227,10 @@ async fn test_get_included_block_metadata() { let metadata_response = client.get_included_block_metadata(&transaction_id).await.unwrap(); assert_eq!(metadata_response.block_id, block_id); - assert_eq!(metadata_response.block_state, BlockState::Finalized); + match metadata_response.block_state { + BlockState::Accepted | BlockState::Confirmed | BlockState::Finalized => {} + _ => panic!("block state is not accepted/confirmed/finalized"), + } println!("{metadata_response:#?}"); } diff --git a/sdk/tests/client/node_api/mod.rs b/sdk/tests/client/node_api/mod.rs index 1d5a9edb0c..31b0304ce2 100644 --- a/sdk/tests/client/node_api/mod.rs +++ b/sdk/tests/client/node_api/mod.rs @@ -11,12 +11,14 @@ use iota_sdk::{ client::{ api::GetAddressesOptions, constants::IOTA_COIN_TYPE, + generate_mnemonic, node_api::indexer::query_parameters::BasicOutputQueryParameters, request_funds_from_faucet, - secret::{SecretManager, SignBlock}, + secret::{mnemonic::MnemonicSecretManager, SecretManager, SignBlock}, Client, }, types::block::{ + address::{Address, Bech32Address, ImplicitAccountCreationAddress}, output::AccountId, payload::{signed_transaction::TransactionId, tagged_data::TaggedDataPayload, Payload}, BlockId, @@ -29,24 +31,63 @@ use crate::client::common::{setup_client_with_node_health_ignored, FAUCET_URL}; const DEFAULT_DEVELOPMENT_SEED: &str = "0x256a818b2aac458941f7274985a410e57fb750f3a3a67969ece5bd9ae7eef5b2"; // Sends a tagged data block to the node to test against it. -async fn setup_tagged_data_block(secret_manager: &SecretManager) -> BlockId { +async fn setup_tagged_data_block() -> Result> { let client = setup_client_with_node_health_ignored().await; - let protocol_params = client.get_protocol_parameters().await.unwrap(); + let secret_manager = SecretManager::Mnemonic(MnemonicSecretManager::try_from_mnemonic(generate_mnemonic()?)?); + + let bech32_hrp = client.get_bech32_hrp().await?; + + let address = secret_manager + .generate_ed25519_address(IOTA_COIN_TYPE, 0, 0, bech32_hrp, None) + .await?; + let address = + Address::ImplicitAccountCreation(ImplicitAccountCreationAddress::new(**address.into_inner().as_ed25519())); + let bech32_address = Bech32Address::new(bech32_hrp, address); + + request_funds_from_faucet(FAUCET_URL, &bech32_address).await?; - client + let mut account_id = AccountId::null(); + // Continue only after funds are received + for i in 0..30 { + let output_ids = client + .basic_output_ids(BasicOutputQueryParameters::only_address_unlock_condition( + bech32_address.clone(), + )) + .await? + .items; + if !output_ids.is_empty() { + account_id = AccountId::from(&output_ids[0]); + break; + } + if i == 29 { + panic!("Faucet no longer wants to hand over coins"); + } + tokio::time::sleep(std::time::Duration::from_secs(2)).await; + } + // Wait until account is read to issue blocks + for _ in 0..60 { + if client.get_account_congestion(&account_id, None).await.is_ok() { + break; + } + tokio::time::sleep(std::time::Duration::from_secs(2)).await; + } + + let block = client .build_basic_block( - AccountId::null(), + account_id, Some(Payload::TaggedData(Box::new( TaggedDataPayload::new(b"Hello".to_vec(), b"Tangle".to_vec()).unwrap(), ))), ) .await .unwrap() - .sign_ed25519(secret_manager, Bip44::new(IOTA_COIN_TYPE)) - .await - .unwrap() - .id(&protocol_params) + .sign_ed25519(&secret_manager, Bip44::new(IOTA_COIN_TYPE)) + .await?; + client.post_block(&block).await?; + + let protocol_params = client.get_protocol_parameters().await.unwrap(); + Ok(block.id(&protocol_params)) } pub fn setup_secret_manager() -> SecretManager { diff --git a/sdk/tests/wallet/balance.rs b/sdk/tests/wallet/balance.rs index 0fd52dc90b..fde0e9ffe4 100644 --- a/sdk/tests/wallet/balance.rs +++ b/sdk/tests/wallet/balance.rs @@ -141,7 +141,7 @@ async fn balance_expiration() -> Result<(), Box> { request_funds(&wallet_0).await?; - let slots_until_expired = 20; + let slots_until_expired = 5; let outputs = [BasicOutputBuilder::new_with_amount(1_000_000) // Send to account 1 with expiration to account 2, both have no amount yet .with_unlock_conditions([ @@ -162,7 +162,7 @@ async fn balance_expiration() -> Result<(), Box> { balance_before_tx.base_coin().total(), balance_after_tx.base_coin().total() ); - assert_eq!(balance_after_tx.base_coin().available(), 0); + assert_eq!(balance_after_tx.base_coin().available(), 999968300); wallet_0 .wait_for_transaction_acceptance(&tx.transaction_id, None, None) @@ -203,13 +203,6 @@ async fn balance_expiration() -> Result<(), Box> { assert_eq!(balance.base_coin().total(), 1_000_000); assert_eq!(balance.base_coin().available(), 1_000_000); - // It's possible to send the expired output - let outputs = [BasicOutputBuilder::new_with_amount(1_000_000) - // Send to wallet 1 with expiration to wallet 2, both have no amount yet - .with_unlock_conditions([AddressUnlockCondition::new(wallet_1.address().await)]) - .finish_output()?]; - let _tx = wallet_2.send_outputs(outputs, None).await?; - tear_down(storage_path_0)?; tear_down(storage_path_1)?; tear_down(storage_path_2)?; @@ -218,7 +211,7 @@ async fn balance_expiration() -> Result<(), Box> { #[ignore] #[tokio::test] -async fn balance_transfer() -> Result<(), Box> { +async fn available_balance_transfer() -> Result<(), Box> { let storage_path_0 = "test-storage/addresses_balance_0"; let storage_path_1 = "test-storage/addresses_balance_1"; setup(storage_path_0)?; @@ -229,13 +222,11 @@ async fn balance_transfer() -> Result<(), Box> { request_funds(&wallet_0).await?; - let balance_0 = wallet_0.balance().await?; - let balance_0_sync = wallet_0.sync(None).await?; + let balance_0 = wallet_0.sync(None).await?; let to_send = balance_0.base_coin().available(); // Check if 0 has balance and sync() and address_balance() match assert!(to_send > 0); - assert_eq!(balance_0, balance_0_sync); // Make sure 1 is empty let balance_1 = wallet_1.sync(None).await?; @@ -244,20 +235,22 @@ async fn balance_transfer() -> Result<(), Box> { // Send to 1 let tx = wallet_0.send(to_send, wallet_1.address().await, None).await?; - // Balance should update without sync + // Available balance should update without sync let balance_0 = wallet_0.balance().await?; let balance_0_sync = wallet_0.sync(None).await?; assert_eq!(balance_0.base_coin().available(), 0); - assert_eq!(balance_0, balance_0_sync); + assert_eq!( + balance_0.base_coin().available(), + balance_0_sync.base_coin().available() + ); wallet_0 .wait_for_transaction_acceptance(&tx.transaction_id, None, None) .await?; - // Balance should have transferred entirely - let balance_1_sync = wallet_1.sync(None).await?; + // Available balance should have transferred entirely + let balance_1 = wallet_1.sync(None).await?; assert!(balance_1.base_coin().available() > 0); - assert_eq!(balance_1, balance_1_sync); tear_down(storage_path_0)?; tear_down(storage_path_1)?; diff --git a/sdk/tests/wallet/burn_outputs.rs b/sdk/tests/wallet/burn_outputs.rs index 7d7dd7209b..722eb77105 100644 --- a/sdk/tests/wallet/burn_outputs.rs +++ b/sdk/tests/wallet/burn_outputs.rs @@ -6,7 +6,7 @@ use iota_sdk::{ types::block::output::{ feature::MetadataFeature, unlock_condition::{AddressUnlockCondition, ExpirationUnlockCondition}, - NativeToken, NftId, NftOutputBuilder, OutputId, UnlockCondition, + AccountId, NativeToken, NftId, NftOutputBuilder, OutputId, UnlockCondition, }, wallet::{CreateNativeTokenParams, MintNftParams, Wallet}, U256, @@ -60,12 +60,15 @@ async fn mint_and_burn_nft() -> Result<(), Box> { #[ignore] #[tokio::test] async fn mint_and_burn_expired_nft() -> Result<(), Box> { - let storage_path = "test-storage/mint_and_burn_expired_nft"; - setup(storage_path)?; + let storage_path_0 = "test-storage/mint_and_burn_expired_nft_0"; + let storage_path_1 = "test-storage/mint_and_burn_expired_nft_1"; + setup(storage_path_0)?; + setup(storage_path_1)?; - let wallet_0 = make_wallet(storage_path, None, None).await?; - let wallet_1 = make_wallet(storage_path, None, None).await?; + let wallet_0 = make_wallet(storage_path_0, None, None).await?; + let wallet_1 = make_wallet(storage_path_1, None, None).await?; request_funds(&wallet_0).await?; + request_funds(&wallet_1).await?; let amount = 1_000_000; let outputs = [NftOutputBuilder::new_with_amount(amount, NftId::null()) @@ -90,10 +93,10 @@ async fn mint_and_burn_expired_nft() -> Result<(), Box> { .wait_for_transaction_acceptance(&transaction.transaction_id, None, None) .await?; let balance = wallet_1.sync(None).await?; - // After burning the amount is available on account_1 - assert_eq!(balance.base_coin().available(), amount); + assert_eq!(balance.nfts().len(), 0); - tear_down(storage_path) + tear_down(storage_path_0)?; + tear_down(storage_path_1) } #[ignore] @@ -108,6 +111,7 @@ async fn create_and_melt_native_token() -> Result<(), Box // First create an account output, this needs to be done only once, because an account can have many foundry outputs let transaction = wallet.create_account_output(None, None).await?; + let account_id = AccountId::from(&OutputId::new(transaction.transaction_id, 0)); wallet .wait_for_transaction_acceptance(&transaction.transaction_id, None, None) .await?; @@ -115,7 +119,7 @@ async fn create_and_melt_native_token() -> Result<(), Box let circulating_supply = U256::from(60i32); let params = CreateNativeTokenParams { - account_id: None, + account_id: Some(account_id), circulating_supply, maximum_supply: U256::from(100i32), foundry_metadata: None, @@ -172,7 +176,7 @@ async fn create_and_melt_native_token() -> Result<(), Box // Call to run tests in sequence destroy_foundry(&wallet).await?; - destroy_account(&wallet).await?; + destroy_account(&wallet, account_id).await?; tear_down(storage_path) } @@ -200,12 +204,10 @@ async fn destroy_foundry(wallet: &Wallet) -> Result<(), Box Result<(), Box> { +async fn destroy_account(wallet: &Wallet, account_id: AccountId) -> Result<(), Box> { let balance = wallet.sync(None).await.unwrap(); println!("account balance -> {}", serde_json::to_string(&balance).unwrap()); - // Let's destroy the first account we can find - let account_id = *balance.accounts().first().unwrap(); println!("account_id -> {account_id}"); let transaction = wallet.burn(account_id, None).await.unwrap(); wallet @@ -234,12 +236,6 @@ async fn create_and_burn_native_tokens() -> Result<(), Box Result<(), Box Result<(), Box Result<(), Box> setup(storage_path_1)?; let wallet_0 = make_wallet(storage_path_0, None, None).await?; - let wallet_1 = make_wallet(storage_path_0, None, None).await?; + let wallet_1 = make_wallet(storage_path_1, None, None).await?; request_funds(&wallet_0).await?; request_funds(&wallet_1).await?; @@ -130,9 +134,9 @@ async fn claim_1_of_2_basic_outputs() -> Result<(), Box> #[ignore] #[tokio::test] -async fn claim_2_basic_outputs_no_outputs_in_claim_account() -> Result<(), Box> { - let storage_path_0 = "test-storage/claim_2_basic_outputs_no_outputs_in_claim_account_0"; - let storage_path_1 = "test-storage/claim_2_basic_outputs_no_outputs_in_claim_account_1"; +async fn claim_2_basic_outputs_no_available_in_claim_account() -> Result<(), Box> { + let storage_path_0 = "test-storage/claim_2_basic_outputs_no_available_in_claim_account_0"; + let storage_path_1 = "test-storage/claim_2_basic_outputs_no_available_in_claim_account_1"; setup(storage_path_0)?; setup(storage_path_1)?; @@ -140,10 +144,31 @@ async fn claim_2_basic_outputs_no_outputs_in_claim_account() -> Result<(), Box Result<(), Box> { #[ignore] #[tokio::test] -async fn claim_2_native_tokens_no_outputs_in_claim_account() -> Result<(), Box> { - let storage_path_0 = "test-storage/claim_2_native_tokens_no_outputs_in_claim_account_0"; - let storage_path_1 = "test-storage/claim_2_native_tokens_no_outputs_in_claim_account_1"; +async fn claim_2_native_tokens_no_available_balance_in_claim_account() -> Result<(), Box> { + let storage_path_0 = "test-storage/claim_2_native_tokens_no_available_balance_in_claim_account_0"; + let storage_path_1 = "test-storage/claim_2_native_tokens_no_available_balance_in_claim_account_1"; setup(storage_path_0)?; setup(storage_path_1)?; @@ -288,6 +313,22 @@ async fn claim_2_native_tokens_no_outputs_in_claim_account() -> Result<(), Box Result<(), Box> { #[ignore] #[tokio::test] -async fn claim_2_nft_outputs_no_outputs_in_claim_account() -> Result<(), Box> { - let storage_path_0 = "test-storage/claim_2_nft_outputs_no_outputs_in_claim_wallet_0"; - let storage_path_1 = "test-storage/claim_2_nft_outputs_no_outputs_in_claim_wallet_1"; +async fn claim_2_nft_outputs_no_available_in_claim_account() -> Result<(), Box> { + let storage_path_0 = "test-storage/claim_2_nft_outputs_no_available_in_claim_account_0"; + let storage_path_1 = "test-storage/claim_2_nft_outputs_no_available_in_claim_account_1"; setup(storage_path_0)?; setup(storage_path_1)?; @@ -457,6 +498,22 @@ async fn claim_2_nft_outputs_no_outputs_in_claim_account() -> Result<(), Box Result<(), Box Result<(), Box Result<(), Box> { + request_funds_from_faucet(FAUCET_URL, &wallet.implicit_account_creation_address().await?).await?; request_funds_from_faucet(FAUCET_URL, &wallet.address().await).await?; // Continue only after funds are received - for _ in 0..30 { + let mut attempts = 0; + let implicit_account = loop { tokio::time::sleep(std::time::Duration::from_secs(2)).await; - let balance = wallet.sync(None).await?; - if balance.base_coin().available() > 0 { - return Ok(()); + wallet + .sync(Some(SyncOptions { + sync_implicit_accounts: true, + ..Default::default() + })) + .await?; + if let Some(account) = wallet.ledger().await.implicit_accounts().next() { + break account.clone(); + } + attempts += 1; + if attempts == 30 { + panic!("Faucet no longer wants to hand over coins"); } + }; + + let mut tries = 0; + while let Err(ClientError::Node(iota_sdk::client::node_api::error::Error::NotFound(_))) = wallet + .client() + .get_account_congestion(&AccountId::from(&implicit_account.output_id), None) + .await + { + tries += 1; + tokio::time::sleep(std::time::Duration::from_secs(2)).await; + if tries > 100 { + panic!("Can't get account for implicit account"); + } + } + + let transaction = wallet + .implicit_account_transition( + &implicit_account.output_id, + BlockIssuerKeySource::ImplicitAccountAddress, + ) + .await?; + + wallet + .wait_for_transaction_acceptance(&transaction.transaction_id, None, None) + .await?; + + wallet.sync(None).await?; + + // Is this better than using the congestion endpoint? + // prepare a big tx and wait the time it takes until enough mana is generated + #[allow(unused_variables)] + if let Err(WalletError::Client(ClientError::TransactionBuilder(TransactionBuilderError::InsufficientMana { + slots_remaining, + .. + }))) = wallet + .prepare_send(vec![SendParams::new(1_000_000, wallet.address().await)?; 10], None) + .await + { + tokio::time::sleep( + wallet + .client() + .get_protocol_parameters() + .await? + .duration_of_slots(slots_remaining), + ) + .await; } - panic!("Faucet no longer wants to hand over coins"); + + Ok(()) } #[allow(dead_code)] diff --git a/sdk/tests/wallet/consolidation.rs b/sdk/tests/wallet/consolidation.rs index 87cb3d3d67..eb85b09afa 100644 --- a/sdk/tests/wallet/consolidation.rs +++ b/sdk/tests/wallet/consolidation.rs @@ -18,6 +18,7 @@ async fn consolidation() -> Result<(), Box> { let wallet_1 = make_wallet(storage_path_1, None, None).await?; request_funds(&wallet_0).await?; + request_funds(&wallet_1).await?; // Send 10 outputs to wallet_1 let amount = 1_000_000; @@ -30,8 +31,8 @@ async fn consolidation() -> Result<(), Box> { .await?; let balance = wallet_1.sync(None).await.unwrap(); - assert_eq!(balance.base_coin().available(), 10 * amount); - assert_eq!(wallet_1.ledger().await.unspent_outputs().len(), 10); + assert_eq!(balance.base_coin().available(), 2009968300); + assert_eq!(wallet_1.ledger().await.unspent_outputs().len(), 12); let tx = wallet_1 .consolidate_outputs(ConsolidationParams::new().with_force(true)) @@ -42,8 +43,8 @@ async fn consolidation() -> Result<(), Box> { let balance = wallet_1.sync(None).await.unwrap(); // Balance still the same - assert_eq!(balance.base_coin().available(), 10 * amount); - // Only one unspent output + assert_eq!(balance.base_coin().available(), 2009968300); + // Account output assert_eq!(wallet_1.ledger().await.unspent_outputs().len(), 1); tear_down(storage_path_0)?; diff --git a/sdk/tests/wallet/output_preparation.rs b/sdk/tests/wallet/output_preparation.rs index 378e0c00c0..1cab220741 100644 --- a/sdk/tests/wallet/output_preparation.rs +++ b/sdk/tests/wallet/output_preparation.rs @@ -43,11 +43,11 @@ async fn output_preparation() -> Result<(), Box> { None, ) .await?; - assert_eq!(output.amount(), 46800); + assert_eq!(output.amount(), 18300); // address and sdr unlock condition assert_eq!(output.unlock_conditions().len(), 2); let sdr = output.unlock_conditions().storage_deposit_return().unwrap(); - assert_eq!(sdr.amount(), 46300); + assert_eq!(sdr.amount(), 17800); let output = wallet .prepare_output( @@ -139,13 +139,13 @@ async fn output_preparation() -> Result<(), Box> { None, ) .await?; - assert_eq!(output.amount(), 49000); + let min_amount_with_metadata_and_tag = 21100; + assert_eq!(output.amount(), min_amount_with_metadata_and_tag); let unlock_conditions = output.unlock_conditions(); // address + sdr assert_eq!(unlock_conditions.len(), 2); let storage_deposit_return = unlock_conditions.storage_deposit_return().unwrap(); - // output amount -1 - assert_eq!(storage_deposit_return.amount(), 48999); + assert_eq!(storage_deposit_return.amount(), min_amount_with_metadata_and_tag - 1); // metadata and tag features assert_eq!(output.features().unwrap().len(), 2); @@ -168,7 +168,7 @@ async fn output_preparation() -> Result<(), Box> { None, ) .await?; - assert_eq!(output.amount(), 54600); + assert_eq!(output.amount(), 26100); // address and storage deposit unlock condition, because of the metadata feature block, 12000 is not enough for the // required storage deposit assert_eq!(output.unlock_conditions().len(), 2); @@ -194,9 +194,9 @@ async fn output_preparation() -> Result<(), Box> { None, ) .await?; - assert_eq!(output.amount(), 49000); + assert_eq!(output.amount(), min_amount_with_metadata_and_tag); let sdr = output.unlock_conditions().storage_deposit_return().unwrap(); - assert_eq!(sdr.amount(), 48999); + assert_eq!(sdr.amount(), min_amount_with_metadata_and_tag - 1); // address and storage deposit unlock condition, because of the metadata feature block, 213000 is not enough for the // required storage deposit @@ -279,11 +279,11 @@ async fn output_preparation() -> Result<(), Box> { ) .await?; - assert_eq!(output.kind(), iota_sdk::types::block::output::BasicOutput::KIND); + assert_eq!(output.kind(), BasicOutput::KIND); assert_eq!(output.amount(), 500000); assert_eq!(output.unlock_conditions().len(), 1); let features = output.features().unwrap(); - assert_eq!(features.len(), 1); + assert_eq!(features.len(), 2); assert_eq!(features.sender().unwrap().address(), expected_address); // error when adding issuer when building basic output @@ -356,7 +356,7 @@ async fn output_preparation() -> Result<(), Box> { assert!(conditions.is_timelocked(0, 0)); assert_eq!( conditions.is_expired(2, CommittableAgeRange { min: 0, max: 0 }), - Some(false) + Some(true) ); // nft with expiration @@ -387,7 +387,7 @@ async fn output_preparation() -> Result<(), Box> { ) .await?; assert_eq!(output.kind(), iota_sdk::types::block::output::NftOutput::KIND); - assert_eq!(output.amount(), 53900); + assert_eq!(output.amount(), 25400); // address, sdr, expiration assert_eq!(output.unlock_conditions().len(), 3); @@ -418,9 +418,9 @@ async fn output_preparation() -> Result<(), Box> { let storage_score_params = wallet.client().get_storage_score_parameters().await?; let minimum_amount = output.minimum_amount(storage_score_params); assert_eq!(output.amount(), minimum_amount); - assert_eq!(output.amount(), 187900); + assert_eq!(output.amount(), 160000); let sdr = output.unlock_conditions().storage_deposit_return().unwrap(); - assert_eq!(sdr.amount(), 145300); + assert_eq!(sdr.amount(), 117400); // address and storage deposit unlock condition, because of the metadata feature block, 42600 is not enough for the // required storage deposit assert_eq!(output.unlock_conditions().len(), 2); @@ -450,7 +450,7 @@ async fn output_preparation_sdr() -> Result<(), Box> { .prepare_output( OutputParams { recipient_address: recipient_address.clone(), - amount: 8001, + amount: 4001, assets: None, features: None, unlocks: None, @@ -461,17 +461,19 @@ async fn output_preparation_sdr() -> Result<(), Box> { .await?; // Check if the output has enough amount to cover the storage deposit output.verify_storage_deposit(storage_score_params)?; - assert_eq!(output.amount(), 50601); + assert_eq!(output.amount(), 18300); // address and sdr unlock condition assert_eq!(output.unlock_conditions().len(), 2); let sdr = output.unlock_conditions().storage_deposit_return().unwrap(); - assert_eq!(sdr.amount(), 42600); + assert_eq!(sdr.amount(), 14299); + + let min_amount = 14100; let output = wallet .prepare_output( OutputParams { recipient_address: recipient_address.clone(), - amount: 42599, + amount: min_amount - 1, assets: None, features: None, unlocks: None, @@ -482,18 +484,18 @@ async fn output_preparation_sdr() -> Result<(), Box> { .await?; // Check if the output has enough amount to cover the storage deposit output.verify_storage_deposit(storage_score_params)?; - assert_eq!(output.amount(), 85199); + assert_eq!(output.amount(), (min_amount * 2) - 1); // address and sdr unlock condition assert_eq!(output.unlock_conditions().len(), 2); let sdr = output.unlock_conditions().storage_deposit_return().unwrap(); - assert_eq!(sdr.amount(), 42600); + assert_eq!(sdr.amount(), min_amount); // ReturnStrategy::Return provided let output = wallet .prepare_output( OutputParams { recipient_address: recipient_address.clone(), - amount: 42599, + amount: min_amount - 1, assets: None, features: None, unlocks: None, @@ -507,18 +509,18 @@ async fn output_preparation_sdr() -> Result<(), Box> { .await?; // Check if the output has enough amount to cover the storage deposit output.verify_storage_deposit(storage_score_params)?; - assert_eq!(output.amount(), 85199); + assert_eq!(output.amount(), (min_amount * 2) - 1); // address and sdr unlock condition assert_eq!(output.unlock_conditions().len(), 2); let sdr = output.unlock_conditions().storage_deposit_return().unwrap(); - assert_eq!(sdr.amount(), 42600); + assert_eq!(sdr.amount(), min_amount); // ReturnStrategy::Gift provided let output = wallet .prepare_output( OutputParams { recipient_address: recipient_address.clone(), - amount: 42599, + amount: min_amount - 1, assets: None, features: None, unlocks: None, @@ -533,7 +535,7 @@ async fn output_preparation_sdr() -> Result<(), Box> { // Check if the output has enough amount to cover the storage deposit output.verify_storage_deposit(storage_score_params)?; // The additional 1 amount will be added, because the storage deposit should be gifted and not returned - assert_eq!(output.amount(), 42600); + assert_eq!(output.amount(), min_amount); // storage deposit gifted, only address unlock condition assert_eq!(output.unlock_conditions().len(), 1); @@ -553,12 +555,22 @@ async fn prepare_nft_output_features_update() -> Result<(), Box Result<(), Box Result<(), Box Result<(), Box Result<(), Box Result<(), Box Result<(), Box Result<(), Box Result<(), Box Result<(), Box Result<(), WalletError> { -// let storage_path_0 = "test-storage/send_amount_0"; -// setup(storage_path_0)?; -// let storage_path_1 = "test-storage/send_amount_1"; -// setup(storage_path_1)?; +#[ignore] +#[tokio::test] +async fn send_amount() -> Result<(), Box> { + let storage_path_0 = "test-storage/send_amount_0"; + setup(storage_path_0)?; + let storage_path_1 = "test-storage/send_amount_1"; + setup(storage_path_1)?; -// let wallet_0 = make_wallet(storage_path_0, None, None).await?; -// request_funds(&wallet_0, 1).await?; + let wallet_0 = make_wallet(storage_path_0, None, None).await?; + request_funds(&wallet_0).await?; -// let wallet_1 = make_wallet(storage_path_1, None, None).await?; + let wallet_1 = make_wallet(storage_path_1, None, None).await?; -// let amount = 1_000_000; -// let tx = wallet_0 -// .send_with_params([SendParams::new(amount, wallet_1.address().clone())?], None) -// .await?; + let amount = 1_000_000; + let tx = wallet_0 + .send_with_params([SendParams::new(amount, wallet_1.address().await)?], None) + .await?; -// wallet_0 -// .wait_for_transaction_acceptance(&tx.transaction_id, None, None) -// .await?; + wallet_0 + .wait_for_transaction_acceptance(&tx.transaction_id, None, None) + .await?; -// let balance = wallet_1.sync(None).await.unwrap(); -// assert_eq!(balance.base_coin().available(), amount); + let balance = wallet_1.sync(None).await.unwrap(); + assert_eq!(balance.base_coin().available(), amount); -// tear_down(storage_path) -// } + tear_down(storage_path_0)?; + tear_down(storage_path_1) +} // #[ignore] // #[tokio::test] -// async fn send_amount_127_outputs() -> Result<(), WalletError> { +// async fn send_amount_127_outputs() -> Result<(), Box> { // let storage_path_0 = "test-storage/send_amount_127_outputs_0"; // setup(storage_path_0)?; // let storage_path_1 = "test-storage/send_amount_127_outputs_1"; @@ -74,7 +75,7 @@ // #[ignore] // #[tokio::test] -// async fn send_amount_custom_input() -> Result<(), WalletError> { +// async fn send_amount_custom_input() -> Result<(), Box> { // let storage_path_0 = "test-storage/send_amount_custom_input_0"; // setup(storage_path_0)?; // let storage_path_1 = "test-storage/send_amount_custom_input_1"; @@ -121,7 +122,7 @@ // #[ignore] // #[tokio::test] -// async fn send_nft() -> Result<(), WalletError> { +// async fn send_nft() -> Result<(), Box> { // let storage_path_0 = "test-storage/send_nft_0"; // setup(storage_path_0)?; // let storage_path_1 = "test-storage/send_nft_1"; @@ -164,7 +165,7 @@ // #[ignore] // #[tokio::test] -// async fn send_with_note() -> Result<(), WalletError> { +// async fn send_with_note() -> Result<(), Box> { // let storage_path_0 = "test-storage/send_with_note_0"; // setup(storage_path_0)?; // let storage_path_1 = "test-storage/send_with_note_1"; @@ -193,7 +194,7 @@ // #[ignore] // #[tokio::test] -// async fn conflicting_transaction() -> Result<(), WalletError> { +// async fn conflicting_transaction() -> Result<(), Box> { // let storage_path_0 = "test-storage/conflicting_transaction_0"; // let storage_path_1 = "test-storage/conflicting_transaction_1"; // setup(storage_path_0)?; @@ -265,7 +266,7 @@ // #[tokio::test] // #[cfg(all(feature = "ledger_nano", feature = "events"))] // #[ignore = "requires ledger nano instance"] -// async fn prepare_transaction_ledger() -> Result<(), WalletError> { +// async fn prepare_transaction_ledger() -> Result<(), Box> { // use iota_sdk::wallet::events::{types::TransactionProgressEvent, WalletEvent, WalletEventType}; // let storage_path_0 = "test-storage/wallet_address_generation_ledger_0";