diff --git a/bindings/core/src/method/wallet.rs b/bindings/core/src/method/wallet.rs index ab248beceb..487707fdbd 100644 --- a/bindings/core/src/method/wallet.rs +++ b/bindings/core/src/method/wallet.rs @@ -6,9 +6,9 @@ use std::path::PathBuf; use crypto::keys::bip44::Bip44; use derivative::Derivative; -use iota_sdk::utils::serde::string; #[cfg(feature = "events")] use iota_sdk::wallet::events::types::{WalletEvent, WalletEventType}; +use iota_sdk::{client::api::options::TransactionOptions, utils::serde::string}; // #[cfg(feature = "participation")] // use iota_sdk::{ // client::node_manager::node::Node, @@ -17,7 +17,7 @@ use iota_sdk::wallet::events::types::{WalletEvent, WalletEventType}; // }; use iota_sdk::{ client::{ - api::{input_selection::Burn, PreparedTransactionDataDto, SignedTransactionDataDto}, + api::{transaction_builder::Burn, PreparedTransactionDataDto, SignedTransactionDataDto}, node_manager::node::NodeAuth, secret::GenerateAddressOptions, }, @@ -29,7 +29,7 @@ use iota_sdk::{ wallet::{ BeginStakingParams, ClientOptions, ConsolidationParams, CreateAccountParams, CreateDelegationParams, CreateNativeTokenParams, FilterOptions, MintNftParams, OutputParams, OutputsToClaim, SendManaParams, - SendNativeTokenParams, SendNftParams, SendParams, SyncOptions, TransactionOptions, + SendNativeTokenParams, SendNftParams, SendParams, SyncOptions, }, U256, }; diff --git a/bindings/core/src/method_handler/utils.rs b/bindings/core/src/method_handler/utils.rs index e8843398e8..23c8a9ed0e 100644 --- a/bindings/core/src/method_handler/utils.rs +++ b/bindings/core/src/method_handler/utils.rs @@ -133,8 +133,8 @@ pub(crate) fn call_utils_method_internal(method: UtilsMethod) -> Result Result<(), Box> { let secret_manager_2 = SecretManager::try_from_mnemonic(std::env::var("MNEMONIC_2").unwrap())?; let issuer_id = std::env::var("ISSUER_ID").unwrap().parse::().unwrap(); - let from_address = secret_manager_1 - .generate_ed25519_addresses(GetAddressesOptions::from_client(&client).await?.with_range(0..1)) - .await?[0] - .clone(); + let chain = Bip44::new(IOTA_COIN_TYPE); + + let from_address = Address::from(Ed25519Address::from_public_key_bytes( + secret_manager_1 + .generate_ed25519_public_keys( + chain.coin_type, + chain.account, + chain.address_index..chain.address_index + 1, + None, + ) + .await?[0] + .to_bytes(), + )) + .to_bech32(client.get_bech32_hrp().await?); // Get output ids of outputs that can be controlled by this address without further unlock constraints let output_ids_response = client @@ -65,7 +78,7 @@ async fn main() -> Result<(), Box> { // Calculate the total amount let mut total_amount = 0; - let mut inputs = Vec::new(); + let mut inputs = BTreeSet::new(); let mut outputs = Vec::new(); for res in outputs_responses { @@ -80,11 +93,7 @@ async fn main() -> Result<(), Box> { total_amount -= native_token_return.amount(); outputs.push(native_token_return); } - inputs.push(InputSigningData { - output: res.output, - output_metadata: res.metadata, - chain: None, - }); + inputs.insert(*res.metadata().output_id()); } println!("Total amount: {total_amount}"); @@ -101,15 +110,17 @@ async fn main() -> Result<(), Box> { .finish_output()?, ); - let prepared_transaction = InputSelection::new( - inputs, - outputs, - [from_address.into_inner()], - client.get_slot_index().await?, - client.get_issuance().await?.latest_commitment.id(), - protocol_parameters.clone(), - ) - .select()?; + let prepared_transaction = client + .build_transaction( + [(from_address.into_inner(), chain)], + outputs, + TransactionOptions { + required_inputs: inputs, + allow_additional_input_selection: false, + ..Default::default() + }, + ) + .await?; let unlocks = secret_manager_1 .transaction_unlocks(&prepared_transaction, &protocol_parameters) .await?; diff --git a/sdk/examples/how_tos/account_output/send_amount.rs b/sdk/examples/how_tos/account_output/send_amount.rs index 18d13f2752..13434b64a8 100644 --- a/sdk/examples/how_tos/account_output/send_amount.rs +++ b/sdk/examples/how_tos/account_output/send_amount.rs @@ -7,9 +7,9 @@ //! `cargo run --release --all-features --example account_output_send_amount` use iota_sdk::{ - client::node_api::indexer::query_parameters::BasicOutputQueryParameters, + client::{api::options::TransactionOptions, node_api::indexer::query_parameters::BasicOutputQueryParameters}, types::block::address::{AccountAddress, ToBech32Ext}, - wallet::{AccountSyncOptions, SyncOptions, TransactionOptions}, + wallet::{AccountSyncOptions, SyncOptions}, Wallet, }; diff --git a/sdk/examples/how_tos/advanced_transactions/send_micro_transaction.rs b/sdk/examples/how_tos/advanced_transactions/send_micro_transaction.rs index 0a9a6e63aa..152b6254ed 100644 --- a/sdk/examples/how_tos/advanced_transactions/send_micro_transaction.rs +++ b/sdk/examples/how_tos/advanced_transactions/send_micro_transaction.rs @@ -11,7 +11,7 @@ //! cargo run --release --all-features --example send_micro_transaction //! ``` -use iota_sdk::{wallet::TransactionOptions, Wallet}; +use iota_sdk::{client::api::options::TransactionOptions, Wallet}; // The base coin micro amount to send const SEND_MICRO_AMOUNT: u64 = 1; diff --git a/sdk/examples/wallet/offline_signing/2_sign_transaction.rs b/sdk/examples/wallet/offline_signing/2_sign_transaction.rs index 14e9b82ba3..dabf816d6a 100644 --- a/sdk/examples/wallet/offline_signing/2_sign_transaction.rs +++ b/sdk/examples/wallet/offline_signing/2_sign_transaction.rs @@ -10,10 +10,7 @@ use iota_sdk::{ client::{ - api::{ - transaction::validate_signed_transaction_payload_length, PreparedTransactionData, SignedTransactionData, - SignedTransactionDataDto, - }, + api::{PreparedTransactionData, SignedTransactionData, SignedTransactionDataDto}, secret::{stronghold::StrongholdSecretManager, SecretManage, SecretManager}, }, types::{block::payload::SignedTransactionPayload, TryFromDto}, @@ -51,7 +48,7 @@ async fn main() -> Result<(), Box> { let signed_transaction = SignedTransactionPayload::new(prepared_transaction_data.transaction.clone(), unlocks)?; - validate_signed_transaction_payload_length(&signed_transaction)?; + signed_transaction.validate_length()?; let signed_transaction_data = SignedTransactionData { payload: signed_transaction, diff --git a/sdk/src/client/api/block_builder/mod.rs b/sdk/src/client/api/block_builder/mod.rs index 331a459197..c632dea9c1 100644 --- a/sdk/src/client/api/block_builder/mod.rs +++ b/sdk/src/client/api/block_builder/mod.rs @@ -1,10 +1,10 @@ // Copyright 2021 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -pub mod input_selection; +pub mod options; pub mod transaction; +pub mod transaction_builder; -pub use self::transaction::verify_semantic; use crate::{ client::{constants::FIVE_MINUTES_IN_NANOSECONDS, Client, ClientError}, types::block::{ diff --git a/sdk/src/wallet/operations/transaction/options.rs b/sdk/src/client/api/block_builder/options.rs similarity index 89% rename from sdk/src/wallet/operations/transaction/options.rs rename to sdk/src/client/api/block_builder/options.rs index b739e9b8bb..44d95f98d4 100644 --- a/sdk/src/wallet/operations/transaction/options.rs +++ b/sdk/src/client/api/block_builder/options.rs @@ -6,10 +6,9 @@ use alloc::collections::{BTreeMap, BTreeSet}; use serde::{Deserialize, Serialize}; use crate::{ - client::api::input_selection::Burn, + client::api::transaction_builder::Burn, types::block::{ address::Address, - context_input::ContextInput, output::{AccountId, OutputId}, payload::tagged_data::TaggedDataPayload, }, @@ -24,11 +23,9 @@ pub struct TransactionOptions { pub remainder_value_strategy: RemainderValueStrategy, /// An optional tagged data payload. pub tagged_data_payload: Option, - /// Transaction context inputs to include. - pub context_inputs: Vec, /// Inputs that must be used for the transaction. pub required_inputs: BTreeSet, - /// Specifies what needs to be burned during input selection. + /// Specifies what needs to be burned in the transaction. pub burn: Option, /// A string attached to the transaction. pub note: Option, @@ -47,7 +44,6 @@ impl Default for TransactionOptions { Self { remainder_value_strategy: Default::default(), tagged_data_payload: Default::default(), - context_inputs: Default::default(), required_inputs: Default::default(), burn: Default::default(), note: Default::default(), diff --git a/sdk/src/client/api/block_builder/transaction.rs b/sdk/src/client/api/block_builder/transaction.rs index 39bd5e24e0..d6cd3fc3e4 100644 --- a/sdk/src/client/api/block_builder/transaction.rs +++ b/sdk/src/client/api/block_builder/transaction.rs @@ -3,12 +3,13 @@ //! Transaction preparation and signing -use alloc::collections::BTreeMap; - use packable::PackableExt; use crate::{ - client::{secret::types::InputSigningData, ClientError}, + client::{ + api::{PreparedTransactionData, SignedTransactionData}, + ClientError, + }, types::block::{ output::{Output, OutputId}, payload::signed_transaction::{SignedTransactionPayload, Transaction}, @@ -27,63 +28,85 @@ const SINGLE_UNLOCK_LENGTH: usize = 1 + 1 + Ed25519Signature::PUBLIC_KEY_LENGTH // Type + reference index const REFERENCE_ACCOUNT_NFT_UNLOCK_LENGTH: usize = 1 + 2; -/// Verifies the semantic of a prepared transaction. -pub fn verify_semantic( - input_signing_data: &[InputSigningData], - transaction_payload: &SignedTransactionPayload, - mana_rewards: impl Into>>, - protocol_parameters: ProtocolParameters, -) -> Result<(), TransactionFailureReason> { - let inputs = input_signing_data - .iter() - .map(|input| (input.output_id(), &input.output)) - .collect::>(); +impl PreparedTransactionData { + /// Verifies the semantic of a prepared transaction. + pub fn verify_semantic(&self, protocol_parameters: &ProtocolParameters) -> Result<(), TransactionFailureReason> { + let inputs = self + .inputs_data + .iter() + .map(|input| (input.output_id(), &input.output)) + .collect::>(); + + let context = SemanticValidationContext::new( + &self.transaction, + &inputs, + None, + Some(&self.mana_rewards), + protocol_parameters, + ); + + context.validate() + } +} - let context = SemanticValidationContext::new( - transaction_payload.transaction(), - &inputs, - Some(transaction_payload.unlocks()), - mana_rewards.into(), - protocol_parameters, - ); +impl SignedTransactionData { + /// Verifies the semantic of a prepared transaction. + pub fn verify_semantic(&self, protocol_parameters: &ProtocolParameters) -> Result<(), TransactionFailureReason> { + let inputs = self + .inputs_data + .iter() + .map(|input| (input.output_id(), &input.output)) + .collect::>(); - context.validate() + let context = SemanticValidationContext::new( + &self.payload.transaction(), + &inputs, + Some(self.payload.unlocks()), + Some(&self.mana_rewards), + protocol_parameters, + ); + + context.validate() + } } -/// Verifies that the signed transaction payload doesn't exceed the block size limit with 8 parents. -pub fn validate_signed_transaction_payload_length( - signed_transaction_payload: &SignedTransactionPayload, -) -> Result<(), ClientError> { - let signed_transaction_payload_bytes = signed_transaction_payload.pack_to_vec(); - if signed_transaction_payload_bytes.len() > MAX_TX_LENGTH_FOR_BLOCK_WITH_8_PARENTS { - return Err(ClientError::InvalidSignedTransactionPayloadLength { - length: signed_transaction_payload_bytes.len(), - max_length: MAX_TX_LENGTH_FOR_BLOCK_WITH_8_PARENTS, - }); +impl SignedTransactionPayload { + /// Verifies that the signed transaction payload doesn't exceed the block size limit with 8 parents. + pub fn validate_length(&self) -> Result<(), ClientError> { + let signed_transaction_payload_bytes = self.pack_to_vec(); + if signed_transaction_payload_bytes.len() > MAX_TX_LENGTH_FOR_BLOCK_WITH_8_PARENTS { + return Err(ClientError::InvalidSignedTransactionPayloadLength { + length: signed_transaction_payload_bytes.len(), + max_length: MAX_TX_LENGTH_FOR_BLOCK_WITH_8_PARENTS, + }); + } + Ok(()) } - Ok(()) } -/// Verifies that the transaction doesn't exceed the block size limit with 8 parents. -/// Assuming one signature unlock and otherwise reference/account/nft unlocks. `validate_transaction_payload_length()` -/// should later be used to check the length again with the correct unlocks. -pub fn validate_transaction_length(transaction: &Transaction) -> Result<(), ClientError> { - let transaction_bytes = transaction.pack_to_vec(); +impl Transaction { + /// Verifies that the transaction doesn't exceed the block size limit with 8 parents. + /// Assuming one signature unlock and otherwise reference/account/nft unlocks. + /// `validate_transaction_payload_length()` should later be used to check the length again with the correct + /// unlocks. + pub fn validate_length(&self) -> Result<(), ClientError> { + let transaction_bytes = self.pack_to_vec(); - // Assuming there is only 1 signature unlock and the rest is reference/account/nft unlocks - let reference_account_nft_unlocks_amount = transaction.inputs().len() - 1; + // Assuming there is only 1 signature unlock and the rest is reference/account/nft unlocks + let reference_account_nft_unlocks_amount = self.inputs().len() - 1; - // Max tx payload length - length for one signature unlock (there might be more unlocks, we check with them - // later again, when we built the transaction payload) - let max_length = MAX_TX_LENGTH_FOR_BLOCK_WITH_8_PARENTS - - SINGLE_UNLOCK_LENGTH - - (reference_account_nft_unlocks_amount * REFERENCE_ACCOUNT_NFT_UNLOCK_LENGTH); + // Max tx payload length - length for one signature unlock (there might be more unlocks, we check with them + // later again, when we built the transaction payload) + let max_length = MAX_TX_LENGTH_FOR_BLOCK_WITH_8_PARENTS + - SINGLE_UNLOCK_LENGTH + - (reference_account_nft_unlocks_amount * REFERENCE_ACCOUNT_NFT_UNLOCK_LENGTH); - if transaction_bytes.len() > max_length { - return Err(ClientError::InvalidTransactionLength { - length: transaction_bytes.len(), - max_length, - }); + if transaction_bytes.len() > max_length { + return Err(ClientError::InvalidTransactionLength { + length: transaction_bytes.len(), + max_length, + }); + } + Ok(()) } - Ok(()) } diff --git a/sdk/src/client/api/block_builder/input_selection/burn.rs b/sdk/src/client/api/block_builder/transaction_builder/burn.rs similarity index 98% rename from sdk/src/client/api/block_builder/input_selection/burn.rs rename to sdk/src/client/api/block_builder/transaction_builder/burn.rs index 03f1540d6f..1cab2c59dd 100644 --- a/sdk/src/client/api/block_builder/input_selection/burn.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/burn.rs @@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize}; use crate::types::block::output::{AccountId, DelegationId, FoundryId, NativeToken, NftId, TokenId}; -/// A type to specify what needs to be burned during input selection. +/// A type to specify what needs to be burned in a transaction. /// Nothing will be burned that has not been explicitly set with this struct. #[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] diff --git a/sdk/src/client/api/block_builder/input_selection/error.rs b/sdk/src/client/api/block_builder/transaction_builder/error.rs similarity index 91% rename from sdk/src/client/api/block_builder/input_selection/error.rs rename to sdk/src/client/api/block_builder/transaction_builder/error.rs index 373da688c4..06dfa4c538 100644 --- a/sdk/src/client/api/block_builder/input_selection/error.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/error.rs @@ -1,7 +1,7 @@ // Copyright 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -//! Error handling for input selection. +//! Error handling for transaction builder. use std::fmt::Debug; @@ -13,15 +13,16 @@ use crate::types::block::{ mana::ManaError, output::{ChainId, NativeTokenError, OutputError, OutputId, TokenId}, payload::PayloadError, + semantic::TransactionFailureReason, signature::SignatureError, unlock::UnlockError, BlockError, }; -/// Errors related to input selection. +/// Errors related to transaction builder. #[derive(Debug, Eq, PartialEq, thiserror::Error)] #[non_exhaustive] -pub enum Error { +pub enum TransactionBuilderError { #[error("additional inputs required for {0:?}, but additional input selection is disabled")] AdditionalInputsRequired(Requirement), /// Can't burn and transition an output at the same time. @@ -64,12 +65,9 @@ pub enum Error { /// No input with matching ed25519 address provided. #[error("no input with matching ed25519 address provided")] MissingInputWithEd25519Address, - /// No available inputs were provided to input selection. + /// No available inputs were provided to transaction builder. #[error("no available inputs provided")] NoAvailableInputsProvided, - /// Required input is forbidden. - #[error("required input {0} is forbidden")] - RequiredInputIsForbidden(OutputId), /// Required input is not available. #[error("required input {0} is not available")] RequiredInputIsNotAvailable(OutputId), @@ -104,4 +102,7 @@ pub enum Error { /// Unlock errors. #[error("{0}")] Unlock(#[from] UnlockError), + /// Semantic errors. + #[error("{0}")] + Semantic(#[from] TransactionFailureReason), } diff --git a/sdk/src/client/api/block_builder/input_selection/mod.rs b/sdk/src/client/api/block_builder/transaction_builder/mod.rs similarity index 71% rename from sdk/src/client/api/block_builder/input_selection/mod.rs rename to sdk/src/client/api/block_builder/transaction_builder/mod.rs index 408d8d3193..f819430c91 100644 --- a/sdk/src/client/api/block_builder/input_selection/mod.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/mod.rs @@ -1,7 +1,7 @@ // Copyright 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -//! Input selection for transactions +//! Builder for transactions pub(crate) mod burn; pub(crate) mod error; @@ -10,26 +10,30 @@ pub(crate) mod requirement; pub(crate) mod transition; use alloc::collections::BTreeMap; -use core::ops::Deref; use std::collections::{HashMap, HashSet}; +use crypto::keys::bip44::Bip44; use packable::PackableExt; -use self::requirement::account::is_account_with_id; -pub use self::{burn::Burn, error::Error, requirement::Requirement}; +pub use self::{burn::Burn, error::TransactionBuilderError, requirement::Requirement}; use crate::{ client::{ - api::{PreparedTransactionData, RemainderData}, + api::{ + options::{RemainderValueStrategy, TransactionOptions}, + PreparedTransactionData, RemainderData, + }, + node_api::indexer::query_parameters::OutputQueryParameters, secret::types::InputSigningData, + Client, ClientError, }, types::block::{ - address::{AccountAddress, Address, NftAddress}, - context_input::ContextInput, + address::{AccountAddress, Address, NftAddress, ToBech32Ext}, + context_input::{BlockIssuanceCreditContextInput, CommitmentContextInput, ContextInput, RewardContextInput}, input::{Input, UtxoInput, INPUT_COUNT_RANGE}, mana::ManaAllotment, output::{ - AccountId, AccountOutput, AccountOutputBuilder, AnchorOutputBuilder, BasicOutputBuilder, FoundryOutput, - NativeTokensBuilder, NftOutput, NftOutputBuilder, Output, OutputId, OUTPUT_COUNT_RANGE, + AccountId, AccountOutputBuilder, AnchorOutputBuilder, BasicOutputBuilder, NftOutputBuilder, Output, + OutputId, OUTPUT_COUNT_RANGE, }, payload::{ signed_transaction::{Transaction, TransactionCapabilities, TransactionCapabilityFlag}, @@ -40,14 +44,147 @@ use crate::{ }, }; -/// Working state for the input selection algorithm. +impl Client { + /// Builds a transaction using the given inputs, outputs, addresses, and options. + pub async fn build_transaction( + &self, + addresses: impl IntoIterator, + outputs: impl IntoIterator, + options: TransactionOptions, + ) -> Result { + let addresses = addresses.into_iter().collect::>(); + let protocol_parameters = self.get_protocol_parameters().await?; + let slot_commitment_id = self.get_issuance().await?.latest_commitment.id(); + + let hrp = protocol_parameters.bech32_hrp(); + + let mut available_inputs = Vec::new(); + for (address, chain) in &addresses { + let output_ids = self + .output_ids(OutputQueryParameters::new().unlockable_by_address(address.clone().to_bech32(hrp))) + .await? + .items; + available_inputs.extend( + self.get_outputs_with_metadata(&output_ids) + .await? + .into_iter() + .map(|res| { + Ok(InputSigningData { + output: res.output, + output_metadata: res.metadata, + chain: Some(*chain), + }) + }) + .collect::, ClientError>>()?, + ); + } + + self.build_transaction_inner( + addresses.into_keys(), + available_inputs, + outputs, + options, + slot_commitment_id, + protocol_parameters, + ) + .await + } + + /// Builds a transaction using the given inputs, outputs, addresses, and options. + pub(crate) async fn build_transaction_inner( + &self, + addresses: impl IntoIterator, + available_inputs: impl IntoIterator, + outputs: impl IntoIterator, + options: TransactionOptions, + slot_commitment_id: SlotCommitmentId, + protocol_parameters: ProtocolParameters, + ) -> Result { + let outputs = outputs.into_iter().collect::>(); + let creation_slot = self.get_slot_index().await?; + + let reference_mana_cost = if let Some(issuer_id) = options.issuer_id { + Some(self.get_account_congestion(&issuer_id, None).await?.reference_mana_cost) + } else { + None + }; + let remainder_address = match options.remainder_value_strategy { + RemainderValueStrategy::ReuseAddress => None, + RemainderValueStrategy::CustomAddress(address) => Some(address), + }; + + let mut mana_rewards = HashMap::new(); + + if let Some(burn) = &options.burn { + for delegation_id in burn.delegations() { + let output_id = self.delegation_output_id(*delegation_id).await?; + mana_rewards.insert( + output_id, + self.get_output_mana_rewards(&output_id, slot_commitment_id.slot_index()) + .await? + .rewards, + ); + } + } + + for output_id in &options.required_inputs { + let input = self.get_output(output_id).await?; + if input.output.can_claim_rewards(outputs.iter().find(|o| { + input + .output + .chain_id() + .map(|chain_id| chain_id.or_from_output_id(output_id)) + == o.chain_id() + })) { + mana_rewards.insert( + *output_id, + self.get_output_mana_rewards(output_id, slot_commitment_id.slot_index()) + .await? + .rewards, + ); + } + } + + let mut transaction_builder = TransactionBuilder::new( + available_inputs, + outputs, + addresses, + creation_slot, + slot_commitment_id, + protocol_parameters, + ) + .with_required_inputs(options.required_inputs) + .with_mana_rewards(mana_rewards) + .with_payload(options.tagged_data_payload) + .with_mana_allotments(options.mana_allotments) + .with_remainder_address(remainder_address) + .with_burn(options.burn); + + if let (Some(account_id), Some(reference_mana_cost)) = (options.issuer_id, reference_mana_cost) { + transaction_builder = transaction_builder.with_min_mana_allotment(account_id, reference_mana_cost); + } + + if !options.allow_additional_input_selection { + transaction_builder = transaction_builder.disable_additional_input_selection(); + } + + let prepared_transaction_data = transaction_builder.finish()?; + + prepared_transaction_data.transaction.validate_length()?; + + Ok(prepared_transaction_data) + } +} + +/// Working state for the transaction builder algorithm. #[derive(Debug)] -pub struct InputSelection { +pub struct TransactionBuilder { available_inputs: Vec, required_inputs: HashSet, - forbidden_inputs: HashSet, selected_inputs: Vec, - context_inputs: HashSet, + bic_context_inputs: HashSet, + commitment_context_input: Option, + reward_context_inputs: HashSet, provided_outputs: Vec, added_outputs: Vec, addresses: HashSet
, @@ -81,8 +218,8 @@ pub(crate) struct Remainders { added_mana: u64, } -impl InputSelection { - /// Creates a new [`InputSelection`]. +impl TransactionBuilder { + /// Creates a new [`TransactionBuilder`]. pub fn new( available_inputs: impl IntoIterator, outputs: impl IntoIterator, @@ -116,9 +253,10 @@ impl InputSelection { Self { available_inputs, required_inputs: HashSet::new(), - forbidden_inputs: HashSet::new(), selected_inputs: Vec::new(), - context_inputs: HashSet::new(), + bic_context_inputs: HashSet::new(), + commitment_context_input: None, + reward_context_inputs: HashSet::new(), provided_outputs: outputs.into_iter().collect(), added_outputs: Vec::new(), addresses, @@ -137,7 +275,7 @@ impl InputSelection { } } - fn init(&mut self) -> Result<(), Error> { + fn init(&mut self) -> Result<(), TransactionBuilderError> { // If automatic min mana allotment is enabled, we need to initialize the allotment debt. if let Some(MinManaAllotment { issuer_id, @@ -156,16 +294,7 @@ impl InputSelection { Requirement::NativeTokens, ]); - // Removes forbidden inputs from available inputs. - self.available_inputs - .retain(|input| !self.forbidden_inputs.contains(input.output_id())); - for required_input in self.required_inputs.clone() { - // Checks that required input is not forbidden. - if self.forbidden_inputs.contains(&required_input) { - return Err(Error::RequiredInputIsForbidden(required_input)); - } - // Checks that required input is available. match self .available_inputs @@ -179,7 +308,7 @@ impl InputSelection { // Selects required input. self.select_input(input)?; } - None => return Err(Error::RequiredInputIsNotAvailable(required_input)), + None => return Err(TransactionBuilderError::RequiredInputIsNotAvailable(required_input)), } } @@ -195,21 +324,21 @@ impl InputSelection { /// Selects inputs that meet the requirements of the outputs to satisfy the semantic validation of the overall /// transaction. Also creates a remainder output and chain transition outputs if required. - pub fn select(mut self) -> Result { + pub fn finish(mut self) -> Result { if !OUTPUT_COUNT_RANGE.contains(&(self.provided_outputs.len() as u16)) { // If burn or mana allotments are provided, outputs will be added later, in the other cases it will just // create remainder outputs. if !self.provided_outputs.is_empty() || (self.burn.is_none() && self.mana_allotments.is_empty() && self.required_inputs.is_empty()) { - return Err(Error::InvalidOutputCount(self.provided_outputs.len())); + return Err(TransactionBuilderError::InvalidOutputCount(self.provided_outputs.len())); } } self.filter_inputs(); if self.available_inputs.is_empty() { - return Err(Error::NoAvailableInputsProvided); + return Err(TransactionBuilderError::NoAvailableInputsProvided); } // Creates the initial state, selected inputs and requirements, based on the provided outputs. @@ -221,7 +350,7 @@ impl InputSelection { let inputs = self.fulfill_requirement(&requirement)?; if !self.allow_additional_input_selection && !inputs.is_empty() { - return Err(Error::AdditionalInputsRequired(requirement)); + return Err(TransactionBuilderError::AdditionalInputsRequired(requirement)); } // Select suggested inputs. @@ -233,7 +362,7 @@ impl InputSelection { let (input_mana, output_mana) = self.mana_sums(false)?; if input_mana < output_mana { - return Err(Error::InsufficientMana { + return Err(TransactionBuilderError::InsufficientMana { found: input_mana, required: output_mana, }); @@ -245,13 +374,13 @@ impl InputSelection { } if !INPUT_COUNT_RANGE.contains(&(self.selected_inputs.len() as u16)) { - return Err(Error::InvalidInputCount(self.selected_inputs.len())); + return Err(TransactionBuilderError::InvalidInputCount(self.selected_inputs.len())); } if self.remainders.added_mana > 0 { let remainder_address = self .get_remainder_address()? - .ok_or(Error::MissingInputWithEd25519Address)? + .ok_or(TransactionBuilderError::MissingInputWithEd25519Address)? .0; let added_mana = self.remainders.added_mana; if let Some(output) = self.get_output_for_added_mana(&remainder_address) { @@ -290,14 +419,12 @@ impl InputSelection { // Check again, because more outputs may have been added. if !OUTPUT_COUNT_RANGE.contains(&(outputs.len() as u16)) { - return Err(Error::InvalidOutputCount(outputs.len())); + return Err(TransactionBuilderError::InvalidOutputCount(outputs.len())); } - Self::validate_transitions(&self.selected_inputs, &outputs)?; - for output_id in self.mana_rewards.keys() { if !self.selected_inputs.iter().any(|i| output_id == i.output_id()) { - return Err(Error::ExtraManaRewards(*output_id)); + return Err(TransactionBuilderError::ExtraManaRewards(*output_id)); } } @@ -308,9 +435,18 @@ impl InputSelection { )?; let mut inputs: Vec = Vec::new(); + let mut context_inputs = self + .bic_context_inputs + .into_iter() + .map(ContextInput::from) + .chain(self.commitment_context_input.map(ContextInput::from)) + .collect::>(); - for input in &inputs_data { + 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()); + } } let mana_allotments = self @@ -325,7 +461,7 @@ impl InputSelection { .with_inputs(inputs) .with_outputs(outputs) .with_mana_allotments(mana_allotments) - .with_context_inputs(self.context_inputs) + .with_context_inputs(context_inputs) .with_creation_slot(self.creation_slot) .with_capabilities(self.transaction_capabilities); @@ -335,15 +471,19 @@ impl InputSelection { let transaction = builder.finish_with_params(&self.protocol_parameters)?; - Ok(PreparedTransactionData { + let data = PreparedTransactionData { transaction, inputs_data, remainders: self.remainders.data, mana_rewards: self.mana_rewards.into_iter().collect(), - }) + }; + + data.verify_semantic(&self.protocol_parameters)?; + + Ok(data) } - fn select_input(&mut self, input: InputSigningData) -> Result, Error> { + fn select_input(&mut self, input: InputSigningData) -> Result, TransactionBuilderError> { log::debug!("Selecting input {:?}", input.output_id()); let mut added_output = None; @@ -372,37 +512,25 @@ impl InputSelection { Ok(added_output) } - /// Sets the required inputs of an [`InputSelection`]. + /// Sets the required inputs of an [`TransactionBuilder`]. pub fn with_required_inputs(mut self, inputs: impl IntoIterator) -> Self { self.required_inputs = inputs.into_iter().collect(); self } - /// Sets the forbidden inputs of an [`InputSelection`]. - pub fn with_forbidden_inputs(mut self, inputs: impl IntoIterator) -> Self { - self.forbidden_inputs = inputs.into_iter().collect(); - self - } - - /// Sets the context inputs of an [`InputSelection`]. - pub fn with_context_inputs(mut self, context_inputs: impl IntoIterator) -> Self { - self.context_inputs = context_inputs.into_iter().collect(); - self - } - - /// Sets the burn of an [`InputSelection`]. + /// Sets the burn of an [`TransactionBuilder`]. pub fn with_burn(mut self, burn: impl Into>) -> Self { self.burn = burn.into(); self } - /// Sets the remainder address of an [`InputSelection`]. + /// Sets the remainder address of an [`TransactionBuilder`]. pub fn with_remainder_address(mut self, address: impl Into>) -> Self { self.remainders.address = address.into(); self } - /// Sets the mana allotments of an [`InputSelection`]. + /// Sets the mana allotments of an [`TransactionBuilder`]. pub fn with_mana_allotments(mut self, mana_allotments: impl IntoIterator) -> Self { self.mana_allotments = mana_allotments.into_iter().collect(); self @@ -458,7 +586,23 @@ impl InputSelection { .chain(&self.remainders.storage_deposit_returns) } - fn required_account_nft_addresses(&self, input: &InputSigningData) -> Result, Error> { + pub(crate) fn context_inputs(&self) -> impl Iterator + '_ { + self.bic_context_inputs + .iter() + .copied() + .map(ContextInput::from) + .chain(self.commitment_context_input.map(ContextInput::from)) + .chain(self.selected_inputs.iter().enumerate().filter_map(|(idx, input)| { + self.reward_context_inputs + .contains(input.output_id()) + .then_some(RewardContextInput::new(idx as u16).unwrap().into()) + })) + } + + fn required_account_nft_addresses( + &self, + input: &InputSigningData, + ) -> Result, TransactionBuilderError> { let required_address = input .output .required_address( @@ -550,7 +694,7 @@ impl InputSelection { mut inputs: Vec, commitment_slot_index: SlotIndex, committable_age_range: CommittableAgeRange, - ) -> Result, Error> { + ) -> Result, TransactionBuilderError> { // initially sort by output to make it deterministic // TODO: rethink this, we only need it deterministic for tests, for the protocol it doesn't matter, also there // might be a more efficient way to do this @@ -639,133 +783,4 @@ impl InputSelection { Ok(sorted_inputs) } - - fn validate_transitions(inputs: &[InputSigningData], outputs: &[Output]) -> Result<(), Error> { - let mut input_native_tokens_builder = NativeTokensBuilder::new(); - let mut output_native_tokens_builder = NativeTokensBuilder::new(); - let mut input_accounts = Vec::new(); - let mut input_chains_foundries = hashbrown::HashMap::new(); - let mut input_foundries = Vec::new(); - let mut input_nfts = Vec::new(); - - for input in inputs { - if let Some(native_token) = input.output.native_token() { - input_native_tokens_builder.add_native_token(*native_token)?; - } - match &input.output { - Output::Basic(basic) => { - if basic.is_implicit_account() { - input_accounts.push(input); - } - } - Output::Account(_) => { - input_accounts.push(input); - } - Output::Foundry(foundry) => { - input_chains_foundries.insert(foundry.chain_id(), (input.output_id(), &input.output)); - input_foundries.push(input); - } - Output::Nft(_) => { - input_nfts.push(input); - } - _ => {} - } - } - - for output in outputs { - if let Some(native_token) = output.native_token() { - output_native_tokens_builder.add_native_token(*native_token)?; - } - } - - // Validate utxo chain transitions - for output in outputs { - match output { - Output::Account(account_output) => { - // Null id outputs are just minted and can't be a transition - if account_output.account_id().is_null() { - continue; - } - - let account_input = input_accounts - .iter() - .find(|i| is_account_with_id(&i.output, account_output.account_id(), i.output_id())) - .expect("ISA is broken because there is no account input"); - - match &account_input.output { - Output::Account(account) => { - if let Err(err) = AccountOutput::transition_inner( - account, - account_output, - &input_chains_foundries, - outputs, - ) { - log::debug!("validate_transitions error {err:?}"); - return Err(Error::UnfulfillableRequirement(Requirement::Account( - *account_output.account_id(), - ))); - } - } - Output::Basic(_) => { - // TODO https://github.com/iotaledger/iota-sdk/issues/1664 - } - _ => panic!( - "unreachable: \"input_accounts\" only contains account outputs and implicit account (basic) outputs" - ), - } - } - Output::Foundry(foundry_output) => { - let foundry_id = foundry_output.id(); - let foundry_input = input_foundries.iter().find(|i| { - if let Output::Foundry(foundry_input) = &i.output { - foundry_id == foundry_input.id() - } else { - false - } - }); - if let Some(foundry_input) = foundry_input { - if let Err(err) = FoundryOutput::transition_inner( - foundry_input.output.as_foundry(), - foundry_output, - input_native_tokens_builder.deref(), - output_native_tokens_builder.deref(), - // We use `all` capabilities here because this transition may be burning - // native tokens, and validation will fail without the capability. - &TransactionCapabilities::all(), - ) { - log::debug!("validate_transitions error {err:?}"); - return Err(Error::UnfulfillableRequirement(Requirement::Foundry( - foundry_output.id(), - ))); - } - } - } - Output::Nft(nft_output) => { - // Null id outputs are just minted and can't be a transition - if nft_output.nft_id().is_null() { - continue; - } - - let nft_input = input_nfts - .iter() - .find(|i| { - if let Output::Nft(nft_input) = &i.output { - *nft_output.nft_id() == nft_input.nft_id_non_null(i.output_id()) - } else { - false - } - }) - .expect("ISA is broken because there is no nft input"); - - if let Err(err) = NftOutput::transition_inner(nft_input.output.as_nft(), nft_output) { - log::debug!("validate_transitions error {err:?}"); - return Err(Error::UnfulfillableRequirement(Requirement::Nft(*nft_output.nft_id()))); - } - } - // other output types don't do transitions - _ => {} - } - } - Ok(()) - } } diff --git a/sdk/src/client/api/block_builder/input_selection/remainder.rs b/sdk/src/client/api/block_builder/transaction_builder/remainder.rs similarity index 95% rename from sdk/src/client/api/block_builder/input_selection/remainder.rs rename to sdk/src/client/api/block_builder/transaction_builder/remainder.rs index 4a26b0cabe..0dee5047ee 100644 --- a/sdk/src/client/api/block_builder/input_selection/remainder.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/remainder.rs @@ -6,10 +6,10 @@ use std::collections::HashMap; use crypto::keys::bip44::Bip44; -use super::{Error, InputSelection}; +use super::{TransactionBuilder, TransactionBuilderError}; use crate::{ client::api::{ - input_selection::requirement::native_tokens::{get_native_tokens, get_native_tokens_diff}, + transaction_builder::requirement::native_tokens::{get_native_tokens, get_native_tokens_diff}, RemainderData, }, types::block::{ @@ -21,9 +21,9 @@ use crate::{ }, }; -impl InputSelection { +impl TransactionBuilder { /// Updates the remainders, overwriting old values. - pub(crate) fn update_remainders(&mut self) -> Result<(), Error> { + pub(crate) fn update_remainders(&mut self) -> Result<(), TransactionBuilderError> { let (storage_deposit_returns, remainders) = self.storage_deposit_returns_and_remainders()?; self.remainders.storage_deposit_returns = storage_deposit_returns; @@ -33,7 +33,7 @@ impl InputSelection { } /// Gets the remainder address from configuration of finds one from the inputs. - pub(crate) fn get_remainder_address(&self) -> Result)>, Error> { + pub(crate) fn get_remainder_address(&self) -> Result)>, TransactionBuilderError> { if let Some(remainder_address) = &self.remainders.address { // Search in inputs for the Bip44 chain for the remainder address, so the ledger can regenerate it for input in self.available_inputs.iter().chain(self.selected_inputs.iter()) { @@ -69,7 +69,7 @@ impl InputSelection { Ok(None) } - pub(crate) fn remainder_amount(&self) -> Result<(u64, bool, bool), Error> { + pub(crate) fn remainder_amount(&self) -> Result<(u64, bool, bool), TransactionBuilderError> { let mut input_native_tokens = get_native_tokens(self.selected_inputs.iter().map(|input| &input.output))?; let mut output_native_tokens = get_native_tokens(self.non_remainder_outputs())?; let (minted_native_tokens, melted_native_tokens) = self.get_minted_and_melted_native_tokens()?; @@ -88,7 +88,7 @@ impl InputSelection { pub(crate) fn storage_deposit_returns_and_remainders( &mut self, - ) -> Result<(Vec, Vec), Error> { + ) -> Result<(Vec, Vec), TransactionBuilderError> { let (input_amount, output_amount, inputs_sdr, outputs_sdr) = self.amount_sums(); let mut storage_deposit_returns = Vec::new(); @@ -134,7 +134,7 @@ impl InputSelection { let (remainder_address, chain) = self .get_remainder_address()? - .ok_or(Error::MissingInputWithEd25519Address)?; + .ok_or(TransactionBuilderError::MissingInputWithEd25519Address)?; // If there is a mana remainder, try to fit it in an existing output if mana_diff > 0 && self.output_for_added_mana_exists(&remainder_address) { @@ -206,7 +206,7 @@ impl InputSelection { pub(crate) fn required_remainder_amount( &self, remainder_native_tokens: Option, - ) -> Result<(u64, bool, bool), Error> { + ) -> Result<(u64, bool, bool), TransactionBuilderError> { let native_tokens_remainder = remainder_native_tokens.is_some(); let remainder_builder = @@ -253,7 +253,7 @@ fn create_remainder_outputs( remainder_address: Address, remainder_address_chain: Option, storage_score_parameters: StorageScoreParameters, -) -> Result, Error> { +) -> Result, TransactionBuilderError> { let mut remainder_outputs = Vec::new(); let mut remaining_amount = amount_diff; let mut catchall_native_token = None; diff --git a/sdk/src/client/api/block_builder/input_selection/requirement/account.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/account.rs similarity index 87% rename from sdk/src/client/api/block_builder/input_selection/requirement/account.rs rename to sdk/src/client/api/block_builder/transaction_builder/requirement/account.rs index 87f8cd20cd..3de6e4755c 100644 --- a/sdk/src/client/api/block_builder/input_selection/requirement/account.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/account.rs @@ -1,7 +1,7 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use super::{Error, InputSelection, Requirement}; +use super::{Requirement, TransactionBuilder, TransactionBuilderError}; use crate::{ client::secret::types::InputSigningData, types::block::output::{AccountId, Output, OutputId}, @@ -26,12 +26,12 @@ pub(crate) fn is_account_with_id_non_null(output: &Output, account_id: &AccountI } } -impl InputSelection { +impl TransactionBuilder { /// Fulfills an account requirement by selecting the appropriate account from the available inputs. pub(crate) fn fulfill_account_requirement( &mut self, account_id: AccountId, - ) -> Result, Error> { + ) -> Result, TransactionBuilderError> { // Check if the requirement is already fulfilled. if let Some(input) = self .selected_inputs @@ -50,7 +50,9 @@ impl InputSelection { .available_inputs .iter() .position(|input| is_account_with_id(&input.output, &account_id, input.output_id())) - .ok_or(Error::UnfulfillableRequirement(Requirement::Account(account_id)))?; + .ok_or(TransactionBuilderError::UnfulfillableRequirement(Requirement::Account( + account_id, + )))?; // Remove the input from the available inputs, swap to make it O(1). let input = self.available_inputs.swap_remove(index); diff --git a/sdk/src/client/api/block_builder/input_selection/requirement/amount.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/amount.rs similarity index 91% rename from sdk/src/client/api/block_builder/input_selection/requirement/amount.rs rename to sdk/src/client/api/block_builder/transaction_builder/requirement/amount.rs index 801e962951..4dced48202 100644 --- a/sdk/src/client/api/block_builder/input_selection/requirement/amount.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/amount.rs @@ -3,7 +3,7 @@ use std::collections::{HashMap, HashSet}; -use super::{native_tokens::get_native_tokens, Error, InputSelection, Requirement}; +use super::{native_tokens::get_native_tokens, Requirement, TransactionBuilder, TransactionBuilderError}; use crate::{ client::secret::types::InputSigningData, types::block::{ @@ -35,7 +35,7 @@ pub(crate) fn sdruc_not_expired( }) } -impl InputSelection { +impl TransactionBuilder { pub(crate) fn amount_sums(&self) -> (u64, u64, HashMap, HashMap) { let mut inputs_sum = 0; let mut outputs_sum = 0; @@ -88,15 +88,15 @@ struct AmountSelection { } impl AmountSelection { - fn new(input_selection: &InputSelection) -> Result { - let (inputs_sum, outputs_sum, inputs_sdr, outputs_sdr) = input_selection.amount_sums(); + fn new(transaction_builder: &TransactionBuilder) -> Result { + let (inputs_sum, outputs_sum, inputs_sdr, outputs_sdr) = transaction_builder.amount_sums(); let selected_native_tokens = HashSet::::from_iter( - input_selection + transaction_builder .selected_inputs .iter() .filter_map(|i| i.output.native_token().map(|n| *n.token_id())), ); - let (remainder_amount, native_tokens_remainder, mana_remainder) = input_selection.remainder_amount()?; + let (remainder_amount, native_tokens_remainder, mana_remainder) = transaction_builder.remainder_amount()?; Ok(Self { newly_selected_inputs: HashMap::new(), @@ -132,17 +132,18 @@ impl AmountSelection { fn fulfil<'a>( &mut self, - input_selection: &InputSelection, + transaction_builder: &TransactionBuilder, inputs: impl Iterator, - ) -> Result { + ) -> Result { for input in inputs { if self.newly_selected_inputs.contains_key(input.output_id()) { continue; } - if let Some(sdruc) = - sdruc_not_expired(&input.output, input_selection.latest_slot_commitment_id.slot_index()) - { + if let Some(sdruc) = sdruc_not_expired( + &input.output, + transaction_builder.latest_slot_commitment_id.slot_index(), + ) { // Skip if no additional amount is made available if input.output.amount() == sdruc.amount() { continue; @@ -169,7 +170,7 @@ impl AmountSelection { if input.output.native_token().is_some() { // Recalculate the remaining amount, as a new native token may require a new remainder output. let (remainder_amount, native_tokens_remainder, mana_remainder) = - self.remainder_amount(input_selection)?; + self.remainder_amount(transaction_builder)?; log::debug!( "Calculated new remainder_amount: {remainder_amount}, native_tokens_remainder: {native_tokens_remainder}" ); @@ -186,11 +187,14 @@ impl AmountSelection { Ok(false) } - pub(crate) fn remainder_amount(&self, input_selection: &InputSelection) -> Result<(u64, bool, bool), Error> { + pub(crate) fn remainder_amount( + &self, + transaction_builder: &TransactionBuilder, + ) -> Result<(u64, bool, bool), TransactionBuilderError> { let input_native_tokens = get_native_tokens(self.newly_selected_inputs.values().map(|input| &input.output))?.finish()?; - input_selection.required_remainder_amount(Some(input_native_tokens)) + transaction_builder.required_remainder_amount(Some(input_native_tokens)) } fn into_newly_selected_inputs(self) -> Vec { @@ -198,12 +202,12 @@ impl AmountSelection { } } -impl InputSelection { +impl TransactionBuilder { fn fulfil<'a>( &self, base_inputs: impl Iterator + Clone, amount_selection: &mut AmountSelection, - ) -> Result { + ) -> Result { let slot_index = self.latest_slot_commitment_id.slot_index(); // No native token, expired SDRUC. @@ -250,7 +254,10 @@ impl InputSelection { Ok(false) } - fn reduce_funds_of_chains(&mut self, amount_selection: &mut AmountSelection) -> Result<(), Error> { + fn reduce_funds_of_chains( + &mut self, + amount_selection: &mut AmountSelection, + ) -> Result<(), TransactionBuilderError> { // Only consider automatically transitioned outputs. for output in self.added_outputs.iter_mut() { let diff = amount_selection.missing_amount(); @@ -293,13 +300,13 @@ impl InputSelection { } } - Err(Error::InsufficientAmount { + Err(TransactionBuilderError::InsufficientAmount { found: amount_selection.inputs_sum, required: amount_selection.inputs_sum + amount_selection.missing_amount(), }) } - pub(crate) fn fulfill_amount_requirement(&mut self) -> Result, Error> { + pub(crate) fn fulfill_amount_requirement(&mut self) -> Result, TransactionBuilderError> { let mut amount_selection = AmountSelection::new(self)?; if amount_selection.missing_amount() == 0 { @@ -345,7 +352,7 @@ impl InputSelection { } if self.selected_inputs.len() + amount_selection.newly_selected_inputs.len() > INPUT_COUNT_MAX.into() { - return Err(Error::InvalidInputCount( + return Err(TransactionBuilderError::InvalidInputCount( self.selected_inputs.len() + amount_selection.newly_selected_inputs.len(), )); } @@ -368,7 +375,7 @@ impl InputSelection { fn fulfill_amount_requirement_inner( &mut self, amount_selection: &mut AmountSelection, - ) -> Result>, Error> { + ) -> Result>, TransactionBuilderError> { let slot_index = self.latest_slot_commitment_id.slot_index(); let basic_ed25519_inputs = self.available_inputs.iter().filter(|input| { diff --git a/sdk/src/client/api/block_builder/input_selection/requirement/context_inputs.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/context_inputs.rs similarity index 75% rename from sdk/src/client/api/block_builder/input_selection/requirement/context_inputs.rs rename to sdk/src/client/api/block_builder/transaction_builder/requirement/context_inputs.rs index ed95a7b027..8315adf8aa 100644 --- a/sdk/src/client/api/block_builder/input_selection/requirement/context_inputs.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/context_inputs.rs @@ -1,37 +1,40 @@ // Copyright 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use super::{Error, InputSelection}; +use super::{TransactionBuilder, TransactionBuilderError}; use crate::{ client::secret::types::InputSigningData, types::block::{ - context_input::{BlockIssuanceCreditContextInput, CommitmentContextInput, RewardContextInput}, + context_input::{BlockIssuanceCreditContextInput, CommitmentContextInput}, output::{AccountId, DelegationOutputBuilder, Output}, }, }; -impl InputSelection { - pub(crate) fn fulfill_context_inputs_requirement(&mut self) -> Result, Error> { +impl TransactionBuilder { + pub(crate) fn fulfill_context_inputs_requirement( + &mut self, + ) -> Result, TransactionBuilderError> { let mut needs_commitment_context = false; - for (idx, input) in self.selected_inputs.iter().enumerate() { + for input in &self.selected_inputs { match &input.output { // Transitioning an issuer account requires a BlockIssuanceCreditContextInput. Output::Account(account) => { if account.features().block_issuer().is_some() { log::debug!("Adding block issuance context input for transitioned account output"); - self.context_inputs.insert( - BlockIssuanceCreditContextInput::from(account.account_id_non_null(input.output_id())) - .into(), - ); + self.bic_context_inputs.insert(BlockIssuanceCreditContextInput::from( + account.account_id_non_null(input.output_id()), + )); } } // Transitioning an implicit account requires a BlockIssuanceCreditContextInput. Output::Basic(basic) => { if basic.is_implicit_account() { log::debug!("Adding block issuance context input for transitioned implicit account output"); - self.context_inputs - .insert(BlockIssuanceCreditContextInput::from(AccountId::from(input.output_id())).into()); + self.bic_context_inputs + .insert(BlockIssuanceCreditContextInput::from(AccountId::from( + input.output_id(), + ))); } } _ => (), @@ -49,7 +52,7 @@ impl InputSelection { if self.mana_rewards.get(input.output_id()).is_some() { log::debug!("Adding reward and commitment context input for output claiming mana rewards"); - self.context_inputs.insert(RewardContextInput::new(idx as _)?.into()); + self.reward_context_inputs.insert(*input.output_id()); needs_commitment_context = true; } } @@ -81,25 +84,16 @@ impl InputSelection { needs_commitment_context = true; } // BlockIssuanceCreditContextInput requires a CommitmentContextInput. - if self - .context_inputs - .iter() - .any(|c| c.kind() == BlockIssuanceCreditContextInput::KIND) - { + if !self.bic_context_inputs.is_empty() { // TODO https://github.com/iotaledger/iota-sdk/issues/1740 log::debug!("Adding commitment context input for output with block issuance credit context input"); needs_commitment_context = true; } - if needs_commitment_context - && !self - .context_inputs - .iter() - .any(|c| c.kind() == CommitmentContextInput::KIND) - { + if needs_commitment_context && self.commitment_context_input.is_none() { // TODO https://github.com/iotaledger/iota-sdk/issues/1740 - self.context_inputs - .insert(CommitmentContextInput::new(self.latest_slot_commitment_id).into()); + self.commitment_context_input + .replace(CommitmentContextInput::new(self.latest_slot_commitment_id)); } Ok(Vec::new()) } diff --git a/sdk/src/client/api/block_builder/input_selection/requirement/delegation.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/delegation.rs similarity index 88% rename from sdk/src/client/api/block_builder/input_selection/requirement/delegation.rs rename to sdk/src/client/api/block_builder/transaction_builder/requirement/delegation.rs index b2e11a5269..99134e017c 100644 --- a/sdk/src/client/api/block_builder/input_selection/requirement/delegation.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/delegation.rs @@ -1,7 +1,7 @@ // Copyright 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use super::{Error, InputSelection, Requirement}; +use super::{Requirement, TransactionBuilder, TransactionBuilderError}; use crate::{ client::secret::types::InputSigningData, types::block::output::{DelegationId, Output, OutputId}, @@ -29,12 +29,12 @@ pub(crate) fn is_delegation_with_id_non_null(output: &Output, delegation_id: &De } } -impl InputSelection { +impl TransactionBuilder { /// Fulfills an delegation requirement by selecting the appropriate delegation from the available inputs. pub(crate) fn fulfill_delegation_requirement( &mut self, delegation_id: DelegationId, - ) -> Result, Error> { + ) -> Result, TransactionBuilderError> { // Check if the requirement is already fulfilled. if let Some(input) = self .selected_inputs @@ -53,7 +53,9 @@ impl InputSelection { .available_inputs .iter() .position(|input| is_delegation_with_id(&input.output, &delegation_id, input.output_id())) - .ok_or(Error::UnfulfillableRequirement(Requirement::Delegation(delegation_id)))?; + .ok_or(TransactionBuilderError::UnfulfillableRequirement( + Requirement::Delegation(delegation_id), + ))?; // Remove the input from the available inputs, swap to make it O(1). let input = self.available_inputs.swap_remove(index); diff --git a/sdk/src/client/api/block_builder/input_selection/requirement/ed25519.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/ed25519.rs similarity index 88% rename from sdk/src/client/api/block_builder/input_selection/requirement/ed25519.rs rename to sdk/src/client/api/block_builder/transaction_builder/requirement/ed25519.rs index 5862af536c..5fd019e52e 100644 --- a/sdk/src/client/api/block_builder/input_selection/requirement/ed25519.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/ed25519.rs @@ -1,10 +1,10 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use super::{Error, InputSelection, Requirement}; +use super::{Requirement, TransactionBuilder, TransactionBuilderError}; use crate::{client::secret::types::InputSigningData, types::block::address::Address}; -impl InputSelection { +impl TransactionBuilder { // Checks if a selected input unlocks a given ED25519 address. fn selected_unlocks_ed25519_address(&self, input: &InputSigningData, address: &Address) -> bool { let required_address = input @@ -39,7 +39,10 @@ impl InputSelection { } /// Fulfills an ed25519 sender requirement by selecting an available input that unlocks its address. - pub(crate) fn fulfill_ed25519_requirement(&mut self, address: &Address) -> Result, Error> { + pub(crate) fn fulfill_ed25519_requirement( + &mut self, + address: &Address, + ) -> Result, TransactionBuilderError> { // Checks if the requirement is already fulfilled. if let Some(input) = self .selected_inputs @@ -77,7 +80,9 @@ impl InputSelection { Ok(vec![input]) } - None => Err(Error::UnfulfillableRequirement(Requirement::Ed25519(address.clone()))), + None => Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Ed25519( + address.clone(), + ))), } } } diff --git a/sdk/src/client/api/block_builder/input_selection/requirement/foundry.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/foundry.rs similarity index 83% rename from sdk/src/client/api/block_builder/input_selection/requirement/foundry.rs rename to sdk/src/client/api/block_builder/transaction_builder/requirement/foundry.rs index f1a2f5be1b..8f0e403fc9 100644 --- a/sdk/src/client/api/block_builder/input_selection/requirement/foundry.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/foundry.rs @@ -1,7 +1,7 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use super::{Error, InputSelection, Requirement}; +use super::{Requirement, TransactionBuilder, TransactionBuilderError}; use crate::{ client::secret::types::InputSigningData, types::block::output::{FoundryId, Output}, @@ -16,12 +16,12 @@ pub(crate) fn is_foundry_with_id(output: &Output, foundry_id: &FoundryId) -> boo } } -impl InputSelection { +impl TransactionBuilder { /// Fulfills a foundry requirement by selecting the appropriate foundry from the available inputs. pub(crate) fn fulfill_foundry_requirement( &mut self, foundry_id: FoundryId, - ) -> Result, Error> { + ) -> Result, TransactionBuilderError> { // Check if the requirement is already fulfilled. if let Some(input) = self .selected_inputs @@ -40,7 +40,9 @@ impl InputSelection { .available_inputs .iter() .position(|input| is_foundry_with_id(&input.output, &foundry_id)) - .ok_or(Error::UnfulfillableRequirement(Requirement::Foundry(foundry_id)))?; + .ok_or(TransactionBuilderError::UnfulfillableRequirement(Requirement::Foundry( + foundry_id, + )))?; // Remove the input from the available inputs, swap to make it O(1). let input = self.available_inputs.swap_remove(index); diff --git a/sdk/src/client/api/block_builder/input_selection/requirement/issuer.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/issuer.rs similarity index 53% rename from sdk/src/client/api/block_builder/input_selection/requirement/issuer.rs rename to sdk/src/client/api/block_builder/transaction_builder/requirement/issuer.rs index 99c961c418..3460e21d6b 100644 --- a/sdk/src/client/api/block_builder/input_selection/requirement/issuer.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/issuer.rs @@ -1,20 +1,23 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use super::{Error, InputSelection, Requirement}; +use super::{Requirement, TransactionBuilder, TransactionBuilderError}; use crate::{client::secret::types::InputSigningData, types::block::address::Address}; -impl InputSelection { +impl TransactionBuilder { /// Fulfills an issuer requirement by fulfilling the equivalent sender requirement. /// Potentially converts the error for a more accurate one. - pub(crate) fn fulfill_issuer_requirement(&mut self, address: &Address) -> Result, Error> { + pub(crate) fn fulfill_issuer_requirement( + &mut self, + address: &Address, + ) -> Result, TransactionBuilderError> { log::debug!("Treating {address:?} issuer requirement as a sender requirement"); match self.fulfill_sender_requirement(address) { Ok(res) => Ok(res), - Err(Error::UnfulfillableRequirement(Requirement::Sender(_))) => { - Err(Error::UnfulfillableRequirement(Requirement::Issuer(address.clone()))) - } + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender(_))) => Err( + TransactionBuilderError::UnfulfillableRequirement(Requirement::Issuer(address.clone())), + ), Err(e) => Err(e), } } diff --git a/sdk/src/client/api/block_builder/input_selection/requirement/mana.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/mana.rs similarity index 84% rename from sdk/src/client/api/block_builder/input_selection/requirement/mana.rs rename to sdk/src/client/api/block_builder/transaction_builder/requirement/mana.rs index 8eef8950da..94538ad6ec 100644 --- a/sdk/src/client/api/block_builder/input_selection/requirement/mana.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/mana.rs @@ -1,14 +1,12 @@ // Copyright 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -// Copyright 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 use std::collections::HashMap; -use super::{Error, InputSelection}; +use super::{TransactionBuilder, TransactionBuilderError}; use crate::{ client::{ - api::input_selection::{MinManaAllotment, Requirement}, + api::transaction_builder::{MinManaAllotment, Requirement}, secret::types::InputSigningData, }, types::block::{ @@ -16,15 +14,15 @@ use crate::{ input::{Input, UtxoInput}, mana::ManaAllotment, output::{AccountOutputBuilder, Output}, - payload::{signed_transaction::Transaction, SignedTransactionPayload}, + payload::{dto::SignedTransactionPayloadDto, signed_transaction::Transaction, SignedTransactionPayload}, signature::Ed25519Signature, unlock::{AccountUnlock, NftUnlock, ReferenceUnlock, SignatureUnlock, Unlock, Unlocks}, BlockError, }, }; -impl InputSelection { - pub(crate) fn fulfill_mana_requirement(&mut self) -> Result, Error> { +impl TransactionBuilder { + pub(crate) fn fulfill_mana_requirement(&mut self) -> Result, TransactionBuilderError> { let Some(MinManaAllotment { issuer_id, reference_mana_cost, @@ -36,6 +34,8 @@ impl InputSelection { return Ok(Vec::new()); }; + let mut should_recalculate = false; + if !self.selected_inputs.is_empty() && self.all_outputs().next().is_some() { self.selected_inputs = Self::sort_input_signing_data( std::mem::take(&mut self.selected_inputs), @@ -61,9 +61,8 @@ impl InputSelection { // Add the empty allotment so the work score includes it self.mana_allotments.entry(issuer_id).or_default(); - // If the transaction fails to build, just keep going in case another requirement helps let transaction = builder - .with_context_inputs(self.context_inputs.clone()) + .with_context_inputs(self.context_inputs()) .with_mana_allotments( self.mana_allotments .iter() @@ -73,6 +72,11 @@ impl InputSelection { let signed_transaction = SignedTransactionPayload::new(transaction, self.null_transaction_unlocks()?)?; + log::debug!( + "signed_transaction: {}", + serde_json::to_string_pretty(&SignedTransactionPayloadDto::from(&signed_transaction)).unwrap() + ); + let block_work_score = self.protocol_parameters.work_score(&signed_transaction) + self.protocol_parameters.work_score_parameters().block(); @@ -85,7 +89,7 @@ impl InputSelection { } = self .min_mana_allotment .as_mut() - .ok_or(Error::UnfulfillableRequirement(Requirement::Mana))?; + .ok_or(TransactionBuilderError::UnfulfillableRequirement(Requirement::Mana))?; // Add the required allotment to the issuing allotment if required_allotment_mana > self.mana_allotments[issuer_id] { @@ -96,14 +100,22 @@ impl InputSelection { *self.mana_allotments.get_mut(issuer_id).unwrap() = required_allotment_mana; log::debug!("Adding {additional_allotment} to allotment debt {allotment_debt}"); *allotment_debt += additional_allotment; + should_recalculate = true; } else { log::debug!("Setting allotment debt to {}", self.mana_allotments[issuer_id]); *allotment_debt = self.mana_allotments[issuer_id]; + // Since the allotment is fine, check if the mana balance is good because + // we can exit early in that case. + let (input_mana, output_mana) = self.mana_sums(true)?; + if input_mana == output_mana { + log::debug!("allotments and mana are both correct, no further action needed"); + return Ok(Vec::new()); + } } - self.reduce_account_output()?; - } else if !self.requirements.contains(&Requirement::Mana) { - self.requirements.push(Requirement::Mana); + should_recalculate |= self.reduce_account_output()?; + } else { + should_recalculate = true; } // Remainders can only be calculated when the input mana is >= the output mana @@ -112,17 +124,16 @@ impl InputSelection { self.update_remainders()?; } - let additional_inputs = self.get_inputs_for_mana_balance()?; - // If we needed more inputs to cover the additional allotment mana - // then update remainders and re-run this requirement - if additional_inputs && !self.requirements.contains(&Requirement::Mana) { + should_recalculate |= self.get_inputs_for_mana_balance()?; + + if should_recalculate && !self.requirements.contains(&Requirement::Mana) { self.requirements.push(Requirement::Mana); } Ok(Vec::new()) } - fn reduce_account_output(&mut self) -> Result<(), Error> { + fn reduce_account_output(&mut self) -> Result { let MinManaAllotment { issuer_id, allotment_debt, @@ -130,7 +141,7 @@ impl InputSelection { } = self .min_mana_allotment .as_mut() - .ok_or(Error::UnfulfillableRequirement(Requirement::Mana))?; + .ok_or(TransactionBuilderError::UnfulfillableRequirement(Requirement::Mana))?; if let Some(output) = self .provided_outputs .iter_mut() @@ -149,11 +160,12 @@ impl InputSelection { .finish_output()?; *allotment_debt = allotment_debt.saturating_sub(output_mana); log::debug!("Allotment debt after reduction: {}", allotment_debt); + return Ok(true); } - Ok(()) + Ok(false) } - pub(crate) fn null_transaction_unlocks(&self) -> Result { + pub(crate) fn null_transaction_unlocks(&self) -> Result { let mut blocks = Vec::new(); let mut block_indexes = HashMap::::new(); @@ -193,7 +205,7 @@ impl InputSelection { // than the current block index match &required_address { Address::Ed25519(_) | Address::ImplicitAccountCreation(_) => {} - _ => Err(Error::MissingInputWithEd25519Address)?, + _ => Err(TransactionBuilderError::MissingInputWithEd25519Address)?, } let block = SignatureUnlock::new( @@ -231,7 +243,7 @@ impl InputSelection { Ok(Unlocks::new(blocks)?) } - pub(crate) fn get_inputs_for_mana_balance(&mut self) -> Result { + pub(crate) fn get_inputs_for_mana_balance(&mut self) -> Result { let (mut selected_mana, mut required_mana) = self.mana_sums(true)?; log::debug!("Mana requirement selected mana: {selected_mana}, required mana: {required_mana}"); @@ -241,7 +253,7 @@ impl InputSelection { log::debug!("Mana requirement already fulfilled"); } else { if !self.allow_additional_input_selection { - return Err(Error::AdditionalInputsRequired(Requirement::Mana)); + return Err(TransactionBuilderError::AdditionalInputsRequired(Requirement::Mana)); } let include_generated = self.burn.as_ref().map_or(true, |b| !b.generated_mana()); // TODO we should do as for the amount and have preferences on which inputs to pick. @@ -260,7 +272,7 @@ impl InputSelection { Ok(added_inputs) } - pub(crate) fn initial_mana_excess(&self) -> Result { + pub(crate) fn initial_mana_excess(&self) -> Result { let output_mana = self.provided_outputs.iter().map(|o| o.mana()).sum::(); let mut input_mana = 0; let include_generated = self.burn.as_ref().map_or(true, |b| !b.generated_mana()); @@ -276,7 +288,7 @@ impl InputSelection { Ok(input_mana.saturating_sub(output_mana)) } - pub(crate) fn mana_sums(&self, include_remainders: bool) -> Result<(u64, u64), Error> { + pub(crate) fn mana_sums(&self, include_remainders: bool) -> Result<(u64, u64), TransactionBuilderError> { let mut required_mana = self.non_remainder_outputs().map(|o| o.mana()).sum::() + self.mana_allotments.values().sum::(); if include_remainders { @@ -288,7 +300,10 @@ impl InputSelection { Ok((self.total_selected_mana(None)?, required_mana)) } - pub(crate) fn total_selected_mana(&self, include_generated: impl Into> + Copy) -> Result { + pub(crate) fn total_selected_mana( + &self, + include_generated: impl Into> + Copy, + ) -> Result { let mut selected_mana = 0; let include_generated = include_generated .into() @@ -301,7 +316,7 @@ impl InputSelection { Ok(selected_mana) } - fn total_mana(&self, input: &InputSigningData, include_generated: bool) -> Result { + fn total_mana(&self, input: &InputSigningData, include_generated: bool) -> Result { Ok(self.mana_rewards.get(input.output_id()).copied().unwrap_or_default() + if include_generated { input.output.available_mana( diff --git a/sdk/src/client/api/block_builder/input_selection/requirement/mod.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/mod.rs similarity index 92% rename from sdk/src/client/api/block_builder/input_selection/requirement/mod.rs rename to sdk/src/client/api/block_builder/transaction_builder/requirement/mod.rs index d0b88a7859..02136c4839 100644 --- a/sdk/src/client/api/block_builder/input_selection/requirement/mod.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/mod.rs @@ -17,7 +17,7 @@ use self::{ account::is_account_with_id_non_null, delegation::is_delegation_with_id_non_null, foundry::is_foundry_with_id, nft::is_nft_with_id_non_null, }; -use super::{Error, InputSelection}; +use super::{TransactionBuilder, TransactionBuilderError}; use crate::{ client::secret::types::InputSigningData, types::block::{ @@ -54,10 +54,13 @@ pub enum Requirement { ContextInputs, } -impl InputSelection { +impl TransactionBuilder { /// Fulfills a requirement by selecting the appropriate available inputs. /// Returns the selected inputs and an optional new requirement. - pub(crate) fn fulfill_requirement(&mut self, requirement: &Requirement) -> Result, Error> { + pub(crate) fn fulfill_requirement( + &mut self, + requirement: &Requirement, + ) -> Result, TransactionBuilderError> { log::debug!("Fulfilling requirement {requirement:?}"); match requirement { @@ -162,7 +165,7 @@ impl InputSelection { } /// Gets requirements from burn. - pub(crate) fn burn_requirements(&mut self) -> Result<(), Error> { + pub(crate) fn burn_requirements(&mut self) -> Result<(), TransactionBuilderError> { if let Some(burn) = self.burn.as_ref() { if burn.mana() && self.initial_mana_excess()? > 0 { self.transaction_capabilities @@ -174,7 +177,7 @@ impl InputSelection { .non_remainder_outputs() .any(|output| is_account_with_id_non_null(output, account_id)) { - return Err(Error::BurnAndTransition(ChainId::from(*account_id))); + return Err(TransactionBuilderError::BurnAndTransition(ChainId::from(*account_id))); } let requirement = Requirement::Account(*account_id); @@ -189,7 +192,7 @@ impl InputSelection { .non_remainder_outputs() .any(|output| is_foundry_with_id(output, foundry_id)) { - return Err(Error::BurnAndTransition(ChainId::from(*foundry_id))); + return Err(TransactionBuilderError::BurnAndTransition(ChainId::from(*foundry_id))); } let requirement = Requirement::Foundry(*foundry_id); @@ -204,7 +207,7 @@ impl InputSelection { .non_remainder_outputs() .any(|output| is_nft_with_id_non_null(output, nft_id)) { - return Err(Error::BurnAndTransition(ChainId::from(*nft_id))); + return Err(TransactionBuilderError::BurnAndTransition(ChainId::from(*nft_id))); } let requirement = Requirement::Nft(*nft_id); @@ -219,7 +222,9 @@ impl InputSelection { .non_remainder_outputs() .any(|output| is_delegation_with_id_non_null(output, delegation_id)) { - return Err(Error::BurnAndTransition(ChainId::from(*delegation_id))); + return Err(TransactionBuilderError::BurnAndTransition(ChainId::from( + *delegation_id, + ))); } let requirement = Requirement::Delegation(*delegation_id); diff --git a/sdk/src/client/api/block_builder/input_selection/requirement/native_tokens.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/native_tokens.rs similarity index 92% rename from sdk/src/client/api/block_builder/input_selection/requirement/native_tokens.rs rename to sdk/src/client/api/block_builder/transaction_builder/requirement/native_tokens.rs index be894bbecc..e1da4ce475 100644 --- a/sdk/src/client/api/block_builder/input_selection/requirement/native_tokens.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/native_tokens.rs @@ -5,7 +5,7 @@ use std::{cmp::Ordering, collections::HashSet}; use primitive_types::U256; -use super::{Error, InputSelection}; +use super::{TransactionBuilder, TransactionBuilderError}; use crate::{ client::secret::types::InputSigningData, types::block::{ @@ -14,7 +14,9 @@ use crate::{ }, }; -pub(crate) fn get_native_tokens<'a>(outputs: impl Iterator) -> Result { +pub(crate) fn get_native_tokens<'a>( + outputs: impl Iterator, +) -> Result { let mut required_native_tokens = NativeTokensBuilder::new(); for output in outputs { @@ -30,7 +32,7 @@ pub(crate) fn get_native_tokens<'a>(outputs: impl Iterator) - pub(crate) fn get_native_tokens_diff( inputs: &NativeTokensBuilder, outputs: &NativeTokensBuilder, -) -> Result, Error> { +) -> Result, TransactionBuilderError> { let mut native_tokens_diff = NativeTokensBuilder::new(); for (token_id, input_amount) in inputs.iter() { @@ -53,8 +55,10 @@ pub(crate) fn get_native_tokens_diff( } } -impl InputSelection { - pub(crate) fn fulfill_native_tokens_requirement(&mut self) -> Result, Error> { +impl TransactionBuilder { + pub(crate) fn fulfill_native_tokens_requirement( + &mut self, + ) -> Result, TransactionBuilderError> { let mut input_native_tokens = get_native_tokens(self.selected_inputs.iter().map(|input| &input.output))?; let mut output_native_tokens = get_native_tokens(self.non_remainder_outputs())?; let (minted_native_tokens, melted_native_tokens) = self.get_minted_and_melted_native_tokens()?; @@ -105,7 +109,7 @@ impl InputSelection { } if amount < diff.amount() { - return Err(Error::InsufficientNativeTokenAmount { + return Err(TransactionBuilderError::InsufficientNativeTokenAmount { token_id: *diff.token_id(), found: amount, required: diff.amount(), @@ -128,7 +132,7 @@ impl InputSelection { pub(crate) fn get_minted_and_melted_native_tokens( &self, - ) -> Result<(NativeTokensBuilder, NativeTokensBuilder), Error> { + ) -> Result<(NativeTokensBuilder, NativeTokensBuilder), TransactionBuilderError> { let mut minted_native_tokens = NativeTokensBuilder::new(); let mut melted_native_tokens = NativeTokensBuilder::new(); diff --git a/sdk/src/client/api/block_builder/input_selection/requirement/nft.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/nft.rs similarity index 83% rename from sdk/src/client/api/block_builder/input_selection/requirement/nft.rs rename to sdk/src/client/api/block_builder/transaction_builder/requirement/nft.rs index 0ae00082c3..122f334052 100644 --- a/sdk/src/client/api/block_builder/input_selection/requirement/nft.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/nft.rs @@ -1,7 +1,7 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use super::{Error, InputSelection, Requirement}; +use super::{Requirement, TransactionBuilder, TransactionBuilderError}; use crate::{ client::secret::types::InputSigningData, types::block::output::{NftId, Output, OutputId}, @@ -29,9 +29,12 @@ pub(crate) fn is_nft_with_id_non_null(output: &Output, nft_id: &NftId) -> bool { } } -impl InputSelection { +impl TransactionBuilder { /// Fulfills an nft requirement by selecting the appropriate nft from the available inputs. - pub(crate) fn fulfill_nft_requirement(&mut self, nft_id: NftId) -> Result, Error> { + pub(crate) fn fulfill_nft_requirement( + &mut self, + nft_id: NftId, + ) -> Result, TransactionBuilderError> { // Check if the requirement is already fulfilled. if let Some(input) = self .selected_inputs @@ -47,7 +50,9 @@ impl InputSelection { .available_inputs .iter() .position(|input| is_nft_with_id(&input.output, &nft_id, input.output_id())) - .ok_or(Error::UnfulfillableRequirement(Requirement::Nft(nft_id)))?; + .ok_or(TransactionBuilderError::UnfulfillableRequirement(Requirement::Nft( + nft_id, + )))?; // Remove the input from the available inputs, swap to make it O(1). let input = self.available_inputs.swap_remove(index); diff --git a/sdk/src/client/api/block_builder/input_selection/requirement/sender.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/sender.rs similarity index 69% rename from sdk/src/client/api/block_builder/input_selection/requirement/sender.rs rename to sdk/src/client/api/block_builder/transaction_builder/requirement/sender.rs index e481cf3023..8ecb4cbf56 100644 --- a/sdk/src/client/api/block_builder/input_selection/requirement/sender.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/sender.rs @@ -1,21 +1,24 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use super::{Error, InputSelection, Requirement}; +use super::{Requirement, TransactionBuilder, TransactionBuilderError}; use crate::{client::secret::types::InputSigningData, types::block::address::Address}; -impl InputSelection { +impl TransactionBuilder { /// Fulfills a sender requirement by selecting an available input that unlocks its address. - pub(crate) fn fulfill_sender_requirement(&mut self, address: &Address) -> Result, Error> { + pub(crate) fn fulfill_sender_requirement( + &mut self, + address: &Address, + ) -> Result, TransactionBuilderError> { match address { Address::Ed25519(_) => { log::debug!("Treating {address:?} sender requirement as an ed25519 requirement"); match self.fulfill_ed25519_requirement(address) { Ok(res) => Ok(res), - Err(Error::UnfulfillableRequirement(Requirement::Ed25519(_))) => { - Err(Error::UnfulfillableRequirement(Requirement::Sender(address.clone()))) - } + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Ed25519(_))) => Err( + TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender(address.clone())), + ), Err(e) => Err(e), } } @@ -25,9 +28,9 @@ impl InputSelection { // A state transition is required to unlock the account address. match self.fulfill_account_requirement(account_address.into_account_id()) { Ok(res) => Ok(res), - Err(Error::UnfulfillableRequirement(Requirement::Account(_))) => { - Err(Error::UnfulfillableRequirement(Requirement::Sender(address.clone()))) - } + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Account(_))) => Err( + TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender(address.clone())), + ), Err(e) => Err(e), } } @@ -36,9 +39,9 @@ impl InputSelection { match self.fulfill_nft_requirement(nft_address.into_nft_id()) { Ok(res) => Ok(res), - Err(Error::UnfulfillableRequirement(Requirement::Nft(_))) => { - Err(Error::UnfulfillableRequirement(Requirement::Sender(address.clone()))) - } + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Nft(_))) => Err( + TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender(address.clone())), + ), Err(e) => Err(e), } } @@ -68,7 +71,9 @@ impl InputSelection { } if cumulative_weight < multi_address.threshold() { - Err(Error::UnfulfillableRequirement(Requirement::Sender(address.clone()))) + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender( + address.clone(), + ))) } else { Ok(Vec::new()) } @@ -78,7 +83,7 @@ impl InputSelection { self.fulfill_sender_requirement(restricted_address.address()) } - _ => Err(Error::UnsupportedAddressType(address.kind())), + _ => Err(TransactionBuilderError::UnsupportedAddressType(address.kind())), } } } diff --git a/sdk/src/client/api/block_builder/input_selection/transition.rs b/sdk/src/client/api/block_builder/transaction_builder/transition.rs similarity index 92% rename from sdk/src/client/api/block_builder/input_selection/transition.rs rename to sdk/src/client/api/block_builder/transaction_builder/transition.rs index cc86d3419d..1999cf5174 100644 --- a/sdk/src/client/api/block_builder/input_selection/transition.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/transition.rs @@ -3,7 +3,7 @@ use super::{ requirement::{account::is_account_with_id_non_null, foundry::is_foundry_with_id, nft::is_nft_with_id_non_null}, - Error, InputSelection, + TransactionBuilder, TransactionBuilderError, }; use crate::{ client::secret::types::InputSigningData, @@ -16,13 +16,13 @@ use crate::{ }, }; -impl InputSelection { +impl TransactionBuilder { /// Transitions an account input by creating a new account output if required. fn transition_account_input( &mut self, input: &AccountOutput, output_id: &OutputId, - ) -> Result, Error> { + ) -> Result, TransactionBuilderError> { let account_id = input.account_id_non_null(output_id); // Do not create an account output if the account input is to be burned. @@ -84,7 +84,11 @@ impl InputSelection { } /// Transitions an nft input by creating a new nft output if required. - fn transition_nft_input(&mut self, input: &NftOutput, output_id: &OutputId) -> Result, Error> { + fn transition_nft_input( + &mut self, + input: &NftOutput, + output_id: &OutputId, + ) -> Result, TransactionBuilderError> { let nft_id = input.nft_id_non_null(output_id); // Do not create an nft output if the nft input is to be burned. @@ -126,7 +130,7 @@ impl InputSelection { &mut self, input: &FoundryOutput, output_id: &OutputId, - ) -> Result, Error> { + ) -> Result, TransactionBuilderError> { let foundry_id = input.id(); // Do not create a foundry output if the foundry input is to be burned. @@ -160,7 +164,10 @@ impl InputSelection { /// Transitions an input by creating a new output if required. /// If no `account_transition` is provided, assumes a state transition. - pub(crate) fn transition_input(&mut self, input: &InputSigningData) -> Result, Error> { + pub(crate) fn transition_input( + &mut self, + input: &InputSigningData, + ) -> Result, TransactionBuilderError> { match &input.output { Output::Account(account_input) => self.transition_account_input(account_input, input.output_id()), Output::Foundry(foundry_input) => self.transition_foundry_input(foundry_input, input.output_id()), diff --git a/sdk/src/client/api/high_level.rs b/sdk/src/client/api/high_level.rs index 67a7cee4da..dec4a84ccd 100644 --- a/sdk/src/client/api/high_level.rs +++ b/sdk/src/client/api/high_level.rs @@ -7,8 +7,8 @@ use futures::{StreamExt, TryStreamExt}; use crate::{ client::{ - api::input_selection::Error as InputSelectionError, constants::FIVE_MINUTES_IN_NANOSECONDS, error::ClientError, - node_api::indexer::query_parameters::BasicOutputQueryParameters, unix_timestamp_now, Client, + api::transaction_builder::TransactionBuilderError, constants::FIVE_MINUTES_IN_NANOSECONDS, + node_api::indexer::query_parameters::BasicOutputQueryParameters, unix_timestamp_now, Client, ClientError, }, types::{ api::core::OutputWithMetadataResponse, @@ -102,7 +102,7 @@ impl Client { } if total_already_spent < amount { - return Err(InputSelectionError::InsufficientAmount { + return Err(TransactionBuilderError::InsufficientAmount { found: total_already_spent, required: amount, })?; diff --git a/sdk/src/client/error.rs b/sdk/src/client/error.rs index 232dd7e93a..7a17381d2a 100644 --- a/sdk/src/client/error.rs +++ b/sdk/src/client/error.rs @@ -9,7 +9,7 @@ use packable::error::UnexpectedEOF; use serde::{ser::Serializer, Serialize}; use crate::{ - client::api::input_selection::Error as InputSelectionError, + client::api::transaction_builder::TransactionBuilderError, types::block::{ address::AddressError, context_input::ContextInputError, @@ -159,9 +159,9 @@ pub enum ClientError { /// URL validation error #[error("{0}")] UrlValidation(String), - /// Input selection error. + /// Transaction builder error. #[error("{0}")] - InputSelection(#[from] InputSelectionError), + TransactionBuilder(#[from] TransactionBuilderError), /// Missing BIP32 chain to sign with. #[error("missing BIP32 chain to sign with")] MissingBip32Chain, diff --git a/sdk/src/client/secret/mod.rs b/sdk/src/client/secret/mod.rs index 5ec6b2b48b..1ff12b4b86 100644 --- a/sdk/src/client/secret/mod.rs +++ b/sdk/src/client/secret/mod.rs @@ -48,10 +48,7 @@ pub use self::types::{GenerateAddressOptions, LedgerNanoStatus}; use crate::client::secret::types::StrongholdDto; use crate::{ client::{ - api::{ - input_selection::Error as InputSelectionError, transaction::validate_signed_transaction_payload_length, - verify_semantic, PreparedTransactionData, - }, + api::{transaction_builder::TransactionBuilderError, PreparedTransactionData, SignedTransactionData}, ClientError, }, types::block::{ @@ -612,7 +609,7 @@ where // than the current block index match &required_address { Address::Ed25519(_) | Address::ImplicitAccountCreation(_) => {} - _ => Err(InputSelectionError::MissingInputWithEd25519Address)?, + _ => Err(TransactionBuilderError::MissingInputWithEd25519Address)?, } let chain = input.chain.ok_or(ClientError::MissingBip32Chain)?; @@ -669,13 +666,19 @@ where } = prepared_transaction_data; let tx_payload = SignedTransactionPayload::new(transaction, unlocks)?; - validate_signed_transaction_payload_length(&tx_payload)?; + tx_payload.validate_length()?; - verify_semantic(&inputs_data, &tx_payload, mana_rewards, protocol_parameters.clone()).inspect_err(|e| { - log::debug!("[sign_transaction] conflict: {e:?} for {tx_payload:#?}"); + let data = SignedTransactionData { + payload: tx_payload, + inputs_data, + mana_rewards, + }; + + data.verify_semantic(protocol_parameters).inspect_err(|e| { + log::debug!("[sign_transaction] conflict: {e:?} for {:#?}", data.payload); })?; - Ok(tx_payload) + Ok(data.payload) } #[async_trait] diff --git a/sdk/src/types/block/semantic/mod.rs b/sdk/src/types/block/semantic/mod.rs index d0400af283..0840359181 100644 --- a/sdk/src/types/block/semantic/mod.rs +++ b/sdk/src/types/block/semantic/mod.rs @@ -30,7 +30,7 @@ pub struct SemanticValidationContext<'a> { pub(crate) unlocks: Option<&'a [Unlock]>, pub(crate) input_amount: u64, pub(crate) input_mana: u64, - pub(crate) mana_rewards: Option>, + pub(crate) mana_rewards: Option<&'a BTreeMap>, pub(crate) commitment_context_input: Option, pub(crate) reward_context_inputs: HashMap, pub(crate) input_native_tokens: BTreeMap, @@ -43,7 +43,7 @@ pub struct SemanticValidationContext<'a> { pub(crate) unlocked_addresses: HashSet
, pub(crate) storage_deposit_returns: HashMap, pub(crate) simple_deposits: HashMap, - pub(crate) protocol_parameters: ProtocolParameters, + pub(crate) protocol_parameters: &'a ProtocolParameters, } impl<'a> SemanticValidationContext<'a> { @@ -52,8 +52,8 @@ impl<'a> SemanticValidationContext<'a> { transaction: &'a Transaction, inputs: &'a [(&'a OutputId, &'a Output)], unlocks: Option<&'a [Unlock]>, - mana_rewards: Option>, - protocol_parameters: ProtocolParameters, + mana_rewards: Option<&'a BTreeMap>, + protocol_parameters: &'a ProtocolParameters, ) -> Self { let transaction_id = transaction.id(); let input_chains = inputs @@ -334,9 +334,11 @@ impl<'a> SemanticValidationContext<'a> { Output::Delegation(output) => (output.amount(), 0, None, None), }; - if let Some(sender) = features.and_then(Features::sender) { - if !self.unlocked_addresses.contains(sender.address()) { - return Err(TransactionFailureReason::SenderFeatureNotUnlocked); + if self.unlocks.is_some() { + if let Some(sender) = features.and_then(Features::sender) { + if !self.unlocked_addresses.contains(sender.address()) { + return Err(TransactionFailureReason::SenderFeatureNotUnlocked); + } } } diff --git a/sdk/src/types/block/semantic/state_transition.rs b/sdk/src/types/block/semantic/state_transition.rs index e420fc7f3c..f2cd780db3 100644 --- a/sdk/src/types/block/semantic/state_transition.rs +++ b/sdk/src/types/block/semantic/state_transition.rs @@ -130,9 +130,11 @@ impl BasicOutput { return Err(TransactionFailureReason::BlockIssuerNotExpired); } - if let Some(issuer) = next_state.immutable_features().issuer() { - if !context.unlocked_addresses.contains(issuer.address()) { - return Err(TransactionFailureReason::IssuerFeatureNotUnlocked); + if context.unlocks.is_some() { + if let Some(issuer) = next_state.immutable_features().issuer() { + if !context.unlocked_addresses.contains(issuer.address()) { + return Err(TransactionFailureReason::IssuerFeatureNotUnlocked); + } } } @@ -172,9 +174,11 @@ impl StateTransitionVerifier for AccountOutput { } } - if let Some(issuer) = next_state.immutable_features().issuer() { - if !context.unlocked_addresses.contains(issuer.address()) { - return Err(TransactionFailureReason::IssuerFeatureNotUnlocked); + if context.unlocks.is_some() { + if let Some(issuer) = next_state.immutable_features().issuer() { + if !context.unlocked_addresses.contains(issuer.address()) { + return Err(TransactionFailureReason::IssuerFeatureNotUnlocked); + } } } @@ -351,9 +355,11 @@ impl StateTransitionVerifier for AnchorOutput { return Err(TransactionFailureReason::NewChainOutputHasNonZeroedId); } - if let Some(issuer) = next_state.immutable_features().issuer() { - if !context.unlocked_addresses.contains(issuer.address()) { - return Err(TransactionFailureReason::IssuerFeatureNotUnlocked); + if context.unlocks.is_some() { + if let Some(issuer) = next_state.immutable_features().issuer() { + if !context.unlocked_addresses.contains(issuer.address()) { + return Err(TransactionFailureReason::IssuerFeatureNotUnlocked); + } } } @@ -487,9 +493,11 @@ impl StateTransitionVerifier for NftOutput { return Err(TransactionFailureReason::NewChainOutputHasNonZeroedId); } - if let Some(issuer) = next_state.immutable_features().issuer() { - if !context.unlocked_addresses.contains(issuer.address()) { - return Err(TransactionFailureReason::IssuerFeatureNotUnlocked); + if context.unlocks.is_some() { + if let Some(issuer) = next_state.immutable_features().issuer() { + if !context.unlocked_addresses.contains(issuer.address()) { + return Err(TransactionFailureReason::IssuerFeatureNotUnlocked); + } } } diff --git a/sdk/src/wallet/error.rs b/sdk/src/wallet/error.rs index fca0ab64e9..ea77f990e1 100644 --- a/sdk/src/wallet/error.rs +++ b/sdk/src/wallet/error.rs @@ -183,17 +183,18 @@ impl Serialize for WalletError { } } -impl From for WalletError { - fn from(error: crate::client::api::input_selection::Error) -> Self { +impl From for WalletError { + fn from(error: crate::client::api::transaction_builder::TransactionBuilderError) -> Self { // Map "same" error so it's easier to handle match error { - crate::client::api::input_selection::Error::InsufficientAmount { found, required } => { - Self::InsufficientFunds { - available: found, - required, - } - } - _ => Self::Client(ClientError::InputSelection(error)), + crate::client::api::transaction_builder::TransactionBuilderError::InsufficientAmount { + found, + required, + } => Self::InsufficientFunds { + available: found, + required, + }, + _ => Self::Client(ClientError::TransactionBuilder(error)), } } } diff --git a/sdk/src/wallet/events/mod.rs b/sdk/src/wallet/events/mod.rs index c75cc39acc..1335a6dd9d 100644 --- a/sdk/src/wallet/events/mod.rs +++ b/sdk/src/wallet/events/mod.rs @@ -148,7 +148,7 @@ mod tests { // emit events emitter.emit(WalletEvent::TransactionProgress( - TransactionProgressEvent::SelectingInputs, + TransactionProgressEvent::BuildingTransaction, )); emitter.emit(WalletEvent::TransactionInclusion(TransactionInclusionEvent { transaction_id: TransactionId::from_str( @@ -164,7 +164,7 @@ mod tests { emitter.clear([WalletEventType::TransactionProgress]); // emit event of removed type emitter.emit(WalletEvent::TransactionProgress( - TransactionProgressEvent::SelectingInputs, + TransactionProgressEvent::BuildingTransaction, )); assert_eq!(2, event_counter.load(Ordering::SeqCst)); @@ -173,7 +173,7 @@ mod tests { emitter.clear([]); // emit events emitter.emit(WalletEvent::TransactionProgress( - TransactionProgressEvent::SelectingInputs, + TransactionProgressEvent::BuildingTransaction, )); emitter.emit(WalletEvent::TransactionInclusion(TransactionInclusionEvent { transaction_id: TransactionId::from_str( @@ -193,7 +193,7 @@ mod tests { for _ in 0..1_000_000 { emitter.emit(WalletEvent::TransactionProgress( - TransactionProgressEvent::SelectingInputs, + TransactionProgressEvent::BuildingTransaction, )); } assert_eq!(1_000_002, event_counter.load(Ordering::SeqCst)); diff --git a/sdk/src/wallet/events/types.rs b/sdk/src/wallet/events/types.rs index b477c8e20b..279da997f7 100644 --- a/sdk/src/wallet/events/types.rs +++ b/sdk/src/wallet/events/types.rs @@ -192,8 +192,8 @@ pub struct TransactionInclusionEvent { #[derive(Clone, Debug, Eq, PartialEq)] #[non_exhaustive] pub enum TransactionProgressEvent { - /// Performing input selection. - SelectingInputs, + /// Building a transaction. + BuildingTransaction, /// Generating remainder value deposit address. GeneratingRemainderDepositAddress(AddressData), /// Prepared transaction. @@ -244,7 +244,7 @@ impl Serialize for TransactionProgressEvent { event: TransactionProgressEvent_<'a>, } let event = match self { - Self::SelectingInputs => TypedTransactionProgressEvent_ { + Self::BuildingTransaction => TypedTransactionProgressEvent_ { kind: 0, event: TransactionProgressEvent_::T0, }, @@ -299,7 +299,7 @@ impl<'de> Deserialize<'de> for TransactionProgressEvent { .ok_or_else(|| serde::de::Error::custom("invalid transaction progress event type"))? as u8 { - 0 => Self::SelectingInputs, + 0 => Self::BuildingTransaction, 1 => Self::GeneratingRemainderDepositAddress(AddressData::deserialize(value).map_err(|e| { serde::de::Error::custom(format!("cannot deserialize GeneratingRemainderDepositAddress: {e}")) })?), diff --git a/sdk/src/wallet/mod.rs b/sdk/src/wallet/mod.rs index e3e2951a0e..590c1f24ee 100644 --- a/sdk/src/wallet/mod.rs +++ b/sdk/src/wallet/mod.rs @@ -68,7 +68,6 @@ pub use self::{ staking::begin::BeginStakingParams, }, prepare_output::{Assets, Features, OutputParams, ReturnStrategy, StorageDeposit, Unlocks}, - RemainderValueStrategy, TransactionOptions, }, }, types::OutputData, diff --git a/sdk/src/wallet/operations/output_claiming.rs b/sdk/src/wallet/operations/output_claiming.rs index cf284ae519..1e7740de4a 100644 --- a/sdk/src/wallet/operations/output_claiming.rs +++ b/sdk/src/wallet/operations/output_claiming.rs @@ -6,7 +6,11 @@ use std::collections::HashSet; use serde::{Deserialize, Serialize}; use crate::{ - client::{api::PreparedTransactionData, secret::SecretManage, ClientError}, + client::{ + api::{options::TransactionOptions, PreparedTransactionData}, + secret::SecretManage, + ClientError, + }, types::block::{ address::{Address, Bech32Address, Ed25519Address}, output::{ @@ -17,7 +21,7 @@ use crate::{ }, wallet::{ core::WalletLedger, - operations::{helpers::time::can_output_be_unlocked_now, transaction::TransactionOptions}, + operations::helpers::time::can_output_be_unlocked_now, types::{OutputData, TransactionWithMetadata}, Wallet, WalletError, }, diff --git a/sdk/src/wallet/operations/output_consolidation.rs b/sdk/src/wallet/operations/output_consolidation.rs index b90263af44..b2ef3dd6ee 100644 --- a/sdk/src/wallet/operations/output_consolidation.rs +++ b/sdk/src/wallet/operations/output_consolidation.rs @@ -8,7 +8,14 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "ledger_nano")] use crate::client::secret::{ledger_nano::LedgerSecretManager, DowncastSecretManager}; use crate::{ - client::{api::PreparedTransactionData, secret::SecretManage, ClientError}, + client::{ + api::{ + options::{RemainderValueStrategy, TransactionOptions}, + PreparedTransactionData, + }, + secret::SecretManage, + ClientError, + }, types::block::{ address::{Address, Bech32Address}, input::INPUT_COUNT_MAX, @@ -17,9 +24,9 @@ use crate::{ }, wallet::{ constants::DEFAULT_OUTPUT_CONSOLIDATION_THRESHOLD, - operations::{helpers::time::can_output_be_unlocked_now, transaction::TransactionOptions}, + operations::helpers::time::can_output_be_unlocked_now, types::{OutputData, TransactionWithMetadata}, - RemainderValueStrategy, Wallet, WalletError, + Wallet, WalletError, }, }; diff --git a/sdk/src/wallet/operations/participation/mod.rs b/sdk/src/wallet/operations/participation/mod.rs index caec21e49d..5ebe35645f 100644 --- a/sdk/src/wallet/operations/participation/mod.rs +++ b/sdk/src/wallet/operations/participation/mod.rs @@ -6,7 +6,7 @@ // They become spendable again when the user reduces the “voting power”. // This is done by creating a special “voting output” that adheres to the following rules, NOT by sending to a different // address. -// If the user has designated funds to vote with, the resulting output MUST NOT be used for input selection. +// If the user has designated funds to vote with, the resulting output MUST NOT be used for building the transaction. // pub(crate) mod event; // pub(crate) mod voting; diff --git a/sdk/src/wallet/operations/transaction/input_selection.rs b/sdk/src/wallet/operations/transaction/build_transaction.rs similarity index 50% rename from sdk/src/wallet/operations/transaction/input_selection.rs rename to sdk/src/wallet/operations/transaction/build_transaction.rs index 44dfd3ceab..72df49b0e3 100644 --- a/sdk/src/wallet/operations/transaction/input_selection.rs +++ b/sdk/src/wallet/operations/transaction/build_transaction.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 use alloc::collections::BTreeSet; -use std::collections::HashMap; use crypto::keys::bip44::Bip44; @@ -10,7 +9,7 @@ use crypto::keys::bip44::Bip44; use crate::wallet::events::types::{TransactionProgressEvent, WalletEvent}; use crate::{ client::{ - api::{input_selection::InputSelection, transaction::validate_transaction_length, PreparedTransactionData}, + api::{options::TransactionOptions, PreparedTransactionData}, secret::{types::InputSigningData, SecretManage}, }, types::block::{ @@ -20,53 +19,36 @@ use crate::{ slot::SlotIndex, }, wallet::{ - operations::helpers::time::can_output_be_unlocked_forever_from_now_on, types::OutputData, - RemainderValueStrategy, TransactionOptions, Wallet, WalletError, + operations::helpers::time::can_output_be_unlocked_forever_from_now_on, types::OutputData, Wallet, WalletError, }, }; impl Wallet { - /// Selects inputs for a transaction. Locking of the inputs only happens in `submit_and_store_transaction()`, so - /// calling this multiple times before submitting can result in conflicting transactions. - /// If this gets problematic we could add a bool in the TransactionOptions to lock them here already. - pub(crate) async fn select_inputs( + /// Builds a transaction using the given outputs and options. + pub(crate) async fn build_transaction( &self, outputs: Vec, mut options: TransactionOptions, ) -> Result { - log::debug!("[TRANSACTION] select_inputs"); + log::debug!("[TRANSACTION] build_transaction"); // Voting output needs to be requested before to prevent a deadlock #[cfg(feature = "participation")] let voting_output = self.get_voting_output().await; let protocol_parameters = self.client().get_protocol_parameters().await?; - let creation_slot = self.client().get_slot_index().await?; + let slot_commitment_id = self.client().get_issuance().await?.latest_commitment.id(); if options.issuer_id.is_none() { options.issuer_id = self.ledger().await.first_account_id(); } - let reference_mana_cost = if let Some(issuer_id) = options.issuer_id { - Some( - self.client() - .get_account_congestion(&issuer_id, None) - .await? - .reference_mana_cost, - ) - } else { - None - }; - let remainder_address = match options.remainder_value_strategy { - RemainderValueStrategy::ReuseAddress => None, - RemainderValueStrategy::CustomAddress(address) => Some(address), - }; - - let wallet_ledger = self.ledger().await; #[cfg(feature = "events")] self.emit(WalletEvent::TransactionProgress( - TransactionProgressEvent::SelectingInputs, + TransactionProgressEvent::BuildingTransaction, )) .await; + let wallet_ledger = self.ledger().await; + #[allow(unused_mut)] let mut forbidden_inputs = wallet_ledger.locked_outputs.clone(); @@ -80,31 +62,18 @@ impl Wallet { // Filter inputs to not include inputs that require additional outputs for storage deposit return or could be // still locked. - let available_outputs_signing_data = filter_inputs( + let available_inputs = filter_inputs( &self.address().await, self.bip_path().await, - wallet_ledger.unspent_outputs.values(), + wallet_ledger + .unspent_outputs + .iter() + .filter_map(|(id, data)| (!forbidden_inputs.contains(id)).then_some(data)), slot_commitment_id.slot_index(), protocol_parameters.committable_age_range(), &options.required_inputs, )?; - let mut mana_rewards = HashMap::new(); - - if let Some(burn) = &options.burn { - for delegation_id in burn.delegations() { - if let Some(output) = wallet_ledger.unspent_delegation_output(delegation_id) { - mana_rewards.insert( - output.output_id, - self.client() - .get_output_mana_rewards(&output.output_id, slot_commitment_id.slot_index()) - .await? - .rewards, - ); - } - } - } - // Check that no input got already locked for output_id in &options.required_inputs { if wallet_ledger.locked_outputs.contains(output_id) { @@ -112,60 +81,24 @@ impl Wallet { "provided custom input {output_id} is already used in another transaction", ))); } - if let Some(input) = wallet_ledger.outputs.get(output_id) { - if input.output.can_claim_rewards(outputs.iter().find(|o| { - input - .output - .chain_id() - .map(|chain_id| chain_id.or_from_output_id(output_id)) - == o.chain_id() - })) { - mana_rewards.insert( - *output_id, - self.client() - .get_output_mana_rewards(output_id, slot_commitment_id.slot_index()) - .await? - .rewards, - ); - } - } - } - - let mut input_selection = InputSelection::new( - available_outputs_signing_data, - outputs, - Some(self.address().await.into_inner()), - creation_slot, - slot_commitment_id, - protocol_parameters.clone(), - ) - .with_required_inputs(options.required_inputs) - .with_forbidden_inputs(forbidden_inputs) - .with_context_inputs(options.context_inputs) - .with_mana_rewards(mana_rewards) - .with_payload(options.tagged_data_payload) - .with_mana_allotments(options.mana_allotments) - .with_remainder_address(remainder_address) - .with_burn(options.burn); - - if let (Some(account_id), Some(reference_mana_cost)) = (options.issuer_id, reference_mana_cost) { - input_selection = input_selection.with_min_mana_allotment(account_id, reference_mana_cost); - } - - if !options.allow_additional_input_selection { - input_selection = input_selection.disable_additional_input_selection(); } - let prepared_transaction_data = input_selection.select()?; - - validate_transaction_length(&prepared_transaction_data.transaction)?; - - Ok(prepared_transaction_data) + Ok(self + .client() + .build_transaction_inner( + [self.address().await.into_inner()], + available_inputs, + outputs, + options, + slot_commitment_id, + protocol_parameters, + ) + .await?) } } /// Filter available outputs to only include outputs that can be unlocked forever from this moment. -/// Note: this is only for the default input selection, it's still possible to send these outputs by using +/// Note: this is only for the default transaction builder, it's still possible to send these outputs by using /// `claim_outputs` or providing their OutputId's in the custom_inputs #[allow(clippy::too_many_arguments)] fn filter_inputs<'a>( diff --git a/sdk/src/wallet/operations/transaction/high_level/burning_melting/melt_native_token.rs b/sdk/src/wallet/operations/transaction/high_level/burning_melting/melt_native_token.rs index 9e1f6c55d1..e797c2b3bc 100644 --- a/sdk/src/wallet/operations/transaction/high_level/burning_melting/melt_native_token.rs +++ b/sdk/src/wallet/operations/transaction/high_level/burning_melting/melt_native_token.rs @@ -77,7 +77,7 @@ where token_scheme.maximum_supply(), )?)) .finish_output()?]; - // Input selection will detect that we're melting native tokens and add the required inputs if available + // Transaction builder will detect that we're melting native tokens and add the required inputs if available self.prepare_transaction(outputs, options).await } diff --git a/sdk/src/wallet/operations/transaction/high_level/burning_melting/mod.rs b/sdk/src/wallet/operations/transaction/high_level/burning_melting/mod.rs index 67a2435665..d3138fa946 100644 --- a/sdk/src/wallet/operations/transaction/high_level/burning_melting/mod.rs +++ b/sdk/src/wallet/operations/transaction/high_level/burning_melting/mod.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ - client::api::{input_selection::Burn, PreparedTransactionData}, + client::api::{transaction_builder::Burn, PreparedTransactionData}, wallet::{operations::transaction::TransactionOptions, types::TransactionWithMetadata, Wallet, WalletError}, }; @@ -40,7 +40,7 @@ impl Wallet { options.burn = Some(burn.into()); // The empty list of outputs is used. Outputs will be generated by - // the input selection algorithm based on the content of the [`Burn`] object. + // the transaction builder algorithm based on the content of the [`Burn`] object. self.prepare_transaction([], options).await } } diff --git a/sdk/src/wallet/operations/transaction/high_level/minting/create_native_token.rs b/sdk/src/wallet/operations/transaction/high_level/minting/create_native_token.rs index 6ea8afc11b..de8d012c87 100644 --- a/sdk/src/wallet/operations/transaction/high_level/minting/create_native_token.rs +++ b/sdk/src/wallet/operations/transaction/high_level/minting/create_native_token.rs @@ -120,28 +120,26 @@ where ); let token_id = TokenId::from(foundry_id); - let outputs = [ - { - let mut foundry_builder = FoundryOutputBuilder::new_with_minimum_amount( - storage_score_params, - account_output.foundry_counter() + 1, - TokenScheme::Simple(SimpleTokenScheme::new( - params.circulating_supply, - 0, - params.maximum_supply, - )?), - ) - .add_unlock_condition(ImmutableAccountAddressUnlockCondition::new(AccountAddress::from( - account_id, - ))); + let outputs = [{ + let mut foundry_builder = FoundryOutputBuilder::new_with_minimum_amount( + storage_score_params, + account_output.foundry_counter() + 1, + TokenScheme::Simple(SimpleTokenScheme::new( + params.circulating_supply, + 0, + params.maximum_supply, + )?), + ) + .add_unlock_condition(ImmutableAccountAddressUnlockCondition::new(AccountAddress::from( + account_id, + ))); - if let Some(foundry_metadata) = params.foundry_metadata { - foundry_builder = foundry_builder.add_immutable_feature(foundry_metadata); - } + if let Some(foundry_metadata) = params.foundry_metadata { + foundry_builder = foundry_builder.add_immutable_feature(foundry_metadata); + } - foundry_builder.finish_output()? - }, // Native Tokens will be added automatically in the remainder output in try_select_inputs() - ]; + foundry_builder.finish_output()? + }]; self.prepare_transaction(outputs, options) .await diff --git a/sdk/src/wallet/operations/transaction/high_level/minting/mint_native_token.rs b/sdk/src/wallet/operations/transaction/high_level/minting/mint_native_token.rs index c0a8ce4415..8925147c54 100644 --- a/sdk/src/wallet/operations/transaction/high_level/minting/mint_native_token.rs +++ b/sdk/src/wallet/operations/transaction/high_level/minting/mint_native_token.rs @@ -121,10 +121,7 @@ where let new_foundry_output_builder = FoundryOutputBuilder::from(foundry_output).with_token_scheme(updated_token_scheme); - let outputs = [ - new_foundry_output_builder.finish_output()?, - // Native Tokens will be added automatically in the remainder output in try_select_inputs() - ]; + let outputs = [new_foundry_output_builder.finish_output()?]; self.prepare_transaction(outputs, options).await } diff --git a/sdk/src/wallet/operations/transaction/high_level/staking/begin.rs b/sdk/src/wallet/operations/transaction/high_level/staking/begin.rs index 303a91b620..17249a69bf 100644 --- a/sdk/src/wallet/operations/transaction/high_level/staking/begin.rs +++ b/sdk/src/wallet/operations/transaction/high_level/staking/begin.rs @@ -4,13 +4,17 @@ use serde::{Deserialize, Serialize}; use crate::{ - client::{api::PreparedTransactionData, secret::SecretManage, ClientError}, + client::{ + api::{options::TransactionOptions, PreparedTransactionData}, + secret::SecretManage, + ClientError, + }, types::block::{ output::{feature::StakingFeature, AccountId, AccountOutputBuilder}, slot::EpochIndex, }, utils::serde::string, - wallet::{types::TransactionWithMetadata, TransactionOptions, Wallet, WalletError}, + wallet::{types::TransactionWithMetadata, Wallet, WalletError}, }; /// Parameters for beginning a staking period. diff --git a/sdk/src/wallet/operations/transaction/high_level/staking/end.rs b/sdk/src/wallet/operations/transaction/high_level/staking/end.rs index a2599d78fd..a5d2ea23df 100644 --- a/sdk/src/wallet/operations/transaction/high_level/staking/end.rs +++ b/sdk/src/wallet/operations/transaction/high_level/staking/end.rs @@ -2,9 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ - client::{api::PreparedTransactionData, secret::SecretManage, ClientError}, + client::{ + api::{options::TransactionOptions, PreparedTransactionData}, + secret::SecretManage, + ClientError, + }, types::block::output::{AccountId, AccountOutputBuilder}, - wallet::{types::TransactionWithMetadata, TransactionOptions, Wallet, WalletError}, + wallet::{types::TransactionWithMetadata, Wallet, WalletError}, }; impl Wallet diff --git a/sdk/src/wallet/operations/transaction/high_level/staking/extend.rs b/sdk/src/wallet/operations/transaction/high_level/staking/extend.rs index 582f68d16a..e1867a70d4 100644 --- a/sdk/src/wallet/operations/transaction/high_level/staking/extend.rs +++ b/sdk/src/wallet/operations/transaction/high_level/staking/extend.rs @@ -2,9 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ - client::{api::PreparedTransactionData, secret::SecretManage, ClientError}, + client::{ + api::{options::TransactionOptions, PreparedTransactionData}, + secret::SecretManage, + ClientError, + }, types::block::output::{feature::StakingFeature, AccountId, AccountOutputBuilder}, - wallet::{types::TransactionWithMetadata, TransactionOptions, Wallet, WalletError}, + wallet::{types::TransactionWithMetadata, Wallet, WalletError}, }; impl Wallet diff --git a/sdk/src/wallet/operations/transaction/mod.rs b/sdk/src/wallet/operations/transaction/mod.rs index ed38674a51..db261869f0 100644 --- a/sdk/src/wallet/operations/transaction/mod.rs +++ b/sdk/src/wallet/operations/transaction/mod.rs @@ -2,20 +2,18 @@ // SPDX-License-Identifier: Apache-2.0 pub(crate) mod account; +mod build_transaction; pub(crate) mod high_level; -mod input_selection; -mod options; pub(crate) mod prepare_output; mod prepare_transaction; mod sign_transaction; pub(crate) mod submit_transaction; -pub use self::options::{RemainderValueStrategy, TransactionOptions}; #[cfg(feature = "storage")] use crate::wallet::core::WalletLedgerDto; use crate::{ client::{ - api::{verify_semantic, PreparedTransactionData, SignedTransactionData}, + api::{options::TransactionOptions, PreparedTransactionData, SignedTransactionData}, secret::SecretManage, ClientError, }, @@ -121,12 +119,8 @@ where let options = options.into(); // Validate transaction before sending and storing it - if let Err(conflict) = verify_semantic( - &signed_transaction_data.inputs_data, - &signed_transaction_data.payload, - signed_transaction_data.mana_rewards, - self.client().get_protocol_parameters().await?, - ) { + if let Err(conflict) = signed_transaction_data.verify_semantic(&self.client().get_protocol_parameters().await?) + { log::debug!( "[TRANSACTION] conflict: {conflict:?} for {:?}", signed_transaction_data.payload diff --git a/sdk/src/wallet/operations/transaction/prepare_output.rs b/sdk/src/wallet/operations/transaction/prepare_output.rs index b394a61f58..21b7694461 100644 --- a/sdk/src/wallet/operations/transaction/prepare_output.rs +++ b/sdk/src/wallet/operations/transaction/prepare_output.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use crate::{ - client::{secret::SecretManage, ClientError}, + client::{api::options::RemainderValueStrategy, secret::SecretManage, ClientError}, types::block::{ address::{Address, Bech32Address, Ed25519Address}, output::{ @@ -20,11 +20,7 @@ use crate::{ BlockError, }, utils::serde::string, - wallet::{ - operations::transaction::{RemainderValueStrategy, TransactionOptions}, - types::OutputData, - Wallet, WalletError, - }, + wallet::{operations::transaction::TransactionOptions, types::OutputData, Wallet, WalletError}, }; impl Wallet { diff --git a/sdk/src/wallet/operations/transaction/prepare_transaction.rs b/sdk/src/wallet/operations/transaction/prepare_transaction.rs index 467f992ed7..e91a7d9144 100644 --- a/sdk/src/wallet/operations/transaction/prepare_transaction.rs +++ b/sdk/src/wallet/operations/transaction/prepare_transaction.rs @@ -34,7 +34,7 @@ impl Wallet { )))?; } - let prepared_transaction_data = self.select_inputs(outputs, options).await?; + let prepared_transaction_data = self.build_transaction(outputs, options).await?; log::debug!( "[TRANSACTION] finished prepare_transaction in {:.2?}", diff --git a/sdk/src/wallet/operations/transaction/sign_transaction.rs b/sdk/src/wallet/operations/transaction/sign_transaction.rs index c0a224fbd5..86f9bddee1 100644 --- a/sdk/src/wallet/operations/transaction/sign_transaction.rs +++ b/sdk/src/wallet/operations/transaction/sign_transaction.rs @@ -14,9 +14,7 @@ use { use crate::wallet::events::types::{TransactionProgressEvent, WalletEvent}; use crate::{ client::{ - api::{ - transaction::validate_signed_transaction_payload_length, PreparedTransactionData, SignedTransactionData, - }, + api::{PreparedTransactionData, SignedTransactionData}, secret::SecretManage, ClientError, }, @@ -86,7 +84,7 @@ where log::debug!("[TRANSACTION] signed transaction: {:?}", payload); - validate_signed_transaction_payload_length(&payload)?; + payload.validate_length()?; Ok(SignedTransactionData { payload, diff --git a/sdk/tests/client/error.rs b/sdk/tests/client/error.rs index 7b14201548..54cbda14db 100644 --- a/sdk/tests/client/error.rs +++ b/sdk/tests/client/error.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use iota_sdk::{ - client::{api::input_selection::Error as IsaError, ClientError}, + client::{api::transaction_builder::TransactionBuilderError, ClientError}, types::block::BlockError, }; use pretty_assertions::assert_eq; @@ -30,18 +30,18 @@ fn stringified_error() { "{\"type\":\"placeholderSecretManager\",\"error\":\"placeholderSecretManager can't be used for address generation or signing\"}" ); - let error = ClientError::InputSelection(IsaError::InsufficientAmount { + let error = ClientError::TransactionBuilder(TransactionBuilderError::InsufficientAmount { found: 0, required: 100, }); assert_eq!( &serde_json::to_string(&error).unwrap(), - "{\"type\":\"inputSelection\",\"error\":\"insufficient amount: found 0, required 100\"}" + "{\"type\":\"transactionBuilder\",\"error\":\"insufficient amount: found 0, required 100\"}" ); - let error = ClientError::InputSelection(IsaError::Block(BlockError::UnsupportedAddressKind(6))); + let error = ClientError::TransactionBuilder(TransactionBuilderError::Block(BlockError::UnsupportedAddressKind(6))); assert_eq!( &serde_json::to_string(&error).unwrap(), - "{\"type\":\"inputSelection\",\"error\":\"unsupported address kind: 6\"}" + "{\"type\":\"transactionBuilder\",\"error\":\"unsupported address kind: 6\"}" ); } diff --git a/sdk/tests/client/mod.rs b/sdk/tests/client/mod.rs index 87346a8f3a..fc72b404f5 100644 --- a/sdk/tests/client/mod.rs +++ b/sdk/tests/client/mod.rs @@ -6,7 +6,6 @@ mod client_builder; mod common; mod error; mod high_level; -mod input_selection; mod input_signing_data; mod mnemonic; #[cfg(feature = "mqtt")] @@ -14,6 +13,7 @@ mod mqtt; mod node_api; mod secret_manager; mod signing; +mod transaction_builder; use std::{collections::HashMap, hash::Hash, str::FromStr}; diff --git a/sdk/tests/client/signing/account.rs b/sdk/tests/client/signing/account.rs index 37aebc578e..37c8757b27 100644 --- a/sdk/tests/client/signing/account.rs +++ b/sdk/tests/client/signing/account.rs @@ -6,10 +6,7 @@ use std::str::FromStr; use crypto::keys::bip44::Bip44; use iota_sdk::{ client::{ - api::{ - transaction::validate_signed_transaction_payload_length, verify_semantic, GetAddressesOptions, - PreparedTransactionData, - }, + api::{GetAddressesOptions, PreparedTransactionData}, constants::SHIMMER_COIN_TYPE, secret::{SecretManage, SecretManager}, Client, @@ -49,7 +46,7 @@ async fn sign_account_state_transition() -> Result<(), Box Result<(), Box Result<(), Box Result<(), Box> { .clone() .into_inner(); - let protocol_parameters = iota_mainnet_protocol_parameters().clone(); + let protocol_parameters = iota_mainnet_protocol_parameters(); let account_id = AccountId::from_str(ACCOUNT_ID_1)?; let account_address = Address::Account(AccountAddress::new(account_id)); let slot_index = SlotIndex::from(10); @@ -203,7 +195,7 @@ async fn account_reference_unlocks() -> Result<(), Box> { .with_outputs(outputs) .with_creation_slot(slot_index + 1) .with_capabilities([TransactionCapabilityFlag::BurnMana]) - .finish_with_params(&protocol_parameters)?; + .finish_with_params(protocol_parameters)?; let prepared_transaction_data = PreparedTransactionData { transaction, @@ -233,14 +225,9 @@ async fn account_reference_unlocks() -> Result<(), Box> { let tx_payload = SignedTransactionPayload::new(prepared_transaction_data.transaction.clone(), unlocks)?; - validate_signed_transaction_payload_length(&tx_payload)?; + tx_payload.validate_length()?; - verify_semantic( - &prepared_transaction_data.inputs_data, - &tx_payload, - prepared_transaction_data.mana_rewards, - protocol_parameters, - )?; + prepared_transaction_data.verify_semantic(protocol_parameters)?; Ok(()) } diff --git a/sdk/tests/client/signing/basic.rs b/sdk/tests/client/signing/basic.rs index 04093f6455..b66af9641f 100644 --- a/sdk/tests/client/signing/basic.rs +++ b/sdk/tests/client/signing/basic.rs @@ -4,10 +4,7 @@ use crypto::keys::bip44::Bip44; use iota_sdk::{ client::{ - api::{ - transaction::validate_signed_transaction_payload_length, verify_semantic, GetAddressesOptions, - PreparedTransactionData, - }, + api::{GetAddressesOptions, PreparedTransactionData}, constants::SHIMMER_COIN_TYPE, secret::{SecretManage, SecretManager}, Client, @@ -41,7 +38,7 @@ async fn single_ed25519_unlock() -> Result<(), Box> { .clone() .into_inner(); - let protocol_parameters = iota_mainnet_protocol_parameters().clone(); + let protocol_parameters = iota_mainnet_protocol_parameters(); let slot_index = SlotIndex::from(10); let inputs = build_inputs( @@ -80,7 +77,7 @@ async fn single_ed25519_unlock() -> Result<(), Box> { .with_outputs(outputs) .with_creation_slot(slot_index + 1) .with_capabilities([TransactionCapabilityFlag::BurnMana]) - .finish_with_params(&protocol_parameters)?; + .finish_with_params(protocol_parameters)?; let prepared_transaction_data = PreparedTransactionData { transaction, @@ -98,14 +95,9 @@ async fn single_ed25519_unlock() -> Result<(), Box> { let tx_payload = SignedTransactionPayload::new(prepared_transaction_data.transaction.clone(), unlocks)?; - validate_signed_transaction_payload_length(&tx_payload)?; + tx_payload.validate_length()?; - verify_semantic( - &prepared_transaction_data.inputs_data, - &tx_payload, - prepared_transaction_data.mana_rewards, - protocol_parameters, - )?; + prepared_transaction_data.verify_semantic(protocol_parameters)?; Ok(()) } @@ -124,7 +116,7 @@ async fn ed25519_reference_unlocks() -> Result<(), Box> { .clone() .into_inner(); - let protocol_parameters = iota_mainnet_protocol_parameters().clone(); + let protocol_parameters = iota_mainnet_protocol_parameters(); let slot_index = SlotIndex::from(10); let inputs = build_inputs( @@ -189,7 +181,7 @@ async fn ed25519_reference_unlocks() -> Result<(), Box> { .with_outputs(outputs) .with_creation_slot(slot_index + 1) .with_capabilities([TransactionCapabilityFlag::BurnMana]) - .finish_with_params(&protocol_parameters)?; + .finish_with_params(protocol_parameters)?; let prepared_transaction_data = PreparedTransactionData { transaction, @@ -219,14 +211,9 @@ async fn ed25519_reference_unlocks() -> Result<(), Box> { let tx_payload = SignedTransactionPayload::new(prepared_transaction_data.transaction.clone(), unlocks)?; - validate_signed_transaction_payload_length(&tx_payload)?; + tx_payload.validate_length()?; - verify_semantic( - &prepared_transaction_data.inputs_data, - &tx_payload, - prepared_transaction_data.mana_rewards, - protocol_parameters, - )?; + prepared_transaction_data.verify_semantic(protocol_parameters)?; Ok(()) } @@ -254,7 +241,7 @@ async fn two_signature_unlocks() -> Result<(), Box> { .clone() .into_inner(); - let protocol_parameters = iota_mainnet_protocol_parameters().clone(); + let protocol_parameters = iota_mainnet_protocol_parameters(); let slot_index = SlotIndex::from(10); let inputs = build_inputs( @@ -307,7 +294,7 @@ async fn two_signature_unlocks() -> Result<(), Box> { .with_outputs(outputs) .with_creation_slot(slot_index + 1) .with_capabilities([TransactionCapabilityFlag::BurnMana]) - .finish_with_params(&protocol_parameters)?; + .finish_with_params(protocol_parameters)?; let prepared_transaction_data = PreparedTransactionData { transaction, @@ -326,14 +313,9 @@ async fn two_signature_unlocks() -> Result<(), Box> { let tx_payload = SignedTransactionPayload::new(prepared_transaction_data.transaction.clone(), unlocks)?; - validate_signed_transaction_payload_length(&tx_payload)?; + tx_payload.validate_length()?; - verify_semantic( - &prepared_transaction_data.inputs_data, - &tx_payload, - prepared_transaction_data.mana_rewards, - protocol_parameters, - )?; + prepared_transaction_data.verify_semantic(protocol_parameters)?; Ok(()) } diff --git a/sdk/tests/client/signing/delegation.rs b/sdk/tests/client/signing/delegation.rs index 1a44d93a26..12646b9b6c 100644 --- a/sdk/tests/client/signing/delegation.rs +++ b/sdk/tests/client/signing/delegation.rs @@ -6,10 +6,7 @@ use std::collections::BTreeMap; use crypto::keys::bip44::Bip44; use iota_sdk::{ client::{ - api::{ - transaction::validate_signed_transaction_payload_length, verify_semantic, GetAddressesOptions, - PreparedTransactionData, - }, + api::{GetAddressesOptions, PreparedTransactionData}, constants::SHIMMER_COIN_TYPE, secret::{SecretManage, SecretManager}, Client, @@ -49,7 +46,7 @@ async fn valid_creation() -> Result<(), Box> { .clone() .into_inner(); - let protocol_parameters = iota_mainnet_protocol_parameters().clone(); + let protocol_parameters = iota_mainnet_protocol_parameters(); let slot_commitment_id = rand_slot_commitment_id(); let slot_index = slot_commitment_id.slot_index(); @@ -90,7 +87,7 @@ async fn valid_creation() -> Result<(), Box> { .with_creation_slot(slot_index + 1) .with_context_inputs([CommitmentContextInput::new(slot_commitment_id).into()]) .with_capabilities([TransactionCapabilityFlag::BurnMana]) - .finish_with_params(&protocol_parameters)?; + .finish_with_params(protocol_parameters)?; let prepared_transaction_data = PreparedTransactionData { transaction, @@ -108,14 +105,9 @@ async fn valid_creation() -> Result<(), Box> { let tx_payload = SignedTransactionPayload::new(prepared_transaction_data.transaction.clone(), unlocks)?; - validate_signed_transaction_payload_length(&tx_payload)?; + tx_payload.validate_length()?; - verify_semantic( - &prepared_transaction_data.inputs_data, - &tx_payload, - prepared_transaction_data.mana_rewards, - protocol_parameters, - )?; + prepared_transaction_data.verify_semantic(protocol_parameters)?; Ok(()) } @@ -134,7 +126,7 @@ async fn creation_missing_commitment_input() -> Result<(), Box Result<(), Box Result<(), Box Result<(), Box> { .clone() .into_inner(); - let protocol_parameters = iota_mainnet_protocol_parameters().clone(); + let protocol_parameters = iota_mainnet_protocol_parameters(); let slot_commitment_id = rand_slot_commitment_id(); let slot_index = slot_commitment_id.slot_index(); @@ -263,7 +250,7 @@ async fn non_null_id_creation() -> Result<(), Box> { .with_outputs(outputs) .with_creation_slot(slot_index + 1) .with_capabilities([TransactionCapabilityFlag::BurnMana]) - .finish_with_params(&protocol_parameters)?; + .finish_with_params(protocol_parameters)?; let prepared_transaction_data = PreparedTransactionData { transaction, @@ -281,14 +268,9 @@ async fn non_null_id_creation() -> Result<(), Box> { let tx_payload = SignedTransactionPayload::new(prepared_transaction_data.transaction.clone(), unlocks)?; - validate_signed_transaction_payload_length(&tx_payload)?; + tx_payload.validate_length()?; - let conflict = verify_semantic( - &prepared_transaction_data.inputs_data, - &tx_payload, - prepared_transaction_data.mana_rewards, - protocol_parameters, - ); + let conflict = prepared_transaction_data.verify_semantic(protocol_parameters); assert_eq!(conflict, Err(TransactionFailureReason::NewChainOutputHasNonZeroedId)); @@ -309,7 +291,7 @@ async fn mismatch_amount_creation() -> Result<(), Box> { .clone() .into_inner(); - let protocol_parameters = iota_mainnet_protocol_parameters().clone(); + let protocol_parameters = iota_mainnet_protocol_parameters(); let slot_commitment_id = rand_slot_commitment_id(); let slot_index = slot_commitment_id.slot_index(); @@ -349,7 +331,7 @@ async fn mismatch_amount_creation() -> Result<(), Box> { .with_outputs(outputs) .with_creation_slot(slot_index + 1) .with_capabilities([TransactionCapabilityFlag::BurnMana]) - .finish_with_params(&protocol_parameters)?; + .finish_with_params(protocol_parameters)?; let prepared_transaction_data = PreparedTransactionData { transaction, @@ -367,14 +349,9 @@ async fn mismatch_amount_creation() -> Result<(), Box> { let tx_payload = SignedTransactionPayload::new(prepared_transaction_data.transaction.clone(), unlocks)?; - validate_signed_transaction_payload_length(&tx_payload)?; + tx_payload.validate_length()?; - let conflict = verify_semantic( - &prepared_transaction_data.inputs_data, - &tx_payload, - prepared_transaction_data.mana_rewards, - protocol_parameters, - ); + let conflict = prepared_transaction_data.verify_semantic(protocol_parameters); assert_eq!(conflict, Err(TransactionFailureReason::DelegationAmountMismatch)); @@ -395,7 +372,7 @@ async fn non_zero_end_epoch_creation() -> Result<(), Box> .clone() .into_inner(); - let protocol_parameters = iota_mainnet_protocol_parameters().clone(); + let protocol_parameters = iota_mainnet_protocol_parameters(); let slot_commitment_id = rand_slot_commitment_id(); let slot_index = slot_commitment_id.slot_index(); @@ -435,7 +412,7 @@ async fn non_zero_end_epoch_creation() -> Result<(), Box> .with_outputs(outputs) .with_creation_slot(slot_index + 1) .with_capabilities([TransactionCapabilityFlag::BurnMana]) - .finish_with_params(&protocol_parameters)?; + .finish_with_params(protocol_parameters)?; let prepared_transaction_data = PreparedTransactionData { transaction, @@ -453,14 +430,9 @@ async fn non_zero_end_epoch_creation() -> Result<(), Box> let tx_payload = SignedTransactionPayload::new(prepared_transaction_data.transaction.clone(), unlocks)?; - validate_signed_transaction_payload_length(&tx_payload)?; + tx_payload.validate_length()?; - let conflict = verify_semantic( - &prepared_transaction_data.inputs_data, - &tx_payload, - prepared_transaction_data.mana_rewards, - protocol_parameters, - ); + let conflict = prepared_transaction_data.verify_semantic(protocol_parameters); assert_eq!(conflict, Err(TransactionFailureReason::DelegationEndEpochNotZero)); @@ -481,7 +453,7 @@ async fn invalid_start_epoch_creation() -> Result<(), Box .clone() .into_inner(); - let protocol_parameters = iota_mainnet_protocol_parameters().clone(); + let protocol_parameters = iota_mainnet_protocol_parameters(); let slot_commitment_id = rand_slot_commitment_id(); let slot_index = slot_commitment_id.slot_index(); @@ -522,7 +494,7 @@ async fn invalid_start_epoch_creation() -> Result<(), Box .with_creation_slot(slot_index + 1) .with_capabilities([TransactionCapabilityFlag::BurnMana]) .with_context_inputs([CommitmentContextInput::new(slot_commitment_id).into()]) - .finish_with_params(&protocol_parameters)?; + .finish_with_params(protocol_parameters)?; let prepared_transaction_data = PreparedTransactionData { transaction, @@ -540,14 +512,9 @@ async fn invalid_start_epoch_creation() -> Result<(), Box let tx_payload = SignedTransactionPayload::new(prepared_transaction_data.transaction.clone(), unlocks)?; - validate_signed_transaction_payload_length(&tx_payload)?; + tx_payload.validate_length()?; - let conflict = verify_semantic( - &prepared_transaction_data.inputs_data, - &tx_payload, - prepared_transaction_data.mana_rewards, - protocol_parameters, - ); + let conflict = prepared_transaction_data.verify_semantic(protocol_parameters); assert_eq!(conflict, Err(TransactionFailureReason::DelegationStartEpochInvalid)); @@ -568,7 +535,7 @@ async fn delay_not_null_id() -> Result<(), Box> { .clone() .into_inner(); - let protocol_parameters = iota_mainnet_protocol_parameters().clone(); + let protocol_parameters = iota_mainnet_protocol_parameters(); let slot_commitment_id_1 = rand_slot_commitment_id(); let slot_index_1 = slot_commitment_id_1.slot_index(); let slot_commitment_id_2 = rand_slot_commitment_id() @@ -620,7 +587,7 @@ async fn delay_not_null_id() -> Result<(), Box> { RewardContextInput::new(0)?.into(), ]) .with_capabilities([TransactionCapabilityFlag::BurnMana]) - .finish_with_params(&protocol_parameters)?; + .finish_with_params(protocol_parameters)?; let prepared_transaction_data = PreparedTransactionData { transaction, @@ -638,14 +605,9 @@ async fn delay_not_null_id() -> Result<(), Box> { let tx_payload = SignedTransactionPayload::new(prepared_transaction_data.transaction.clone(), unlocks)?; - validate_signed_transaction_payload_length(&tx_payload)?; + tx_payload.validate_length()?; - let conflict = verify_semantic( - &prepared_transaction_data.inputs_data, - &tx_payload, - prepared_transaction_data.mana_rewards, - protocol_parameters, - ); + let conflict = prepared_transaction_data.verify_semantic(protocol_parameters); assert_eq!( conflict, @@ -669,7 +631,7 @@ async fn delay_modified_amount() -> Result<(), Box> { .clone() .into_inner(); - let protocol_parameters = iota_mainnet_protocol_parameters().clone(); + let protocol_parameters = iota_mainnet_protocol_parameters(); let slot_commitment_id_1 = rand_slot_commitment_id(); let slot_index_1 = slot_commitment_id_1.slot_index(); let slot_commitment_id_2 = rand_slot_commitment_id() @@ -721,7 +683,7 @@ async fn delay_modified_amount() -> Result<(), Box> { RewardContextInput::new(0)?.into(), ]) .with_capabilities([TransactionCapabilityFlag::BurnMana]) - .finish_with_params(&protocol_parameters)?; + .finish_with_params(protocol_parameters)?; let prepared_transaction_data = PreparedTransactionData { transaction, @@ -739,14 +701,9 @@ async fn delay_modified_amount() -> Result<(), Box> { let tx_payload = SignedTransactionPayload::new(prepared_transaction_data.transaction.clone(), unlocks)?; - validate_signed_transaction_payload_length(&tx_payload)?; + tx_payload.validate_length()?; - let conflict = verify_semantic( - &prepared_transaction_data.inputs_data, - &tx_payload, - prepared_transaction_data.mana_rewards, - protocol_parameters, - ); + let conflict = prepared_transaction_data.verify_semantic(protocol_parameters); assert_eq!(conflict, Err(TransactionFailureReason::DelegationModified)); @@ -767,7 +724,7 @@ async fn delay_modified_validator() -> Result<(), Box> { .clone() .into_inner(); - let protocol_parameters = iota_mainnet_protocol_parameters().clone(); + let protocol_parameters = iota_mainnet_protocol_parameters(); let slot_commitment_id_1 = rand_slot_commitment_id(); let slot_index_1 = slot_commitment_id_1.slot_index(); let slot_commitment_id_2 = rand_slot_commitment_id() @@ -819,7 +776,7 @@ async fn delay_modified_validator() -> Result<(), Box> { RewardContextInput::new(0)?.into(), ]) .with_capabilities([TransactionCapabilityFlag::BurnMana]) - .finish_with_params(&protocol_parameters)?; + .finish_with_params(protocol_parameters)?; let prepared_transaction_data = PreparedTransactionData { transaction, @@ -837,14 +794,9 @@ async fn delay_modified_validator() -> Result<(), Box> { let tx_payload = SignedTransactionPayload::new(prepared_transaction_data.transaction.clone(), unlocks)?; - validate_signed_transaction_payload_length(&tx_payload)?; + tx_payload.validate_length()?; - let conflict = verify_semantic( - &prepared_transaction_data.inputs_data, - &tx_payload, - prepared_transaction_data.mana_rewards, - protocol_parameters, - ); + let conflict = prepared_transaction_data.verify_semantic(protocol_parameters); assert_eq!(conflict, Err(TransactionFailureReason::DelegationModified)); @@ -865,7 +817,7 @@ async fn delay_modified_start_epoch() -> Result<(), Box> .clone() .into_inner(); - let protocol_parameters = iota_mainnet_protocol_parameters().clone(); + let protocol_parameters = iota_mainnet_protocol_parameters(); let slot_commitment_id_1 = rand_slot_commitment_id(); let slot_index_1 = slot_commitment_id_1.slot_index(); let slot_commitment_id_2 = rand_slot_commitment_id() @@ -917,7 +869,7 @@ async fn delay_modified_start_epoch() -> Result<(), Box> RewardContextInput::new(0)?.into(), ]) .with_capabilities([TransactionCapabilityFlag::BurnMana]) - .finish_with_params(&protocol_parameters)?; + .finish_with_params(protocol_parameters)?; let prepared_transaction_data = PreparedTransactionData { transaction, @@ -935,14 +887,9 @@ async fn delay_modified_start_epoch() -> Result<(), Box> let tx_payload = SignedTransactionPayload::new(prepared_transaction_data.transaction.clone(), unlocks)?; - validate_signed_transaction_payload_length(&tx_payload)?; + tx_payload.validate_length()?; - let conflict = verify_semantic( - &prepared_transaction_data.inputs_data, - &tx_payload, - prepared_transaction_data.mana_rewards, - protocol_parameters, - ); + let conflict = prepared_transaction_data.verify_semantic(protocol_parameters); assert_eq!(conflict, Err(TransactionFailureReason::DelegationModified)); @@ -963,7 +910,7 @@ async fn delay_pre_registration_slot_end_epoch() -> Result<(), Box Result<(), Box Result<(), Box Result<(), Box> { .clone() .into_inner(); - let protocol_parameters = iota_mainnet_protocol_parameters().clone(); + let protocol_parameters = iota_mainnet_protocol_parameters(); let slot_commitment_id_1 = rand_slot_commitment_id(); let slot_index_1 = slot_commitment_id_1.slot_index(); let slot_commitment_id_2 = rand_slot_commitment_id() @@ -1111,7 +1053,7 @@ async fn destroy_null_id() -> Result<(), Box> { RewardContextInput::new(0)?.into(), ]) .with_capabilities([TransactionCapabilityFlag::BurnMana]) - .finish_with_params(&protocol_parameters)?; + .finish_with_params(protocol_parameters)?; let mut mana_rewards = BTreeMap::default(); mana_rewards.insert(*inputs[0].output_id(), 0); @@ -1132,14 +1074,9 @@ async fn destroy_null_id() -> Result<(), Box> { let tx_payload = SignedTransactionPayload::new(prepared_transaction_data.transaction.clone(), unlocks)?; - validate_signed_transaction_payload_length(&tx_payload)?; + tx_payload.validate_length()?; - verify_semantic( - &prepared_transaction_data.inputs_data, - &tx_payload, - prepared_transaction_data.mana_rewards, - protocol_parameters, - )?; + prepared_transaction_data.verify_semantic(protocol_parameters)?; Ok(()) } @@ -1158,7 +1095,7 @@ async fn destroy_reward_missing() -> Result<(), Box> { .clone() .into_inner(); - let protocol_parameters = iota_mainnet_protocol_parameters().clone(); + let protocol_parameters = iota_mainnet_protocol_parameters(); let slot_commitment_id_1 = rand_slot_commitment_id(); let slot_index_1 = slot_commitment_id_1.slot_index(); let slot_commitment_id_2 = rand_slot_commitment_id() @@ -1205,7 +1142,7 @@ async fn destroy_reward_missing() -> Result<(), Box> { .with_creation_slot(slot_index_2 + 1) .with_context_inputs([CommitmentContextInput::new(slot_commitment_id_2).into()]) .with_capabilities([TransactionCapabilityFlag::BurnMana]) - .finish_with_params(&protocol_parameters)?; + .finish_with_params(protocol_parameters)?; let prepared_transaction_data = PreparedTransactionData { transaction, @@ -1223,14 +1160,9 @@ async fn destroy_reward_missing() -> Result<(), Box> { let tx_payload = SignedTransactionPayload::new(prepared_transaction_data.transaction.clone(), unlocks)?; - validate_signed_transaction_payload_length(&tx_payload)?; + tx_payload.validate_length()?; - let conflict = verify_semantic( - &prepared_transaction_data.inputs_data, - &tx_payload, - prepared_transaction_data.mana_rewards, - protocol_parameters, - ); + let conflict = prepared_transaction_data.verify_semantic(protocol_parameters); assert_eq!(conflict, Err(TransactionFailureReason::DelegationRewardInputMissing)); diff --git a/sdk/tests/client/signing/mod.rs b/sdk/tests/client/signing/mod.rs index 4f01bd34bf..258fda7eea 100644 --- a/sdk/tests/client/signing/mod.rs +++ b/sdk/tests/client/signing/mod.rs @@ -11,10 +11,7 @@ use std::str::FromStr; use crypto::keys::bip44::Bip44; use iota_sdk::{ client::{ - api::{ - input_selection::InputSelection, transaction::validate_signed_transaction_payload_length, verify_semantic, - GetAddressesOptions, PreparedTransactionData, - }, + api::{transaction_builder::TransactionBuilder, GetAddressesOptions, PreparedTransactionData}, constants::SHIMMER_COIN_TYPE, secret::{SecretManage, SecretManager}, }, @@ -44,7 +41,7 @@ async fn all_combined() -> Result<(), Box> { "mirror add nothing long orphan hat this rough scare gallery fork twelve old shrug voyage job table obscure mimic holiday possible proud giraffe fan".to_owned(), )?; - let protocol_parameters = iota_mainnet_protocol_parameters().clone(); + let protocol_parameters = iota_mainnet_protocol_parameters(); let ed25519_bech32_addresses = secret_manager .generate_ed25519_addresses( @@ -367,7 +364,7 @@ async fn all_combined() -> Result<(), Box> { }, ]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [ed25519_0, ed25519_1, ed25519_2], @@ -375,7 +372,7 @@ async fn all_combined() -> Result<(), Box> { slot_commitment_id, protocol_parameters.clone(), ) - .select() + .finish() .unwrap(); let transaction = Transaction::builder(protocol_parameters.network_id()) @@ -391,7 +388,7 @@ async fn all_combined() -> Result<(), Box> { ) .with_outputs(outputs) .with_creation_slot(slot_index) - .finish_with_params(&protocol_parameters)?; + .finish_with_params(protocol_parameters)?; let prepared_transaction_data = PreparedTransactionData { transaction, @@ -484,14 +481,9 @@ async fn all_combined() -> Result<(), Box> { let tx_payload = SignedTransactionPayload::new(prepared_transaction_data.transaction.clone(), unlocks)?; - validate_signed_transaction_payload_length(&tx_payload)?; + tx_payload.validate_length()?; - verify_semantic( - &prepared_transaction_data.inputs_data, - &tx_payload, - prepared_transaction_data.mana_rewards, - protocol_parameters, - )?; + prepared_transaction_data.verify_semantic(protocol_parameters)?; Ok(()) } diff --git a/sdk/tests/client/signing/nft.rs b/sdk/tests/client/signing/nft.rs index c714c178d4..bb0f1318a0 100644 --- a/sdk/tests/client/signing/nft.rs +++ b/sdk/tests/client/signing/nft.rs @@ -6,10 +6,7 @@ use std::str::FromStr; use crypto::keys::bip44::Bip44; use iota_sdk::{ client::{ - api::{ - transaction::validate_signed_transaction_payload_length, verify_semantic, GetAddressesOptions, - PreparedTransactionData, - }, + api::{GetAddressesOptions, PreparedTransactionData}, constants::SHIMMER_COIN_TYPE, secret::{SecretManage, SecretManager}, Client, @@ -49,7 +46,7 @@ async fn nft_reference_unlocks() -> Result<(), Box> { .clone() .into_inner(); - let protocol_parameters = iota_mainnet_protocol_parameters().clone(); + let protocol_parameters = iota_mainnet_protocol_parameters(); let nft_id = NftId::from_str(NFT_ID_1)?; let nft_address = Address::Nft(NftAddress::new(nft_id)); let slot_index = SlotIndex::from(10); @@ -127,7 +124,7 @@ async fn nft_reference_unlocks() -> Result<(), Box> { .with_outputs(outputs) .with_creation_slot(slot_index + 1) .with_capabilities([TransactionCapabilityFlag::BurnMana]) - .finish_with_params(&protocol_parameters)?; + .finish_with_params(protocol_parameters)?; let prepared_transaction_data = PreparedTransactionData { transaction, @@ -157,14 +154,9 @@ async fn nft_reference_unlocks() -> Result<(), Box> { let tx_payload = SignedTransactionPayload::new(prepared_transaction_data.transaction.clone(), unlocks)?; - validate_signed_transaction_payload_length(&tx_payload)?; + tx_payload.validate_length()?; - verify_semantic( - &prepared_transaction_data.inputs_data, - &tx_payload, - prepared_transaction_data.mana_rewards, - protocol_parameters, - )?; + prepared_transaction_data.verify_semantic(protocol_parameters)?; Ok(()) } diff --git a/sdk/tests/client/input_selection/account_outputs.rs b/sdk/tests/client/transaction_builder/account_outputs.rs similarity index 93% rename from sdk/tests/client/input_selection/account_outputs.rs rename to sdk/tests/client/transaction_builder/account_outputs.rs index cb01629154..c77abfbc2b 100644 --- a/sdk/tests/client/input_selection/account_outputs.rs +++ b/sdk/tests/client/transaction_builder/account_outputs.rs @@ -5,14 +5,16 @@ use std::str::FromStr; use iota_sdk::{ client::{ - api::input_selection::{Burn, Error, InputSelection, Requirement}, + api::transaction_builder::{Burn, Requirement, TransactionBuilder, TransactionBuilderError}, secret::types::InputSigningData, }, types::block::{ address::{Address, ImplicitAccountCreationAddress}, mana::ManaAllotment, output::{ - unlock_condition::AddressUnlockCondition, AccountId, AccountOutputBuilder, BasicOutputBuilder, Output, + feature::{BlockIssuerFeature, BlockIssuerKeys, Ed25519PublicKeyHashBlockIssuerKey}, + unlock_condition::AddressUnlockCondition, + AccountId, AccountOutputBuilder, BasicOutputBuilder, Output, }, payload::signed_transaction::{TransactionCapabilities, TransactionCapabilityFlag}, protocol::iota_mainnet_protocol_parameters, @@ -54,7 +56,7 @@ fn input_account_eq_output_account() { issuer: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -62,7 +64,7 @@ fn input_account_eq_output_account() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -96,7 +98,7 @@ fn transition_account_id_zero() { issuer: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -104,7 +106,7 @@ fn transition_account_id_zero() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -138,7 +140,7 @@ fn transition_account_id_zero() { // None, // )]); -// let selected = InputSelection::new( +// let selected = TransactionBuilder::new( // inputs, // outputs, // [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -186,7 +188,7 @@ fn transition_account_id_zero() { // None, // )]); -// let selected = InputSelection::new( +// let selected = TransactionBuilder::new( // inputs, // outputs, // [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -231,7 +233,7 @@ fn transition_account_id_zero() { // None, // )]); -// let selected = InputSelection::new( +// let selected = TransactionBuilder::new( // inputs.clone(), // outputs, // [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -273,7 +275,7 @@ fn create_account() { issuer: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -281,7 +283,7 @@ fn create_account() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -325,7 +327,7 @@ fn burn_account() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -334,7 +336,7 @@ fn burn_account() { protocol_parameters, ) .with_burn(Burn::new().add_account(account_id_2)) - .select() + .finish() .unwrap(); assert_eq!( @@ -373,7 +375,7 @@ fn burn_account() { // None, // )]); -// let selected = InputSelection::new( +// let selected = TransactionBuilder::new( // inputs, // outputs, // [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -418,7 +420,7 @@ fn missing_input_for_account_output() { issuer: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -426,11 +428,11 @@ fn missing_input_for_account_output() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::UnfulfillableRequirement(Requirement::Account(account_id))) if account_id == account_id_2 + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Account(account_id))) if account_id == account_id_2 )); } @@ -475,7 +477,7 @@ fn missing_input_for_account_output_2() { issuer: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -483,11 +485,11 @@ fn missing_input_for_account_output_2() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::UnfulfillableRequirement(Requirement::Account(account_id))) if account_id == account_id_2 + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Account(account_id))) if account_id == account_id_2 )); } @@ -519,7 +521,7 @@ fn missing_input_for_account_output_but_created() { issuer: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -527,7 +529,7 @@ fn missing_input_for_account_output_but_created() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(selected.is_ok()); } @@ -578,7 +580,7 @@ fn account_in_output_and_sender() { }]); outputs.push(account_output); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -586,7 +588,7 @@ fn account_in_output_and_sender() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -619,7 +621,7 @@ fn missing_ed25519_sender() { issuer: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -627,11 +629,11 @@ fn missing_ed25519_sender() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::UnfulfillableRequirement(Requirement::Sender(sender))) if sender == Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap() + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender(sender))) if sender == Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap() )); } @@ -663,7 +665,7 @@ fn missing_ed25519_issuer_created() { issuer: Some(Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap()), }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -671,11 +673,11 @@ fn missing_ed25519_issuer_created() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::UnfulfillableRequirement(Requirement::Issuer(issuer))) if issuer == Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap() + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Issuer(issuer))) if issuer == Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap() )); } @@ -705,7 +707,7 @@ fn missing_ed25519_issuer_transition() { issuer: Some(Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap()), }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -713,7 +715,7 @@ fn missing_ed25519_issuer_transition() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(selected.is_ok()); } @@ -744,7 +746,7 @@ fn missing_account_sender() { issuer: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -752,11 +754,11 @@ fn missing_account_sender() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::UnfulfillableRequirement(Requirement::Sender(sender))) if sender == Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap() + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender(sender))) if sender == Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap() )); } @@ -788,7 +790,7 @@ fn missing_account_issuer_created() { issuer: Some(Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap()), }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -796,11 +798,11 @@ fn missing_account_issuer_created() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::UnfulfillableRequirement(Requirement::Issuer(issuer))) if issuer == Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap() + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Issuer(issuer))) if issuer == Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap() )); } @@ -830,7 +832,7 @@ fn missing_account_issuer_transition() { issuer: Some(Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap()), }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -838,7 +840,7 @@ fn missing_account_issuer_transition() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(selected.is_ok()); } @@ -869,7 +871,7 @@ fn missing_nft_sender() { issuer: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -877,11 +879,11 @@ fn missing_nft_sender() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::UnfulfillableRequirement(Requirement::Sender(sender))) if sender == Address::try_from_bech32(BECH32_ADDRESS_NFT_1).unwrap() + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender(sender))) if sender == Address::try_from_bech32(BECH32_ADDRESS_NFT_1).unwrap() )); } @@ -913,7 +915,7 @@ fn missing_nft_issuer_created() { issuer: Some(Address::try_from_bech32(BECH32_ADDRESS_NFT_1).unwrap()), }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -921,11 +923,11 @@ fn missing_nft_issuer_created() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::UnfulfillableRequirement(Requirement::Issuer(issuer))) if issuer == Address::try_from_bech32(BECH32_ADDRESS_NFT_1).unwrap() + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Issuer(issuer))) if issuer == Address::try_from_bech32(BECH32_ADDRESS_NFT_1).unwrap() )); } @@ -955,7 +957,7 @@ fn missing_nft_issuer_transition() { issuer: Some(Address::try_from_bech32(BECH32_ADDRESS_NFT_1).unwrap()), }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -963,7 +965,7 @@ fn missing_nft_issuer_transition() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(selected.is_ok()); } @@ -1008,7 +1010,7 @@ fn increase_account_amount() { issuer: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1016,7 +1018,7 @@ fn increase_account_amount() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1063,7 +1065,7 @@ fn decrease_account_amount() { issuer: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1071,7 +1073,7 @@ fn decrease_account_amount() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -1132,7 +1134,7 @@ fn prefer_basic_to_account() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1140,7 +1142,7 @@ fn prefer_basic_to_account() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -1190,7 +1192,7 @@ fn take_amount_from_account_to_fund_basic() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1198,7 +1200,7 @@ fn take_amount_from_account_to_fund_basic() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1262,7 +1264,7 @@ fn account_burn_should_validate_account_sender() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1271,7 +1273,7 @@ fn account_burn_should_validate_account_sender() { protocol_parameters, ) .with_burn(Burn::new().add_account(account_id_1)) - .select() + .finish() .unwrap(); assert_eq!( @@ -1336,7 +1338,7 @@ fn account_burn_should_validate_account_address() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1345,7 +1347,7 @@ fn account_burn_should_validate_account_address() { protocol_parameters, ) .with_burn(Burn::new().add_account(account_id_1)) - .select() + .finish() .unwrap(); assert_eq!( @@ -1396,7 +1398,7 @@ fn transitioned_zero_account_id_no_longer_is_zero() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1404,7 +1406,7 @@ fn transitioned_zero_account_id_no_longer_is_zero() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1467,7 +1469,7 @@ fn two_accounts_required() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1475,7 +1477,7 @@ fn two_accounts_required() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1533,7 +1535,7 @@ fn state_controller_sender_required() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1541,7 +1543,7 @@ fn state_controller_sender_required() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1586,7 +1588,7 @@ fn state_controller_sender_required_already_selected() { }, ]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1595,7 +1597,7 @@ fn state_controller_sender_required_already_selected() { protocol_parameters, ) .with_required_inputs([*inputs[0].output_id()]) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1628,7 +1630,7 @@ fn state_transition_and_required() { issuer: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1637,7 +1639,7 @@ fn state_transition_and_required() { protocol_parameters, ) .with_required_inputs([*inputs[0].output_id()]) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1670,7 +1672,7 @@ fn remainder_address_in_state_controller() { issuer: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1678,7 +1680,7 @@ fn remainder_address_in_state_controller() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1728,7 +1730,7 @@ fn min_allot_account_mana() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1737,7 +1739,7 @@ fn min_allot_account_mana() { protocol_parameters, ) .with_min_mana_allotment(account_id_1, 2) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1798,7 +1800,7 @@ fn min_allot_account_mana_additional() { .finish_output() .unwrap()]; - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1808,7 +1810,7 @@ fn min_allot_account_mana_additional() { ) .with_min_mana_allotment(account_id_1, 2) .with_mana_allotments(Some((account_id_1, provided_allotment))) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1864,7 +1866,7 @@ fn min_allot_account_mana_cannot_select_additional() { }) .collect::>(); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), None, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1876,11 +1878,11 @@ fn min_allot_account_mana_cannot_select_additional() { .with_mana_allotments(Some((account_id_2, provided_allotment))) .with_required_inputs([*inputs[0].output_id()]) .disable_additional_input_selection() - .select() + .finish() .unwrap_err(); assert!( - matches!(selected, Error::AdditionalInputsRequired(_)), + matches!(selected, TransactionBuilderError::AdditionalInputsRequired(_)), "expected AdditionalInputsRequired, found {selected:?}" ); } @@ -1917,7 +1919,7 @@ fn min_allot_account_mana_requirement_twice() { }) .collect::>(); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), None, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1927,7 +1929,7 @@ fn min_allot_account_mana_requirement_twice() { ) .with_min_mana_allotment(account_id_1, 2) .with_required_inputs([*inputs[1].output_id()]) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1998,7 +2000,7 @@ fn min_allot_account_mana_requirement_covered() { }]); outputs.push(account_output); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -2008,7 +2010,7 @@ fn min_allot_account_mana_requirement_covered() { ) .with_min_mana_allotment(account_id_1, 2) .with_mana_allotments(Some((account_id_1, provided_allotment))) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -2073,7 +2075,7 @@ fn min_allot_account_mana_requirement_covered_2() { }]); outputs.push(account_output); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -2083,7 +2085,7 @@ fn min_allot_account_mana_requirement_covered_2() { ) .with_min_mana_allotment(account_id_1, 2) .with_mana_allotments(Some((account_id_1, provided_allotment))) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -2101,14 +2103,13 @@ fn min_allot_account_mana_requirement_covered_2() { fn implicit_account_transition() { let protocol_parameters = iota_mainnet_protocol_parameters().clone(); let account_id_1 = AccountId::from_str(ACCOUNT_ID_1).unwrap(); + let ed25519_address = Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(); let inputs = [BasicOutputBuilder::new_with_amount(1_000_000) .add_unlock_condition(AddressUnlockCondition::new(Address::ImplicitAccountCreation( - ImplicitAccountCreationAddress::new( - **Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap().as_ed25519(), - ), + ImplicitAccountCreationAddress::new(**ed25519_address.as_ed25519()), ))) - .with_mana(7871) + .with_mana(10000) .finish_output() .unwrap()]; let inputs = inputs @@ -2127,11 +2128,19 @@ fn implicit_account_transition() { .add_unlock_condition(AddressUnlockCondition::new( Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), )) + .with_features([BlockIssuerFeature::new( + u32::MAX, + BlockIssuerKeys::from_vec(vec![ + Ed25519PublicKeyHashBlockIssuerKey::new(**ed25519_address.as_ed25519()).into(), + ]) + .unwrap(), + ) + .unwrap()]) .finish_output() .unwrap(), ]; - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -2141,7 +2150,7 @@ fn implicit_account_transition() { ) .with_required_inputs(vec![input_output_id]) .with_min_mana_allotment(account_id_1, 2) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -2150,10 +2159,10 @@ fn implicit_account_transition() { assert_eq!(selected.transaction.allotments().len(), 1); assert_eq!( selected.transaction.allotments()[0], - ManaAllotment::new(account_id_1, 7870).unwrap() + ManaAllotment::new(account_id_1, 9948).unwrap() ); // One remainder Mana - assert_eq!(selected.transaction.outputs()[0].mana(), 1); + assert_eq!(selected.transaction.outputs()[0].mana(), 52); } #[test] @@ -2177,7 +2186,7 @@ fn auto_transition_account_less_than_min() { Some(SLOT_INDEX), ); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), None, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -2186,7 +2195,7 @@ fn auto_transition_account_less_than_min() { protocol_parameters.clone(), ) .with_required_inputs([*inputs[0].output_id()]) - .select() + .finish() .unwrap_err(); let min_amount = AccountOutputBuilder::from(inputs[0].output.as_account()) @@ -2197,7 +2206,7 @@ fn auto_transition_account_less_than_min() { assert_eq!( selected, - Error::InsufficientAmount { + TransactionBuilderError::InsufficientAmount { found: small_amount, required: min_amount }, @@ -2239,7 +2248,7 @@ fn auto_transition_account_less_than_min_additional() { Some(SLOT_INDEX), ); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), None, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -2248,7 +2257,7 @@ fn auto_transition_account_less_than_min_additional() { protocol_parameters.clone(), ) .with_required_inputs([*inputs[0].output_id()]) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); diff --git a/sdk/tests/client/input_selection/basic_outputs.rs b/sdk/tests/client/transaction_builder/basic_outputs.rs similarity index 95% rename from sdk/tests/client/input_selection/basic_outputs.rs rename to sdk/tests/client/transaction_builder/basic_outputs.rs index 7b468b4786..56362ea42c 100644 --- a/sdk/tests/client/input_selection/basic_outputs.rs +++ b/sdk/tests/client/transaction_builder/basic_outputs.rs @@ -5,7 +5,7 @@ use std::str::FromStr; use iota_sdk::{ client::{ - api::input_selection::{Error, InputSelection, Requirement}, + api::transaction_builder::{Requirement, TransactionBuilder, TransactionBuilderError}, secret::types::InputSigningData, }, types::block::{ @@ -55,7 +55,7 @@ fn input_amount_equal_output_amount() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -63,7 +63,7 @@ fn input_amount_equal_output_amount() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -99,7 +99,7 @@ fn input_amount_lower_than_output_amount() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -107,11 +107,11 @@ fn input_amount_lower_than_output_amount() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::InsufficientAmount { + Err(TransactionBuilderError::InsufficientAmount { found: 1_000_000, required: 2_000_000, }) @@ -161,7 +161,7 @@ fn input_amount_lower_than_output_amount_2() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -169,11 +169,11 @@ fn input_amount_lower_than_output_amount_2() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::InsufficientAmount { + Err(TransactionBuilderError::InsufficientAmount { found: 3_000_000, required: 3_500_000, }) @@ -209,7 +209,7 @@ fn input_amount_greater_than_output_amount() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -217,7 +217,7 @@ fn input_amount_greater_than_output_amount() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -266,7 +266,7 @@ fn input_amount_greater_than_output_amount_with_remainder_address() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -275,7 +275,7 @@ fn input_amount_greater_than_output_amount_with_remainder_address() { protocol_parameters, ) .with_remainder_address(remainder_address) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -337,7 +337,7 @@ fn two_same_inputs_one_needed() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -345,7 +345,7 @@ fn two_same_inputs_one_needed() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); // One input has enough amount. @@ -408,7 +408,7 @@ fn two_inputs_one_needed() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -416,7 +416,7 @@ fn two_inputs_one_needed() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data, [inputs[0].clone()]); @@ -466,7 +466,7 @@ fn two_inputs_one_needed_reversed() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -474,7 +474,7 @@ fn two_inputs_one_needed_reversed() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data, [inputs[1].clone()]); @@ -524,7 +524,7 @@ fn two_inputs_both_needed() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -532,7 +532,7 @@ fn two_inputs_both_needed() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -582,7 +582,7 @@ fn two_inputs_remainder() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -590,7 +590,7 @@ fn two_inputs_remainder() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -634,7 +634,7 @@ fn two_inputs_remainder() { // None, // )]); -// let selected = InputSelection::new( +// let selected = TransactionBuilder::new( // inputs, // outputs, // [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -731,7 +731,7 @@ fn ed25519_sender() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [ @@ -742,7 +742,7 @@ fn ed25519_sender() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); // Sender + another for amount @@ -786,7 +786,7 @@ fn missing_ed25519_sender() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -794,11 +794,11 @@ fn missing_ed25519_sender() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::UnfulfillableRequirement(Requirement::Sender(sender))) if sender == Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap() + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender(sender))) if sender == Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap() )); } @@ -880,7 +880,7 @@ fn account_sender() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -888,7 +888,7 @@ fn account_sender() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); // Sender + another for amount @@ -947,7 +947,7 @@ fn account_sender_zero_id() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -955,7 +955,7 @@ fn account_sender_zero_id() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -998,7 +998,7 @@ fn missing_account_sender() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1006,11 +1006,11 @@ fn missing_account_sender() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::UnfulfillableRequirement(Requirement::Sender(sender))) if sender == Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap() + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender(sender))) if sender == Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap() )); } @@ -1094,7 +1094,7 @@ fn nft_sender() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1102,7 +1102,7 @@ fn nft_sender() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); // Sender + another for amount @@ -1164,7 +1164,7 @@ fn nft_sender_zero_id() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1172,7 +1172,7 @@ fn nft_sender_zero_id() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1215,7 +1215,7 @@ fn missing_nft_sender() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1223,11 +1223,11 @@ fn missing_nft_sender() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::UnfulfillableRequirement(Requirement::Sender(sender))) if sender == Address::try_from_bech32(BECH32_ADDRESS_NFT_1).unwrap() + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender(sender))) if sender == Address::try_from_bech32(BECH32_ADDRESS_NFT_1).unwrap() )); } @@ -1260,7 +1260,7 @@ fn simple_remainder() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1268,7 +1268,7 @@ fn simple_remainder() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1311,7 +1311,7 @@ fn simple_remainder() { // None, // )]); -// let selected = InputSelection::new( +// let selected = TransactionBuilder::new( // inputs, // outputs, // [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1349,7 +1349,7 @@ fn simple_remainder() { // None, // )]); -// let selected = InputSelection::new( +// let selected = TransactionBuilder::new( // inputs, // outputs, // [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1395,7 +1395,7 @@ fn one_provided_one_needed() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1403,7 +1403,7 @@ fn one_provided_one_needed() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1439,7 +1439,7 @@ fn insufficient_amount() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1447,11 +1447,11 @@ fn insufficient_amount() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::InsufficientAmount { + Err(TransactionBuilderError::InsufficientAmount { found: 1_000_000, required: 1_250_000, }) @@ -1501,7 +1501,7 @@ fn two_inputs_remainder_2() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1509,7 +1509,7 @@ fn two_inputs_remainder_2() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -1571,7 +1571,7 @@ fn two_inputs_remainder_3() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1579,7 +1579,7 @@ fn two_inputs_remainder_3() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1618,7 +1618,7 @@ fn two_inputs_remainder_3() { // None, // )]); -// let selected = InputSelection::new( +// let selected = TransactionBuilder::new( // inputs.clone(), // outputs.clone(), // [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1666,7 +1666,7 @@ fn sender_already_selected() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [ @@ -1678,7 +1678,7 @@ fn sender_already_selected() { protocol_parameters, ) .with_required_inputs([*inputs[0].output_id()]) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1714,7 +1714,7 @@ fn single_mandatory_input() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [ @@ -1726,7 +1726,7 @@ fn single_mandatory_input() { protocol_parameters, ) .with_required_inputs([*inputs[0].output_id()]) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1766,7 +1766,7 @@ fn too_many_inputs() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1774,11 +1774,11 @@ fn too_many_inputs() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert_eq!( selected.unwrap_err(), - iota_sdk::client::api::input_selection::Error::InvalidInputCount(129) + iota_sdk::client::api::transaction_builder::TransactionBuilderError::InvalidInputCount(129) ) } @@ -1833,7 +1833,7 @@ fn more_than_max_inputs_only_one_needed() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1841,7 +1841,7 @@ fn more_than_max_inputs_only_one_needed() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &needed_input)); @@ -1880,7 +1880,7 @@ fn too_many_outputs() { .take(129), ); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1888,11 +1888,11 @@ fn too_many_outputs() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert_eq!( selected.unwrap_err(), - iota_sdk::client::api::input_selection::Error::InvalidOutputCount(129) + iota_sdk::client::api::transaction_builder::TransactionBuilderError::InvalidOutputCount(129) ) } @@ -1929,7 +1929,7 @@ fn too_many_outputs_with_remainder() { .take(128), ); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1937,12 +1937,12 @@ fn too_many_outputs_with_remainder() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert_eq!( selected.unwrap_err(), // 129 because of required remainder - iota_sdk::client::api::input_selection::Error::InvalidOutputCount(129) + iota_sdk::client::api::transaction_builder::TransactionBuilderError::InvalidOutputCount(129) ) } @@ -2027,7 +2027,7 @@ fn restricted_ed25519() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap()], @@ -2035,7 +2035,7 @@ fn restricted_ed25519() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -2089,7 +2089,7 @@ fn restricted_nft() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -2097,7 +2097,7 @@ fn restricted_nft() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -2149,7 +2149,7 @@ fn restricted_account() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -2157,7 +2157,7 @@ fn restricted_account() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -2246,7 +2246,7 @@ fn restricted_ed25519_sender() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [ @@ -2257,7 +2257,7 @@ fn restricted_ed25519_sender() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); // Sender + another for amount @@ -2341,7 +2341,7 @@ fn multi_address_sender_already_fulfilled() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [ @@ -2354,7 +2354,7 @@ fn multi_address_sender_already_fulfilled() { protocol_parameters, ) .with_required_inputs([*inputs[0].output_id(), *inputs[1].output_id(), *inputs[2].output_id()]) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -2421,7 +2421,7 @@ fn ed25519_backed_available_address() { }, ]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), // Restricted address is provided, but it can also unlock the ed25519 one @@ -2430,7 +2430,7 @@ fn ed25519_backed_available_address() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -2469,7 +2469,7 @@ fn automatic_allotment_provided_in_and_output() { .unwrap(), ]; - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -2478,7 +2478,7 @@ fn automatic_allotment_provided_in_and_output() { protocol_parameters, ) .with_min_mana_allotment(account_id_1, 2) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); diff --git a/sdk/tests/client/input_selection/burn.rs b/sdk/tests/client/transaction_builder/burn.rs similarity index 95% rename from sdk/tests/client/input_selection/burn.rs rename to sdk/tests/client/transaction_builder/burn.rs index 39734d6250..c170df6642 100644 --- a/sdk/tests/client/input_selection/burn.rs +++ b/sdk/tests/client/transaction_builder/burn.rs @@ -8,7 +8,7 @@ use std::{ use iota_sdk::{ client::{ - api::input_selection::{Burn, Error, InputSelection, Requirement}, + api::transaction_builder::{Burn, Requirement, TransactionBuilder, TransactionBuilderError}, secret::types::InputSigningData, }, types::block::{ @@ -27,7 +27,7 @@ use pretty_assertions::assert_eq; use crate::client::{ assert_remainder_or_return, build_inputs, build_outputs, - input_selection::native_tokens::nt_remainder_min_storage_deposit, + transaction_builder::native_tokens::nt_remainder_min_storage_deposit, unsorted_eq, Build::{Account, Basic, Foundry, Nft}, ACCOUNT_ID_0, ACCOUNT_ID_1, ACCOUNT_ID_2, BECH32_ADDRESS_ED25519_0, NFT_ID_0, NFT_ID_1, NFT_ID_2, @@ -76,7 +76,7 @@ fn burn_account_present() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -85,7 +85,7 @@ fn burn_account_present() { protocol_parameters, ) .with_burn(Burn::new().add_account(account_id_1)) - .select() + .finish() .unwrap(); assert_eq!( @@ -139,7 +139,7 @@ fn burn_account_present_and_required() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -149,7 +149,7 @@ fn burn_account_present_and_required() { ) .with_burn(Burn::new().add_account(account_id_1)) .with_required_inputs([*inputs[0].output_id()]) - .select() + .finish() .unwrap(); assert_eq!( @@ -206,7 +206,7 @@ fn burn_account_id_zero() { }]); let nft_id = NftId::from(inputs[0].output_id()); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -215,7 +215,7 @@ fn burn_account_id_zero() { protocol_parameters, ) .with_burn(Burn::new().add_nft(nft_id)) - .select() + .finish() .unwrap(); assert_eq!( @@ -257,7 +257,7 @@ fn burn_account_absent() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -266,12 +266,12 @@ fn burn_account_absent() { protocol_parameters, ) .with_burn(Burn::new().add_account(account_id_1)) - .select() + .finish() .unwrap_err(); assert_eq!( selected, - Error::UnfulfillableRequirement(Requirement::Account(account_id_1)) + TransactionBuilderError::UnfulfillableRequirement(Requirement::Account(account_id_1)) ); } @@ -328,7 +328,7 @@ fn burn_accounts_present() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -337,7 +337,7 @@ fn burn_accounts_present() { protocol_parameters, ) .with_burn(Burn::new().set_accounts(HashSet::from([account_id_1, account_id_2]))) - .select() + .finish() .unwrap(); assert_eq!( @@ -399,7 +399,7 @@ fn burn_account_in_outputs() { }, ]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -408,10 +408,13 @@ fn burn_account_in_outputs() { protocol_parameters, ) .with_burn(Burn::new().add_account(account_id_1)) - .select() + .finish() .unwrap_err(); - assert_eq!(selected, Error::BurnAndTransition(ChainId::Account(account_id_1))); + assert_eq!( + selected, + TransactionBuilderError::BurnAndTransition(ChainId::Account(account_id_1)) + ); } #[test] @@ -458,7 +461,7 @@ fn burn_nft_present() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -467,7 +470,7 @@ fn burn_nft_present() { protocol_parameters, ) .with_burn(Burn::new().add_nft(nft_id_1)) - .select() + .finish() .unwrap(); assert_eq!( @@ -523,7 +526,7 @@ fn burn_nft_present_and_required() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -533,7 +536,7 @@ fn burn_nft_present_and_required() { ) .with_burn(Burn::new().add_nft(nft_id_1)) .with_required_inputs([*inputs[0].output_id()]) - .select() + .finish() .unwrap(); assert_eq!( @@ -588,7 +591,7 @@ fn burn_nft_id_zero() { }]); let account_id = AccountId::from(inputs[0].output_id()); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -597,7 +600,7 @@ fn burn_nft_id_zero() { protocol_parameters, ) .with_burn(Burn::new().add_account(account_id)) - .select() + .finish() .unwrap(); assert_eq!( @@ -639,7 +642,7 @@ fn burn_nft_absent() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -648,10 +651,13 @@ fn burn_nft_absent() { protocol_parameters, ) .with_burn(Burn::new().add_nft(nft_id_1)) - .select() + .finish() .unwrap_err(); - assert_eq!(selected, Error::UnfulfillableRequirement(Requirement::Nft(nft_id_1))); + assert_eq!( + selected, + TransactionBuilderError::UnfulfillableRequirement(Requirement::Nft(nft_id_1)) + ); } #[test] @@ -711,7 +717,7 @@ fn burn_nfts_present() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -720,7 +726,7 @@ fn burn_nfts_present() { protocol_parameters, ) .with_burn(Burn::new().set_nfts(HashSet::from([nft_id_1, nft_id_2]))) - .select() + .finish() .unwrap(); assert_eq!( @@ -786,7 +792,7 @@ fn burn_nft_in_outputs() { }, ]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -795,10 +801,13 @@ fn burn_nft_in_outputs() { protocol_parameters, ) .with_burn(Burn::new().add_nft(nft_id_1)) - .select() + .finish() .unwrap_err(); - assert_eq!(selected, Error::BurnAndTransition(ChainId::Nft(nft_id_1))); + assert_eq!( + selected, + TransactionBuilderError::BurnAndTransition(ChainId::Nft(nft_id_1)) + ); } #[test] @@ -853,7 +862,7 @@ fn burn_foundry_present() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -862,7 +871,7 @@ fn burn_foundry_present() { protocol_parameters, ) .with_burn(Burn::new().add_foundry(inputs[0].output.as_foundry().id())) - .select() + .finish() .unwrap(); assert_eq!( @@ -958,7 +967,7 @@ fn burn_foundry_absent() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -967,12 +976,12 @@ fn burn_foundry_absent() { protocol_parameters, ) .with_burn(Burn::new().add_foundry(foundry_id_1)) - .select() + .finish() .unwrap_err(); assert_eq!( selected, - Error::UnfulfillableRequirement(Requirement::Foundry(foundry_id_1)) + TransactionBuilderError::UnfulfillableRequirement(Requirement::Foundry(foundry_id_1)) ); } @@ -1026,7 +1035,7 @@ fn burn_foundries_present() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1038,7 +1047,7 @@ fn burn_foundries_present() { inputs[0].output.as_foundry().id(), inputs[1].output.as_foundry().id(), ]))) - .select() + .finish() .unwrap(); assert_eq!( @@ -1116,7 +1125,7 @@ fn burn_foundry_in_outputs() { ]); let foundry_id_1 = inputs[0].output.as_foundry().id(); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1125,10 +1134,13 @@ fn burn_foundry_in_outputs() { protocol_parameters, ) .with_burn(Burn::new().add_foundry(foundry_id_1)) - .select() + .finish() .unwrap_err(); - assert_eq!(selected, Error::BurnAndTransition(ChainId::Foundry(foundry_id_1))); + assert_eq!( + selected, + TransactionBuilderError::BurnAndTransition(ChainId::Foundry(foundry_id_1)) + ); } #[test] @@ -1167,7 +1179,7 @@ fn burn_native_tokens() { let nt_remainder_output_amount = nt_remainder_min_storage_deposit(&protocol_parameters); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), None, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1179,7 +1191,7 @@ fn burn_native_tokens() { (TokenId::from_str(TOKEN_ID_1).unwrap(), 20), (TokenId::from_str(TOKEN_ID_2).unwrap(), 30), ]))) - .select() + .finish() .unwrap(); assert_eq!( @@ -1255,7 +1267,7 @@ fn burn_foundry_and_its_account() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1268,7 +1280,7 @@ fn burn_foundry_and_its_account() { .add_foundry(inputs[0].output.as_foundry().id()) .add_account(account_id_1), ) - .select() + .finish() .unwrap(); assert_eq!( @@ -1324,7 +1336,7 @@ fn burn_mana() { .finish_output() .unwrap()]; - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1334,7 +1346,7 @@ fn burn_mana() { ) .with_required_inputs([*inputs[0].output_id()]) .with_burn(Burn::new().set_mana(true)) - .select() + .finish() .unwrap(); assert_eq!( @@ -1382,7 +1394,7 @@ fn burn_mana_need_additional() { .finish_output() .unwrap()]; - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1392,7 +1404,7 @@ fn burn_mana_need_additional() { ) .with_required_inputs([*inputs[0].output_id()]) .with_burn(Burn::new().set_mana(true)) - .select() + .finish() .unwrap(); assert_eq!( @@ -1442,7 +1454,7 @@ fn burn_mana_need_additional_account() { .finish_output() .unwrap()]; - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1452,7 +1464,7 @@ fn burn_mana_need_additional_account() { ) .with_required_inputs([*inputs[0].output_id()]) .with_burn(Burn::new().set_mana(true)) - .select() + .finish() .unwrap(); assert_eq!( @@ -1502,7 +1514,7 @@ fn burn_generated_mana() { .finish_output() .unwrap()]; - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1511,7 +1523,7 @@ fn burn_generated_mana() { protocol_parameters, ) .with_burn(Burn::new().set_generated_mana(true)) - .select() + .finish() .unwrap(); assert_eq!( @@ -1551,7 +1563,7 @@ fn burn_generated_mana_remainder() { }) .collect::>(); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), None, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1561,7 +1573,7 @@ fn burn_generated_mana_remainder() { ) .with_burn(Burn::new().set_generated_mana(true)) .with_required_inputs(inputs.iter().map(|i| *i.output_id())) - .select() + .finish() .unwrap(); assert_eq!( @@ -1603,7 +1615,7 @@ fn burn_generated_mana_account() { }) .collect::>(); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), None, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1613,7 +1625,7 @@ fn burn_generated_mana_account() { ) .with_burn(Burn::new().set_generated_mana(true)) .with_required_inputs(inputs.iter().map(|i| *i.output_id())) - .select() + .finish() .unwrap(); assert_eq!( diff --git a/sdk/tests/client/input_selection/delegation_outputs.rs b/sdk/tests/client/transaction_builder/delegation_outputs.rs similarity index 88% rename from sdk/tests/client/input_selection/delegation_outputs.rs rename to sdk/tests/client/transaction_builder/delegation_outputs.rs index e5d94b1d49..612e6781a7 100644 --- a/sdk/tests/client/input_selection/delegation_outputs.rs +++ b/sdk/tests/client/transaction_builder/delegation_outputs.rs @@ -3,7 +3,7 @@ use iota_sdk::{ client::{ - api::input_selection::{Burn, InputSelection}, + api::transaction_builder::{Burn, TransactionBuilder}, secret::types::InputSigningData, }, types::block::{ @@ -73,7 +73,7 @@ fn remainder_needed_for_mana() { let mana_rewards = 100; - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -83,10 +83,11 @@ fn remainder_needed_for_mana() { ) .with_burn(Burn::from(delegation_id)) .add_mana_rewards(delegation_output_id, mana_rewards) - .select() + .finish() .unwrap(); - let inputs = inputs + let inputs = selected + .inputs_data .iter() .map(|input| (input.output_id(), &input.output)) .collect::>(); @@ -97,18 +98,7 @@ fn remainder_needed_for_mana() { &inputs, None, None, - protocol_parameters.clone(), - ) - .validate() - .unwrap(); - - // validating with rewards - iota_sdk::types::block::semantic::SemanticValidationContext::new( - &selected.transaction, - &inputs, - None, - Some(std::collections::BTreeMap::from([(delegation_output_id, mana_rewards)])), - protocol_parameters.clone(), + protocol_parameters, ) .validate() .unwrap(); diff --git a/sdk/tests/client/input_selection/expiration.rs b/sdk/tests/client/transaction_builder/expiration.rs similarity index 95% rename from sdk/tests/client/input_selection/expiration.rs rename to sdk/tests/client/transaction_builder/expiration.rs index d79a8f9e7f..45d94865b8 100644 --- a/sdk/tests/client/input_selection/expiration.rs +++ b/sdk/tests/client/transaction_builder/expiration.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use iota_sdk::{ - client::api::input_selection::{Error, InputSelection}, + client::api::transaction_builder::{TransactionBuilder, TransactionBuilderError}, types::block::{ address::Address, output::{AccountId, NftId}, @@ -50,7 +50,7 @@ fn one_output_expiration_not_expired() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -58,9 +58,12 @@ fn one_output_expiration_not_expired() { SlotCommitmentHash::null().into_slot_commitment_id(99), protocol_parameters, ) - .select(); + .finish(); - assert!(matches!(selected, Err(Error::NoAvailableInputsProvided))); + assert!(matches!( + selected, + Err(TransactionBuilderError::NoAvailableInputsProvided) + )); } #[test] @@ -92,7 +95,7 @@ fn expiration_equal_timestamp() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -100,7 +103,7 @@ fn expiration_equal_timestamp() { SlotCommitmentHash::null().into_slot_commitment_id(199), protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -136,7 +139,7 @@ fn one_output_expiration_expired() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -144,7 +147,7 @@ fn one_output_expiration_expired() { SlotCommitmentHash::null().into_slot_commitment_id(99), protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -194,7 +197,7 @@ fn two_outputs_one_expiration_expired() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -202,7 +205,7 @@ fn two_outputs_one_expiration_expired() { SlotCommitmentHash::null().into_slot_commitment_id(99), protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -253,7 +256,7 @@ fn two_outputs_one_unexpired_one_missing() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -261,7 +264,7 @@ fn two_outputs_one_unexpired_one_missing() { SlotCommitmentHash::null().into_slot_commitment_id(99), protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -324,7 +327,7 @@ fn two_outputs_two_expired() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_2).unwrap()], @@ -332,7 +335,7 @@ fn two_outputs_two_expired() { SlotCommitmentHash::null().into_slot_commitment_id(199), protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -383,7 +386,7 @@ fn two_outputs_two_expired_2() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [ @@ -394,7 +397,7 @@ fn two_outputs_two_expired_2() { SlotCommitmentHash::null().into_slot_commitment_id(199), protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -430,7 +433,7 @@ fn expiration_expired_with_sdr() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -438,7 +441,7 @@ fn expiration_expired_with_sdr() { SlotCommitmentHash::null().into_slot_commitment_id(99), protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -474,7 +477,7 @@ fn expiration_expired_with_sdr_2() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -482,7 +485,7 @@ fn expiration_expired_with_sdr_2() { SlotCommitmentHash::null().into_slot_commitment_id(99), protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -518,7 +521,7 @@ fn expiration_expired_with_sdr_and_timelock() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -526,7 +529,7 @@ fn expiration_expired_with_sdr_and_timelock() { SlotCommitmentHash::null().into_slot_commitment_id(99), protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -562,7 +565,7 @@ fn expiration_expired_with_sdr_and_timelock_2() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -570,7 +573,7 @@ fn expiration_expired_with_sdr_and_timelock_2() { SlotCommitmentHash::null().into_slot_commitment_id(99), protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -656,7 +659,7 @@ fn sender_in_expiration() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [ @@ -667,7 +670,7 @@ fn sender_in_expiration() { SlotCommitmentHash::null().into_slot_commitment_id(99), protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -704,7 +707,7 @@ fn sender_in_expiration_already_selected() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [ @@ -716,7 +719,7 @@ fn sender_in_expiration_already_selected() { protocol_parameters, ) .with_required_inputs([*inputs[0].output_id()]) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -752,7 +755,7 @@ fn remainder_in_expiration() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [ @@ -763,7 +766,7 @@ fn remainder_in_expiration() { SlotCommitmentHash::null().into_slot_commitment_id(99), protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -810,7 +813,7 @@ fn expiration_expired_non_ed25519_in_address_unlock_condition() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -818,7 +821,7 @@ fn expiration_expired_non_ed25519_in_address_unlock_condition() { SlotCommitmentHash::null().into_slot_commitment_id(99), protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -868,7 +871,7 @@ fn expiration_expired_only_account_addresses() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -876,7 +879,7 @@ fn expiration_expired_only_account_addresses() { SlotCommitmentHash::null().into_slot_commitment_id(99), protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -913,7 +916,7 @@ fn one_nft_output_expiration_unexpired() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap()], @@ -921,7 +924,7 @@ fn one_nft_output_expiration_unexpired() { SlotCommitmentHash::null().into_slot_commitment_id(99), protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -958,7 +961,7 @@ fn one_nft_output_expiration_expired() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -966,7 +969,7 @@ fn one_nft_output_expiration_expired() { SlotCommitmentHash::null().into_slot_commitment_id(99), protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); diff --git a/sdk/tests/client/input_selection/foundry_outputs.rs b/sdk/tests/client/transaction_builder/foundry_outputs.rs similarity index 96% rename from sdk/tests/client/input_selection/foundry_outputs.rs rename to sdk/tests/client/transaction_builder/foundry_outputs.rs index e095fbf0ac..d363420fe2 100644 --- a/sdk/tests/client/input_selection/foundry_outputs.rs +++ b/sdk/tests/client/transaction_builder/foundry_outputs.rs @@ -5,7 +5,7 @@ use std::str::FromStr; use iota_sdk::{ client::{ - api::input_selection::{Burn, Error, InputSelection, Requirement}, + api::transaction_builder::{Burn, Requirement, TransactionBuilder, TransactionBuilderError}, secret::types::InputSigningData, }, types::block::{ @@ -17,6 +17,7 @@ use iota_sdk::{ payload::signed_transaction::{TransactionCapabilities, TransactionCapabilityFlag}, protocol::iota_mainnet_protocol_parameters, rand::output::{rand_output_id_with_slot_index, rand_output_metadata_with_id}, + semantic::TransactionFailureReason, }, }; use pretty_assertions::assert_eq; @@ -55,7 +56,7 @@ fn missing_input_account_for_foundry() { native_token: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -63,11 +64,11 @@ fn missing_input_account_for_foundry() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::UnfulfillableRequirement(Requirement::Account(account_id))) if account_id == account_id_2 + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Account(account_id))) if account_id == account_id_2 )); } @@ -95,7 +96,7 @@ fn missing_input_account_for_foundry() { // None, // )]); -// let selected = InputSelection::new( +// let selected = TransactionBuilder::new( // inputs.clone(), // outputs, // [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -149,7 +150,7 @@ fn minted_native_tokens_in_new_remainder() { native_token: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -157,7 +158,7 @@ fn minted_native_tokens_in_new_remainder() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -225,7 +226,7 @@ fn minted_native_tokens_in_provided_output() { }, ]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -233,7 +234,7 @@ fn minted_native_tokens_in_provided_output() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -298,7 +299,7 @@ fn melt_native_tokens() { native_token: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -306,7 +307,7 @@ fn melt_native_tokens() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -358,7 +359,7 @@ fn destroy_foundry_with_account_state_transition() { // Account output gets the amount from the foundry output added let outputs = [account_output]; - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -367,7 +368,7 @@ fn destroy_foundry_with_account_state_transition() { protocol_parameters, ) .with_burn(Burn::new().add_foundry(inputs[1].output.as_foundry().id())) - .select() + .finish() .unwrap(); assert_eq!( @@ -419,7 +420,7 @@ fn destroy_foundry_with_account_burn() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -432,7 +433,7 @@ fn destroy_foundry_with_account_burn() { .add_foundry(inputs[1].output.as_foundry().id()) .add_account(account_id_2), ) - .select() + .finish() .unwrap(); assert_eq!( @@ -509,7 +510,7 @@ fn prefer_basic_to_foundry() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -517,7 +518,7 @@ fn prefer_basic_to_foundry() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -578,7 +579,7 @@ fn simple_foundry_transition_basic_not_needed() { native_token: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -586,7 +587,7 @@ fn simple_foundry_transition_basic_not_needed() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 2); @@ -662,7 +663,7 @@ fn simple_foundry_transition_basic_not_needed_with_remainder() { native_token: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -670,7 +671,7 @@ fn simple_foundry_transition_basic_not_needed_with_remainder() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 2); @@ -730,7 +731,7 @@ fn simple_foundry_transition_basic_not_needed_with_remainder() { // Some(BECH32_ADDRESS_ACCOUNT_SENDER), // )]; -// let selected = InputSelection::new(inputs.clone(), outputs.clone(), [],protocol_parameters) +// let selected = TransactionBuilder::new(inputs.clone(), outputs.clone(), [],protocol_parameters) // .select() // .unwrap(); @@ -812,7 +813,7 @@ fn mint_and_burn_at_the_same_time() { native_token: Some((&token_id.to_string(), 110)), }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -821,12 +822,12 @@ fn mint_and_burn_at_the_same_time() { protocol_parameters, ) .with_burn(Burn::new().add_native_token(token_id, 10)) - .select() + .finish() .unwrap_err(); assert_eq!( selected, - Error::UnfulfillableRequirement(Requirement::Foundry(foundry_id)) + TransactionBuilderError::Semantic(TransactionFailureReason::NativeTokenSumUnbalanced) ); } @@ -886,7 +887,7 @@ fn take_amount_from_account_and_foundry_to_fund_basic() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -894,7 +895,7 @@ fn take_amount_from_account_and_foundry_to_fund_basic() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -953,7 +954,7 @@ fn create_native_token_but_burn_account() { native_token: Some((&token_id.to_string(), 100)), }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -962,7 +963,7 @@ fn create_native_token_but_burn_account() { protocol_parameters, ) .with_burn(Burn::new().add_account(account_id_1)) - .select() + .finish() .unwrap(); assert_eq!( @@ -1025,7 +1026,7 @@ fn melted_tokens_not_provided() { native_token: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1033,11 +1034,11 @@ fn melted_tokens_not_provided() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::InsufficientNativeTokenAmount { + Err(TransactionBuilderError::InsufficientNativeTokenAmount { token_id, found, required, @@ -1084,7 +1085,7 @@ fn burned_tokens_not_provided() { native_token: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1093,12 +1094,12 @@ fn burned_tokens_not_provided() { protocol_parameters, ) .with_burn(Burn::new().add_native_token(token_id_1, 100)) - .select() + .finish() .unwrap_err(); assert_eq!( selected, - Error::InsufficientNativeTokenAmount { + TransactionBuilderError::InsufficientNativeTokenAmount { token_id: token_id_1, found: 0.into(), required: 100.into(), @@ -1144,7 +1145,7 @@ fn foundry_in_outputs_and_required() { native_token: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1153,7 +1154,7 @@ fn foundry_in_outputs_and_required() { protocol_parameters, ) .with_required_inputs([*inputs[1].output_id()]) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1220,7 +1221,7 @@ fn melt_and_burn_native_tokens() { native_token: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1230,7 +1231,7 @@ fn melt_and_burn_native_tokens() { ) // Burn 456 native tokens .with_burn(Burn::new().add_native_token(token_id, 456)) - .select() + .finish() .unwrap(); assert_eq!( @@ -1286,7 +1287,7 @@ fn auto_transition_foundry_less_than_min() { chain: None, }); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), None, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1295,7 +1296,7 @@ fn auto_transition_foundry_less_than_min() { protocol_parameters.clone(), ) .with_required_inputs([*inputs[0].output_id()]) - .select() + .finish() .unwrap_err(); let min_amount = FoundryOutputBuilder::from(inputs[0].output.as_foundry()) @@ -1311,7 +1312,7 @@ fn auto_transition_foundry_less_than_min() { assert_eq!( selected, - Error::InsufficientAmount { + TransactionBuilderError::InsufficientAmount { found: small_amount_foundry + small_amount_account, required: min_amount }, @@ -1367,7 +1368,7 @@ fn auto_transition_foundry_less_than_min_additional() { chain: None, }); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), None, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1376,7 +1377,7 @@ fn auto_transition_foundry_less_than_min_additional() { protocol_parameters.clone(), ) .with_required_inputs([*inputs[0].output_id()]) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); diff --git a/sdk/tests/client/input_selection/mod.rs b/sdk/tests/client/transaction_builder/mod.rs similarity index 100% rename from sdk/tests/client/input_selection/mod.rs rename to sdk/tests/client/transaction_builder/mod.rs diff --git a/sdk/tests/client/input_selection/native_tokens.rs b/sdk/tests/client/transaction_builder/native_tokens.rs similarity index 96% rename from sdk/tests/client/input_selection/native_tokens.rs rename to sdk/tests/client/transaction_builder/native_tokens.rs index e5870611a3..a84f317993 100644 --- a/sdk/tests/client/input_selection/native_tokens.rs +++ b/sdk/tests/client/transaction_builder/native_tokens.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use iota_sdk::{ - client::api::input_selection::{Burn, Error, InputSelection}, + client::api::transaction_builder::{Burn, TransactionBuilder, TransactionBuilderError}, types::block::{ address::Address, output::{unlock_condition::AddressUnlockCondition, BasicOutputBuilder, NativeToken, TokenId}, @@ -63,7 +63,7 @@ fn two_native_tokens_one_needed() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -71,7 +71,7 @@ fn two_native_tokens_one_needed() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data[0], inputs[0]); @@ -133,7 +133,7 @@ fn two_native_tokens_both_needed_plus_remainder() { }, ]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -141,7 +141,7 @@ fn two_native_tokens_both_needed_plus_remainder() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -214,7 +214,7 @@ fn three_inputs_two_needed_plus_remainder() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -222,7 +222,7 @@ fn three_inputs_two_needed_plus_remainder() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 2); @@ -295,7 +295,7 @@ fn three_inputs_two_needed_no_remainder() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -303,7 +303,7 @@ fn three_inputs_two_needed_no_remainder() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 2); @@ -339,7 +339,7 @@ fn insufficient_native_tokens_one_input() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -347,11 +347,11 @@ fn insufficient_native_tokens_one_input() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::InsufficientNativeTokenAmount { + Err(TransactionBuilderError::InsufficientNativeTokenAmount { token_id, found, required, @@ -413,7 +413,7 @@ fn insufficient_native_tokens_three_inputs() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -421,11 +421,11 @@ fn insufficient_native_tokens_three_inputs() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::InsufficientNativeTokenAmount { + Err(TransactionBuilderError::InsufficientNativeTokenAmount { token_id, found, required, @@ -475,7 +475,7 @@ fn burn_and_send_at_the_same_time() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -488,7 +488,7 @@ fn burn_and_send_at_the_same_time() { .add_native_token(TokenId::from_str(TOKEN_ID_1).unwrap(), 10) .add_native_token(TokenId::from_str(TOKEN_ID_2).unwrap(), 100), ) - .select() + .finish() .unwrap(); assert_eq!( @@ -530,7 +530,7 @@ fn burn_one_input_no_output() { Some(SLOT_INDEX), ); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), None, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -539,7 +539,7 @@ fn burn_one_input_no_output() { protocol_parameters, ) .with_burn(Burn::new().add_native_token(TokenId::from_str(TOKEN_ID_1).unwrap(), 50)) - .select() + .finish() .unwrap(); assert_eq!( @@ -599,7 +599,7 @@ fn multiple_native_tokens() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -607,7 +607,7 @@ fn multiple_native_tokens() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -644,7 +644,7 @@ fn insufficient_native_tokens() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -652,11 +652,11 @@ fn insufficient_native_tokens() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::InsufficientNativeTokenAmount { + Err(TransactionBuilderError::InsufficientNativeTokenAmount { token_id, found, required, @@ -692,7 +692,7 @@ fn insufficient_native_tokens_2() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -700,11 +700,11 @@ fn insufficient_native_tokens_2() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::InsufficientNativeTokenAmount { + Err(TransactionBuilderError::InsufficientNativeTokenAmount { token_id, found, required, @@ -742,7 +742,7 @@ fn insufficient_amount_for_remainder() { let nt_remainder_min_storage_deposit = nt_remainder_min_storage_deposit(&protocol_parameters); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -750,11 +750,11 @@ fn insufficient_amount_for_remainder() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert_eq!( selected.unwrap_err(), - Error::InsufficientAmount { + TransactionBuilderError::InsufficientAmount { found: 1_000_000, required: 1_000_000 + nt_remainder_min_storage_deposit, } @@ -790,7 +790,7 @@ fn single_output_native_token_no_remainder() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -798,7 +798,7 @@ fn single_output_native_token_no_remainder() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -834,7 +834,7 @@ fn single_output_native_token_remainder_1() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -842,7 +842,7 @@ fn single_output_native_token_remainder_1() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -885,7 +885,7 @@ fn single_output_native_token_remainder_2() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -893,7 +893,7 @@ fn single_output_native_token_remainder_2() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -950,7 +950,7 @@ fn two_basic_outputs_1() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -958,7 +958,7 @@ fn two_basic_outputs_1() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -1016,7 +1016,7 @@ fn two_basic_outputs_2() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1024,7 +1024,7 @@ fn two_basic_outputs_2() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -1082,7 +1082,7 @@ fn two_basic_outputs_3() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1090,7 +1090,7 @@ fn two_basic_outputs_3() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -1148,7 +1148,7 @@ fn two_basic_outputs_4() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1156,7 +1156,7 @@ fn two_basic_outputs_4() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -1214,7 +1214,7 @@ fn two_basic_outputs_5() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1222,7 +1222,7 @@ fn two_basic_outputs_5() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -1280,7 +1280,7 @@ fn two_basic_outputs_6() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1288,7 +1288,7 @@ fn two_basic_outputs_6() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1345,7 +1345,7 @@ fn two_basic_outputs_7() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1353,7 +1353,7 @@ fn two_basic_outputs_7() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1410,7 +1410,7 @@ fn two_basic_outputs_8() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1418,11 +1418,11 @@ fn two_basic_outputs_8() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::InsufficientNativeTokenAmount { + Err(TransactionBuilderError::InsufficientNativeTokenAmount { token_id, found, required, @@ -1472,7 +1472,7 @@ fn two_basic_outputs_native_tokens_not_needed() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1480,7 +1480,7 @@ fn two_basic_outputs_native_tokens_not_needed() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -1564,7 +1564,7 @@ fn multiple_remainders() { let nt_remainder_min_storage_deposit = nt_remainder_min_storage_deposit(&protocol_parameters); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1572,7 +1572,7 @@ fn multiple_remainders() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 4); @@ -1657,7 +1657,7 @@ pub fn nt_remainder_min_storage_deposit(protocol_parameters: &ProtocolParameters // None, // )]); -// let selected = InputSelection::new( +// let selected = TransactionBuilder::new( // inputs.clone(), // outputs.clone(), // addresses([BECH32_ADDRESS_ED25519_0]), diff --git a/sdk/tests/client/input_selection/nft_outputs.rs b/sdk/tests/client/transaction_builder/nft_outputs.rs similarity index 93% rename from sdk/tests/client/input_selection/nft_outputs.rs rename to sdk/tests/client/transaction_builder/nft_outputs.rs index 6fbdcee3b8..8a574ef3e1 100644 --- a/sdk/tests/client/input_selection/nft_outputs.rs +++ b/sdk/tests/client/transaction_builder/nft_outputs.rs @@ -5,7 +5,7 @@ use std::str::FromStr; use iota_sdk::{ client::{ - api::input_selection::{Burn, Error, InputSelection, Requirement}, + api::transaction_builder::{Burn, Requirement, TransactionBuilder, TransactionBuilderError}, secret::types::InputSigningData, }, types::block::{ @@ -14,6 +14,7 @@ use iota_sdk::{ payload::signed_transaction::{TransactionCapabilities, TransactionCapabilityFlag}, protocol::iota_mainnet_protocol_parameters, rand::output::{rand_output_id_with_slot_index, rand_output_metadata_with_id}, + semantic::TransactionFailureReason, }, }; use pretty_assertions::{assert_eq, assert_ne}; @@ -55,7 +56,7 @@ fn input_nft_eq_output_nft() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -63,7 +64,7 @@ fn input_nft_eq_output_nft() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -101,7 +102,7 @@ fn transition_nft_id_zero() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -109,7 +110,7 @@ fn transition_nft_id_zero() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -143,7 +144,7 @@ fn transition_nft_id_zero() { // None, // )]); -// let selected = InputSelection::new( +// let selected = TransactionBuilder::new( // inputs, // outputs, // [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -190,7 +191,7 @@ fn transition_nft_id_zero() { // None, // )]); -// let selected = InputSelection::new( +// let selected = TransactionBuilder::new( // inputs.clone(), // outputs, // [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -234,7 +235,7 @@ fn mint_nft() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -242,7 +243,7 @@ fn mint_nft() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -288,7 +289,7 @@ fn burn_nft() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -297,7 +298,7 @@ fn burn_nft() { protocol_parameters, ) .with_burn(Burn::new().add_nft(nft_id_2)) - .select() + .finish() .unwrap(); assert_eq!( @@ -336,7 +337,7 @@ fn burn_nft() { // None, // )]); -// let selected = InputSelection::new( +// let selected = TransactionBuilder::new( // inputs, // outputs, // [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -383,7 +384,7 @@ fn missing_input_for_nft_output() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -391,11 +392,11 @@ fn missing_input_for_nft_output() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::UnfulfillableRequirement(Requirement::Nft(nft_id))) if nft_id == nft_id_2 + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Nft(nft_id))) if nft_id == nft_id_2 )); } @@ -429,7 +430,7 @@ fn missing_input_for_nft_output_but_created() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -437,7 +438,7 @@ fn missing_input_for_nft_output_but_created() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(selected.is_ok()); } @@ -497,7 +498,7 @@ fn nft_in_output_and_sender() { }, ]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -505,7 +506,7 @@ fn nft_in_output_and_sender() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -550,7 +551,7 @@ fn missing_ed25519_sender() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -558,11 +559,11 @@ fn missing_ed25519_sender() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::UnfulfillableRequirement(Requirement::Sender(sender))) if sender == Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap() + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender(sender))) if sender == Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap() )); } @@ -596,7 +597,7 @@ fn missing_ed25519_issuer_created() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -604,11 +605,11 @@ fn missing_ed25519_issuer_created() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::UnfulfillableRequirement(Requirement::Issuer(issuer))) if issuer == Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap() + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Issuer(issuer))) if issuer == Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap() )); } @@ -642,7 +643,7 @@ fn missing_ed25519_issuer_transition() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -650,7 +651,7 @@ fn missing_ed25519_issuer_transition() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(selected.is_ok()); } @@ -685,7 +686,7 @@ fn missing_account_sender() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -693,11 +694,11 @@ fn missing_account_sender() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::UnfulfillableRequirement(Requirement::Sender(sender))) if sender == Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap() + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender(sender))) if sender == Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap() )); } @@ -731,7 +732,7 @@ fn missing_account_issuer_created() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -739,11 +740,11 @@ fn missing_account_issuer_created() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::UnfulfillableRequirement(Requirement::Issuer(issuer))) if issuer == Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap() + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Issuer(issuer))) if issuer == Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap() )); } @@ -777,7 +778,7 @@ fn missing_account_issuer_transition() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -785,7 +786,7 @@ fn missing_account_issuer_transition() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(selected.is_ok()); } @@ -820,7 +821,7 @@ fn missing_nft_sender() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -828,11 +829,11 @@ fn missing_nft_sender() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::UnfulfillableRequirement(Requirement::Sender(sender))) if sender == Address::try_from_bech32(BECH32_ADDRESS_NFT_1).unwrap() + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender(sender))) if sender == Address::try_from_bech32(BECH32_ADDRESS_NFT_1).unwrap() )); } @@ -866,7 +867,7 @@ fn missing_nft_issuer_created() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -874,11 +875,11 @@ fn missing_nft_issuer_created() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::UnfulfillableRequirement(Requirement::Issuer(issuer))) if issuer == Address::try_from_bech32(BECH32_ADDRESS_NFT_1).unwrap() + Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Issuer(issuer))) if issuer == Address::try_from_bech32(BECH32_ADDRESS_NFT_1).unwrap() )); } @@ -912,7 +913,7 @@ fn missing_nft_issuer_transition() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -920,7 +921,7 @@ fn missing_nft_issuer_transition() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(selected.is_ok()); } @@ -969,7 +970,7 @@ fn increase_nft_amount() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -977,7 +978,7 @@ fn increase_nft_amount() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1028,7 +1029,7 @@ fn decrease_nft_amount() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1036,7 +1037,7 @@ fn decrease_nft_amount() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -1099,7 +1100,7 @@ fn prefer_basic_to_nft() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1107,7 +1108,7 @@ fn prefer_basic_to_nft() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -1159,7 +1160,7 @@ fn take_amount_from_nft_to_fund_basic() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1167,7 +1168,7 @@ fn take_amount_from_nft_to_fund_basic() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1231,7 +1232,7 @@ fn nft_burn_should_validate_nft_sender() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1240,7 +1241,7 @@ fn nft_burn_should_validate_nft_sender() { protocol_parameters, ) .with_burn(Burn::new().add_nft(nft_id_1)) - .select() + .finish() .unwrap(); assert_eq!( @@ -1295,7 +1296,7 @@ fn nft_burn_should_validate_nft_address() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1304,7 +1305,7 @@ fn nft_burn_should_validate_nft_address() { protocol_parameters, ) .with_burn(Burn::new().add_nft(nft_id_1)) - .select() + .finish() .unwrap(); assert_eq!( @@ -1345,7 +1346,7 @@ fn transitioned_zero_nft_id_no_longer_is_zero() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1353,7 +1354,7 @@ fn transitioned_zero_nft_id_no_longer_is_zero() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -1423,7 +1424,7 @@ fn changed_immutable_metadata() { let outputs = [updated_nft_output]; - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1431,14 +1432,13 @@ fn changed_immutable_metadata() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish() + .unwrap_err(); - assert!(matches!( + assert_eq!( selected, - Err(Error::UnfulfillableRequirement(Requirement::Nft( - nft_id, - ))) if nft_id == nft_id_1 - )); + TransactionBuilderError::Semantic(TransactionFailureReason::ChainOutputImmutableFeaturesChanged) + ); } #[test] @@ -1464,7 +1464,7 @@ fn auto_transition_nft_less_than_min() { Some(SLOT_INDEX), ); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), None, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1473,7 +1473,7 @@ fn auto_transition_nft_less_than_min() { protocol_parameters.clone(), ) .with_required_inputs([*inputs[0].output_id()]) - .select() + .finish() .unwrap_err(); let min_amount = NftOutputBuilder::from(inputs[0].output.as_nft()) @@ -1484,7 +1484,7 @@ fn auto_transition_nft_less_than_min() { assert_eq!( selected, - Error::InsufficientAmount { + TransactionBuilderError::InsufficientAmount { found: small_amount, required: min_amount }, @@ -1528,7 +1528,7 @@ fn auto_transition_nft_less_than_min_additional() { Some(SLOT_INDEX), ); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), None, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -1537,7 +1537,7 @@ fn auto_transition_nft_less_than_min_additional() { protocol_parameters.clone(), ) .with_required_inputs([*inputs[0].output_id()]) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); diff --git a/sdk/tests/client/input_selection/outputs.rs b/sdk/tests/client/transaction_builder/outputs.rs similarity index 91% rename from sdk/tests/client/input_selection/outputs.rs rename to sdk/tests/client/transaction_builder/outputs.rs index 64bc74bc10..df100cc656 100644 --- a/sdk/tests/client/input_selection/outputs.rs +++ b/sdk/tests/client/transaction_builder/outputs.rs @@ -5,7 +5,7 @@ use std::{collections::HashSet, str::FromStr}; use iota_sdk::{ client::{ - api::input_selection::{Burn, Error, InputSelection}, + api::transaction_builder::{Burn, TransactionBuilder, TransactionBuilderError}, secret::types::InputSigningData, }, types::block::{ @@ -39,7 +39,7 @@ fn no_inputs() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -47,9 +47,12 @@ fn no_inputs() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); - assert!(matches!(selected, Err(Error::NoAvailableInputsProvided))); + assert!(matches!( + selected, + Err(TransactionBuilderError::NoAvailableInputsProvided) + )); } #[test] @@ -73,7 +76,7 @@ fn no_outputs() { ); let outputs = Vec::new(); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -81,9 +84,9 @@ fn no_outputs() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); - assert!(matches!(selected, Err(Error::InvalidOutputCount(0)))); + assert!(matches!(selected, Err(TransactionBuilderError::InvalidOutputCount(0)))); } #[test] @@ -107,7 +110,7 @@ fn no_outputs_but_required_input() { ); let outputs = Vec::new(); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -116,7 +119,7 @@ fn no_outputs_but_required_input() { protocol_parameters, ) .with_required_inputs(HashSet::from([*inputs[0].output_id()])) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data, inputs); @@ -150,7 +153,7 @@ fn no_outputs_but_burn() { ); let outputs = Vec::new(); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -159,7 +162,7 @@ fn no_outputs_but_burn() { protocol_parameters, ) .with_burn(Burn::new().add_account(account_id_2)) - .select() + .finish() .unwrap(); assert_eq!( @@ -205,7 +208,7 @@ fn no_address_provided() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, None, @@ -213,9 +216,12 @@ fn no_address_provided() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); - assert!(matches!(selected, Err(Error::NoAvailableInputsProvided))); + assert!(matches!( + selected, + Err(TransactionBuilderError::NoAvailableInputsProvided) + )); } #[test] @@ -247,7 +253,7 @@ fn no_matching_address_provided() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap()], @@ -255,9 +261,12 @@ fn no_matching_address_provided() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); - assert!(matches!(selected, Err(Error::NoAvailableInputsProvided))); + assert!(matches!( + selected, + Err(TransactionBuilderError::NoAvailableInputsProvided) + )); } #[test] @@ -303,7 +312,7 @@ fn two_addresses_one_missing() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -311,11 +320,11 @@ fn two_addresses_one_missing() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::InsufficientAmount { + Err(TransactionBuilderError::InsufficientAmount { found: 1_000_000, required: 2_000_000, }) @@ -365,7 +374,7 @@ fn two_addresses() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [ @@ -376,7 +385,7 @@ fn two_addresses() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -428,7 +437,7 @@ fn consolidate_with_min_allotment() { }) .collect::>(); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), None, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -438,7 +447,7 @@ fn consolidate_with_min_allotment() { ) .with_min_mana_allotment(account_id_1, 10) .with_required_inputs(inputs.iter().map(|i| *i.output_id())) - .select() + .finish() .unwrap(); assert_eq!(selected.transaction.outputs().len(), 1); diff --git a/sdk/tests/client/input_selection/storage_deposit_return.rs b/sdk/tests/client/transaction_builder/storage_deposit_return.rs similarity index 96% rename from sdk/tests/client/input_selection/storage_deposit_return.rs rename to sdk/tests/client/transaction_builder/storage_deposit_return.rs index 40a8c7a29c..217b2bab1c 100644 --- a/sdk/tests/client/input_selection/storage_deposit_return.rs +++ b/sdk/tests/client/transaction_builder/storage_deposit_return.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use iota_sdk::{ - client::api::input_selection::{Error, InputSelection}, + client::api::transaction_builder::{TransactionBuilder, TransactionBuilderError}, types::block::{address::Address, output::AccountId, protocol::iota_mainnet_protocol_parameters}, }; use pretty_assertions::assert_eq; @@ -45,7 +45,7 @@ fn sdruc_output_not_provided_no_remainder() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -53,7 +53,7 @@ fn sdruc_output_not_provided_no_remainder() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -111,7 +111,7 @@ fn sdruc_output_provided_no_remainder() { }, ]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -119,7 +119,7 @@ fn sdruc_output_provided_no_remainder() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -155,7 +155,7 @@ fn sdruc_output_provided_remainder() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -163,7 +163,7 @@ fn sdruc_output_provided_remainder() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -224,7 +224,7 @@ fn two_sdrucs_to_the_same_address_both_needed() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -232,7 +232,7 @@ fn two_sdrucs_to_the_same_address_both_needed() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -293,7 +293,7 @@ fn two_sdrucs_to_the_same_address_one_needed() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -301,7 +301,7 @@ fn two_sdrucs_to_the_same_address_one_needed() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -363,7 +363,7 @@ fn two_sdrucs_to_different_addresses_both_needed() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -371,7 +371,7 @@ fn two_sdrucs_to_different_addresses_both_needed() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -444,7 +444,7 @@ fn two_sdrucs_to_different_addresses_one_needed() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -452,7 +452,7 @@ fn two_sdrucs_to_different_addresses_one_needed() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -500,7 +500,7 @@ fn insufficient_amount_because_of_sdruc() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -508,11 +508,11 @@ fn insufficient_amount_because_of_sdruc() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select(); + .finish(); assert!(matches!( selected, - Err(Error::InsufficientAmount { + Err(TransactionBuilderError::InsufficientAmount { found: 2_000_000, required: 3_000_000, }) @@ -562,7 +562,7 @@ fn useless_sdruc_required_for_sender_feature() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [ @@ -573,7 +573,7 @@ fn useless_sdruc_required_for_sender_feature() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -633,7 +633,7 @@ fn sdruc_required_non_ed25519_in_address_unlock() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -641,7 +641,7 @@ fn sdruc_required_non_ed25519_in_address_unlock() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -713,7 +713,7 @@ fn useless_sdruc_non_ed25519_in_address_unlock() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -721,7 +721,7 @@ fn useless_sdruc_non_ed25519_in_address_unlock() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 2); diff --git a/sdk/tests/client/input_selection/timelock.rs b/sdk/tests/client/transaction_builder/timelock.rs similarity index 93% rename from sdk/tests/client/input_selection/timelock.rs rename to sdk/tests/client/transaction_builder/timelock.rs index 214a92f6df..328c580ede 100644 --- a/sdk/tests/client/input_selection/timelock.rs +++ b/sdk/tests/client/transaction_builder/timelock.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use iota_sdk::{ - client::api::input_selection::{Error, InputSelection}, + client::api::transaction_builder::{TransactionBuilder, TransactionBuilderError}, types::block::{ address::Address, protocol::iota_mainnet_protocol_parameters, @@ -44,7 +44,7 @@ fn one_output_timelock_not_expired() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs, outputs, [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -52,9 +52,12 @@ fn one_output_timelock_not_expired() { SlotCommitmentHash::null().into_slot_commitment_id(99), protocol_parameters, ) - .select(); + .finish(); - assert!(matches!(selected, Err(Error::NoAvailableInputsProvided))); + assert!(matches!( + selected, + Err(TransactionBuilderError::NoAvailableInputsProvided) + )); } #[test] @@ -86,7 +89,7 @@ fn timelock_equal_timestamp() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -94,7 +97,7 @@ fn timelock_equal_timestamp() { SlotCommitmentHash::null().into_slot_commitment_id(199), protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); @@ -144,7 +147,7 @@ fn two_outputs_one_timelock_expired() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -152,7 +155,7 @@ fn two_outputs_one_timelock_expired() { SlotCommitmentHash::null().into_slot_commitment_id(99), protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -203,7 +206,7 @@ fn two_outputs_one_timelocked_one_missing() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -211,7 +214,7 @@ fn two_outputs_one_timelocked_one_missing() { SlotCommitmentHash::null().into_slot_commitment_id(99), protocol_parameters, ) - .select() + .finish() .unwrap(); assert_eq!(selected.inputs_data.len(), 1); @@ -248,7 +251,7 @@ fn one_output_timelock_expired() { expiration: None, }]); - let selected = InputSelection::new( + let selected = TransactionBuilder::new( inputs.clone(), outputs.clone(), [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], @@ -256,7 +259,7 @@ fn one_output_timelock_expired() { SlotCommitmentHash::null().into_slot_commitment_id(99), protocol_parameters, ) - .select() + .finish() .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); diff --git a/sdk/tests/wallet/burn_outputs.rs b/sdk/tests/wallet/burn_outputs.rs index 37b1b9e73f..7d7dd7209b 100644 --- a/sdk/tests/wallet/burn_outputs.rs +++ b/sdk/tests/wallet/burn_outputs.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use iota_sdk::{ - client::api::input_selection::Burn, + client::api::transaction_builder::Burn, types::block::output::{ feature::MetadataFeature, unlock_condition::{AddressUnlockCondition, ExpirationUnlockCondition}, diff --git a/sdk/tests/wallet/claim_outputs.rs b/sdk/tests/wallet/claim_outputs.rs index 6ef0a5c1e4..b30cad0626 100644 --- a/sdk/tests/wallet/claim_outputs.rs +++ b/sdk/tests/wallet/claim_outputs.rs @@ -2,11 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 use iota_sdk::{ + client::api::options::TransactionOptions, types::block::output::{ unlock_condition::{AddressUnlockCondition, ExpirationUnlockCondition}, BasicOutputBuilder, NativeToken, NftId, NftOutputBuilder, UnlockCondition, }, - wallet::{CreateNativeTokenParams, OutputsToClaim, SendNativeTokenParams, SendParams, TransactionOptions}, + wallet::{CreateNativeTokenParams, OutputsToClaim, SendNativeTokenParams, SendParams}, U256, }; use pretty_assertions::assert_eq; diff --git a/sdk/tests/wallet/events.rs b/sdk/tests/wallet/events.rs index fd029fde36..34219b52d0 100644 --- a/sdk/tests/wallet/events.rs +++ b/sdk/tests/wallet/events.rs @@ -85,7 +85,7 @@ fn wallet_events_serde() { })); assert_serde_eq(WalletEvent::TransactionProgress( - TransactionProgressEvent::SelectingInputs, + TransactionProgressEvent::BuildingTransaction, )); assert_serde_eq(WalletEvent::TransactionProgress(