From 9e708325eadee45e01c495a68e51e2adb70e3689 Mon Sep 17 00:00:00 2001 From: Marc Espin Date: Tue, 5 Mar 2024 14:33:40 +0100 Subject: [PATCH 01/21] feat: Wallet events improvements (#2100) * feat: Reorder wallet events and add a new block event * Add `BlockSigningHash` and update wallet events tests * update comment * add missing unlocks * I hope this works * fixes and improvements * fix typo in nodejs bindings * emit broadcasting event after block signing and before emitting * python events * revert wallet events * improvements * use HexEncodedString in ts * clean up --- bindings/nodejs/lib/types/wallet/event.ts | 36 +++++++++---- sdk/src/wallet/events/types.rs | 54 ++++++++++++++----- sdk/src/wallet/operations/block.rs | 12 +++++ .../transaction/submit_transaction.rs | 6 --- sdk/tests/wallet/events.rs | 47 ++++++++++++++-- 5 files changed, 121 insertions(+), 34 deletions(-) diff --git a/bindings/nodejs/lib/types/wallet/event.ts b/bindings/nodejs/lib/types/wallet/event.ts index 10b8884e79..b27da01efd 100644 --- a/bindings/nodejs/lib/types/wallet/event.ts +++ b/bindings/nodejs/lib/types/wallet/event.ts @@ -121,12 +121,14 @@ enum TransactionProgressType { GeneratingRemainderDepositAddress = 1, /** Prepared transaction. */ PreparedTransaction = 2, - /** Prepared transaction signing hash hex encoded, required for blindsigning with a Ledger Nano. */ - PreparedTransactionSigningHash = 3, /** Signing the transaction. */ - SigningTransaction = 4, + SigningTransaction = 3, + /** Prepared transaction signing hash hex encoded, required for blindsigning with a Ledger Nano. */ + PreparedTransactionSigningHash = 4, + /** Prepared block signing input, required for blindsigning with a Ledger Nano. */ + PreparedBlockSigningInput = 5, /** Broadcasting. */ - Broadcasting = 5, + Broadcasting = 6, } /** @@ -207,6 +209,15 @@ class PreparedTransactionProgress extends TransactionProgress { } } +/** + * A 'signing transaction' progress. + */ +class SigningTransactionProgress extends TransactionProgress { + constructor() { + super(TransactionProgressType.SigningTransaction); + } +} + /** * A 'prepared transaction hash' progress. */ @@ -223,11 +234,17 @@ class PreparedTransactionSigningHashProgress extends TransactionProgress { } /** - * A 'signing transaction' progress. + * A 'prepared block input' progress. */ -class SigningTransactionProgress extends TransactionProgress { - constructor() { - super(TransactionProgressType.SigningTransaction); +class PreparedBlockSigningInputProgress extends TransactionProgress { + blockSigningInput: HexEncodedString; + + /** + * @param signingHash The signing hash of the block. + */ + constructor(signingInput: HexEncodedString) { + super(TransactionProgressType.PreparedBlockSigningInput); + this.blockSigningInput = signingInput; } } @@ -252,8 +269,9 @@ export { SelectingInputsProgress, GeneratingRemainderDepositAddressProgress, PreparedTransactionProgress, - PreparedTransactionSigningHashProgress, SigningTransactionProgress, + PreparedTransactionSigningHashProgress, + PreparedBlockSigningInputProgress, BroadcastingProgress, TransactionProgressType, }; diff --git a/sdk/src/wallet/events/types.rs b/sdk/src/wallet/events/types.rs index 1c25a29efc..b477c8e20b 100644 --- a/sdk/src/wallet/events/types.rs +++ b/sdk/src/wallet/events/types.rs @@ -198,10 +198,12 @@ pub enum TransactionProgressEvent { GeneratingRemainderDepositAddress(AddressData), /// Prepared transaction. PreparedTransaction(Box), - /// Prepared transaction signing hash hex encoded, required for blindsigning with a ledger nano - PreparedTransactionSigningHash(String), /// Signing the transaction. SigningTransaction, + /// Prepared transaction signing hash hex encoded, required for blindsigning with a ledger nano + PreparedTransactionSigningHash(String), + /// Prepared block signing input, required for blind signing with ledger nano + PreparedBlockSigningInput(String), /// Broadcasting. Broadcasting, } @@ -217,15 +219,22 @@ impl Serialize for TransactionProgressEvent { signing_hash: &'a str, } + #[derive(Serialize)] + #[serde(rename_all = "camelCase")] + struct PreparedBlockSigningInput_<'a> { + block_signing_input: &'a str, + } + #[derive(Serialize)] #[serde(untagged)] enum TransactionProgressEvent_<'a> { T0, T1(&'a AddressData), T2(&'a PreparedTransactionDataDto), - T3(PreparedTransactionSigningHash_<'a>), - T4, - T5, + T3, + T4(PreparedTransactionSigningHash_<'a>), + T5(PreparedBlockSigningInput_<'a>), + T6, } #[derive(Serialize)] struct TypedTransactionProgressEvent_<'a> { @@ -247,17 +256,21 @@ impl Serialize for TransactionProgressEvent { kind: 2, event: TransactionProgressEvent_::T2(e), }, - Self::PreparedTransactionSigningHash(e) => TypedTransactionProgressEvent_ { + Self::SigningTransaction => TypedTransactionProgressEvent_ { kind: 3, - event: TransactionProgressEvent_::T3(PreparedTransactionSigningHash_ { signing_hash: e }), + event: TransactionProgressEvent_::T3, }, - Self::SigningTransaction => TypedTransactionProgressEvent_ { + Self::PreparedTransactionSigningHash(e) => TypedTransactionProgressEvent_ { kind: 4, - event: TransactionProgressEvent_::T4, + event: TransactionProgressEvent_::T4(PreparedTransactionSigningHash_ { signing_hash: e }), }, - Self::Broadcasting => TypedTransactionProgressEvent_ { + Self::PreparedBlockSigningInput(e) => TypedTransactionProgressEvent_ { kind: 5, - event: TransactionProgressEvent_::T5, + event: TransactionProgressEvent_::T5(PreparedBlockSigningInput_ { block_signing_input: e }), + }, + Self::Broadcasting => TypedTransactionProgressEvent_ { + kind: 6, + event: TransactionProgressEvent_::T6, }, }; event.serialize(serializer) @@ -272,6 +285,12 @@ impl<'de> Deserialize<'de> for TransactionProgressEvent { signing_hash: String, } + #[derive(Deserialize)] + #[serde(rename_all = "camelCase")] + struct PreparedBlockSigningInput_ { + block_signing_input: String, + } + let value = serde_json::Value::deserialize(d)?; Ok( match value @@ -287,15 +306,22 @@ impl<'de> Deserialize<'de> for TransactionProgressEvent { 2 => Self::PreparedTransaction(Box::new(PreparedTransactionDataDto::deserialize(value).map_err( |e| serde::de::Error::custom(format!("cannot deserialize PreparedTransactionDataDto: {e}")), )?)), - 3 => Self::PreparedTransactionSigningHash( + 3 => Self::SigningTransaction, + 4 => Self::PreparedTransactionSigningHash( PreparedTransactionSigningHash_::deserialize(value) .map_err(|e| { serde::de::Error::custom(format!("cannot deserialize PreparedTransactionSigningHash: {e}")) })? .signing_hash, ), - 4 => Self::SigningTransaction, - 5 => Self::Broadcasting, + 5 => Self::PreparedBlockSigningInput( + PreparedBlockSigningInput_::deserialize(value) + .map_err(|e| { + serde::de::Error::custom(format!("cannot deserialize PreparedBlockSigningInput: {e}")) + })? + .block_signing_input, + ), + 6 => Self::Broadcasting, _ => return Err(serde::de::Error::custom("invalid transaction progress event type")), }, ) diff --git a/sdk/src/wallet/operations/block.rs b/sdk/src/wallet/operations/block.rs index e16d5e4495..bf3ea48ce2 100644 --- a/sdk/src/wallet/operations/block.rs +++ b/sdk/src/wallet/operations/block.rs @@ -1,6 +1,8 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +#[cfg(feature = "events")] +use crate::wallet::events::types::{TransactionProgressEvent, WalletEvent}; use crate::{ client::{ secret::{SecretManage, SignBlock}, @@ -46,6 +48,12 @@ where } } + #[cfg(feature = "events")] + self.emit(WalletEvent::TransactionProgress( + TransactionProgressEvent::PreparedBlockSigningInput(prefix_hex::encode(unsigned_block.signing_input())), + )) + .await; + let block = unsigned_block .sign_ed25519( &*self.get_secret_manager().read().await, @@ -53,6 +61,10 @@ where ) .await?; + #[cfg(feature = "events")] + self.emit(WalletEvent::TransactionProgress(TransactionProgressEvent::Broadcasting)) + .await; + let block_id = self.client().post_block(&block).await?; log::debug!("submitted block {}", block_id); diff --git a/sdk/src/wallet/operations/transaction/submit_transaction.rs b/sdk/src/wallet/operations/transaction/submit_transaction.rs index 4fa6737eda..994299045a 100644 --- a/sdk/src/wallet/operations/transaction/submit_transaction.rs +++ b/sdk/src/wallet/operations/transaction/submit_transaction.rs @@ -1,8 +1,6 @@ // Copyright 2021 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -#[cfg(feature = "events")] -use crate::wallet::events::types::{TransactionProgressEvent, WalletEvent}; use crate::{ client::{secret::SecretManage, ClientError}, types::block::{output::AccountId, payload::Payload, BlockId}, @@ -22,10 +20,6 @@ where ) -> Result { log::debug!("[TRANSACTION] submit_signed_transaction"); - #[cfg(feature = "events")] - self.emit(WalletEvent::TransactionProgress(TransactionProgressEvent::Broadcasting)) - .await; - self.submit_basic_block(Some(Payload::from(payload)), issuer_id, true) .await } diff --git a/sdk/tests/wallet/events.rs b/sdk/tests/wallet/events.rs index 2293698ee1..fd029fde36 100644 --- a/sdk/tests/wallet/events.rs +++ b/sdk/tests/wallet/events.rs @@ -5,18 +5,27 @@ use iota_sdk::{ client::api::PreparedTransactionDataDto, types::block::{ address::{Address, Bech32Address, Ed25519Address}, + core::{ + basic::{MaxBurnedManaAmount, StrongParents}, + BlockHeader, + }, input::{Input, UtxoInput}, output::{ - unlock_condition::AddressUnlockCondition, BasicOutput, LeafHash, Output, OutputCommitmentProof, + unlock_condition::AddressUnlockCondition, AccountId, BasicOutput, LeafHash, Output, OutputCommitmentProof, OutputIdProof, }, - payload::signed_transaction::{Transaction, TransactionHash, TransactionId}, + payload::{ + signed_transaction::{Transaction, TransactionHash, TransactionId}, + Payload, SignedTransactionPayload, + }, protocol::iota_mainnet_protocol_parameters, rand::{ mana::rand_mana_allotment, output::{rand_basic_output, rand_output_metadata}, }, - slot::SlotIndex, + slot::{SlotCommitmentId, SlotIndex}, + unlock::{EmptyUnlock, Unlock, Unlocks}, + BlockBody, BlockId, UnsignedBlock, }, wallet::{ events::types::{ @@ -115,14 +124,42 @@ fn wallet_events_serde() { mana_rewards: Default::default(), })), )); + + let block_payload = SignedTransactionPayload::new( + transaction, + Unlocks::new([Unlock::Empty(EmptyUnlock), Unlock::Empty(EmptyUnlock)]).unwrap(), + ) + .unwrap(); + let payload = Payload::from(block_payload); + let block = UnsignedBlock::new( + BlockHeader::new( + protocol_parameters.version(), + protocol_parameters.network_id(), + 0u64, + SlotCommitmentId::new([0; 36]), + SlotIndex(0), + AccountId::new([0; 32]), + ), + BlockBody::build_basic( + StrongParents::from_vec(vec![BlockId::new([0; 36])]).unwrap(), + MaxBurnedManaAmount::Amount(0), + ) + .with_payload(payload) + .finish_block_body() + .unwrap(), + ); + + assert_serde_eq(WalletEvent::TransactionProgress( + TransactionProgressEvent::PreparedBlockSigningInput(prefix_hex::encode(block.signing_input())), + )); } assert_serde_eq(WalletEvent::TransactionProgress( - TransactionProgressEvent::PreparedTransactionSigningHash(ED25519_ADDRESS.to_string()), + TransactionProgressEvent::SigningTransaction, )); assert_serde_eq(WalletEvent::TransactionProgress( - TransactionProgressEvent::SigningTransaction, + TransactionProgressEvent::PreparedTransactionSigningHash(ED25519_ADDRESS.to_string()), )); assert_serde_eq(WalletEvent::TransactionProgress(TransactionProgressEvent::Broadcasting)); From 12e5ec8d3da4ef26e50c85c01dff061d0e30cc83 Mon Sep 17 00:00:00 2001 From: /alex/ Date: Tue, 5 Mar 2024 14:47:28 +0100 Subject: [PATCH 02/21] Validator route pagination (#2101) * little improvements * add get_validators example with optional pagination * the example * fix example doc * update * python: add example * nodejs: add example * update year Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> * rust: update example * python: update example * nodejs: update example * python: nit * docs * fix * single letter closures --------- Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> Co-authored-by: Thibault Martinez --- .../nodejs/examples/client/get-validators.ts | 42 +++++++++++++++++++ .../python/examples/client/get_validators.py | 24 +++++++++++ sdk/Cargo.toml | 5 +++ .../client/node_api_core/get_validators.rs | 40 ++++++++++++++++++ sdk/src/client/node_api/core/routes.rs | 6 +-- sdk/src/client/node_api/mod.rs | 4 +- sdk/src/types/api/core.rs | 22 +++++----- 7 files changed, 127 insertions(+), 16 deletions(-) create mode 100644 bindings/nodejs/examples/client/get-validators.ts create mode 100644 bindings/python/examples/client/get_validators.py create mode 100644 sdk/examples/client/node_api_core/get_validators.rs diff --git a/bindings/nodejs/examples/client/get-validators.ts b/bindings/nodejs/examples/client/get-validators.ts new file mode 100644 index 0000000000..aab1570964 --- /dev/null +++ b/bindings/nodejs/examples/client/get-validators.ts @@ -0,0 +1,42 @@ +// Copyright 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { Client, initLogger } from '@iota/sdk'; +require('dotenv').config({ path: '.env' }); + +// Run with command: +// yarn run-example ./client/get-validators.ts [PAGE_SIZE] [CURSOR] + +// This example returns the validators known by the node by querying the corresponding endpoint. +// You can provide a custom PAGE_SIZE and additionally a CURSOR from a previous request. +async function run() { + initLogger(); + for (const envVar of ['NODE_URL']) { + if (!(envVar in process.env)) { + throw new Error(`.env ${envVar} is undefined, see .env.example`); + } + } + + const client = await Client.create({ + // Insert your node URL in the .env. + nodes: [process.env.NODE_URL as string], + }); + + let pageSize = 1; + let cursor = ''; + if (process.argv.length > 1) { + pageSize = parseInt(process.argv[2]); + if (process.argv.length > 2) { + cursor = process.argv[3]; + } + } + + try { + const validators = await client.getValidators(pageSize, cursor); + console.log(validators); + } catch (error) { + console.error('Error: ', error); + } +} + +void run().then(() => process.exit()); diff --git a/bindings/python/examples/client/get_validators.py b/bindings/python/examples/client/get_validators.py new file mode 100644 index 0000000000..6a1a883197 --- /dev/null +++ b/bindings/python/examples/client/get_validators.py @@ -0,0 +1,24 @@ +import dataclasses +import json +import os +import sys + +from dotenv import load_dotenv +from iota_sdk import Client + +load_dotenv() + +node_url = os.environ.get('NODE_URL', 'https://api.testnet.shimmer.network') +page_size = 1 +cursor = "" + +if len(sys.argv) > 1: + page_size = int(sys.argv[1]) + if len(sys.argv) > 2: + cursor = sys.argv[2] + +# Create a Client instance +client = Client(nodes=[node_url]) + +validators = client.get_validators(page_size, cursor) +print(f'{json.dumps(dataclasses.asdict(validators), indent=4)}') diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 1bc2f68937..b5da33b071 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -485,6 +485,11 @@ name = "node_api_core_get_included_block_raw" path = "examples/client/node_api_core/16_get_included_block_raw.rs" required-features = ["client"] +[[example]] +name = "node_api_core_get_validators" +path = "examples/client/node_api_core/get_validators.rs" +required-features = ["client"] + # Node API indexer examples [[example]] diff --git a/sdk/examples/client/node_api_core/get_validators.rs b/sdk/examples/client/node_api_core/get_validators.rs new file mode 100644 index 0000000000..7467d4f149 --- /dev/null +++ b/sdk/examples/client/node_api_core/get_validators.rs @@ -0,0 +1,40 @@ +// Copyright 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +//! This example returns the validators known by the node by querying the corresponding endpoint. +//! You can provide a custom PAGE_SIZE and additionally a CURSOR from a previous request. +//! +//! Rename `.env.example` to `.env` first, then run the command: +//! ```sh +//! cargo run --release --all-features --example node_api_core_get_validators [PAGE_SIZE] [CURSOR] [NODE_URL] +//! ``` + +use iota_sdk::client::{Client, ClientError}; + +#[tokio::main] +async fn main() -> Result<(), ClientError> { + // If not provided we use the default node from the `.env` file. + dotenvy::dotenv().ok(); + + let page_size = std::env::args().nth(1).map(|s| s.parse::().unwrap()); + let cursor = std::env::args().nth(2); + + // Take the node URL from command line argument or use one from env as default. + let node_url = std::env::args() + .nth(3) + .unwrap_or_else(|| std::env::var("NODE_URL").expect("NODE_URL not set")); + + // Create a node client. + let client = Client::builder() + .with_node(&node_url)? + .with_ignore_node_health() + .finish() + .await?; + + // Get validators. + let validators = client.get_validators(page_size, cursor).await?; + + println!("{validators:#?}"); + + Ok(()) +} diff --git a/sdk/src/client/node_api/core/routes.rs b/sdk/src/client/node_api/core/routes.rs index 93309f2f99..798b73f61b 100644 --- a/sdk/src/client/node_api/core/routes.rs +++ b/sdk/src/client/node_api/core/routes.rs @@ -101,7 +101,7 @@ impl Client { ) -> Result { let bech32_address = account_id.to_bech32(self.get_bech32_hrp().await?); let path = &format!("api/core/v3/accounts/{bech32_address}/congestion"); - let query = query_tuples_to_query_string([work_score.into().map(|i| ("workScore", i.to_string()))]); + let query = query_tuples_to_query_string([work_score.into().map(|s| ("workScore", s.to_string()))]); self.get_request(path, query.as_deref(), false).await } @@ -138,8 +138,8 @@ impl Client { ) -> Result { const PATH: &str = "api/core/v3/validators"; let query = query_tuples_to_query_string([ - page_size.into().map(|i| ("pageSize", i.to_string())), - cursor.into().map(|i| ("cursor", i)), + page_size.into().map(|n| ("pageSize", n.to_string())), + cursor.into().map(|c| ("cursor", c)), ]); self.get_request(PATH, query.as_deref(), false).await diff --git a/sdk/src/client/node_api/mod.rs b/sdk/src/client/node_api/mod.rs index b88a53afae..6526a8a167 100644 --- a/sdk/src/client/node_api/mod.rs +++ b/sdk/src/client/node_api/mod.rs @@ -19,8 +19,8 @@ pub(crate) fn query_tuples_to_query_string( ) -> Option { let query = tuples .into_iter() - .filter_map(|tuple| tuple.map(|(key, value)| format!("{}={}", key, value))) + .filter_map(|tuple| tuple.map(|(key, value)| format!("{key}={value}"))) .collect::>(); - if query.is_empty() { None } else { Some(query.join("&")) } + (!query.is_empty()).then_some(query.join("&")) } diff --git a/sdk/src/types/api/core.rs b/sdk/src/types/api/core.rs index 2c3d49da62..a943e6ff9a 100644 --- a/sdk/src/types/api/core.rs +++ b/sdk/src/types/api/core.rs @@ -205,24 +205,24 @@ pub struct BaseTokenResponse { #[serde(rename_all = "camelCase")] pub struct ValidatorResponse { /// Account address of the validator. - address: Bech32Address, + pub address: Bech32Address, /// The epoch index until which the validator registered to stake. - staking_end_epoch: EpochIndex, + pub staking_end_epoch: EpochIndex, /// The total stake of the pool, including delegators. #[serde(with = "string")] - pool_stake: u64, + pub pool_stake: u64, /// The stake of a validator. #[serde(with = "string")] - validator_stake: u64, + pub validator_stake: u64, /// The fixed cost of the validator, which it receives as part of its Mana rewards. #[serde(with = "string")] - fixed_cost: u64, + pub fixed_cost: u64, /// Shows whether the validator was active recently. - active: bool, + pub active: bool, /// The latest protocol version the validator supported. - latest_supported_protocol_version: u8, + pub latest_supported_protocol_version: u8, /// The protocol hash of the latest supported protocol of the validator. - latest_supported_protocol_hash: ProtocolParametersHash, + pub latest_supported_protocol_hash: ProtocolParametersHash, } /// Response of GET /api/core/v3/blocks/validators. @@ -232,13 +232,13 @@ pub struct ValidatorResponse { #[serde(rename_all = "camelCase")] pub struct ValidatorsResponse { /// List of registered validators ready for the next epoch. - validators: Vec, + pub validators: Vec, /// The number of validators returned per one API request with pagination. - page_size: u32, + pub page_size: u32, /// The cursor that needs to be provided as cursor query parameter to request the next page. If empty, this was the /// last page. #[serde(default, skip_serializing_if = "Option::is_none")] - cursor: Option, + pub cursor: Option, } /// Response of GET /api/core/v3/rewards/{outputId}. From 8c83dcf5a652a3121b7518fd5b41f70d1b551884 Mon Sep 17 00:00:00 2001 From: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> Date: Tue, 5 Mar 2024 15:49:37 +0100 Subject: [PATCH 03/21] Rename inputs_from_transaction_id to get_transaction_inputs (#2129) * Rename inputs_from_transaction_id to get_transaction_inputs * Update sdk/tests/client/high_level.rs Co-authored-by: Thibault Martinez --------- Co-authored-by: Thibault Martinez --- sdk/Cargo.toml | 4 ++-- ...nputs_from_transaction_id.rs => get_transaction_inputs.rs} | 4 ++-- sdk/src/client/api/high_level.rs | 2 +- sdk/tests/client/high_level.rs | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) rename sdk/examples/client/high_level/{inputs_from_transaction_id.rs => get_transaction_inputs.rs} (88%) diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index b5da33b071..e66e4a34d1 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -409,8 +409,8 @@ required-features = ["client"] # High Level examples [[example]] -name = "inputs_from_transaction_id" -path = "examples/client/high_level/inputs_from_transaction_id.rs" +name = "get_transaction_inputs" +path = "examples/client/high_level/get_transaction_inputs.rs" required-features = ["client"] [[example]] diff --git a/sdk/examples/client/high_level/inputs_from_transaction_id.rs b/sdk/examples/client/high_level/get_transaction_inputs.rs similarity index 88% rename from sdk/examples/client/high_level/inputs_from_transaction_id.rs rename to sdk/examples/client/high_level/get_transaction_inputs.rs index 66ac9180ae..5cede3b3a6 100644 --- a/sdk/examples/client/high_level/inputs_from_transaction_id.rs +++ b/sdk/examples/client/high_level/get_transaction_inputs.rs @@ -7,7 +7,7 @@ //! //! Rename `.env.example` to `.env` first, then run the command: //! ```sh -//! cargo run --release --example inputs_from_transaction_id +//! cargo run --release --example get_transaction_inputs //! ``` use iota_sdk::{client::Client, types::block::payload::signed_transaction::TransactionId}; @@ -30,7 +30,7 @@ async fn main() -> Result<(), Box> { .expect("missing example argument: TRANSACTION ID") .parse::()?; - let inputs = client.inputs_from_transaction_id(&transaction_id).await?; + let inputs = client.get_transaction_inputs(&transaction_id).await?; println!("Transaction inputs:\n{:#?}", inputs); diff --git a/sdk/src/client/api/high_level.rs b/sdk/src/client/api/high_level.rs index b392f2ea23..67a7cee4da 100644 --- a/sdk/src/client/api/high_level.rs +++ b/sdk/src/client/api/high_level.rs @@ -25,7 +25,7 @@ use crate::{ impl Client { /// Get the inputs of a transaction for the given transaction id. - pub async fn inputs_from_transaction_id( + pub async fn get_transaction_inputs( &self, transaction_id: &TransactionId, ) -> Result, ClientError> { diff --git a/sdk/tests/client/high_level.rs b/sdk/tests/client/high_level.rs index c120a3215f..ba8e625e76 100644 --- a/sdk/tests/client/high_level.rs +++ b/sdk/tests/client/high_level.rs @@ -8,10 +8,10 @@ use crate::client::{common::setup_client_with_node_health_ignored, node_api::set #[ignore] #[tokio::test] -async fn test_find_inputs_from_transaction_id() { +async fn test_get_transaction_inputs() { let client = setup_client_with_node_health_ignored().await; let (_block_id, transaction_id) = setup_transaction_block(&client).await; - let inputs = client.inputs_from_transaction_id(&transaction_id).await.unwrap(); + let inputs = client.get_transaction_inputs(&transaction_id).await.unwrap(); assert_eq!(inputs.len(), 1); } From c8f368204a4c1ba2a512327292f91443f7643bc7 Mon Sep 17 00:00:00 2001 From: DaughterOfMars Date: Tue, 5 Mar 2024 10:14:56 -0500 Subject: [PATCH 04/21] Fix balance locking (#2116) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * block on balance only for non-wasm * unpub * move doc * fix balance and sync_pending_transactions locking * remove fn * check for staking --------- Co-authored-by: Thibault Martinez Co-authored-by: Thoralf Müller Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> --- sdk/src/wallet/core/mod.rs | 5 - sdk/src/wallet/operations/balance.rs | 552 +++++++++--------- .../wallet/operations/syncing/transactions.rs | 78 ++- 3 files changed, 324 insertions(+), 311 deletions(-) diff --git a/sdk/src/wallet/core/mod.rs b/sdk/src/wallet/core/mod.rs index fafdc5ee3f..f6bef8d88a 100644 --- a/sdk/src/wallet/core/mod.rs +++ b/sdk/src/wallet/core/mod.rs @@ -406,11 +406,6 @@ impl Wallet { self.ledger.write().await } - /// Get the wallet's ledger RwLock. - pub(crate) fn ledger_rw(&self) -> &Arc> { - &self.ledger - } - #[cfg(feature = "storage")] pub(crate) fn storage_manager(&self) -> &StorageManager { &self.storage_manager diff --git a/sdk/src/wallet/operations/balance.rs b/sdk/src/wallet/operations/balance.rs index f983b4b3d9..e137da3c04 100644 --- a/sdk/src/wallet/operations/balance.rs +++ b/sdk/src/wallet/operations/balance.rs @@ -1,6 +1,8 @@ // Copyright 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use std::collections::HashSet; + use primitive_types::U256; use crate::{ @@ -24,334 +26,330 @@ impl Wallet { let slot_index = self.client().get_slot_index().await?; let wallet_address = self.address().await; - let wallet_ledger = self.ledger_rw().clone(); + let wallet_ledger = self.ledger().await; let network_id = protocol_parameters.network_id(); let storage_score_params = protocol_parameters.storage_score_parameters(); - let client = self.client().clone(); - tokio::task::spawn_blocking(move || { - let wallet_ledger = wallet_ledger.blocking_read(); - let mut balance = Balance::default(); - let mut total_storage_cost = 0; - let mut total_native_tokens = NativeTokensBuilder::default(); + let mut balance = Balance::default(); + let mut total_storage_cost = 0; + let mut total_native_tokens = NativeTokensBuilder::default(); - #[cfg(feature = "participation")] - let voting_output = wallet_ledger.get_voting_output(); + #[cfg(feature = "participation")] + let voting_output = wallet_ledger.get_voting_output(); - let claimable_outputs = - wallet_ledger.claimable_outputs(&wallet_address, OutputsToClaim::All, slot_index, &protocol_parameters)?; + let claimable_outputs = + wallet_ledger.claimable_outputs(&wallet_address, OutputsToClaim::All, slot_index, &protocol_parameters)?; - #[cfg(feature = "participation")] - { - if let Some(voting_output) = &voting_output { - if voting_output.output.as_basic().address() == wallet_address.inner() { - balance.base_coin.voting_power = voting_output.output.amount(); - } + #[cfg(feature = "participation")] + { + if let Some(voting_output) = &voting_output { + if voting_output.output.as_basic().address() == wallet_address.inner() { + balance.base_coin.voting_power = voting_output.output.amount(); } } + } + + let mut reward_outputs = HashSet::new(); + + for (output_id, output_data) in &wallet_ledger.unspent_outputs { + // Check if output is from the network we're currently connected to + if output_data.network_id != network_id { + continue; + } + + let output = &output_data.output; + let storage_cost = output.minimum_amount(storage_score_params); + + // Add account, foundry, and delegation outputs here because they can't have a + // [`StorageDepositReturnUnlockCondition`] or time related unlock conditions + match output { + Output::Account(account) => { + // Add amount + balance.base_coin.total += account.amount(); + balance.mana.total += output.decayed_mana( + &protocol_parameters, + output_id.transaction_id().slot_index(), + slot_index, + )?; + // Add mana rewards + if account.features().staking().is_some() { + reward_outputs.insert(*output_id); + } + + // Add storage deposit + balance.required_storage_deposit.account += storage_cost; + if !wallet_ledger.locked_outputs.contains(output_id) { + total_storage_cost += storage_cost; + } - for (output_id, output_data) in &wallet_ledger.unspent_outputs { - // Check if output is from the network we're currently connected to - if output_data.network_id != network_id { - continue; + let account_id = account.account_id_non_null(output_id); + balance.accounts.insert(account_id); } + Output::Foundry(foundry) => { + // Add amount + balance.base_coin.total += foundry.amount(); + // Add storage deposit + balance.required_storage_deposit.foundry += storage_cost; + if !wallet_ledger.locked_outputs.contains(output_id) { + total_storage_cost += storage_cost; + } + + // Add native token + if let Some(native_token) = output.native_token() { + total_native_tokens.add_native_token(*native_token)?; + } + + balance.foundries.insert(foundry.id()); + } + Output::Delegation(delegation) => { + // Add amount + balance.base_coin.total += delegation.amount(); + // Add mana rewards + reward_outputs.insert(*output_id); + // Add storage deposit + balance.required_storage_deposit.delegation += storage_cost; + if !wallet_ledger.locked_outputs.contains(output_id) { + total_storage_cost += storage_cost; + } - let output = &output_data.output; - let storage_cost = output.minimum_amount(storage_score_params); + let delegation_id = delegation.delegation_id_non_null(output_id); + balance.delegations.insert(delegation_id); + } + _ => { + // If there is only an [AddressUnlockCondition], then we can spend the output at any time + // without restrictions + if let [UnlockCondition::Address(_)] = output + .unlock_conditions() + .expect("output needs to have unlock conditions") + .as_ref() + { + // add nft_id for nft outputs + if let Output::Nft(nft) = &output { + let nft_id = nft.nft_id_non_null(output_id); + balance.nfts.insert(nft_id); + } - // Add account, foundry, and delegation outputs here because they can't have a - // [`StorageDepositReturnUnlockCondition`] or time related unlock conditions - match output { - Output::Account(account) => { // Add amount - balance.base_coin.total += account.amount(); + balance.base_coin.total += output.amount(); + // Add decayed mana balance.mana.total += output.decayed_mana( &protocol_parameters, output_id.transaction_id().slot_index(), slot_index, )?; - // Add mana rewards - // NOTE: Block here because we really don't want to yield to the executor while we are holding the - // ledger lock - if let Ok(response) = tokio::runtime::Handle::current() - .block_on(client.get_output_mana_rewards(output_id, slot_index)) - { - balance.mana.rewards += response.rewards; - } - - // Add storage deposit - balance.required_storage_deposit.account += storage_cost; - if !wallet_ledger.locked_outputs.contains(output_id) { - total_storage_cost += storage_cost; - } - let account_id = account.account_id_non_null(output_id); - balance.accounts.insert(account_id); - } - Output::Foundry(foundry) => { - // Add amount - balance.base_coin.total += foundry.amount(); // Add storage deposit - balance.required_storage_deposit.foundry += storage_cost; - if !wallet_ledger.locked_outputs.contains(output_id) { - total_storage_cost += storage_cost; + if output.is_basic() { + balance.required_storage_deposit.basic += storage_cost; + if output.native_token().is_some() && !wallet_ledger.locked_outputs.contains(output_id) { + total_storage_cost += storage_cost; + } + } else if output.is_nft() { + balance.required_storage_deposit.nft += storage_cost; + if !wallet_ledger.locked_outputs.contains(output_id) { + total_storage_cost += storage_cost; + } } // Add native token if let Some(native_token) = output.native_token() { total_native_tokens.add_native_token(*native_token)?; } - - balance.foundries.insert(foundry.id()); - } - Output::Delegation(delegation) => { - // Add amount - balance.base_coin.total += delegation.amount(); - // Add mana rewards - // NOTE: Block here because we really don't want to yield to the executor while we are holding the - // ledger lock - if let Ok(response) = tokio::runtime::Handle::current() - .block_on(client.get_output_mana_rewards(output_id, slot_index)) - { - balance.mana.rewards += response.rewards; - } - // Add storage deposit - balance.required_storage_deposit.delegation += storage_cost; - if !wallet_ledger.locked_outputs.contains(output_id) { - total_storage_cost += storage_cost; - } - - let delegation_id = delegation.delegation_id_non_null(output_id); - balance.delegations.insert(delegation_id); - } - _ => { - // If there is only an [AddressUnlockCondition], then we can spend the output at any time - // without restrictions - if let [UnlockCondition::Address(_)] = output - .unlock_conditions() - .expect("output needs to have unlock conditions") - .as_ref() - { - // add nft_id for nft outputs - if let Output::Nft(nft) = &output { - let nft_id = nft.nft_id_non_null(output_id); - balance.nfts.insert(nft_id); - } - - // Add amount - balance.base_coin.total += output.amount(); - // Add decayed mana - balance.mana.total += output.decayed_mana( - &protocol_parameters, - output_id.transaction_id().slot_index(), + } else { + // if we have multiple unlock conditions for basic or nft outputs, then we can't + // spend the balance at the moment or in the future + + let is_claimable = claimable_outputs.contains(output_id); + + // For outputs that are expired or have a timelock unlock condition, but no expiration + // unlock condition and we then can unlock them, then + // they can never be not available for us anymore + // and should be added to the balance + if is_claimable { + // check if output can be unlocked always from now on, in that case it should be + // added to the total amount + let output_can_be_unlocked_now_and_in_future = can_output_be_unlocked_forever_from_now_on( + // We use the addresses with unspent outputs, because other addresses of + // the account without unspent + // outputs can't be related to this output + wallet_address.inner(), + output, slot_index, - )?; - - // Add storage deposit - if output.is_basic() { - balance.required_storage_deposit.basic += storage_cost; - if output.native_token().is_some() && !wallet_ledger.locked_outputs.contains(output_id) { - total_storage_cost += storage_cost; - } - } else if output.is_nft() { - balance.required_storage_deposit.nft += storage_cost; - if !wallet_ledger.locked_outputs.contains(output_id) { - total_storage_cost += storage_cost; + protocol_parameters.committable_age_range(), + ); + + if output_can_be_unlocked_now_and_in_future { + // If output has a StorageDepositReturnUnlockCondition, the amount of it should + // be subtracted, because this part + // needs to be sent back + let amount = output + .unlock_conditions() + .and_then(|u| u.storage_deposit_return()) + .map_or_else( + || output.amount(), + |sdr| { + if wallet_address.inner() == sdr.return_address() { + // sending to ourself, we get the full amount + output.amount() + } else { + // Sending to someone else + output.amount() - sdr.amount() + } + }, + ); + + // add nft_id for nft outputs + if let Output::Nft(output) = &output { + let nft_id = output.nft_id_non_null(output_id); + balance.nfts.insert(nft_id); } - } - // Add native token - if let Some(native_token) = output.native_token() { - total_native_tokens.add_native_token(*native_token)?; - } - } else { - // if we have multiple unlock conditions for basic or nft outputs, then we can't - // spend the balance at the moment or in the future - - let is_claimable = claimable_outputs.contains(output_id); - - // For outputs that are expired or have a timelock unlock condition, but no expiration - // unlock condition and we then can unlock them, then - // they can never be not available for us anymore - // and should be added to the balance - if is_claimable { - // check if output can be unlocked always from now on, in that case it should be - // added to the total amount - let output_can_be_unlocked_now_and_in_future = can_output_be_unlocked_forever_from_now_on( - // We use the addresses with unspent outputs, because other addresses of - // the account without unspent - // outputs can't be related to this output - wallet_address.inner(), - output, + // Add amount + balance.base_coin.total += amount; + // Add decayed mana + balance.mana.total += output.decayed_mana( + &protocol_parameters, + output_id.transaction_id().slot_index(), slot_index, - protocol_parameters.committable_age_range(), - ); - - if output_can_be_unlocked_now_and_in_future { - // If output has a StorageDepositReturnUnlockCondition, the amount of it should - // be subtracted, because this part - // needs to be sent back - let amount = output - .unlock_conditions() - .and_then(|u| u.storage_deposit_return()) - .map_or_else( - || output.amount(), - |sdr| { - if wallet_address.inner() == sdr.return_address() { - // sending to ourself, we get the full amount - output.amount() - } else { - // Sending to someone else - output.amount() - sdr.amount() - } - }, - ); - - // add nft_id for nft outputs - if let Output::Nft(output) = &output { - let nft_id = output.nft_id_non_null(output_id); - balance.nfts.insert(nft_id); + )?; + + // Add storage deposit + if output.is_basic() { + balance.required_storage_deposit.basic += storage_cost; + // Amount for basic outputs isn't added to total storage cost if there aren't native + // tokens, since we can spend it without burning. + if output.native_token().is_some() + && !wallet_ledger.locked_outputs.contains(output_id) + { + total_storage_cost += storage_cost; } - - // Add amount - balance.base_coin.total += amount; - // Add decayed mana - balance.mana.total += output.decayed_mana( - &protocol_parameters, - output_id.transaction_id().slot_index(), - slot_index, - )?; - - // Add storage deposit - if output.is_basic() { - balance.required_storage_deposit.basic += storage_cost; - // Amount for basic outputs isn't added to total storage cost if there aren't native - // tokens, since we can spend it without burning. - if output.native_token().is_some() - && !wallet_ledger.locked_outputs.contains(output_id) - { - total_storage_cost += storage_cost; - } - } else if output.is_nft() { - balance.required_storage_deposit.nft += storage_cost; - if !wallet_ledger.locked_outputs.contains(output_id) { - total_storage_cost += storage_cost; - } + } else if output.is_nft() { + balance.required_storage_deposit.nft += storage_cost; + if !wallet_ledger.locked_outputs.contains(output_id) { + total_storage_cost += storage_cost; } + } - // Add native token - if let Some(native_token) = output.native_token() { - total_native_tokens.add_native_token(*native_token)?; - } - } else { - // only add outputs that can't be locked now and at any point in the future - balance.potentially_locked_outputs.insert(*output_id, true); + // Add native token + if let Some(native_token) = output.native_token() { + total_native_tokens.add_native_token(*native_token)?; } } else { - // Don't add expired outputs that can't ever be unlocked by us - if let Some(expiration) = output - .unlock_conditions() - .expect("output needs to have unlock conditions") - .expiration() - { - // Not expired, could get unlockable when it's expired, so we insert it - if slot_index < expiration.slot_index() { - balance.potentially_locked_outputs.insert(*output_id, false); - } - } else { + // only add outputs that can't be locked now and at any point in the future + balance.potentially_locked_outputs.insert(*output_id, true); + } + } else { + // Don't add expired outputs that can't ever be unlocked by us + if let Some(expiration) = output + .unlock_conditions() + .expect("output needs to have unlock conditions") + .expiration() + { + // Not expired, could get unlockable when it's expired, so we insert it + if slot_index < expiration.slot_index() { balance.potentially_locked_outputs.insert(*output_id, false); } + } else { + balance.potentially_locked_outputs.insert(*output_id, false); } } } } } + } - // for `available` get locked_outputs, sum outputs amount and subtract from total_amount - log::debug!("[BALANCE] locked outputs: {:#?}", wallet_ledger.locked_outputs); - - let mut locked_amount = 0; - let mut locked_mana = DecayedMana::default(); - let mut locked_native_tokens = NativeTokensBuilder::default(); + // for `available` get locked_outputs, sum outputs amount and subtract from total_amount + log::debug!("[BALANCE] locked outputs: {:#?}", wallet_ledger.locked_outputs); - for locked_output in &wallet_ledger.locked_outputs { - // Skip potentially_locked_outputs, as their amounts aren't added to the balance - if balance.potentially_locked_outputs.contains_key(locked_output) { - continue; - } - if let Some(output_data) = wallet_ledger.unspent_outputs.get(locked_output) { - // Only check outputs that are in this network - if output_data.network_id == network_id { - locked_amount += output_data.output.amount(); - locked_mana += output_data.output.decayed_mana( - &protocol_parameters, - output_data.output_id.transaction_id().slot_index(), - slot_index, - )?; + let mut locked_amount = 0; + let mut locked_mana = DecayedMana::default(); + let mut locked_native_tokens = NativeTokensBuilder::default(); - if let Some(native_token) = output_data.output.native_token() { - locked_native_tokens.add_native_token(*native_token)?; - } + for locked_output in &wallet_ledger.locked_outputs { + // Skip potentially_locked_outputs, as their amounts aren't added to the balance + if balance.potentially_locked_outputs.contains_key(locked_output) { + continue; + } + if let Some(output_data) = wallet_ledger.unspent_outputs.get(locked_output) { + // Only check outputs that are in this network + if output_data.network_id == network_id { + locked_amount += output_data.output.amount(); + locked_mana += output_data.output.decayed_mana( + &protocol_parameters, + output_data.output_id.transaction_id().slot_index(), + slot_index, + )?; + + if let Some(native_token) = output_data.output.native_token() { + locked_native_tokens.add_native_token(*native_token)?; } } } - - log::debug!( - "[BALANCE] total_amount: {}, total_potential: {}, total_stored: {}, locked_amount: {}, locked_mana: {:?}, total_storage_cost: {}", - balance.base_coin.total, - balance.mana.total.potential, - balance.mana.total.stored, - locked_amount, - locked_mana, - total_storage_cost, + } + + log::debug!( + "[BALANCE] total_amount: {}, total_potential: {}, total_stored: {}, locked_amount: {}, locked_mana: {:?}, total_storage_cost: {}", + balance.base_coin.total, + balance.mana.total.potential, + balance.mana.total.stored, + locked_amount, + locked_mana, + total_storage_cost, + ); + + locked_amount += total_storage_cost; + + for native_token in total_native_tokens.finish_set()? { + // Check if some amount is currently locked + let locked_native_token_amount = locked_native_tokens.iter().find_map(|(id, amount)| { + if id == native_token.token_id() { + Some(amount) + } else { + None + } + }); + + let metadata = wallet_ledger + .native_token_foundries + .get(&FoundryId::from(*native_token.token_id())) + .and_then(|foundry| foundry.immutable_features().metadata()) + .cloned(); + + balance.native_tokens.insert( + *native_token.token_id(), + NativeTokensBalance { + total: native_token.amount(), + available: native_token.amount() - *locked_native_token_amount.unwrap_or(&U256::from(0u8)), + metadata, + }, ); + } - locked_amount += total_storage_cost; - - for native_token in total_native_tokens.finish_set()? { - // Check if some amount is currently locked - let locked_native_token_amount = locked_native_tokens.iter().find_map(|(id, amount)| { - if id == native_token.token_id() { - Some(amount) - } else { - None - } - }); - - let metadata = wallet_ledger - .native_token_foundries - .get(&FoundryId::from(*native_token.token_id())) - .and_then(|foundry| foundry.immutable_features().metadata()) - .cloned(); - - balance.native_tokens.insert( - *native_token.token_id(), - NativeTokensBalance { - total: native_token.amount(), - available: native_token.amount() - *locked_native_token_amount.unwrap_or(&U256::from(0u8)), - metadata, - }, - ); - } + drop(wallet_ledger); - #[cfg(not(feature = "participation"))] - { - balance.base_coin.available = balance.base_coin.total.saturating_sub(locked_amount); + for output_id in reward_outputs { + if let Ok(response) = self.client().get_output_mana_rewards(&output_id, slot_index).await { + balance.mana.rewards += response.rewards; } - #[cfg(feature = "participation")] - { - balance.base_coin.available = balance - .base_coin - .total - .saturating_sub(locked_amount) - .saturating_sub(balance.base_coin.voting_power); - } - balance.mana.available = DecayedMana { - potential: balance.mana.total.potential.saturating_sub(locked_mana.potential), - stored: balance.mana.total.stored.saturating_sub(locked_mana.stored), - }; - - Ok(balance) - }).await? + } + + #[cfg(not(feature = "participation"))] + { + balance.base_coin.available = balance.base_coin.total.saturating_sub(locked_amount); + } + #[cfg(feature = "participation")] + { + balance.base_coin.available = balance + .base_coin + .total + .saturating_sub(locked_amount) + .saturating_sub(balance.base_coin.voting_power); + } + balance.mana.available = DecayedMana { + potential: balance.mana.total.potential.saturating_sub(locked_mana.potential), + stored: balance.mana.total.stored.saturating_sub(locked_mana.stored), + }; + + Ok(balance) } } diff --git a/sdk/src/wallet/operations/syncing/transactions.rs b/sdk/src/wallet/operations/syncing/transactions.rs index 4ebe6b8eba..064f0bd5d1 100644 --- a/sdk/src/wallet/operations/syncing/transactions.rs +++ b/sdk/src/wallet/operations/syncing/transactions.rs @@ -1,6 +1,8 @@ // Copyright 2021 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use std::collections::HashMap; + use crate::{ client::{secret::SecretManage, ClientError}, types::{ @@ -30,6 +32,7 @@ where /// be synced again pub(crate) async fn sync_pending_transactions(&self) -> Result { log::debug!("[SYNC] sync pending transactions"); + let network_id = self.client().get_network_id().await?; let wallet_ledger = self.ledger().await; // only set to true if a transaction got confirmed for which we don't have an output @@ -40,22 +43,34 @@ where return Ok(confirmed_unknown_output); } - let network_id = self.client().get_network_id().await?; - let mut updated_transactions = Vec::new(); let mut spent_output_ids = Vec::new(); // Inputs from conflicting transactions that are unspent, but should be removed from the locked outputs so they // are available again let mut output_ids_to_unlock = Vec::new(); - for transaction_id in &wallet_ledger.pending_transactions { + let pending_transactions = wallet_ledger + .pending_transactions + .iter() + .copied() + .map(|id| { + ( + id, + wallet_ledger + .transactions + .get(&id) + // panic during development to easier detect if something is wrong, should be handled different + // later + .expect("transaction id stored, but transaction is missing") + .clone(), + ) + }) + .collect::>(); + + drop(wallet_ledger); + + for (transaction_id, mut transaction) in pending_transactions { log::debug!("[SYNC] sync pending transaction {transaction_id}"); - let mut transaction = wallet_ledger - .transactions - .get(transaction_id) - // panic during development to easier detect if something is wrong, should be handled different later - .expect("transaction id stored, but transaction is missing") - .clone(); // only check transaction from the network we're connected to if transaction.network_id != network_id { @@ -64,25 +79,31 @@ where // check if we have an output (remainder, if not sending to an own address) that got created by this // transaction, if that's the case, then the transaction got confirmed - let transaction_output = wallet_ledger + let transaction_output = self + .ledger() + .await .outputs .keys() - .find(|o| o.transaction_id() == transaction_id); + .find(|o| o.transaction_id() == &transaction_id) + .copied(); if let Some(transaction_output) = transaction_output { - // Save to unwrap, we just got the output - let confirmed_output_data = wallet_ledger.outputs.get(transaction_output).expect("output exists"); - log::debug!( - "[SYNC] confirmed transaction {transaction_id} in block {}", - confirmed_output_data.metadata.block_id() - ); - updated_transaction_and_outputs( - transaction, - Some(*confirmed_output_data.metadata.block_id()), - InclusionState::Confirmed, - &mut updated_transactions, - &mut spent_output_ids, - ); + { + let wallet_ledger = self.ledger().await; + // Safe to unwrap, we just got the output + let confirmed_output_data = wallet_ledger.outputs.get(&transaction_output).expect("output exists"); + log::debug!( + "[SYNC] confirmed transaction {transaction_id} in block {}", + confirmed_output_data.metadata.block_id() + ); + updated_transaction_and_outputs( + transaction, + Some(*confirmed_output_data.metadata.block_id()), + InclusionState::Confirmed, + &mut updated_transactions, + &mut spent_output_ids, + ); + } continue; } @@ -90,7 +111,7 @@ where let mut input_got_spent = false; for input in transaction.payload.transaction().inputs() { let Input::Utxo(input) = input; - if let Some(input) = wallet_ledger.outputs.get(input.output_id()) { + if let Some(input) = self.ledger().await.outputs.get(input.output_id()) { if input.metadata.is_spent() { input_got_spent = true; } @@ -152,7 +173,7 @@ where } } else if input_got_spent { process_transaction_with_unknown_state( - &wallet_ledger, + &*self.ledger().await, transaction, &mut updated_transactions, &mut output_ids_to_unlock, @@ -162,7 +183,7 @@ where Err(ClientError::Node(crate::client::node_api::error::Error::NotFound(_))) => { if input_got_spent { process_transaction_with_unknown_state( - &wallet_ledger, + &*self.ledger().await, transaction, &mut updated_transactions, &mut output_ids_to_unlock, @@ -173,7 +194,7 @@ where } } else if input_got_spent { process_transaction_with_unknown_state( - &wallet_ledger, + &*self.ledger().await, transaction, &mut updated_transactions, &mut output_ids_to_unlock, @@ -188,7 +209,6 @@ where updated_transactions.push(transaction); } } - drop(wallet_ledger); // updates account with balances, output ids, outputs self.update_with_transactions(updated_transactions, spent_output_ids, output_ids_to_unlock) From 55607c63cb80a92b8f1eb901cd03e5350fd85997 Mon Sep 17 00:00:00 2001 From: DaughterOfMars Date: Tue, 5 Mar 2024 11:33:06 -0500 Subject: [PATCH 05/21] Make ISA a transaction builder (#2102) * Rename input selection to transaction builder. Move transaction options to client to support a client version of transaction builder. * fmt * revert flag name * find/replace mistakes * typo * error swap * review * finish renaming * Fix reward context inputs and tests * remove context inputs from transaction options * revert merge mistake * remove comments * usused import * Remove unused ContextInput * review * more ts instances * review * double copyright * review * fix mana allotment not recalculating after changes are made * remove error variant * allow providing chains * DRY --------- Co-authored-by: Thibault Martinez --- bindings/core/src/method/wallet.rs | 6 +- bindings/core/src/method_handler/utils.rs | 4 +- bindings/nodejs/examples/wallet/events.ts | 6 +- bindings/nodejs/lib/types/wallet/event.ts | 12 +- .../lib/types/wallet/transaction-options.ts | 5 +- bindings/python/iota_sdk/types/event.py | 19 + .../iota_sdk/types/transaction_options.py | 5 +- cli/src/wallet_cli/mod.rs | 4 +- sdk/examples/client/send_all.rs | 53 ++- .../how_tos/account_output/send_amount.rs | 4 +- .../send_micro_transaction.rs | 2 +- .../offline_signing/2_sign_transaction.rs | 7 +- sdk/src/client/api/block_builder/mod.rs | 4 +- .../api/block_builder}/options.rs | 8 +- .../client/api/block_builder/transaction.rs | 125 +++--- .../burn.rs | 2 +- .../error.rs | 15 +- .../mod.rs | 397 +++++++++--------- .../remainder.rs | 20 +- .../requirement/account.rs | 10 +- .../requirement/amount.rs | 49 ++- .../requirement/context_inputs.rs | 44 +- .../requirement/delegation.rs | 10 +- .../requirement/ed25519.rs | 13 +- .../requirement/foundry.rs | 10 +- .../requirement/issuer.rs | 15 +- .../requirement/mana.rs | 71 ++-- .../requirement/mod.rs | 21 +- .../requirement/native_tokens.rs | 18 +- .../requirement/nft.rs | 13 +- .../requirement/sender.rs | 33 +- .../transition.rs | 19 +- sdk/src/client/api/high_level.rs | 6 +- sdk/src/client/error.rs | 6 +- sdk/src/client/secret/mod.rs | 21 +- sdk/src/types/block/semantic/mod.rs | 16 +- .../types/block/semantic/state_transition.rs | 32 +- sdk/src/wallet/error.rs | 19 +- sdk/src/wallet/events/mod.rs | 8 +- sdk/src/wallet/events/types.rs | 8 +- sdk/src/wallet/mod.rs | 1 - sdk/src/wallet/operations/output_claiming.rs | 8 +- .../wallet/operations/output_consolidation.rs | 13 +- .../wallet/operations/participation/mod.rs | 2 +- ...nput_selection.rs => build_transaction.rs} | 119 ++---- .../burning_melting/melt_native_token.rs | 2 +- .../high_level/burning_melting/mod.rs | 4 +- .../high_level/minting/create_native_token.rs | 38 +- .../high_level/minting/mint_native_token.rs | 5 +- .../transaction/high_level/staking/begin.rs | 8 +- .../transaction/high_level/staking/end.rs | 8 +- .../transaction/high_level/staking/extend.rs | 8 +- sdk/src/wallet/operations/transaction/mod.rs | 14 +- .../operations/transaction/prepare_output.rs | 8 +- .../transaction/prepare_transaction.rs | 2 +- .../transaction/sign_transaction.rs | 6 +- sdk/tests/client/error.rs | 10 +- sdk/tests/client/mod.rs | 2 +- sdk/tests/client/signing/account.rs | 31 +- sdk/tests/client/signing/basic.rs | 44 +- sdk/tests/client/signing/delegation.rs | 174 +++----- sdk/tests/client/signing/mod.rs | 22 +- sdk/tests/client/signing/nft.rs | 18 +- .../account_outputs.rs | 205 ++++----- .../basic_outputs.rs | 172 ++++---- .../burn.rs | 124 +++--- .../delegation_outputs.rs | 22 +- .../expiration.rs | 79 ++-- .../foundry_outputs.rs | 89 ++-- .../mod.rs | 0 .../native_tokens.rs | 116 ++--- .../nft_outputs.rs | 138 +++--- .../outputs.rs | 57 +-- .../storage_deposit_return.rs | 48 +-- .../timelock.rs | 27 +- sdk/tests/wallet/burn_outputs.rs | 2 +- sdk/tests/wallet/claim_outputs.rs | 3 +- sdk/tests/wallet/events.rs | 2 +- 78 files changed, 1375 insertions(+), 1396 deletions(-) rename sdk/src/{wallet/operations/transaction => client/api/block_builder}/options.rs (89%) rename sdk/src/client/api/block_builder/{input_selection => transaction_builder}/burn.rs (98%) rename sdk/src/client/api/block_builder/{input_selection => transaction_builder}/error.rs (91%) rename sdk/src/client/api/block_builder/{input_selection => transaction_builder}/mod.rs (71%) rename sdk/src/client/api/block_builder/{input_selection => transaction_builder}/remainder.rs (95%) rename sdk/src/client/api/block_builder/{input_selection => transaction_builder}/requirement/account.rs (87%) rename sdk/src/client/api/block_builder/{input_selection => transaction_builder}/requirement/amount.rs (91%) rename sdk/src/client/api/block_builder/{input_selection => transaction_builder}/requirement/context_inputs.rs (75%) rename sdk/src/client/api/block_builder/{input_selection => transaction_builder}/requirement/delegation.rs (88%) rename sdk/src/client/api/block_builder/{input_selection => transaction_builder}/requirement/ed25519.rs (88%) rename sdk/src/client/api/block_builder/{input_selection => transaction_builder}/requirement/foundry.rs (83%) rename sdk/src/client/api/block_builder/{input_selection => transaction_builder}/requirement/issuer.rs (53%) rename sdk/src/client/api/block_builder/{input_selection => transaction_builder}/requirement/mana.rs (84%) rename sdk/src/client/api/block_builder/{input_selection => transaction_builder}/requirement/mod.rs (92%) rename sdk/src/client/api/block_builder/{input_selection => transaction_builder}/requirement/native_tokens.rs (92%) rename sdk/src/client/api/block_builder/{input_selection => transaction_builder}/requirement/nft.rs (83%) rename sdk/src/client/api/block_builder/{input_selection => transaction_builder}/requirement/sender.rs (69%) rename sdk/src/client/api/block_builder/{input_selection => transaction_builder}/transition.rs (92%) rename sdk/src/wallet/operations/transaction/{input_selection.rs => build_transaction.rs} (50%) rename sdk/tests/client/{input_selection => transaction_builder}/account_outputs.rs (93%) rename sdk/tests/client/{input_selection => transaction_builder}/basic_outputs.rs (95%) rename sdk/tests/client/{input_selection => transaction_builder}/burn.rs (95%) rename sdk/tests/client/{input_selection => transaction_builder}/delegation_outputs.rs (88%) rename sdk/tests/client/{input_selection => transaction_builder}/expiration.rs (95%) rename sdk/tests/client/{input_selection => transaction_builder}/foundry_outputs.rs (96%) rename sdk/tests/client/{input_selection => transaction_builder}/mod.rs (100%) rename sdk/tests/client/{input_selection => transaction_builder}/native_tokens.rs (96%) rename sdk/tests/client/{input_selection => transaction_builder}/nft_outputs.rs (93%) rename sdk/tests/client/{input_selection => transaction_builder}/outputs.rs (91%) rename sdk/tests/client/{input_selection => transaction_builder}/storage_deposit_return.rs (96%) rename sdk/tests/client/{input_selection => transaction_builder}/timelock.rs (93%) 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( From cf486c6d41928a5838eaf5dfed770810f101e8df Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Tue, 5 Mar 2024 18:22:17 +0100 Subject: [PATCH 06/21] Add commitment input syntactic checks (#2133) * Add commitment input syntactic checks * verify_inputs_packable * Add errors * remove semantic checks * Fix tests * Don't loop if true --- sdk/src/types/block/payload/error.rs | 6 +++ .../payload/signed_transaction/transaction.rs | 33 ++++++++++++++- sdk/src/types/block/semantic/mod.rs | 6 --- sdk/tests/client/signing/delegation.rs | 42 +++++++------------ 4 files changed, 52 insertions(+), 35 deletions(-) diff --git a/sdk/src/types/block/payload/error.rs b/sdk/src/types/block/payload/error.rs index c1dbfc3393..0ee5331cd8 100644 --- a/sdk/src/types/block/payload/error.rs +++ b/sdk/src/types/block/payload/error.rs @@ -52,6 +52,12 @@ pub enum PayloadError { MissingCreationSlot, #[display(fmt = "input count and unlock count mismatch: {input_count} != {unlock_count}")] InputUnlockCountMismatch { input_count: usize, unlock_count: usize }, + #[display(fmt = "missing commitment context input for staking feature")] + MissingCommitmentInputForStakingFeature, + #[display(fmt = "missing commitment context input for block issuer feature")] + MissingCommitmentInputForBlockIssuerFeature, + #[display(fmt = "missing commitment context input for delegation output")] + MissingCommitmentInputForDelegationOutput, #[from] TransactionSemantic(TransactionFailureReason), #[from] diff --git a/sdk/src/types/block/payload/signed_transaction/transaction.rs b/sdk/src/types/block/payload/signed_transaction/transaction.rs index 8a6f7aa127..3f7b688708 100644 --- a/sdk/src/types/block/payload/signed_transaction/transaction.rs +++ b/sdk/src/types/block/payload/signed_transaction/transaction.rs @@ -187,7 +187,7 @@ impl TransactionBuilder { verify_outputs(&outputs, protocol_parameters)?; } - Ok(Transaction { + let transaction = Transaction { network_id: self.network_id, creation_slot, context_inputs: ContextInputs::from_vec(self.context_inputs)?, @@ -196,7 +196,11 @@ impl TransactionBuilder { capabilities: self.capabilities, payload: self.payload, outputs, - }) + }; + + verify_transaction(&transaction)?; + + Ok(transaction) } /// Finishes a [`TransactionBuilder`] into a [`Transaction`] without protocol @@ -213,6 +217,7 @@ pub(crate) type OutputCount = BoundedU16<{ *OUTPUT_COUNT_RANGE.start() }, { *OUT #[derive(Clone, Debug, Eq, PartialEq, Packable)] #[packable(unpack_error = PayloadError)] #[packable(unpack_visitor = ProtocolParameters)] +#[packable(verify_with = verify_transaction_packable)] pub struct Transaction { /// The unique value denoting whether the block was meant for mainnet, testnet, or a private network. #[packable(verify_with = verify_network_id)] @@ -439,6 +444,30 @@ fn verify_outputs(outputs: &[Output], visitor: &ProtocolParameters) -> Result<() Ok(()) } +fn verify_transaction_packable(transaction: &Transaction, _: &ProtocolParameters) -> Result<(), PayloadError> { + verify_transaction(transaction) +} + +fn verify_transaction(transaction: &Transaction) -> Result<(), PayloadError> { + if transaction.context_inputs().commitment().is_none() { + for output in transaction.outputs.iter() { + if output.features().is_some_and(|f| f.staking().is_some()) { + return Err(PayloadError::MissingCommitmentInputForStakingFeature); + } + + if output.features().is_some_and(|f| f.block_issuer().is_some()) { + return Err(PayloadError::MissingCommitmentInputForBlockIssuerFeature); + } + + if output.is_delegation() { + return Err(PayloadError::MissingCommitmentInputForDelegationOutput); + } + } + } + + Ok(()) +} + #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[non_exhaustive] pub enum TransactionCapabilityFlag { diff --git a/sdk/src/types/block/semantic/mod.rs b/sdk/src/types/block/semantic/mod.rs index 0840359181..f3f02fb28b 100644 --- a/sdk/src/types/block/semantic/mod.rs +++ b/sdk/src/types/block/semantic/mod.rs @@ -297,9 +297,6 @@ impl<'a> SemanticValidationContext<'a> { if output.features().block_issuer().is_some() { let account_id = output.account_id_non_null(&OutputId::new(self.transaction_id, index as u16)); - if self.commitment_context_input.is_none() { - return Err(TransactionFailureReason::BlockIssuerCommitmentInputMissing); - } if !bic_context_inputs.contains(&account_id) { return Err(TransactionFailureReason::BlockIssuanceCreditInputMissing); } @@ -318,9 +315,6 @@ impl<'a> SemanticValidationContext<'a> { } } if output.features().staking().is_some() { - if self.commitment_context_input.is_none() { - return Err(TransactionFailureReason::StakingCommitmentInputMissing); - } if output.features().block_issuer().is_none() { return Err(TransactionFailureReason::StakingBlockIssuerFeatureMissing); } diff --git a/sdk/tests/client/signing/delegation.rs b/sdk/tests/client/signing/delegation.rs index 12646b9b6c..6da6ac9dd9 100644 --- a/sdk/tests/client/signing/delegation.rs +++ b/sdk/tests/client/signing/delegation.rs @@ -17,11 +17,12 @@ use iota_sdk::{ output::DelegationId, payload::{ signed_transaction::{Transaction, TransactionCapabilityFlag}, - SignedTransactionPayload, + PayloadError, SignedTransactionPayload, }, protocol::iota_mainnet_protocol_parameters, rand::{address::rand_account_address, output::rand_delegation_id, slot::rand_slot_commitment_id}, semantic::TransactionFailureReason, + slot::SlotCommitmentHash, unlock::SignatureUnlock, }, }; @@ -156,7 +157,7 @@ async fn creation_missing_commitment_input() -> Result<(), Box Result<(), Box Result<(), Box> { .with_outputs(outputs) .with_creation_slot(slot_index + 1) .with_capabilities([TransactionCapabilityFlag::BurnMana]) + .with_context_inputs([ + CommitmentContextInput::new(SlotCommitmentHash::null().into_slot_commitment_id(0)).into(), + ]) .finish_with_params(protocol_parameters)?; let prepared_transaction_data = PreparedTransactionData { @@ -331,6 +313,9 @@ async fn mismatch_amount_creation() -> Result<(), Box> { .with_outputs(outputs) .with_creation_slot(slot_index + 1) .with_capabilities([TransactionCapabilityFlag::BurnMana]) + .with_context_inputs([ + CommitmentContextInput::new(SlotCommitmentHash::null().into_slot_commitment_id(0)).into(), + ]) .finish_with_params(protocol_parameters)?; let prepared_transaction_data = PreparedTransactionData { @@ -412,6 +397,9 @@ async fn non_zero_end_epoch_creation() -> Result<(), Box> .with_outputs(outputs) .with_creation_slot(slot_index + 1) .with_capabilities([TransactionCapabilityFlag::BurnMana]) + .with_context_inputs([ + CommitmentContextInput::new(SlotCommitmentHash::null().into_slot_commitment_id(0)).into(), + ]) .finish_with_params(protocol_parameters)?; let prepared_transaction_data = PreparedTransactionData { From 7e5dda3143b6afcccb73dcd9cdebf553e582869c Mon Sep 17 00:00:00 2001 From: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> Date: Tue, 5 Mar 2024 18:29:41 +0100 Subject: [PATCH 07/21] Rename prepare_transaction() to prepare_send_outputs() (#2132) * Rename prepare_transaction() to prepare_send_outputs() * Remove duplicate * Fix export * Rename file and move send_outputs() --------- Co-authored-by: Thibault Martinez --- bindings/core/src/method/wallet.rs | 4 +- bindings/core/src/method_handler/wallet.rs | 4 +- bindings/core/src/response.rs | 2 +- .../nodejs/lib/types/wallet/bridge/index.ts | 4 +- .../nodejs/lib/types/wallet/bridge/wallet.ts | 4 +- bindings/nodejs/lib/wallet/wallet.ts | 35 +------ bindings/python/iota_sdk/wallet/wallet.py | 23 ++--- sdk/src/wallet/operations/output_claiming.rs | 2 +- .../wallet/operations/output_consolidation.rs | 2 +- .../wallet/operations/participation/voting.rs | 4 +- .../operations/participation/voting_power.rs | 4 +- .../wallet/operations/transaction/account.rs | 2 +- .../transaction/high_level/allot_mana.rs | 2 +- .../burning_melting/melt_native_token.rs | 2 +- .../high_level/burning_melting/mod.rs | 2 +- .../transaction/high_level/create_account.rs | 2 +- .../high_level/delegation/create.rs | 2 +- .../high_level/delegation/delay.rs | 2 +- .../high_level/minting/create_native_token.rs | 4 +- .../high_level/minting/mint_native_token.rs | 2 +- .../high_level/minting/mint_nfts.rs | 4 +- .../operations/transaction/high_level/send.rs | 2 +- .../transaction/high_level/send_mana.rs | 2 +- .../high_level/send_native_tokens.rs | 2 +- .../transaction/high_level/send_nft.rs | 4 +- .../transaction/high_level/staking/begin.rs | 2 +- .../transaction/high_level/staking/end.rs | 2 +- .../transaction/high_level/staking/extend.rs | 2 +- sdk/src/wallet/operations/transaction/mod.rs | 55 +---------- .../transaction/prepare_transaction.rs | 45 --------- .../operations/transaction/send_outputs.rs | 97 +++++++++++++++++++ 31 files changed, 145 insertions(+), 180 deletions(-) delete mode 100644 sdk/src/wallet/operations/transaction/prepare_transaction.rs create mode 100644 sdk/src/wallet/operations/transaction/send_outputs.rs diff --git a/bindings/core/src/method/wallet.rs b/bindings/core/src/method/wallet.rs index 487707fdbd..2bf51e0d14 100644 --- a/bindings/core/src/method/wallet.rs +++ b/bindings/core/src/method/wallet.rs @@ -385,9 +385,9 @@ pub enum WalletMethod { // #[cfg_attr(docsrs, doc(cfg(feature = "participation")))] // #[serde(rename_all = "camelCase")] // PrepareStopParticipating { event_id: ParticipationEventId }, - /// Prepare transaction. + /// Prepare to send outputs. /// Expected response: [`PreparedTransaction`](crate::Response::PreparedTransaction) - PrepareTransaction { + PrepareSendOutputs { outputs: Vec, #[serde(default)] options: Option, diff --git a/bindings/core/src/method_handler/wallet.rs b/bindings/core/src/method_handler/wallet.rs index ec2587670f..f7f1f5fb21 100644 --- a/bindings/core/src/method_handler/wallet.rs +++ b/bindings/core/src/method_handler/wallet.rs @@ -366,8 +366,8 @@ pub(crate) async fn call_wallet_method_internal( // let data = wallet.prepare_stop_participating(event_id).await?; // Response::PreparedTransaction(data) // } - WalletMethod::PrepareTransaction { outputs, options } => { - let data = wallet.prepare_transaction(outputs, options).await?; + WalletMethod::PrepareSendOutputs { outputs, options } => { + let data = wallet.prepare_send_outputs(outputs, options).await?; Response::PreparedTransaction(data) } // #[cfg(feature = "participation")] diff --git a/bindings/core/src/response.rs b/bindings/core/src/response.rs index 1c125938b3..372741e172 100644 --- a/bindings/core/src/response.rs +++ b/bindings/core/src/response.rs @@ -337,7 +337,7 @@ pub enum Response { /// - [`PrepareSendNativeTokens`](crate::method::WalletMethod::PrepareSendNativeTokens), /// - [`PrepareSendNft`](crate::method::WalletMethod::PrepareSendNft), /// - [`PrepareStopParticipating`](crate::method::WalletMethod::PrepareStopParticipating) - /// - [`PrepareTransaction`](crate::method::WalletMethod::PrepareTransaction) + /// - [`PrepareSendOutputs`](crate::method::WalletMethod::PrepareSendOutputs) /// - [`PrepareVote`](crate::method::WalletMethod::PrepareVote) /// - [`PrepareImplicitAccountTransition`](crate::method::WalletMethod::PrepareImplicitAccountTransition) PreparedTransaction(PreparedTransactionData), diff --git a/bindings/nodejs/lib/types/wallet/bridge/index.ts b/bindings/nodejs/lib/types/wallet/bridge/index.ts index 0c7628c57d..00e935d384 100644 --- a/bindings/nodejs/lib/types/wallet/bridge/index.ts +++ b/bindings/nodejs/lib/types/wallet/bridge/index.ts @@ -31,7 +31,7 @@ import type { __PrepareBeginStakingMethod__, __PrepareExtendStakingMethod__, __PrepareEndStakingMethod__, - __PrepareTransactionMethod__, + __PrepareSendOutputsMethod__, __RegisterParticipationEventsMethod__, __WaitForTransactionAcceptanceMethod__, __SendMethod__, @@ -112,7 +112,7 @@ export type __WalletMethod__ = | __PrepareBeginStakingMethod__ | __PrepareExtendStakingMethod__ | __PrepareEndStakingMethod__ - | __PrepareTransactionMethod__ + | __PrepareSendOutputsMethod__ | __RegisterParticipationEventsMethod__ | __WaitForTransactionAcceptanceMethod__ | __SendMethod__ diff --git a/bindings/nodejs/lib/types/wallet/bridge/wallet.ts b/bindings/nodejs/lib/types/wallet/bridge/wallet.ts index 6baa05d442..baff7e5311 100644 --- a/bindings/nodejs/lib/types/wallet/bridge/wallet.ts +++ b/bindings/nodejs/lib/types/wallet/bridge/wallet.ts @@ -351,8 +351,8 @@ export type __PrepareSendMethod__ = { }; }; -export type __PrepareTransactionMethod__ = { - name: 'prepareTransaction'; +export type __PrepareSendOutputsMethod__ = { + name: 'prepareSendOutputs'; data: { outputs: Output[]; options?: TransactionOptions; diff --git a/bindings/nodejs/lib/wallet/wallet.ts b/bindings/nodejs/lib/wallet/wallet.ts index 0d43e63b47..e1d83e5ae9 100644 --- a/bindings/nodejs/lib/wallet/wallet.ts +++ b/bindings/nodejs/lib/wallet/wallet.ts @@ -1463,17 +1463,17 @@ export class Wallet { } /** - * Send a transaction. + * Send outputs. * * @param outputs Outputs to use in the transaction. * @param options Additional transaction options. * @returns The transaction data. */ - async sendTransaction( + async sendOutputs( outputs: Output[], options?: TransactionOptions, ): Promise { - return (await this.prepareTransaction(outputs, options)).send(); + return (await this.prepareSendOutputs(outputs, options)).send(); } /** @@ -1483,12 +1483,12 @@ export class Wallet { * @param options Additional transaction options. * @returns The prepared transaction data. */ - async prepareTransaction( + async prepareSendOutputs( outputs: Output[], options?: TransactionOptions, ): Promise { const response = await this.methodHandler.callMethod({ - name: 'prepareTransaction', + name: 'prepareSendOutputs', data: { outputs, options, @@ -1682,31 +1682,6 @@ export class Wallet { ); } - /** - * Send outputs in a transaction. - * - * @param outputs The outputs to send. - * @param transactionOptions Additional transaction options. - * @returns The sent transaction. - */ - async sendOutputs( - outputs: Output[], - transactionOptions?: TransactionOptions, - ): Promise { - const response = await this.methodHandler.callMethod({ - name: 'sendOutputs', - data: { - outputs, - options: transactionOptions, - }, - }); - - const parsed = JSON.parse( - response, - ) as Response; - return plainToInstance(TransactionWithMetadata, parsed.payload); - } - /** * Send mana. * diff --git a/bindings/python/iota_sdk/wallet/wallet.py b/bindings/python/iota_sdk/wallet/wallet.py index d83d2dc1c3..eb8c8e6913 100644 --- a/bindings/python/iota_sdk/wallet/wallet.py +++ b/bindings/python/iota_sdk/wallet/wallet.py @@ -701,18 +701,18 @@ def announce_candidacy(self, account_id: HexStr) -> BlockId: } )) - def send_transaction( + def send_outputs( self, outputs: List[Output], options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: - """Send a transaction. + """Send outputs. """ - return self.prepare_transaction(outputs, options).send() + return self.prepare_send_outputs(outputs, options).send() - def prepare_transaction( + def prepare_send_outputs( self, outputs: List[Output], options: Optional[TransactionOptions] = None) -> PreparedTransaction: - """Prepare transaction. + """Prepare to send outputs. """ prepared = PreparedTransactionData.from_dict(self._call_method( - 'prepareTransaction', { + 'prepareSendOutputs', { 'outputs': outputs, 'options': options } @@ -792,17 +792,6 @@ def prepare_send_nft(self, params: List[SendNftParams], )) return PreparedTransaction(self, prepared) - def send_outputs( - self, outputs: List[Output], options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: - """Send outputs in a transaction. - """ - return TransactionWithMetadata.from_dict(self._call_method( - 'sendOutputs', { - 'outputs': outputs, - 'options': options, - } - )) - def send_mana( self, params: SendManaParams, options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: """Send mana. diff --git a/sdk/src/wallet/operations/output_claiming.rs b/sdk/src/wallet/operations/output_claiming.rs index 1e7740de4a..6b2dc20dab 100644 --- a/sdk/src/wallet/operations/output_claiming.rs +++ b/sdk/src/wallet/operations/output_claiming.rs @@ -288,7 +288,7 @@ where } } - self.prepare_transaction( + self.prepare_send_outputs( // We only need to provide the NFT outputs, ISA automatically creates basic outputs as remainder outputs nft_outputs_to_send, TransactionOptions { diff --git a/sdk/src/wallet/operations/output_consolidation.rs b/sdk/src/wallet/operations/output_consolidation.rs index b2ef3dd6ee..e4c9450b5a 100644 --- a/sdk/src/wallet/operations/output_consolidation.rs +++ b/sdk/src/wallet/operations/output_consolidation.rs @@ -120,7 +120,7 @@ where ..Default::default() }); - self.prepare_transaction([], options).await + self.prepare_send_outputs([], options).await } /// Determines whether an output should be consolidated or not. diff --git a/sdk/src/wallet/operations/participation/voting.rs b/sdk/src/wallet/operations/participation/voting.rs index 11b89a77f4..30399596a2 100644 --- a/sdk/src/wallet/operations/participation/voting.rs +++ b/sdk/src/wallet/operations/participation/voting.rs @@ -106,7 +106,7 @@ where ]) .finish_output()?; - self.prepare_transaction( + self.prepare_send_outputs( [new_output], Some(TransactionOptions { // Only use previous voting output as input. @@ -183,7 +183,7 @@ where ]) .finish_output()?; - self.prepare_transaction( + self.prepare_send_outputs( [new_output], Some(TransactionOptions { // Only use previous voting output as input. diff --git a/sdk/src/wallet/operations/participation/voting_power.rs b/sdk/src/wallet/operations/participation/voting_power.rs index d59a959cca..d48388604e 100644 --- a/sdk/src/wallet/operations/participation/voting_power.rs +++ b/sdk/src/wallet/operations/participation/voting_power.rs @@ -77,7 +77,7 @@ where ), }; - self.prepare_transaction([new_output], tx_options).await + self.prepare_send_outputs([new_output], tx_options).await } /// Reduces an account's "voting power" by a given amount. @@ -114,7 +114,7 @@ where (new_output, Some(tagged_data_payload)) }; - self.prepare_transaction( + self.prepare_send_outputs( [new_output], Some(TransactionOptions { // Use the previous voting output and additionally others for possible additional required amount for diff --git a/sdk/src/wallet/operations/transaction/account.rs b/sdk/src/wallet/operations/transaction/account.rs index 1bcdb5f53e..a6dc6e9454 100644 --- a/sdk/src/wallet/operations/transaction/account.rs +++ b/sdk/src/wallet/operations/transaction/account.rs @@ -103,7 +103,7 @@ where ..Default::default() }; - self.prepare_transaction(vec![account], transaction_options.clone()) + self.prepare_send_outputs(vec![account], transaction_options.clone()) .await } } diff --git a/sdk/src/wallet/operations/transaction/high_level/allot_mana.rs b/sdk/src/wallet/operations/transaction/high_level/allot_mana.rs index 4cf7c4e03e..752320db22 100644 --- a/sdk/src/wallet/operations/transaction/high_level/allot_mana.rs +++ b/sdk/src/wallet/operations/transaction/high_level/allot_mana.rs @@ -41,6 +41,6 @@ where *options.mana_allotments.entry(account_id).or_default() += mana; } - self.prepare_transaction([], options).await + self.prepare_send_outputs([], options).await } } 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 e797c2b3bc..6d1322fbdf 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 @@ -78,7 +78,7 @@ where )?)) .finish_output()?]; // Transaction builder will detect that we're melting native tokens and add the required inputs if available - self.prepare_transaction(outputs, options).await + self.prepare_send_outputs(outputs, options).await } /// Find and return unspent `OutputData` for given `account_id` and `foundry_id` 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 d3138fa946..718722eee2 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 @@ -41,6 +41,6 @@ impl Wallet { // The empty list of outputs is used. Outputs will be generated by // the transaction builder algorithm based on the content of the [`Burn`] object. - self.prepare_transaction([], options).await + self.prepare_send_outputs([], options).await } } diff --git a/sdk/src/wallet/operations/transaction/high_level/create_account.rs b/sdk/src/wallet/operations/transaction/high_level/create_account.rs index 6f9f349d9b..fd9d167e72 100644 --- a/sdk/src/wallet/operations/transaction/high_level/create_account.rs +++ b/sdk/src/wallet/operations/transaction/high_level/create_account.rs @@ -100,7 +100,7 @@ where let outputs = [account_output_builder.finish_output()?]; - self.prepare_transaction(outputs, options).await + self.prepare_send_outputs(outputs, options).await } /// Gets an existing account output. diff --git a/sdk/src/wallet/operations/transaction/high_level/delegation/create.rs b/sdk/src/wallet/operations/transaction/high_level/delegation/create.rs index 18c1bfd25b..acc6ba55b0 100644 --- a/sdk/src/wallet/operations/transaction/high_level/delegation/create.rs +++ b/sdk/src/wallet/operations/transaction/high_level/delegation/create.rs @@ -104,7 +104,7 @@ where .add_unlock_condition(AddressUnlockCondition::new(address)) .finish_output()?; - let transaction = self.prepare_transaction([output], options).await?; + let transaction = self.prepare_send_outputs([output], options).await?; Ok(PreparedCreateDelegationTransaction { delegation_id: DelegationId::from(&transaction.transaction.id().into_output_id(0)), diff --git a/sdk/src/wallet/operations/transaction/high_level/delegation/delay.rs b/sdk/src/wallet/operations/transaction/high_level/delegation/delay.rs index 9784b18485..98242dc057 100644 --- a/sdk/src/wallet/operations/transaction/high_level/delegation/delay.rs +++ b/sdk/src/wallet/operations/transaction/high_level/delegation/delay.rs @@ -87,6 +87,6 @@ where } }; - self.prepare_transaction(outputs, None).await + self.prepare_send_outputs(outputs, None).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 de8d012c87..a00e862141 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 @@ -54,7 +54,7 @@ where { /// Creates a new foundry output with minted native tokens. /// - /// Calls [Wallet::prepare_transaction()](crate::wallet::Wallet::prepare_transaction) internally, the options may + /// Calls [Wallet::prepare_send_outputs()](crate::wallet::Wallet::prepare_send_outputs) internally, the options may /// define the remainder value strategy or custom inputs. /// ```ignore /// let params = CreateNativeTokenParams { @@ -141,7 +141,7 @@ where foundry_builder.finish_output()? }]; - self.prepare_transaction(outputs, options) + self.prepare_send_outputs(outputs, options) .await .map(|transaction| PreparedCreateNativeTokenTransaction { token_id, transaction }) } else { 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 8925147c54..3b465419d1 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 @@ -123,6 +123,6 @@ where let outputs = [new_foundry_output_builder.finish_output()?]; - self.prepare_transaction(outputs, options).await + self.prepare_send_outputs(outputs, options).await } } diff --git a/sdk/src/wallet/operations/transaction/high_level/minting/mint_nfts.rs b/sdk/src/wallet/operations/transaction/high_level/minting/mint_nfts.rs index fa0818d5a9..1a7eeb01da 100644 --- a/sdk/src/wallet/operations/transaction/high_level/minting/mint_nfts.rs +++ b/sdk/src/wallet/operations/transaction/high_level/minting/mint_nfts.rs @@ -114,7 +114,7 @@ where { /// Mints NFTs. /// - /// Calls [Wallet::prepare_transaction()](crate::wallet::Wallet::prepare_transaction) internally. The options may + /// Calls [Wallet::prepare_send_outputs()](crate::wallet::Wallet::prepare_send_outputs) internally. The options may /// define the remainder value strategy or custom inputs. Note that addresses need to be bech32-encoded. /// ```ignore /// let nft_id: [u8; 38] = @@ -207,6 +207,6 @@ where outputs.push(nft_builder.finish_output()?); } - self.prepare_transaction(outputs, options).await + self.prepare_send_outputs(outputs, options).await } } diff --git a/sdk/src/wallet/operations/transaction/high_level/send.rs b/sdk/src/wallet/operations/transaction/high_level/send.rs index 841b6fd2ca..29ae57de2f 100644 --- a/sdk/src/wallet/operations/transaction/high_level/send.rs +++ b/sdk/src/wallet/operations/transaction/high_level/send.rs @@ -195,6 +195,6 @@ where } } - self.prepare_transaction(outputs, options).await + self.prepare_send_outputs(outputs, options).await } } diff --git a/sdk/src/wallet/operations/transaction/high_level/send_mana.rs b/sdk/src/wallet/operations/transaction/high_level/send_mana.rs index 0c515f6394..7c71453414 100644 --- a/sdk/src/wallet/operations/transaction/high_level/send_mana.rs +++ b/sdk/src/wallet/operations/transaction/high_level/send_mana.rs @@ -90,6 +90,6 @@ where let output = output_builder.finish_output()?; - self.prepare_transaction(vec![output], options).await + self.prepare_send_outputs(vec![output], options).await } } diff --git a/sdk/src/wallet/operations/transaction/high_level/send_native_tokens.rs b/sdk/src/wallet/operations/transaction/high_level/send_native_tokens.rs index a4ce1d549c..18574337f3 100644 --- a/sdk/src/wallet/operations/transaction/high_level/send_native_tokens.rs +++ b/sdk/src/wallet/operations/transaction/high_level/send_native_tokens.rs @@ -176,6 +176,6 @@ where ) } - self.prepare_transaction(outputs, options).await + self.prepare_send_outputs(outputs, options).await } } diff --git a/sdk/src/wallet/operations/transaction/high_level/send_nft.rs b/sdk/src/wallet/operations/transaction/high_level/send_nft.rs index b6fee70d8c..8ac76efa4b 100644 --- a/sdk/src/wallet/operations/transaction/high_level/send_nft.rs +++ b/sdk/src/wallet/operations/transaction/high_level/send_nft.rs @@ -45,7 +45,7 @@ where ClientError: From, { /// Sends an NFT to the provided address. - /// Calls [Wallet::prepare_transaction()](crate::wallet::Wallet::prepare_transaction) internally. The + /// Calls [Wallet::prepare_send_outputs()](crate::wallet::Wallet::prepare_send_outputs) internally. The /// options may define the remainder value strategy. Note that custom inputs will be replaced with the required /// nft inputs and addresses need to be bech32-encoded. /// ```ignore @@ -106,6 +106,6 @@ where }; } - self.prepare_transaction(outputs, options).await + self.prepare_send_outputs(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 17249a69bf..387cf32cae 100644 --- a/sdk/src/wallet/operations/transaction/high_level/staking/begin.rs +++ b/sdk/src/wallet/operations/transaction/high_level/staking/begin.rs @@ -102,7 +102,7 @@ where )) .finish_output()?; - let transaction = self.prepare_transaction([output], options).await?; + let transaction = self.prepare_send_outputs([output], options).await?; Ok(transaction) } 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 a5d2ea23df..4dd5ca0abf 100644 --- a/sdk/src/wallet/operations/transaction/high_level/staking/end.rs +++ b/sdk/src/wallet/operations/transaction/high_level/staking/end.rs @@ -74,7 +74,7 @@ where .with_features(features) .finish_output()?; - let transaction = self.prepare_transaction([output], options).await?; + let transaction = self.prepare_send_outputs([output], options).await?; Ok(transaction) } 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 e1867a70d4..a4c8b9414f 100644 --- a/sdk/src/wallet/operations/transaction/high_level/staking/extend.rs +++ b/sdk/src/wallet/operations/transaction/high_level/staking/extend.rs @@ -89,7 +89,7 @@ where let output = output_builder.finish_output()?; - let transaction = self.prepare_transaction([output], options).await?; + let transaction = self.prepare_send_outputs([output], options).await?; Ok(transaction) } diff --git a/sdk/src/wallet/operations/transaction/mod.rs b/sdk/src/wallet/operations/transaction/mod.rs index db261869f0..063180d48d 100644 --- a/sdk/src/wallet/operations/transaction/mod.rs +++ b/sdk/src/wallet/operations/transaction/mod.rs @@ -5,7 +5,7 @@ pub(crate) mod account; mod build_transaction; pub(crate) mod high_level; pub(crate) mod prepare_output; -mod prepare_transaction; +mod send_outputs; mod sign_transaction; pub(crate) mod submit_transaction; @@ -17,10 +17,7 @@ use crate::{ secret::SecretManage, ClientError, }, - types::block::{ - output::{Output, OutputWithMetadata}, - payload::signed_transaction::SignedTransactionPayload, - }, + types::block::{output::OutputWithMetadata, payload::signed_transaction::SignedTransactionPayload}, wallet::{ types::{InclusionState, TransactionWithMetadata}, Wallet, WalletError, @@ -32,54 +29,6 @@ where WalletError: From, ClientError: From, { - /// Sends a transaction by specifying its outputs. - /// - /// Note that, if sending a block fails, the method will return `None` for the block id, but the wallet - /// will reissue the transaction during syncing. - /// ```ignore - /// let outputs = [ - /// BasicOutputBuilder::new_with_amount(1_000_000)? - /// .add_unlock_condition(AddressUnlockCondition::new( - /// Address::try_from_bech32("rms1qpszqzadsym6wpppd6z037dvlejmjuke7s24hm95s9fg9vpua7vluaw60xu")?, - /// )) - /// .finish_output(account.client.get_token_supply().await?;)?, - /// ]; - /// let tx = account - /// .send_outputs( - /// outputs, - /// Some(TransactionOptions { - /// remainder_value_strategy: RemainderValueStrategy::ReuseAddress, - /// ..Default::default() - /// }), - /// ) - /// .await?; - /// println!("Transaction created: {}", tx.transaction_id); - /// if let Some(block_id) = tx.block_id { - /// println!("Block sent: {}", block_id); - /// } - /// ``` - pub async fn send_outputs( - &self, - outputs: impl Into> + Send, - options: impl Into> + Send, - ) -> Result { - let outputs = outputs.into(); - let options = options.into(); - // here to check before syncing, how to prevent duplicated verification (also in prepare_transaction())? - // Checking it also here is good to return earlier if something is invalid - let protocol_parameters = self.client().get_protocol_parameters().await?; - - // Check if the outputs have enough amount to cover the storage deposit - for output in &outputs { - output.verify_storage_deposit(protocol_parameters.storage_score_parameters())?; - } - - let prepared_transaction_data = self.prepare_transaction(outputs, options.clone()).await?; - - self.sign_and_submit_transaction(prepared_transaction_data, options) - .await - } - /// Signs a transaction, submit it to a node and store it in the wallet pub async fn sign_and_submit_transaction( &self, diff --git a/sdk/src/wallet/operations/transaction/prepare_transaction.rs b/sdk/src/wallet/operations/transaction/prepare_transaction.rs deleted file mode 100644 index e91a7d9144..0000000000 --- a/sdk/src/wallet/operations/transaction/prepare_transaction.rs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use instant::Instant; -use packable::bounded::TryIntoBoundedU16Error; - -use crate::{ - client::{api::PreparedTransactionData, secret::SecretManage}, - types::block::{input::INPUT_COUNT_MAX, output::Output, payload::PayloadError}, - wallet::{operations::transaction::TransactionOptions, Wallet, WalletError}, -}; - -impl Wallet { - /// Get inputs and build the transaction - pub async fn prepare_transaction( - &self, - outputs: impl Into> + Send, - options: impl Into> + Send, - ) -> Result { - log::debug!("[TRANSACTION] prepare_transaction"); - let options = options.into().unwrap_or_default(); - let outputs = outputs.into(); - let prepare_transaction_start_time = Instant::now(); - let storage_score_params = self.client().get_storage_score_parameters().await?; - - // Check if the outputs have enough amount to cover the storage deposit - for output in &outputs { - output.verify_storage_deposit(storage_score_params)?; - } - - if options.required_inputs.len() as u16 > INPUT_COUNT_MAX { - return Err(PayloadError::InputCount(TryIntoBoundedU16Error::Truncated( - options.required_inputs.len(), - )))?; - } - - let prepared_transaction_data = self.build_transaction(outputs, options).await?; - - log::debug!( - "[TRANSACTION] finished prepare_transaction in {:.2?}", - prepare_transaction_start_time.elapsed() - ); - Ok(prepared_transaction_data) - } -} diff --git a/sdk/src/wallet/operations/transaction/send_outputs.rs b/sdk/src/wallet/operations/transaction/send_outputs.rs new file mode 100644 index 0000000000..1cfb01f5ac --- /dev/null +++ b/sdk/src/wallet/operations/transaction/send_outputs.rs @@ -0,0 +1,97 @@ +// Copyright 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use instant::Instant; +use packable::bounded::TryIntoBoundedU16Error; + +use crate::{ + client::{api::PreparedTransactionData, secret::SecretManage, ClientError}, + types::block::{input::INPUT_COUNT_MAX, output::Output, payload::PayloadError}, + wallet::{operations::transaction::TransactionOptions, types::TransactionWithMetadata, Wallet, WalletError}, +}; + +impl Wallet +where + WalletError: From, + ClientError: From, +{ + /// Sends a transaction by specifying its outputs. + /// + /// Note that, if sending a block fails, the method will return `None` for the block id, but the wallet + /// will reissue the transaction during syncing. + /// ```ignore + /// let outputs = [ + /// BasicOutputBuilder::new_with_amount(1_000_000)? + /// .add_unlock_condition(AddressUnlockCondition::new( + /// Address::try_from_bech32("rms1qpszqzadsym6wpppd6z037dvlejmjuke7s24hm95s9fg9vpua7vluaw60xu")?, + /// )) + /// .finish_output(account.client.get_token_supply().await?;)?, + /// ]; + /// let tx = account + /// .send_outputs( + /// outputs, + /// Some(TransactionOptions { + /// remainder_value_strategy: RemainderValueStrategy::ReuseAddress, + /// ..Default::default() + /// }), + /// ) + /// .await?; + /// println!("Transaction created: {}", tx.transaction_id); + /// if let Some(block_id) = tx.block_id { + /// println!("Block sent: {}", block_id); + /// } + /// ``` + pub async fn send_outputs( + &self, + outputs: impl Into> + Send, + options: impl Into> + Send, + ) -> Result { + let outputs = outputs.into(); + let options = options.into(); + // here to check before syncing, how to prevent duplicated verification (also in prepare_send_outputs())? + // Checking it also here is good to return earlier if something is invalid + let protocol_parameters = self.client().get_protocol_parameters().await?; + + // Check if the outputs have enough amount to cover the storage deposit + for output in &outputs { + output.verify_storage_deposit(protocol_parameters.storage_score_parameters())?; + } + + let prepared_transaction_data = self.prepare_send_outputs(outputs, options.clone()).await?; + + self.sign_and_submit_transaction(prepared_transaction_data, options) + .await + } + + /// Get inputs and build the transaction + pub async fn prepare_send_outputs( + &self, + outputs: impl Into> + Send, + options: impl Into> + Send, + ) -> Result { + log::debug!("[TRANSACTION] prepare_send_outputs"); + let options = options.into().unwrap_or_default(); + let outputs = outputs.into(); + let prepare_send_outputs_start_time = Instant::now(); + let storage_score_params = self.client().get_storage_score_parameters().await?; + + // Check if the outputs have enough amount to cover the storage deposit + for output in &outputs { + output.verify_storage_deposit(storage_score_params)?; + } + + if options.required_inputs.len() as u16 > INPUT_COUNT_MAX { + return Err(PayloadError::InputCount(TryIntoBoundedU16Error::Truncated( + options.required_inputs.len(), + )))?; + } + + let prepared_transaction_data = self.build_transaction(outputs, options).await?; + + log::debug!( + "[TRANSACTION] finished prepare_send_outputs in {:.2?}", + prepare_send_outputs_start_time.elapsed() + ); + Ok(prepared_transaction_data) + } +} From a3ec61a68303c74a4be00fa9ffa61c4201fac9b8 Mon Sep 17 00:00:00 2001 From: DaughterOfMars Date: Tue, 5 Mar 2024 14:04:45 -0500 Subject: [PATCH 08/21] Simplify amount requirement (#2065) * Simplify amount requirement * fix test to match name * fmt * println * sort anchors low * fmt? * tuning * revert comments * stupid test * reorder * Remove anchor --------- Co-authored-by: Thibault Martinez --- .../transaction_builder/remainder.rs | 112 ++-- .../transaction_builder/requirement/amount.rs | 523 ++++++------------ .../transaction_builder/requirement/mana.rs | 92 ++- .../transaction_builder/requirement/mod.rs | 29 + .../requirement/native_tokens.rs | 223 ++++---- sdk/tests/client/signing/mod.rs | 83 +-- .../transaction_builder/basic_outputs.rs | 8 +- .../transaction_builder/native_tokens.rs | 23 +- .../client/transaction_builder/outputs.rs | 132 ++++- 9 files changed, 574 insertions(+), 651 deletions(-) diff --git a/sdk/src/client/api/block_builder/transaction_builder/remainder.rs b/sdk/src/client/api/block_builder/transaction_builder/remainder.rs index 0dee5047ee..7111bdfb7b 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/remainder.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/remainder.rs @@ -5,18 +5,16 @@ use alloc::collections::BTreeMap; use std::collections::HashMap; use crypto::keys::bip44::Bip44; +use primitive_types::U256; use super::{TransactionBuilder, TransactionBuilderError}; use crate::{ - client::api::{ - transaction_builder::requirement::native_tokens::{get_native_tokens, get_native_tokens_diff}, - RemainderData, - }, + client::api::{transaction_builder::requirement::native_tokens::get_native_tokens_diff, RemainderData}, types::block::{ address::{Address, Ed25519Address}, output::{ - unlock_condition::AddressUnlockCondition, AccountOutput, AnchorOutput, BasicOutput, BasicOutputBuilder, - NativeTokens, NativeTokensBuilder, NftOutput, Output, StorageScoreParameters, + unlock_condition::AddressUnlockCondition, AccountOutput, BasicOutput, BasicOutputBuilder, NativeToken, + NftOutput, Output, StorageScoreParameters, TokenId, }, }, }; @@ -69,23 +67,6 @@ impl TransactionBuilder { Ok(None) } - 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()?; - - input_native_tokens.merge(minted_native_tokens)?; - output_native_tokens.merge(melted_native_tokens)?; - - if let Some(burn) = self.burn.as_ref() { - output_native_tokens.merge(NativeTokensBuilder::from(burn.native_tokens.clone()))?; - } - - let native_tokens_diff = get_native_tokens_diff(&input_native_tokens, &output_native_tokens)?; - - self.required_remainder_amount(native_tokens_diff) - } - pub(crate) fn storage_deposit_returns_and_remainders( &mut self, ) -> Result<(Vec, Vec), TransactionBuilderError> { @@ -109,18 +90,10 @@ impl TransactionBuilder { } } - 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()?; - - input_native_tokens.merge(minted_native_tokens)?; - output_native_tokens.merge(melted_native_tokens)?; - - if let Some(burn) = self.burn.as_ref() { - output_native_tokens.merge(NativeTokensBuilder::from(burn.native_tokens.clone()))?; - } - - let native_tokens_diff = get_native_tokens_diff(&input_native_tokens, &output_native_tokens)?; + let (input_nts, output_nts) = self.get_input_output_native_tokens(); + log::debug!("input_nts: {input_nts:#?}"); + log::debug!("output_nts: {output_nts:#?}"); + let native_tokens_diff = get_native_tokens_diff(input_nts, output_nts); let (input_mana, output_mana) = self.mana_sums(false)?; @@ -142,7 +115,7 @@ impl TransactionBuilder { self.remainders.added_mana = std::mem::take(&mut mana_diff); } - if input_amount == output_amount && mana_diff == 0 && native_tokens_diff.is_none() { + if input_amount == output_amount && mana_diff == 0 && native_tokens_diff.is_empty() { log::debug!("No remainder required"); return Ok((storage_deposit_returns, Vec::new())); } @@ -174,12 +147,10 @@ impl TransactionBuilder { pub(crate) fn get_output_for_added_mana(&mut self, remainder_address: &Address) -> Option<&mut Output> { // Establish the order in which we want to pick an output - let sort_order = HashMap::from([ - (AccountOutput::KIND, 1), - (BasicOutput::KIND, 2), - (NftOutput::KIND, 3), - (AnchorOutput::KIND, 4), - ]); + let sort_order = [AccountOutput::KIND, BasicOutput::KIND, NftOutput::KIND] + .into_iter() + .zip(0..) + .collect::>(); // Remove those that do not have an ordering and sort let ordered_outputs = self .provided_outputs @@ -203,11 +174,9 @@ impl TransactionBuilder { /// Calculates the required amount for required remainder outputs (multiple outputs are required if multiple native /// tokens are remaining) and returns if there are native tokens as remainder. - pub(crate) fn required_remainder_amount( - &self, - remainder_native_tokens: Option, - ) -> Result<(u64, bool, bool), TransactionBuilderError> { - let native_tokens_remainder = remainder_native_tokens.is_some(); + pub(crate) fn required_remainder_amount(&self) -> Result<(u64, bool, bool), TransactionBuilderError> { + let (input_nts, output_nts) = self.get_input_output_native_tokens(); + let remainder_native_tokens = get_native_tokens_diff(input_nts, output_nts); let remainder_builder = BasicOutputBuilder::new_with_minimum_amount(self.protocol_parameters.storage_score_parameters()) @@ -215,14 +184,19 @@ impl TransactionBuilder { [0; 32], )))); - let remainder_amount = if let Some(native_tokens) = remainder_native_tokens { + let remainder_amount = if !remainder_native_tokens.is_empty() { let nt_remainder_amount = remainder_builder - .with_native_token(*native_tokens.first().unwrap()) + .with_native_token( + remainder_native_tokens + .first_key_value() + .map(|(token_id, amount)| NativeToken::new(*token_id, amount)) + .unwrap()?, + ) .finish_output()? .amount(); // Amount can be just multiplied, because all remainder outputs with a native token have the same storage // cost. - nt_remainder_amount * native_tokens.len() as u64 + nt_remainder_amount * remainder_native_tokens.len() as u64 } else { remainder_builder.finish_output()?.amount() }; @@ -242,14 +216,14 @@ impl TransactionBuilder { mana_remainder &= selected_mana > required_mana + initial_excess; } - Ok((remainder_amount, native_tokens_remainder, mana_remainder)) + Ok((remainder_amount, !remainder_native_tokens.is_empty(), mana_remainder)) } } fn create_remainder_outputs( amount_diff: u64, mana_diff: u64, - native_tokens_diff: Option, + mut native_tokens: BTreeMap, remainder_address: Address, remainder_address_chain: Option, storage_score_parameters: StorageScoreParameters, @@ -259,24 +233,22 @@ fn create_remainder_outputs( let mut catchall_native_token = None; // Start with the native tokens - if let Some(native_tokens) = native_tokens_diff { - if let Some((last, nts)) = native_tokens.split_last() { - // Save this one for the catchall - catchall_native_token.replace(*last); - // Create remainder outputs with min amount - for native_token in nts { - let output = BasicOutputBuilder::new_with_minimum_amount(storage_score_parameters) - .add_unlock_condition(AddressUnlockCondition::new(remainder_address.clone())) - .with_native_token(*native_token) - .finish_output()?; - log::debug!( - "Created remainder output of amount {}, mana {} and native token {native_token:?} for {remainder_address:?}", - output.amount(), - output.mana() - ); - remaining_amount = remaining_amount.saturating_sub(output.amount()); - remainder_outputs.push(output); - } + if let Some((token_id, amount)) = native_tokens.pop_last() { + // Save this one for the catchall + catchall_native_token.replace(NativeToken::new(token_id, amount)?); + // Create remainder outputs with min amount + for (token_id, amount) in native_tokens { + let output = BasicOutputBuilder::new_with_minimum_amount(storage_score_parameters) + .add_unlock_condition(AddressUnlockCondition::new(remainder_address.clone())) + .with_native_token(NativeToken::new(token_id, amount)?) + .finish_output()?; + log::debug!( + "Created remainder output of amount {}, mana {} and native token ({token_id}: {amount}) for {remainder_address:?}", + output.amount(), + output.mana() + ); + remaining_amount = remaining_amount.saturating_sub(output.amount()); + remainder_outputs.push(output); } } let mut catchall = BasicOutputBuilder::new_with_amount(remaining_amount) diff --git a/sdk/src/client/api/block_builder/transaction_builder/requirement/amount.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/amount.rs index 4dced48202..81611ac609 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/requirement/amount.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/amount.rs @@ -1,19 +1,18 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use std::collections::{HashMap, HashSet}; +use std::{collections::HashMap, sync::OnceLock}; -use super::{native_tokens::get_native_tokens, Requirement, TransactionBuilder, TransactionBuilderError}; +use super::{Requirement, TransactionBuilder, TransactionBuilderError}; use crate::{ - client::secret::types::InputSigningData, + client::{api::transaction_builder::requirement::PriorityMap, secret::types::InputSigningData}, types::block::{ address::Address, - input::INPUT_COUNT_MAX, output::{ - unlock_condition::StorageDepositReturnUnlockCondition, AccountOutputBuilder, FoundryOutputBuilder, - MinimumOutputAmount, NftOutputBuilder, Output, OutputId, TokenId, + unlock_condition::StorageDepositReturnUnlockCondition, AccountOutput, AccountOutputBuilder, BasicOutput, + FoundryOutput, FoundryOutputBuilder, MinimumOutputAmount, NftOutput, NftOutputBuilder, Output, }, - slot::SlotIndex, + slot::{SlotCommitmentId, SlotIndex}, }, }; @@ -31,11 +30,61 @@ pub(crate) fn sdruc_not_expired( .map_or(false, |expiration| slot_index >= expiration.slot_index()); // We only have to send the storage deposit return back if the output is not expired - if !expired { Some(sdr) } else { None } + (!expired).then_some(sdr) }) } impl TransactionBuilder { + pub(crate) fn fulfill_amount_requirement(&mut self) -> Result, TransactionBuilderError> { + let (mut input_amount, mut output_amount) = self.amount_balance()?; + if input_amount >= output_amount { + log::debug!("Amount requirement already fulfilled"); + return Ok(Vec::new()); + } + + log::debug!("Fulfilling amount requirement with input amount {input_amount}, output amount {output_amount}"); + + if !self.allow_additional_input_selection { + return Err(TransactionBuilderError::AdditionalInputsRequired(Requirement::Amount)); + } + // If we have no inputs to balance with, try reducing outputs instead + if self.available_inputs.is_empty() { + if !self.reduce_funds_of_chains(input_amount, &mut output_amount)? { + return Err(TransactionBuilderError::InsufficientAmount { + found: input_amount, + required: output_amount, + }); + } + } else { + let mut priority_map = PriorityMap::::generate(&mut self.available_inputs); + loop { + let Some(input) = priority_map.next(output_amount - input_amount, self.latest_slot_commitment_id) + else { + break; + }; + log::debug!("selecting input with amount {}", input.output.amount()); + self.select_input(input)?; + (input_amount, output_amount) = self.amount_balance()?; + // Try to reduce output funds + if self.reduce_funds_of_chains(input_amount, &mut output_amount)? { + break; + } + } + // Return unselected inputs to the available list + for input in priority_map.into_inputs() { + self.available_inputs.push(input); + } + if output_amount > input_amount { + return Err(TransactionBuilderError::InsufficientAmount { + found: input_amount, + required: output_amount, + }); + } + } + + Ok(Vec::new()) + } + pub(crate) fn amount_sums(&self) -> (u64, u64, HashMap, HashMap) { let mut inputs_sum = 0; let mut outputs_sum = 0; @@ -72,386 +121,140 @@ impl TransactionBuilder { (inputs_sum, outputs_sum, inputs_sdr, outputs_sdr) } -} - -#[derive(Debug, Clone)] -struct AmountSelection { - newly_selected_inputs: HashMap, - inputs_sum: u64, - outputs_sum: u64, - inputs_sdr: HashMap, - outputs_sdr: HashMap, - remainder_amount: u64, - native_tokens_remainder: bool, - mana_remainder: bool, - selected_native_tokens: HashSet, -} - -impl AmountSelection { - 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( - 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) = transaction_builder.remainder_amount()?; - - Ok(Self { - newly_selected_inputs: HashMap::new(), - inputs_sum, - outputs_sum, - inputs_sdr, - outputs_sdr, - remainder_amount, - native_tokens_remainder, - mana_remainder, - selected_native_tokens, - }) - } - fn missing_amount(&self) -> u64 { - // If there is already a remainder, make sure it's enough to cover the storage deposit. - if self.inputs_sum > self.outputs_sum { - let diff = self.inputs_sum - self.outputs_sum; + pub(crate) fn amount_balance(&self) -> Result<(u64, u64), TransactionBuilderError> { + let (inputs_sum, mut outputs_sum, _, _) = self.amount_sums(); + let (remainder_amount, native_tokens_remainder, mana_remainder) = self.required_remainder_amount()?; + if inputs_sum > outputs_sum { + let diff = inputs_sum - outputs_sum; - if self.remainder_amount > diff { - self.remainder_amount - diff - } else { - 0 + if remainder_amount > diff { + outputs_sum += remainder_amount - diff } - } else if self.inputs_sum < self.outputs_sum { - self.outputs_sum - self.inputs_sum - } else if self.native_tokens_remainder || self.mana_remainder { - self.remainder_amount - } else { - 0 + } else if native_tokens_remainder || mana_remainder { + outputs_sum += remainder_amount } + Ok((inputs_sum, outputs_sum)) } - fn fulfil<'a>( + fn reduce_funds_of_chains( &mut self, - transaction_builder: &TransactionBuilder, - inputs: impl Iterator, + input_amount: u64, + output_amount: &mut u64, ) -> 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, - transaction_builder.latest_slot_commitment_id.slot_index(), - ) { - // Skip if no additional amount is made available - if input.output.amount() == sdruc.amount() { - continue; - } - let input_sdr = self.inputs_sdr.get(sdruc.return_address()).unwrap_or(&0) + sdruc.amount(); - let output_sdr = *self.outputs_sdr.get(sdruc.return_address()).unwrap_or(&0); - - if input_sdr > output_sdr { - let diff = input_sdr - output_sdr; - self.outputs_sum += diff; - *self.outputs_sdr.entry(sdruc.return_address().clone()).or_default() += sdruc.amount(); - } - - *self.inputs_sdr.entry(sdruc.return_address().clone()).or_default() += sdruc.amount(); - } - - if let Some(nt) = input.output.native_token() { - self.selected_native_tokens.insert(*nt.token_id()); - } - - self.inputs_sum += input.output.amount(); - self.newly_selected_inputs.insert(*input.output_id(), input.clone()); - - 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(transaction_builder)?; + if *output_amount > input_amount { + // Only consider automatically transitioned outputs. + for output in self.added_outputs.iter_mut() { + let missing_amount = *output_amount - input_amount; + let amount = output.amount(); + let minimum_amount = output.minimum_amount(self.protocol_parameters.storage_score_parameters()); + + let new_amount = if amount >= missing_amount + minimum_amount { + *output_amount = input_amount; + amount - missing_amount + } else { + *output_amount -= amount - minimum_amount; + minimum_amount + }; + + // PANIC: unwrap is fine as non-chain outputs have been filtered out already. log::debug!( - "Calculated new remainder_amount: {remainder_amount}, native_tokens_remainder: {native_tokens_remainder}" + "Reducing amount of {} to {} to fulfill amount requirement", + output.chain_id().unwrap(), + new_amount ); - self.remainder_amount = remainder_amount; - self.native_tokens_remainder = native_tokens_remainder; - self.mana_remainder = mana_remainder; - } - if self.missing_amount() == 0 { - return Ok(true); + *output = match output { + Output::Account(output) => AccountOutputBuilder::from(&*output) + .with_amount(new_amount) + .finish_output()?, + Output::Foundry(output) => FoundryOutputBuilder::from(&*output) + .with_amount(new_amount) + .finish_output()?, + Output::Nft(output) => NftOutputBuilder::from(&*output) + .with_amount(new_amount) + .finish_output()?, + _ => continue, + }; + + if *output_amount == input_amount { + break; + } } } - Ok(false) - } - - 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()?; - - transaction_builder.required_remainder_amount(Some(input_native_tokens)) - } - - fn into_newly_selected_inputs(self) -> Vec { - self.newly_selected_inputs.into_values().collect() + Ok(input_amount >= *output_amount) } } -impl TransactionBuilder { - fn fulfil<'a>( - &self, - base_inputs: impl Iterator + Clone, - amount_selection: &mut AmountSelection, - ) -> Result { - let slot_index = self.latest_slot_commitment_id.slot_index(); - - // No native token, expired SDRUC. - let inputs = base_inputs.clone().filter(|input| { - input.output.native_token().is_none() && sdruc_not_expired(&input.output, slot_index).is_none() - }); - - if amount_selection.fulfil(self, inputs)? { - return Ok(true); - } - - // No native token, unexpired SDRUC. - let inputs = base_inputs.clone().filter(|input| { - input.output.native_token().is_none() && sdruc_not_expired(&input.output, slot_index).is_some() - }); - - if amount_selection.fulfil(self, inputs)? { - return Ok(true); - } - - // Native token, expired SDRUC. - let inputs = base_inputs.clone().filter(|input| { - input.output.native_token().is_some() && sdruc_not_expired(&input.output, slot_index).is_none() - }); - - if amount_selection.fulfil(self, inputs)? { - return Ok(true); - } - - // Native token, unexpired SDRUC. - let inputs = base_inputs.clone().filter(|input| { - input.output.native_token().is_some() && sdruc_not_expired(&input.output, slot_index).is_some() - }); - - if amount_selection.fulfil(self, inputs)? { - return Ok(true); - } - - // Everything else. - if amount_selection.fulfil(self, base_inputs)? { - return Ok(true); - } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +struct AmountPriority { + kind_priority: usize, + has_native_token: bool, +} - Ok(false) +impl PartialOrd for AmountPriority { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) } - - 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(); - let amount = output.amount(); - let minimum_amount = output.minimum_amount(self.protocol_parameters.storage_score_parameters()); - - let new_amount = if amount >= diff + minimum_amount { - amount - diff - } else { - minimum_amount - }; - - // TODO check that new_amount is enough for the storage cost - - // PANIC: unwrap is fine as non-chain outputs have been filtered out already. - log::debug!( - "Reducing amount of {} to {} to fulfill amount requirement", - output.chain_id().unwrap(), - new_amount - ); - - let new_output = match output { - Output::Account(output) => AccountOutputBuilder::from(&*output) - .with_amount(new_amount) - .finish_output()?, - Output::Foundry(output) => FoundryOutputBuilder::from(&*output) - .with_amount(new_amount) - .finish_output()?, - Output::Nft(output) => NftOutputBuilder::from(&*output) - .with_amount(new_amount) - .finish_output()?, - _ => panic!("only account, nft and foundry can be automatically created"), - }; - - amount_selection.outputs_sum -= amount - new_amount; - *output = new_output; - - if amount_selection.missing_amount() == 0 { - return Ok(()); - } - } - - Err(TransactionBuilderError::InsufficientAmount { - found: amount_selection.inputs_sum, - required: amount_selection.inputs_sum + amount_selection.missing_amount(), - }) +} +impl Ord for AmountPriority { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + (self.kind_priority, self.has_native_token).cmp(&(other.kind_priority, other.has_native_token)) } +} - pub(crate) fn fulfill_amount_requirement(&mut self) -> Result, TransactionBuilderError> { - let mut amount_selection = AmountSelection::new(self)?; - - if amount_selection.missing_amount() == 0 { - log::debug!("Amount requirement already fulfilled"); - return Ok(amount_selection.into_newly_selected_inputs()); - } else { - log::debug!( - "Fulfilling amount requirement with input {}, output {}, input sdrs {:?} and output sdrs {:?}", - amount_selection.inputs_sum, - amount_selection.outputs_sum, - amount_selection.inputs_sdr, - amount_selection.outputs_sdr - ); - } - - // TODO if consolidate strategy: sum all the lowest amount until diff is covered. - // TODO this would be lowest amount of input strategy. - - // Try to select outputs first with ordering from low to high amount, if that fails, try reversed. - - log::debug!("Ordering inputs from low to high amount"); - // Sort inputs per amount, low to high. - self.available_inputs - .sort_by(|left, right| left.output.amount().cmp(&right.output.amount())); - - if let Some(r) = self.fulfill_amount_requirement_inner(&mut amount_selection)? { - return Ok(r); - } - - if self.selected_inputs.len() + amount_selection.newly_selected_inputs.len() > INPUT_COUNT_MAX.into() { - // Clear before trying with reversed ordering. - log::debug!("Clearing amount selection"); - amount_selection = AmountSelection::new(self)?; - - log::debug!("Ordering inputs from high to low amount"); - // Sort inputs per amount, high to low. - self.available_inputs - .sort_by(|left, right| right.output.amount().cmp(&left.output.amount())); - - if let Some(r) = self.fulfill_amount_requirement_inner(&mut amount_selection)? { - return Ok(r); - } - } - - if self.selected_inputs.len() + amount_selection.newly_selected_inputs.len() > INPUT_COUNT_MAX.into() { - return Err(TransactionBuilderError::InvalidInputCount( - self.selected_inputs.len() + amount_selection.newly_selected_inputs.len(), - )); - } - - if amount_selection.missing_amount() != 0 { - self.reduce_funds_of_chains(&mut amount_selection)?; - } - - log::debug!( - "Outputs {:?} selected to fulfill the amount requirement", - amount_selection.newly_selected_inputs - ); - - self.available_inputs - .retain(|input| !amount_selection.newly_selected_inputs.contains_key(input.output_id())); - - Ok(amount_selection.into_newly_selected_inputs()) +impl From<&InputSigningData> for Option { + fn from(value: &InputSigningData) -> Self { + sort_order_type() + .get(&value.output.kind()) + .map(|&kind_priority| AmountPriority { + kind_priority, + has_native_token: value.output.native_token().is_some(), + }) } +} - fn fulfill_amount_requirement_inner( - &mut self, - amount_selection: &mut AmountSelection, - ) -> Result>, TransactionBuilderError> { - let slot_index = self.latest_slot_commitment_id.slot_index(); +/// Establish the order in which we want to pick an input +pub fn sort_order_type() -> &'static HashMap { + static MAP: OnceLock> = OnceLock::new(); + MAP.get_or_init(|| { + [ + BasicOutput::KIND, + AccountOutput::KIND, + NftOutput::KIND, + FoundryOutput::KIND, + ] + .into_iter() + .zip(0_usize..) + .collect::>() + }) +} - let basic_ed25519_inputs = self.available_inputs.iter().filter(|input| { - if let Output::Basic(output) = &input.output { - output - .unlock_conditions() - .locked_address( - output.address(), - slot_index, - self.protocol_parameters.committable_age_range(), - ) - .expect("slot index was provided") - .expect("expiration unlockable outputs already filtered out") - .is_ed25519() - } else { - false +impl PriorityMap { + fn next(&mut self, missing_amount: u64, slot_committment_id: SlotCommitmentId) -> Option { + let amount_sort = |output: &Output| { + let mut amount = output.amount(); + if let Some(sdruc) = sdruc_not_expired(output, slot_committment_id.slot_index()) { + amount -= sdruc.amount(); } - }); - - if self.fulfil(basic_ed25519_inputs, amount_selection)? { - return Ok(None); - } - - let basic_non_ed25519_inputs = self.available_inputs.iter().filter(|input| { - if let Output::Basic(output) = &input.output { - !output - .unlock_conditions() - .locked_address( - output.address(), - slot_index, - self.protocol_parameters.committable_age_range(), - ) - .expect("slot index was provided") - .expect("expiration unlockable outputs already filtered out") - .is_ed25519() + // If the amount is greater than the missing amount, we want the smallest ones first + if amount >= missing_amount { + (false, amount) + // Otherwise, we want the biggest first } else { - false + (true, u64::MAX - amount) } - }); - - if self.fulfil(basic_non_ed25519_inputs, amount_selection)? { - return Ok(None); - } - - // Other kinds of outputs. - - log::debug!("Trying other types of outputs"); - - let mut inputs = self - .available_inputs - .iter() - .filter(|input| !input.output.is_basic()) - .peekable(); - - if inputs.peek().is_some() { - amount_selection.fulfil(self, inputs)?; - - log::debug!( - "Outputs {:?} selected to fulfill the amount requirement", - amount_selection.newly_selected_inputs - ); - log::debug!("Triggering another amount round as non-basic outputs need to be transitioned first"); - - if self.selected_inputs.len() + amount_selection.newly_selected_inputs.len() <= INPUT_COUNT_MAX.into() { - self.available_inputs - .retain(|input| !amount_selection.newly_selected_inputs.contains_key(input.output_id())); - - // TODO explanation of Amount - self.requirements.push(Requirement::Amount); - - Ok(Some(amount_selection.clone().into_newly_selected_inputs())) - } else { - Ok(None) + }; + if let Some((priority, mut inputs)) = self.0.pop_first() { + // Sort in reverse so we can pop from the back + inputs.sort_unstable_by(|i1, i2| amount_sort(&i2.output).cmp(&amount_sort(&i1.output))); + let input = inputs.pop(); + if !inputs.is_empty() { + self.0.insert(priority, inputs); } - } else { - Ok(None) + return input; } + None } } diff --git a/sdk/src/client/api/block_builder/transaction_builder/requirement/mana.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/mana.rs index 94538ad6ec..8c945907d3 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/requirement/mana.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/mana.rs @@ -1,20 +1,20 @@ // Copyright 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use std::collections::HashMap; +use std::{collections::HashMap, sync::OnceLock}; use super::{TransactionBuilder, TransactionBuilderError}; use crate::{ client::{ - api::transaction_builder::{MinManaAllotment, Requirement}, + api::transaction_builder::{requirement::PriorityMap, MinManaAllotment, Requirement}, secret::types::InputSigningData, }, types::block::{ address::Address, input::{Input, UtxoInput}, mana::ManaAllotment, - output::{AccountOutputBuilder, Output}, - payload::{dto::SignedTransactionPayloadDto, signed_transaction::Transaction, SignedTransactionPayload}, + output::{AccountOutput, AccountOutputBuilder, BasicOutput, FoundryOutput, NftOutput, Output}, + payload::{signed_transaction::Transaction, SignedTransactionPayload}, signature::Ed25519Signature, unlock::{AccountUnlock, NftUnlock, ReferenceUnlock, SignatureUnlock, Unlock, Unlocks}, BlockError, @@ -72,11 +72,6 @@ impl TransactionBuilder { 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(); @@ -256,8 +251,11 @@ impl TransactionBuilder { 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. - while let Some(input) = self.available_inputs.pop() { + let mut priority_map = PriorityMap::::generate(&mut self.available_inputs); + loop { + let Some(input) = priority_map.next(required_mana - selected_mana) else { + break; + }; selected_mana += self.total_mana(&input, include_generated)?; if let Some(output) = self.select_input(input)? { required_mana += output.mana(); @@ -268,6 +266,10 @@ impl TransactionBuilder { break; } } + // Return unselected inputs to the available list + for input in priority_map.into_inputs() { + self.available_inputs.push(input); + } } Ok(added_inputs) } @@ -329,3 +331,71 @@ impl TransactionBuilder { }) } } + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +struct ManaPriority { + kind_priority: usize, + has_native_token: bool, +} + +impl PartialOrd for ManaPriority { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl Ord for ManaPriority { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + (self.kind_priority, self.has_native_token).cmp(&(other.kind_priority, other.has_native_token)) + } +} + +impl From<&InputSigningData> for Option { + fn from(value: &InputSigningData) -> Self { + sort_order_type() + .get(&value.output.kind()) + .map(|&kind_priority| ManaPriority { + kind_priority, + has_native_token: value.output.native_token().is_some(), + }) + } +} + +/// Establish the order in which we want to pick an input +pub fn sort_order_type() -> &'static HashMap { + static MAP: OnceLock> = OnceLock::new(); + MAP.get_or_init(|| { + [ + BasicOutput::KIND, + NftOutput::KIND, + AccountOutput::KIND, + FoundryOutput::KIND, + ] + .into_iter() + .zip(0_usize..) + .collect::>() + }) +} + +impl PriorityMap { + fn next(&mut self, missing_mana: u64) -> Option { + let mana_sort = |mana: u64| { + // If the mana is greater than the missing mana, we want the smallest ones first + if mana >= missing_mana { + (false, mana) + // Otherwise, we want the biggest first + } else { + (true, u64::MAX - mana) + } + }; + if let Some((priority, mut inputs)) = self.0.pop_first() { + // Sort in reverse so we can pop from the back + inputs.sort_unstable_by(|i1, i2| mana_sort(i2.output.mana()).cmp(&mana_sort(i1.output.mana()))); + let input = inputs.pop(); + if !inputs.is_empty() { + self.0.insert(priority, inputs); + } + return input; + } + None + } +} diff --git a/sdk/src/client/api/block_builder/transaction_builder/requirement/mod.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/mod.rs index 02136c4839..03a4b80369 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/requirement/mod.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/mod.rs @@ -13,6 +13,8 @@ pub(crate) mod native_tokens; pub(crate) mod nft; pub(crate) mod sender; +use alloc::collections::BTreeMap; + 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, @@ -236,3 +238,30 @@ impl TransactionBuilder { Ok(()) } } + +/// A mapping of prioritized inputs. +/// This allows us to avoid sorting all available inputs every loop, and instead we iterate once and sort +/// only the smaller index vectors as needed. +#[derive(Debug)] +struct PriorityMap

(BTreeMap>); + +impl PriorityMap

+where + for<'a> Option

: From<&'a InputSigningData>, +{ + fn generate(available_inputs: &mut Vec) -> Self { + let inputs = core::mem::take(available_inputs); + Self(inputs.into_iter().fold(BTreeMap::new(), |mut map, i| { + if let Some(priority) = Option::

::from(&i) { + map.entry(priority).or_default().push(i); + } else { + available_inputs.push(i); + } + map + })) + } + + fn into_inputs(self) -> impl Iterator { + self.0.into_values().flatten() + } +} diff --git a/sdk/src/client/api/block_builder/transaction_builder/requirement/native_tokens.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/native_tokens.rs index e1da4ce475..57d467bf8b 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/requirement/native_tokens.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/native_tokens.rs @@ -1,6 +1,7 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use alloc::collections::BTreeMap; use std::{cmp::Ordering, collections::HashSet}; use primitive_types::U256; @@ -9,132 +10,121 @@ use super::{TransactionBuilder, TransactionBuilderError}; use crate::{ client::secret::types::InputSigningData, types::block::{ - output::{NativeToken, NativeTokens, NativeTokensBuilder, Output, TokenScheme}, + output::{Output, TokenId, TokenScheme}, payload::signed_transaction::TransactionCapabilityFlag, }, }; -pub(crate) fn get_native_tokens<'a>( - outputs: impl Iterator, -) -> Result { - let mut required_native_tokens = NativeTokensBuilder::new(); - - for output in outputs { - if let Some(native_token) = output.native_token() { - required_native_tokens.add_native_token(*native_token)?; - } - } - - Ok(required_native_tokens) -} - -// TODO only handles one side -pub(crate) fn get_native_tokens_diff( - inputs: &NativeTokensBuilder, - outputs: &NativeTokensBuilder, -) -> Result, TransactionBuilderError> { - let mut native_tokens_diff = NativeTokensBuilder::new(); - - for (token_id, input_amount) in inputs.iter() { - match outputs.get(token_id) { - None => { - native_tokens_diff.insert(*token_id, *input_amount); - } - Some(output_amount) => { - if input_amount > output_amount { - native_tokens_diff.insert(*token_id, input_amount - output_amount); - } - } - } - } - - if native_tokens_diff.is_empty() { - Ok(None) - } else { - Ok(Some(native_tokens_diff.finish()?)) - } -} - 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()?; - - input_native_tokens.merge(minted_native_tokens)?; - output_native_tokens.merge(melted_native_tokens)?; - - if let Some(burn) = self.burn.as_ref().filter(|burn| !burn.native_tokens.is_empty()) { + let (input_nts, output_nts) = self.get_input_output_native_tokens(); + let diffs = get_native_tokens_diff(output_nts, input_nts); + if self.burn.as_ref().map_or(false, |burn| !burn.native_tokens.is_empty()) { self.transaction_capabilities .add_capability(TransactionCapabilityFlag::BurnNativeTokens); - output_native_tokens.merge(NativeTokensBuilder::from(burn.native_tokens.clone()))?; } + if diffs.is_empty() { + log::debug!("Native tokens requirement already fulfilled"); - // TODO weird that it happens in this direction? - if let Some(diffs) = get_native_tokens_diff(&output_native_tokens, &input_native_tokens)? { - log::debug!( - "Fulfilling native tokens requirement with input {input_native_tokens:?} and output {output_native_tokens:?}" - ); - - let mut newly_selected_inputs = Vec::new(); - let mut newly_selected_ids = HashSet::new(); - - for diff in diffs.iter() { - let mut amount = U256::zero(); - // TODO sort ? - let inputs = self.available_inputs.iter().filter(|input| { - input - .output - .native_token() - .is_some_and(|native_token| native_token.token_id() == diff.token_id()) - }); - - for input in inputs { - amount += input - .output - .native_token() - // PANIC: safe to unwrap as the filter guarantees inputs containing this native token. - .unwrap() - .amount(); - - if newly_selected_ids.insert(*input.output_id()) { - newly_selected_inputs.push(input.clone()); - } + return Ok(Vec::new()); + } - if amount >= diff.amount() { - break; - } + log::debug!("Fulfilling native tokens requirement"); + + let mut newly_selected_inputs = Vec::new(); + let mut newly_selected_ids = HashSet::new(); + + for (&token_id, &amount) in diffs.iter() { + let mut input_amount = U256::zero(); + // TODO sort ? + let inputs = self.available_inputs.iter().filter(|input| { + input + .output + .native_token() + .is_some_and(|native_token| native_token.token_id() == &token_id) + }); + + for input in inputs { + input_amount += input + .output + .native_token() + // PANIC: safe to unwrap as the filter guarantees inputs containing this native token. + .unwrap() + .amount(); + + if newly_selected_ids.insert(*input.output_id()) { + newly_selected_inputs.push(input.clone()); } - if amount < diff.amount() { - return Err(TransactionBuilderError::InsufficientNativeTokenAmount { - token_id: *diff.token_id(), - found: amount, - required: diff.amount(), - }); + if input_amount >= amount { + break; } } - log::debug!("Outputs {newly_selected_ids:?} selected to fulfill the native tokens requirement"); + if input_amount < amount { + return Err(TransactionBuilderError::InsufficientNativeTokenAmount { + token_id, + found: input_amount, + required: amount, + }); + } + } + + log::debug!("Outputs {newly_selected_ids:?} selected to fulfill the native tokens requirement"); - self.available_inputs - .retain(|input| !newly_selected_ids.contains(input.output_id())); + self.available_inputs + .retain(|input| !newly_selected_ids.contains(input.output_id())); - Ok(newly_selected_inputs) - } else { - log::debug!("Native tokens requirement already fulfilled"); + Ok(newly_selected_inputs) + } - Ok(Vec::new()) + pub(crate) fn get_input_output_native_tokens(&self) -> (BTreeMap, BTreeMap) { + let mut input_native_tokens = self + .selected_inputs + .iter() + .filter_map(|i| i.output.native_token().map(|t| (*t.token_id(), t.amount()))) + .fold(BTreeMap::new(), |mut nts, (token_id, amount)| { + *nts.entry(token_id).or_default() += amount; + nts + }); + let mut output_native_tokens = self + .non_remainder_outputs() + .filter_map(|output| output.native_token().map(|t| (*t.token_id(), t.amount()))) + .fold(BTreeMap::new(), |mut nts, (token_id, amount)| { + *nts.entry(token_id).or_default() += amount; + nts + }); + let (minted_native_tokens, melted_native_tokens) = self.get_minted_and_melted_native_tokens(); + + minted_native_tokens + .into_iter() + .fold(&mut input_native_tokens, |nts, (token_id, amount)| { + *nts.entry(token_id).or_default() += amount; + nts + }); + melted_native_tokens + .into_iter() + .fold(&mut output_native_tokens, |nts, (token_id, amount)| { + *nts.entry(token_id).or_default() += amount; + nts + }); + + if let Some(burn) = self.burn.as_ref() { + burn.native_tokens + .iter() + .fold(&mut output_native_tokens, |nts, (token_id, amount)| { + *nts.entry(*token_id).or_default() += *amount; + nts + }); } + (input_native_tokens, output_native_tokens) } - pub(crate) fn get_minted_and_melted_native_tokens( - &self, - ) -> Result<(NativeTokensBuilder, NativeTokensBuilder), TransactionBuilderError> { - let mut minted_native_tokens = NativeTokensBuilder::new(); - let mut melted_native_tokens = NativeTokensBuilder::new(); + pub(crate) fn get_minted_and_melted_native_tokens(&self) -> (BTreeMap, BTreeMap) { + let mut minted_native_tokens = BTreeMap::new(); + let mut melted_native_tokens = BTreeMap::new(); for output in self.non_remainder_outputs() { if let Output::Foundry(output_foundry) = output { @@ -157,15 +147,13 @@ impl TransactionBuilder { let minted_native_token_amount = output_foundry_simple_ts.circulating_supply() - input_foundry_simple_ts.circulating_supply(); - minted_native_tokens - .add_native_token(NativeToken::new(token_id, minted_native_token_amount)?)?; + *minted_native_tokens.entry(token_id).or_default() += minted_native_token_amount; } Ordering::Less => { let melted_native_token_amount = input_foundry_simple_ts.circulating_supply() - output_foundry_simple_ts.circulating_supply(); - melted_native_tokens - .add_native_token(NativeToken::new(token_id, melted_native_token_amount)?)?; + *melted_native_tokens.entry(token_id).or_default() += melted_native_token_amount; } Ordering::Equal => {} } @@ -176,16 +164,25 @@ impl TransactionBuilder { // If we created the foundry with this transaction, then we need to add the circulating supply as minted // tokens if initial_creation { - let circulating_supply = output_foundry_simple_ts.circulating_supply(); - - if !circulating_supply.is_zero() { - minted_native_tokens - .add_native_token(NativeToken::new(output_foundry.token_id(), circulating_supply)?)?; - } + *minted_native_tokens.entry(output_foundry.token_id()).or_default() += + output_foundry_simple_ts.circulating_supply(); } } } - Ok((minted_native_tokens, melted_native_tokens)) + (minted_native_tokens, melted_native_tokens) } } + +pub(crate) fn get_native_tokens_diff( + first: BTreeMap, + second: BTreeMap, +) -> BTreeMap { + first + .into_iter() + .filter_map(|(id, in_amount)| { + let out_amount = second.get(&id).copied().unwrap_or_default(); + (in_amount > out_amount).then_some((id, in_amount.saturating_sub(out_amount))) + }) + .collect() +} diff --git a/sdk/tests/client/signing/mod.rs b/sdk/tests/client/signing/mod.rs index 258fda7eea..39a93557ad 100644 --- a/sdk/tests/client/signing/mod.rs +++ b/sdk/tests/client/signing/mod.rs @@ -23,7 +23,6 @@ use iota_sdk::{ payload::{signed_transaction::Transaction, SignedTransactionPayload}, protocol::iota_mainnet_protocol_parameters, slot::{SlotCommitmentHash, SlotCommitmentId, SlotIndex}, - unlock::{SignatureUnlock, Unlock}, }, }; use pretty_assertions::assert_eq; @@ -401,83 +400,11 @@ async fn all_combined() -> Result<(), Box> { .transaction_unlocks(&prepared_transaction_data, &protocol_parameters) .await?; - assert_eq!(unlocks.len(), 15); - assert_eq!((*unlocks).first().unwrap().kind(), SignatureUnlock::KIND); - match (*unlocks).get(1).unwrap() { - Unlock::Reference(a) => { - assert_eq!(a.index(), 0); - } - _ => panic!("Invalid unlock 1"), - } - assert_eq!((*unlocks).get(2).unwrap().kind(), SignatureUnlock::KIND); - assert_eq!((*unlocks).get(3).unwrap().kind(), SignatureUnlock::KIND); - match (*unlocks).get(4).unwrap() { - Unlock::Reference(a) => { - assert_eq!(a.index(), 3); - } - _ => panic!("Invalid unlock 4"), - } - match (*unlocks).get(5).unwrap() { - Unlock::Reference(a) => { - assert_eq!(a.index(), 3); - } - _ => panic!("Invalid unlock 5"), - } - match (*unlocks).get(6).unwrap() { - Unlock::Account(a) => { - assert_eq!(a.index(), 5); - } - _ => panic!("Invalid unlock 6"), - } - match (*unlocks).get(7).unwrap() { - Unlock::Account(a) => { - assert_eq!(a.index(), 5); - } - _ => panic!("Invalid unlock 7"), - } - match (*unlocks).get(8).unwrap() { - Unlock::Reference(a) => { - assert_eq!(a.index(), 3); - } - _ => panic!("Invalid unlock 8"), - } - - match (*unlocks).get(9).unwrap() { - Unlock::Nft(a) => { - assert_eq!(a.index(), 8); - } - _ => panic!("Invalid unlock 9"), - } - match (*unlocks).get(10).unwrap() { - Unlock::Account(a) => { - assert_eq!(a.index(), 9); - } - _ => panic!("Invalid unlock 10"), - } - match (*unlocks).get(11).unwrap() { - Unlock::Account(a) => { - assert_eq!(a.index(), 9); - } - _ => panic!("Invalid unlock 11"), - } - match (*unlocks).get(12).unwrap() { - Unlock::Account(a) => { - assert_eq!(a.index(), 9); - } - _ => panic!("Invalid unlock 12"), - } - match (*unlocks).get(13).unwrap() { - Unlock::Nft(a) => { - assert_eq!(a.index(), 11); - } - _ => panic!("Invalid unlock 13"), - } - match (*unlocks).get(14).unwrap() { - Unlock::Nft(a) => { - assert_eq!(a.index(), 10); - } - _ => panic!("Invalid unlock 14"), - } + assert_eq!(unlocks.len(), 13); + assert_eq!(unlocks.iter().filter(|u| u.is_signature()).count(), 3); + assert_eq!(unlocks.iter().filter(|u| u.is_reference()).count(), 4); + assert_eq!(unlocks.iter().filter(|u| u.is_account()).count(), 3); + assert_eq!(unlocks.iter().filter(|u| u.is_nft()).count(), 3); let tx_payload = SignedTransactionPayload::new(prepared_transaction_data.transaction.clone(), unlocks)?; diff --git a/sdk/tests/client/transaction_builder/basic_outputs.rs b/sdk/tests/client/transaction_builder/basic_outputs.rs index 56362ea42c..49427af41f 100644 --- a/sdk/tests/client/transaction_builder/basic_outputs.rs +++ b/sdk/tests/client/transaction_builder/basic_outputs.rs @@ -1562,7 +1562,7 @@ fn two_inputs_remainder_3() { Some(SLOT_INDEX), ); let outputs = build_outputs([Basic { - amount: 1_750_000, + amount: 2_750_000, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1589,7 +1589,7 @@ fn two_inputs_remainder_3() { if !outputs.contains(output) { assert_remainder_or_return( output, - 1_250_000, + 250_000, Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), None, ); @@ -1754,7 +1754,7 @@ fn too_many_inputs() { ) }) .take(129), - None, + Some(SLOT_INDEX), ); let outputs = build_outputs([Basic { amount: 129_000_000, @@ -1803,7 +1803,7 @@ fn more_than_max_inputs_only_one_needed() { ) }) .take(1000), - None, + Some(SLOT_INDEX), ); // Add the needed input let needed_input = build_inputs( diff --git a/sdk/tests/client/transaction_builder/native_tokens.rs b/sdk/tests/client/transaction_builder/native_tokens.rs index a84f317993..9154120c13 100644 --- a/sdk/tests/client/transaction_builder/native_tokens.rs +++ b/sdk/tests/client/transaction_builder/native_tokens.rs @@ -962,15 +962,24 @@ fn two_basic_outputs_1() { .unwrap(); assert_eq!(selected.inputs_data.len(), 1); - assert!(selected.inputs_data.contains(&inputs[0])); + assert!(selected.inputs_data.contains(&inputs[0]) || selected.inputs_data.contains(&inputs[1])); assert_eq!(selected.transaction.outputs().len(), 2); assert!(selected.transaction.outputs().contains(&outputs[0])); - assert_remainder_or_return( - &selected.transaction.outputs()[1], - 500_000, - Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), - Some((TOKEN_ID_1, 100)), - ); + if selected.inputs_data.contains(&inputs[0]) { + assert_remainder_or_return( + &selected.transaction.outputs()[1], + 500_000, + Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), + Some((TOKEN_ID_1, 100)), + ); + } else { + assert_remainder_or_return( + &selected.transaction.outputs()[1], + 500_000, + Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), + Some((TOKEN_ID_1, 200)), + ); + } } #[test] diff --git a/sdk/tests/client/transaction_builder/outputs.rs b/sdk/tests/client/transaction_builder/outputs.rs index df100cc656..6335a3df09 100644 --- a/sdk/tests/client/transaction_builder/outputs.rs +++ b/sdk/tests/client/transaction_builder/outputs.rs @@ -10,7 +10,7 @@ use iota_sdk::{ }, types::block::{ address::Address, - output::{unlock_condition::AddressUnlockCondition, AccountId, BasicOutputBuilder}, + output::{unlock_condition::AddressUnlockCondition, AccountId, BasicOutputBuilder, NftId}, payload::signed_transaction::{TransactionCapabilities, TransactionCapabilityFlag}, protocol::iota_mainnet_protocol_parameters, rand::output::{rand_output_id_with_slot_index, rand_output_metadata_with_id}, @@ -20,8 +20,9 @@ use pretty_assertions::assert_eq; use crate::client::{ assert_remainder_or_return, build_inputs, build_outputs, unsorted_eq, - Build::{Account, Basic}, - ACCOUNT_ID_1, ACCOUNT_ID_2, BECH32_ADDRESS_ED25519_0, BECH32_ADDRESS_ED25519_1, SLOT_COMMITMENT_ID, SLOT_INDEX, + Build::{Account, Basic, Nft}, + ACCOUNT_ID_1, ACCOUNT_ID_2, BECH32_ADDRESS_ED25519_0, BECH32_ADDRESS_ED25519_1, NFT_ID_1, SLOT_COMMITMENT_ID, + SLOT_INDEX, }; #[test] @@ -320,15 +321,16 @@ fn two_addresses_one_missing() { SLOT_COMMITMENT_ID, protocol_parameters, ) - .finish(); + .finish() + .unwrap_err(); - assert!(matches!( + assert_eq!( selected, - Err(TransactionBuilderError::InsufficientAmount { + TransactionBuilderError::InsufficientAmount { found: 1_000_000, required: 2_000_000, - }) - )); + } + ); } #[test] @@ -455,3 +457,117 @@ fn consolidate_with_min_allotment() { assert_eq!(selected.transaction.allotments()[0].mana(), 39440); assert_eq!(selected.transaction.outputs().iter().map(|o| o.mana()).sum::(), 0); } + +#[test] +fn transition_no_more_than_needed_for_account_amount() { + let protocol_parameters = iota_mainnet_protocol_parameters().clone(); + let account_id_2 = AccountId::from_str(ACCOUNT_ID_2).unwrap(); + let nft_id_1 = NftId::from_str(NFT_ID_1).unwrap(); + + let inputs = build_inputs( + [ + ( + Account { + amount: 500_000, + account_id: account_id_2, + address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), + sender: None, + issuer: None, + }, + None, + ), + ( + Nft { + amount: 500_000, + nft_id: nft_id_1, + address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), + sender: None, + issuer: None, + sdruc: None, + expiration: None, + }, + None, + ), + ], + Some(SLOT_INDEX), + ); + let outputs = build_outputs([Account { + amount: 500_000, + account_id: account_id_2, + address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), + sender: None, + issuer: None, + }]); + + let selected = TransactionBuilder::new( + inputs.clone(), + outputs.clone(), + [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], + SLOT_INDEX, + SLOT_COMMITMENT_ID, + protocol_parameters, + ) + .finish() + .unwrap(); + + assert_eq!(selected.inputs_data.len(), 1); + assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); +} + +#[test] +fn transition_no_more_than_needed_for_nft_amount() { + let protocol_parameters = iota_mainnet_protocol_parameters().clone(); + let account_id_2 = AccountId::from_str(ACCOUNT_ID_2).unwrap(); + let nft_id_1 = NftId::from_str(NFT_ID_1).unwrap(); + + let inputs = build_inputs( + [ + ( + Account { + amount: 500_000, + account_id: account_id_2, + address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), + sender: None, + issuer: None, + }, + None, + ), + ( + Nft { + amount: 500_000, + nft_id: nft_id_1, + address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), + sender: None, + issuer: None, + sdruc: None, + expiration: None, + }, + None, + ), + ], + Some(SLOT_INDEX), + ); + let outputs = build_outputs([Nft { + amount: 500_000, + nft_id: nft_id_1, + address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), + sender: None, + issuer: None, + sdruc: None, + expiration: None, + }]); + + let selected = TransactionBuilder::new( + inputs.clone(), + outputs.clone(), + [Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()], + SLOT_INDEX, + SLOT_COMMITMENT_ID, + protocol_parameters, + ) + .finish() + .unwrap(); + + assert_eq!(selected.inputs_data.len(), 1); + assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); +} From c132578662d948faa59421258da855186accd077 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Tue, 5 Mar 2024 20:48:58 +0100 Subject: [PATCH 09/21] Remove output `transition_inner`s (#2136) * Remove output `transition_inner`s * nit --------- Co-authored-by: DaughterOfMars --- sdk/src/types/block/output/account.rs | 57 ------ sdk/src/types/block/output/anchor.rs | 34 ---- sdk/src/types/block/output/delegation.rs | 17 -- sdk/src/types/block/output/foundry.rs | 92 +-------- sdk/src/types/block/output/nft.rs | 10 - .../types/block/semantic/state_transition.rs | 192 +++++++++++++++--- 6 files changed, 169 insertions(+), 233 deletions(-) diff --git a/sdk/src/types/block/output/account.rs b/sdk/src/types/block/output/account.rs index 1375ac4c11..c386d42123 100644 --- a/sdk/src/types/block/output/account.rs +++ b/sdk/src/types/block/output/account.rs @@ -3,7 +3,6 @@ use alloc::collections::BTreeSet; -use hashbrown::HashMap; use packable::{ error::{UnpackError, UnpackErrorExt}, packer::Packer, @@ -23,7 +22,6 @@ use crate::types::block::{ StorageScoreParameters, }, protocol::{ProtocolParameters, WorkScore, WorkScoreParameters}, - semantic::TransactionFailureReason, slot::SlotIndex, }; @@ -442,61 +440,6 @@ impl AccountOutput { potential: potential_mana, }) } - - // Transition, just without full SemanticValidationContext - pub(crate) fn transition_inner( - current_state: &Self, - next_state: &Self, - input_chains: &HashMap, - outputs: &[Output], - ) -> Result<(), TransactionFailureReason> { - if current_state.immutable_features != next_state.immutable_features { - return Err(TransactionFailureReason::ChainOutputImmutableFeaturesChanged); - } - - // TODO update when TIP is updated - // // Governance transition. - // if current_state.amount != next_state.amount - // || current_state.foundry_counter != next_state.foundry_counter - // { - // return Err(StateTransitionError::MutatedFieldWithoutRights); - // } - - // // State transition. - // if current_state.features.metadata() != next_state.features.metadata() { - // return Err(StateTransitionError::MutatedFieldWithoutRights); - // } - - let created_foundries = outputs.iter().filter_map(|output| { - if let Output::Foundry(foundry) = output { - if foundry.account_address().account_id() == &next_state.account_id - && !input_chains.contains_key(&foundry.chain_id()) - { - Some(foundry) - } else { - None - } - } else { - None - } - }); - - let mut created_foundries_count = 0; - - for foundry in created_foundries { - created_foundries_count += 1; - - if foundry.serial_number() != current_state.foundry_counter + created_foundries_count { - return Err(TransactionFailureReason::FoundrySerialInvalid); - } - } - - if current_state.foundry_counter + created_foundries_count != next_state.foundry_counter { - return Err(TransactionFailureReason::AccountInvalidFoundryCounter); - } - - Ok(()) - } } impl StorageScore for AccountOutput { diff --git a/sdk/src/types/block/output/anchor.rs b/sdk/src/types/block/output/anchor.rs index 28538a3a16..9c59ef5b45 100644 --- a/sdk/src/types/block/output/anchor.rs +++ b/sdk/src/types/block/output/anchor.rs @@ -3,7 +3,6 @@ use alloc::collections::BTreeSet; -use hashbrown::HashMap; use packable::{ error::{UnpackError, UnpackErrorExt}, packer::Packer, @@ -498,39 +497,6 @@ impl AnchorOutput { potential: potential_mana, }) } - - // Transition, just without full ValidationContext - pub(crate) fn transition_inner( - current_state: &Self, - next_state: &Self, - _input_chains: &HashMap, - _outputs: &[Output], - ) -> Result<(), TransactionFailureReason> { - if current_state.immutable_features != next_state.immutable_features { - return Err(TransactionFailureReason::ChainOutputImmutableFeaturesChanged); - } - - if next_state.state_index == current_state.state_index + 1 { - // State transition. - if current_state.state_controller_address() != next_state.state_controller_address() - || current_state.governor_address() != next_state.governor_address() - || current_state.features.metadata() != next_state.features.metadata() - { - return Err(TransactionFailureReason::AnchorInvalidStateTransition); - } - } else if next_state.state_index == current_state.state_index { - // Governance transition. - if current_state.amount != next_state.amount - || current_state.features().state_metadata() != next_state.features().state_metadata() - { - return Err(TransactionFailureReason::AnchorInvalidGovernanceTransition); - } - } else { - return Err(TransactionFailureReason::AnchorInvalidStateTransition); - } - - Ok(()) - } } impl StorageScore for AnchorOutput { diff --git a/sdk/src/types/block/output/delegation.rs b/sdk/src/types/block/output/delegation.rs index e3e8d7f098..eadc8ccd39 100644 --- a/sdk/src/types/block/output/delegation.rs +++ b/sdk/src/types/block/output/delegation.rs @@ -17,7 +17,6 @@ use crate::types::block::{ StorageScoreParameters, }, protocol::{ProtocolParameters, WorkScore, WorkScoreParameters}, - semantic::TransactionFailureReason, slot::{EpochIndex, SlotIndex}, }; @@ -383,22 +382,6 @@ impl DelegationOutput { potential: potential_mana, }) } - - // Transition, just without full SemanticValidationContext. - pub(crate) fn transition_inner(current_state: &Self, next_state: &Self) -> Result<(), TransactionFailureReason> { - if !current_state.delegation_id.is_null() || next_state.delegation_id.is_null() { - return Err(TransactionFailureReason::DelegationOutputTransitionedTwice); - } - - if current_state.delegated_amount != next_state.delegated_amount - || current_state.start_epoch != next_state.start_epoch - || current_state.validator_address != next_state.validator_address - { - return Err(TransactionFailureReason::DelegationModified); - } - - Ok(()) - } } impl StorageScore for DelegationOutput { diff --git a/sdk/src/types/block/output/foundry.rs b/sdk/src/types/block/output/foundry.rs index 54bb85d5e8..6ed1c2a764 100644 --- a/sdk/src/types/block/output/foundry.rs +++ b/sdk/src/types/block/output/foundry.rs @@ -1,8 +1,7 @@ // Copyright 2021 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use alloc::collections::{BTreeMap, BTreeSet}; -use core::cmp::Ordering; +use alloc::collections::BTreeSet; use packable::{ error::{UnpackError, UnpackErrorExt}, @@ -10,7 +9,6 @@ use packable::{ unpacker::Unpacker, Packable, PackableExt, }; -use primitive_types::U256; use crate::types::block::{ address::{AccountAddress, Address}, @@ -21,9 +19,7 @@ use crate::types::block::{ ChainId, DecayedMana, MinimumOutputAmount, NativeToken, Output, OutputBuilderAmount, OutputError, StorageScore, StorageScoreParameters, TokenId, TokenScheme, }, - payload::signed_transaction::{TransactionCapabilities, TransactionCapabilityFlag}, protocol::{ProtocolParameters, WorkScore, WorkScoreParameters}, - semantic::TransactionFailureReason, slot::SlotIndex, }; @@ -455,92 +451,6 @@ impl FoundryOutput { potential: potential_mana, }) } - - // Transition, just without full SemanticValidationContext - pub(crate) fn transition_inner( - current_state: &Self, - next_state: &Self, - input_native_tokens: &BTreeMap, - output_native_tokens: &BTreeMap, - capabilities: &TransactionCapabilities, - ) -> Result<(), TransactionFailureReason> { - if current_state.account_address() != next_state.account_address() - || current_state.serial_number != next_state.serial_number - || current_state.immutable_features != next_state.immutable_features - { - return Err(TransactionFailureReason::ChainOutputImmutableFeaturesChanged); - } - - let token_id = next_state.token_id(); - let input_tokens = input_native_tokens.get(&token_id).copied().unwrap_or_default(); - let output_tokens = output_native_tokens.get(&token_id).copied().unwrap_or_default(); - let TokenScheme::Simple(ref current_token_scheme) = current_state.token_scheme; - let TokenScheme::Simple(ref next_token_scheme) = next_state.token_scheme; - - if current_token_scheme.maximum_supply() != next_token_scheme.maximum_supply() { - return Err(TransactionFailureReason::SimpleTokenSchemeMaximumSupplyChanged); - } - - if current_token_scheme.minted_tokens() > next_token_scheme.minted_tokens() - || current_token_scheme.melted_tokens() > next_token_scheme.melted_tokens() - { - return Err(TransactionFailureReason::SimpleTokenSchemeMintedMeltedTokenDecrease); - } - - match input_tokens.cmp(&output_tokens) { - Ordering::Less => { - // Mint - - // This can't underflow as it is known that current_minted_tokens <= next_minted_tokens. - let minted_diff = next_token_scheme.minted_tokens() - current_token_scheme.minted_tokens(); - // This can't underflow as it is known that input_tokens < output_tokens (Ordering::Less). - let token_diff = output_tokens - input_tokens; - - if minted_diff != token_diff { - return Err(TransactionFailureReason::NativeTokenSumUnbalanced); - } - - if current_token_scheme.melted_tokens() != next_token_scheme.melted_tokens() { - return Err(TransactionFailureReason::NativeTokenSumUnbalanced); - } - } - Ordering::Equal => { - // Transition - - if current_token_scheme.minted_tokens() != next_token_scheme.minted_tokens() - || current_token_scheme.melted_tokens() != next_token_scheme.melted_tokens() - { - return Err(TransactionFailureReason::NativeTokenSumUnbalanced); - } - } - Ordering::Greater => { - // Melt / Burn - - if current_token_scheme.melted_tokens() != next_token_scheme.melted_tokens() - && current_token_scheme.minted_tokens() != next_token_scheme.minted_tokens() - { - return Err(TransactionFailureReason::NativeTokenSumUnbalanced); - } - - // This can't underflow as it is known that current_melted_tokens <= next_melted_tokens. - let melted_diff = next_token_scheme.melted_tokens() - current_token_scheme.melted_tokens(); - // This can't underflow as it is known that input_tokens > output_tokens (Ordering::Greater). - let token_diff = input_tokens - output_tokens; - - if melted_diff > token_diff { - return Err(TransactionFailureReason::NativeTokenSumUnbalanced); - } - - let burned_diff = token_diff - melted_diff; - - if !burned_diff.is_zero() && !capabilities.has_capability(TransactionCapabilityFlag::BurnNativeTokens) { - return Err(TransactionFailureReason::CapabilitiesNativeTokenBurningNotAllowed)?; - } - } - } - - Ok(()) - } } impl StorageScore for FoundryOutput { diff --git a/sdk/src/types/block/output/nft.rs b/sdk/src/types/block/output/nft.rs index 1cfa541c5c..da879a6166 100644 --- a/sdk/src/types/block/output/nft.rs +++ b/sdk/src/types/block/output/nft.rs @@ -22,7 +22,6 @@ use crate::types::block::{ OutputId, StorageScore, StorageScoreParameters, }, protocol::{ProtocolParameters, WorkScore, WorkScoreParameters}, - semantic::TransactionFailureReason, slot::SlotIndex, }; @@ -453,15 +452,6 @@ impl NftOutput { potential: potential_mana, }) } - - // Transition, just without full SemanticValidationContext - pub(crate) fn transition_inner(current_state: &Self, next_state: &Self) -> Result<(), TransactionFailureReason> { - if current_state.immutable_features != next_state.immutable_features { - return Err(TransactionFailureReason::ChainOutputImmutableFeaturesChanged); - } - - Ok(()) - } } impl StorageScore for NftOutput { diff --git a/sdk/src/types/block/semantic/state_transition.rs b/sdk/src/types/block/semantic/state_transition.rs index f2cd780db3..fa287e6e7d 100644 --- a/sdk/src/types/block/semantic/state_transition.rs +++ b/sdk/src/types/block/semantic/state_transition.rs @@ -1,6 +1,8 @@ // Copyright 2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use core::cmp::Ordering; + use crate::types::block::{ output::{ AccountOutput, AnchorOutput, BasicOutput, ChainId, DelegationOutput, FoundryOutput, NftOutput, Output, @@ -192,6 +194,51 @@ impl StateTransitionVerifier for AccountOutput { next_state: &Self, context: &SemanticValidationContext<'_>, ) -> Result<(), TransactionFailureReason> { + if current_state.immutable_features() != next_state.immutable_features() { + return Err(TransactionFailureReason::ChainOutputImmutableFeaturesChanged); + } + + // TODO update when TIP is updated + // // Governance transition. + // if current_state.amount != next_state.amount + // || current_state.foundry_counter != next_state.foundry_counter + // { + // return Err(StateTransitionError::MutatedFieldWithoutRights); + // } + + // // State transition. + // if current_state.features.metadata() != next_state.features.metadata() { + // return Err(StateTransitionError::MutatedFieldWithoutRights); + // } + + let created_foundries = context.transaction.outputs().iter().filter_map(|output| { + if let Output::Foundry(foundry) = output { + if foundry.account_address().account_id() == next_state.account_id() + && !context.input_chains.contains_key(&foundry.chain_id()) + { + Some(foundry) + } else { + None + } + } else { + None + } + }); + + let mut created_foundries_count = 0; + + for foundry in created_foundries { + created_foundries_count += 1; + + if foundry.serial_number() != current_state.foundry_counter() + created_foundries_count { + return Err(TransactionFailureReason::FoundrySerialInvalid); + } + } + + if current_state.foundry_counter() + created_foundries_count != next_state.foundry_counter() { + return Err(TransactionFailureReason::AccountInvalidFoundryCounter); + } + match ( current_state.features().block_issuer(), next_state.features().block_issuer(), @@ -299,12 +346,7 @@ impl StateTransitionVerifier for AccountOutput { _ => {} } - Self::transition_inner( - current_state, - next_state, - &context.input_chains, - context.transaction.outputs(), - ) + Ok(()) } fn destruction( @@ -371,14 +413,32 @@ impl StateTransitionVerifier for AnchorOutput { current_state: &Self, _next_output_id: &OutputId, next_state: &Self, - context: &SemanticValidationContext<'_>, + _context: &SemanticValidationContext<'_>, ) -> Result<(), TransactionFailureReason> { - Self::transition_inner( - current_state, - next_state, - &context.input_chains, - context.transaction.outputs(), - ) + if current_state.immutable_features() != next_state.immutable_features() { + return Err(TransactionFailureReason::ChainOutputImmutableFeaturesChanged); + } + + if next_state.state_index() == current_state.state_index() + 1 { + // State transition. + if current_state.state_controller_address() != next_state.state_controller_address() + || current_state.governor_address() != next_state.governor_address() + || current_state.features().metadata() != next_state.features().metadata() + { + return Err(TransactionFailureReason::AnchorInvalidStateTransition); + } + } else if next_state.state_index() == current_state.state_index() { + // Governance transition. + if current_state.amount() != next_state.amount() + || current_state.features().state_metadata() != next_state.features().state_metadata() + { + return Err(TransactionFailureReason::AnchorInvalidGovernanceTransition); + } + } else { + return Err(TransactionFailureReason::AnchorInvalidStateTransition); + } + + Ok(()) } fn destruction( @@ -442,13 +502,86 @@ impl StateTransitionVerifier for FoundryOutput { next_state: &Self, context: &SemanticValidationContext<'_>, ) -> Result<(), TransactionFailureReason> { - Self::transition_inner( - current_state, - next_state, - &context.input_native_tokens, - &context.output_native_tokens, - context.transaction.capabilities(), - ) + if current_state.account_address() != next_state.account_address() + || current_state.serial_number() != next_state.serial_number() + || current_state.immutable_features() != next_state.immutable_features() + { + return Err(TransactionFailureReason::ChainOutputImmutableFeaturesChanged); + } + + let token_id = next_state.token_id(); + let input_tokens = context.input_native_tokens.get(&token_id).copied().unwrap_or_default(); + let output_tokens = context.output_native_tokens.get(&token_id).copied().unwrap_or_default(); + let TokenScheme::Simple(ref current_token_scheme) = current_state.token_scheme(); + let TokenScheme::Simple(ref next_token_scheme) = next_state.token_scheme(); + + if current_token_scheme.maximum_supply() != next_token_scheme.maximum_supply() { + return Err(TransactionFailureReason::SimpleTokenSchemeMaximumSupplyChanged); + } + + if current_token_scheme.minted_tokens() > next_token_scheme.minted_tokens() + || current_token_scheme.melted_tokens() > next_token_scheme.melted_tokens() + { + return Err(TransactionFailureReason::SimpleTokenSchemeMintedMeltedTokenDecrease); + } + + match input_tokens.cmp(&output_tokens) { + Ordering::Less => { + // Mint + + // This can't underflow as it is known that current_minted_tokens <= next_minted_tokens. + let minted_diff = next_token_scheme.minted_tokens() - current_token_scheme.minted_tokens(); + // This can't underflow as it is known that input_tokens < output_tokens (Ordering::Less). + let token_diff = output_tokens - input_tokens; + + if minted_diff != token_diff { + return Err(TransactionFailureReason::NativeTokenSumUnbalanced); + } + + if current_token_scheme.melted_tokens() != next_token_scheme.melted_tokens() { + return Err(TransactionFailureReason::NativeTokenSumUnbalanced); + } + } + Ordering::Equal => { + // Transition + + if current_token_scheme.minted_tokens() != next_token_scheme.minted_tokens() + || current_token_scheme.melted_tokens() != next_token_scheme.melted_tokens() + { + return Err(TransactionFailureReason::NativeTokenSumUnbalanced); + } + } + Ordering::Greater => { + // Melt / Burn + + if current_token_scheme.melted_tokens() != next_token_scheme.melted_tokens() + && current_token_scheme.minted_tokens() != next_token_scheme.minted_tokens() + { + return Err(TransactionFailureReason::NativeTokenSumUnbalanced); + } + + // This can't underflow as it is known that current_melted_tokens <= next_melted_tokens. + let melted_diff = next_token_scheme.melted_tokens() - current_token_scheme.melted_tokens(); + // This can't underflow as it is known that input_tokens > output_tokens (Ordering::Greater). + let token_diff = input_tokens - output_tokens; + + if melted_diff > token_diff { + return Err(TransactionFailureReason::NativeTokenSumUnbalanced); + } + + let burned_diff = token_diff - melted_diff; + + if !burned_diff.is_zero() + && !context + .transaction + .has_capability(TransactionCapabilityFlag::BurnNativeTokens) + { + return Err(TransactionFailureReason::CapabilitiesNativeTokenBurningNotAllowed)?; + } + } + } + + Ok(()) } fn destruction( @@ -511,7 +644,11 @@ impl StateTransitionVerifier for NftOutput { next_state: &Self, _context: &SemanticValidationContext<'_>, ) -> Result<(), TransactionFailureReason> { - Self::transition_inner(current_state, next_state) + if current_state.immutable_features() != next_state.immutable_features() { + return Err(TransactionFailureReason::ChainOutputImmutableFeaturesChanged); + } + + Ok(()) } fn destruction( @@ -568,15 +705,22 @@ impl StateTransitionVerifier for DelegationOutput { next_state: &Self, context: &SemanticValidationContext<'_>, ) -> Result<(), TransactionFailureReason> { - Self::transition_inner(current_state, next_state)?; + if !current_state.delegation_id().is_null() || next_state.delegation_id().is_null() { + return Err(TransactionFailureReason::DelegationOutputTransitionedTwice); + } - let protocol_parameters = &context.protocol_parameters; + if current_state.delegated_amount() != next_state.delegated_amount() + || current_state.start_epoch() != next_state.start_epoch() + || current_state.validator_address() != next_state.validator_address() + { + return Err(TransactionFailureReason::DelegationModified); + } let slot_commitment_id = context .commitment_context_input .ok_or(TransactionFailureReason::DelegationCommitmentInputMissing)?; - if next_state.end_epoch() != protocol_parameters.delegation_end_epoch(slot_commitment_id) { + if next_state.end_epoch() != context.protocol_parameters.delegation_end_epoch(slot_commitment_id) { return Err(TransactionFailureReason::DelegationEndEpochInvalid); } From a3d8012e3d34346aa174b42344b18b8d7229b6bf Mon Sep 17 00:00:00 2001 From: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> Date: Wed, 6 Mar 2024 11:37:47 +0100 Subject: [PATCH 10/21] Temporarily use a specific commit as inx-plugins aren't updated yet (#2145) --- .github/actions/private-tangle/setup/action.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/actions/private-tangle/setup/action.yml b/.github/actions/private-tangle/setup/action.yml index 2504053375..6db6ab6e1d 100644 --- a/.github/actions/private-tangle/setup/action.yml +++ b/.github/actions/private-tangle/setup/action.yml @@ -7,6 +7,8 @@ runs: uses: actions/checkout@v3 with: repository: iotaledger/iota-core + # TODO: remove ref when inx plugins are updated to the latest commit + ref: 25995c84ccc4f4cd9041881035f338acca513b0a path: iota-core - name: Prepare files for start and stop From eb1873ee587db14d3b53486134604e96d47f382f Mon Sep 17 00:00:00 2001 From: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> Date: Wed, 6 Mar 2024 14:49:05 +0100 Subject: [PATCH 11/21] Add mana field to `Build` enum variants in transaction builder tests (#2141) * Add mana field to transaction builder tests * Use Build enum in more tests --------- Co-authored-by: Thibault Martinez --- sdk/tests/client/mod.rs | 9 + sdk/tests/client/signing/account.rs | 7 + sdk/tests/client/signing/basic.rs | 9 + sdk/tests/client/signing/delegation.rs | 8 + sdk/tests/client/signing/mod.rs | 26 ++ sdk/tests/client/signing/nft.rs | 5 + .../transaction_builder/account_outputs.rs | 302 +++++++++++------- .../transaction_builder/basic_outputs.rs | 144 +++++++-- sdk/tests/client/transaction_builder/burn.rs | 53 +++ .../client/transaction_builder/expiration.rs | 46 +++ .../transaction_builder/foundry_outputs.rs | 22 ++ .../transaction_builder/native_tokens.rs | 72 +++++ .../client/transaction_builder/nft_outputs.rs | 57 ++++ .../client/transaction_builder/outputs.rs | 20 ++ .../storage_deposit_return.rs | 31 ++ .../client/transaction_builder/timelock.rs | 12 + 16 files changed, 696 insertions(+), 127 deletions(-) diff --git a/sdk/tests/client/mod.rs b/sdk/tests/client/mod.rs index fc72b404f5..768fef6bbc 100644 --- a/sdk/tests/client/mod.rs +++ b/sdk/tests/client/mod.rs @@ -65,6 +65,7 @@ const SLOT_COMMITMENT_ID: SlotCommitmentId = SlotCommitmentHash::null().const_in enum Build<'a> { Basic { amount: u64, + mana: u64, address: Address, native_token: Option<(&'a str, u64)>, sender: Option

, @@ -74,6 +75,7 @@ enum Build<'a> { }, Nft { amount: u64, + mana: u64, nft_id: NftId, address: Address, sender: Option
, @@ -83,6 +85,7 @@ enum Build<'a> { }, Account { amount: u64, + mana: u64, account_id: AccountId, address: Address, sender: Option
, @@ -111,6 +114,7 @@ impl<'a> Build<'a> { match self { Build::Basic { amount, + mana, address, native_token, sender, @@ -119,6 +123,7 @@ impl<'a> Build<'a> { expiration, } => { let mut builder = BasicOutputBuilder::new_with_amount(amount) + .with_mana(mana) .add_unlock_condition(AddressUnlockCondition::new(address.clone())); if let Some((id, amount)) = native_token { @@ -149,6 +154,7 @@ impl<'a> Build<'a> { } Build::Nft { amount, + mana, nft_id, address, sender, @@ -157,6 +163,7 @@ impl<'a> Build<'a> { expiration, } => { let mut builder = NftOutputBuilder::new_with_amount(amount, nft_id) + .with_mana(mana) .add_unlock_condition(AddressUnlockCondition::new(address)); if let Some(sender) = sender { @@ -180,12 +187,14 @@ impl<'a> Build<'a> { } Build::Account { amount, + mana, account_id, address, sender, issuer, } => { let mut builder = AccountOutputBuilder::new_with_amount(amount, account_id) + .with_mana(mana) .add_unlock_condition(AddressUnlockCondition::new(address)); if let Some(sender) = sender { diff --git a/sdk/tests/client/signing/account.rs b/sdk/tests/client/signing/account.rs index 37c8757b27..dcf647039c 100644 --- a/sdk/tests/client/signing/account.rs +++ b/sdk/tests/client/signing/account.rs @@ -54,6 +54,7 @@ async fn sign_account_state_transition() -> Result<(), Box Result<(), Box Result<(), Box> { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id, address: address.clone(), sender: None, @@ -141,6 +144,7 @@ async fn account_reference_unlocks() -> Result<(), Box> { ( Basic { amount: 1_000_000, + mana: 0, address: account_address.clone(), native_token: None, sender: None, @@ -153,6 +157,7 @@ async fn account_reference_unlocks() -> Result<(), Box> { ( Basic { amount: 1_000_000, + mana: 0, address: account_address.clone(), native_token: None, sender: None, @@ -169,6 +174,7 @@ async fn account_reference_unlocks() -> Result<(), Box> { let outputs = build_outputs([ Account { amount: 1_000_000, + mana: 0, account_id: account_id, address: address, sender: None, @@ -176,6 +182,7 @@ async fn account_reference_unlocks() -> Result<(), Box> { }, Basic { amount: 2_000_000, + mana: 0, address: account_address, native_token: None, sender: None, diff --git a/sdk/tests/client/signing/basic.rs b/sdk/tests/client/signing/basic.rs index b66af9641f..39a60ea051 100644 --- a/sdk/tests/client/signing/basic.rs +++ b/sdk/tests/client/signing/basic.rs @@ -45,6 +45,7 @@ async fn single_ed25519_unlock() -> Result<(), Box> { [( Basic { amount: 1_000_000, + mana: 0, address: address_0.clone(), native_token: None, sender: None, @@ -59,6 +60,7 @@ async fn single_ed25519_unlock() -> Result<(), Box> { let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: address_0, native_token: None, sender: None, @@ -124,6 +126,7 @@ async fn ed25519_reference_unlocks() -> Result<(), Box> { ( Basic { amount: 1_000_000, + mana: 0, address: address_0.clone(), native_token: None, sender: None, @@ -136,6 +139,7 @@ async fn ed25519_reference_unlocks() -> Result<(), Box> { ( Basic { amount: 1_000_000, + mana: 0, address: address_0.clone(), native_token: None, sender: None, @@ -148,6 +152,7 @@ async fn ed25519_reference_unlocks() -> Result<(), Box> { ( Basic { amount: 1_000_000, + mana: 0, address: address_0.clone(), native_token: None, sender: None, @@ -163,6 +168,7 @@ async fn ed25519_reference_unlocks() -> Result<(), Box> { let outputs = build_outputs([Basic { amount: 3_000_000, + mana: 0, address: address_0, native_token: None, sender: None, @@ -249,6 +255,7 @@ async fn two_signature_unlocks() -> Result<(), Box> { ( Basic { amount: 1_000_000, + mana: 0, address: address_0.clone(), native_token: None, sender: None, @@ -261,6 +268,7 @@ async fn two_signature_unlocks() -> Result<(), Box> { ( Basic { amount: 1_000_000, + mana: 0, address: address_1, native_token: None, sender: None, @@ -276,6 +284,7 @@ async fn two_signature_unlocks() -> Result<(), Box> { let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: address_0, native_token: None, sender: None, diff --git a/sdk/tests/client/signing/delegation.rs b/sdk/tests/client/signing/delegation.rs index 6da6ac9dd9..7b7625b97c 100644 --- a/sdk/tests/client/signing/delegation.rs +++ b/sdk/tests/client/signing/delegation.rs @@ -55,6 +55,7 @@ async fn valid_creation() -> Result<(), Box> { [( Basic { amount: 1_000_000, + mana: 0, address: address.clone(), native_token: None, sender: None, @@ -135,6 +136,7 @@ async fn creation_missing_commitment_input() -> Result<(), Box Result<(), Box> { [( Basic { amount: 1_000_000, + mana: 0, address: address.clone(), native_token: None, sender: None, @@ -281,6 +284,7 @@ async fn mismatch_amount_creation() -> Result<(), Box> { [( Basic { amount: 1_000_000, + mana: 0, address: address.clone(), native_token: None, sender: None, @@ -365,6 +369,7 @@ async fn non_zero_end_epoch_creation() -> Result<(), Box> [( Basic { amount: 1_000_000, + mana: 0, address: address.clone(), native_token: None, sender: None, @@ -449,6 +454,7 @@ async fn invalid_start_epoch_creation() -> Result<(), Box [( Basic { amount: 1_000_000, + mana: 0, address: address.clone(), native_token: None, sender: None, @@ -1019,6 +1025,7 @@ async fn destroy_null_id() -> Result<(), Box> { let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address, native_token: None, sender: None, @@ -1111,6 +1118,7 @@ async fn destroy_reward_missing() -> Result<(), Box> { let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address, native_token: None, sender: None, diff --git a/sdk/tests/client/signing/mod.rs b/sdk/tests/client/signing/mod.rs index 39a93557ad..99562cd7a5 100644 --- a/sdk/tests/client/signing/mod.rs +++ b/sdk/tests/client/signing/mod.rs @@ -75,6 +75,7 @@ async fn all_combined() -> Result<(), Box> { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: nft_1.clone(), sender: None, @@ -85,6 +86,7 @@ async fn all_combined() -> Result<(), Box> { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_2, address: ed25519_0.clone(), sender: None, @@ -95,6 +97,7 @@ async fn all_combined() -> Result<(), Box> { ( Basic { amount: 1_000_000, + mana: 0, address: account_1.clone(), native_token: None, sender: None, @@ -107,6 +110,7 @@ async fn all_combined() -> Result<(), Box> { ( Basic { amount: 1_000_000, + mana: 0, address: account_2.clone(), native_token: None, sender: None, @@ -119,6 +123,7 @@ async fn all_combined() -> Result<(), Box> { ( Basic { amount: 1_000_000, + mana: 0, address: account_2, native_token: None, sender: None, @@ -131,6 +136,7 @@ async fn all_combined() -> Result<(), Box> { ( Basic { amount: 1_000_000, + mana: 0, address: nft_2.clone(), native_token: None, sender: None, @@ -143,6 +149,7 @@ async fn all_combined() -> Result<(), Box> { ( Basic { amount: 1_000_000, + mana: 0, address: nft_2, native_token: None, sender: None, @@ -155,6 +162,7 @@ async fn all_combined() -> Result<(), Box> { ( Basic { amount: 1_000_000, + mana: 0, address: nft_4.clone(), native_token: None, sender: None, @@ -167,6 +175,7 @@ async fn all_combined() -> Result<(), Box> { ( Basic { amount: 1_000_000, + mana: 0, address: ed25519_0.clone(), native_token: None, sender: None, @@ -179,6 +188,7 @@ async fn all_combined() -> Result<(), Box> { ( Basic { amount: 1_000_000, + mana: 0, address: ed25519_1.clone(), native_token: None, sender: None, @@ -191,6 +201,7 @@ async fn all_combined() -> Result<(), Box> { ( Basic { amount: 1_000_000, + mana: 0, address: ed25519_2.clone(), native_token: None, sender: None, @@ -203,6 +214,7 @@ async fn all_combined() -> Result<(), Box> { ( Basic { amount: 1_000_000, + mana: 0, address: ed25519_2.clone(), native_token: None, sender: None, @@ -215,6 +227,7 @@ async fn all_combined() -> Result<(), Box> { ( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_1, address: ed25519_0.clone(), sender: None, @@ -227,6 +240,7 @@ async fn all_combined() -> Result<(), Box> { ( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_2, address: account_1.clone(), sender: None, @@ -240,6 +254,7 @@ async fn all_combined() -> Result<(), Box> { ( Basic { amount: 2_000_000, + mana: 0, address: ed25519_0.clone(), native_token: None, sender: None, @@ -252,6 +267,7 @@ async fn all_combined() -> Result<(), Box> { ( Basic { amount: 2_000_000, + mana: 0, address: ed25519_0.clone(), native_token: None, sender: None, @@ -264,6 +280,7 @@ async fn all_combined() -> Result<(), Box> { ( Basic { amount: 2_000_000, + mana: 0, address: ed25519_0.clone(), native_token: None, sender: None, @@ -276,6 +293,7 @@ async fn all_combined() -> Result<(), Box> { ( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_3, address: account_1.clone(), sender: None, @@ -288,6 +306,7 @@ async fn all_combined() -> Result<(), Box> { ( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_4, address: account_1, sender: None, @@ -304,6 +323,7 @@ async fn all_combined() -> Result<(), Box> { let outputs = build_outputs([ Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: nft_1, sender: None, @@ -311,6 +331,7 @@ async fn all_combined() -> Result<(), Box> { }, Account { amount: 1_000_000, + mana: 0, account_id: account_id_2, address: ed25519_0.clone(), sender: None, @@ -318,6 +339,7 @@ async fn all_combined() -> Result<(), Box> { }, Basic { amount: 10_000_000, + mana: 0, address: ed25519_0.clone(), native_token: None, sender: None, @@ -327,6 +349,7 @@ async fn all_combined() -> Result<(), Box> { }, Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_1, address: ed25519_0.clone(), sender: None, @@ -336,6 +359,7 @@ async fn all_combined() -> Result<(), Box> { }, Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_2, address: ed25519_0.clone(), sender: None, @@ -345,6 +369,7 @@ async fn all_combined() -> Result<(), Box> { }, Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_3, address: ed25519_0.clone(), sender: None, @@ -354,6 +379,7 @@ async fn all_combined() -> Result<(), Box> { }, Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_4, address: ed25519_0.clone(), sender: None, diff --git a/sdk/tests/client/signing/nft.rs b/sdk/tests/client/signing/nft.rs index bb0f1318a0..b52efa6395 100644 --- a/sdk/tests/client/signing/nft.rs +++ b/sdk/tests/client/signing/nft.rs @@ -56,6 +56,7 @@ async fn nft_reference_unlocks() -> Result<(), Box> { ( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id, address: address_0.clone(), sender: None, @@ -68,6 +69,7 @@ async fn nft_reference_unlocks() -> Result<(), Box> { ( Basic { amount: 1_000_000, + mana: 0, address: nft_address.clone(), native_token: None, sender: None, @@ -80,6 +82,7 @@ async fn nft_reference_unlocks() -> Result<(), Box> { ( Basic { amount: 1_000_000, + mana: 0, address: nft_address.clone(), native_token: None, sender: None, @@ -96,6 +99,7 @@ async fn nft_reference_unlocks() -> Result<(), Box> { let outputs = build_outputs([ Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id, address: address_0, sender: None, @@ -105,6 +109,7 @@ async fn nft_reference_unlocks() -> Result<(), Box> { }, Basic { amount: 2_000_000, + mana: 0, address: nft_address, native_token: None, sender: None, diff --git a/sdk/tests/client/transaction_builder/account_outputs.rs b/sdk/tests/client/transaction_builder/account_outputs.rs index c77abfbc2b..6563a5ebad 100644 --- a/sdk/tests/client/transaction_builder/account_outputs.rs +++ b/sdk/tests/client/transaction_builder/account_outputs.rs @@ -39,6 +39,7 @@ fn input_account_eq_output_account() { [( Account { amount: 1_000_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -50,6 +51,7 @@ fn input_account_eq_output_account() { ); let outputs = build_outputs([Account { amount: 1_000_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -80,6 +82,7 @@ fn transition_account_id_zero() { [( Account { amount: 1_000_000, + mana: 0, account_id: account_id_0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -92,6 +95,7 @@ fn transition_account_id_zero() { let account_id = AccountId::from(inputs[0].output_id()); let outputs = build_outputs([Account { amount: 1_000_000, + mana: 0, account_id, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -256,6 +260,7 @@ fn create_account() { [( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -269,6 +274,7 @@ fn create_account() { ); let outputs = build_outputs([Account { amount: 1_000_000, + mana: 0, account_id: account_id_0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -308,6 +314,7 @@ fn burn_account() { [( Account { amount: 2_000_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -319,6 +326,7 @@ fn burn_account() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -401,6 +409,7 @@ fn missing_input_for_account_output() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -414,6 +423,7 @@ fn missing_input_for_account_output() { ); let outputs = build_outputs([Account { amount: 1_000_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -447,6 +457,7 @@ fn missing_input_for_account_output_2() { ( Account { amount: 2_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -457,6 +468,7 @@ fn missing_input_for_account_output_2() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -471,6 +483,7 @@ fn missing_input_for_account_output_2() { ); let outputs = build_outputs([Account { amount: 1_000_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -502,6 +515,7 @@ fn missing_input_for_account_output_but_created() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -515,6 +529,7 @@ fn missing_input_for_account_output_but_created() { ); let outputs = build_outputs([Account { amount: 1_000_000, + mana: 0, account_id: account_id_0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -544,6 +559,7 @@ fn account_in_output_and_sender() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -554,6 +570,7 @@ fn account_in_output_and_sender() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -571,6 +588,7 @@ fn account_in_output_and_sender() { .unwrap(); let mut outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap()), @@ -604,6 +622,7 @@ fn missing_ed25519_sender() { [( Account { amount: 1_000_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -615,6 +634,7 @@ fn missing_ed25519_sender() { ); let outputs = build_outputs([Account { amount: 1_000_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: Some(Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap()), @@ -646,6 +666,7 @@ fn missing_ed25519_issuer_created() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -659,6 +680,7 @@ fn missing_ed25519_issuer_created() { ); let outputs = build_outputs([Account { amount: 1_000_000, + mana: 0, account_id: account_id_0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -690,6 +712,7 @@ fn missing_ed25519_issuer_transition() { [( Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -701,6 +724,7 @@ fn missing_ed25519_issuer_transition() { ); let outputs = build_outputs([Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -729,6 +753,7 @@ fn missing_account_sender() { [( Account { amount: 1_000_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -740,6 +765,7 @@ fn missing_account_sender() { ); let outputs = build_outputs([Account { amount: 1_000_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: Some(Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap()), @@ -771,6 +797,7 @@ fn missing_account_issuer_created() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -784,6 +811,7 @@ fn missing_account_issuer_created() { ); let outputs = build_outputs([Account { amount: 1_000_000, + mana: 0, account_id: account_id_0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -815,6 +843,7 @@ fn missing_account_issuer_transition() { [( Account { amount: 1_000_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -826,6 +855,7 @@ fn missing_account_issuer_transition() { ); let outputs = build_outputs([Account { amount: 1_000_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -854,6 +884,7 @@ fn missing_nft_sender() { [( Account { amount: 1_000_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -865,6 +896,7 @@ fn missing_nft_sender() { ); let outputs = build_outputs([Account { amount: 1_000_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: Some(Address::try_from_bech32(BECH32_ADDRESS_NFT_1).unwrap()), @@ -896,6 +928,7 @@ fn missing_nft_issuer_created() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -909,6 +942,7 @@ fn missing_nft_issuer_created() { ); let outputs = build_outputs([Account { amount: 1_000_000, + mana: 0, account_id: account_id_0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -940,6 +974,7 @@ fn missing_nft_issuer_transition() { [( Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -951,6 +986,7 @@ fn missing_nft_issuer_transition() { ); let outputs = build_outputs([Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -980,6 +1016,7 @@ fn increase_account_amount() { ( Account { amount: 2_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -990,6 +1027,7 @@ fn increase_account_amount() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1004,6 +1042,7 @@ fn increase_account_amount() { ); let outputs = build_outputs([Account { amount: 3_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1035,6 +1074,7 @@ fn decrease_account_amount() { ( Account { amount: 2_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1045,6 +1085,7 @@ fn decrease_account_amount() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1059,6 +1100,7 @@ fn decrease_account_amount() { ); let outputs = build_outputs([Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1102,6 +1144,7 @@ fn prefer_basic_to_account() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1112,6 +1155,7 @@ fn prefer_basic_to_account() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1126,6 +1170,7 @@ fn prefer_basic_to_account() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1160,6 +1205,7 @@ fn take_amount_from_account_to_fund_basic() { ( Account { amount: 2_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1170,6 +1216,7 @@ fn take_amount_from_account_to_fund_basic() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1184,6 +1231,7 @@ fn take_amount_from_account_to_fund_basic() { ); let outputs = build_outputs([Basic { amount: 1_200_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1232,6 +1280,7 @@ fn account_burn_should_validate_account_sender() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1244,6 +1293,7 @@ fn account_burn_should_validate_account_sender() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1256,6 +1306,7 @@ fn account_burn_should_validate_account_sender() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap()), @@ -1306,6 +1357,7 @@ fn account_burn_should_validate_account_address() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap(), native_token: None, sender: None, @@ -1318,6 +1370,7 @@ fn account_burn_should_validate_account_address() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1330,6 +1383,7 @@ fn account_burn_should_validate_account_address() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1379,6 +1433,7 @@ fn transitioned_zero_account_id_no_longer_is_zero() { [( Account { amount: 2_000_000, + mana: 0, account_id: account_id_0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1390,6 +1445,7 @@ fn transitioned_zero_account_id_no_longer_is_zero() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1439,6 +1495,7 @@ fn two_accounts_required() { ( Account { amount: 2_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1449,6 +1506,7 @@ fn two_accounts_required() { ( Account { amount: 2_000_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1461,6 +1519,7 @@ fn two_accounts_required() { ); let outputs = build_outputs([Basic { amount: 3_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1516,6 +1575,7 @@ fn state_controller_sender_required() { [( Account { amount: 2_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1527,6 +1587,7 @@ fn state_controller_sender_required() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()), @@ -1560,6 +1621,7 @@ fn state_controller_sender_required_already_selected() { [( Account { amount: 2_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1572,6 +1634,7 @@ fn state_controller_sender_required_already_selected() { let outputs = build_outputs([ Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1579,6 +1642,7 @@ fn state_controller_sender_required_already_selected() { }, Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()), @@ -1613,6 +1677,7 @@ fn state_transition_and_required() { [( Account { amount: 2_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1624,6 +1689,7 @@ fn state_transition_and_required() { ); let outputs = build_outputs([Account { amount: 2_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1655,6 +1721,7 @@ fn remainder_address_in_state_controller() { [( Account { amount: 2_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1666,6 +1733,7 @@ fn remainder_address_in_state_controller() { ); let outputs = build_outputs([Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1703,25 +1771,27 @@ fn min_allot_account_mana() { let protocol_parameters = iota_mainnet_protocol_parameters().clone(); let account_id_1 = AccountId::from_str(ACCOUNT_ID_1).unwrap(); - let mut inputs = Vec::new(); let mana_input_amount = 1_000_000; let required_allotment = 7864; - let account_output = AccountOutputBuilder::new_with_amount(2_000_000, account_id_1) - .add_unlock_condition(AddressUnlockCondition::new( - Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), - )) - .with_mana(mana_input_amount) - .finish_output() - .unwrap(); - inputs.push(InputSigningData { - output: account_output, - output_metadata: rand_output_metadata_with_id(rand_output_id_with_slot_index(SLOT_INDEX)), - chain: None, - }); + let inputs = build_inputs( + [( + Account { + amount: 2_000_000, + mana: mana_input_amount, + account_id: account_id_1, + address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), + sender: None, + issuer: None, + }, + None, + )], + Some(SLOT_INDEX), + ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()), @@ -1894,30 +1964,35 @@ fn min_allot_account_mana_requirement_twice() { let required_allotment = 7900; - let inputs = [ - AccountOutputBuilder::new_with_amount(2_000_000, account_id_1) - .add_unlock_condition(AddressUnlockCondition::new( - Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), - )) - .with_mana(required_allotment) - .finish_output() - .unwrap(), - BasicOutputBuilder::new_with_amount(1_000_000) - .add_unlock_condition(AddressUnlockCondition::new( - Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), - )) - .with_mana(100) - .finish_output() - .unwrap(), - ]; - let inputs = inputs - .into_iter() - .map(|input| InputSigningData { - output: input, - output_metadata: rand_output_metadata_with_id(rand_output_id_with_slot_index(SLOT_INDEX)), - chain: None, - }) - .collect::>(); + let inputs = build_inputs( + [ + ( + Account { + amount: 2_000_000, + mana: required_allotment, + account_id: account_id_1, + address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), + sender: None, + issuer: None, + }, + None, + ), + ( + Basic { + amount: 1_000_000, + mana: 100, + address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), + native_token: None, + sender: None, + sdruc: None, + timelock: None, + expiration: None, + }, + None, + ), + ], + Some(SLOT_INDEX), + ); let selected = TransactionBuilder::new( inputs.clone(), @@ -1956,41 +2031,45 @@ fn min_allot_account_mana_requirement_covered() { let provided_allotment = 7900; - let account_input = AccountOutputBuilder::new_with_amount(2_000_000, account_id_1) - .add_unlock_condition(AddressUnlockCondition::new( - Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), - )) - .with_mana(provided_allotment - 100) - .finish_output() - .unwrap(); + let inputs = build_inputs( + [ + ( + Account { + amount: 2_000_000, + mana: provided_allotment - 100, + account_id: account_id_1, + address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), + sender: None, + issuer: None, + }, + None, + ), + ( + Basic { + amount: 1_000_000, + mana: 100, + address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), + native_token: None, + sender: None, + sdruc: None, + timelock: None, + expiration: None, + }, + None, + ), + ], + Some(SLOT_INDEX), + ); // Must manually add account output with mana reduced for the manual allotment - let account_output = AccountOutputBuilder::from(account_input.as_account()) + let account_output = AccountOutputBuilder::from(inputs[0].output.as_account()) .with_mana(0) .finish_output() .unwrap(); - let inputs = [ - account_input, - BasicOutputBuilder::new_with_amount(1_000_000) - .add_unlock_condition(AddressUnlockCondition::new( - Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), - )) - .with_mana(100) - .finish_output() - .unwrap(), - ]; - let inputs = inputs - .into_iter() - .map(|input| InputSigningData { - output: input, - output_metadata: rand_output_metadata_with_id(rand_output_id_with_slot_index(SLOT_INDEX)), - chain: None, - }) - .collect::>(); - let mut outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -2031,41 +2110,45 @@ fn min_allot_account_mana_requirement_covered_2() { let provided_allotment = 7900; - let account_input = AccountOutputBuilder::new_with_amount(2_000_000, account_id_1) - .add_unlock_condition(AddressUnlockCondition::new( - Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), - )) - .with_mana(100) - .finish_output() - .unwrap(); + let inputs = build_inputs( + [ + ( + Account { + amount: 2_000_000, + mana: 100, + account_id: account_id_1, + address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), + sender: None, + issuer: None, + }, + None, + ), + ( + Basic { + amount: 1_000_000, + mana: provided_allotment - 100, + address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), + native_token: None, + sender: None, + sdruc: None, + timelock: None, + expiration: None, + }, + None, + ), + ], + Some(SLOT_INDEX), + ); // Must manually add account output with mana reduced for the manual allotment - let account_output = AccountOutputBuilder::from(account_input.as_account()) + let account_output = AccountOutputBuilder::from(inputs[0].output.as_account()) .with_mana(0) .finish_output() .unwrap(); - let inputs = [ - account_input, - BasicOutputBuilder::new_with_amount(1_000_000) - .add_unlock_condition(AddressUnlockCondition::new( - Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), - )) - .with_mana(provided_allotment - 100) - .finish_output() - .unwrap(), - ]; - let inputs = inputs - .into_iter() - .map(|input| InputSigningData { - output: input, - output_metadata: rand_output_metadata_with_id(rand_output_id_with_slot_index(SLOT_INDEX)), - chain: None, - }) - .collect::>(); - let mut outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -2105,22 +2188,24 @@ fn implicit_account_transition() { 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(**ed25519_address.as_ed25519()), - ))) - .with_mana(10000) - .finish_output() - .unwrap()]; - let inputs = inputs - .into_iter() - .map(|input| InputSigningData { - output: input, - output_metadata: rand_output_metadata_with_id(rand_output_id_with_slot_index(SLOT_INDEX)), - chain: None, - }) - .collect::>(); - + let inputs = build_inputs( + [( + Basic { + amount: 1_000_000, + mana: 10000, + address: Address::ImplicitAccountCreation(ImplicitAccountCreationAddress::new( + **ed25519_address.as_ed25519(), + )), + native_token: None, + sender: None, + sdruc: None, + timelock: None, + expiration: None, + }, + None, + )], + Some(SLOT_INDEX), + ); let input_output_id = *inputs[0].output_id(); let account_id = AccountId::from(&input_output_id); let outputs = vec![ @@ -2176,6 +2261,7 @@ fn auto_transition_account_less_than_min() { [( Account { amount: small_amount, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -2225,6 +2311,7 @@ fn auto_transition_account_less_than_min_additional() { ( Account { amount: small_amount, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -2235,6 +2322,7 @@ fn auto_transition_account_less_than_min_additional() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, native_token: None, diff --git a/sdk/tests/client/transaction_builder/basic_outputs.rs b/sdk/tests/client/transaction_builder/basic_outputs.rs index 49427af41f..6b49e167f0 100644 --- a/sdk/tests/client/transaction_builder/basic_outputs.rs +++ b/sdk/tests/client/transaction_builder/basic_outputs.rs @@ -4,16 +4,12 @@ use std::str::FromStr; use iota_sdk::{ - client::{ - api::transaction_builder::{Requirement, TransactionBuilder, TransactionBuilderError}, - secret::types::InputSigningData, - }, + client::api::transaction_builder::{Requirement, TransactionBuilder, TransactionBuilderError}, types::block::{ address::{Address, AddressCapabilities, MultiAddress, RestrictedAddress, WeightedAddress}, mana::ManaAllotment, output::{unlock_condition::AddressUnlockCondition, AccountId, BasicOutputBuilder, NftId}, protocol::iota_mainnet_protocol_parameters, - rand::output::{rand_output_id_with_slot_index, rand_output_metadata_with_id}, }, }; use pretty_assertions::assert_eq; @@ -34,6 +30,7 @@ fn input_amount_equal_output_amount() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -47,6 +44,7 @@ fn input_amount_equal_output_amount() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -78,6 +76,7 @@ fn input_amount_lower_than_output_amount() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -91,6 +90,7 @@ fn input_amount_lower_than_output_amount() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -127,6 +127,7 @@ fn input_amount_lower_than_output_amount_2() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -139,6 +140,7 @@ fn input_amount_lower_than_output_amount_2() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -153,6 +155,7 @@ fn input_amount_lower_than_output_amount_2() { ); let outputs = build_outputs([Basic { amount: 3_500_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -188,6 +191,7 @@ fn input_amount_greater_than_output_amount() { [( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -201,6 +205,7 @@ fn input_amount_greater_than_output_amount() { ); let outputs = build_outputs([Basic { amount: 500_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -245,6 +250,7 @@ fn input_amount_greater_than_output_amount_with_remainder_address() { [( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -258,6 +264,7 @@ fn input_amount_greater_than_output_amount_with_remainder_address() { ); let outputs = build_outputs([Basic { amount: 500_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -303,6 +310,7 @@ fn two_same_inputs_one_needed() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -315,6 +323,7 @@ fn two_same_inputs_one_needed() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -329,6 +338,7 @@ fn two_same_inputs_one_needed() { ); let outputs = build_outputs([Basic { amount: 500_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -374,6 +384,7 @@ fn two_inputs_one_needed() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -386,6 +397,7 @@ fn two_inputs_one_needed() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -400,6 +412,7 @@ fn two_inputs_one_needed() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -432,6 +445,7 @@ fn two_inputs_one_needed_reversed() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -444,6 +458,7 @@ fn two_inputs_one_needed_reversed() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -458,6 +473,7 @@ fn two_inputs_one_needed_reversed() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -490,6 +506,7 @@ fn two_inputs_both_needed() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -502,6 +519,7 @@ fn two_inputs_both_needed() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -516,6 +534,7 @@ fn two_inputs_both_needed() { ); let outputs = build_outputs([Basic { amount: 3_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -548,6 +567,7 @@ fn two_inputs_remainder() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -560,6 +580,7 @@ fn two_inputs_remainder() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -574,6 +595,7 @@ fn two_inputs_remainder() { ); let outputs = build_outputs([Basic { amount: 2_500_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -661,6 +683,7 @@ fn ed25519_sender() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -673,6 +696,7 @@ fn ed25519_sender() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -685,6 +709,7 @@ fn ed25519_sender() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -697,6 +722,7 @@ fn ed25519_sender() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -709,6 +735,7 @@ fn ed25519_sender() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -723,6 +750,7 @@ fn ed25519_sender() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap()), @@ -765,6 +793,7 @@ fn missing_ed25519_sender() { [( Basic { amount: 5_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -778,6 +807,7 @@ fn missing_ed25519_sender() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap()), @@ -812,6 +842,7 @@ fn account_sender() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -824,6 +855,7 @@ fn account_sender() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -836,6 +868,7 @@ fn account_sender() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -846,6 +879,7 @@ fn account_sender() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -858,6 +892,7 @@ fn account_sender() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -872,6 +907,7 @@ fn account_sender() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap()), @@ -914,6 +950,7 @@ fn account_sender_zero_id() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -926,6 +963,7 @@ fn account_sender_zero_id() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -939,6 +977,7 @@ fn account_sender_zero_id() { let account_id = AccountId::from(inputs[1].output_id()); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(Address::from(account_id)), @@ -977,6 +1016,7 @@ fn missing_account_sender() { [( Basic { amount: 5_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -990,6 +1030,7 @@ fn missing_account_sender() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap()), @@ -1024,6 +1065,7 @@ fn nft_sender() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1036,6 +1078,7 @@ fn nft_sender() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1048,6 +1091,7 @@ fn nft_sender() { ( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1060,6 +1104,7 @@ fn nft_sender() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1072,6 +1117,7 @@ fn nft_sender() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1086,6 +1132,7 @@ fn nft_sender() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(Address::try_from_bech32(BECH32_ADDRESS_NFT_1).unwrap()), @@ -1129,6 +1176,7 @@ fn nft_sender_zero_id() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1141,6 +1189,7 @@ fn nft_sender_zero_id() { ( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1156,6 +1205,7 @@ fn nft_sender_zero_id() { let nft_id = NftId::from(inputs[1].output_id()); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(Address::from(nft_id)), @@ -1194,6 +1244,7 @@ fn missing_nft_sender() { [( Basic { amount: 5_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1207,6 +1258,7 @@ fn missing_nft_sender() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(Address::try_from_bech32(BECH32_ADDRESS_NFT_1).unwrap()), @@ -1239,6 +1291,7 @@ fn simple_remainder() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1252,6 +1305,7 @@ fn simple_remainder() { ); let outputs = build_outputs([Basic { amount: 500_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1374,6 +1428,7 @@ fn one_provided_one_needed() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1387,6 +1442,7 @@ fn one_provided_one_needed() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1418,6 +1474,7 @@ fn insufficient_amount() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1431,6 +1488,7 @@ fn insufficient_amount() { ); let outputs = build_outputs([Basic { amount: 1_250_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1467,6 +1525,7 @@ fn two_inputs_remainder_2() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1479,6 +1538,7 @@ fn two_inputs_remainder_2() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1493,6 +1553,7 @@ fn two_inputs_remainder_2() { ); let outputs = build_outputs([Basic { amount: 500_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1537,6 +1598,7 @@ fn two_inputs_remainder_3() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1549,6 +1611,7 @@ fn two_inputs_remainder_3() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1563,6 +1626,7 @@ fn two_inputs_remainder_3() { ); let outputs = build_outputs([Basic { amount: 2_750_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1645,6 +1709,7 @@ fn sender_already_selected() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -1658,6 +1723,7 @@ fn sender_already_selected() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap()), @@ -1693,6 +1759,7 @@ fn single_mandatory_input() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -1706,6 +1773,7 @@ fn single_mandatory_input() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1743,6 +1811,7 @@ fn too_many_inputs() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1758,6 +1827,7 @@ fn too_many_inputs() { ); let outputs = build_outputs([Basic { amount: 129_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1792,6 +1862,7 @@ fn more_than_max_inputs_only_one_needed() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1810,6 +1881,7 @@ fn more_than_max_inputs_only_one_needed() { [( Basic { amount: 129_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1825,6 +1897,7 @@ fn more_than_max_inputs_only_one_needed() { let outputs = build_outputs([Basic { amount: 129_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1855,6 +1928,7 @@ fn too_many_outputs() { let inputs = build_inputs( [( Basic { + mana: 0, amount: 2_000_000_000, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, @@ -1870,6 +1944,7 @@ fn too_many_outputs() { let outputs = build_outputs( std::iter::repeat_with(|| Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1904,6 +1979,7 @@ fn too_many_outputs_with_remainder() { [( Basic { amount: 2_000_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1919,6 +1995,7 @@ fn too_many_outputs_with_remainder() { let outputs = build_outputs( std::iter::repeat_with(|| Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1957,6 +2034,7 @@ fn restricted_ed25519() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1969,6 +2047,7 @@ fn restricted_ed25519() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1981,6 +2060,7 @@ fn restricted_ed25519() { ( Basic { amount: 1_000_000, + mana: 0, address: restricted, native_token: None, sender: None, @@ -1993,6 +2073,7 @@ fn restricted_ed25519() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -2005,6 +2086,7 @@ fn restricted_ed25519() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -2019,6 +2101,7 @@ fn restricted_ed25519() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -2055,6 +2138,7 @@ fn restricted_nft() { ( Basic { amount: 2_000_000, + mana: 0, address: restricted, native_token: None, sender: None, @@ -2067,6 +2151,7 @@ fn restricted_nft() { ( Nft { amount: 2_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -2081,6 +2166,7 @@ fn restricted_nft() { ); let outputs = build_outputs([Basic { amount: 3_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -2117,6 +2203,7 @@ fn restricted_account() { ( Basic { amount: 3_000_000, + mana: 0, address: restricted, native_token: None, sender: None, @@ -2129,6 +2216,7 @@ fn restricted_account() { ( Account { amount: 2_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -2141,6 +2229,7 @@ fn restricted_account() { ); let outputs = build_outputs([Basic { amount: 3_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -2176,6 +2265,7 @@ fn restricted_ed25519_sender() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -2188,6 +2278,7 @@ fn restricted_ed25519_sender() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -2200,6 +2291,7 @@ fn restricted_ed25519_sender() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -2212,6 +2304,7 @@ fn restricted_ed25519_sender() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -2224,6 +2317,7 @@ fn restricted_ed25519_sender() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -2238,6 +2332,7 @@ fn restricted_ed25519_sender() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(restricted_sender), @@ -2295,6 +2390,7 @@ fn multi_address_sender_already_fulfilled() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -2307,6 +2403,7 @@ fn multi_address_sender_already_fulfilled() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -2319,6 +2416,7 @@ fn multi_address_sender_already_fulfilled() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_2).unwrap(), native_token: None, sender: None, @@ -2333,6 +2431,7 @@ fn multi_address_sender_already_fulfilled() { ); let outputs = build_outputs([Basic { amount: 3_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(multi), @@ -2376,6 +2475,7 @@ fn ed25519_backed_available_address() { ( Basic { amount: 1_000_000, + mana: 0, address: restricted_address.clone(), native_token: None, sender: None, @@ -2388,6 +2488,7 @@ fn ed25519_backed_available_address() { ( Basic { amount: 1_000_000, + mana: 0, address: ed25519.clone(), native_token: None, sender: None, @@ -2403,6 +2504,7 @@ fn ed25519_backed_available_address() { let outputs = build_outputs([ Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -2412,6 +2514,7 @@ fn ed25519_backed_available_address() { }, Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(restricted_address.clone()), @@ -2443,21 +2546,22 @@ fn automatic_allotment_provided_in_and_output() { let protocol_parameters = iota_mainnet_protocol_parameters().clone(); let account_id_1 = AccountId::from_str(ACCOUNT_ID_1).unwrap(); - let inputs = [BasicOutputBuilder::new_with_amount(1_000_000) - .add_unlock_condition(AddressUnlockCondition::new( - Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), - )) - .with_mana(7577) - .finish_output() - .unwrap()]; - let inputs = inputs - .into_iter() - .map(|input| InputSigningData { - output: input, - output_metadata: rand_output_metadata_with_id(rand_output_id_with_slot_index(SLOT_INDEX)), - chain: None, - }) - .collect::>(); + let inputs = build_inputs( + [( + Basic { + amount: 1_000_000, + mana: 7577, + address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), + native_token: None, + sender: None, + sdruc: None, + timelock: None, + expiration: None, + }, + None, + )], + Some(SLOT_INDEX), + ); let outputs = vec![ BasicOutputBuilder::new_with_amount(1_000_000) diff --git a/sdk/tests/client/transaction_builder/burn.rs b/sdk/tests/client/transaction_builder/burn.rs index c170df6642..1b6a5fad5b 100644 --- a/sdk/tests/client/transaction_builder/burn.rs +++ b/sdk/tests/client/transaction_builder/burn.rs @@ -44,6 +44,7 @@ fn burn_account_present() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -54,6 +55,7 @@ fn burn_account_present() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -68,6 +70,7 @@ fn burn_account_present() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -107,6 +110,7 @@ fn burn_account_present_and_required() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -117,6 +121,7 @@ fn burn_account_present_and_required() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -131,6 +136,7 @@ fn burn_account_present_and_required() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -171,6 +177,7 @@ fn burn_account_id_zero() { ( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -183,6 +190,7 @@ fn burn_account_id_zero() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -197,6 +205,7 @@ fn burn_account_id_zero() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -236,6 +245,7 @@ fn burn_account_absent() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -249,6 +259,7 @@ fn burn_account_absent() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -286,6 +297,7 @@ fn burn_accounts_present() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -296,6 +308,7 @@ fn burn_accounts_present() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -306,6 +319,7 @@ fn burn_accounts_present() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -320,6 +334,7 @@ fn burn_accounts_present() { ); let outputs = build_outputs([Basic { amount: 3_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -358,6 +373,7 @@ fn burn_account_in_outputs() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -368,6 +384,7 @@ fn burn_account_in_outputs() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -383,6 +400,7 @@ fn burn_account_in_outputs() { let outputs = build_outputs([ Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -390,6 +408,7 @@ fn burn_account_in_outputs() { }, Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -427,6 +446,7 @@ fn burn_nft_present() { ( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -439,6 +459,7 @@ fn burn_nft_present() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -453,6 +474,7 @@ fn burn_nft_present() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -492,6 +514,7 @@ fn burn_nft_present_and_required() { ( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -504,6 +527,7 @@ fn burn_nft_present_and_required() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -518,6 +542,7 @@ fn burn_nft_present_and_required() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -558,6 +583,7 @@ fn burn_nft_id_zero() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -568,6 +594,7 @@ fn burn_nft_id_zero() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -582,6 +609,7 @@ fn burn_nft_id_zero() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -621,6 +649,7 @@ fn burn_nft_absent() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -634,6 +663,7 @@ fn burn_nft_absent() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -671,6 +701,7 @@ fn burn_nfts_present() { ( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -683,6 +714,7 @@ fn burn_nfts_present() { ( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -695,6 +727,7 @@ fn burn_nfts_present() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -709,6 +742,7 @@ fn burn_nfts_present() { ); let outputs = build_outputs([Basic { amount: 3_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -747,6 +781,7 @@ fn burn_nft_in_outputs() { ( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -759,6 +794,7 @@ fn burn_nft_in_outputs() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -774,6 +810,7 @@ fn burn_nft_in_outputs() { let outputs = build_outputs([ Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -783,6 +820,7 @@ fn burn_nft_in_outputs() { }, Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -830,6 +868,7 @@ fn burn_foundry_present() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -840,6 +879,7 @@ fn burn_foundry_present() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -854,6 +894,7 @@ fn burn_foundry_present() { ); let outputs = build_outputs([Basic { amount: 500_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -935,6 +976,7 @@ fn burn_foundry_absent() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -945,6 +987,7 @@ fn burn_foundry_absent() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -959,6 +1002,7 @@ fn burn_foundry_absent() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1015,6 +1059,7 @@ fn burn_foundries_present() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1027,6 +1072,7 @@ fn burn_foundries_present() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1093,6 +1139,7 @@ fn burn_foundry_in_outputs() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1115,6 +1162,7 @@ fn burn_foundry_in_outputs() { }, Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1152,6 +1200,7 @@ fn burn_native_tokens() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -1164,6 +1213,7 @@ fn burn_native_tokens() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_2, 100)), sender: None, @@ -1235,6 +1285,7 @@ fn burn_foundry_and_its_account() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1245,6 +1296,7 @@ fn burn_foundry_and_its_account() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1259,6 +1311,7 @@ fn burn_foundry_and_its_account() { ); let outputs = build_outputs([Basic { amount: 500_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, diff --git a/sdk/tests/client/transaction_builder/expiration.rs b/sdk/tests/client/transaction_builder/expiration.rs index 45d94865b8..3cd519324b 100644 --- a/sdk/tests/client/transaction_builder/expiration.rs +++ b/sdk/tests/client/transaction_builder/expiration.rs @@ -29,6 +29,7 @@ fn one_output_expiration_not_expired() { [( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -42,6 +43,7 @@ fn one_output_expiration_not_expired() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -74,6 +76,7 @@ fn expiration_equal_timestamp() { [( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -87,6 +90,7 @@ fn expiration_equal_timestamp() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -118,6 +122,7 @@ fn one_output_expiration_expired() { [( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -131,6 +136,7 @@ fn one_output_expiration_expired() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -163,6 +169,7 @@ fn two_outputs_one_expiration_expired() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -175,6 +182,7 @@ fn two_outputs_one_expiration_expired() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -189,6 +197,7 @@ fn two_outputs_one_expiration_expired() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -222,6 +231,7 @@ fn two_outputs_one_unexpired_one_missing() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -234,6 +244,7 @@ fn two_outputs_one_unexpired_one_missing() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -248,6 +259,7 @@ fn two_outputs_one_unexpired_one_missing() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -281,6 +293,7 @@ fn two_outputs_two_expired() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -293,6 +306,7 @@ fn two_outputs_two_expired() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -305,6 +319,7 @@ fn two_outputs_two_expired() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -319,6 +334,7 @@ fn two_outputs_two_expired() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -352,6 +368,7 @@ fn two_outputs_two_expired_2() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -364,6 +381,7 @@ fn two_outputs_two_expired_2() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -378,6 +396,7 @@ fn two_outputs_two_expired_2() { ); let outputs = build_outputs([Basic { amount: 4_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -412,6 +431,7 @@ fn expiration_expired_with_sdr() { [( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -425,6 +445,7 @@ fn expiration_expired_with_sdr() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -456,6 +477,7 @@ fn expiration_expired_with_sdr_2() { [( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -469,6 +491,7 @@ fn expiration_expired_with_sdr_2() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -500,6 +523,7 @@ fn expiration_expired_with_sdr_and_timelock() { [( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -513,6 +537,7 @@ fn expiration_expired_with_sdr_and_timelock() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -544,6 +569,7 @@ fn expiration_expired_with_sdr_and_timelock_2() { [( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -557,6 +583,7 @@ fn expiration_expired_with_sdr_and_timelock_2() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -589,6 +616,7 @@ fn sender_in_expiration() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -601,6 +629,7 @@ fn sender_in_expiration() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -613,6 +642,7 @@ fn sender_in_expiration() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -625,6 +655,7 @@ fn sender_in_expiration() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -637,6 +668,7 @@ fn sender_in_expiration() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -651,6 +683,7 @@ fn sender_in_expiration() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap()), @@ -686,6 +719,7 @@ fn sender_in_expiration_already_selected() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -699,6 +733,7 @@ fn sender_in_expiration_already_selected() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap()), @@ -734,6 +769,7 @@ fn remainder_in_expiration() { [( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap(), native_token: None, sender: None, @@ -747,6 +783,7 @@ fn remainder_in_expiration() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap()), @@ -792,6 +829,7 @@ fn expiration_expired_non_ed25519_in_address_unlock_condition() { [( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap(), native_token: None, sender: None, @@ -805,6 +843,7 @@ fn expiration_expired_non_ed25519_in_address_unlock_condition() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -838,6 +877,7 @@ fn expiration_expired_only_account_addresses() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap(), native_token: None, sender: None, @@ -850,6 +890,7 @@ fn expiration_expired_only_account_addresses() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -863,6 +904,7 @@ fn expiration_expired_only_account_addresses() { let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -895,6 +937,7 @@ fn one_nft_output_expiration_unexpired() { [( Nft { amount: 2_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), sender: None, @@ -908,6 +951,7 @@ fn one_nft_output_expiration_unexpired() { ); let outputs = build_outputs([Nft { amount: 2_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), sender: None, @@ -940,6 +984,7 @@ fn one_nft_output_expiration_expired() { [( Nft { amount: 2_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), sender: None, @@ -953,6 +998,7 @@ fn one_nft_output_expiration_expired() { ); let outputs = build_outputs([Nft { amount: 2_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), sender: None, diff --git a/sdk/tests/client/transaction_builder/foundry_outputs.rs b/sdk/tests/client/transaction_builder/foundry_outputs.rs index d363420fe2..daf19c3dd7 100644 --- a/sdk/tests/client/transaction_builder/foundry_outputs.rs +++ b/sdk/tests/client/transaction_builder/foundry_outputs.rs @@ -37,6 +37,7 @@ fn missing_input_account_for_foundry() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -120,6 +121,7 @@ fn minted_native_tokens_in_new_remainder() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -132,6 +134,7 @@ fn minted_native_tokens_in_new_remainder() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -185,6 +188,7 @@ fn minted_native_tokens_in_provided_output() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -197,6 +201,7 @@ fn minted_native_tokens_in_provided_output() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -217,6 +222,7 @@ fn minted_native_tokens_in_provided_output() { }, Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((&token_id.to_string(), 100)), sender: None, @@ -254,6 +260,7 @@ fn melt_native_tokens() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -332,6 +339,7 @@ fn destroy_foundry_with_account_state_transition() { ( Account { amount: 50_300, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -390,6 +398,7 @@ fn destroy_foundry_with_account_burn() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -412,6 +421,7 @@ fn destroy_foundry_with_account_burn() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -468,6 +478,7 @@ fn prefer_basic_to_foundry() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -488,6 +499,7 @@ fn prefer_basic_to_foundry() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -502,6 +514,7 @@ fn prefer_basic_to_foundry() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -536,6 +549,7 @@ fn simple_foundry_transition_basic_not_needed() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -621,6 +635,7 @@ fn simple_foundry_transition_basic_not_needed_with_remainder() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -843,6 +858,7 @@ fn take_amount_from_account_and_foundry_to_fund_basic() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -879,6 +895,7 @@ fn take_amount_from_account_and_foundry_to_fund_basic() { }); let outputs = build_outputs([Basic { amount: 3_200_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -926,6 +943,7 @@ fn create_native_token_but_burn_account() { ( Account { amount: 2_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -998,6 +1016,7 @@ fn melted_tokens_not_provided() { ( Account { amount: 2_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1057,6 +1076,7 @@ fn burned_tokens_not_provided() { ( Account { amount: 2_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1179,6 +1199,7 @@ fn melt_and_burn_native_tokens() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1343,6 +1364,7 @@ fn auto_transition_foundry_less_than_min_additional() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, native_token: None, diff --git a/sdk/tests/client/transaction_builder/native_tokens.rs b/sdk/tests/client/transaction_builder/native_tokens.rs index 9154120c13..cf8c2d8176 100644 --- a/sdk/tests/client/transaction_builder/native_tokens.rs +++ b/sdk/tests/client/transaction_builder/native_tokens.rs @@ -29,6 +29,7 @@ fn two_native_tokens_one_needed() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 150)), sender: None, @@ -41,6 +42,7 @@ fn two_native_tokens_one_needed() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_2, 100)), sender: None, @@ -55,6 +57,7 @@ fn two_native_tokens_one_needed() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 150)), sender: None, @@ -88,6 +91,7 @@ fn two_native_tokens_both_needed_plus_remainder() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -100,6 +104,7 @@ fn two_native_tokens_both_needed_plus_remainder() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_2, 150)), sender: None, @@ -115,6 +120,7 @@ fn two_native_tokens_both_needed_plus_remainder() { let outputs = build_outputs([ Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -124,6 +130,7 @@ fn two_native_tokens_both_needed_plus_remainder() { }, Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_2, 100)), sender: None, @@ -168,6 +175,7 @@ fn three_inputs_two_needed_plus_remainder() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -180,6 +188,7 @@ fn three_inputs_two_needed_plus_remainder() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -192,6 +201,7 @@ fn three_inputs_two_needed_plus_remainder() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -206,6 +216,7 @@ fn three_inputs_two_needed_plus_remainder() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 120)), sender: None, @@ -249,6 +260,7 @@ fn three_inputs_two_needed_no_remainder() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -261,6 +273,7 @@ fn three_inputs_two_needed_no_remainder() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -273,6 +286,7 @@ fn three_inputs_two_needed_no_remainder() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -287,6 +301,7 @@ fn three_inputs_two_needed_no_remainder() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 200)), sender: None, @@ -318,6 +333,7 @@ fn insufficient_native_tokens_one_input() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -331,6 +347,7 @@ fn insufficient_native_tokens_one_input() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 150)), sender: None, @@ -367,6 +384,7 @@ fn insufficient_native_tokens_three_inputs() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -379,6 +397,7 @@ fn insufficient_native_tokens_three_inputs() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -391,6 +410,7 @@ fn insufficient_native_tokens_three_inputs() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -405,6 +425,7 @@ fn insufficient_native_tokens_three_inputs() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 301)), sender: None, @@ -441,6 +462,7 @@ fn burn_and_send_at_the_same_time() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -453,6 +475,7 @@ fn burn_and_send_at_the_same_time() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_2, 100)), sender: None, @@ -467,6 +490,7 @@ fn burn_and_send_at_the_same_time() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 50)), sender: None, @@ -518,6 +542,7 @@ fn burn_one_input_no_output() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -565,6 +590,7 @@ fn multiple_native_tokens() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -577,6 +603,7 @@ fn multiple_native_tokens() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_2, 100)), sender: None, @@ -591,6 +618,7 @@ fn multiple_native_tokens() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -623,6 +651,7 @@ fn insufficient_native_tokens() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -636,6 +665,7 @@ fn insufficient_native_tokens() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 150)), sender: None, @@ -671,6 +701,7 @@ fn insufficient_native_tokens_2() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -684,6 +715,7 @@ fn insufficient_native_tokens_2() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 150)), sender: None, @@ -719,6 +751,7 @@ fn insufficient_amount_for_remainder() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -732,6 +765,7 @@ fn insufficient_amount_for_remainder() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 50)), sender: None, @@ -769,6 +803,7 @@ fn single_output_native_token_no_remainder() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -782,6 +817,7 @@ fn single_output_native_token_no_remainder() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -813,6 +849,7 @@ fn single_output_native_token_remainder_1() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -826,6 +863,7 @@ fn single_output_native_token_remainder_1() { ); let outputs = build_outputs([Basic { amount: 500_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 50)), sender: None, @@ -864,6 +902,7 @@ fn single_output_native_token_remainder_2() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -877,6 +916,7 @@ fn single_output_native_token_remainder_2() { ); let outputs = build_outputs([Basic { amount: 500_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -916,6 +956,7 @@ fn two_basic_outputs_1() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -928,6 +969,7 @@ fn two_basic_outputs_1() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 200)), sender: None, @@ -942,6 +984,7 @@ fn two_basic_outputs_1() { ); let outputs = build_outputs([Basic { amount: 500_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -991,6 +1034,7 @@ fn two_basic_outputs_2() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -1003,6 +1047,7 @@ fn two_basic_outputs_2() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 200)), sender: None, @@ -1017,6 +1062,7 @@ fn two_basic_outputs_2() { ); let outputs = build_outputs([Basic { amount: 500_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 50)), sender: None, @@ -1057,6 +1103,7 @@ fn two_basic_outputs_3() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -1069,6 +1116,7 @@ fn two_basic_outputs_3() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 200)), sender: None, @@ -1083,6 +1131,7 @@ fn two_basic_outputs_3() { ); let outputs = build_outputs([Basic { amount: 500_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 75)), sender: None, @@ -1123,6 +1172,7 @@ fn two_basic_outputs_4() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -1135,6 +1185,7 @@ fn two_basic_outputs_4() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 200)), sender: None, @@ -1149,6 +1200,7 @@ fn two_basic_outputs_4() { ); let outputs = build_outputs([Basic { amount: 500_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -1189,6 +1241,7 @@ fn two_basic_outputs_5() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -1201,6 +1254,7 @@ fn two_basic_outputs_5() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 200)), sender: None, @@ -1215,6 +1269,7 @@ fn two_basic_outputs_5() { ); let outputs = build_outputs([Basic { amount: 500_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -1255,6 +1310,7 @@ fn two_basic_outputs_6() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -1267,6 +1323,7 @@ fn two_basic_outputs_6() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 200)), sender: None, @@ -1281,6 +1338,7 @@ fn two_basic_outputs_6() { ); let outputs = build_outputs([Basic { amount: 500_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 250)), sender: None, @@ -1320,6 +1378,7 @@ fn two_basic_outputs_7() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -1332,6 +1391,7 @@ fn two_basic_outputs_7() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 200)), sender: None, @@ -1346,6 +1406,7 @@ fn two_basic_outputs_7() { ); let outputs = build_outputs([Basic { amount: 500_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 300)), sender: None, @@ -1385,6 +1446,7 @@ fn two_basic_outputs_8() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -1397,6 +1459,7 @@ fn two_basic_outputs_8() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 200)), sender: None, @@ -1411,6 +1474,7 @@ fn two_basic_outputs_8() { ); let outputs = build_outputs([Basic { amount: 500_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 350)), sender: None, @@ -1447,6 +1511,7 @@ fn two_basic_outputs_native_tokens_not_needed() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -1459,6 +1524,7 @@ fn two_basic_outputs_native_tokens_not_needed() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1473,6 +1539,7 @@ fn two_basic_outputs_native_tokens_not_needed() { ); let outputs = build_outputs([Basic { amount: 500_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1513,6 +1580,7 @@ fn multiple_remainders() { ( Basic { amount: 5_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -1525,6 +1593,7 @@ fn multiple_remainders() { ( Basic { amount: 5_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -1537,6 +1606,7 @@ fn multiple_remainders() { ( Basic { amount: 5_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_1, 100)), sender: None, @@ -1549,6 +1619,7 @@ fn multiple_remainders() { ( Basic { amount: 5_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: Some((TOKEN_ID_2, 100)), sender: None, @@ -1563,6 +1634,7 @@ fn multiple_remainders() { ); let outputs = build_outputs([Basic { amount: 15_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, diff --git a/sdk/tests/client/transaction_builder/nft_outputs.rs b/sdk/tests/client/transaction_builder/nft_outputs.rs index 8a574ef3e1..48ee64a36a 100644 --- a/sdk/tests/client/transaction_builder/nft_outputs.rs +++ b/sdk/tests/client/transaction_builder/nft_outputs.rs @@ -35,6 +35,7 @@ fn input_nft_eq_output_nft() { [( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -48,6 +49,7 @@ fn input_nft_eq_output_nft() { ); let outputs = build_outputs([Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -80,6 +82,7 @@ fn transition_nft_id_zero() { [( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -94,6 +97,7 @@ fn transition_nft_id_zero() { let nft_id = NftId::from(inputs[0].output_id()); let outputs = build_outputs([Nft { amount: 1_000_000, + mana: 0, nft_id, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -214,6 +218,7 @@ fn mint_nft() { [( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -227,6 +232,7 @@ fn mint_nft() { ); let outputs = build_outputs([Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -268,6 +274,7 @@ fn burn_nft() { [( Nft { amount: 2_000_000, + mana: 0, nft_id: nft_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -281,6 +288,7 @@ fn burn_nft() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -363,6 +371,7 @@ fn missing_input_for_nft_output() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -376,6 +385,7 @@ fn missing_input_for_nft_output() { ); let outputs = build_outputs([Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -409,6 +419,7 @@ fn missing_input_for_nft_output_but_created() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -422,6 +433,7 @@ fn missing_input_for_nft_output_but_created() { ); let outputs = build_outputs([Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -453,6 +465,7 @@ fn nft_in_output_and_sender() { ( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -465,6 +478,7 @@ fn nft_in_output_and_sender() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -480,6 +494,7 @@ fn nft_in_output_and_sender() { let outputs = build_outputs([ Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -489,6 +504,7 @@ fn nft_in_output_and_sender() { }, Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(Address::try_from_bech32(BECH32_ADDRESS_NFT_1).unwrap()), @@ -530,6 +546,7 @@ fn missing_ed25519_sender() { [( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -543,6 +560,7 @@ fn missing_ed25519_sender() { ); let outputs = build_outputs([Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: Some(Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap()), @@ -576,6 +594,7 @@ fn missing_ed25519_issuer_created() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -589,6 +608,7 @@ fn missing_ed25519_issuer_created() { ); let outputs = build_outputs([Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -622,6 +642,7 @@ fn missing_ed25519_issuer_transition() { [( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -635,6 +656,7 @@ fn missing_ed25519_issuer_transition() { ); let outputs = build_outputs([Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -665,6 +687,7 @@ fn missing_account_sender() { [( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -678,6 +701,7 @@ fn missing_account_sender() { ); let outputs = build_outputs([Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: Some(Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap()), @@ -711,6 +735,7 @@ fn missing_account_issuer_created() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -724,6 +749,7 @@ fn missing_account_issuer_created() { ); let outputs = build_outputs([Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -757,6 +783,7 @@ fn missing_account_issuer_transition() { [( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -770,6 +797,7 @@ fn missing_account_issuer_transition() { ); let outputs = build_outputs([Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -800,6 +828,7 @@ fn missing_nft_sender() { [( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -813,6 +842,7 @@ fn missing_nft_sender() { ); let outputs = build_outputs([Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: Some(Address::try_from_bech32(BECH32_ADDRESS_NFT_1).unwrap()), @@ -846,6 +876,7 @@ fn missing_nft_issuer_created() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -859,6 +890,7 @@ fn missing_nft_issuer_created() { ); let outputs = build_outputs([Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -892,6 +924,7 @@ fn missing_nft_issuer_transition() { [( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -905,6 +938,7 @@ fn missing_nft_issuer_transition() { ); let outputs = build_outputs([Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -936,6 +970,7 @@ fn increase_nft_amount() { ( Nft { amount: 2_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -948,6 +983,7 @@ fn increase_nft_amount() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -962,6 +998,7 @@ fn increase_nft_amount() { ); let outputs = build_outputs([Nft { amount: 3_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -995,6 +1032,7 @@ fn decrease_nft_amount() { ( Nft { amount: 2_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1007,6 +1045,7 @@ fn decrease_nft_amount() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1021,6 +1060,7 @@ fn decrease_nft_amount() { ); let outputs = build_outputs([Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1066,6 +1106,7 @@ fn prefer_basic_to_nft() { ( Nft { amount: 2_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1078,6 +1119,7 @@ fn prefer_basic_to_nft() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1092,6 +1134,7 @@ fn prefer_basic_to_nft() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1126,6 +1169,7 @@ fn take_amount_from_nft_to_fund_basic() { ( Nft { amount: 2_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1138,6 +1182,7 @@ fn take_amount_from_nft_to_fund_basic() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1152,6 +1197,7 @@ fn take_amount_from_nft_to_fund_basic() { ); let outputs = build_outputs([Basic { amount: 1_200_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1198,6 +1244,7 @@ fn nft_burn_should_validate_nft_sender() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1210,6 +1257,7 @@ fn nft_burn_should_validate_nft_sender() { ( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1224,6 +1272,7 @@ fn nft_burn_should_validate_nft_sender() { ); let outputs = build_outputs([Basic { amount: 3_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(Address::try_from_bech32(BECH32_ADDRESS_NFT_1).unwrap()), @@ -1262,6 +1311,7 @@ fn nft_burn_should_validate_nft_address() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_NFT_1).unwrap(), native_token: None, sender: None, @@ -1274,6 +1324,7 @@ fn nft_burn_should_validate_nft_address() { ( Nft { amount: 1_000_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1288,6 +1339,7 @@ fn nft_burn_should_validate_nft_address() { ); let outputs = build_outputs([Basic { amount: 3_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1325,6 +1377,7 @@ fn transitioned_zero_nft_id_no_longer_is_zero() { [( Nft { amount: 2_000_000, + mana: 0, nft_id: nft_id_0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1338,6 +1391,7 @@ fn transitioned_zero_nft_id_no_longer_is_zero() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -1452,6 +1506,7 @@ fn auto_transition_nft_less_than_min() { [( Nft { amount: small_amount, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1503,6 +1558,7 @@ fn auto_transition_nft_less_than_min_additional() { ( Nft { amount: small_amount, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -1515,6 +1571,7 @@ fn auto_transition_nft_less_than_min_additional() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, native_token: None, diff --git a/sdk/tests/client/transaction_builder/outputs.rs b/sdk/tests/client/transaction_builder/outputs.rs index 6335a3df09..df2ff75769 100644 --- a/sdk/tests/client/transaction_builder/outputs.rs +++ b/sdk/tests/client/transaction_builder/outputs.rs @@ -32,6 +32,7 @@ fn no_inputs() { let inputs = Vec::new(); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -64,6 +65,7 @@ fn no_outputs() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -98,6 +100,7 @@ fn no_outputs_but_required_input() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -143,6 +146,7 @@ fn no_outputs_but_burn() { [( Account { amount: 2_000_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -188,6 +192,7 @@ fn no_address_provided() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -201,6 +206,7 @@ fn no_address_provided() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -233,6 +239,7 @@ fn no_matching_address_provided() { [( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -246,6 +253,7 @@ fn no_matching_address_provided() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -279,6 +287,7 @@ fn two_addresses_one_missing() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -291,6 +300,7 @@ fn two_addresses_one_missing() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -305,6 +315,7 @@ fn two_addresses_one_missing() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -342,6 +353,7 @@ fn two_addresses() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -354,6 +366,7 @@ fn two_addresses() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -368,6 +381,7 @@ fn two_addresses() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -469,6 +483,7 @@ fn transition_no_more_than_needed_for_account_amount() { ( Account { amount: 500_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -479,6 +494,7 @@ fn transition_no_more_than_needed_for_account_amount() { ( Nft { amount: 500_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -493,6 +509,7 @@ fn transition_no_more_than_needed_for_account_amount() { ); let outputs = build_outputs([Account { amount: 500_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -525,6 +542,7 @@ fn transition_no_more_than_needed_for_nft_amount() { ( Account { amount: 500_000, + mana: 0, account_id: account_id_2, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -535,6 +553,7 @@ fn transition_no_more_than_needed_for_nft_amount() { ( Nft { amount: 500_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -549,6 +568,7 @@ fn transition_no_more_than_needed_for_nft_amount() { ); let outputs = build_outputs([Nft { amount: 500_000, + mana: 0, nft_id: nft_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, diff --git a/sdk/tests/client/transaction_builder/storage_deposit_return.rs b/sdk/tests/client/transaction_builder/storage_deposit_return.rs index 217b2bab1c..9cf6bdd855 100644 --- a/sdk/tests/client/transaction_builder/storage_deposit_return.rs +++ b/sdk/tests/client/transaction_builder/storage_deposit_return.rs @@ -24,6 +24,7 @@ fn sdruc_output_not_provided_no_remainder() { [( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -37,6 +38,7 @@ fn sdruc_output_not_provided_no_remainder() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -79,6 +81,7 @@ fn sdruc_output_provided_no_remainder() { [( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -93,6 +96,7 @@ fn sdruc_output_provided_no_remainder() { let outputs = build_outputs([ Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -102,6 +106,7 @@ fn sdruc_output_provided_no_remainder() { }, Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -134,6 +139,7 @@ fn sdruc_output_provided_remainder() { [( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -147,6 +153,7 @@ fn sdruc_output_provided_remainder() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -190,6 +197,7 @@ fn two_sdrucs_to_the_same_address_both_needed() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -202,6 +210,7 @@ fn two_sdrucs_to_the_same_address_both_needed() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -216,6 +225,7 @@ fn two_sdrucs_to_the_same_address_both_needed() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -259,6 +269,7 @@ fn two_sdrucs_to_the_same_address_one_needed() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -271,6 +282,7 @@ fn two_sdrucs_to_the_same_address_one_needed() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -285,6 +297,7 @@ fn two_sdrucs_to_the_same_address_one_needed() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -329,6 +342,7 @@ fn two_sdrucs_to_different_addresses_both_needed() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -341,6 +355,7 @@ fn two_sdrucs_to_different_addresses_both_needed() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -355,6 +370,7 @@ fn two_sdrucs_to_different_addresses_both_needed() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -410,6 +426,7 @@ fn two_sdrucs_to_different_addresses_one_needed() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -422,6 +439,7 @@ fn two_sdrucs_to_different_addresses_one_needed() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -436,6 +454,7 @@ fn two_sdrucs_to_different_addresses_one_needed() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -479,6 +498,7 @@ fn insufficient_amount_because_of_sdruc() { [( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -492,6 +512,7 @@ fn insufficient_amount_because_of_sdruc() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -528,6 +549,7 @@ fn useless_sdruc_required_for_sender_feature() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -540,6 +562,7 @@ fn useless_sdruc_required_for_sender_feature() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -554,6 +577,7 @@ fn useless_sdruc_required_for_sender_feature() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: Some(Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap()), @@ -601,6 +625,7 @@ fn sdruc_required_non_ed25519_in_address_unlock() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap(), native_token: None, sender: None, @@ -613,6 +638,7 @@ fn sdruc_required_non_ed25519_in_address_unlock() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -625,6 +651,7 @@ fn sdruc_required_non_ed25519_in_address_unlock() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_2).unwrap(), native_token: None, sender: Some(Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap()), @@ -669,6 +696,7 @@ fn useless_sdruc_non_ed25519_in_address_unlock() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap(), native_token: None, sender: None, @@ -681,6 +709,7 @@ fn useless_sdruc_non_ed25519_in_address_unlock() { ( Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ACCOUNT_1).unwrap(), native_token: None, sender: None, @@ -693,6 +722,7 @@ fn useless_sdruc_non_ed25519_in_address_unlock() { ( Account { amount: 1_000_000, + mana: 0, account_id: account_id_1, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), sender: None, @@ -705,6 +735,7 @@ fn useless_sdruc_non_ed25519_in_address_unlock() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_2).unwrap(), native_token: None, sender: None, diff --git a/sdk/tests/client/transaction_builder/timelock.rs b/sdk/tests/client/transaction_builder/timelock.rs index 328c580ede..a357bfc4d0 100644 --- a/sdk/tests/client/transaction_builder/timelock.rs +++ b/sdk/tests/client/transaction_builder/timelock.rs @@ -23,6 +23,7 @@ fn one_output_timelock_not_expired() { [( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -36,6 +37,7 @@ fn one_output_timelock_not_expired() { ); let outputs = build_outputs([Basic { amount: 1_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -68,6 +70,7 @@ fn timelock_equal_timestamp() { [( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -81,6 +84,7 @@ fn timelock_equal_timestamp() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -113,6 +117,7 @@ fn two_outputs_one_timelock_expired() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -125,6 +130,7 @@ fn two_outputs_one_timelock_expired() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -139,6 +145,7 @@ fn two_outputs_one_timelock_expired() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -172,6 +179,7 @@ fn two_outputs_one_timelocked_one_missing() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -184,6 +192,7 @@ fn two_outputs_one_timelocked_one_missing() { ( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -198,6 +207,7 @@ fn two_outputs_one_timelocked_one_missing() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, @@ -230,6 +240,7 @@ fn one_output_timelock_expired() { [( Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), native_token: None, sender: None, @@ -243,6 +254,7 @@ fn one_output_timelock_expired() { ); let outputs = build_outputs([Basic { amount: 2_000_000, + mana: 0, address: Address::try_from_bech32(BECH32_ADDRESS_ED25519_1).unwrap(), native_token: None, sender: None, From 182692ef3e393d8c73dbe5cc0c46e287c35252b7 Mon Sep 17 00:00:00 2001 From: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> Date: Wed, 6 Mar 2024 17:11:45 +0100 Subject: [PATCH 12/21] Fix implicit account creation address syncing (#2130) Co-authored-by: Thibault Martinez --- sdk/src/wallet/operations/syncing/addresses/output_ids/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/src/wallet/operations/syncing/addresses/output_ids/mod.rs b/sdk/src/wallet/operations/syncing/addresses/output_ids/mod.rs index cf1e1a9f64..16caf0613c 100644 --- a/sdk/src/wallet/operations/syncing/addresses/output_ids/mod.rs +++ b/sdk/src/wallet/operations/syncing/addresses/output_ids/mod.rs @@ -63,6 +63,7 @@ impl Wallet { if (address.is_ed25519() && sync_options.wallet.basic_outputs) || (address.is_nft() && sync_options.nft.basic_outputs) || (address.is_account() && sync_options.account.basic_outputs) + || (address.is_implicit_account_creation() && sync_options.sync_implicit_accounts) { // basic outputs #[cfg(target_family = "wasm")] From 5f2a80af09d5896a240e3e1ce126b30e415b7ae5 Mon Sep 17 00:00:00 2001 From: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> Date: Wed, 6 Mar 2024 17:37:56 +0100 Subject: [PATCH 13/21] Replace hexPublicKeyToBech32Address by publicKeyHash (#2135) * Replacey hexPublicKeyToBech32Address by publicKeyHash; Use Blake2b256Hash instead of ComputeAccountId/ComputeNftId/PublicKeyHash in bindings core * Fix typo, static instead of async * Lower case * Remove __PublicKeyHashMethod__ --------- Co-authored-by: Thibault Martinez --- bindings/core/src/method/client.rs | 8 ----- bindings/core/src/method/utils.rs | 20 +++-------- bindings/core/src/method_handler/client.rs | 3 -- bindings/core/src/method_handler/utils.rs | 16 ++++----- bindings/core/src/response.rs | 10 ++---- .../sign_and_verify_ed25519/sign-ed25519.ts | 4 +-- bindings/nodejs/lib/client/client.ts | 22 ------------ .../nodejs/lib/types/client/bridge/client.ts | 8 ----- .../nodejs/lib/types/client/bridge/index.ts | 2 -- .../nodejs/lib/types/utils/bridge/index.ts | 8 ++--- .../nodejs/lib/types/utils/bridge/utils.ts | 21 ++--------- bindings/nodejs/lib/utils/utils.ts | 36 +++++++++---------- bindings/nodejs/tests/utils/utils.spec.ts | 4 +-- .../sign_and_verify_ed25519/sign_ed25519.py | 5 +-- bindings/python/iota_sdk/client/_utils.py | 10 ------ bindings/python/iota_sdk/utils.py | 23 ++++++------ bindings/wasm/tests/utilityMethods.spec.ts | 2 +- 17 files changed, 56 insertions(+), 146 deletions(-) diff --git a/bindings/core/src/method/client.rs b/bindings/core/src/method/client.rs index 53354b813d..1959bb429c 100644 --- a/bindings/core/src/method/client.rs +++ b/bindings/core/src/method/client.rs @@ -467,14 +467,6 @@ pub enum ClientMethod { /// Human readable part bech32_hrp: Option, }, - /// Transforms a hex encoded public key to a bech32 encoded address - #[serde(rename_all = "camelCase")] - HexPublicKeyToBech32Address { - /// Hex encoded public key - hex: String, - /// Human readable part - bech32_hrp: Option, - }, /// Calculate the minimum required amount for an output. /// Expected response: /// [`Amount`](crate::Response::Amount) diff --git a/bindings/core/src/method/utils.rs b/bindings/core/src/method/utils.rs index eeae9f517a..a6fd6ef84b 100644 --- a/bindings/core/src/method/utils.rs +++ b/bindings/core/src/method/utils.rs @@ -19,7 +19,7 @@ use iota_sdk::{ unlock::Unlock, BlockDto, }, - utils::serde::{option_mana_rewards, string}, + utils::serde::{option_mana_rewards, prefix_hex_bytes, string}, }; use serde::{Deserialize, Serialize}; @@ -65,12 +65,6 @@ pub enum UtilsMethod { nft_id: NftId, bech32_hrp: Hrp, }, - /// Transforms a hex encoded public key to a bech32 encoded address - #[serde(rename_all = "camelCase")] - HexPublicKeyToBech32Address { - hex: String, - bech32_hrp: Hrp, - }, /// Returns a valid Address parsed from a String. ParseBech32Address { address: Bech32Address, @@ -96,10 +90,11 @@ pub enum UtilsMethod { TransactionId { payload: SignedTransactionPayloadDto, }, - /// Computes the account ID + /// Computes the Blake2b256 hash of the provided hex encoded bytes. #[serde(rename_all = "camelCase")] - ComputeAccountId { - output_id: OutputId, + Blake2b256Hash { + #[serde(with = "prefix_hex_bytes")] + bytes: Vec, }, /// Computes the Foundry ID #[serde(rename_all = "camelCase")] @@ -108,11 +103,6 @@ pub enum UtilsMethod { serial_number: u32, token_scheme_type: u8, }, - /// Computes the NFT ID - #[serde(rename_all = "camelCase")] - ComputeNftId { - output_id: OutputId, - }, /// Computes the output ID from transaction id and output index ComputeOutputId { id: TransactionId, diff --git a/bindings/core/src/method_handler/client.rs b/bindings/core/src/method_handler/client.rs index c9e44bbb62..1f220cba00 100644 --- a/bindings/core/src/method_handler/client.rs +++ b/bindings/core/src/method_handler/client.rs @@ -321,9 +321,6 @@ pub(crate) async fn call_client_method_internal( ClientMethod::NftIdToBech32 { nft_id, bech32_hrp } => { Response::Bech32Address(client.nft_id_to_bech32(nft_id, bech32_hrp).await?) } - ClientMethod::HexPublicKeyToBech32Address { hex, bech32_hrp } => { - Response::Bech32Address(client.hex_public_key_to_bech32_address(&hex, bech32_hrp).await?) - } ClientMethod::ComputeMinimumOutputAmount { output } => { let storage_score_params = client.get_storage_score_parameters().await?; diff --git a/bindings/core/src/method_handler/utils.rs b/bindings/core/src/method_handler/utils.rs index 23c8a9ed0e..c966f83cdd 100644 --- a/bindings/core/src/method_handler/utils.rs +++ b/bindings/core/src/method_handler/utils.rs @@ -1,14 +1,17 @@ // Copyright 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crypto::keys::bip39::Mnemonic; +use crypto::{ + hashes::{blake2b::Blake2b256, Digest}, + keys::bip39::Mnemonic, +}; use iota_sdk::{ - client::{hex_public_key_to_bech32_address, hex_to_bech32, verify_mnemonic, Client}, + client::{hex_to_bech32, verify_mnemonic, Client}, types::{ block::{ address::{AccountAddress, Address, ToBech32Ext}, input::UtxoInput, - output::{AccountId, FoundryId, MinimumOutputAmount, NftId, Output, OutputId, TokenId}, + output::{FoundryId, MinimumOutputAmount, Output, OutputId, TokenId}, payload::{signed_transaction::Transaction, SignedTransactionPayload}, semantic::SemanticValidationContext, signature::SignatureError, @@ -34,9 +37,7 @@ pub(crate) fn call_utils_method_internal(method: UtilsMethod) -> Result Response::Bech32Address(nft_id.to_bech32(bech32_hrp)), - UtilsMethod::HexPublicKeyToBech32Address { hex, bech32_hrp } => { - Response::Bech32Address(hex_public_key_to_bech32_address(&hex, bech32_hrp)?) - } + UtilsMethod::ParseBech32Address { address } => Response::ParsedBech32Address(address.into_inner()), UtilsMethod::IsAddressValid { address } => Response::Bool(Address::is_valid_bech32(&address)), UtilsMethod::GenerateMnemonic => Response::GeneratedMnemonic(Client::generate_mnemonic()?.to_string()), @@ -55,7 +56,7 @@ pub(crate) fn call_utils_method_internal(method: UtilsMethod) -> Result Response::AccountId(AccountId::from(&output_id)), + UtilsMethod::Blake2b256Hash { bytes } => Response::Hash(prefix_hex::encode(Blake2b256::digest(bytes).to_vec())), UtilsMethod::ComputeFoundryId { account_id, serial_number, @@ -65,7 +66,6 @@ pub(crate) fn call_utils_method_internal(method: UtilsMethod) -> Result Response::NftId(NftId::from(&output_id)), UtilsMethod::ComputeOutputId { id, index } => Response::OutputId(OutputId::new(id, index)), UtilsMethod::ComputeTokenId { account_id, diff --git a/bindings/core/src/response.rs b/bindings/core/src/response.rs index 372741e172..8a3f060e0e 100644 --- a/bindings/core/src/response.rs +++ b/bindings/core/src/response.rs @@ -26,7 +26,7 @@ use iota_sdk::{ block::{ address::{Address, Bech32Address, Hrp}, input::UtxoInput, - output::{AccountId, DecayedMana, FoundryId, NftId, Output, OutputId, OutputMetadata, TokenId}, + output::{DecayedMana, FoundryId, Output, OutputId, OutputMetadata, TokenId}, payload::{dto::SignedTransactionPayloadDto, signed_transaction::TransactionId}, protocol::ProtocolParameters, signature::Ed25519Signature, @@ -213,15 +213,10 @@ pub enum Response { /// - [`TransactionId`](crate::method::UtilsMethod::TransactionId) TransactionId(TransactionId), /// Response for: - /// - [`ComputeAccountId`](crate::method::UtilsMethod::ComputeAccountId) - AccountId(AccountId), - /// Response for: - /// - [`ComputeNftId`](crate::method::UtilsMethod::ComputeNftId) - NftId(NftId), - /// Response for: /// - [`ComputeFoundryId`](crate::method::UtilsMethod::ComputeFoundryId) FoundryId(FoundryId), /// Response for: + /// - [`Blake2b256Hash`](crate::method::UtilsMethod::Blake2b256Hash) /// - [`TransactionSigningHash`](crate::method::UtilsMethod::TransactionSigningHash) Hash(String), /// Response for [`Bech32ToHex`](crate::method::UtilsMethod::Bech32ToHex) @@ -251,7 +246,6 @@ pub enum Response { /// - [`AnchorIdToBech32`](crate::method::UtilsMethod::AnchorIdToBech32) /// - [`NftIdToBech32`](crate::method::ClientMethod::NftIdToBech32) /// - [`NftIdToBech32`](crate::method::UtilsMethod::NftIdToBech32) - /// - [`HexPublicKeyToBech32Address`](crate::method::ClientMethod::HexPublicKeyToBech32Address) /// - [`HexToBech32`](crate::method::ClientMethod::HexToBech32) /// - [`ImplicitAccountCreationAddress`](crate::method::WalletMethod::ImplicitAccountCreationAddress) Bech32Address(Bech32Address), diff --git a/bindings/nodejs/examples/how_tos/sign_and_verify_ed25519/sign-ed25519.ts b/bindings/nodejs/examples/how_tos/sign_and_verify_ed25519/sign-ed25519.ts index 5513d2d398..179d1d54c4 100644 --- a/bindings/nodejs/examples/how_tos/sign_and_verify_ed25519/sign-ed25519.ts +++ b/bindings/nodejs/examples/how_tos/sign_and_verify_ed25519/sign-ed25519.ts @@ -65,8 +65,8 @@ async function run() { `Public key: ${ed25519Signature.publicKey}\nSignature: ${ed25519Signature.signature}`, ); - const bech32Address = Utils.hexPublicKeyToBech32Address( - ed25519Signature.publicKey, + const bech32Address = Utils.addressToBech32( + Utils.publicKeyHash(ed25519Signature.publicKey), 'rms', ); console.log('Address: ' + bech32Address); diff --git a/bindings/nodejs/lib/client/client.ts b/bindings/nodejs/lib/client/client.ts index 5acd86b698..e93a7a1b92 100644 --- a/bindings/nodejs/lib/client/client.ts +++ b/bindings/nodejs/lib/client/client.ts @@ -946,28 +946,6 @@ export class Client { return JSON.parse(response).payload; } - /** - * Convert a hex encoded public key to a Bech32 encoded address. - * - * @param hex The hexadecimal string representation of a public key. - * @param bech32Hrp The Bech32 HRP (human readable part) to be used. - * @returns The corresponding Bech32 address. - */ - async hexPublicKeyToBech32Address( - hex: HexEncodedString, - bech32Hrp?: string, - ): Promise { - const response = await this.methodHandler.callMethod({ - name: 'hexPublicKeyToBech32Address', - data: { - hex, - bech32Hrp, - }, - }); - - return JSON.parse(response).payload; - } - /** * Return the unhealthy nodes. */ diff --git a/bindings/nodejs/lib/types/client/bridge/client.ts b/bindings/nodejs/lib/types/client/bridge/client.ts index 7e4451501e..4a01110729 100644 --- a/bindings/nodejs/lib/types/client/bridge/client.ts +++ b/bindings/nodejs/lib/types/client/bridge/client.ts @@ -357,14 +357,6 @@ export interface __NftIdToBech32Method__ { }; } -export interface __HexPublicKeyToBech32AddressMethod__ { - name: 'hexPublicKeyToBech32Address'; - data: { - hex: HexEncodedString; - bech32Hrp?: string; - }; -} - export interface __FindBlocksMethod__ { name: 'findBlocks'; data: { diff --git a/bindings/nodejs/lib/types/client/bridge/index.ts b/bindings/nodejs/lib/types/client/bridge/index.ts index d97de46ead..72d414c2d3 100644 --- a/bindings/nodejs/lib/types/client/bridge/index.ts +++ b/bindings/nodejs/lib/types/client/bridge/index.ts @@ -49,7 +49,6 @@ import type { __AccountIdToBech32Method__, __AnchorIdToBech32Method__, __NftIdToBech32Method__, - __HexPublicKeyToBech32AddressMethod__, __AccountOutputIdsMethod__, __AccountOutputIdMethod__, __AnchorOutputIdsMethod__, @@ -121,7 +120,6 @@ export type __ClientMethods__ = | __AccountIdToBech32Method__ | __AnchorIdToBech32Method__ | __NftIdToBech32Method__ - | __HexPublicKeyToBech32AddressMethod__ | __AccountOutputIdsMethod__ | __AccountOutputIdMethod__ | __AnchorOutputIdsMethod__ diff --git a/bindings/nodejs/lib/types/utils/bridge/index.ts b/bindings/nodejs/lib/types/utils/bridge/index.ts index ceb1078924..405f4bf2b9 100644 --- a/bindings/nodejs/lib/types/utils/bridge/index.ts +++ b/bindings/nodejs/lib/types/utils/bridge/index.ts @@ -1,13 +1,12 @@ import type { __GenerateMnemonicMethod__, __MnemonicToHexSeedMethod__, - __ComputeAccountIdMethod__, __ComputeOutputIdMethod__, __ComputeTokenIdMethod__, - __ComputeNftIdMethod__, __ComputeFoundryIdMethod__, __ComputeMinimumOutputAmountMethod__, __ParseBech32AddressMethod__, + __Blake2b256HashMethod__, __BlockIdMethod__, __TransactionIdMethod__, __Bech32ToHexMethod__, @@ -16,7 +15,6 @@ import type { __AccountIdToBech32Method__, __AnchorIdToBech32Method__, __NftIdToBech32Method__, - __HexPublicKeyToBech32AddressMethod__, __IsAddressValidMethod__, __ProtocolParametersHashMethod__, __TransactionSigningHashMethod__, @@ -40,13 +38,12 @@ import type { export type __UtilsMethods__ = | __GenerateMnemonicMethod__ | __MnemonicToHexSeedMethod__ - | __ComputeAccountIdMethod__ - | __ComputeNftIdMethod__ | __ComputeFoundryIdMethod__ | __ComputeOutputIdMethod__ | __ComputeTokenIdMethod__ | __ComputeMinimumOutputAmountMethod__ | __ParseBech32AddressMethod__ + | __Blake2b256HashMethod__ | __BlockIdMethod__ | __TransactionIdMethod__ | __Bech32ToHexMethod__ @@ -55,7 +52,6 @@ export type __UtilsMethods__ = | __AccountIdToBech32Method__ | __AnchorIdToBech32Method__ | __NftIdToBech32Method__ - | __HexPublicKeyToBech32AddressMethod__ | __IsAddressValidMethod__ | __ProtocolParametersHashMethod__ | __TransactionSigningHashMethod__ diff --git a/bindings/nodejs/lib/types/utils/bridge/utils.ts b/bindings/nodejs/lib/types/utils/bridge/utils.ts index 11382bbfe9..8b6952439c 100644 --- a/bindings/nodejs/lib/types/utils/bridge/utils.ts +++ b/bindings/nodejs/lib/types/utils/bridge/utils.ts @@ -31,13 +31,6 @@ export interface __MnemonicToHexSeedMethod__ { }; } -export interface __ComputeAccountIdMethod__ { - name: 'computeAccountId'; - data: { - outputId: OutputId; - }; -} - export interface __ComputeFoundryIdMethod__ { name: 'computeFoundryId'; data: { @@ -47,10 +40,10 @@ export interface __ComputeFoundryIdMethod__ { }; } -export interface __ComputeNftIdMethod__ { - name: 'computeNftId'; +export interface __Blake2b256HashMethod__ { + name: 'blake2b256Hash'; data: { - outputId: OutputId; + bytes: HexEncodedString; }; } @@ -148,14 +141,6 @@ export interface __NftIdToBech32Method__ { }; } -export interface __HexPublicKeyToBech32AddressMethod__ { - name: 'hexPublicKeyToBech32Address'; - data: { - hex: HexEncodedString; - bech32Hrp: string; - }; -} - export interface __IsAddressValidMethod__ { name: 'isAddressValid'; data: { diff --git a/bindings/nodejs/lib/utils/utils.ts b/bindings/nodejs/lib/utils/utils.ts index 25263e415f..d0f81b51c1 100644 --- a/bindings/nodejs/lib/utils/utils.ts +++ b/bindings/nodejs/lib/utils/utils.ts @@ -21,6 +21,7 @@ import { Unlock, DecayedMana, NumericString, + Ed25519Address, } from '../types'; import { AccountId, @@ -70,9 +71,9 @@ export class Utils { */ static computeAccountId(outputId: OutputId): AccountId { return callUtilsMethod({ - name: 'computeAccountId', + name: 'blake2b256Hash', data: { - outputId, + bytes: outputId, }, }); } @@ -108,9 +109,9 @@ export class Utils { */ static computeNftId(outputId: OutputId): NftId { return callUtilsMethod({ - name: 'computeNftId', + name: 'blake2b256Hash', data: { - outputId, + bytes: outputId, }, }); } @@ -369,23 +370,20 @@ export class Utils { } /** - * Convert a hex-encoded public key to a Bech32-encoded address string. + * Hashes a hex encoded public key with Blake2b256. * - * @param hex A hex-encoded public key. - * @param bech32Hrp The Bech32 HRP (human readable part) to use. - * @returns The Bech32-encoded address string. + * @param hex The hexadecimal string representation of a public key. + * @returns The Ed25519 address with the hashed public key. */ - static hexPublicKeyToBech32Address( - hex: HexEncodedString, - bech32Hrp: string, - ): Bech32Address { - return callUtilsMethod({ - name: 'hexPublicKeyToBech32Address', - data: { - hex, - bech32Hrp, - }, - }); + static publicKeyHash(hex: HexEncodedString): Ed25519Address { + return new Ed25519Address( + callUtilsMethod({ + name: 'blake2b256Hash', + data: { + bytes: hex, + }, + }), + ); } /** diff --git a/bindings/nodejs/tests/utils/utils.spec.ts b/bindings/nodejs/tests/utils/utils.spec.ts index 330b799fba..b7d89eef17 100644 --- a/bindings/nodejs/tests/utils/utils.spec.ts +++ b/bindings/nodejs/tests/utils/utils.spec.ts @@ -25,9 +25,9 @@ describe('Utils methods', () => { const hexPublicKey = '0x2baaf3bca8ace9f862e60184bd3e79df25ff230f7eaaa4c7f03daa9833ba854a'; - const address = Utils.hexPublicKeyToBech32Address(hexPublicKey, 'rms'); + const address = Utils.publicKeyHash(hexPublicKey); - expect(address).toBeValidAddress(); + expect(address.pubKeyHash).toBe('0x96f9de0989e77d0e150e850a5a600e83045fa57419eaf3b20225b763d4e23813'); }); it('validates address', () => { diff --git a/bindings/python/examples/how_tos/sign_and_verify_ed25519/sign_ed25519.py b/bindings/python/examples/how_tos/sign_and_verify_ed25519/sign_ed25519.py index 711e9755c4..a004efa417 100644 --- a/bindings/python/examples/how_tos/sign_and_verify_ed25519/sign_ed25519.py +++ b/bindings/python/examples/how_tos/sign_and_verify_ed25519/sign_ed25519.py @@ -38,6 +38,7 @@ print( f'Public key: {ed25519_signature.public_key}\nSignature: {ed25519_signature.signature}') -bech32_address = Utils.hex_public_key_to_bech32_address( - ed25519_signature.public_key, "rms") +bech32_address = Utils.address_to_bech32( + Utils.public_key_hash(ed25519_signature.public_key), + "rms") print(f'Address: {bech32_address}') diff --git a/bindings/python/iota_sdk/client/_utils.py b/bindings/python/iota_sdk/client/_utils.py index a2d9a46c95..f829cb74cc 100644 --- a/bindings/python/iota_sdk/client/_utils.py +++ b/bindings/python/iota_sdk/client/_utils.py @@ -81,16 +81,6 @@ def nft_id_to_bech32(self, nft_id: HexStr, 'bech32Hrp': bech32_hrp }) - # pylint: disable=redefined-builtin - def hex_public_key_to_bech32_address( - self, hex_str: HexStr, bech32_hrp: Optional[str] = None) -> str: - """Transforms a hex encoded public key to a bech32 encoded address. - """ - return self._call_method('hexPublicKeyToBech32Address', { - 'hex': hex_str, - 'bech32Hrp': bech32_hrp - }) - def computer_minimum_output_amount(self, output: Output) -> int: """Minimum required output amount. """ diff --git a/bindings/python/iota_sdk/utils.py b/bindings/python/iota_sdk/utils.py index 196697be74..7a50f7a0b5 100644 --- a/bindings/python/iota_sdk/utils.py +++ b/bindings/python/iota_sdk/utils.py @@ -7,7 +7,7 @@ from iota_sdk.common import custom_encoder from iota_sdk.types.block.id import BlockId from iota_sdk.types.signature import Ed25519Signature -from iota_sdk.types.address import Address, deserialize_address +from iota_sdk.types.address import Address, Ed25519Address, deserialize_address from iota_sdk.types.common import HexStr from iota_sdk.types.decayed_mana import DecayedMana from iota_sdk.types.payload import Transaction, SignedTransactionPayload @@ -86,14 +86,13 @@ def nft_id_to_bech32(nft_id: HexStr, bech32_hrp: str) -> str: # pylint: disable=redefined-builtin @staticmethod - def hex_public_key_to_bech32_address( - hex_str: HexStr, bech32_hrp: str) -> str: - """Convert a hex encoded public key to a Bech32 encoded address. + def public_key_hash( + hex_str: HexStr) -> Ed25519Address: + """Hashes a hex encoded public key with Blake2b256. """ - return _call_method('hexPublicKeyToBech32Address', { - 'hex': hex_str, - 'bech32Hrp': bech32_hrp - }) + return Ed25519Address(_call_method('blake2b256Hash', { + 'bytes': hex_str, + })) @staticmethod def parse_bech32_address(address: str) -> Address: @@ -131,8 +130,8 @@ def mnemonic_to_hex_seed(mnemonic: str) -> HexStr: def compute_account_id(output_id: OutputId) -> HexStr: """Compute the account id for the given account output id. """ - return _call_method('computeAccountId', { - 'outputId': repr(output_id) + return _call_method('blake2b256Hash', { + 'bytes': repr(output_id) }) @staticmethod @@ -159,8 +158,8 @@ def compute_minimum_output_amount(output, storage_score_parameters) -> int: def compute_nft_id(output_id: OutputId) -> HexStr: """Compute the NFT id for the given NFT output id. """ - return _call_method('computeNftId', { - 'outputId': repr(output_id) + return _call_method('blake2b256Hash', { + 'bytes': repr(output_id) }) @staticmethod diff --git a/bindings/wasm/tests/utilityMethods.spec.ts b/bindings/wasm/tests/utilityMethods.spec.ts index 5c56aa27ce..34f677b70d 100644 --- a/bindings/wasm/tests/utilityMethods.spec.ts +++ b/bindings/wasm/tests/utilityMethods.spec.ts @@ -32,7 +32,7 @@ describe('Utils methods', () => { const hexPublicKey = '0x2baaf3bca8ace9f862e60184bd3e79df25ff230f7eaaa4c7f03daa9833ba854a'; - const address = Utils.hexPublicKeyToBech32Address(hexPublicKey, 'rms'); + const address = Utils.addressToBech32(Utils.publicKeyHash(hexPublicKey), 'rms'); expect(address).toBe( 'rms1qzt0nhsf38nh6rs4p6zs5knqp6psgha9wsv74uajqgjmwc75ugupx4aaacx', From 4aac8377acd35ed018784546227c2001a497e7a9 Mon Sep 17 00:00:00 2001 From: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> Date: Wed, 6 Mar 2024 18:01:55 +0100 Subject: [PATCH 14/21] Cleanup bindings send methods (#2137) * Cleanup bindings send methods * format * Keep prepare_send * Revert comment change --------- Co-authored-by: DaughterOfMars --- bindings/core/src/method/wallet.rs | 29 +------------- bindings/core/src/method_handler/wallet.rs | 20 ---------- bindings/core/src/response.rs | 9 ++--- .../nodejs/lib/types/wallet/bridge/index.ts | 8 +--- .../nodejs/lib/types/wallet/bridge/wallet.ts | 21 +--------- bindings/nodejs/lib/wallet/wallet.ts | 40 ++++--------------- bindings/python/iota_sdk/wallet/wallet.py | 33 +++++---------- 7 files changed, 27 insertions(+), 133 deletions(-) diff --git a/bindings/core/src/method/wallet.rs b/bindings/core/src/method/wallet.rs index 2bf51e0d14..70e9997662 100644 --- a/bindings/core/src/method/wallet.rs +++ b/bindings/core/src/method/wallet.rs @@ -119,10 +119,6 @@ pub enum WalletMethod { /// Expected response: [`OutputIds`](crate::Response::OutputIds) #[serde(rename_all = "camelCase")] ClaimableOutputs { outputs_to_claim: OutputsToClaim }, - /// Claim outputs. - /// Expected response: [`SentTransaction`](crate::Response::SentTransaction) - #[serde(rename_all = "camelCase")] - ClaimOutputs { output_ids_to_claim: Vec }, // /// Removes a previously registered participation event from local storage. // /// Expected response: [`Ok`](crate::Response::Ok) // #[cfg(feature = "participation")] @@ -308,7 +304,7 @@ pub enum WalletMethod { #[serde(default)] transaction_options: Option, }, - /// Prepare to send base coins. + /// Prepare to send base coins to multiple addresses, or with additional parameters. /// Expected response: [`PreparedTransaction`](crate::Response::PreparedTransaction) PrepareSend { params: Vec, @@ -421,29 +417,6 @@ pub enum WalletMethod { /// Maximum attempts max_attempts: Option, }, - /// Send base coins. - /// Expected response: [`SentTransaction`](crate::Response::SentTransaction) - Send { - #[serde(with = "string")] - amount: u64, - address: Bech32Address, - #[serde(default)] - options: Option, - }, - /// Send base coins to multiple addresses, or with additional parameters. - /// Expected response: [`SentTransaction`](crate::Response::SentTransaction) - SendWithParams { - params: Vec, - #[serde(default)] - options: Option, - }, - /// Send outputs in a transaction. - /// Expected response: [`SentTransaction`](crate::Response::SentTransaction) - SendOutputs { - outputs: Vec, - #[serde(default)] - options: Option, - }, /// Set the alias of the wallet. /// Expected response: [`Ok`](crate::Response::Ok) SetAlias { alias: String }, diff --git a/bindings/core/src/method_handler/wallet.rs b/bindings/core/src/method_handler/wallet.rs index f7f1f5fb21..a4fbb1f626 100644 --- a/bindings/core/src/method_handler/wallet.rs +++ b/bindings/core/src/method_handler/wallet.rs @@ -138,10 +138,6 @@ pub(crate) async fn call_wallet_method_internal( let output_ids = wallet.claimable_outputs(outputs_to_claim).await?; Response::OutputIds(output_ids) } - WalletMethod::ClaimOutputs { output_ids_to_claim } => { - let transaction = wallet.claim_outputs(output_ids_to_claim.to_vec()).await?; - Response::SentTransaction(TransactionWithMetadataDto::from(&transaction)) - } // #[cfg(feature = "participation")] // WalletMethod::DeregisterParticipationEvent { event_id } => { // wallet.deregister_participation_event(&event_id).await?; @@ -390,22 +386,6 @@ pub(crate) async fn call_wallet_method_internal( .await?; Response::Ok } - WalletMethod::Send { - amount, - address, - options, - } => { - let transaction = wallet.send(amount, address, options).await?; - Response::SentTransaction(TransactionWithMetadataDto::from(&transaction)) - } - WalletMethod::SendWithParams { params, options } => { - let transaction = wallet.send_with_params(params, options).await?; - Response::SentTransaction(TransactionWithMetadataDto::from(&transaction)) - } - WalletMethod::SendOutputs { outputs, options } => { - let transaction = wallet.send_outputs(outputs, options).await?; - Response::SentTransaction(TransactionWithMetadataDto::from(&transaction)) - } WalletMethod::SetAlias { alias } => { wallet.set_alias(&alias).await?; Response::Ok diff --git a/bindings/core/src/response.rs b/bindings/core/src/response.rs index 8a3f060e0e..2b7a8f889c 100644 --- a/bindings/core/src/response.rs +++ b/bindings/core/src/response.rs @@ -271,12 +271,12 @@ pub enum Response { /// - [`VerifySecp256k1EcdsaSignature`](crate::method::UtilsMethod::VerifySecp256k1EcdsaSignature) Bool(bool), /// Response for: - /// - [`Backup`](crate::method::WalletMethod::Backup), + /// - [`BackupToStrongholdSnapshot`](crate::method::WalletMethod::BackupToStrongholdSnapshot), /// - [`ClearListeners`](crate::method::WalletMethod::ClearListeners) /// - [`ClearStrongholdPassword`](crate::method::WalletMethod::ClearStrongholdPassword), /// - [`DeregisterParticipationEvent`](crate::method::WalletMethod::DeregisterParticipationEvent), /// - [`EmitTestEvent`](crate::method::WalletMethod::EmitTestEvent), - /// - [`RestoreBackup`](crate::method::WalletMethod::RestoreBackup), + /// - [`RestoreFromStrongholdSnapshot`](crate::method::WalletMethod::RestoreFromStrongholdSnapshot), /// - [`SetAlias`](crate::method::WalletMethod::SetAlias), /// - [`SetClientOptions`](crate::method::WalletMethod::SetClientOptions), /// - [`SetDefaultSyncOptions`](crate::method::WalletMethod::SetDefaultSyncOptions), @@ -326,10 +326,10 @@ pub enum Response { /// - [`PrepareMeltNativeToken`](crate::method::WalletMethod::PrepareMeltNativeToken) /// - [`PrepareMintNativeToken`](crate::method::WalletMethod::PrepareMintNativeToken), /// - [`PrepareMintNfts`](crate::method::WalletMethod::PrepareMintNfts), - /// - [`PrepareSend`](crate::method::WalletMethod::PrepareSend), /// - [`PrepareSendMana`](crate::method::WalletMethod::PrepareSendMana), /// - [`PrepareSendNativeTokens`](crate::method::WalletMethod::PrepareSendNativeTokens), /// - [`PrepareSendNft`](crate::method::WalletMethod::PrepareSendNft), + /// - [`PrepareSend`](crate::method::WalletMethod::PrepareSend), /// - [`PrepareStopParticipating`](crate::method::WalletMethod::PrepareStopParticipating) /// - [`PrepareSendOutputs`](crate::method::WalletMethod::PrepareSendOutputs) /// - [`PrepareVote`](crate::method::WalletMethod::PrepareVote) @@ -358,9 +358,6 @@ pub enum Response { /// - [`Sync`](crate::method::WalletMethod::Sync) Balance(Balance), /// Response for: - /// - [`ClaimOutputs`](crate::method::WalletMethod::ClaimOutputs) - /// - [`Send`](crate::method::WalletMethod::Send) - /// - [`SendOutputs`](crate::method::WalletMethod::SendOutputs) /// - [`SignAndSubmitTransaction`](crate::method::WalletMethod::SignAndSubmitTransaction) /// - [`SubmitAndStoreTransaction`](crate::method::WalletMethod::SubmitAndStoreTransaction) SentTransaction(TransactionWithMetadataDto), diff --git a/bindings/nodejs/lib/types/wallet/bridge/index.ts b/bindings/nodejs/lib/types/wallet/bridge/index.ts index 00e935d384..96f0c82d2f 100644 --- a/bindings/nodejs/lib/types/wallet/bridge/index.ts +++ b/bindings/nodejs/lib/types/wallet/bridge/index.ts @@ -24,8 +24,8 @@ import type { __PrepareMintNativeTokenMethod__, __PrepareMintNftsMethod__, __PrepareOutputMethod__, - __PrepareSendMethod__, __PrepareSendManaMethod__, + __PrepareSendMethod__, __PrepareCreateDelegationMethod__, __PrepareDelayDelegationClaimingMethod__, __PrepareBeginStakingMethod__, @@ -34,8 +34,6 @@ import type { __PrepareSendOutputsMethod__, __RegisterParticipationEventsMethod__, __WaitForTransactionAcceptanceMethod__, - __SendMethod__, - __SendWithParamsMethod__, __PrepareSendNativeTokensMethod__, __PrepareSendNftMethod__, __SendOutputsMethod__, @@ -105,8 +103,8 @@ export type __WalletMethod__ = | __PrepareMintNativeTokenMethod__ | __PrepareMintNftsMethod__ | __PrepareOutputMethod__ - | __PrepareSendMethod__ | __PrepareSendManaMethod__ + | __PrepareSendMethod__ | __PrepareCreateDelegationMethod__ | __PrepareDelayDelegationClaimingMethod__ | __PrepareBeginStakingMethod__ @@ -115,8 +113,6 @@ export type __WalletMethod__ = | __PrepareSendOutputsMethod__ | __RegisterParticipationEventsMethod__ | __WaitForTransactionAcceptanceMethod__ - | __SendMethod__ - | __SendWithParamsMethod__ | __PrepareSendNativeTokensMethod__ | __PrepareSendNftMethod__ | __SendOutputsMethod__ diff --git a/bindings/nodejs/lib/types/wallet/bridge/wallet.ts b/bindings/nodejs/lib/types/wallet/bridge/wallet.ts index baff7e5311..d6621c47b2 100644 --- a/bindings/nodejs/lib/types/wallet/bridge/wallet.ts +++ b/bindings/nodejs/lib/types/wallet/bridge/wallet.ts @@ -343,14 +343,6 @@ export type __PrepareOutputMethod__ = { }; }; -export type __PrepareSendMethod__ = { - name: 'prepareSend'; - data: { - params: SendParams[]; - options?: TransactionOptions; - }; -}; - export type __PrepareSendOutputsMethod__ = { name: 'prepareSendOutputs'; data: { @@ -375,17 +367,8 @@ export type __WaitForTransactionAcceptanceMethod__ = { }; }; -export type __SendMethod__ = { - name: 'send'; - data: { - amount: NumericString; - address: string; - options?: TransactionOptions; - }; -}; - -export type __SendWithParamsMethod__ = { - name: 'sendWithParams'; +export type __PrepareSendMethod__ = { + name: 'prepareSend'; data: { params: SendParams[]; options?: TransactionOptions; diff --git a/bindings/nodejs/lib/wallet/wallet.ts b/bindings/nodejs/lib/wallet/wallet.ts index e1d83e5ae9..8f5f33e3dd 100644 --- a/bindings/nodejs/lib/wallet/wallet.ts +++ b/bindings/nodejs/lib/wallet/wallet.ts @@ -1463,11 +1463,11 @@ export class Wallet { } /** - * Send outputs. + * Send outputs in a transaction. * - * @param outputs Outputs to use in the transaction. - * @param options Additional transaction options. - * @returns The transaction data. + * @param outputs The outputs to send. + * @param transactionOptions Additional transaction options. + * @returns The sent transaction. */ async sendOutputs( outputs: Output[], @@ -1555,18 +1555,9 @@ export class Wallet { if (typeof amount === 'bigint') { amount = amount.toString(10); } - const response = await this.methodHandler.callMethod({ - name: 'send', - data: { - amount, - address, - options: transactionOptions, - }, - }); - const parsed = JSON.parse( - response, - ) as Response; - return plainToInstance(TransactionWithMetadata, parsed.payload); + return ( + await this.prepareSend([{ address, amount }], transactionOptions) + ).send(); } /** @@ -1580,22 +1571,7 @@ export class Wallet { params: SendParams[], transactionOptions?: TransactionOptions, ): Promise { - for (let i = 0; i < params.length; i++) { - if (typeof params[i].amount === 'bigint') { - params[i].amount = params[i].amount.toString(10); - } - } - const response = await this.methodHandler.callMethod({ - name: 'sendWithParams', - data: { - params, - options: transactionOptions, - }, - }); - const parsed = JSON.parse( - response, - ) as Response; - return plainToInstance(TransactionWithMetadata, parsed.payload); + return (await this.prepareSend(params, transactionOptions)).send(); } /** diff --git a/bindings/python/iota_sdk/wallet/wallet.py b/bindings/python/iota_sdk/wallet/wallet.py index eb8c8e6913..f9e666197c 100644 --- a/bindings/python/iota_sdk/wallet/wallet.py +++ b/bindings/python/iota_sdk/wallet/wallet.py @@ -591,18 +591,6 @@ def prepare_output(self, params: OutputParams, }) ) - def prepare_send(self, params: List[SendParams], - options: Optional[TransactionOptions] = None) -> PreparedTransaction: - """Prepare to send base coins. - """ - prepared = PreparedTransactionData.from_dict(self._call_method( - 'prepareSend', { - 'params': params, - 'options': options - } - )) - return PreparedTransaction(self, prepared) - def create_delegation(self, params: CreateDelegationParams, options: Optional[TransactionOptions] = None) -> CreateDelegationTransaction: """Create a delegation. @@ -735,24 +723,25 @@ def send(self, amount: int, address: str, options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: """Send base coins. """ - return TransactionWithMetadata.from_dict(self._call_method( - 'send', { - 'amount': str(amount), - 'address': address, - 'options': options - } - )) + return self.prepare_send([SendParams(address, amount)], options).send() def send_with_params( self, params: List[SendParams], options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: """Send base coins to multiple addresses or with additional parameters. """ - return TransactionWithMetadata.from_dict(self._call_method( - 'sendWithParams', { - 'params': [param.to_dict() for param in params], + return self.prepare_send(params, options).send() + + def prepare_send(self, params: List[SendParams], + options: Optional[TransactionOptions] = None) -> PreparedTransaction: + """Prepare to send with params. + """ + prepared = PreparedTransactionData.from_dict(self._call_method( + 'prepareSend', { + 'params': params, 'options': options } )) + return PreparedTransaction(self, prepared) def send_native_tokens( self, params: List[SendNativeTokenParams], options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: From 23f19d1608d0d68dd373e1ea6f3601e79dc1c354 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Thu, 7 Mar 2024 10:33:37 +0100 Subject: [PATCH 15/21] Make Block Issuer Feature when Staking Feature is present requirement syntactic (#2156) * Make Block Issuer Feature when Staking Feature is present requirement syntactic * nodejs * Python * error * move things around * nit --- .../models/api/transaction-failure-reason.ts | 85 ++++----- .../iota_sdk/types/transaction_metadata.py | 176 +++++++++--------- sdk/src/types/block/output/account.rs | 11 +- sdk/src/types/block/output/feature/error.rs | 2 + sdk/src/types/block/semantic/error.rs | 84 ++++----- sdk/src/types/block/semantic/mod.rs | 5 - 6 files changed, 178 insertions(+), 185 deletions(-) diff --git a/bindings/nodejs/lib/types/models/api/transaction-failure-reason.ts b/bindings/nodejs/lib/types/models/api/transaction-failure-reason.ts index a5b5934b5a..653df684ec 100644 --- a/bindings/nodejs/lib/types/models/api/transaction-failure-reason.ts +++ b/bindings/nodejs/lib/types/models/api/transaction-failure-reason.ts @@ -33,48 +33,47 @@ export enum TransactionFailureReason { SenderFeatureNotUnlocked = 25, IssuerFeatureNotUnlocked = 26, StakingRewardInputMissing = 27, - StakingBlockIssuerFeatureMissing = 28, - StakingCommitmentInputMissing = 29, - StakingRewardClaimingInvalid = 30, - StakingFeatureRemovedBeforeUnbonding = 31, - StakingFeatureModifiedBeforeUnbonding = 32, - StakingStartEpochInvalid = 33, - StakingEndEpochTooEarly = 34, - BlockIssuerCommitmentInputMissing = 35, - BlockIssuanceCreditInputMissing = 36, - BlockIssuerNotExpired = 37, - BlockIssuerExpiryTooEarly = 38, - ManaMovedOffBlockIssuerAccount = 39, - AccountLocked = 40, - TimelockCommitmentInputMissing = 41, - TimelockNotExpired = 42, - ExpirationCommitmentInputMissing = 43, - ExpirationNotUnlockable = 44, - ReturnAmountNotFulFilled = 45, - NewChainOutputHasNonZeroedId = 46, - ChainOutputImmutableFeaturesChanged = 47, - ImplicitAccountDestructionDisallowed = 48, - MultipleImplicitAccountCreationAddresses = 49, - AccountInvalidFoundryCounter = 50, - AnchorInvalidStateTransition = 51, - AnchorInvalidGovernanceTransition = 52, - FoundryTransitionWithoutAccount = 53, - FoundrySerialInvalid = 54, - DelegationCommitmentInputMissing = 55, - DelegationRewardInputMissing = 56, - DelegationRewardsClaimingInvalid = 57, - DelegationOutputTransitionedTwice = 58, - DelegationModified = 59, - DelegationStartEpochInvalid = 60, - DelegationAmountMismatch = 61, - DelegationEndEpochNotZero = 62, - DelegationEndEpochInvalid = 63, - CapabilitiesNativeTokenBurningNotAllowed = 64, - CapabilitiesManaBurningNotAllowed = 65, - CapabilitiesAccountDestructionNotAllowed = 66, - CapabilitiesAnchorDestructionNotAllowed = 67, - CapabilitiesFoundryDestructionNotAllowed = 68, - CapabilitiesNftDestructionNotAllowed = 69, + StakingCommitmentInputMissing = 28, + StakingRewardClaimingInvalid = 29, + StakingFeatureRemovedBeforeUnbonding = 30, + StakingFeatureModifiedBeforeUnbonding = 31, + StakingStartEpochInvalid = 32, + StakingEndEpochTooEarly = 33, + BlockIssuerCommitmentInputMissing = 34, + BlockIssuanceCreditInputMissing = 35, + BlockIssuerNotExpired = 36, + BlockIssuerExpiryTooEarly = 37, + ManaMovedOffBlockIssuerAccount = 38, + AccountLocked = 39, + TimelockCommitmentInputMissing = 40, + TimelockNotExpired = 41, + ExpirationCommitmentInputMissing = 42, + ExpirationNotUnlockable = 43, + ReturnAmountNotFulFilled = 44, + NewChainOutputHasNonZeroedId = 45, + ChainOutputImmutableFeaturesChanged = 46, + ImplicitAccountDestructionDisallowed = 47, + MultipleImplicitAccountCreationAddresses = 48, + AccountInvalidFoundryCounter = 49, + AnchorInvalidStateTransition = 50, + AnchorInvalidGovernanceTransition = 51, + FoundryTransitionWithoutAccount = 52, + FoundrySerialInvalid = 53, + DelegationCommitmentInputMissing = 54, + DelegationRewardInputMissing = 55, + DelegationRewardsClaimingInvalid = 56, + DelegationOutputTransitionedTwice = 57, + DelegationModified = 58, + DelegationStartEpochInvalid = 59, + DelegationAmountMismatch = 60, + DelegationEndEpochNotZero = 61, + DelegationEndEpochInvalid = 62, + CapabilitiesNativeTokenBurningNotAllowed = 63, + CapabilitiesManaBurningNotAllowed = 64, + CapabilitiesAccountDestructionNotAllowed = 65, + CapabilitiesAnchorDestructionNotAllowed = 66, + CapabilitiesFoundryDestructionNotAllowed = 67, + CapabilitiesNftDestructionNotAllowed = 68, SemanticValidationFailed = 255, } @@ -138,8 +137,6 @@ export const TRANSACTION_FAILURE_REASON_STRINGS: { 'Issuer feature is not unlocked.', [TransactionFailureReason.StakingRewardInputMissing]: 'Staking feature removal or resetting requires a reward input.', - [TransactionFailureReason.StakingBlockIssuerFeatureMissing]: - 'Block issuer feature missing for account with staking feature.', [TransactionFailureReason.StakingCommitmentInputMissing]: 'Staking feature validation requires a commitment input.', [TransactionFailureReason.StakingRewardClaimingInvalid]: diff --git a/bindings/python/iota_sdk/types/transaction_metadata.py b/bindings/python/iota_sdk/types/transaction_metadata.py index 0c6d628678..d7928d0e98 100644 --- a/bindings/python/iota_sdk/types/transaction_metadata.py +++ b/bindings/python/iota_sdk/types/transaction_metadata.py @@ -52,48 +52,47 @@ class TransactionFailureReason(Enum): SenderFeatureNotUnlocked = 25 IssuerFeatureNotUnlocked = 26 StakingRewardInputMissing = 27 - StakingBlockIssuerFeatureMissing = 28 - StakingCommitmentInputMissing = 29 - StakingRewardClaimingInvalid = 30 - StakingFeatureRemovedBeforeUnbonding = 31 - StakingFeatureModifiedBeforeUnbonding = 32 - StakingStartEpochInvalid = 33 - StakingEndEpochTooEarly = 34 - BlockIssuerCommitmentInputMissing = 35 - BlockIssuanceCreditInputMissing = 36 - BlockIssuerNotExpired = 37 - BlockIssuerExpiryTooEarly = 38 - ManaMovedOffBlockIssuerAccount = 39 - AccountLocked = 40 - TimelockCommitmentInputMissing = 41 - TimelockNotExpired = 42 - ExpirationCommitmentInputMissing = 43 - ExpirationNotUnlockable = 44 - ReturnAmountNotFulFilled = 45 - NewChainOutputHasNonZeroedId = 46 - ChainOutputImmutableFeaturesChanged = 47 - ImplicitAccountDestructionDisallowed = 48 - MultipleImplicitAccountCreationAddresses = 49 - AccountInvalidFoundryCounter = 50 - AnchorInvalidStateTransition = 51 - AnchorInvalidGovernanceTransition = 52 - FoundryTransitionWithoutAccount = 53 - FoundrySerialInvalid = 54 - DelegationCommitmentInputMissing = 55 - DelegationRewardInputMissing = 56 - DelegationRewardsClaimingInvalid = 57 - DelegationOutputTransitionedTwice = 58 - DelegationModified = 59 - DelegationStartEpochInvalid = 60 - DelegationAmountMismatch = 61 - DelegationEndEpochNotZero = 62 - DelegationEndEpochInvalid = 63 - CapabilitiesNativeTokenBurningNotAllowed = 64 - CapabilitiesManaBurningNotAllowed = 65 - CapabilitiesAccountDestructionNotAllowed = 66 - CapabilitiesAnchorDestructionNotAllowed = 67 - CapabilitiesFoundryDestructionNotAllowed = 68 - CapabilitiesNftDestructionNotAllowed = 69 + StakingCommitmentInputMissing = 28 + StakingRewardClaimingInvalid = 29 + StakingFeatureRemovedBeforeUnbonding = 30 + StakingFeatureModifiedBeforeUnbonding = 31 + StakingStartEpochInvalid = 32 + StakingEndEpochTooEarly = 33 + BlockIssuerCommitmentInputMissing = 34 + BlockIssuanceCreditInputMissing = 35 + BlockIssuerNotExpired = 36 + BlockIssuerExpiryTooEarly = 37 + ManaMovedOffBlockIssuerAccount = 38 + AccountLocked = 39 + TimelockCommitmentInputMissing = 40 + TimelockNotExpired = 41 + ExpirationCommitmentInputMissing = 42 + ExpirationNotUnlockable = 43 + ReturnAmountNotFulFilled = 44 + NewChainOutputHasNonZeroedId = 45 + ChainOutputImmutableFeaturesChanged = 46 + ImplicitAccountDestructionDisallowed = 47 + MultipleImplicitAccountCreationAddresses = 48 + AccountInvalidFoundryCounter = 49 + AnchorInvalidStateTransition = 50 + AnchorInvalidGovernanceTransition = 51 + FoundryTransitionWithoutAccount = 52 + FoundrySerialInvalid = 53 + DelegationCommitmentInputMissing = 54 + DelegationRewardInputMissing = 54 + DelegationRewardsClaimingInvalid = 56 + DelegationOutputTransitionedTwice = 57 + DelegationModified = 58 + DelegationStartEpochInvalid = 59 + DelegationAmountMismatch = 60 + DelegationEndEpochNotZero = 61 + DelegationEndEpochInvalid = 62 + CapabilitiesNativeTokenBurningNotAllowed = 63 + CapabilitiesManaBurningNotAllowed = 64 + CapabilitiesAccountDestructionNotAllowed = 65 + CapabilitiesAnchorDestructionNotAllowed = 66 + CapabilitiesFoundryDestructionNotAllowed = 67 + CapabilitiesNftDestructionNotAllowed = 68 SemanticValidationFailed = 255 def __str__(self): @@ -106,7 +105,7 @@ def __str__(self): 5: "Invalid unlock for chain address.", 6: "Invalid unlock for direct unlockable address.", 7: "Invalid unlock for multi address.", - 8: "Commitment input required with reward or BIC input.", + 8: "Commitment input references an invalid or non-existent commitment.", 9: "BIC input reference cannot be loaded.", 10: "Reward input does not reference a staking account or a delegation output.", 11: "Staking rewards could not be calculated due to storage issues or overflow.", @@ -117,56 +116,55 @@ def __str__(self): 16: "Mana decay creation slot/epoch index exceeds target slot/epoch index.", 17: "Native token sums are unbalanced.", 18: "Simple token scheme minted/melted value decreased.", - 19: "Simple token scheme minting invalid.", - 20: "Simple token scheme melting invalid.", - 21: "Simple token scheme maximum supply changed.", - 22: "Simple token scheme genesis invalid.", + 19: "Simple token scheme's minted tokens did not increase by the minted amount or melted tokens changed.", + 20: "Simple token scheme's melted tokens did not increase by the melted amount or minted tokens changed.", + 21: "Simple token scheme's maximum supply cannot change during transition.", + 22: "Newly created simple token scheme's melted tokens are not zero or minted tokens do not equal native token amount in transaction.", 23: "Multi address length and multi unlock length do not match.", 24: "Multi address unlock threshold not reached.", 25: "Sender feature is not unlocked.", 26: "Issuer feature is not unlocked.", 27: "Staking feature removal or resetting requires a reward input.", - 28: "Block issuer feature missing for account with staking feature.", - 29: "Staking feature validation requires a commitment input.", - 30: "Staking feature must be removed or reset in order to claim rewards.", - 31: "Staking feature can only be removed after the unbonding period.", - 32: "Staking start epoch, fixed cost and staked amount cannot be modified while bonded.", - 33: "Staking start epoch must be the epoch of the transaction.", - 34: "Staking end epoch must be set to the transaction epoch plus the unbonding period.", - 35: "Commitment input missing for block issuer feature.", - 36: "Block issuance credit input missing for account with block issuer feature.", - 37: "Block issuer feature has not expired.", - 38: "Block issuer feature expiry set too early.", - 39: "Mana cannot be moved off block issuer accounts except with manalocks.", - 40: "Account is locked due to negative block issuance credits.", - 41: "Transaction's containing a timelock condition require a commitment input.", - 42: "Timelock not expired.", - 43: "Transaction's containing an expiration condition require a commitment input.", - 44: "Expiration unlock condition cannot be unlocked.", - 45: "Return amount not fulfilled.", - 46: "New chain output has non-zeroed ID.", - 47: "Immutable features in chain output modified during transition.", - 48: "Cannot destroy implicit account; must be transitioned to account.", - 49: "Multiple implicit account creation addresses on the input side.", - 50: "Foundry counter in account decreased or did not increase by the number of new foundries.", - 51: "Anchor has an invalid state transition.", - 52: "Anchor has an invalid governance transition.", - 53: "Foundry output transitioned without accompanying account on input or output side.", - 54: "Foundry output serial number is invalid.", - 55: "Delegation output validation requires a commitment input.", - 56: "Delegation output cannot be destroyed without a reward input.", - 57: "Invalid delegation mana rewards claiming.", - 58: "Delegation output attempted to be transitioned twice.", - 59: "Delegated amount, validator ID and start epoch cannot be modified.", - 60: "Invalid start epoch.", - 61: "Delegated amount does not match amount.", - 62: "End epoch must be set to zero at output genesis.", - 63: "Delegation end epoch does not match current epoch.", - 64: "Native token burning is not allowed by the transaction capabilities.", - 65: "Mana burning is not allowed by the transaction capabilities.", - 66: "Account destruction is not allowed by the transaction capabilities.", - 67: "Anchor destruction is not allowed by the transaction capabilities.", - 68: "Foundry destruction is not allowed by the transaction capabilities.", - 69: "NFT destruction is not allowed by the transaction capabilities.", + 28: "Staking feature validation requires a commitment input.", + 29: "Staking feature must be removed or reset in order to claim rewards.", + 30: "Staking feature can only be removed after the unbonding period.", + 31: "Staking start epoch, fixed cost and staked amount cannot be modified while bonded.", + 32: "Staking start epoch must be the epoch of the transaction.", + 33: "Staking end epoch must be set to the transaction epoch plus the unbonding period.", + 34: "Commitment input missing for block issuer feature.", + 35: "Block issuance credit input missing for account with block issuer feature.", + 36: "Block issuer feature has not expired.", + 37: "Block issuer feature expiry set too early.", + 38: "Mana cannot be moved off block issuer accounts except with manalocks.", + 39: "Account is locked due to negative block issuance credits.", + 40: "Transaction's containing a timelock condition require a commitment input.", + 41: "Timelock not expired.", + 42: "Transaction's containing an expiration condition require a commitment input.", + 43: "Expiration unlock condition cannot be unlocked.", + 44: "Return amount not fulfilled.", + 45: "New chain output has non-zeroed ID.", + 46: "Immutable features in chain output modified during transition.", + 47: "Cannot destroy implicit account; must be transitioned to account.", + 48: "Multiple implicit account creation addresses on the input side.", + 49: "Foundry counter in account decreased or did not increase by the number of new foundries.", + 50: "Anchor has an invalid state transition.", + 51: "Anchor has an invalid governance transition.", + 52: "Foundry output transitioned without accompanying account on input or output side.", + 53: "Foundry output serial number is invalid.", + 54: "Delegation output validation requires a commitment input.", + 55: "Delegation output cannot be destroyed without a reward input.", + 56: "Invalid delegation mana rewards claiming.", + 57: "Delegation output attempted to be transitioned twice.", + 58: "Delegated amount, validator ID and start epoch cannot be modified.", + 59: "Invalid start epoch.", + 60: "Delegated amount does not match amount.", + 61: "End epoch must be set to zero at output genesis.", + 62: "Delegation end epoch does not match current epoch.", + 63: "Native token burning is not allowed by the transaction capabilities.", + 64: "Mana burning is not allowed by the transaction capabilities.", + 65: "Account destruction is not allowed by the transaction capabilities.", + 66: "Anchor destruction is not allowed by the transaction capabilities.", + 67: "Foundry destruction is not allowed by the transaction capabilities.", + 68: "NFT destruction is not allowed by the transaction capabilities.", 255: "Semantic validation failed.", }[self.value] diff --git a/sdk/src/types/block/output/account.rs b/sdk/src/types/block/output/account.rs index c386d42123..e3b22d7af1 100644 --- a/sdk/src/types/block/output/account.rs +++ b/sdk/src/types/block/output/account.rs @@ -13,7 +13,7 @@ use packable::{ use crate::types::block::{ address::{AccountAddress, Address}, output::{ - feature::{verify_allowed_features, Feature, FeatureFlags, Features}, + feature::{verify_allowed_features, Feature, FeatureError, FeatureFlags, Features}, unlock_condition::{ verify_allowed_unlock_conditions, verify_restricted_addresses, UnlockCondition, UnlockConditionFlags, UnlockConditions, @@ -259,7 +259,7 @@ impl AccountOutputBuilder { OutputBuilderAmount::MinimumAmount(params) => output.minimum_amount(params), }; - verify_staked_amount(output.amount, &output.features)?; + verify_staking(output.amount, &output.features)?; Ok(output) } @@ -511,7 +511,7 @@ impl Packable for AccountOutput { verify_allowed_features(&features, Self::ALLOWED_FEATURES) .map_err(UnpackError::Packable) .coerce()?; - verify_staked_amount(amount, &features).map_err(UnpackError::Packable)?; + verify_staking(amount, &features).map_err(UnpackError::Packable)?; } let immutable_features = Features::unpack_inner(unpacker, visitor).coerce()?; @@ -560,8 +560,11 @@ fn verify_unlock_conditions(unlock_conditions: &UnlockConditions, account_id: &A )?) } -fn verify_staked_amount(amount: u64, features: &Features) -> Result<(), OutputError> { +fn verify_staking(amount: u64, features: &Features) -> Result<(), OutputError> { if let Some(staking) = features.staking() { + if features.block_issuer().is_none() { + return Err(FeatureError::StakingBlockIssuerMissing)?; + } if amount < staking.staked_amount() { return Err(OutputError::InvalidStakedAmount); } diff --git a/sdk/src/types/block/output/feature/error.rs b/sdk/src/types/block/output/feature/error.rs index 6e041fc356..f340d55766 100644 --- a/sdk/src/types/block/output/feature/error.rs +++ b/sdk/src/types/block/output/feature/error.rs @@ -44,6 +44,8 @@ pub enum FeatureError { InvalidBlockIssuerKeyCount(>::Error), #[display(fmt = "block issuer keys are not unique and/or sorted")] BlockIssuerKeysNotUniqueSorted, + #[display(fmt = "block issuer feature missing for account with staking feature")] + StakingBlockIssuerMissing, #[from] NativeToken(NativeTokenError), #[from] diff --git a/sdk/src/types/block/semantic/error.rs b/sdk/src/types/block/semantic/error.rs index c9d13ab0d7..a9ac1ba4a8 100644 --- a/sdk/src/types/block/semantic/error.rs +++ b/sdk/src/types/block/semantic/error.rs @@ -98,90 +98,88 @@ pub enum TransactionFailureReason { IssuerFeatureNotUnlocked = 26, #[display(fmt = "staking feature removal or resetting requires a reward input")] StakingRewardInputMissing = 27, - #[display(fmt = "block issuer feature missing for account with staking feature")] - StakingBlockIssuerFeatureMissing = 28, #[display(fmt = "staking feature validation requires a commitment input")] - StakingCommitmentInputMissing = 29, + StakingCommitmentInputMissing = 28, #[display(fmt = "staking feature must be removed or reset in order to claim rewards")] - StakingRewardClaimingInvalid = 30, + StakingRewardClaimingInvalid = 29, #[display(fmt = "staking feature can only be removed after the unbonding period")] - StakingFeatureRemovedBeforeUnbonding = 31, + StakingFeatureRemovedBeforeUnbonding = 30, #[display(fmt = "staking start epoch, fixed cost and staked amount cannot be modified while bonded")] - StakingFeatureModifiedBeforeUnbonding = 32, + StakingFeatureModifiedBeforeUnbonding = 31, #[display(fmt = "staking start epoch must be the epoch of the transaction")] - StakingStartEpochInvalid = 33, + StakingStartEpochInvalid = 32, #[display(fmt = "staking end epoch must be set to the transaction epoch plus the unbonding period")] - StakingEndEpochTooEarly = 34, + StakingEndEpochTooEarly = 33, #[display(fmt = "commitment input missing for block issuer feature")] - BlockIssuerCommitmentInputMissing = 35, + BlockIssuerCommitmentInputMissing = 34, #[display(fmt = "block issuance credit input missing for account with block issuer feature")] - BlockIssuanceCreditInputMissing = 36, + BlockIssuanceCreditInputMissing = 35, #[display(fmt = "block issuer feature has not expired")] - BlockIssuerNotExpired = 37, + BlockIssuerNotExpired = 36, #[display(fmt = "block issuer feature expiry set too early")] - BlockIssuerExpiryTooEarly = 38, + BlockIssuerExpiryTooEarly = 37, #[display(fmt = "mana cannot be moved off block issuer accounts except with manalocks")] - ManaMovedOffBlockIssuerAccount = 39, + ManaMovedOffBlockIssuerAccount = 38, #[display(fmt = "account is locked due to negative block issuance credits")] - AccountLocked = 40, + AccountLocked = 39, #[display(fmt = "transaction's containing a timelock condition require a commitment input")] - TimelockCommitmentInputMissing = 41, + TimelockCommitmentInputMissing = 40, #[display(fmt = "timelock not expired")] - TimelockNotExpired = 42, + TimelockNotExpired = 41, #[display(fmt = "transaction's containing an expiration condition require a commitment input")] - ExpirationCommitmentInputMissing = 43, + ExpirationCommitmentInputMissing = 42, #[display(fmt = "expiration unlock condition cannot be unlocked")] - ExpirationNotUnlockable = 44, + ExpirationNotUnlockable = 43, #[display(fmt = "return amount not fulfilled")] - ReturnAmountNotFulFilled = 45, + ReturnAmountNotFulFilled = 44, #[display(fmt = "new chain output has non-zeroed ID")] - NewChainOutputHasNonZeroedId = 46, + NewChainOutputHasNonZeroedId = 45, #[display(fmt = "immutable features in chain output modified during transition")] - ChainOutputImmutableFeaturesChanged = 47, + ChainOutputImmutableFeaturesChanged = 46, #[display(fmt = "cannot destroy implicit account; must be transitioned to account")] - ImplicitAccountDestructionDisallowed = 48, + ImplicitAccountDestructionDisallowed = 47, #[display(fmt = "multiple implicit account creation addresses on the input side")] - MultipleImplicitAccountCreationAddresses = 49, + MultipleImplicitAccountCreationAddresses = 48, #[display(fmt = "foundry counter in account decreased or did not increase by the number of new foundries")] - AccountInvalidFoundryCounter = 50, + AccountInvalidFoundryCounter = 49, #[display(fmt = "invalid anchor state transition")] - AnchorInvalidStateTransition = 51, + AnchorInvalidStateTransition = 50, #[display(fmt = "invalid anchor governance transition")] - AnchorInvalidGovernanceTransition = 52, + AnchorInvalidGovernanceTransition = 51, #[display(fmt = "foundry output transitioned without accompanying account on input or output side")] - FoundryTransitionWithoutAccount = 53, + FoundryTransitionWithoutAccount = 52, #[display(fmt = "foundry output serial number is invalid")] - FoundrySerialInvalid = 54, + FoundrySerialInvalid = 53, #[display(fmt = "delegation output validation requires a commitment input")] - DelegationCommitmentInputMissing = 55, + DelegationCommitmentInputMissing = 54, #[display(fmt = "delegation output cannot be destroyed without a reward input")] - DelegationRewardInputMissing = 56, + DelegationRewardInputMissing = 55, #[display(fmt = "invalid delegation mana rewards claiming")] - DelegationRewardsClaimingInvalid = 57, + DelegationRewardsClaimingInvalid = 56, #[display(fmt = "attempted to transition delegation output twice")] - DelegationOutputTransitionedTwice = 58, + DelegationOutputTransitionedTwice = 57, #[display(fmt = "delegated amount, validator ID and start epoch cannot be modified")] - DelegationModified = 59, + DelegationModified = 58, #[display(fmt = "delegation output has invalid start epoch")] - DelegationStartEpochInvalid = 60, + DelegationStartEpochInvalid = 59, #[display(fmt = "delegated amount does not match amount")] - DelegationAmountMismatch = 61, + DelegationAmountMismatch = 60, #[display(fmt = "end epoch must be set to zero at output genesis")] - DelegationEndEpochNotZero = 62, + DelegationEndEpochNotZero = 61, #[display(fmt = "delegation end epoch does not match current epoch")] - DelegationEndEpochInvalid = 63, + DelegationEndEpochInvalid = 62, #[display(fmt = "native token burning is not allowed by the transaction capabilities")] - CapabilitiesNativeTokenBurningNotAllowed = 64, + CapabilitiesNativeTokenBurningNotAllowed = 63, #[display(fmt = "mana burning is not allowed by the transaction capabilities")] - CapabilitiesManaBurningNotAllowed = 65, + CapabilitiesManaBurningNotAllowed = 64, #[display(fmt = "account destruction is not allowed by the transaction capabilities")] - CapabilitiesAccountDestructionNotAllowed = 66, + CapabilitiesAccountDestructionNotAllowed = 65, #[display(fmt = "anchor destruction is not allowed by the transaction capabilities")] - CapabilitiesAnchorDestructionNotAllowed = 67, + CapabilitiesAnchorDestructionNotAllowed = 66, #[display(fmt = "foundry destruction is not allowed by the transaction capabilities")] - CapabilitiesFoundryDestructionNotAllowed = 68, + CapabilitiesFoundryDestructionNotAllowed = 67, #[display(fmt = "NFT destruction is not allowed by the transaction capabilities")] - CapabilitiesNftDestructionNotAllowed = 69, + CapabilitiesNftDestructionNotAllowed = 68, #[display(fmt = "semantic validation failed")] SemanticValidationFailed = 255, } diff --git a/sdk/src/types/block/semantic/mod.rs b/sdk/src/types/block/semantic/mod.rs index f3f02fb28b..89f04c5f91 100644 --- a/sdk/src/types/block/semantic/mod.rs +++ b/sdk/src/types/block/semantic/mod.rs @@ -314,11 +314,6 @@ impl<'a> SemanticValidationContext<'a> { .ok_or(TransactionFailureReason::ManaOverflow)?; } } - if output.features().staking().is_some() { - if output.features().block_issuer().is_none() { - return Err(TransactionFailureReason::StakingBlockIssuerFeatureMissing); - } - } (output.amount(), output.mana(), None, Some(output.features())) } From d03aaa9912e5d0bc1e63fd3d47ea4992eece98a1 Mon Sep 17 00:00:00 2001 From: DaughterOfMars Date: Thu, 7 Mar 2024 08:48:58 -0500 Subject: [PATCH 16/21] Clean up requirements (#2153) Co-authored-by: Thibault Martinez --- .../block_builder/transaction_builder/mod.rs | 11 +--- .../requirement/account.rs | 22 ++++---- .../transaction_builder/requirement/amount.rs | 6 +-- .../requirement/context_inputs.rs | 15 ++---- .../requirement/delegation.rs | 19 ++++--- .../requirement/ed25519.rs | 17 ++++--- .../requirement/foundry.rs | 22 ++++---- .../transaction_builder/requirement/issuer.rs | 20 +++----- .../transaction_builder/requirement/mana.rs | 8 +-- .../transaction_builder/requirement/mod.rs | 5 +- .../requirement/native_tokens.rs | 20 +++++--- .../transaction_builder/requirement/nft.rs | 22 ++++---- .../transaction_builder/requirement/sender.rs | 50 +++++++++---------- 13 files changed, 118 insertions(+), 119 deletions(-) diff --git a/sdk/src/client/api/block_builder/transaction_builder/mod.rs b/sdk/src/client/api/block_builder/transaction_builder/mod.rs index f819430c91..0bbf3255f7 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/mod.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/mod.rs @@ -347,16 +347,7 @@ impl TransactionBuilder { // Process all the requirements until there are no more. while let Some(requirement) = self.requirements.pop() { // Fulfill the requirement. - let inputs = self.fulfill_requirement(&requirement)?; - - if !self.allow_additional_input_selection && !inputs.is_empty() { - return Err(TransactionBuilderError::AdditionalInputsRequired(requirement)); - } - - // Select suggested inputs. - for input in inputs { - self.select_input(input)?; - } + self.fulfill_requirement(&requirement)?; } let (input_mana, output_mana) = self.mana_sums(false)?; diff --git a/sdk/src/client/api/block_builder/transaction_builder/requirement/account.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/account.rs index 3de6e4755c..f7eb0925e2 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/requirement/account.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/account.rs @@ -2,10 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use super::{Requirement, TransactionBuilder, TransactionBuilderError}; -use crate::{ - client::secret::types::InputSigningData, - types::block::output::{AccountId, Output, OutputId}, -}; +use crate::types::block::output::{AccountId, Output, OutputId}; /// Checks if an output is an account with output ID that matches the given account ID. pub(crate) fn is_account_with_id(output: &Output, account_id: &AccountId, output_id: &OutputId) -> bool { @@ -28,10 +25,7 @@ pub(crate) fn is_account_with_id_non_null(output: &Output, account_id: &AccountI 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, TransactionBuilderError> { + pub(crate) fn fulfill_account_requirement(&mut self, account_id: AccountId) -> Result<(), TransactionBuilderError> { // Check if the requirement is already fulfilled. if let Some(input) = self .selected_inputs @@ -42,7 +36,13 @@ impl TransactionBuilder { "{account_id:?} requirement already fulfilled by {:?}", input.output_id() ); - return Ok(Vec::new()); + return Ok(()); + } + + if !self.allow_additional_input_selection { + return Err(TransactionBuilderError::AdditionalInputsRequired(Requirement::Account( + account_id, + ))); } // Check if the requirement can be fulfilled. @@ -58,6 +58,8 @@ impl TransactionBuilder { log::debug!("{account_id:?} requirement fulfilled by {:?}", input.output_id()); - Ok(vec![input]) + self.select_input(input)?; + + Ok(()) } } diff --git a/sdk/src/client/api/block_builder/transaction_builder/requirement/amount.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/amount.rs index 81611ac609..ad2ae9f372 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/requirement/amount.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/amount.rs @@ -35,11 +35,11 @@ pub(crate) fn sdruc_not_expired( } impl TransactionBuilder { - pub(crate) fn fulfill_amount_requirement(&mut self) -> Result, TransactionBuilderError> { + pub(crate) fn fulfill_amount_requirement(&mut self) -> Result<(), TransactionBuilderError> { let (mut input_amount, mut output_amount) = self.amount_balance()?; if input_amount >= output_amount { log::debug!("Amount requirement already fulfilled"); - return Ok(Vec::new()); + return Ok(()); } log::debug!("Fulfilling amount requirement with input amount {input_amount}, output amount {output_amount}"); @@ -82,7 +82,7 @@ impl TransactionBuilder { } } - Ok(Vec::new()) + Ok(()) } pub(crate) fn amount_sums(&self) -> (u64, u64, HashMap, HashMap) { diff --git a/sdk/src/client/api/block_builder/transaction_builder/requirement/context_inputs.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/context_inputs.rs index 8315adf8aa..963754114c 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/requirement/context_inputs.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/context_inputs.rs @@ -2,18 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 use super::{TransactionBuilder, TransactionBuilderError}; -use crate::{ - client::secret::types::InputSigningData, - types::block::{ - context_input::{BlockIssuanceCreditContextInput, CommitmentContextInput}, - output::{AccountId, DelegationOutputBuilder, Output}, - }, +use crate::types::block::{ + context_input::{BlockIssuanceCreditContextInput, CommitmentContextInput}, + output::{AccountId, DelegationOutputBuilder, Output}, }; impl TransactionBuilder { - pub(crate) fn fulfill_context_inputs_requirement( - &mut self, - ) -> Result, TransactionBuilderError> { + pub(crate) fn fulfill_context_inputs_requirement(&mut self) -> Result<(), TransactionBuilderError> { let mut needs_commitment_context = false; for input in &self.selected_inputs { @@ -95,6 +90,6 @@ impl TransactionBuilder { self.commitment_context_input .replace(CommitmentContextInput::new(self.latest_slot_commitment_id)); } - Ok(Vec::new()) + Ok(()) } } diff --git a/sdk/src/client/api/block_builder/transaction_builder/requirement/delegation.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/delegation.rs index 99134e017c..96b6faf949 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/requirement/delegation.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/delegation.rs @@ -2,10 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use super::{Requirement, TransactionBuilder, TransactionBuilderError}; -use crate::{ - client::secret::types::InputSigningData, - types::block::output::{DelegationId, Output, OutputId}, -}; +use crate::types::block::output::{DelegationId, Output, OutputId}; /// Checks if an output is an delegation with a given delegation ID. /// Assumes that the output delegation ID can be null and hashes the output ID. @@ -34,7 +31,7 @@ impl TransactionBuilder { pub(crate) fn fulfill_delegation_requirement( &mut self, delegation_id: DelegationId, - ) -> Result, TransactionBuilderError> { + ) -> Result<(), TransactionBuilderError> { // Check if the requirement is already fulfilled. if let Some(input) = self .selected_inputs @@ -45,7 +42,13 @@ impl TransactionBuilder { "{delegation_id:?} requirement already fulfilled by {:?}", input.output_id() ); - return Ok(Vec::new()); + return Ok(()); + } + + if !self.allow_additional_input_selection { + return Err(TransactionBuilderError::AdditionalInputsRequired( + Requirement::Delegation(delegation_id), + )); } // Check if the requirement can be fulfilled. @@ -61,6 +64,8 @@ impl TransactionBuilder { log::debug!("{delegation_id:?} requirement fulfilled by {:?}", input.output_id()); - Ok(vec![input]) + self.select_input(input)?; + + Ok(()) } } diff --git a/sdk/src/client/api/block_builder/transaction_builder/requirement/ed25519.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/ed25519.rs index 5fd019e52e..8f53fb28fa 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/requirement/ed25519.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/ed25519.rs @@ -39,10 +39,7 @@ impl TransactionBuilder { } /// 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, TransactionBuilderError> { + 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 @@ -53,7 +50,13 @@ impl TransactionBuilder { "{address:?} sender requirement already fulfilled by {:?}", input.output_id() ); - return Ok(Vec::new()); + return Ok(()); + } + + if !self.allow_additional_input_selection { + return Err(TransactionBuilderError::AdditionalInputsRequired(Requirement::Ed25519( + address.clone(), + ))); } // Checks if the requirement can be fulfilled by a basic output. @@ -78,7 +81,9 @@ impl TransactionBuilder { log::debug!("{address:?} sender requirement fulfilled by {:?}", input.output_id(),); - Ok(vec![input]) + self.select_input(input)?; + + Ok(()) } None => Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Ed25519( address.clone(), diff --git a/sdk/src/client/api/block_builder/transaction_builder/requirement/foundry.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/foundry.rs index 8f0e403fc9..1404de1872 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/requirement/foundry.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/foundry.rs @@ -2,10 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use super::{Requirement, TransactionBuilder, TransactionBuilderError}; -use crate::{ - client::secret::types::InputSigningData, - types::block::output::{FoundryId, Output}, -}; +use crate::types::block::output::{FoundryId, Output}; /// Checks if an output is a foundry with a given foundry ID. pub(crate) fn is_foundry_with_id(output: &Output, foundry_id: &FoundryId) -> bool { @@ -18,10 +15,7 @@ pub(crate) fn is_foundry_with_id(output: &Output, foundry_id: &FoundryId) -> boo 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, TransactionBuilderError> { + pub(crate) fn fulfill_foundry_requirement(&mut self, foundry_id: FoundryId) -> Result<(), TransactionBuilderError> { // Check if the requirement is already fulfilled. if let Some(input) = self .selected_inputs @@ -32,7 +26,13 @@ impl TransactionBuilder { "{foundry_id:?} requirement already fulfilled by {:?}", input.output_id() ); - return Ok(Vec::new()); + return Ok(()); + } + + if !self.allow_additional_input_selection { + return Err(TransactionBuilderError::AdditionalInputsRequired(Requirement::Foundry( + foundry_id, + ))); } // Check if the requirement can be fulfilled. @@ -48,6 +48,8 @@ impl TransactionBuilder { log::debug!("{foundry_id:?} requirement fulfilled by {:?}", input.output_id()); - Ok(vec![input]) + self.select_input(input)?; + + Ok(()) } } diff --git a/sdk/src/client/api/block_builder/transaction_builder/requirement/issuer.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/issuer.rs index 3460e21d6b..18120a71a9 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/requirement/issuer.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/issuer.rs @@ -2,23 +2,19 @@ // SPDX-License-Identifier: Apache-2.0 use super::{Requirement, TransactionBuilder, TransactionBuilderError}; -use crate::{client::secret::types::InputSigningData, types::block::address::Address}; +use crate::types::block::address::Address; 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, TransactionBuilderError> { + 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(TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender(_))) => Err( - TransactionBuilderError::UnfulfillableRequirement(Requirement::Issuer(address.clone())), - ), - Err(e) => Err(e), - } + self.fulfill_sender_requirement(address).map_err(|e| match e { + TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender(_)) => { + TransactionBuilderError::UnfulfillableRequirement(Requirement::Issuer(address.clone())) + } + e => e, + }) } } diff --git a/sdk/src/client/api/block_builder/transaction_builder/requirement/mana.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/mana.rs index 8c945907d3..5b0de1d367 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/requirement/mana.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/mana.rs @@ -22,7 +22,7 @@ use crate::{ }; impl TransactionBuilder { - pub(crate) fn fulfill_mana_requirement(&mut self) -> Result, TransactionBuilderError> { + pub(crate) fn fulfill_mana_requirement(&mut self) -> Result<(), TransactionBuilderError> { let Some(MinManaAllotment { issuer_id, reference_mana_cost, @@ -31,7 +31,7 @@ impl TransactionBuilder { else { // If there is no min allotment calculation needed, just check mana self.get_inputs_for_mana_balance()?; - return Ok(Vec::new()); + return Ok(()); }; let mut should_recalculate = false; @@ -104,7 +104,7 @@ impl TransactionBuilder { 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()); + return Ok(()); } } @@ -125,7 +125,7 @@ impl TransactionBuilder { self.requirements.push(Requirement::Mana); } - Ok(Vec::new()) + Ok(()) } fn reduce_account_output(&mut self) -> Result { diff --git a/sdk/src/client/api/block_builder/transaction_builder/requirement/mod.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/mod.rs index 03a4b80369..1d257b9d4f 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/requirement/mod.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/mod.rs @@ -59,10 +59,7 @@ pub enum Requirement { 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, TransactionBuilderError> { + pub(crate) fn fulfill_requirement(&mut self, requirement: &Requirement) -> Result<(), TransactionBuilderError> { log::debug!("Fulfilling requirement {requirement:?}"); match requirement { diff --git a/sdk/src/client/api/block_builder/transaction_builder/requirement/native_tokens.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/native_tokens.rs index 57d467bf8b..5595928a10 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/requirement/native_tokens.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/native_tokens.rs @@ -8,7 +8,7 @@ use primitive_types::U256; use super::{TransactionBuilder, TransactionBuilderError}; use crate::{ - client::secret::types::InputSigningData, + client::api::transaction_builder::Requirement, types::block::{ output::{Output, TokenId, TokenScheme}, payload::signed_transaction::TransactionCapabilityFlag, @@ -16,9 +16,7 @@ use crate::{ }; impl TransactionBuilder { - pub(crate) fn fulfill_native_tokens_requirement( - &mut self, - ) -> Result, TransactionBuilderError> { + pub(crate) fn fulfill_native_tokens_requirement(&mut self) -> Result<(), TransactionBuilderError> { let (input_nts, output_nts) = self.get_input_output_native_tokens(); let diffs = get_native_tokens_diff(output_nts, input_nts); if self.burn.as_ref().map_or(false, |burn| !burn.native_tokens.is_empty()) { @@ -28,7 +26,13 @@ impl TransactionBuilder { if diffs.is_empty() { log::debug!("Native tokens requirement already fulfilled"); - return Ok(Vec::new()); + return Ok(()); + } + + if !self.allow_additional_input_selection { + return Err(TransactionBuilderError::AdditionalInputsRequired( + Requirement::NativeTokens, + )); } log::debug!("Fulfilling native tokens requirement"); @@ -77,7 +81,11 @@ impl TransactionBuilder { self.available_inputs .retain(|input| !newly_selected_ids.contains(input.output_id())); - Ok(newly_selected_inputs) + for input in newly_selected_inputs { + self.select_input(input)?; + } + + Ok(()) } pub(crate) fn get_input_output_native_tokens(&self) -> (BTreeMap, BTreeMap) { diff --git a/sdk/src/client/api/block_builder/transaction_builder/requirement/nft.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/nft.rs index 122f334052..505b561cfb 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/requirement/nft.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/nft.rs @@ -2,10 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use super::{Requirement, TransactionBuilder, TransactionBuilderError}; -use crate::{ - client::secret::types::InputSigningData, - types::block::output::{NftId, Output, OutputId}, -}; +use crate::types::block::output::{NftId, Output, OutputId}; /// Checks if an output is an nft with a given nft ID. /// Assumes that the output nft ID can be null and hashes the output ID. @@ -31,10 +28,7 @@ pub(crate) fn is_nft_with_id_non_null(output: &Output, nft_id: &NftId) -> bool { 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, TransactionBuilderError> { + 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 @@ -42,7 +36,13 @@ impl TransactionBuilder { .find(|input| is_nft_with_id(&input.output, &nft_id, input.output_id())) { log::debug!("{nft_id:?} requirement already fulfilled by {:?}", input.output_id()); - return Ok(Vec::new()); + return Ok(()); + } + + if !self.allow_additional_input_selection { + return Err(TransactionBuilderError::AdditionalInputsRequired(Requirement::Nft( + nft_id, + ))); } // Check if the requirement can be fulfilled. @@ -58,6 +58,8 @@ impl TransactionBuilder { log::debug!("{nft_id:?} requirement fulfilled by {:?}", input.output_id()); - Ok(vec![input]) + self.select_input(input)?; + + Ok(()) } } diff --git a/sdk/src/client/api/block_builder/transaction_builder/requirement/sender.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/sender.rs index 8ecb4cbf56..11d951cdef 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/requirement/sender.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/sender.rs @@ -2,48 +2,44 @@ // SPDX-License-Identifier: Apache-2.0 use super::{Requirement, TransactionBuilder, TransactionBuilderError}; -use crate::{client::secret::types::InputSigningData, types::block::address::Address}; +use crate::types::block::address::Address; 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, TransactionBuilderError> { + 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(TransactionBuilderError::UnfulfillableRequirement(Requirement::Ed25519(_))) => Err( - TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender(address.clone())), - ), - Err(e) => Err(e), - } + self.fulfill_ed25519_requirement(address).map_err(|e| match e { + TransactionBuilderError::UnfulfillableRequirement(Requirement::Ed25519(_)) => { + TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender(address.clone())) + } + e => e, + }) } Address::Account(account_address) => { log::debug!("Treating {address:?} sender requirement as an account requirement"); // 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(TransactionBuilderError::UnfulfillableRequirement(Requirement::Account(_))) => Err( - TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender(address.clone())), - ), - Err(e) => Err(e), - } + self.fulfill_account_requirement(account_address.into_account_id()) + .map_err(|e| match e { + TransactionBuilderError::UnfulfillableRequirement(Requirement::Account(_)) => { + TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender(address.clone())) + } + e => e, + }) } Address::Nft(nft_address) => { log::debug!("Treating {address:?} sender requirement as an nft requirement"); - match self.fulfill_nft_requirement(nft_address.into_nft_id()) { - Ok(res) => Ok(res), - Err(TransactionBuilderError::UnfulfillableRequirement(Requirement::Nft(_))) => Err( - TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender(address.clone())), - ), - Err(e) => Err(e), - } + self.fulfill_nft_requirement(nft_address.into_nft_id()) + .map_err(|e| match e { + TransactionBuilderError::UnfulfillableRequirement(Requirement::Nft(_)) => { + TransactionBuilderError::UnfulfillableRequirement(Requirement::Sender(address.clone())) + } + e => e, + }) } // TODO https://github.com/iotaledger/iota-sdk/issues/1721 Address::Multi(multi_address) => { @@ -75,7 +71,7 @@ impl TransactionBuilder { address.clone(), ))) } else { - Ok(Vec::new()) + Ok(()) } } Address::Restricted(restricted_address) => { From 23631d8cd9866027f96f3c3f6539b54fbf6d502a Mon Sep 17 00:00:00 2001 From: DaughterOfMars Date: Thu, 7 Mar 2024 08:49:44 -0500 Subject: [PATCH 17/21] Fix burn in bindings (#2155) * Fix burn in bindings * doc * order --- bindings/nodejs/lib/types/client/burn.ts | 14 +++++++++++--- bindings/python/iota_sdk/types/burn.py | 24 +++++++++++++++++------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/bindings/nodejs/lib/types/client/burn.ts b/bindings/nodejs/lib/types/client/burn.ts index 8a518e4948..2cc60d5a6a 100644 --- a/bindings/nodejs/lib/types/client/burn.ts +++ b/bindings/nodejs/lib/types/client/burn.ts @@ -2,7 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 import { u256 } from '../utils'; -import { AccountId, FoundryId, NftId, TokenId } from '../block/id'; +import { + AccountId, + DelegationId, + FoundryId, + NftId, + TokenId, +} from '../block/id'; /** A DTO for [`Burn`] */ export interface Burn { @@ -12,10 +18,12 @@ export interface Burn { generatedMana?: boolean; /** Accounts to burn */ accounts?: AccountId[]; - /** NFTs to burn */ - nfts?: NftId[]; /** Foundries to burn */ foundries?: FoundryId[]; + /** NFTs to burn */ + nfts?: NftId[]; + /** Delegations to burn */ + delegations?: DelegationId[]; /** Amounts of native tokens to burn */ nativeTokens?: Map; } diff --git a/bindings/python/iota_sdk/types/burn.py b/bindings/python/iota_sdk/types/burn.py index c58398db15..4b1a6c137f 100644 --- a/bindings/python/iota_sdk/types/burn.py +++ b/bindings/python/iota_sdk/types/burn.py @@ -17,16 +17,18 @@ class Burn: mana: Whether initial excess mana should be burned (only from inputs/outputs that have been specified manually). generated_mana: Whether generated mana should be burned. accounts: The accounts to burn. - nfts: The NFTs to burn. foundries: The foundries to burn. + nfts: The NFTs to burn. + delegations: The delegations to burn. native_tokens: The native tokens to burn. """ mana: Optional[bool] = None generated_mana: Optional[bool] = None accounts: Optional[List[HexStr]] = None - nfts: Optional[List[HexStr]] = None foundries: Optional[List[HexStr]] = None + nfts: Optional[List[HexStr]] = None + delegations: Optional[List[HexStr]] = None native_tokens: Optional[List[NativeToken]] = None def set_mana(self, burn_mana: bool) -> Burn: @@ -49,6 +51,14 @@ def add_account(self, account: HexStr) -> Burn: self.accounts.append(account) return self + def add_foundry(self, foundry: HexStr) -> Burn: + """Add a foundry to the burn. + """ + if self.foundries is None: + self.foundries = [] + self.foundries.append(foundry) + return self + def add_nft(self, nft: HexStr) -> Burn: """Add an NFT to the burn. """ @@ -57,12 +67,12 @@ def add_nft(self, nft: HexStr) -> Burn: self.nfts.append(nft) return self - def add_foundry(self, foundry: HexStr) -> Burn: - """Add a foundry to the burn. + def add_delegation(self, delegation: HexStr) -> Burn: + """Add a delegation to the burn. """ - if self.foundries is None: - self.foundries = [] - self.foundries.append(foundry) + if self.delegations is None: + self.delegations = [] + self.delegations.append(delegation) return self def add_native_token(self, native_token: NativeToken) -> Burn: From b0baecde1c8ece758f9e7fef96f3354bd412d776 Mon Sep 17 00:00:00 2001 From: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> Date: Thu, 7 Mar 2024 15:36:44 +0100 Subject: [PATCH 18/21] Remove redundant address conversion methods (#2151) * Remove Bech32ToHex, HexToBech32, AccountIdToBech32, AnchorIdToBech32, NftIdToBech32 * Fix casing --------- Co-authored-by: Thibault Martinez --- bindings/core/src/method/client.rs | 32 ------- bindings/core/src/method/utils.rs | 30 +----- bindings/core/src/method_handler/client.rs | 12 --- bindings/core/src/method_handler/utils.rs | 12 +-- bindings/core/src/response.rs | 12 --- bindings/core/tests/utils.rs | 10 +- .../nodejs/examples/client/11-build-output.ts | 14 +-- .../client/13-build-account-output.ts | 11 +-- .../examples/client/15-build-nft-output.ts | 11 +-- .../how_tos/account_output/request-funds.ts | 6 +- .../how_tos/account_output/send-amount.ts | 6 +- .../advanced_transaction.ts | 7 +- .../nft_collection/01_mint_collection_nft.ts | 14 ++- .../nodejs/examples/how_tos/nfts/mint_nft.ts | 11 +-- .../examples/how_tos/outputs/features.ts | 11 +-- .../how_tos/outputs/unlock-conditions.ts | 20 ++-- .../how_tos/wallet/consolidate-outputs.ts | 4 +- .../wallet/17-check-unlock-conditions.ts | 14 ++- bindings/nodejs/lib/client/client.ts | 88 ------------------ .../nodejs/lib/types/client/bridge/client.ts | 32 ------- .../nodejs/lib/types/client/bridge/index.ts | 8 -- .../nodejs/lib/types/utils/bridge/index.ts | 12 +-- .../nodejs/lib/types/utils/bridge/utils.ts | 42 +-------- bindings/nodejs/lib/utils/utils.ts | 93 ------------------- .../tests/client/outputBuilders.spec.ts | 12 +-- .../tests/client/utilityMethods.spec.ts | 20 ++-- .../python/examples/client/build_account.py | 10 +- .../python/examples/client/build_basic.py | 14 ++- bindings/python/examples/client/build_nft.py | 9 +- .../how_tos/account_output/request_funds.py | 6 +- .../how_tos/account_output/send_amount.py | 6 +- .../advanced_transaction.py | 5 +- .../nft_collection/01_mint_collection_nft.py | 8 +- .../examples/how_tos/outputs/features.py | 11 +-- .../how_tos/outputs/unlock_conditions.py | 16 ++-- .../how_tos/wallet/consolidate_outputs.py | 4 +- .../wallet/13-check-unlock-conditions.py | 4 +- bindings/python/iota_sdk/client/_utils.py | 38 -------- bindings/python/iota_sdk/utils.py | 46 --------- bindings/wasm/tests/utilityMethods.spec.ts | 14 +-- sdk/src/client/utils.rs | 74 --------------- 41 files changed, 134 insertions(+), 675 deletions(-) diff --git a/bindings/core/src/method/client.rs b/bindings/core/src/method/client.rs index 1959bb429c..dff0781fb1 100644 --- a/bindings/core/src/method/client.rs +++ b/bindings/core/src/method/client.rs @@ -432,41 +432,9 @@ pub enum ClientMethod { ////////////////////////////////////////////////////////////////////// // Utils ////////////////////////////////////////////////////////////////////// - /// Transforms a hex encoded address to a bech32 encoded address - #[serde(rename_all = "camelCase")] - HexToBech32 { - /// Hex encoded bech32 address - hex: String, - /// Human readable part - bech32_hrp: Option, - }, /// Converts an address to its bech32 representation #[serde(rename_all = "camelCase")] AddressToBech32 { address: Address, bech32_hrp: Option }, - /// Transforms an account id to a bech32 encoded address - #[serde(rename_all = "camelCase")] - AccountIdToBech32 { - /// Account ID - account_id: AccountId, - /// Human readable part - bech32_hrp: Option, - }, - /// Transforms an anchor id to a bech32 encoded address - #[serde(rename_all = "camelCase")] - AnchorIdToBech32 { - /// Anchor ID - anchor_id: AnchorId, - /// Human readable part - bech32_hrp: Option, - }, - /// Transforms an nft id to a bech32 encoded address - #[serde(rename_all = "camelCase")] - NftIdToBech32 { - /// Nft ID - nft_id: NftId, - /// Human readable part - bech32_hrp: Option, - }, /// Calculate the minimum required amount for an output. /// Expected response: /// [`Amount`](crate::Response::Amount) diff --git a/bindings/core/src/method/utils.rs b/bindings/core/src/method/utils.rs index a6fd6ef84b..9eacde8567 100644 --- a/bindings/core/src/method/utils.rs +++ b/bindings/core/src/method/utils.rs @@ -8,7 +8,7 @@ use iota_sdk::{ client::secret::types::InputSigningData, types::block::{ address::{Address, Bech32Address, Hrp}, - output::{AccountId, AnchorId, NftId, Output, OutputId, StorageScoreParameters}, + output::{AccountId, Output, OutputId, StorageScoreParameters}, payload::signed_transaction::{ dto::{SignedTransactionPayloadDto, TransactionDto}, TransactionId, @@ -31,40 +31,12 @@ use crate::OmittedDebug; #[serde(tag = "name", content = "data", rename_all = "camelCase")] #[non_exhaustive] pub enum UtilsMethod { - /// Transforms bech32 to hex - Bech32ToHex { - bech32: Bech32Address, - }, - /// Transforms a hex encoded address to a bech32 encoded address - #[serde(rename_all = "camelCase")] - HexToBech32 { - hex: String, - bech32_hrp: Hrp, - }, /// Converts an address to its bech32 representation #[serde(rename_all = "camelCase")] AddressToBech32 { address: Address, bech32_hrp: Hrp, }, - /// Transforms an account id to a bech32 encoded address - #[serde(rename_all = "camelCase")] - AccountIdToBech32 { - account_id: AccountId, - bech32_hrp: Hrp, - }, - /// Transforms an anchor id to a bech32 encoded address - #[serde(rename_all = "camelCase")] - AnchorIdToBech32 { - anchor_id: AnchorId, - bech32_hrp: Hrp, - }, - /// Transforms an nft id to a bech32 encoded address - #[serde(rename_all = "camelCase")] - NftIdToBech32 { - nft_id: NftId, - bech32_hrp: Hrp, - }, /// Returns a valid Address parsed from a String. ParseBech32Address { address: Bech32Address, diff --git a/bindings/core/src/method_handler/client.rs b/bindings/core/src/method_handler/client.rs index 1f220cba00..b63fe2cec0 100644 --- a/bindings/core/src/method_handler/client.rs +++ b/bindings/core/src/method_handler/client.rs @@ -306,21 +306,9 @@ pub(crate) async fn call_client_method_internal( ClientMethod::FindInputs { addresses, amount } => { Response::Inputs(client.find_inputs(addresses, amount).await?) } - ClientMethod::HexToBech32 { hex, bech32_hrp } => { - Response::Bech32Address(client.hex_to_bech32(&hex, bech32_hrp).await?) - } ClientMethod::AddressToBech32 { address, bech32_hrp } => { Response::Bech32Address(client.address_to_bech32(address, bech32_hrp).await?) } - ClientMethod::AccountIdToBech32 { account_id, bech32_hrp } => { - Response::Bech32Address(client.account_id_to_bech32(account_id, bech32_hrp).await?) - } - ClientMethod::AnchorIdToBech32 { anchor_id, bech32_hrp } => { - Response::Bech32Address(client.anchor_id_to_bech32(anchor_id, bech32_hrp).await?) - } - ClientMethod::NftIdToBech32 { nft_id, bech32_hrp } => { - Response::Bech32Address(client.nft_id_to_bech32(nft_id, bech32_hrp).await?) - } ClientMethod::ComputeMinimumOutputAmount { output } => { let storage_score_params = client.get_storage_score_parameters().await?; diff --git a/bindings/core/src/method_handler/utils.rs b/bindings/core/src/method_handler/utils.rs index c966f83cdd..8933f10600 100644 --- a/bindings/core/src/method_handler/utils.rs +++ b/bindings/core/src/method_handler/utils.rs @@ -6,7 +6,7 @@ use crypto::{ keys::bip39::Mnemonic, }; use iota_sdk::{ - client::{hex_to_bech32, verify_mnemonic, Client}, + client::{verify_mnemonic, Client}, types::{ block::{ address::{AccountAddress, Address, ToBech32Ext}, @@ -27,17 +27,7 @@ use crate::{method::UtilsMethod, response::Response}; /// Call a utils method. pub(crate) fn call_utils_method_internal(method: UtilsMethod) -> Result { let response = match method { - UtilsMethod::Bech32ToHex { bech32 } => Response::Bech32ToHex(Client::bech32_to_hex(bech32)?), - UtilsMethod::HexToBech32 { hex, bech32_hrp } => Response::Bech32Address(hex_to_bech32(&hex, bech32_hrp)?), UtilsMethod::AddressToBech32 { address, bech32_hrp } => Response::Bech32Address(address.to_bech32(bech32_hrp)), - UtilsMethod::AccountIdToBech32 { account_id, bech32_hrp } => { - Response::Bech32Address(account_id.to_bech32(bech32_hrp)) - } - UtilsMethod::AnchorIdToBech32 { anchor_id, bech32_hrp } => { - Response::Bech32Address(anchor_id.to_bech32(bech32_hrp)) - } - UtilsMethod::NftIdToBech32 { nft_id, bech32_hrp } => Response::Bech32Address(nft_id.to_bech32(bech32_hrp)), - UtilsMethod::ParseBech32Address { address } => Response::ParsedBech32Address(address.into_inner()), UtilsMethod::IsAddressValid { address } => Response::Bool(Address::is_valid_bech32(&address)), UtilsMethod::GenerateMnemonic => Response::GeneratedMnemonic(Client::generate_mnemonic()?.to_string()), diff --git a/bindings/core/src/response.rs b/bindings/core/src/response.rs index 2b7a8f889c..b53baa6045 100644 --- a/bindings/core/src/response.rs +++ b/bindings/core/src/response.rs @@ -198,9 +198,6 @@ pub enum Response { /// [`OutputIdToUtxoInput`](crate::method::UtilsMethod::OutputIdToUtxoInput) Input(UtxoInput), /// Response for: - /// - [`Bech32ToHex`](crate::method::UtilsMethod::Bech32ToHex) - Bech32ToHex(String), - /// Response for: /// - [`ParseBech32Address`](crate::method::UtilsMethod::ParseBech32Address) ParsedBech32Address(Address), /// Response for: @@ -219,8 +216,6 @@ pub enum Response { /// - [`Blake2b256Hash`](crate::method::UtilsMethod::Blake2b256Hash) /// - [`TransactionSigningHash`](crate::method::UtilsMethod::TransactionSigningHash) Hash(String), - /// Response for [`Bech32ToHex`](crate::method::UtilsMethod::Bech32ToHex) - HexAddress(String), /// Response for [`OutputHexBytes`](crate::method::UtilsMethod::OutputHexBytes) HexBytes(String), /// Response for [`CallPluginRoute`](crate::method::ClientMethod::CallPluginRoute) @@ -240,13 +235,6 @@ pub enum Response { /// Response for: /// - [`AddressToBech32`](crate::method::ClientMethod::AddressToBech32) /// - [`AddressToBech32`](crate::method::UtilsMethod::AddressToBech32) - /// - [`AccountIdToBech32`](crate::method::ClientMethod::AccountIdToBech32) - /// - [`AccountIdToBech32`](crate::method::UtilsMethod::AccountIdToBech32) - /// - [`AnchorIdToBech32`](crate::method::ClientMethod::AnchorIdToBech32) - /// - [`AnchorIdToBech32`](crate::method::UtilsMethod::AnchorIdToBech32) - /// - [`NftIdToBech32`](crate::method::ClientMethod::NftIdToBech32) - /// - [`NftIdToBech32`](crate::method::UtilsMethod::NftIdToBech32) - /// - [`HexToBech32`](crate::method::ClientMethod::HexToBech32) /// - [`ImplicitAccountCreationAddress`](crate::method::WalletMethod::ImplicitAccountCreationAddress) Bech32Address(Bech32Address), /// - [`Faucet`](crate::method::ClientMethod::RequestFundsFromFaucet) diff --git a/bindings/core/tests/utils.rs b/bindings/core/tests/utils.rs index 4c0cf2d96c..358041f6c6 100644 --- a/bindings/core/tests/utils.rs +++ b/bindings/core/tests/utils.rs @@ -15,15 +15,15 @@ async fn utils() -> Result<(), Box> { let bech32_address = Bech32Address::try_from_str("rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy")?; - let method = UtilsMethod::Bech32ToHex { - bech32: bech32_address.clone(), + let method = UtilsMethod::ParseBech32Address { + address: bech32_address.clone(), }; let response = call_utils_method(method); match response { - Response::Bech32ToHex(hex) => { - match call_utils_method(UtilsMethod::HexToBech32 { - hex, + Response::ParsedBech32Address(address) => { + match call_utils_method(UtilsMethod::AddressToBech32 { + address, bech32_hrp: Hrp::from_str_unchecked("rms"), }) { Response::Bech32Address(address_bech32) => { diff --git a/bindings/nodejs/examples/client/11-build-output.ts b/bindings/nodejs/examples/client/11-build-output.ts index 187d4dc64b..4af2342088 100644 --- a/bindings/nodejs/examples/client/11-build-output.ts +++ b/bindings/nodejs/examples/client/11-build-output.ts @@ -11,7 +11,6 @@ import { SenderFeature, TagFeature, StorageDepositReturnUnlockCondition, - Ed25519Address, ExpirationUnlockCondition, TimelockUnlockCondition, utf8ToHex, @@ -36,12 +35,12 @@ async function run() { }); try { - const hexAddress = Utils.bech32ToHex( + const ed25519Address = Utils.parseBech32Address( 'rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy', ); const addressUnlockCondition: UnlockCondition = - new AddressUnlockCondition(new Ed25519Address(hexAddress)); + new AddressUnlockCondition(ed25519Address); // Build most basic output with amount and a single address unlock condition const basicOutput = await client.buildBasicOutput({ @@ -68,7 +67,7 @@ async function run() { unlockConditions: [ addressUnlockCondition, new StorageDepositReturnUnlockCondition( - new Ed25519Address(hexAddress), + ed25519Address, '1000000', ), ], @@ -81,10 +80,7 @@ async function run() { amount: BigInt(1000000), unlockConditions: [ addressUnlockCondition, - new ExpirationUnlockCondition( - new Ed25519Address(hexAddress), - 1, - ), + new ExpirationUnlockCondition(ed25519Address, 1), ], }); @@ -114,7 +110,7 @@ async function run() { const basicOutputWithSender = await client.buildBasicOutput({ amount: BigInt(1000000), unlockConditions: [addressUnlockCondition], - features: [new SenderFeature(new Ed25519Address(hexAddress))], + features: [new SenderFeature(ed25519Address)], }); console.log(JSON.stringify(basicOutputWithSender, null, 2)); diff --git a/bindings/nodejs/examples/client/13-build-account-output.ts b/bindings/nodejs/examples/client/13-build-account-output.ts index 0b37a76302..9d3772329a 100644 --- a/bindings/nodejs/examples/client/13-build-account-output.ts +++ b/bindings/nodejs/examples/client/13-build-account-output.ts @@ -7,7 +7,6 @@ import { Utils, MetadataFeature, SenderFeature, - Ed25519Address, IssuerFeature, AddressUnlockCondition, utf8ToHex, @@ -32,22 +31,20 @@ async function run() { }); try { - const hexAddress = Utils.bech32ToHex( + const ed25519Address = Utils.parseBech32Address( 'rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy', ); const accountOutput = await client.buildAccountOutput({ accountId: '0x0000000000000000000000000000000000000000000000000000000000000000', - unlockConditions: [ - new AddressUnlockCondition(new Ed25519Address(hexAddress)), - ], + unlockConditions: [new AddressUnlockCondition(ed25519Address)], features: [ - new SenderFeature(new Ed25519Address(hexAddress)), + new SenderFeature(ed25519Address), new MetadataFeature({ data: utf8ToHex('hello') }), ], immutableFeatures: [ - new IssuerFeature(new Ed25519Address(hexAddress)), + new IssuerFeature(ed25519Address), new MetadataFeature({ data: utf8ToHex('hello') }), ], }); diff --git a/bindings/nodejs/examples/client/15-build-nft-output.ts b/bindings/nodejs/examples/client/15-build-nft-output.ts index 494695a5d7..c91fba145e 100644 --- a/bindings/nodejs/examples/client/15-build-nft-output.ts +++ b/bindings/nodejs/examples/client/15-build-nft-output.ts @@ -10,7 +10,6 @@ import { TagFeature, MetadataFeature, SenderFeature, - Ed25519Address, IssuerFeature, Irc27Metadata, } from '@iota/sdk'; @@ -34,7 +33,7 @@ async function run() { }); try { - const hexAddress = Utils.bech32ToHex( + const ed25519Address = Utils.parseBech32Address( 'rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy', ); @@ -47,15 +46,13 @@ async function run() { const nftOutput = await client.buildNftOutput({ // NftId needs to be null the first time nftId: '0x0000000000000000000000000000000000000000000000000000000000000000', - unlockConditions: [ - new AddressUnlockCondition(new Ed25519Address(hexAddress)), - ], + unlockConditions: [new AddressUnlockCondition(ed25519Address)], immutableFeatures: [ - new IssuerFeature(new Ed25519Address(hexAddress)), + new IssuerFeature(ed25519Address), tip27ImmutableMetadata.asFeature(), ], features: [ - new SenderFeature(new Ed25519Address(hexAddress)), + new SenderFeature(ed25519Address), new MetadataFeature({ data: utf8ToHex('mutable metadata'), }), diff --git a/bindings/nodejs/examples/how_tos/account_output/request-funds.ts b/bindings/nodejs/examples/how_tos/account_output/request-funds.ts index 5810703e76..ee8fd0aa22 100644 --- a/bindings/nodejs/examples/how_tos/account_output/request-funds.ts +++ b/bindings/nodejs/examples/how_tos/account_output/request-funds.ts @@ -1,7 +1,7 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { Utils, Wallet, initLogger } from '@iota/sdk'; +import { Utils, Wallet, initLogger, AccountAddress } from '@iota/sdk'; // This example uses secrets in environment variables for simplicity which should not be done in production. // @@ -41,8 +41,8 @@ async function run() { const client = await wallet.getClient(); // Get Account address - const accountAddress = Utils.accountIdToBech32( - accountId, + const accountAddress = Utils.addressToBech32( + new AccountAddress(accountId), await client.getBech32Hrp(), ); diff --git a/bindings/nodejs/examples/how_tos/account_output/send-amount.ts b/bindings/nodejs/examples/how_tos/account_output/send-amount.ts index bf046dc034..0c18455776 100644 --- a/bindings/nodejs/examples/how_tos/account_output/send-amount.ts +++ b/bindings/nodejs/examples/how_tos/account_output/send-amount.ts @@ -1,7 +1,7 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { Wallet, initLogger, Utils } from '@iota/sdk'; +import { Wallet, initLogger, Utils, AccountAddress } from '@iota/sdk'; // This example uses secrets in environment variables for simplicity which should not be done in production. // @@ -52,8 +52,8 @@ async function run() { const client = await wallet.getClient(); // Get Account address - const accountAddress = Utils.accountIdToBech32( - accountId, + const accountAddress = Utils.addressToBech32( + new AccountAddress(accountId), await client.getBech32Hrp(), ); diff --git a/bindings/nodejs/examples/how_tos/advanced_transactions/advanced_transaction.ts b/bindings/nodejs/examples/how_tos/advanced_transactions/advanced_transaction.ts index 982043f1a7..8d6f216af4 100644 --- a/bindings/nodejs/examples/how_tos/advanced_transactions/advanced_transaction.ts +++ b/bindings/nodejs/examples/how_tos/advanced_transactions/advanced_transaction.ts @@ -3,7 +3,6 @@ import { AddressUnlockCondition, - Ed25519Address, TimelockUnlockCondition, Utils, Wallet, @@ -49,10 +48,8 @@ async function run() { const basicOutput = await client.buildBasicOutput({ unlockConditions: [ new AddressUnlockCondition( - new Ed25519Address( - Utils.bech32ToHex( - 'rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy', - ), + Utils.parseBech32Address( + 'rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy', ), ), new TimelockUnlockCondition(slotIndex), diff --git a/bindings/nodejs/examples/how_tos/nft_collection/01_mint_collection_nft.ts b/bindings/nodejs/examples/how_tos/nft_collection/01_mint_collection_nft.ts index b79dfefa58..ce79c8447a 100644 --- a/bindings/nodejs/examples/how_tos/nft_collection/01_mint_collection_nft.ts +++ b/bindings/nodejs/examples/how_tos/nft_collection/01_mint_collection_nft.ts @@ -1,7 +1,14 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { MintNftParams, NftId, Utils, Wallet, Irc27Metadata } from '@iota/sdk'; +import { + MintNftParams, + NftId, + Utils, + Wallet, + Irc27Metadata, + NftAddress, +} from '@iota/sdk'; require('dotenv').config({ path: '.env' }); // The NFT collection size @@ -49,7 +56,10 @@ async function run() { const client = await wallet.getClient(); const bech32Hrp = await client.getBech32Hrp(); - const issuer = Utils.nftIdToBech32(issuerNftId, bech32Hrp); + const issuer = Utils.addressToBech32( + new NftAddress(issuerNftId), + bech32Hrp, + ); const nftMintParams = []; // Create the metadata with another index for each diff --git a/bindings/nodejs/examples/how_tos/nfts/mint_nft.ts b/bindings/nodejs/examples/how_tos/nfts/mint_nft.ts index 83c23c4d56..aa2ca76030 100644 --- a/bindings/nodejs/examples/how_tos/nfts/mint_nft.ts +++ b/bindings/nodejs/examples/how_tos/nfts/mint_nft.ts @@ -3,7 +3,6 @@ import { AddressUnlockCondition, - Ed25519Address, IssuerFeature, MintNftParams, SenderFeature, @@ -83,19 +82,17 @@ async function run() { // Build an NFT manually by using the `NftOutputBuilder` const client = await wallet.getClient(); - const hexAddress = Utils.bech32ToHex(senderAddress); + const ed25519Address = Utils.parseBech32Address(senderAddress); const output = await client.buildNftOutput({ amount: NFT2_AMOUNT, nftId: '0x0000000000000000000000000000000000000000000000000000000000000000', unlockConditions: [ new AddressUnlockCondition( - new Ed25519Address(Utils.bech32ToHex(NFT1_OWNER_ADDRESS)), + Utils.parseBech32Address(NFT1_OWNER_ADDRESS), ), ], - immutableFeatures: [ - new IssuerFeature(new Ed25519Address(hexAddress)), - ], - features: [new SenderFeature(new Ed25519Address(hexAddress))], + immutableFeatures: [new IssuerFeature(ed25519Address)], + features: [new SenderFeature(ed25519Address)], }); transaction = await wallet.sendOutputs([output]); diff --git a/bindings/nodejs/examples/how_tos/outputs/features.ts b/bindings/nodejs/examples/how_tos/outputs/features.ts index 996261c13b..6dcbc60e74 100644 --- a/bindings/nodejs/examples/how_tos/outputs/features.ts +++ b/bindings/nodejs/examples/how_tos/outputs/features.ts @@ -10,7 +10,6 @@ import { MetadataFeature, SenderFeature, TagFeature, - Ed25519Address, IssuerFeature, utf8ToHex, } from '@iota/sdk'; @@ -26,27 +25,25 @@ async function run() { const client = await Client.create({}); try { - const hexAddress = Utils.bech32ToHex( + const ed25519Address = Utils.parseBech32Address( 'rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy', ); const addressUnlockCondition: UnlockCondition = - new AddressUnlockCondition(new Ed25519Address(hexAddress)); + new AddressUnlockCondition(ed25519Address); // Output with sender feature const nftOutputWithSender = await client.buildNftOutput({ nftId: '0x0000000000000000000000000000000000000000000000000000000000000000', unlockConditions: [addressUnlockCondition], - features: [new SenderFeature(new Ed25519Address(hexAddress))], + features: [new SenderFeature(ed25519Address)], }); // Output with issuer feature const nftOutputWithIssuer = await client.buildNftOutput({ nftId: '0x0000000000000000000000000000000000000000000000000000000000000000', unlockConditions: [addressUnlockCondition], - immutableFeatures: [ - new IssuerFeature(new Ed25519Address(hexAddress)), - ], + immutableFeatures: [new IssuerFeature(ed25519Address)], }); // Output with metadata feature diff --git a/bindings/nodejs/examples/how_tos/outputs/unlock-conditions.ts b/bindings/nodejs/examples/how_tos/outputs/unlock-conditions.ts index 4be6b6fd61..83e38a4c28 100644 --- a/bindings/nodejs/examples/how_tos/outputs/unlock-conditions.ts +++ b/bindings/nodejs/examples/how_tos/outputs/unlock-conditions.ts @@ -8,7 +8,6 @@ import { UnlockCondition, AddressUnlockCondition, StorageDepositReturnUnlockCondition, - Ed25519Address, ExpirationUnlockCondition, TimelockUnlockCondition, SimpleTokenScheme, @@ -27,13 +26,13 @@ async function run() { const client = await Client.create({}); try { - const hexAddress = Utils.bech32ToHex( + const ed25519Address = Utils.parseBech32Address( 'rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy', ); - const accountHexAddress = Utils.bech32ToHex( + const accountAddress = Utils.parseBech32Address( 'rms1pr59qm43mjtvhcajfmupqf23x29llam88yecn6pyul80rx099krmv2fnnux', - ); + ) as AccountAddress; const tokenSchema = new SimpleTokenScheme( BigInt(50), @@ -42,7 +41,7 @@ async function run() { ); const addressUnlockCondition: UnlockCondition = - new AddressUnlockCondition(new Ed25519Address(hexAddress)); + new AddressUnlockCondition(ed25519Address); // Most simple output const basicOutput = await client.buildBasicOutput({ @@ -54,7 +53,7 @@ async function run() { unlockConditions: [ addressUnlockCondition, new StorageDepositReturnUnlockCondition( - new Ed25519Address(hexAddress), + ed25519Address, '1000000', ), ], @@ -72,10 +71,7 @@ async function run() { const basicOutputWithExpiration = await client.buildBasicOutput({ unlockConditions: [ addressUnlockCondition, - new ExpirationUnlockCondition( - new Ed25519Address(hexAddress), - 1, - ), + new ExpirationUnlockCondition(ed25519Address, 1), ], }); @@ -84,9 +80,7 @@ async function run() { serialNumber: 1, tokenScheme: tokenSchema, unlockConditions: [ - new ImmutableAccountAddressUnlockCondition( - new AccountAddress(accountHexAddress), - ), + new ImmutableAccountAddressUnlockCondition(accountAddress), ], }); diff --git a/bindings/nodejs/examples/how_tos/wallet/consolidate-outputs.ts b/bindings/nodejs/examples/how_tos/wallet/consolidate-outputs.ts index a3e0bd95e8..9e28f71cb4 100644 --- a/bindings/nodejs/examples/how_tos/wallet/consolidate-outputs.ts +++ b/bindings/nodejs/examples/how_tos/wallet/consolidate-outputs.ts @@ -50,7 +50,7 @@ async function run() { console.log(`OUTPUT #${i}`); console.log( '- address: %s\n- amount: %d\n- native token: %s', - Utils.hexToBech32(address.toString(), 'rms'), + Utils.addressToBech32(address, 'rms'), output.getAmount(), output instanceof CommonOutput ? (output as CommonOutput).getNativeToken() ?? [] @@ -84,7 +84,7 @@ async function run() { console.log(`OUTPUT #${i}`); console.log( '- address: %s\n- amount: %d\n- native tokens: %s', - Utils.hexToBech32(address.toString(), 'rms'), + Utils.addressToBech32(address, 'rms'), output.getAmount(), output instanceof CommonOutput ? (output as CommonOutput).getNativeToken() diff --git a/bindings/nodejs/examples/wallet/17-check-unlock-conditions.ts b/bindings/nodejs/examples/wallet/17-check-unlock-conditions.ts index 480dbf7fa9..abd9c5562a 100644 --- a/bindings/nodejs/examples/wallet/17-check-unlock-conditions.ts +++ b/bindings/nodejs/examples/wallet/17-check-unlock-conditions.ts @@ -1,7 +1,13 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { AddressUnlockCondition, BasicOutput, Output, Utils } from '@iota/sdk'; +import { + AddressUnlockCondition, + BasicOutput, + Ed25519Address, + Output, + Utils, +} from '@iota/sdk'; import { getUnlockedWallet } from './common'; @@ -27,7 +33,9 @@ async function run() { amount: AMOUNT, }); - const hexEncodedwalletAddress = Utils.bech32ToHex(walletAddress); + const hexEncodedwalletAddress = Utils.parseBech32Address( + walletAddress, + ) as Ed25519Address; if (output instanceof BasicOutput) { const basicOutput = output as BasicOutput; @@ -36,7 +44,7 @@ async function run() { basicOutput.unlockConditions.length === 1 && basicOutput.unlockConditions[0] instanceof AddressUnlockCondition && - hexEncodedwalletAddress.includes( + hexEncodedwalletAddress.pubKeyHash.includes( ( basicOutput .unlockConditions[0] as AddressUnlockCondition diff --git a/bindings/nodejs/lib/client/client.ts b/bindings/nodejs/lib/client/client.ts index e93a7a1b92..ed3c1e43c6 100644 --- a/bindings/nodejs/lib/client/client.ts +++ b/bindings/nodejs/lib/client/client.ts @@ -836,28 +836,6 @@ export class Client { return JSON.parse(response).payload; } - /** - * Convert a hex encoded address to a Bech32 encoded address. - * - * @param hex The hexadecimal string representation of an address. - * @param bech32Hrp The Bech32 HRP (human readable part) to be used. - * @returns The corresponding Bech32 address. - */ - async hexToBech32( - hex: HexEncodedString, - bech32Hrp?: string, - ): Promise { - const response = await this.methodHandler.callMethod({ - name: 'hexToBech32', - data: { - hex, - bech32Hrp, - }, - }); - - return JSON.parse(response).payload; - } - /** * Converts an address to its bech32 representation * @@ -880,72 +858,6 @@ export class Client { return JSON.parse(response).payload; } - /** - * Transforms an account id to a bech32 encoded address. - * - * @param accountId An account ID. - * @param bech32Hrp The Bech32 HRP (human readable part) to be used. - * @returns The corresponding Bech32 address. - */ - async accountIdToBech32( - accountId: AccountId, - bech32Hrp?: string, - ): Promise { - const response = await this.methodHandler.callMethod({ - name: 'accountIdToBech32', - data: { - accountId, - bech32Hrp, - }, - }); - - return JSON.parse(response).payload; - } - - /** - * Transforms an anchor id to a bech32 encoded address. - * - * @param anchorId An anchor ID. - * @param bech32Hrp The Bech32 HRP (human readable part) to be used. - * @returns The corresponding Bech32 address. - */ - async anchorIdToBech32( - anchorId: AnchorId, - bech32Hrp?: string, - ): Promise { - const response = await this.methodHandler.callMethod({ - name: 'anchorIdToBech32', - data: { - anchorId, - bech32Hrp, - }, - }); - - return JSON.parse(response).payload; - } - - /** - * Convert an NFT ID to a Bech32 encoded address. - * - * @param nftId An NFT ID. - * @param bech32Hrp The Bech32 HRP (human readable part) to be used. - * @returns The corresponding Bech32 address. - */ - async nftIdToBech32( - nftId: NftId, - bech32Hrp?: string, - ): Promise { - const response = await this.methodHandler.callMethod({ - name: 'nftIdToBech32', - data: { - nftId, - bech32Hrp, - }, - }); - - return JSON.parse(response).payload; - } - /** * Return the unhealthy nodes. */ diff --git a/bindings/nodejs/lib/types/client/bridge/client.ts b/bindings/nodejs/lib/types/client/bridge/client.ts index 4a01110729..6d1cc5f7c1 100644 --- a/bindings/nodejs/lib/types/client/bridge/client.ts +++ b/bindings/nodejs/lib/types/client/bridge/client.ts @@ -317,14 +317,6 @@ export interface __GetProtocolParametersMethod__ { name: 'getProtocolParameters'; } -export interface __HexToBech32Method__ { - name: 'hexToBech32'; - data: { - hex: HexEncodedString; - bech32Hrp?: string; - }; -} - export interface __AddressToBech32Method__ { name: 'addressToBech32'; data: { @@ -333,30 +325,6 @@ export interface __AddressToBech32Method__ { }; } -export interface __AccountIdToBech32Method__ { - name: 'accountIdToBech32'; - data: { - accountId: AccountId; - bech32Hrp?: string; - }; -} - -export interface __AnchorIdToBech32Method__ { - name: 'anchorIdToBech32'; - data: { - anchorId: AnchorId; - bech32Hrp?: string; - }; -} - -export interface __NftIdToBech32Method__ { - name: 'nftIdToBech32'; - data: { - nftId: NftId; - bech32Hrp?: string; - }; -} - export interface __FindBlocksMethod__ { name: 'findBlocks'; data: { diff --git a/bindings/nodejs/lib/types/client/bridge/index.ts b/bindings/nodejs/lib/types/client/bridge/index.ts index 72d414c2d3..2bfb84fc57 100644 --- a/bindings/nodejs/lib/types/client/bridge/index.ts +++ b/bindings/nodejs/lib/types/client/bridge/index.ts @@ -44,11 +44,7 @@ import type { __GetCommitmentBySlotRawMethod__, __GetUtxoChangesBySlotMethod__, __GetUtxoChangesFullBySlotMethod__, - __HexToBech32Method__, __AddressToBech32Method__, - __AccountIdToBech32Method__, - __AnchorIdToBech32Method__, - __NftIdToBech32Method__, __AccountOutputIdsMethod__, __AccountOutputIdMethod__, __AnchorOutputIdsMethod__, @@ -115,11 +111,7 @@ export type __ClientMethods__ = | __GetCommitmentBySlotRawMethod__ | __GetUtxoChangesBySlotMethod__ | __GetUtxoChangesFullBySlotMethod__ - | __HexToBech32Method__ | __AddressToBech32Method__ - | __AccountIdToBech32Method__ - | __AnchorIdToBech32Method__ - | __NftIdToBech32Method__ | __AccountOutputIdsMethod__ | __AccountOutputIdMethod__ | __AnchorOutputIdsMethod__ diff --git a/bindings/nodejs/lib/types/utils/bridge/index.ts b/bindings/nodejs/lib/types/utils/bridge/index.ts index 405f4bf2b9..22de248ba6 100644 --- a/bindings/nodejs/lib/types/utils/bridge/index.ts +++ b/bindings/nodejs/lib/types/utils/bridge/index.ts @@ -5,16 +5,11 @@ import type { __ComputeTokenIdMethod__, __ComputeFoundryIdMethod__, __ComputeMinimumOutputAmountMethod__, - __ParseBech32AddressMethod__, __Blake2b256HashMethod__, __BlockIdMethod__, __TransactionIdMethod__, - __Bech32ToHexMethod__, - __HexToBech32Method__, __AddressToBech32Method__, - __AccountIdToBech32Method__, - __AnchorIdToBech32Method__, - __NftIdToBech32Method__, + __ParseBech32AddressMethod__, __IsAddressValidMethod__, __ProtocolParametersHashMethod__, __TransactionSigningHashMethod__, @@ -46,12 +41,7 @@ export type __UtilsMethods__ = | __Blake2b256HashMethod__ | __BlockIdMethod__ | __TransactionIdMethod__ - | __Bech32ToHexMethod__ - | __HexToBech32Method__ | __AddressToBech32Method__ - | __AccountIdToBech32Method__ - | __AnchorIdToBech32Method__ - | __NftIdToBech32Method__ | __IsAddressValidMethod__ | __ProtocolParametersHashMethod__ | __TransactionSigningHashMethod__ diff --git a/bindings/nodejs/lib/types/utils/bridge/utils.ts b/bindings/nodejs/lib/types/utils/bridge/utils.ts index 8b6952439c..b27576946e 100644 --- a/bindings/nodejs/lib/types/utils/bridge/utils.ts +++ b/bindings/nodejs/lib/types/utils/bridge/utils.ts @@ -10,12 +10,11 @@ import { Block, ProtocolParameters, OutputId, - NftId, Bech32Address, Unlock, Address, } from '../../'; -import { AccountId, AnchorId } from '../../block/id'; +import { AccountId } from '../../block/id'; import { SlotCommitment } from '../../block/slot'; import { InputSigningData } from '../../client'; import { NumericString } from '../numeric'; @@ -94,21 +93,6 @@ export interface __TransactionIdMethod__ { }; } -export interface __Bech32ToHexMethod__ { - name: 'bech32ToHex'; - data: { - bech32: Bech32Address; - }; -} - -export interface __HexToBech32Method__ { - name: 'hexToBech32'; - data: { - hex: HexEncodedString; - bech32Hrp: string; - }; -} - export interface __AddressToBech32Method__ { name: 'addressToBech32'; data: { @@ -117,30 +101,6 @@ export interface __AddressToBech32Method__ { }; } -export interface __AccountIdToBech32Method__ { - name: 'accountIdToBech32'; - data: { - accountId: AccountId; - bech32Hrp: string; - }; -} - -export interface __AnchorIdToBech32Method__ { - name: 'anchorIdToBech32'; - data: { - anchorId: AnchorId; - bech32Hrp: string; - }; -} - -export interface __NftIdToBech32Method__ { - name: 'nftIdToBech32'; - data: { - nftId: NftId; - bech32Hrp: string; - }; -} - export interface __IsAddressValidMethod__ { name: 'isAddressValid'; data: { diff --git a/bindings/nodejs/lib/utils/utils.ts b/bindings/nodejs/lib/utils/utils.ts index d0f81b51c1..073be7a5bb 100644 --- a/bindings/nodejs/lib/utils/utils.ts +++ b/bindings/nodejs/lib/utils/utils.ts @@ -25,7 +25,6 @@ import { } from '../types'; import { AccountId, - AnchorId, BlockId, FoundryId, NftId, @@ -260,41 +259,6 @@ export class Utils { }); } - /** - * Convert a Bech32 address to a hex-encoded string. - * - * @param bech32 A Bech32 address. - * @returns The hex-encoded string. - */ - static bech32ToHex(bech32: Bech32Address): HexEncodedString { - return callUtilsMethod({ - name: 'bech32ToHex', - data: { - bech32, - }, - }); - } - - /** - * Convert a hex-encoded address string to a Bech32-encoded address string. - * - * @param hex A hex-encoded address string. - * @param bech32Hrp The Bech32 HRP (human readable part) to use. - * @returns The Bech32-encoded address string. - */ - static hexToBech32( - hex: HexEncodedString, - bech32Hrp: string, - ): Bech32Address { - return callUtilsMethod({ - name: 'hexToBech32', - data: { - hex, - bech32Hrp, - }, - }); - } - /** * Converts an address to its bech32 representation. * @@ -312,63 +276,6 @@ export class Utils { }); } - /** - * Transforms an account id to a bech32 encoded address. - * - * @param accountId An account ID. - * @param bech32Hrp The Bech32 HRP (human readable part) to use. - * @returns The Bech32-encoded address string. - */ - static accountIdToBech32( - accountId: AccountId, - bech32Hrp: string, - ): Bech32Address { - return callUtilsMethod({ - name: 'accountIdToBech32', - data: { - accountId, - bech32Hrp, - }, - }); - } - - /** - * Transforms an anchor id to a bech32 encoded address. - * - * @param anchorId An anchor ID. - * @param bech32Hrp The Bech32 HRP (human readable part) to use. - * @returns The Bech32-encoded address string. - */ - static anchorIdToBech32( - anchorId: AnchorId, - bech32Hrp: string, - ): Bech32Address { - return callUtilsMethod({ - name: 'anchorIdToBech32', - data: { - anchorId, - bech32Hrp, - }, - }); - } - - /** - * Convert an NFT ID to a Bech32-encoded address string. - * - * @param nftId An NFT ID. - * @param bech32Hrp The Bech32 HRP (human readable part) to use. - * @returns The Bech32-encoded address string. - */ - static nftIdToBech32(nftId: NftId, bech32Hrp: string): Bech32Address { - return callUtilsMethod({ - name: 'nftIdToBech32', - data: { - nftId, - bech32Hrp, - }, - }); - } - /** * Hashes a hex encoded public key with Blake2b256. * diff --git a/bindings/nodejs/tests/client/outputBuilders.spec.ts b/bindings/nodejs/tests/client/outputBuilders.spec.ts index 074b69700d..b5b1452933 100644 --- a/bindings/nodejs/tests/client/outputBuilders.spec.ts +++ b/bindings/nodejs/tests/client/outputBuilders.spec.ts @@ -33,7 +33,7 @@ describe.skip('Output builder methods', () => { }, }); - const hexAddress = Utils.bech32ToHex(addresses[0]); + const address = Utils.parseBech32Address(addresses[0]); const client = await makeClient(); // most simple basic output @@ -41,7 +41,7 @@ describe.skip('Output builder methods', () => { amount: BigInt(1000000), unlockConditions: [ new AddressUnlockCondition( - new Ed25519Address(hexAddress), + address, ), ], }); @@ -57,7 +57,7 @@ describe.skip('Output builder methods', () => { }, }); - const hexAddress = Utils.bech32ToHex(addresses[0]); + const address = Utils.parseBech32Address(addresses[0]); const client = await makeClient(); const accountId = @@ -67,7 +67,7 @@ describe.skip('Output builder methods', () => { accountId, unlockConditions: [ new AddressUnlockCondition( - new Ed25519Address(hexAddress), + address, ), ], }); @@ -110,14 +110,14 @@ describe.skip('Output builder methods', () => { }, }); - const hexAddress = Utils.bech32ToHex(addresses[0]); + const address = Utils.parseBech32Address(addresses[0]); // most simple nft output const nftOutput = await client.buildNftOutput({ nftId: '0x7ffec9e1233204d9c6dce6812b1539ee96af691ca2e4d9065daa85907d33e5d3', unlockConditions: [ new AddressUnlockCondition( - new Ed25519Address(hexAddress), + address ), ], }); diff --git a/bindings/nodejs/tests/client/utilityMethods.spec.ts b/bindings/nodejs/tests/client/utilityMethods.spec.ts index c1bc80b825..f238292604 100644 --- a/bindings/nodejs/tests/client/utilityMethods.spec.ts +++ b/bindings/nodejs/tests/client/utilityMethods.spec.ts @@ -7,7 +7,7 @@ import 'dotenv/config'; import { Client, SecretManager, Utils } from '../../'; import '../customMatchers'; -import { SlotCommitment } from '../../out/types/block/slot'; +import { AccountAddress, Ed25519Address } from '../../lib'; async function makeOfflineClient(): Promise { return await Client.create({}); @@ -30,19 +30,19 @@ describe('Client utility methods', () => { }); it('converts address to hex and bech32', async () => { - const address = + const bech32address = 'rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy'; - const hexAddress = Utils.bech32ToHex(address); + const address = Utils.parseBech32Address(bech32address) as Ed25519Address; - expect(hexAddress.slice(0, 2)).toBe('0x'); + expect(address.pubKeyHash.slice(0, 2)).toBe('0x'); let offlineClient = await makeOfflineClient(); - const bech32Address = await offlineClient.hexToBech32( - hexAddress, + const convertedBech32Address = await offlineClient.addressToBech32( + address, 'rms', ); - expect(bech32Address).toBe(address); + expect(convertedBech32Address).toBe(bech32address); }); it('account id to address', async () => { @@ -50,12 +50,12 @@ describe('Client utility methods', () => { '0xcf077d276686ba64c0404b9eb2d15556782113c5a1985f262b70f9964d3bbd7f'; const offlineClient = await makeOfflineClient(); - const accountAddress = await offlineClient.accountIdToBech32( - accountId, + const bech32AccountAddress = await offlineClient.addressToBech32( + new AccountAddress(accountId), 'rms', ); - expect(accountAddress).toBe( + expect(bech32AccountAddress).toBe( 'rms1pr8swlf8v6rt5exqgp9eavk324t8sggnckseshex9dc0n9jd8w7h7wcnhn7', ); }); diff --git a/bindings/python/examples/client/build_account.py b/bindings/python/examples/client/build_account.py index adaf36a53b..bc7369c233 100644 --- a/bindings/python/examples/client/build_account.py +++ b/bindings/python/examples/client/build_account.py @@ -2,7 +2,7 @@ import os from dotenv import load_dotenv -from iota_sdk import (Client, Ed25519Address, AddressUnlockCondition, +from iota_sdk import (Client, AddressUnlockCondition, IssuerFeature, MetadataFeature, SenderFeature, Utils, utf8_to_hex) @@ -14,19 +14,19 @@ # Create a Client instance client = Client(nodes=[node_url]) -hexAddress = Utils.bech32_to_hex( +address = Utils.parse_bech32_address( 'rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy') account_id = '0x0000000000000000000000000000000000000000000000000000000000000000' unlock_conditions = [ - AddressUnlockCondition(Ed25519Address(hexAddress)), + AddressUnlockCondition(address), ] features = [ - SenderFeature(Ed25519Address(hexAddress)), + SenderFeature(address), MetadataFeature({'data': utf8_to_hex('Hello, World!')}) ] immutable_features = [ - IssuerFeature(Ed25519Address(hexAddress)), + IssuerFeature(address), MetadataFeature({'data': utf8_to_hex('Hello, World!')}) ] diff --git a/bindings/python/examples/client/build_basic.py b/bindings/python/examples/client/build_basic.py index 76bd61e884..00839eab31 100644 --- a/bindings/python/examples/client/build_basic.py +++ b/bindings/python/examples/client/build_basic.py @@ -2,7 +2,7 @@ import os from dotenv import load_dotenv -from iota_sdk import (AddressUnlockCondition, Client, Ed25519Address, +from iota_sdk import (AddressUnlockCondition, Client, ExpirationUnlockCondition, MetadataFeature, SenderFeature, StorageDepositReturnUnlockCondition, TagFeature, TimelockUnlockCondition, Utils, utf8_to_hex) @@ -14,12 +14,10 @@ # Create a Client instance client = Client(nodes=[node_url]) -hex_address = Utils.bech32_to_hex( +address = Utils.parse_bech32_address( 'rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy') -address_unlock_condition = AddressUnlockCondition( - Ed25519Address(hex_address) -) +address_unlock_condition = AddressUnlockCondition(address) # Build most basic output with amount and a single address unlock condition basic_output = client.build_basic_output( @@ -47,7 +45,7 @@ unlock_conditions=[ address_unlock_condition, StorageDepositReturnUnlockCondition( - return_address=Ed25519Address(hex_address), + return_address=address, amount=1000000 ) ], @@ -60,7 +58,7 @@ unlock_conditions=[ address_unlock_condition, ExpirationUnlockCondition( - return_address=Ed25519Address(hex_address), + return_address=address, slot_index=1 ) ], @@ -96,7 +94,7 @@ address_unlock_condition ], features=[ - SenderFeature(Ed25519Address(hex_address)) + SenderFeature(address) ], amount=1000000, ) diff --git a/bindings/python/examples/client/build_nft.py b/bindings/python/examples/client/build_nft.py index 8100a7feca..85c95677e3 100644 --- a/bindings/python/examples/client/build_nft.py +++ b/bindings/python/examples/client/build_nft.py @@ -5,7 +5,6 @@ from iota_sdk import ( AddressUnlockCondition, Client, - Ed25519Address, IssuerFeature, MetadataFeature, SenderFeature, @@ -22,7 +21,7 @@ # Create a Client instance client = Client(nodes=[node_url]) -hexAddress = Utils.bech32_to_hex( +address = Utils.parse_bech32_address( 'rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy') tip_27_immutable_metadata = Irc27Metadata( @@ -34,16 +33,16 @@ # Build NFT output nft_output = client.build_nft_output( unlock_conditions=[ - AddressUnlockCondition(Ed25519Address(hexAddress)) + AddressUnlockCondition(address) ], # NftId needs to be null the first time nft_id='0x0000000000000000000000000000000000000000000000000000000000000000', immutable_features=[ - IssuerFeature(Ed25519Address(hexAddress)), + IssuerFeature(address), tip_27_immutable_metadata.as_feature() ], features=[ - SenderFeature(Ed25519Address(hexAddress)), + SenderFeature(address), MetadataFeature(utf8_to_hex('mutable metadata')), TagFeature(utf8_to_hex('my tag')) ] diff --git a/bindings/python/examples/how_tos/account_output/request_funds.py b/bindings/python/examples/how_tos/account_output/request_funds.py index dd414bddd0..63edd41911 100644 --- a/bindings/python/examples/how_tos/account_output/request_funds.py +++ b/bindings/python/examples/how_tos/account_output/request_funds.py @@ -2,7 +2,7 @@ import time from dotenv import load_dotenv -from iota_sdk import Wallet, WalletOptions, Utils, SyncOptions, WalletSyncOptions +from iota_sdk import AccountAddress, Wallet, WalletOptions, Utils, SyncOptions, WalletSyncOptions # In this example we request funds to the wallet's first account output # address. @@ -27,8 +27,8 @@ print(f'Account Id: {account_id}') # Get Account address -account_address = Utils.account_id_to_bech32( - account_id, wallet.get_client().get_bech32_hrp()) +account_address = Utils.address_to_bech32( + AccountAddress(account_id), wallet.get_client().get_bech32_hrp()) faucet_response = wallet.get_client().request_funds_from_faucet( FAUCET_URL, account_address) print(faucet_response) diff --git a/bindings/python/examples/how_tos/account_output/send_amount.py b/bindings/python/examples/how_tos/account_output/send_amount.py index f21d018bd5..50a1730386 100644 --- a/bindings/python/examples/how_tos/account_output/send_amount.py +++ b/bindings/python/examples/how_tos/account_output/send_amount.py @@ -1,7 +1,7 @@ import os from dotenv import load_dotenv -from iota_sdk import Wallet, WalletOptions, Utils, NodeIndexerAPI, SyncOptions, WalletSyncOptions, SendParams +from iota_sdk import AccountAddress, Wallet, WalletOptions, Utils, NodeIndexerAPI, SyncOptions, WalletSyncOptions, SendParams # In this example we send funds from an account output. @@ -26,8 +26,8 @@ print(f'Account Id: {account_id}') # Get account address -account_address = Utils.account_id_to_bech32( - account_id, wallet.get_client().get_bech32_hrp()) +account_address = Utils.address_to_bech32( + AccountAddress(account_id), wallet.get_client().get_bech32_hrp()) # Find first output unlockable by the account address query_parameters = NodeIndexerAPI.BasicOutputQueryParameters( diff --git a/bindings/python/examples/how_tos/advanced_transactions/advanced_transaction.py b/bindings/python/examples/how_tos/advanced_transactions/advanced_transaction.py index d01b938590..f7151d8c20 100644 --- a/bindings/python/examples/how_tos/advanced_transactions/advanced_transaction.py +++ b/bindings/python/examples/how_tos/advanced_transactions/advanced_transaction.py @@ -6,7 +6,6 @@ from iota_sdk import ( AddressUnlockCondition, Client, - Ed25519Address, Wallet, WalletOptions, Utils, @@ -39,8 +38,8 @@ basic_output = Client().build_basic_output( unlock_conditions=[ AddressUnlockCondition( - Ed25519Address( - Utils.bech32_to_hex('rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy')) + Utils.parse_bech32_address( + 'rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy') ), TimelockUnlockCondition(in_an_hour), ], diff --git a/bindings/python/examples/how_tos/nft_collection/01_mint_collection_nft.py b/bindings/python/examples/how_tos/nft_collection/01_mint_collection_nft.py index 7350e8cde2..53a05f3a50 100644 --- a/bindings/python/examples/how_tos/nft_collection/01_mint_collection_nft.py +++ b/bindings/python/examples/how_tos/nft_collection/01_mint_collection_nft.py @@ -2,7 +2,7 @@ import sys from dotenv import load_dotenv -from iota_sdk import MintNftParams, Utils, Wallet, WalletOptions, Irc27Metadata +from iota_sdk import HexStr, MintNftParams, NFTAddress, Utils, Wallet, WalletOptions, Irc27Metadata load_dotenv() @@ -16,7 +16,7 @@ if len(sys.argv) < 2: raise Exception("missing example argument: ISSUER_NFT_ID") -issuer_nft_id = sys.argv[1] +issuer_nft_id = HexStr(sys.argv[1]) wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) @@ -29,12 +29,12 @@ wallet.sync() bech32_hrp = wallet.get_client().get_bech32_hrp() -issuer = Utils.nft_id_to_bech32(issuer_nft_id, bech32_hrp) +issuer = Utils.address_to_bech32(NFTAddress(issuer_nft_id), bech32_hrp) def get_immutable_metadata(index: int) -> str: """Returns the immutable metadata for the NFT with the given index""" - Irc27Metadata( + return Irc27Metadata( "video/mp4", "https://ipfs.io/ipfs/QmPoYcVm9fx47YXNTkhpMEYSxCD3Bqh7PJYr7eo5YjLgiT", "Shimmer OG NFT #" + str(index), diff --git a/bindings/python/examples/how_tos/outputs/features.py b/bindings/python/examples/how_tos/outputs/features.py index a3bb5482b9..210e82623d 100644 --- a/bindings/python/examples/how_tos/outputs/features.py +++ b/bindings/python/examples/how_tos/outputs/features.py @@ -5,7 +5,6 @@ from iota_sdk import ( AddressUnlockCondition, Client, - Ed25519Address, Utils, SenderFeature, IssuerFeature, @@ -18,12 +17,10 @@ client = Client() -hex_address = Utils.bech32_to_hex( +address = Utils.parse_bech32_address( 'rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy') -address_unlock_condition = AddressUnlockCondition( - Ed25519Address(hex_address) -) +address_unlock_condition = AddressUnlockCondition(address) # Output with sender feature nft_output = client.build_nft_output( @@ -32,7 +29,7 @@ address_unlock_condition ], features=[ - SenderFeature(Ed25519Address(hex_address)) + SenderFeature(address) ], ) outputs = [nft_output] @@ -44,7 +41,7 @@ address_unlock_condition, ], immutable_features=[ - IssuerFeature(Ed25519Address(hex_address)) + IssuerFeature(address) ], ) outputs.append(nft_output) diff --git a/bindings/python/examples/how_tos/outputs/unlock_conditions.py b/bindings/python/examples/how_tos/outputs/unlock_conditions.py index 01ee5db20b..e0d349cc8c 100644 --- a/bindings/python/examples/how_tos/outputs/unlock_conditions.py +++ b/bindings/python/examples/how_tos/outputs/unlock_conditions.py @@ -3,9 +3,7 @@ from dotenv import load_dotenv from iota_sdk import ( AddressUnlockCondition, - AccountAddress, Client, - Ed25519Address, Utils, ExpirationUnlockCondition, SimpleTokenScheme, @@ -18,15 +16,13 @@ client = Client() -hex_address = Utils.bech32_to_hex( +ed25519_address = Utils.parse_bech32_address( 'rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy') -account_hex_address = Utils.bech32_to_hex( +account_address = Utils.parse_bech32_address( 'rms1pr59qm43mjtvhcajfmupqf23x29llam88yecn6pyul80rx099krmv2fnnux') -address_unlock_condition = AddressUnlockCondition( - Ed25519Address(hex_address) -) +address_unlock_condition = AddressUnlockCondition(ed25519_address) token_scheme = SimpleTokenScheme(50, 0, 100) @@ -42,7 +38,7 @@ address_unlock_condition, StorageDepositReturnUnlockCondition( 1000000, - Ed25519Address(hex_address), + ed25519_address, ), ], ) @@ -63,7 +59,7 @@ address_unlock_condition, ExpirationUnlockCondition( 1, - Ed25519Address(hex_address), + ed25519_address, ), ], ) @@ -75,7 +71,7 @@ token_scheme=token_scheme, unlock_conditions=[ ImmutableAccountAddressUnlockCondition( - AccountAddress(account_hex_address), + account_address, ), ], ) diff --git a/bindings/python/examples/how_tos/wallet/consolidate_outputs.py b/bindings/python/examples/how_tos/wallet/consolidate_outputs.py index efb0edb804..1d623980dd 100644 --- a/bindings/python/examples/how_tos/wallet/consolidate_outputs.py +++ b/bindings/python/examples/how_tos/wallet/consolidate_outputs.py @@ -32,7 +32,7 @@ for i, output_data in enumerate(outputs): print(f'OUTPUT #{i}') print( - f'- address: #{Utils.hex_to_bech32(output_data.address.pub_key_hash, "rms")}') + f'- address: #{Utils.address_to_bech32(output_data.address, "rms")}') print(f'- amount: #{output_data.output.amount}') native_tokens = [ @@ -65,7 +65,7 @@ for i, output_data in enumerate(outputs): print(f'OUTPUT #{i}') print( - f'- address: #{Utils.hex_to_bech32(output_data.address.pub_key_hash, "rms")}') + f'- address: #{Utils.address_to_bech32(output_data.address, "rms")}') print(f'- amount: #{output_data.output.amount}') native_tokens = [ diff --git a/bindings/python/examples/wallet/13-check-unlock-conditions.py b/bindings/python/examples/wallet/13-check-unlock-conditions.py index bf354557e9..66538afcc6 100644 --- a/bindings/python/examples/wallet/13-check-unlock-conditions.py +++ b/bindings/python/examples/wallet/13-check-unlock-conditions.py @@ -15,13 +15,13 @@ output = wallet.prepare_output(OutputParams( address, 1000000)) -hexEncodedWalletAddress = Utils.bech32_to_hex(address) +wallet_address = Utils.parse_bech32_address(address) controlled_by_wallet = False if len( output.unlock_conditions) == 1 and output.unlock_conditions[0].type == 0: - if output.unlock_conditions[0].address.pub_key_hash == hexEncodedWalletAddress: + if output.unlock_conditions[0].address.pub_key_hash == wallet_address.pub_key_hash: controlled_by_wallet = True print( diff --git a/bindings/python/iota_sdk/client/_utils.py b/bindings/python/iota_sdk/client/_utils.py index f829cb74cc..6a68fcc9c3 100644 --- a/bindings/python/iota_sdk/client/_utils.py +++ b/bindings/python/iota_sdk/client/_utils.py @@ -6,7 +6,6 @@ from iota_sdk.types.address import Address from iota_sdk.types.block.block import Block from iota_sdk.types.block.id import BlockId -from iota_sdk.types.common import HexStr from iota_sdk.types.output import Output @@ -35,16 +34,6 @@ def _call_method(self, name, data=None): no payload. """ - # pylint: disable=redefined-builtin - def hex_to_bech32(self, hex_str: HexStr, - bech32_hrp: Optional[str] = None) -> str: - """Transforms a hex encoded address to a bech32 encoded address. - """ - return self._call_method('hexToBech32', { - 'hex': hex_str, - 'bech32Hrp': bech32_hrp - }) - def address_to_bech32(self, address: Address, bech32_hrp: Optional[str] = None) -> str: """Converts an address to its bech32 representation. @@ -54,33 +43,6 @@ def address_to_bech32(self, address: Address, 'bech32Hrp': bech32_hrp }) - def account_id_to_bech32(self, account_id: HexStr, - bech32_hrp: Optional[str] = None) -> str: - """Transforms an account id to a bech32 encoded address. - """ - return self._call_method('accountIdToBech32', { - 'accountId': account_id, - 'bech32Hrp': bech32_hrp - }) - - def anchor_id_to_bech32(self, anchor_id: HexStr, - bech32_hrp: Optional[str] = None) -> str: - """Transforms an anchor id to a bech32 encoded address. - """ - return self._call_method('anchorIdToBech32', { - 'anchorId': anchor_id, - 'bech32Hrp': bech32_hrp - }) - - def nft_id_to_bech32(self, nft_id: HexStr, - bech32_hrp: Optional[str] = None) -> str: - """Transforms an nft id to a bech32 encoded address. - """ - return self._call_method('nftIdToBech32', { - 'nftId': nft_id, - 'bech32Hrp': bech32_hrp - }) - def computer_minimum_output_amount(self, output: Output) -> int: """Minimum required output amount. """ diff --git a/bindings/python/iota_sdk/utils.py b/bindings/python/iota_sdk/utils.py index 7a50f7a0b5..02f27c60fe 100644 --- a/bindings/python/iota_sdk/utils.py +++ b/bindings/python/iota_sdk/utils.py @@ -29,25 +29,6 @@ class Utils: """Utility functions. """ - - @staticmethod - def bech32_to_hex(bech32: str) -> HexStr: - """Convert a Bech32 string to a hex string. - """ - return _call_method('bech32ToHex', { - 'bech32': bech32 - }) - - # pylint: disable=redefined-builtin - @staticmethod - def hex_to_bech32(hex_str: HexStr, bech32_hrp: str) -> str: - """Convert a hex encoded address to a Bech32 encoded address. - """ - return _call_method('hexToBech32', { - 'hex': hex_str, - 'bech32Hrp': bech32_hrp - }) - @staticmethod def address_to_bech32(address: Address, bech32_hrp: str) -> str: """Convert an address to its bech32 representation. @@ -57,33 +38,6 @@ def address_to_bech32(address: Address, bech32_hrp: str) -> str: 'bech32Hrp': bech32_hrp }) - @staticmethod - def account_id_to_bech32(account_id: HexStr, bech32_hrp: str) -> str: - """Convert an account id to a Bech32 encoded address. - """ - return _call_method('accountIdToBech32', { - 'accountId': account_id, - 'bech32Hrp': bech32_hrp - }) - - @staticmethod - def anchor_id_to_bech32(anchor_id: HexStr, bech32_hrp: str) -> str: - """Convert an anchor id to a Bech32 encoded address. - """ - return _call_method('anchorIdToBech32', { - 'anchorId': anchor_id, - 'bech32Hrp': bech32_hrp - }) - - @staticmethod - def nft_id_to_bech32(nft_id: HexStr, bech32_hrp: str) -> str: - """Convert an NFT ID to a Bech32 encoded address. - """ - return _call_method('nftIdToBech32', { - 'nftId': nft_id, - 'bech32Hrp': bech32_hrp - }) - # pylint: disable=redefined-builtin @staticmethod def public_key_hash( diff --git a/bindings/wasm/tests/utilityMethods.spec.ts b/bindings/wasm/tests/utilityMethods.spec.ts index 34f677b70d..fbcf791456 100644 --- a/bindings/wasm/tests/utilityMethods.spec.ts +++ b/bindings/wasm/tests/utilityMethods.spec.ts @@ -1,4 +1,4 @@ -import { Utils } from '../node/lib'; +import { AccountAddress, Ed25519Address, Utils } from '../node/lib'; describe('Utils methods', () => { it('generates and validates mnemonic', async () => { @@ -17,15 +17,15 @@ describe('Utils methods', () => { }); it('converts address to hex and bech32', async () => { - const address = + const bech32Address = 'rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy'; - const hexAddress = Utils.bech32ToHex(address); + const address = Utils.parseBech32Address(bech32Address) as Ed25519Address; - expect(hexAddress.slice(0, 2)).toBe('0x'); + expect(address.pubKeyHash.slice(0, 2)).toBe('0x'); - const bech32Address = Utils.hexToBech32(hexAddress, 'rms'); + const convertedBech32Address = Utils.addressToBech32(address, 'rms'); - expect(bech32Address).toBe(address); + expect(convertedBech32Address).toBe(bech32Address); }); it('converts hex public key to bech32 address', async () => { @@ -63,7 +63,7 @@ describe('Utils methods', () => { const accountId = '0x0ebc2867a240719a70faacdfc3840e857fa450b37d95297ac4f166c2f70c3345'; - const accountAddress = Utils.accountIdToBech32(accountId, 'rms'); + const accountAddress = Utils.addressToBech32(new AccountAddress(accountId), 'rms'); expect(accountAddress).toBe( 'rms1pq8tc2r85fq8rxnsl2kdlsuyp6zhlfzskd7e22t6cnckdshhpse52a27mlc', diff --git a/sdk/src/client/utils.rs b/sdk/src/client/utils.rs index 1db387bab3..fc705f0137 100644 --- a/sdk/src/client/utils.rs +++ b/sdk/src/client/utils.rs @@ -19,33 +19,12 @@ use crate::{ client::ClientError, types::block::{ address::{Address, Bech32Address, Ed25519Address, Hrp, ToBech32Ext}, - output::{AccountId, AnchorId, NftId}, payload::TaggedDataPayload, Block, BlockId, }, utils::ConvertTo, }; -/// Transforms bech32 to hex -pub fn bech32_to_hex(bech32: impl ConvertTo) -> Result { - Ok(match bech32.convert()?.inner() { - Address::Ed25519(ed) => ed.to_string(), - Address::Account(account) => account.to_string(), - Address::Nft(nft) => nft.to_string(), - Address::Anchor(anchor) => anchor.to_string(), - Address::ImplicitAccountCreation(implicit) => implicit.to_string(), - Address::Multi(multi) => multi.to_string(), - Address::Restricted(restricted) => restricted.to_string(), - }) -} - -/// Transforms a hex encoded address to a bech32 encoded address -pub fn hex_to_bech32(hex: &str, bech32_hrp: impl ConvertTo) -> Result { - let address = hex.parse::()?; - - Ok(Address::Ed25519(address).try_to_bech32(bech32_hrp)?) -} - /// Transforms a prefix hex encoded public key to a bech32 encoded address pub fn hex_public_key_to_bech32_address( hex: &str, @@ -108,18 +87,6 @@ pub async fn request_funds_from_faucet(url: &str, bech32_address: &Bech32Address } impl Client { - /// Transforms a hex encoded address to a bech32 encoded address - pub async fn hex_to_bech32( - &self, - hex: &str, - bech32_hrp: Option>, - ) -> Result { - match bech32_hrp { - Some(hrp) => Ok(hex_to_bech32(hex, hrp)?), - None => Ok(hex_to_bech32(hex, self.get_bech32_hrp().await?)?), - } - } - /// Converts an address to its bech32 representation pub async fn address_to_bech32( &self, @@ -132,42 +99,6 @@ impl Client { } } - /// Transforms an account id to a bech32 encoded address - pub async fn account_id_to_bech32( - &self, - account_id: AccountId, - bech32_hrp: Option>, - ) -> Result { - match bech32_hrp { - Some(hrp) => Ok(account_id.to_bech32(hrp.convert()?)), - None => Ok(account_id.to_bech32(self.get_bech32_hrp().await?)), - } - } - - /// Transforms an anchor id to a bech32 encoded address - pub async fn anchor_id_to_bech32( - &self, - anchor_id: AnchorId, - bech32_hrp: Option>, - ) -> Result { - match bech32_hrp { - Some(hrp) => Ok(anchor_id.to_bech32(hrp.convert()?)), - None => Ok(anchor_id.to_bech32(self.get_bech32_hrp().await?)), - } - } - - /// Transforms an nft id to a bech32 encoded address - pub async fn nft_id_to_bech32( - &self, - nft_id: NftId, - bech32_hrp: Option>, - ) -> Result { - match bech32_hrp { - Some(hrp) => Ok(nft_id.to_bech32(hrp.convert()?)), - None => Ok(nft_id.to_bech32(self.get_bech32_hrp().await?)), - } - } - /// Transforms a hex encoded public key to a bech32 encoded address pub async fn hex_public_key_to_bech32_address( &self, @@ -180,11 +111,6 @@ impl Client { } } - /// Transforms bech32 to hex - pub fn bech32_to_hex(bech32: impl ConvertTo) -> Result { - bech32_to_hex(bech32) - } - /// Generates a new mnemonic. pub fn generate_mnemonic() -> Result { generate_mnemonic() From ee51d3ee477732c011e9c6226692beb976d86922 Mon Sep 17 00:00:00 2001 From: DaughterOfMars Date: Thu, 7 Mar 2024 09:48:47 -0500 Subject: [PATCH 19/21] Fix clippy warnings (#2157) * fix clippy * nit --------- Co-authored-by: Thibault Martinez --- bindings/core/src/method/wallet.rs | 4 +- sdk/examples/client/send_all.rs | 2 +- .../client/api/block_builder/transaction.rs | 2 +- .../block_builder/transaction_builder/mod.rs | 10 +-- .../transaction_builder/requirement/mana.rs | 2 +- sdk/src/client/api/wait_for_tx_acceptance.rs | 2 +- sdk/src/client/error.rs | 9 ++- sdk/src/types/block/output/account.rs | 2 +- sdk/src/types/block/semantic/mod.rs | 69 ++++++++++++++----- sdk/src/wallet/error.rs | 2 +- sdk/tests/client/signing/account.rs | 14 ++-- sdk/tests/client/signing/basic.rs | 6 +- sdk/tests/client/signing/delegation.rs | 42 +++++------ sdk/tests/client/signing/mod.rs | 2 +- sdk/tests/client/signing/nft.rs | 6 +- .../transaction_builder/account_outputs.rs | 14 ++-- .../transaction_builder/basic_outputs.rs | 20 +++--- sdk/tests/client/transaction_builder/burn.rs | 6 +- .../transaction_builder/delegation_outputs.rs | 2 +- .../client/transaction_builder/expiration.rs | 30 ++++---- .../transaction_builder/native_tokens.rs | 4 +- .../client/transaction_builder/nft_outputs.rs | 12 ++-- .../client/transaction_builder/outputs.rs | 6 +- .../storage_deposit_return.rs | 2 +- .../client/transaction_builder/timelock.rs | 8 +-- sdk/tests/types/block.rs | 4 +- sdk/tests/types/output/account.rs | 2 +- sdk/tests/types/output/basic.rs | 2 +- sdk/tests/types/output/foundry.rs | 2 +- sdk/tests/types/output/nft.rs | 2 +- sdk/tests/types/payload.rs | 2 +- sdk/tests/types/signed_transaction_payload.rs | 8 +-- 32 files changed, 169 insertions(+), 131 deletions(-) diff --git a/bindings/core/src/method/wallet.rs b/bindings/core/src/method/wallet.rs index 70e9997662..e5d1bad209 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::client::api::options::TransactionOptions; #[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, @@ -22,7 +22,7 @@ use iota_sdk::{ secret::GenerateAddressOptions, }, types::block::{ - address::{Bech32Address, Hrp}, + address::Hrp, output::{AccountId, DelegationId, Output, OutputId, TokenId}, payload::signed_transaction::TransactionId, }, diff --git a/sdk/examples/client/send_all.rs b/sdk/examples/client/send_all.rs index 0418a151be..1206389d57 100644 --- a/sdk/examples/client/send_all.rs +++ b/sdk/examples/client/send_all.rs @@ -88,7 +88,7 @@ async fn main() -> Result<(), Box> { let native_token_return = BasicOutputBuilder::new_with_minimum_amount(protocol_parameters.storage_score_parameters()) .add_unlock_condition(AddressUnlockCondition::new(from_address.clone())) - .with_native_token(native_token.clone()) + .with_native_token(*native_token) .finish_output()?; total_amount -= native_token_return.amount(); outputs.push(native_token_return); diff --git a/sdk/src/client/api/block_builder/transaction.rs b/sdk/src/client/api/block_builder/transaction.rs index d6cd3fc3e4..6edf74ee4e 100644 --- a/sdk/src/client/api/block_builder/transaction.rs +++ b/sdk/src/client/api/block_builder/transaction.rs @@ -59,7 +59,7 @@ impl SignedTransactionData { .collect::>(); let context = SemanticValidationContext::new( - &self.payload.transaction(), + self.payload.transaction(), &inputs, Some(self.payload.unlocks()), Some(&self.mana_rewards), diff --git a/sdk/src/client/api/block_builder/transaction_builder/mod.rs b/sdk/src/client/api/block_builder/transaction_builder/mod.rs index 0bbf3255f7..0e951614e4 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/mod.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/mod.rs @@ -48,8 +48,8 @@ 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, + addresses: impl IntoIterator + Send, + outputs: impl IntoIterator + Send, options: TransactionOptions, ) -> Result { let addresses = addresses.into_iter().collect::>(); @@ -93,9 +93,9 @@ impl Client { /// 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, + addresses: impl IntoIterator + Send, + available_inputs: impl IntoIterator + Send, + outputs: impl IntoIterator + Send, options: TransactionOptions, slot_commitment_id: SlotCommitmentId, protocol_parameters: ProtocolParameters, diff --git a/sdk/src/client/api/block_builder/transaction_builder/requirement/mana.rs b/sdk/src/client/api/block_builder/transaction_builder/requirement/mana.rs index 5b0de1d367..58a0b8989f 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/requirement/mana.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/requirement/mana.rs @@ -309,7 +309,7 @@ impl TransactionBuilder { let mut selected_mana = 0; let include_generated = include_generated .into() - .unwrap_or(self.burn.as_ref().map_or(true, |b| !b.generated_mana())); + .unwrap_or_else(|| self.burn.as_ref().map_or(true, |b| !b.generated_mana())); for input in &self.selected_inputs { selected_mana += self.total_mana(input, include_generated)?; diff --git a/sdk/src/client/api/wait_for_tx_acceptance.rs b/sdk/src/client/api/wait_for_tx_acceptance.rs index aa34d91403..a12c71eb60 100644 --- a/sdk/src/client/api/wait_for_tx_acceptance.rs +++ b/sdk/src/client/api/wait_for_tx_acceptance.rs @@ -42,6 +42,6 @@ impl Client { tokio::time::sleep(duration).await; } - Err(ClientError::TransactionAcceptance(transaction_id.to_string()).into()) + Err(ClientError::TransactionAcceptance(transaction_id.to_string())) } } diff --git a/sdk/src/client/error.rs b/sdk/src/client/error.rs index 7a17381d2a..12b202d576 100644 --- a/sdk/src/client/error.rs +++ b/sdk/src/client/error.rs @@ -185,7 +185,7 @@ pub enum ClientError { #[cfg(feature = "ledger_nano")] #[cfg_attr(docsrs, doc(cfg(feature = "ledger_nano")))] #[error("{0}")] - Ledger(#[from] crate::client::secret::ledger_nano::Error), + Ledger(Box), /// MQTT error #[cfg(feature = "mqtt")] @@ -236,3 +236,10 @@ crate::impl_from_error_via!(ClientError via BlockError: UnlockError, SignatureError, ); + +#[cfg(feature = "ledger_nano")] +impl From for ClientError { + fn from(value: crate::client::secret::ledger_nano::Error) -> Self { + Self::Ledger(value.into()) + } +} diff --git a/sdk/src/types/block/output/account.rs b/sdk/src/types/block/output/account.rs index e3b22d7af1..ca0ea23646 100644 --- a/sdk/src/types/block/output/account.rs +++ b/sdk/src/types/block/output/account.rs @@ -563,7 +563,7 @@ fn verify_unlock_conditions(unlock_conditions: &UnlockConditions, account_id: &A fn verify_staking(amount: u64, features: &Features) -> Result<(), OutputError> { if let Some(staking) = features.staking() { if features.block_issuer().is_none() { - return Err(FeatureError::StakingBlockIssuerMissing)?; + return Err(FeatureError::StakingBlockIssuerMissing.into()); } if amount < staking.staked_amount() { return Err(OutputError::InvalidStakedAmount); diff --git a/sdk/src/types/block/semantic/mod.rs b/sdk/src/types/block/semantic/mod.rs index 89f04c5f91..10f8270cab 100644 --- a/sdk/src/types/block/semantic/mod.rs +++ b/sdk/src/types/block/semantic/mod.rs @@ -89,7 +89,10 @@ impl<'a> SemanticValidationContext<'a> { input_amount: 0, input_mana: 0, mana_rewards, - commitment_context_input: None, + commitment_context_input: transaction + .context_inputs() + .commitment() + .map(|c| c.slot_commitment_id()), reward_context_inputs: Default::default(), input_native_tokens: BTreeMap::::new(), input_chains, @@ -107,19 +110,22 @@ impl<'a> SemanticValidationContext<'a> { /// pub fn validate(mut self) -> Result<(), TransactionFailureReason> { - self.commitment_context_input = self - .transaction - .context_inputs() - .commitment() - .map(|c| c.slot_commitment_id()); + self.validate_reward_context_inputs()?; - let bic_context_inputs = self - .transaction - .context_inputs() - .block_issuance_credits() - .map(|bic| *bic.account_id()) - .collect::>(); + self.validate_inputs()?; + + self.validate_outputs()?; + + self.validate_storage_deposit_returns()?; + self.validate_balances()?; + + self.validate_transitions()?; + + Ok(()) + } + + fn validate_reward_context_inputs(&mut self) -> Result<(), TransactionFailureReason> { for reward_context_input in self.transaction.context_inputs().rewards() { if let Some(output_id) = self.inputs.get(reward_context_input.index() as usize).map(|v| v.0) { self.reward_context_inputs.insert(*output_id, *reward_context_input); @@ -127,8 +133,16 @@ impl<'a> SemanticValidationContext<'a> { return Err(TransactionFailureReason::RewardInputReferenceInvalid); } } + Ok(()) + } - // Validation of inputs. + fn validate_inputs(&mut self) -> Result<(), TransactionFailureReason> { + let bic_context_inputs = self + .transaction + .context_inputs() + .block_issuance_credits() + .map(|bic| *bic.account_id()) + .collect::>(); let mut has_implicit_account_creation_address = false; @@ -155,7 +169,7 @@ impl<'a> SemanticValidationContext<'a> { .checked_add( consumed_output .available_mana( - &self.protocol_parameters, + self.protocol_parameters, output_id.transaction_id().slot_index(), self.transaction.creation_slot(), ) @@ -230,7 +244,7 @@ impl<'a> SemanticValidationContext<'a> { .checked_add( consumed_output .available_mana( - &self.protocol_parameters, + self.protocol_parameters, output_id.transaction_id().slot_index(), self.transaction.creation_slot(), ) @@ -266,6 +280,17 @@ impl<'a> SemanticValidationContext<'a> { } } + Ok(()) + } + + fn validate_outputs(&mut self) -> Result<(), TransactionFailureReason> { + let bic_context_inputs = self + .transaction + .context_inputs() + .block_issuance_credits() + .map(|bic| *bic.account_id()) + .collect::>(); + // Add allotted mana for mana_allotment in self.transaction.allotments().iter() { self.output_mana = self @@ -274,7 +299,6 @@ impl<'a> SemanticValidationContext<'a> { .ok_or(TransactionFailureReason::ManaOverflow)?; } - // Validation of outputs. for (index, created_output) in self.transaction.outputs().iter().enumerate() { let (amount, mana, created_native_token, features) = match created_output { Output::Basic(output) => { @@ -377,8 +401,10 @@ impl<'a> SemanticValidationContext<'a> { .ok_or(TransactionFailureReason::SemanticValidationFailed)?; } } + Ok(()) + } - // Validation of storage deposit returns. + fn validate_storage_deposit_returns(&mut self) -> Result<(), TransactionFailureReason> { for (return_address, return_amount) in self.storage_deposit_returns.iter() { if let Some(deposit_amount) = self.simple_deposits.get(return_address) { if deposit_amount < return_amount { @@ -388,8 +414,11 @@ impl<'a> SemanticValidationContext<'a> { return Err(TransactionFailureReason::ReturnAmountNotFulFilled); } } + Ok(()) + } - // Validation of amounts. + fn validate_balances(&mut self) -> Result<(), TransactionFailureReason> { + // Validation of amounts if self.input_amount != self.output_amount { return Err(TransactionFailureReason::InputOutputBaseTokenMismatch); } @@ -422,7 +451,10 @@ impl<'a> SemanticValidationContext<'a> { return Err(TransactionFailureReason::NativeTokenSumUnbalanced); } } + Ok(()) + } + fn validate_transitions(&mut self) -> Result<(), TransactionFailureReason> { // Validation of state transitions and destructions. for (chain_id, current_state) in self.input_chains.iter() { self.verify_state_transition( @@ -437,7 +469,6 @@ impl<'a> SemanticValidationContext<'a> { self.verify_state_transition(None, Some((&next_state.0, next_state.1)))?; } } - Ok(()) } } diff --git a/sdk/src/wallet/error.rs b/sdk/src/wallet/error.rs index ea77f990e1..50d6f5488f 100644 --- a/sdk/src/wallet/error.rs +++ b/sdk/src/wallet/error.rs @@ -223,7 +223,7 @@ impl From for WalletError { #[cfg(feature = "ledger_nano")] impl From for WalletError { fn from(error: crate::client::secret::ledger_nano::Error) -> Self { - Self::Client(ClientError::Ledger(error)) + Self::Client(ClientError::from(error)) } } diff --git a/sdk/tests/client/signing/account.rs b/sdk/tests/client/signing/account.rs index dcf647039c..f1eb6c5bec 100644 --- a/sdk/tests/client/signing/account.rs +++ b/sdk/tests/client/signing/account.rs @@ -55,7 +55,7 @@ async fn sign_account_state_transition() -> Result<(), Box Result<(), Box Result<(), Box Result<(), Box> { Account { amount: 1_000_000, mana: 0, - account_id: account_id, + account_id, address: address.clone(), sender: None, issuer: None, @@ -175,8 +175,8 @@ async fn account_reference_unlocks() -> Result<(), Box> { Account { amount: 1_000_000, mana: 0, - account_id: account_id, - address: address, + account_id, + address, sender: None, issuer: None, }, @@ -212,7 +212,7 @@ async fn account_reference_unlocks() -> Result<(), Box> { }; let unlocks = secret_manager - .transaction_unlocks(&prepared_transaction_data, &protocol_parameters) + .transaction_unlocks(&prepared_transaction_data, protocol_parameters) .await?; assert_eq!(unlocks.len(), 3); diff --git a/sdk/tests/client/signing/basic.rs b/sdk/tests/client/signing/basic.rs index 39a60ea051..9afce0e84e 100644 --- a/sdk/tests/client/signing/basic.rs +++ b/sdk/tests/client/signing/basic.rs @@ -89,7 +89,7 @@ async fn single_ed25519_unlock() -> Result<(), Box> { }; let unlocks = secret_manager - .transaction_unlocks(&prepared_transaction_data, &protocol_parameters) + .transaction_unlocks(&prepared_transaction_data, protocol_parameters) .await?; assert_eq!(unlocks.len(), 1); @@ -197,7 +197,7 @@ async fn ed25519_reference_unlocks() -> Result<(), Box> { }; let unlocks = secret_manager - .transaction_unlocks(&prepared_transaction_data, &protocol_parameters) + .transaction_unlocks(&prepared_transaction_data, protocol_parameters) .await?; assert_eq!(unlocks.len(), 3); @@ -313,7 +313,7 @@ async fn two_signature_unlocks() -> Result<(), Box> { }; let unlocks = secret_manager - .transaction_unlocks(&prepared_transaction_data, &protocol_parameters) + .transaction_unlocks(&prepared_transaction_data, protocol_parameters) .await?; assert_eq!(unlocks.len(), 2); diff --git a/sdk/tests/client/signing/delegation.rs b/sdk/tests/client/signing/delegation.rs index 7b7625b97c..e1289923c5 100644 --- a/sdk/tests/client/signing/delegation.rs +++ b/sdk/tests/client/signing/delegation.rs @@ -99,7 +99,7 @@ async fn valid_creation() -> Result<(), Box> { }; let unlocks = secret_manager - .transaction_unlocks(&prepared_transaction_data, &protocol_parameters) + .transaction_unlocks(&prepared_transaction_data, protocol_parameters) .await?; assert_eq!(unlocks.len(), 1); @@ -216,7 +216,7 @@ async fn non_null_id_creation() -> Result<(), Box> { amount: 1_000_000, delegation_amount: 1_000_000, delegation_id: rand_delegation_id(), - address: address, + address, validator_address: rand_account_address(), start_epoch: *protocol_parameters.delegation_start_epoch(slot_commitment_id), end_epoch: 0, @@ -245,7 +245,7 @@ async fn non_null_id_creation() -> Result<(), Box> { }; let unlocks = secret_manager - .transaction_unlocks(&prepared_transaction_data, &protocol_parameters) + .transaction_unlocks(&prepared_transaction_data, protocol_parameters) .await?; assert_eq!(unlocks.len(), 1); @@ -301,7 +301,7 @@ async fn mismatch_amount_creation() -> Result<(), Box> { amount: 1_000_000, delegation_amount: 1_500_000, delegation_id: DelegationId::null(), - address: address, + address, validator_address: rand_account_address(), start_epoch: *protocol_parameters.delegation_start_epoch(slot_commitment_id), end_epoch: 0, @@ -330,7 +330,7 @@ async fn mismatch_amount_creation() -> Result<(), Box> { }; let unlocks = secret_manager - .transaction_unlocks(&prepared_transaction_data, &protocol_parameters) + .transaction_unlocks(&prepared_transaction_data, protocol_parameters) .await?; assert_eq!(unlocks.len(), 1); @@ -386,7 +386,7 @@ async fn non_zero_end_epoch_creation() -> Result<(), Box> amount: 1_000_000, delegation_amount: 1_000_000, delegation_id: DelegationId::null(), - address: address, + address, validator_address: rand_account_address(), start_epoch: *protocol_parameters.delegation_start_epoch(slot_commitment_id), end_epoch: 100, @@ -415,7 +415,7 @@ async fn non_zero_end_epoch_creation() -> Result<(), Box> }; let unlocks = secret_manager - .transaction_unlocks(&prepared_transaction_data, &protocol_parameters) + .transaction_unlocks(&prepared_transaction_data, protocol_parameters) .await?; assert_eq!(unlocks.len(), 1); @@ -471,7 +471,7 @@ async fn invalid_start_epoch_creation() -> Result<(), Box amount: 1_000_000, delegation_amount: 1_000_000, delegation_id: DelegationId::null(), - address: address, + address, validator_address: rand_account_address(), start_epoch: *protocol_parameters.delegation_start_epoch(slot_commitment_id) + 5, end_epoch: 0, @@ -498,7 +498,7 @@ async fn invalid_start_epoch_creation() -> Result<(), Box }; let unlocks = secret_manager - .transaction_unlocks(&prepared_transaction_data, &protocol_parameters) + .transaction_unlocks(&prepared_transaction_data, protocol_parameters) .await?; assert_eq!(unlocks.len(), 1); @@ -561,7 +561,7 @@ async fn delay_not_null_id() -> Result<(), Box> { amount: 1_000_000, delegation_amount: 1_000_000, delegation_id, - address: address, + address, validator_address, start_epoch: *protocol_parameters.delegation_start_epoch(slot_commitment_id_1), end_epoch: *protocol_parameters.delegation_end_epoch(slot_commitment_id_2), @@ -591,7 +591,7 @@ async fn delay_not_null_id() -> Result<(), Box> { }; let unlocks = secret_manager - .transaction_unlocks(&prepared_transaction_data, &protocol_parameters) + .transaction_unlocks(&prepared_transaction_data, protocol_parameters) .await?; assert_eq!(unlocks.len(), 1); @@ -657,7 +657,7 @@ async fn delay_modified_amount() -> Result<(), Box> { amount: 1_000_000, delegation_amount: 900_000, delegation_id, - address: address, + address, validator_address, start_epoch: *protocol_parameters.delegation_start_epoch(slot_commitment_id_1), end_epoch: *protocol_parameters.delegation_end_epoch(slot_commitment_id_2), @@ -687,7 +687,7 @@ async fn delay_modified_amount() -> Result<(), Box> { }; let unlocks = secret_manager - .transaction_unlocks(&prepared_transaction_data, &protocol_parameters) + .transaction_unlocks(&prepared_transaction_data, protocol_parameters) .await?; assert_eq!(unlocks.len(), 1); @@ -750,7 +750,7 @@ async fn delay_modified_validator() -> Result<(), Box> { amount: 1_000_000, delegation_amount: 1_000_000, delegation_id, - address: address, + address, validator_address: rand_account_address(), start_epoch: *protocol_parameters.delegation_start_epoch(slot_commitment_id_1), end_epoch: *protocol_parameters.delegation_end_epoch(slot_commitment_id_2), @@ -780,7 +780,7 @@ async fn delay_modified_validator() -> Result<(), Box> { }; let unlocks = secret_manager - .transaction_unlocks(&prepared_transaction_data, &protocol_parameters) + .transaction_unlocks(&prepared_transaction_data, protocol_parameters) .await?; assert_eq!(unlocks.len(), 1); @@ -843,7 +843,7 @@ async fn delay_modified_start_epoch() -> Result<(), Box> amount: 1_000_000, delegation_amount: 1_000_000, delegation_id, - address: address, + address, validator_address, start_epoch: *protocol_parameters.delegation_start_epoch(slot_commitment_id_1) + 1, end_epoch: *protocol_parameters.delegation_end_epoch(slot_commitment_id_2), @@ -873,7 +873,7 @@ async fn delay_modified_start_epoch() -> Result<(), Box> }; let unlocks = secret_manager - .transaction_unlocks(&prepared_transaction_data, &protocol_parameters) + .transaction_unlocks(&prepared_transaction_data, protocol_parameters) .await?; assert_eq!(unlocks.len(), 1); @@ -936,7 +936,7 @@ async fn delay_pre_registration_slot_end_epoch() -> Result<(), Box Result<(), Box Result<(), Box> { }; let unlocks = secret_manager - .transaction_unlocks(&prepared_transaction_data, &protocol_parameters) + .transaction_unlocks(&prepared_transaction_data, protocol_parameters) .await?; assert_eq!(unlocks.len(), 1); @@ -1148,7 +1148,7 @@ async fn destroy_reward_missing() -> Result<(), Box> { }; let unlocks = secret_manager - .transaction_unlocks(&prepared_transaction_data, &protocol_parameters) + .transaction_unlocks(&prepared_transaction_data, protocol_parameters) .await?; assert_eq!(unlocks.len(), 1); diff --git a/sdk/tests/client/signing/mod.rs b/sdk/tests/client/signing/mod.rs index 99562cd7a5..668f1a20b3 100644 --- a/sdk/tests/client/signing/mod.rs +++ b/sdk/tests/client/signing/mod.rs @@ -423,7 +423,7 @@ async fn all_combined() -> Result<(), Box> { }; let unlocks = secret_manager - .transaction_unlocks(&prepared_transaction_data, &protocol_parameters) + .transaction_unlocks(&prepared_transaction_data, protocol_parameters) .await?; assert_eq!(unlocks.len(), 13); diff --git a/sdk/tests/client/signing/nft.rs b/sdk/tests/client/signing/nft.rs index b52efa6395..2c2fcc9d0c 100644 --- a/sdk/tests/client/signing/nft.rs +++ b/sdk/tests/client/signing/nft.rs @@ -57,7 +57,7 @@ async fn nft_reference_unlocks() -> Result<(), Box> { Nft { amount: 1_000_000, mana: 0, - nft_id: nft_id, + nft_id, address: address_0.clone(), sender: None, issuer: None, @@ -100,7 +100,7 @@ async fn nft_reference_unlocks() -> Result<(), Box> { Nft { amount: 1_000_000, mana: 0, - nft_id: nft_id, + nft_id, address: address_0, sender: None, issuer: None, @@ -139,7 +139,7 @@ async fn nft_reference_unlocks() -> Result<(), Box> { }; let unlocks = secret_manager - .transaction_unlocks(&prepared_transaction_data, &protocol_parameters) + .transaction_unlocks(&prepared_transaction_data, protocol_parameters) .await?; assert_eq!(unlocks.len(), 3); diff --git a/sdk/tests/client/transaction_builder/account_outputs.rs b/sdk/tests/client/transaction_builder/account_outputs.rs index 6563a5ebad..a04fe40d95 100644 --- a/sdk/tests/client/transaction_builder/account_outputs.rs +++ b/sdk/tests/client/transaction_builder/account_outputs.rs @@ -70,7 +70,7 @@ fn input_account_eq_output_account() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -114,7 +114,7 @@ fn transition_account_id_zero() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } // #[test] @@ -352,7 +352,7 @@ fn burn_account() { &TransactionCapabilities::from([TransactionCapabilityFlag::DestroyAccountOutputs]) ); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } // #[test] @@ -610,7 +610,7 @@ fn account_in_output_and_sender() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -1061,7 +1061,7 @@ fn increase_account_amount() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -1665,7 +1665,7 @@ fn state_controller_sender_required_already_selected() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -1709,7 +1709,7 @@ fn state_transition_and_required() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] diff --git a/sdk/tests/client/transaction_builder/basic_outputs.rs b/sdk/tests/client/transaction_builder/basic_outputs.rs index 6b49e167f0..aa9f36d366 100644 --- a/sdk/tests/client/transaction_builder/basic_outputs.rs +++ b/sdk/tests/client/transaction_builder/basic_outputs.rs @@ -65,7 +65,7 @@ fn input_amount_equal_output_amount() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -433,7 +433,7 @@ fn two_inputs_one_needed() { .unwrap(); assert_eq!(selected.inputs_data, [inputs[0].clone()]); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -494,7 +494,7 @@ fn two_inputs_one_needed_reversed() { .unwrap(); assert_eq!(selected.inputs_data, [inputs[1].clone()]); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -555,7 +555,7 @@ fn two_inputs_both_needed() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -1463,7 +1463,7 @@ fn one_provided_one_needed() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -1748,7 +1748,7 @@ fn sender_already_selected() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -1798,7 +1798,7 @@ fn single_mandatory_input() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -1918,7 +1918,7 @@ fn more_than_max_inputs_only_one_needed() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &needed_input)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -2123,7 +2123,7 @@ fn restricted_ed25519() { assert_eq!(selected.inputs_data.len(), 1); assert_eq!(selected.inputs_data, [inputs[2].clone()]); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -2457,7 +2457,7 @@ fn multi_address_sender_already_fulfilled() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] diff --git a/sdk/tests/client/transaction_builder/burn.rs b/sdk/tests/client/transaction_builder/burn.rs index 1b6a5fad5b..7a183f3a64 100644 --- a/sdk/tests/client/transaction_builder/burn.rs +++ b/sdk/tests/client/transaction_builder/burn.rs @@ -360,7 +360,7 @@ fn burn_accounts_present() { &TransactionCapabilities::from([TransactionCapabilityFlag::DestroyAccountOutputs]) ); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -768,7 +768,7 @@ fn burn_nfts_present() { &TransactionCapabilities::from([TransactionCapabilityFlag::DestroyNftOutputs]) ); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -1584,7 +1584,7 @@ fn burn_generated_mana() { &TransactionCapabilities::from([TransactionCapabilityFlag::BurnMana]) ); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] diff --git a/sdk/tests/client/transaction_builder/delegation_outputs.rs b/sdk/tests/client/transaction_builder/delegation_outputs.rs index 612e6781a7..cd0641a24f 100644 --- a/sdk/tests/client/transaction_builder/delegation_outputs.rs +++ b/sdk/tests/client/transaction_builder/delegation_outputs.rs @@ -114,7 +114,7 @@ fn remainder_needed_for_mana() { .map(|i| i .output .available_mana( - &protocol_parameters, + protocol_parameters, i.output_id().transaction_id().slot_index(), SLOT_INDEX ) diff --git a/sdk/tests/client/transaction_builder/expiration.rs b/sdk/tests/client/transaction_builder/expiration.rs index 3cd519324b..7bd10813bb 100644 --- a/sdk/tests/client/transaction_builder/expiration.rs +++ b/sdk/tests/client/transaction_builder/expiration.rs @@ -111,7 +111,7 @@ fn expiration_equal_timestamp() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -157,7 +157,7 @@ fn one_output_expiration_expired() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -219,7 +219,7 @@ fn two_outputs_one_expiration_expired() { assert_eq!(selected.inputs_data.len(), 1); assert_eq!(selected.inputs_data[0], inputs[1]); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -281,7 +281,7 @@ fn two_outputs_one_unexpired_one_missing() { assert_eq!(selected.inputs_data.len(), 1); assert_eq!(selected.inputs_data[0], inputs[1]); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -356,7 +356,7 @@ fn two_outputs_two_expired() { assert_eq!(selected.inputs_data.len(), 1); assert_eq!(selected.inputs_data[0], inputs[1]); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -420,7 +420,7 @@ fn two_outputs_two_expired_2() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -466,7 +466,7 @@ fn expiration_expired_with_sdr() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -512,7 +512,7 @@ fn expiration_expired_with_sdr_2() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -558,7 +558,7 @@ fn expiration_expired_with_sdr_and_timelock() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -604,7 +604,7 @@ fn expiration_expired_with_sdr_and_timelock_2() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -708,7 +708,7 @@ fn sender_in_expiration() { assert_eq!(selected.inputs_data.len(), 1); assert!(selected.inputs_data.contains(&inputs[2])); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -758,7 +758,7 @@ fn sender_in_expiration_already_selected() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -864,7 +864,7 @@ fn expiration_expired_non_ed25519_in_address_unlock_condition() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -972,7 +972,7 @@ fn one_nft_output_expiration_unexpired() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -1019,5 +1019,5 @@ fn one_nft_output_expiration_expired() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } diff --git a/sdk/tests/client/transaction_builder/native_tokens.rs b/sdk/tests/client/transaction_builder/native_tokens.rs index cf8c2d8176..3e6edeeef6 100644 --- a/sdk/tests/client/transaction_builder/native_tokens.rs +++ b/sdk/tests/client/transaction_builder/native_tokens.rs @@ -640,7 +640,7 @@ fn multiple_native_tokens() { assert_eq!(selected.inputs_data.len(), 1); assert!(selected.inputs_data.contains(&inputs[0])); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -838,7 +838,7 @@ fn single_output_native_token_no_remainder() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] diff --git a/sdk/tests/client/transaction_builder/nft_outputs.rs b/sdk/tests/client/transaction_builder/nft_outputs.rs index 48ee64a36a..1f243a8b3a 100644 --- a/sdk/tests/client/transaction_builder/nft_outputs.rs +++ b/sdk/tests/client/transaction_builder/nft_outputs.rs @@ -70,7 +70,7 @@ fn input_nft_eq_output_nft() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -118,7 +118,7 @@ fn transition_nft_id_zero() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } // #[test] @@ -314,7 +314,7 @@ fn burn_nft() { &TransactionCapabilities::from([TransactionCapabilityFlag::DestroyNftOutputs]) ); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } // #[test] @@ -1019,7 +1019,7 @@ fn increase_nft_amount() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -1298,7 +1298,7 @@ fn nft_burn_should_validate_nft_sender() { &TransactionCapabilities::from([TransactionCapabilityFlag::DestroyNftOutputs]) ); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -1365,7 +1365,7 @@ fn nft_burn_should_validate_nft_address() { &TransactionCapabilities::from([TransactionCapabilityFlag::DestroyNftOutputs]) ); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] diff --git a/sdk/tests/client/transaction_builder/outputs.rs b/sdk/tests/client/transaction_builder/outputs.rs index df2ff75769..24d8f778f6 100644 --- a/sdk/tests/client/transaction_builder/outputs.rs +++ b/sdk/tests/client/transaction_builder/outputs.rs @@ -405,7 +405,7 @@ fn two_addresses() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -528,7 +528,7 @@ fn transition_no_more_than_needed_for_account_amount() { .unwrap(); assert_eq!(selected.inputs_data.len(), 1); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -589,5 +589,5 @@ fn transition_no_more_than_needed_for_nft_amount() { .unwrap(); assert_eq!(selected.inputs_data.len(), 1); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } diff --git a/sdk/tests/client/transaction_builder/storage_deposit_return.rs b/sdk/tests/client/transaction_builder/storage_deposit_return.rs index 9cf6bdd855..50393f63bd 100644 --- a/sdk/tests/client/transaction_builder/storage_deposit_return.rs +++ b/sdk/tests/client/transaction_builder/storage_deposit_return.rs @@ -128,7 +128,7 @@ fn sdruc_output_provided_no_remainder() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] diff --git a/sdk/tests/client/transaction_builder/timelock.rs b/sdk/tests/client/transaction_builder/timelock.rs index a357bfc4d0..2a63c2b8c3 100644 --- a/sdk/tests/client/transaction_builder/timelock.rs +++ b/sdk/tests/client/transaction_builder/timelock.rs @@ -105,7 +105,7 @@ fn timelock_equal_timestamp() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -167,7 +167,7 @@ fn two_outputs_one_timelock_expired() { assert_eq!(selected.inputs_data.len(), 1); assert_eq!(selected.inputs_data[0], inputs[1]); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -229,7 +229,7 @@ fn two_outputs_one_timelocked_one_missing() { assert_eq!(selected.inputs_data.len(), 1); assert_eq!(selected.inputs_data[0], inputs[1]); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } #[test] @@ -275,5 +275,5 @@ fn one_output_timelock_expired() { .unwrap(); assert!(unsorted_eq(&selected.inputs_data, &inputs)); - assert!(unsorted_eq(&selected.transaction.outputs(), &outputs)); + assert!(unsorted_eq(selected.transaction.outputs(), &outputs)); } diff --git a/sdk/tests/types/block.rs b/sdk/tests/types/block.rs index 43c9265968..819b7b3398 100644 --- a/sdk/tests/types/block.rs +++ b/sdk/tests/types/block.rs @@ -153,7 +153,7 @@ fn dto_mismatch_version() { } }); let block_dto = serde_json::from_value::(block_dto_json).unwrap(); - let block_res = Block::try_from_dto_with_params(block_dto, &protocol_parameters); + let block_res = Block::try_from_dto_with_params(block_dto, protocol_parameters); assert_eq!( block_res, @@ -196,7 +196,7 @@ fn dto_mismatch_network_id() { } }); let block_dto = serde_json::from_value::(block_dto_json).unwrap(); - let block_res = Block::try_from_dto_with_params(block_dto, &protocol_parameters); + let block_res = Block::try_from_dto_with_params(block_dto, protocol_parameters); assert_eq!( block_res, diff --git a/sdk/tests/types/output/account.rs b/sdk/tests/types/output/account.rs index d5ee080729..7d441107b9 100644 --- a/sdk/tests/types/output/account.rs +++ b/sdk/tests/types/output/account.rs @@ -73,7 +73,7 @@ fn pack_unpack() { let protocol_parameters = iota_mainnet_protocol_parameters(); let output = rand_account_output(protocol_parameters.token_supply()); let bytes = output.pack_to_vec(); - let output_unpacked = AccountOutput::unpack_bytes_verified(bytes, &protocol_parameters).unwrap(); + let output_unpacked = AccountOutput::unpack_bytes_verified(bytes, protocol_parameters).unwrap(); assert_eq!(output, output_unpacked); } diff --git a/sdk/tests/types/output/basic.rs b/sdk/tests/types/output/basic.rs index 8ce4baf1b1..f24b66de14 100644 --- a/sdk/tests/types/output/basic.rs +++ b/sdk/tests/types/output/basic.rs @@ -67,7 +67,7 @@ fn pack_unpack() { let protocol_parameters = iota_mainnet_protocol_parameters(); let output = rand_basic_output(protocol_parameters.token_supply()); let bytes = output.pack_to_vec(); - let output_unpacked = BasicOutput::unpack_bytes_verified(bytes, &protocol_parameters).unwrap(); + let output_unpacked = BasicOutput::unpack_bytes_verified(bytes, protocol_parameters).unwrap(); assert_eq!(output, output_unpacked); } diff --git a/sdk/tests/types/output/foundry.rs b/sdk/tests/types/output/foundry.rs index 1057a4bf5c..ef5b56b494 100644 --- a/sdk/tests/types/output/foundry.rs +++ b/sdk/tests/types/output/foundry.rs @@ -68,7 +68,7 @@ fn pack_unpack() { let protocol_parameters = iota_mainnet_protocol_parameters(); let output = rand_foundry_output(protocol_parameters.token_supply()); let bytes = output.pack_to_vec(); - let output_unpacked = FoundryOutput::unpack_bytes_verified(bytes, &protocol_parameters).unwrap(); + let output_unpacked = FoundryOutput::unpack_bytes_verified(bytes, protocol_parameters).unwrap(); assert_eq!(output, output_unpacked); } diff --git a/sdk/tests/types/output/nft.rs b/sdk/tests/types/output/nft.rs index 2214df96fd..abff7af9d3 100644 --- a/sdk/tests/types/output/nft.rs +++ b/sdk/tests/types/output/nft.rs @@ -64,7 +64,7 @@ fn pack_unpack() { let protocol_parameters = iota_mainnet_protocol_parameters(); let output = rand_nft_output(protocol_parameters.token_supply()); let bytes = output.pack_to_vec(); - let output_unpacked = NftOutput::unpack_bytes_verified(bytes, &protocol_parameters).unwrap(); + let output_unpacked = NftOutput::unpack_bytes_verified(bytes, protocol_parameters).unwrap(); assert_eq!(output, output_unpacked); } diff --git a/sdk/tests/types/payload.rs b/sdk/tests/types/payload.rs index 82cd4f4706..a61a3c5ff2 100644 --- a/sdk/tests/types/payload.rs +++ b/sdk/tests/types/payload.rs @@ -40,7 +40,7 @@ fn transaction() { let transaction = Transaction::builder(protocol_parameters.network_id()) .with_inputs(vec![input1, input2]) .add_output(output) - .add_mana_allotment(rand_mana_allotment(&protocol_parameters)) + .add_mana_allotment(rand_mana_allotment(protocol_parameters)) .finish_with_params(protocol_parameters) .unwrap(); diff --git a/sdk/tests/types/signed_transaction_payload.rs b/sdk/tests/types/signed_transaction_payload.rs index e452ece43d..d2e700183a 100644 --- a/sdk/tests/types/signed_transaction_payload.rs +++ b/sdk/tests/types/signed_transaction_payload.rs @@ -47,7 +47,7 @@ fn builder_too_few_unlocks() { let transaction = Transaction::builder(protocol_parameters.network_id()) .with_inputs([input1, input2]) .add_output(output) - .add_mana_allotment(rand_mana_allotment(&protocol_parameters)) + .add_mana_allotment(rand_mana_allotment(protocol_parameters)) .finish_with_params(protocol_parameters) .unwrap(); @@ -83,7 +83,7 @@ fn builder_too_many_unlocks() { let transaction = Transaction::builder(protocol_parameters.network_id()) .add_input(input1) .add_output(output) - .add_mana_allotment(rand_mana_allotment(&protocol_parameters)) + .add_mana_allotment(rand_mana_allotment(protocol_parameters)) .finish_with_params(protocol_parameters) .unwrap(); @@ -122,7 +122,7 @@ fn pack_unpack_valid() { let transaction = Transaction::builder(protocol_parameters.network_id()) .with_inputs([input1, input2]) .add_output(output) - .add_mana_allotment(rand_mana_allotment(&protocol_parameters)) + .add_mana_allotment(rand_mana_allotment(protocol_parameters)) .finish_with_params(protocol_parameters) .unwrap(); @@ -163,7 +163,7 @@ fn getters() { let transaction = Transaction::builder(protocol_parameters.network_id()) .with_inputs([input1, input2]) .add_output(output) - .add_mana_allotment(rand_mana_allotment(&protocol_parameters)) + .add_mana_allotment(rand_mana_allotment(protocol_parameters)) .finish_with_params(protocol_parameters) .unwrap(); From 85d30ef4588dbff82dd58960a243a531efaf6199 Mon Sep 17 00:00:00 2001 From: /alex/ Date: Thu, 7 Mar 2024 16:24:09 +0100 Subject: [PATCH 20/21] Remove ed25519 address generation from wallet (#1586) * rm ed25519 address generation from wallet * rename fn * rm with_address * update bindings * handle builder address cases * unused import * re-add address to wallet options * fix tests * re-enable placeholder test * move tests * nodejs: address to wallet options * revert going through json file for address * revert unrelated change * Update sdk/tests/client/secret_manager/address_generation.rs Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> * recipient Co-authored-by: DaughterOfMars * bad wildcards * PR suggestion * import * example doc Co-authored-by: DaughterOfMars * fix example * rename generate methods * Core: add SecretManager:GenerateEd25519AddressAsBech32 method * update examples and tests * Core: simplify SecretManager:GenerateEd25519AddressAsBech32 method * Python: update binding * nit * core: undo rename; update docs * add todo * Python: nits * NodeJs: add binding method * nit * sdk: undo rename; format * core: import * NodeJs: method suffix * fix test * nit * remove single address generation binding * Python: small fix --------- Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> Co-authored-by: Thibault Martinez Co-authored-by: DaughterOfMars Co-authored-by: Thibault Martinez --- bindings/core/src/method/secret_manager.rs | 2 +- bindings/core/src/method/wallet.rs | 17 -- bindings/core/src/method_handler/wallet.rs | 22 +- .../examples/how_tos/wallet/create-wallet.ts | 11 - .../nodejs/examples/wallet/getting-started.ts | 11 - .../lib/secret_manager/secret-manager.ts | 5 +- .../client/generate-addresses-options.ts | 2 + bindings/nodejs/src/wallet.rs | 2 +- .../secret_manager/secret_manager.spec.ts | 48 ++++ .../iota_sdk/secret_manager/secret_manager.py | 55 ++++- bindings/python/src/wallet.rs | 2 +- bindings/python/tests/test_secret_manager.py | 24 ++ ...eration_test.py => test_wallet_address.py} | 8 +- bindings/wasm/src/wallet.rs | 2 +- cli/src/cli.rs | 2 +- cli/src/wallet_cli/mod.rs | 2 +- sdk/examples/client/02_address_balance.rs | 14 +- sdk/examples/wallet/ledger_nano.rs | 18 +- sdk/examples/wallet/logger.rs | 13 +- .../offline_signing/0_generate_address.rs | 11 +- .../offline_signing/1_prepare_transaction.rs | 9 +- sdk/examples/wallet/spammer.rs | 10 - sdk/src/client/api/address.rs | 59 ++++- sdk/src/wallet/core/builder.rs | 12 +- sdk/src/wallet/core/mod.rs | 8 +- .../core/operations/address_generation.rs | 93 -------- sdk/src/wallet/core/operations/mod.rs | 1 - sdk/src/wallet/operations/block.rs | 2 +- sdk/tests/client/common/constants.rs | 2 + sdk/tests/client/common/mod.rs | 14 +- .../secret_manager/address_generation.rs | 111 ++++++++++ sdk/tests/client/secret_manager/mnemonic.rs | 16 +- sdk/tests/client/secret_manager/mod.rs | 1 + sdk/tests/client/secret_manager/stronghold.rs | 22 +- sdk/tests/wallet/address_generation.rs | 209 ------------------ sdk/tests/wallet/common/mod.rs | 2 +- sdk/tests/wallet/mod.rs | 1 - 37 files changed, 380 insertions(+), 463 deletions(-) create mode 100644 bindings/nodejs/tests/secret_manager/secret_manager.spec.ts create mode 100644 bindings/python/tests/test_secret_manager.py rename bindings/python/tests/{address_generation_test.py => test_wallet_address.py} (91%) delete mode 100644 sdk/src/wallet/core/operations/address_generation.rs create mode 100644 sdk/tests/client/secret_manager/address_generation.rs delete mode 100644 sdk/tests/wallet/address_generation.rs diff --git a/bindings/core/src/method/secret_manager.rs b/bindings/core/src/method/secret_manager.rs index 2a13acbca0..4cb40cf985 100644 --- a/bindings/core/src/method/secret_manager.rs +++ b/bindings/core/src/method/secret_manager.rs @@ -19,7 +19,7 @@ use crate::OmittedDebug; #[serde(tag = "name", content = "data", rename_all = "camelCase")] #[non_exhaustive] pub enum SecretManagerMethod { - /// Generate Ed25519 addresses. + /// Generate multiple Ed25519 addresses at once. GenerateEd25519Addresses { /// Addresses generation options options: GetAddressesOptions, diff --git a/bindings/core/src/method/wallet.rs b/bindings/core/src/method/wallet.rs index e5d1bad209..1522beb9a8 100644 --- a/bindings/core/src/method/wallet.rs +++ b/bindings/core/src/method/wallet.rs @@ -19,7 +19,6 @@ use iota_sdk::{ client::{ api::{transaction_builder::Burn, PreparedTransactionDataDto, SignedTransactionDataDto}, node_manager::node::NodeAuth, - secret::GenerateAddressOptions, }, types::block::{ address::Hrp, @@ -456,27 +455,11 @@ pub enum WalletMethod { /// Expected response: [`OutputsData`](crate::Response::OutputsData) #[serde(rename_all = "camelCase")] UnspentOutputs { filter_options: Option }, - /// Emits an event for testing if the event system is working /// Expected response: [`Ok`](crate::Response::Ok) #[cfg(feature = "events")] #[cfg_attr(docsrs, doc(cfg(feature = "events")))] EmitTestEvent { event: WalletEvent }, - - // TODO: reconsider whether to have the following methods on the wallet - /// Generate an address without storing it - /// Expected response: [`Bech32Address`](crate::Response::Bech32Address) - #[serde(rename_all = "camelCase")] - GenerateEd25519Address { - /// Account index - account_index: u32, - /// Account index - address_index: u32, - /// Options - options: Option, - /// Bech32 HRP - bech32_hrp: Option, - }, /// Get the ledger nano status /// Expected response: [`LedgerNanoStatus`](crate::Response::LedgerNanoStatus) #[cfg(feature = "ledger_nano")] diff --git a/bindings/core/src/method_handler/wallet.rs b/bindings/core/src/method_handler/wallet.rs index a4fbb1f626..cc7086f978 100644 --- a/bindings/core/src/method_handler/wallet.rs +++ b/bindings/core/src/method_handler/wallet.rs @@ -6,10 +6,7 @@ use std::time::Duration; use crypto::signatures::ed25519::PublicKey; use iota_sdk::{ client::api::{PreparedTransactionData, SignedTransactionData, SignedTransactionDataDto}, - types::{ - block::{address::ToBech32Ext, output::feature::BlockIssuerKeySource}, - TryFromDto, - }, + types::{block::output::feature::BlockIssuerKeySource, TryFromDto}, wallet::{types::TransactionWithMetadataDto, Wallet}, }; @@ -73,23 +70,6 @@ pub(crate) async fn call_wallet_method_internal( let ledger_nano_status = wallet.get_ledger_nano_status().await?; Response::LedgerNanoStatus(ledger_nano_status) } - WalletMethod::GenerateEd25519Address { - account_index, - address_index, - options, - bech32_hrp, - } => { - let address = wallet - .generate_ed25519_address(account_index, address_index, options) - .await?; - - let bech32_hrp = match bech32_hrp { - Some(bech32_hrp) => bech32_hrp, - None => *wallet.address().await.hrp(), - }; - - Response::Bech32Address(address.to_bech32(bech32_hrp)) - } #[cfg(feature = "stronghold")] WalletMethod::SetStrongholdPassword { password } => { wallet.set_stronghold_password(password).await?; diff --git a/bindings/nodejs/examples/how_tos/wallet/create-wallet.ts b/bindings/nodejs/examples/how_tos/wallet/create-wallet.ts index 882caa0736..1609378d19 100644 --- a/bindings/nodejs/examples/how_tos/wallet/create-wallet.ts +++ b/bindings/nodejs/examples/how_tos/wallet/create-wallet.ts @@ -44,18 +44,7 @@ async function run() { // The mnemonic can't be retrieved from the Stronghold file, so make a backup in a secure place! await secretManager.storeMnemonic(process.env.MNEMONIC as string); - const walletAddress = await secretManager.generateEd25519Addresses({ - coinType: CoinType.IOTA, - accountIndex: 0, - range: { - start: 0, - end: 1, - }, - bech32Hrp: 'tst', - }); - const walletOptions: WalletOptions = { - address: walletAddress[0], storagePath: process.env.WALLET_DB_PATH, clientOptions: { nodes: [process.env.NODE_URL as string], diff --git a/bindings/nodejs/examples/wallet/getting-started.ts b/bindings/nodejs/examples/wallet/getting-started.ts index 6d08c99e2d..95d119ce79 100644 --- a/bindings/nodejs/examples/wallet/getting-started.ts +++ b/bindings/nodejs/examples/wallet/getting-started.ts @@ -44,18 +44,7 @@ async function run() { // The mnemonic can't be retrieved from the Stronghold file, so make a backup in a secure place! await secretManager.storeMnemonic(mnemonic); - const wallet_address = await secretManager.generateEd25519Addresses({ - coinType: CoinType.IOTA, - accountIndex: 0, - range: { - start: 0, - end: 1, - }, - bech32Hrp: 'tst', - }); - const walletOptions: WalletOptions = { - address: wallet_address[0], storagePath: WALLET_DB_PATH, clientOptions: { nodes: [NODE_URL as string], diff --git a/bindings/nodejs/lib/secret_manager/secret-manager.ts b/bindings/nodejs/lib/secret_manager/secret-manager.ts index 76747845b8..3f65aa864d 100644 --- a/bindings/nodejs/lib/secret_manager/secret-manager.ts +++ b/bindings/nodejs/lib/secret_manager/secret-manager.ts @@ -21,6 +21,7 @@ import { UnsignedBlock, Block, parseBlock, + Bech32Address, } from '../types'; import { plainToInstance } from 'class-transformer'; @@ -44,14 +45,14 @@ export class SecretManager { } /** - * Generate Ed25519 addresses. + * Generate multiple Ed25519 addresses at once. * * @param generateAddressesOptions Options to generate addresses. * @returns An array of generated addresses. */ async generateEd25519Addresses( generateAddressesOptions: GenerateAddressesOptions, - ): Promise { + ): Promise { const response = await this.methodHandler.callMethod({ name: 'generateEd25519Addresses', data: { diff --git a/bindings/nodejs/lib/types/client/generate-addresses-options.ts b/bindings/nodejs/lib/types/client/generate-addresses-options.ts index a66e7ec431..342baf257e 100644 --- a/bindings/nodejs/lib/types/client/generate-addresses-options.ts +++ b/bindings/nodejs/lib/types/client/generate-addresses-options.ts @@ -3,6 +3,8 @@ import type { CoinType } from './constants'; import type { Range } from './range'; +// TODO: Rename (to GetAddressOptions) and refactor (move out range field), +// so we can use it for the single address generation method as well? /** * Input options for GenerateAddresses */ diff --git a/bindings/nodejs/src/wallet.rs b/bindings/nodejs/src/wallet.rs index 4d26911d2c..e7f7a0a5b1 100644 --- a/bindings/nodejs/src/wallet.rs +++ b/bindings/nodejs/src/wallet.rs @@ -121,7 +121,7 @@ pub async fn get_client(wallet: External) -> Result) -> Result> { if let Some(wallet) = &**wallet.as_ref().read().await { - Ok(External::new(wallet.get_secret_manager().clone())) + Ok(External::new(wallet.secret_manager().clone())) } else { Err(destroyed_err("Wallet")) } diff --git a/bindings/nodejs/tests/secret_manager/secret_manager.spec.ts b/bindings/nodejs/tests/secret_manager/secret_manager.spec.ts new file mode 100644 index 0000000000..98a4a00336 --- /dev/null +++ b/bindings/nodejs/tests/secret_manager/secret_manager.spec.ts @@ -0,0 +1,48 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import 'reflect-metadata'; + +import { describe, it, expect } from '@jest/globals'; +import { + CoinType, + SecretManager, + Utils, +} from '../../lib/'; + +describe('SecretManager', () => { + it('generate IOTA Ed25519 address', async () => { + const mnemonicSecretManager = { + mnemonic: "acoustic trophy damage hint search taste love bicycle foster cradle brown govern endless depend situate athlete pudding blame question genius transfer van random vast" + }; + + let bech32_hrp = Utils.iotaMainnetProtocolParameters().bech32Hrp; + + const secretManager = SecretManager.create(mnemonicSecretManager); + const addresses = await secretManager.generateEd25519Addresses({ + coinType: CoinType.IOTA, + bech32Hrp: bech32_hrp, + }); + + expect(addresses[0]).toEqual('iota1qpg2xkj66wwgn8p2ggnp7p582gj8g6p79us5hve2tsudzpsr2ap4skprwjg'); + + }, 20000); + + it('generate Shimmer Ed25519 address', async () => { + const mnemonicSecretManager = { + mnemonic: "acoustic trophy damage hint search taste love bicycle foster cradle brown govern endless depend situate athlete pudding blame question genius transfer van random vast" + }; + + let bech32_hrp = Utils.shimmerMainnetProtocolParameters().bech32Hrp; + + const secretManager = SecretManager.create(mnemonicSecretManager); + const addresses = await secretManager.generateEd25519Addresses({ + coinType: CoinType.Shimmer, + bech32Hrp: bech32_hrp, + range: { start: 0, end: 1 }, + }); + + expect(addresses[0]).toEqual('smr1qzev36lk0gzld0k28fd2fauz26qqzh4hd4cwymlqlv96x7phjxcw6ckj80y'); + + }, 20000); +}); diff --git a/bindings/python/iota_sdk/secret_manager/secret_manager.py b/bindings/python/iota_sdk/secret_manager/secret_manager.py index d78b807206..36c0b5901b 100644 --- a/bindings/python/iota_sdk/secret_manager/secret_manager.py +++ b/bindings/python/iota_sdk/secret_manager/secret_manager.py @@ -125,24 +125,69 @@ def _call_method(self, name, data=None): return json_response['payload'] return response + # TODO: Should we include `bech32` in the method name? + def generate_ed25519_address(self, + coin_type: int, + bech32_hrp: str, + account_index: Optional[int] = None, + address_index: Optional[int] = None, + internal: Optional[bool] = None, + legder_nano_prompt: Optional[bool] = None): + """Generate a single Ed25519 address. + + Args: + coin_type: The coin type to generate the address for. + bech32_hrp: The bech32 HRP (human readable part) to use. + account_index: An account index. + address_index: An address index. + internal: Whether the generated address should be internal. + ledger_nano_prompt: Whether to display the address on Ledger Nano devices. + + Returns: + The generated Ed25519 address. + """ + + options = {} + options['coinType'] = coin_type + options['bech32Hrp'] = bech32_hrp + if address_index is not None: + options['range'] = {} + options['range']['start'] = address_index + options['range']['end'] = address_index + 1 + if account_index is not None: + options['accountIndex'] = account_index + if internal is not None or legder_nano_prompt is not None: + options['options'] = {} + if internal is not None: + options['options']['internal'] = internal + if legder_nano_prompt is not None: + options['options']['ledgerNanoPrompot'] = legder_nano_prompt + + return self._call_method('generateEd25519Addresses', { + 'options': options + })[0] + # pylint: disable=unused-argument + + # TODO: Should `coin_type` and `bech32_hrp` be mandatory to provide? + # TODO: Should we include `bech32` in the method name? def generate_ed25519_addresses(self, + coin_type: Optional[int] = None, + bech32_hrp: Optional[str] = None, account_index: Optional[int] = None, start: Optional[int] = None, end: Optional[int] = None, internal: Optional[bool] = None, - coin_type: Optional[int] = None, - bech32_hrp: Optional[str] = None, ledger_nano_prompt: Optional[bool] = None): - """Generate Ed25519 addresses. + """Generate multiple Ed25519 addresses at once. Args: + coin_type: The coin type to generate addresses for. + bech32_hrp: The bech32 HRP (human readable part) to use. account_index: An account index. start: The start index of the addresses to generate. end: The end index of the addresses to generate. internal: Whether the generated addresses should be internal. - coin_type: The coin type to generate addresses for. - bech32_hrp: The bech32 HRP (human readable part) to use. ledger_nano_prompt: Whether to display the address on Ledger Nano devices. Returns: diff --git a/bindings/python/src/wallet.rs b/bindings/python/src/wallet.rs index b42f1a2b8f..8a977c1e70 100644 --- a/bindings/python/src/wallet.rs +++ b/bindings/python/src/wallet.rs @@ -116,7 +116,7 @@ pub fn get_secret_manager_from_wallet(wallet: &Wallet) -> Result Result Result { if let Some(wallet) = &*method_handler.0.read().await { - Ok(SecretManagerMethodHandler::new(wallet.get_secret_manager().clone())) + Ok(SecretManagerMethodHandler::new(wallet.secret_manager().clone())) } else { // Notify that the wallet was destroyed Err(destroyed_err("Wallet")) diff --git a/cli/src/cli.rs b/cli/src/cli.rs index cf614cc480..d6f247346c 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -169,7 +169,7 @@ pub async fn new_wallet(cli: Cli) -> Result, Error> { if storage_path.is_dir() { match Wallet::builder().with_storage_path(storage_path).finish().await { Ok(wallet) => { - let linked_secret_manager = match &mut *wallet.get_secret_manager().write().await { + let linked_secret_manager = match &mut *wallet.secret_manager().write().await { SecretManager::Stronghold(stronghold) => { let snapshot_path = stronghold.snapshot_path().to_path_buf(); let snapshot_exists = snapshot_path.exists(); diff --git a/cli/src/wallet_cli/mod.rs b/cli/src/wallet_cli/mod.rs index 18ba7fac9b..b6c0f3aa06 100644 --- a/cli/src/wallet_cli/mod.rs +++ b/cli/src/wallet_cli/mod.rs @@ -1377,7 +1377,7 @@ pub enum PromptResponse { } async fn ensure_password(wallet: &Wallet) -> Result<(), Error> { - if matches!(*wallet.get_secret_manager().read().await, SecretManager::Stronghold(_)) + if matches!(*wallet.secret_manager().read().await, SecretManager::Stronghold(_)) && !wallet.is_stronghold_password_available().await? { let password = get_password("Stronghold password", false)?; diff --git a/sdk/examples/client/02_address_balance.rs b/sdk/examples/client/02_address_balance.rs index 148ba7eefb..b9e027b50f 100644 --- a/sdk/examples/client/02_address_balance.rs +++ b/sdk/examples/client/02_address_balance.rs @@ -11,8 +11,8 @@ use iota_sdk::{ client::{ - api::GetAddressesOptions, node_api::indexer::query_parameters::BasicOutputQueryParameters, - secret::SecretManager, Client, + api::GetAddressesOptions, constants::SHIMMER_COIN_TYPE, + node_api::indexer::query_parameters::BasicOutputQueryParameters, secret::SecretManager, Client, }, types::block::output::NativeTokensBuilder, }; @@ -36,14 +36,8 @@ async fn main() -> Result<(), Box> { // Generate the first address let first_address = secret_manager - .generate_ed25519_addresses( - GetAddressesOptions::from_client(&client) - .await? - .with_account_index(0) - .with_range(0..1), - ) - .await?[0] - .clone(); + .generate_ed25519_address(SHIMMER_COIN_TYPE, 0, 0, client.get_bech32_hrp().await?, None) + .await?; // Get output ids of outputs that can be controlled by this address without further unlock constraints let output_ids_response = client diff --git a/sdk/examples/wallet/ledger_nano.rs b/sdk/examples/wallet/ledger_nano.rs index ffd7ed0fbb..bdb9391db3 100644 --- a/sdk/examples/wallet/ledger_nano.rs +++ b/sdk/examples/wallet/ledger_nano.rs @@ -23,8 +23,6 @@ use iota_sdk::{ wallet::{ClientOptions, Wallet}, }; -// The address to send coins to -const RECV_ADDRESS: &str = "rms1qpszqzadsym6wpppd6z037dvlejmjuke7s24hm95s9fg9vpua7vluaw60xu"; // The amount of base coins we'll send const SEND_AMOUNT: u64 = 1_000_000; @@ -38,24 +36,20 @@ async fn main() -> Result<(), Box> { } let client_options = ClientOptions::new().with_node(&std::env::var("NODE_URL").unwrap())?; - let secret_manager = LedgerSecretManager::new(true); + let secret_manager = SecretManager::LedgerNano(LedgerSecretManager::new(true)); + let wallet = Wallet::builder() - .with_secret_manager(SecretManager::LedgerNano(secret_manager)) + .with_secret_manager(secret_manager) .with_storage_path(&std::env::var("WALLET_DB_PATH").unwrap()) .with_client_options(client_options) .with_bip_path(Bip44::new(SHIMMER_COIN_TYPE)) .finish() .await?; + let recv_address = wallet.address().await; + println!("recipient address: {recv_address}"); println!("{:?}", wallet.get_ledger_nano_status().await?); - println!("Generating address..."); - let now = tokio::time::Instant::now(); - let address = wallet.generate_ed25519_address(0, 0, None).await?; - println!("took: {:.2?}", now.elapsed()); - - println!("ADDRESS:\n{address:#?}"); - let now = tokio::time::Instant::now(); let balance = wallet.sync(None).await?; println!("Wallet synced in: {:.2?}", now.elapsed()); @@ -63,7 +57,7 @@ async fn main() -> Result<(), Box> { println!("Balance BEFORE:\n{:?}", balance.base_coin()); println!("Sending the coin-transfer transaction..."); - let transaction = wallet.send(SEND_AMOUNT, RECV_ADDRESS, None).await?; + let transaction = wallet.send(SEND_AMOUNT, recv_address, None).await?; println!("Transaction sent: {}", transaction.transaction_id); wallet diff --git a/sdk/examples/wallet/logger.rs b/sdk/examples/wallet/logger.rs index 9a8b877281..907a75120d 100644 --- a/sdk/examples/wallet/logger.rs +++ b/sdk/examples/wallet/logger.rs @@ -10,6 +10,7 @@ use iota_sdk::{ client::{ + api::GetAddressesOptions, constants::SHIMMER_COIN_TYPE, secret::{mnemonic::MnemonicSecretManager, SecretManager}, }, @@ -38,18 +39,20 @@ async fn main() -> Result<(), Box> { // Restore a wallet let client_options = ClientOptions::new().with_node(&std::env::var("NODE_URL").unwrap())?; + let secret_manager = SecretManager::Mnemonic(MnemonicSecretManager::try_from_mnemonic( + std::env::var("MNEMONIC").unwrap(), + )?); + let secret_manager = MnemonicSecretManager::try_from_mnemonic(std::env::var("MNEMONIC").unwrap())?; - let wallet = Wallet::builder() - .with_secret_manager(SecretManager::Mnemonic(secret_manager)) + + let wallet = Wallet::::builder() .with_storage_path(&std::env::var("WALLET_DB_PATH").unwrap()) .with_client_options(client_options) + .with_secret_manager(secret_manager) .with_bip_path(Bip44::new(SHIMMER_COIN_TYPE)) .finish() .await?; - println!("Generating address..."); - let _ = wallet.generate_ed25519_address(0, 0, None).await?; - println!("Syncing wallet"); wallet.sync(None).await?; diff --git a/sdk/examples/wallet/offline_signing/0_generate_address.rs b/sdk/examples/wallet/offline_signing/0_generate_address.rs index 35689871e3..c8f6d21b19 100644 --- a/sdk/examples/wallet/offline_signing/0_generate_address.rs +++ b/sdk/examples/wallet/offline_signing/0_generate_address.rs @@ -14,6 +14,7 @@ use iota_sdk::{ secret::{stronghold::StrongholdSecretManager, SecretManager}, }, crypto::keys::{bip39::Mnemonic, bip44::Bip44}, + types::block::address::Bech32Address, wallet::{ClientOptions, Wallet}, }; @@ -50,17 +51,17 @@ async fn main() -> Result<(), Box> { .with_bip_path(Bip44::new(SHIMMER_COIN_TYPE)) .finish() .await?; - println!("Generated a new wallet"); - write_wallet_address_to_file(&wallet).await + write_wallet_address_to_file(&wallet.address().await).await?; + + Ok(()) } -async fn write_wallet_address_to_file(wallet: &Wallet) -> Result<(), Box> { +async fn write_wallet_address_to_file(address: &Bech32Address) -> Result<(), Box> { use tokio::io::AsyncWriteExt; - let wallet_address = wallet.address().await; - let json = serde_json::to_string_pretty(&wallet_address)?; + let json = serde_json::to_string_pretty(address)?; let mut file = tokio::io::BufWriter::new(tokio::fs::File::create(ADDRESS_FILE_PATH).await?); println!("example.address.json:\n{json}"); file.write_all(json.as_bytes()).await?; diff --git a/sdk/examples/wallet/offline_signing/1_prepare_transaction.rs b/sdk/examples/wallet/offline_signing/1_prepare_transaction.rs index ca0df7dc2c..93c374fc96 100644 --- a/sdk/examples/wallet/offline_signing/1_prepare_transaction.rs +++ b/sdk/examples/wallet/offline_signing/1_prepare_transaction.rs @@ -9,16 +9,19 @@ //! ``` use iota_sdk::{ - client::{api::PreparedTransactionDataDto, constants::SHIMMER_COIN_TYPE, secret::SecretManager}, + client::{ + api::PreparedTransactionDataDto, constants::SHIMMER_COIN_TYPE, secret::SecretManager, + stronghold::StrongholdAdapter, + }, crypto::keys::bip44::Bip44, types::block::address::Bech32Address, wallet::{ClientOptions, SendParams, Wallet}, }; const ONLINE_WALLET_DB_PATH: &str = "./examples/wallet/offline_signing/example-online-walletdb"; -const ADDRESS_FILE_PATH: &str = "./examples/wallet/offline_signing/example.address.json"; const PREPARED_TRANSACTION_FILE_PATH: &str = "./examples/wallet/offline_signing/example.prepared_transaction.json"; const PROTOCOL_PARAMETERS_FILE_PATH: &str = "./examples/wallet/offline_signing/example.protocol_parameters.json"; +const ADDRESS_FILE_PATH: &str = "./examples/wallet/offline_signing/example.address.json"; // Address to which we want to send the amount. const RECV_ADDRESS: &str = "rms1qpszqzadsym6wpppd6z037dvlejmjuke7s24hm95s9fg9vpua7vluaw60xu"; // The amount to send. @@ -35,7 +38,7 @@ async fn main() -> Result<(), Box> { let params = [SendParams::new(SEND_AMOUNT, RECV_ADDRESS)?]; - // Recovers addresses from example `0_address_generation`. + // Recovers the address generated by the example `0_generate_address.rs`. let address = read_address_from_file().await?; let client_options = ClientOptions::new().with_node(&std::env::var("NODE_URL").unwrap())?; diff --git a/sdk/examples/wallet/spammer.rs b/sdk/examples/wallet/spammer.rs index e9e24818fd..52786a6f73 100644 --- a/sdk/examples/wallet/spammer.rs +++ b/sdk/examples/wallet/spammer.rs @@ -48,19 +48,9 @@ async fn main() -> Result<(), Box> { let secret_manager = MnemonicSecretManager::try_from_mnemonic(std::env::var("MNEMONIC").unwrap())?; let bip_path = Bip44::new(SHIMMER_COIN_TYPE); - let address = Bech32Address::new( - Hrp::from_str_unchecked("smr"), - Address::from( - secret_manager - .generate_ed25519_addresses(bip_path.coin_type, bip_path.account, 0..1, None) - .await?[0], - ), - ); - let wallet = Wallet::builder() .with_secret_manager(SecretManager::Mnemonic(secret_manager)) .with_client_options(client_options) - .with_address(address) .with_bip_path(bip_path) .finish() .await?; diff --git a/sdk/src/client/api/address.rs b/sdk/src/client/api/address.rs index a341dc1a26..d8a70a96b4 100644 --- a/sdk/src/client/api/address.rs +++ b/sdk/src/client/api/address.rs @@ -16,6 +16,9 @@ use crate::{ utils::ConvertTo, }; +// TODO: Should we rename ths struct to `GetAddressOptions`, thereby moving out the `range` field, so +// it can be used by `GenerateEd25519Address` and `GenerateEd25519Addresses`? Do we even still need +// the latter? #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] #[serde(default)] @@ -33,6 +36,9 @@ pub struct GetAddressesOptions { } impl GetAddressesOptions { + // TODO: can we remove this function? It's not clear from the outside that it's just the default + // with a requested HRP. I think the caller can just do what this function does. Also ... with this + // we do several API requests unnecessarily since oftentimes we could just re-use the HRP. pub async fn from_client(client: &Client) -> Result { Ok(Self::default().with_bech32_hrp(client.get_bech32_hrp().await?)) } @@ -97,7 +103,34 @@ impl Default for GetAddressesOptions { } impl SecretManager { - /// Get a vector of public bech32 addresses + // TODO: while `SecretManage::generate...` returns `Ed25519Address`, `SecretManager` + // converts those to `Bech32Address`es, hence, should we add `bech32` to its method name + // to make that the difference clear? + // TODO: make `account_index` and `address_index` impl Into>? + /// Generates a Bech32 formatted Ed25519 address. + pub async fn generate_ed25519_address( + &self, + coin_type: u32, + account_index: u32, + address_index: u32, + bech32_hrp: impl ConvertTo, + options: impl Into> + Send, + ) -> Result { + let hrp: Hrp = bech32_hrp.convert()?; + Ok(SecretManage::generate_ed25519_addresses( + self, + coin_type, + account_index, + address_index..address_index + 1, + options, + ) + // Panic: if the secret manager hasn't failed then there must be an address. + .await?[0] + .to_bech32(hrp)) + } + + // TODO: Same as for `generate_ed25519_address`. + /// Generates a vector of Bech32 formatted Ed25519 addresses. pub async fn generate_ed25519_addresses( &self, GetAddressesOptions { @@ -117,7 +150,29 @@ impl SecretManager { ) } - /// Get a vector of EVM address strings + /// Generates a single EVM address hex string. + pub async fn generate_evm_address( + &self, + coin_type: u32, + account_index: u32, + address_index: u32, + options: impl Into> + Send, + ) -> Result { + Ok(prefix_hex::encode( + SecretManage::generate_evm_addresses( + self, + coin_type, + account_index, + address_index..address_index + 1, + options, + ) + // Panic: if the secret manager hasn't failed then there must be an address. + .await?[0] + .as_ref(), + )) + } + + /// Generates a vector of EVM address hex strings. pub async fn generate_evm_addresses( &self, GetAddressesOptions { diff --git a/sdk/src/wallet/core/builder.rs b/sdk/src/wallet/core/builder.rs index ec36ec2c6c..7848733294 100644 --- a/sdk/src/wallet/core/builder.rs +++ b/sdk/src/wallet/core/builder.rs @@ -170,11 +170,12 @@ where // May use a previously stored secret manager if it wasn't provided if self.secret_manager.is_none() { - let secret_manager = loaded_wallet_builder - .as_ref() - .and_then(|builder| builder.secret_manager.clone()); - - self.secret_manager = secret_manager; + self.secret_manager.replace( + loaded_wallet_builder + .as_ref() + .and_then(|builder| builder.secret_manager.clone()) + .ok_or(WalletError::MissingParameter("secret_manager"))?, + ); } let mut verify_address = false; @@ -277,6 +278,7 @@ where last_synced: Mutex::new(0), background_syncing_status, client, + // TODO: make secret manager optional secret_manager: self.secret_manager.expect("make WalletInner::secret_manager optional?"), #[cfg(feature = "events")] event_emitter: tokio::sync::RwLock::new(EventEmitter::new()), diff --git a/sdk/src/wallet/core/mod.rs b/sdk/src/wallet/core/mod.rs index f6bef8d88a..6e7a8e30cd 100644 --- a/sdk/src/wallet/core/mod.rs +++ b/sdk/src/wallet/core/mod.rs @@ -427,12 +427,12 @@ impl Wallet { } impl WalletInner { - /// Get the [SecretManager] - pub fn get_secret_manager(&self) -> &Arc> { + /// Get the [`SecretManager`] of the wallet. + pub fn secret_manager(&self) -> &Arc> { &self.secret_manager } - /// Listen to wallet events, empty vec will listen to all events + /// Listen to wallet events, empty vec will listen to all events. #[cfg(feature = "events")] #[cfg_attr(docsrs, doc(cfg(feature = "events")))] pub async fn listen + Send>(&self, events: I, handler: F) @@ -444,7 +444,7 @@ impl WalletInner { emitter.on(events, handler); } - /// Remove wallet event listeners, empty vec will remove all listeners + /// Remove wallet event listeners, empty vec will remove all listeners. #[cfg(feature = "events")] #[cfg_attr(docsrs, doc(cfg(feature = "events")))] pub async fn clear_listeners + Send>(&self, events: I) diff --git a/sdk/src/wallet/core/operations/address_generation.rs b/sdk/src/wallet/core/operations/address_generation.rs deleted file mode 100644 index e950efe78b..0000000000 --- a/sdk/src/wallet/core/operations/address_generation.rs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - client::{ - secret::{GenerateAddressOptions, SecretManage, SecretManager}, - ClientError, - }, - types::block::address::Ed25519Address, - wallet::{Wallet, WalletError}, -}; -#[cfg(all(feature = "events", feature = "ledger_nano"))] -use crate::{ - types::block::address::ToBech32Ext, - wallet::events::types::{AddressData, WalletEvent}, -}; - -impl Wallet { - /// Generate an address without storing it - /// ```ignore - /// let public_addresses = wallet - /// .generate_ed25519_address(None) - /// .await?; - /// ``` - pub async fn generate_ed25519_address( - &self, - account_index: u32, - address_index: u32, - options: impl Into> + Send, - ) -> Result { - // TODO: not sure yet whether we also should allow this method to generate addresses for different bip - // paths. - let coin_type = self.bip_path().await.ok_or(WalletError::MissingBipPath)?.coin_type; - - let address = match &*self.secret_manager.read().await { - #[cfg(feature = "ledger_nano")] - SecretManager::LedgerNano(ledger_nano) => { - // If we don't sync, then we want to display the prompt on the ledger with the address. But the user - // needs to have it visible on the computer first, so we need to generate it without the - // prompt first - let options = options.into(); - #[cfg(feature = "events")] - if options.as_ref().map_or(false, |o| o.ledger_nano_prompt) { - let changed_options = options.map(|mut options| { - // Change options so ledger will not show the prompt the first time - options.ledger_nano_prompt = false; - options - }); - // Generate without prompt to be able to display it - let address = ledger_nano - .generate_ed25519_addresses( - coin_type, - account_index, - address_index..address_index + 1, - changed_options, - ) - .await?; - - let bech32_hrp = self.bech32_hrp().await; - - self.emit(WalletEvent::LedgerAddressGeneration(AddressData { - address: address[0].to_bech32(bech32_hrp), - })) - .await; - } - // Generate with prompt so the user can verify - ledger_nano - .generate_ed25519_addresses(coin_type, account_index, address_index..address_index + 1, options) - .await? - } - #[cfg(feature = "stronghold")] - SecretManager::Stronghold(stronghold) => { - stronghold - .generate_ed25519_addresses(coin_type, account_index, address_index..address_index + 1, options) - .await? - } - SecretManager::Mnemonic(mnemonic) => { - mnemonic - .generate_ed25519_addresses(coin_type, account_index, address_index..address_index + 1, options) - .await? - } - #[cfg(feature = "private_key_secret_manager")] - SecretManager::PrivateKey(private_key) => { - private_key - .generate_ed25519_addresses(coin_type, account_index, address_index..address_index + 1, options) - .await? - } - SecretManager::Placeholder => return Err(ClientError::PlaceholderSecretManager.into()), - }; - - Ok(*address.first().ok_or(WalletError::MissingParameter("address"))?) - } -} diff --git a/sdk/src/wallet/core/operations/mod.rs b/sdk/src/wallet/core/operations/mod.rs index e01ca173a6..058c1e84dc 100644 --- a/sdk/src/wallet/core/operations/mod.rs +++ b/sdk/src/wallet/core/operations/mod.rs @@ -1,7 +1,6 @@ // Copyright 2021 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -pub(crate) mod address_generation; pub(crate) mod background_syncing; pub(crate) mod client; #[cfg(feature = "ledger_nano")] diff --git a/sdk/src/wallet/operations/block.rs b/sdk/src/wallet/operations/block.rs index bf3ea48ce2..afdfb18240 100644 --- a/sdk/src/wallet/operations/block.rs +++ b/sdk/src/wallet/operations/block.rs @@ -56,7 +56,7 @@ where let block = unsigned_block .sign_ed25519( - &*self.get_secret_manager().read().await, + &*self.secret_manager().read().await, self.bip_path().await.ok_or(WalletError::MissingBipPath)?, ) .await?; diff --git a/sdk/tests/client/common/constants.rs b/sdk/tests/client/common/constants.rs index 7f7a02fb2b..c06eb0d4e6 100644 --- a/sdk/tests/client/common/constants.rs +++ b/sdk/tests/client/common/constants.rs @@ -4,3 +4,5 @@ pub static NODE_LOCAL: &str = "http://localhost:8050"; pub static FAUCET_URL: &str = "http://localhost:8091/api/enqueue"; + +pub static DEFAULT_MNEMONIC: &str = "inhale gorilla deny three celery song category owner lottery rent author wealth penalty crawl hobby obtain glad warm early rain clutch slab august bleak"; diff --git a/sdk/tests/client/common/mod.rs b/sdk/tests/client/common/mod.rs index ef018ba902..5e249dca6c 100644 --- a/sdk/tests/client/common/mod.rs +++ b/sdk/tests/client/common/mod.rs @@ -10,7 +10,7 @@ mod constants; // }; use iota_sdk::client::Client; -pub use self::constants::{FAUCET_URL, NODE_LOCAL}; +pub use self::constants::{DEFAULT_MNEMONIC, FAUCET_URL, NODE_LOCAL}; /// Sets up a Client with node health ignored. pub async fn setup_client_with_node_health_ignored() -> Client { @@ -62,3 +62,15 @@ pub async fn setup_client_with_node_health_ignored() -> Client { // } // panic!("Faucet no longer wants to hand over coins"); // } + +#[allow(dead_code)] +pub(crate) fn setup(path: &str) -> Result<(), Box> { + // Ignore error in case the path didn't exist yet. + std::fs::remove_dir_all(path).ok(); + Ok(()) +} + +#[allow(dead_code)] +pub(crate) fn tear_down(path: &str) -> Result<(), Box> { + Ok(std::fs::remove_dir_all(path)?) +} diff --git a/sdk/tests/client/secret_manager/address_generation.rs b/sdk/tests/client/secret_manager/address_generation.rs new file mode 100644 index 0000000000..7ff9584cce --- /dev/null +++ b/sdk/tests/client/secret_manager/address_generation.rs @@ -0,0 +1,111 @@ +// Copyright 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(feature = "stronghold")] +use crypto::keys::bip39::Mnemonic; +use crypto::keys::bip44::Bip44; +#[cfg(feature = "stronghold")] +use iota_sdk::client::secret::stronghold::StrongholdSecretManager; +#[cfg(feature = "ledger_nano")] +use iota_sdk::client::secret::{ledger_nano::LedgerSecretManager, GenerateAddressOptions}; +#[cfg(feature = "events")] +use iota_sdk::wallet::events::{WalletEvent, WalletEventType}; +use iota_sdk::{ + client::{ + api::GetAddressesOptions, + constants::{IOTA_COIN_TYPE, SHIMMER_COIN_TYPE}, + secret::{mnemonic::MnemonicSecretManager, SecretManager}, + ClientError, + }, + types::block::address::{Hrp, ToBech32Ext}, + wallet::{ClientOptions, Wallet}, +}; +use pretty_assertions::assert_eq; + +use crate::client::common::{setup, tear_down, DEFAULT_MNEMONIC, NODE_LOCAL}; + +#[tokio::test] +async fn address_generation_mnemonic() -> Result<(), Box> { + let secret_manager = + SecretManager::Mnemonic(MnemonicSecretManager::try_from_mnemonic(DEFAULT_MNEMONIC.to_owned())?); + + let address = secret_manager + .generate_ed25519_address(IOTA_COIN_TYPE, 0, 0, "smr", None) + .await?; + + assert_eq!( + address, + // Address generated with bip32 path: [44, 4218, 0, 0, 0]. + "smr1qrpwecegav7eh0z363ca69laxej64rrt4e3u0rtycyuh0mam3vq3ulygj9p" + ); + + Ok(()) +} + +#[cfg(feature = "stronghold")] +#[tokio::test] +async fn address_generation_stronghold() -> Result<(), Box> { + let storage_path = "test-storage/address_generation_stronghold"; + setup(storage_path)?; + + iota_stronghold::engine::snapshot::try_set_encrypt_work_factor(0).unwrap(); + + let secret_manager = StrongholdSecretManager::builder() + .password("some_hopefully_secure_password".to_owned()) + .build(format!("{}/test.stronghold", storage_path))?; + + secret_manager + .store_mnemonic(Mnemonic::from(DEFAULT_MNEMONIC.to_string())) + .await?; + + let secret_manager = SecretManager::Stronghold(secret_manager); + + let address = secret_manager + .generate_ed25519_address(IOTA_COIN_TYPE, 0, 0, "smr", None) + .await?; + + assert_eq!( + address, + // Address generated with bip32 path: [44, 4218, 0, 0, 0]. + "smr1qrpwecegav7eh0z363ca69laxej64rrt4e3u0rtycyuh0mam3vq3ulygj9p" + ); + + tear_down(storage_path) +} + +#[tokio::test] +#[cfg(feature = "ledger_nano")] +#[ignore = "requires ledger nano instance"] +async fn address_generation_ledger() -> Result<(), Box> { + let mut secret_manager = LedgerSecretManager::new(true); + secret_manager.non_interactive = true; + + let secret_manager = SecretManager::LedgerNano(secret_manager); + + let address = secret_manager + .generate_ed25519_address(IOTA_COIN_TYPE, 0, 0, "smr", None) + .await?; + + assert_eq!( + address.to_bech32_unchecked("smr"), + // Address generated with bip32 path: [44, 4218, 0, 0, 0]. + // This address was generated with a MnemonicSecretManager and the ledger simulator mnemonic. + // "glory promote mansion idle axis finger extra february uncover one trip resource lawn turtle enact monster + // seven myth punch hobby comfort wild raise skin". + "smr1qqdnv60ryxynaeyu8paq3lp9rkll7d7d92vpumz88fdj4l0pn5mruy3qdpm" + ); + + Ok(()) +} + +#[tokio::test] +async fn address_generation_placeholder() { + let secret_manager = SecretManager::Placeholder; + + assert!(matches!( + secret_manager + .generate_ed25519_address(SHIMMER_COIN_TYPE, 0, 0, "smr", None) + .await, + Err(ClientError::PlaceholderSecretManager) + )); +} diff --git a/sdk/tests/client/secret_manager/mnemonic.rs b/sdk/tests/client/secret_manager/mnemonic.rs index faca1dc980..84cf4e6cbe 100644 --- a/sdk/tests/client/secret_manager/mnemonic.rs +++ b/sdk/tests/client/secret_manager/mnemonic.rs @@ -2,7 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 use iota_sdk::client::{ - api::GetAddressesOptions, constants::SHIMMER_TESTNET_BECH32_HRP, secret::SecretManager, ClientError, + api::GetAddressesOptions, + constants::{SHIMMER_COIN_TYPE, SHIMMER_TESTNET_BECH32_HRP}, + secret::SecretManager, + ClientError, }; use pretty_assertions::assert_eq; @@ -11,18 +14,13 @@ async fn mnemonic_secret_manager() -> Result<(), ClientError> { let dto = r#"{"mnemonic": "acoustic trophy damage hint search taste love bicycle foster cradle brown govern endless depend situate athlete pudding blame question genius transfer van random vast"}"#; let secret_manager: SecretManager = dto.parse()?; - let addresses = secret_manager - .generate_ed25519_addresses( - GetAddressesOptions::default() - .with_bech32_hrp(SHIMMER_TESTNET_BECH32_HRP) - .with_account_index(0) - .with_range(0..1), - ) + let address = secret_manager + .generate_ed25519_address(SHIMMER_COIN_TYPE, 0, 0, SHIMMER_TESTNET_BECH32_HRP, None) .await .unwrap(); assert_eq!( - addresses[0], + address, "rms1qzev36lk0gzld0k28fd2fauz26qqzh4hd4cwymlqlv96x7phjxcw6v3ea5a" ); diff --git a/sdk/tests/client/secret_manager/mod.rs b/sdk/tests/client/secret_manager/mod.rs index 4e2a7988d5..4ed72f4a0b 100644 --- a/sdk/tests/client/secret_manager/mod.rs +++ b/sdk/tests/client/secret_manager/mod.rs @@ -1,6 +1,7 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +mod address_generation; mod mnemonic; #[cfg(feature = "private_key_secret_manager")] mod private_key; diff --git a/sdk/tests/client/secret_manager/stronghold.rs b/sdk/tests/client/secret_manager/stronghold.rs index 84b5f55a8c..bb087859ee 100644 --- a/sdk/tests/client/secret_manager/stronghold.rs +++ b/sdk/tests/client/secret_manager/stronghold.rs @@ -2,7 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 use iota_sdk::client::{ - api::GetAddressesOptions, constants::SHIMMER_TESTNET_BECH32_HRP, secret::SecretManager, ClientError, + api::GetAddressesOptions, + constants::{SHIMMER_COIN_TYPE, SHIMMER_TESTNET_BECH32_HRP}, + secret::SecretManager, + ClientError, }; use pretty_assertions::assert_eq; @@ -24,18 +27,13 @@ async fn stronghold_secret_manager() -> Result<(), ClientError> { panic!("expect a Stronghold secret manager, but it's not the case!"); } - let addresses = secret_manager - .generate_ed25519_addresses( - GetAddressesOptions::default() - .with_bech32_hrp(SHIMMER_TESTNET_BECH32_HRP) - .with_account_index(0) - .with_range(0..1), - ) + let address = secret_manager + .generate_ed25519_address(SHIMMER_COIN_TYPE, 0, 0, SHIMMER_TESTNET_BECH32_HRP, None) .await .unwrap(); assert_eq!( - addresses[0], + address, "rms1qzev36lk0gzld0k28fd2fauz26qqzh4hd4cwymlqlv96x7phjxcw6v3ea5a" ); @@ -64,11 +62,7 @@ async fn stronghold_mnemonic_missing() -> Result<(), ClientError> { // Generating addresses will fail because no mnemonic has been stored let error = SecretManager::Stronghold(stronghold_secret_manager) - .generate_ed25519_addresses( - GetAddressesOptions::default(), - // .with_bech32_hrp(SHIMMER_TESTNET_BECH32_HRP) - // .with_coin_type(iota_sdk::client::constants::SHIMMER_COIN_TYPE) - ) + .generate_ed25519_address(SHIMMER_COIN_TYPE, 0, 0, SHIMMER_TESTNET_BECH32_HRP, None) .await .unwrap_err(); diff --git a/sdk/tests/wallet/address_generation.rs b/sdk/tests/wallet/address_generation.rs deleted file mode 100644 index 2540e51c85..0000000000 --- a/sdk/tests/wallet/address_generation.rs +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2023 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#[cfg(feature = "stronghold")] -use crypto::keys::bip39::Mnemonic; -use crypto::keys::bip44::Bip44; -#[cfg(feature = "stronghold")] -use iota_sdk::client::secret::stronghold::StrongholdSecretManager; -#[cfg(feature = "ledger_nano")] -use iota_sdk::client::secret::{ledger_nano::LedgerSecretManager, GenerateAddressOptions}; -#[cfg(feature = "events")] -use iota_sdk::wallet::events::{WalletEvent, WalletEventType}; -use iota_sdk::{ - client::{ - constants::IOTA_COIN_TYPE, - secret::{mnemonic::MnemonicSecretManager, SecretManager}, - }, - types::block::{address::ToBech32Ext, protocol::iota_mainnet_protocol_parameters}, - wallet::{ClientOptions, Wallet}, -}; -use pretty_assertions::assert_eq; - -use crate::wallet::common::{setup, tear_down, DEFAULT_MNEMONIC, NODE_LOCAL}; - -#[tokio::test] -async fn wallet_address_generation_mnemonic() -> Result<(), Box> { - let storage_path = "test-storage/wallet_address_generation_mnemonic"; - setup(storage_path)?; - - let client_options = ClientOptions::new() - .with_node(NODE_LOCAL)? - .with_protocol_parameters(iota_mainnet_protocol_parameters().clone()); - let secret_manager = MnemonicSecretManager::try_from_mnemonic(DEFAULT_MNEMONIC.to_owned())?; - - #[allow(unused_mut)] - let mut wallet_builder = Wallet::builder() - .with_secret_manager(SecretManager::Mnemonic(secret_manager)) - .with_client_options(client_options) - .with_bip_path(Bip44::new(IOTA_COIN_TYPE)); - - #[cfg(feature = "storage")] - { - wallet_builder = wallet_builder.with_storage_path(storage_path); - } - let wallet = wallet_builder.finish().await?; - - let address = wallet.generate_ed25519_address(0, 0, None).await?; - - assert_eq!( - address.to_bech32_unchecked("smr"), - // Address generated with bip32 path: [44, 4218, 0, 0, 0]. - "smr1qrpwecegav7eh0z363ca69laxej64rrt4e3u0rtycyuh0mam3vq3ulygj9p" - ); - - tear_down(storage_path) -} - -#[cfg(feature = "stronghold")] -#[tokio::test] -async fn wallet_address_generation_stronghold() -> Result<(), Box> { - let storage_path = "test-storage/wallet_address_generation_stronghold"; - setup(storage_path)?; - - iota_stronghold::engine::snapshot::try_set_encrypt_work_factor(0).unwrap(); - - let secret_manager = StrongholdSecretManager::builder() - .password("some_hopefully_secure_password".to_owned()) - .build("test-storage/wallet_address_generation_stronghold/test.stronghold")?; - secret_manager - .store_mnemonic(Mnemonic::from(DEFAULT_MNEMONIC.to_string())) - .await?; - - let client_options = ClientOptions::new() - .with_node(NODE_LOCAL)? - .with_protocol_parameters(iota_mainnet_protocol_parameters().clone()); - #[allow(unused_mut)] - let mut wallet_builder = Wallet::builder() - .with_secret_manager(SecretManager::Stronghold(secret_manager)) - .with_client_options(client_options) - .with_bip_path(Bip44::new(IOTA_COIN_TYPE)); - #[cfg(feature = "storage")] - { - wallet_builder = wallet_builder.with_storage_path(storage_path); - } - let wallet = wallet_builder.finish().await?; - - let address = wallet.generate_ed25519_address(0, 0, None).await?; - - assert_eq!( - address.to_bech32_unchecked("smr"), - // Address generated with bip32 path: [44, 4218, 0, 0, 0]. - "smr1qrpwecegav7eh0z363ca69laxej64rrt4e3u0rtycyuh0mam3vq3ulygj9p" - ); - - tear_down(storage_path) -} - -#[tokio::test] -#[cfg(all(feature = "ledger_nano", feature = "events"))] -#[ignore = "requires ledger nano instance"] -async fn wallet_address_generation_ledger() -> Result<(), Box> { - let storage_path = "test-storage/wallet_address_generation_ledger"; - setup(storage_path)?; - - let client_options = ClientOptions::new().with_node(NODE_LOCAL)?; - let mut secret_manager = LedgerSecretManager::new(true); - secret_manager.non_interactive = true; - - #[allow(unused_mut)] - let mut wallet_builder = Wallet::builder() - .with_secret_manager(SecretManager::LedgerNano(secret_manager)) - .with_client_options(client_options) - .with_bip_path(Bip44::new(IOTA_COIN_TYPE)); - - #[cfg(feature = "storage")] - { - wallet_builder = wallet_builder.with_storage_path(storage_path); - } - let wallet = wallet_builder.finish().await?; - - let address = wallet.generate_ed25519_address(0, 0, None).await?; - - assert_eq!( - address.to_bech32_unchecked("smr"), - // Address generated with bip32 path: [44, 4218, 0, 0, 0]. - // This address was generated with a MnemonicSecretManager and the ledger simulator mnemonic. - // "glory promote mansion idle axis finger extra february uncover one trip resource lawn turtle enact monster - // seven myth punch hobby comfort wild raise skin". - "smr1qqdnv60ryxynaeyu8paq3lp9rkll7d7d92vpumz88fdj4l0pn5mruy3qdpm" - ); - - let (sender, mut receiver) = tokio::sync::mpsc::channel(1); - - wallet - .listen([WalletEventType::LedgerAddressGeneration], move |event| { - if let WalletEvent::LedgerAddressGeneration(address) = event { - sender - .try_send(address.address.clone()) - .expect("too many LedgerAddressGeneration events"); - } else { - panic!("expected LedgerAddressGeneration event") - } - }) - .await; - - let address = wallet - .generate_ed25519_address( - 0, - 0, - Some(GenerateAddressOptions { - ledger_nano_prompt: true, - ..Default::default() - }), - ) - .await?; - - assert_eq!( - address.to_bech32_unchecked("smr"), - // Address generated with bip32 path: [44, 4218, 0, 0, 0]. - // This address was generated with a MnemonicSecretManager and the ledger simulator mnemonic. - // "glory promote mansion idle axis finger extra february uncover one trip resource lawn turtle enact monster - // seven myth punch hobby comfort wild raise skin". - "smr1qqdnv60ryxynaeyu8paq3lp9rkll7d7d92vpumz88fdj4l0pn5mruy3qdpm" - ); - - assert_eq!( - receiver - .recv() - .await - .expect("never received event") - .into_inner() - .to_bech32_unchecked("smr"), - // Address generated with bip32 path: [44, 4218, 0, 0, 0]. - // This address was generated with a MnemonicSecretManager and the ledger simulator mnemonic. - // "glory promote mansion idle axis finger extra february uncover one trip resource lawn turtle enact monster - // seven myth punch hobby comfort wild raise skin". - "smr1qqdnv60ryxynaeyu8paq3lp9rkll7d7d92vpumz88fdj4l0pn5mruy3qdpm" - ); - - tear_down(storage_path) -} - -// #[tokio::test] -// async fn wallet_address_generation_placeholder() -> Result<(), Box> { -// let storage_path = "test-storage/wallet_address_generation_placeholder"; -// setup(storage_path)?; - -// let client_options = ClientOptions::new().with_node(NODE_LOCAL)?; - -// #[allow(unused_mut)] -// let mut wallet_builder = Wallet::builder() -// .with_secret_manager(SecretManager::Placeholder) -// .with_client_options(client_options) -// .with_bip_path(Bip44::new(IOTA_COIN_TYPE)); - -// #[cfg(feature = "storage")] -// { -// wallet_builder = wallet_builder.with_storage_path(storage_path); -// } -// let wallet = wallet_builder.finish().await?; - -// if let Err(Error::Client(error)) = wallet.generate_ed25519_address(0, 0, None).await { -// assert!(matches!(*error, ClientError::PlaceholderSecretManager)) -// } else { -// panic!("expected PlaceholderSecretManager") -// } - -// tear_down(storage_path) -// } diff --git a/sdk/tests/wallet/common/mod.rs b/sdk/tests/wallet/common/mod.rs index 82a48d1679..045ca06ce3 100644 --- a/sdk/tests/wallet/common/mod.rs +++ b/sdk/tests/wallet/common/mod.rs @@ -15,7 +15,7 @@ use iota_sdk::{ wallet::{ClientOptions, Wallet}, }; -pub use self::constants::*; +pub use self::constants::{DEFAULT_MNEMONIC, FAUCET_URL, NODE_LOCAL, NODE_OTHER}; /// It creates a new wallet with a mnemonic secret manager, a client options object, /// SHIMMER_COIN_TYPE, and a storage path diff --git a/sdk/tests/wallet/mod.rs b/sdk/tests/wallet/mod.rs index 0cb359191e..4ef0f24d96 100644 --- a/sdk/tests/wallet/mod.rs +++ b/sdk/tests/wallet/mod.rs @@ -1,7 +1,6 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -mod address_generation; #[cfg(all(feature = "stronghold", feature = "storage"))] mod backup_restore; mod balance; From ffb6fc278023ea282184604b00ac51681fb1cc77 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Fri, 8 Mar 2024 11:32:58 +0100 Subject: [PATCH 21/21] Add network metrics endpoint (#2143) * Remove metrics from InfoResponse * NetworkMetricsResponse * get_network_metrics * Add to bindings core * python binding * Nodejs binding * lint * python fix * Comment tests * lint * tmp * TODO --- .../actions/private-tangle/setup/action.yml | 9 +++---- bindings/core/src/method/client.rs | 2 ++ bindings/core/src/method_handler/client.rs | 1 + bindings/core/src/response.rs | 9 ++++--- bindings/nodejs/lib/client/client.ts | 12 +++++++++ .../nodejs/lib/types/client/bridge/client.ts | 4 +++ .../nodejs/lib/types/client/bridge/index.ts | 2 ++ bindings/nodejs/lib/types/models/api/index.ts | 1 + .../nodejs/lib/types/models/api/info/index.ts | 1 - .../lib/types/models/api/info/node-info.ts | 5 ---- ...ode-info-metrics.ts => network-metrics.ts} | 6 ++--- .../python/iota_sdk/client/_node_core_api.py | 21 +++++++++++++--- bindings/python/iota_sdk/client/client.py | 3 ++- bindings/python/iota_sdk/client/responses.py | 25 ++++++++++++++++--- bindings/python/iota_sdk/types/node_info.py | 21 ---------------- bindings/python/tests/test_api_responses.py | 5 ++-- sdk/src/client/node_api/core/routes.rs | 15 ++++++++--- sdk/src/types/api/core.rs | 6 ++--- sdk/tests/types/api/core.rs | 3 ++- 19 files changed, 95 insertions(+), 56 deletions(-) rename bindings/nodejs/lib/types/models/api/{info/node-info-metrics.ts => network-metrics.ts} (76%) diff --git a/.github/actions/private-tangle/setup/action.yml b/.github/actions/private-tangle/setup/action.yml index 6db6ab6e1d..de56383ba7 100644 --- a/.github/actions/private-tangle/setup/action.yml +++ b/.github/actions/private-tangle/setup/action.yml @@ -7,8 +7,6 @@ runs: uses: actions/checkout@v3 with: repository: iotaledger/iota-core - # TODO: remove ref when inx plugins are updated to the latest commit - ref: 25995c84ccc4f4cd9041881035f338acca513b0a path: iota-core - name: Prepare files for start and stop @@ -40,6 +38,7 @@ runs: - name: Wait for tangle to start shell: bash run: wget -qO- https://raw.githubusercontent.com/eficode/wait-for/v2.2.4/wait-for | sh -s -- -t 120 http://localhost:8050/health -- echo "Tangle is up" - - name: Wait for faucet to start - shell: bash - run: wget -qO- https://raw.githubusercontent.com/eficode/wait-for/v2.2.4/wait-for | sh -s -- -t 120 http://localhost:8088/health -- echo "Faucet is up" + # TODO https://github.com/iotaledger/iota-sdk/issues/2154 + # - name: Wait for faucet to start + # shell: bash + # run: wget -qO- https://raw.githubusercontent.com/eficode/wait-for/v2.2.4/wait-for | sh -s -- -t 120 http://localhost:8088/health -- echo "Faucet is up" diff --git a/bindings/core/src/method/client.rs b/bindings/core/src/method/client.rs index dff0781fb1..d10431bf32 100644 --- a/bindings/core/src/method/client.rs +++ b/bindings/core/src/method/client.rs @@ -149,6 +149,8 @@ pub enum ClientMethod { }, /// Returns general information about the node together with its URL. GetNodeInfo, + /// Returns network metrics. + GetNetworkMetrics, /// Check the readiness of the node to issue a new block, the reference mana cost based on the rate setter and /// current network congestion, and the block issuance credits of the requested account. #[serde(rename_all = "camelCase")] diff --git a/bindings/core/src/method_handler/client.rs b/bindings/core/src/method_handler/client.rs index b63fe2cec0..f3159e5d5d 100644 --- a/bindings/core/src/method_handler/client.rs +++ b/bindings/core/src/method_handler/client.rs @@ -184,6 +184,7 @@ pub(crate) async fn call_client_method_internal( ClientMethod::GetHealth { url } => Response::Bool(client.get_health(&url).await?), ClientMethod::GetInfo { url, auth } => Response::Info(Client::get_info(&url, auth).await?), ClientMethod::GetNodeInfo => Response::NodeInfo(client.get_node_info().await?), + ClientMethod::GetNetworkMetrics => Response::NetworkMetrics(client.get_network_metrics().await?), ClientMethod::GetRoutes => Response::Routes(client.get_routes().await?), ClientMethod::GetAccountCongestion { account_id, work_score } => { Response::Congestion(client.get_account_congestion(&account_id, work_score).await?) diff --git a/bindings/core/src/response.rs b/bindings/core/src/response.rs index b53baa6045..928e877dfc 100644 --- a/bindings/core/src/response.rs +++ b/bindings/core/src/response.rs @@ -17,9 +17,9 @@ use iota_sdk::{ api::{ core::{ BlockMetadataResponse, BlockWithMetadataResponse, CommitteeResponse, CongestionResponse, InfoResponse, - IssuanceBlockHeaderResponse, ManaRewardsResponse, OutputResponse, OutputWithMetadataResponse, - RoutesResponse, TransactionMetadataResponse, UtxoChangesFullResponse, UtxoChangesResponse, - ValidatorResponse, ValidatorsResponse, + IssuanceBlockHeaderResponse, ManaRewardsResponse, NetworkMetricsResponse, OutputResponse, + OutputWithMetadataResponse, RoutesResponse, TransactionMetadataResponse, UtxoChangesFullResponse, + UtxoChangesResponse, ValidatorResponse, ValidatorsResponse, }, plugins::indexer::OutputIdsResponse, }, @@ -102,6 +102,9 @@ pub enum Response { /// - [`GetNodeInfo`](crate::method::ClientMethod::GetNodeInfo) NodeInfo(NodeInfoResponse), /// Response for: + /// - [`GetNetworkMetrics`](crate::method::ClientMethod::GetNetworkMetrics) + NetworkMetrics(NetworkMetricsResponse), + /// Response for: /// - [`GetRoutes`](crate::method::ClientMethod::GetRoutes) Routes(RoutesResponse), /// Response for: diff --git a/bindings/nodejs/lib/client/client.ts b/bindings/nodejs/lib/client/client.ts index ed3c1e43c6..5f1e7dcf7d 100644 --- a/bindings/nodejs/lib/client/client.ts +++ b/bindings/nodejs/lib/client/client.ts @@ -66,6 +66,7 @@ import { IssuanceBlockHeaderResponse, OutputMetadataResponse, OutputWithMetadataResponse, + NetworkMetricsResponse, } from '../types/models/api'; import { RoutesResponse } from '../types/models/api/routes-response'; @@ -156,6 +157,17 @@ export class Client { return JSON.parse(response).payload; } + /** + * Get the network metrics. + */ + async getNetworkMetrics(): Promise { + const response = await this.methodHandler.callMethod({ + name: 'getNetworkMetrics', + }); + + return JSON.parse(response).payload; + } + // Accounts routes. /** diff --git a/bindings/nodejs/lib/types/client/bridge/client.ts b/bindings/nodejs/lib/types/client/bridge/client.ts index 6d1cc5f7c1..80252b747f 100644 --- a/bindings/nodejs/lib/types/client/bridge/client.ts +++ b/bindings/nodejs/lib/types/client/bridge/client.ts @@ -59,6 +59,10 @@ export interface __GetInfoMethod__ { }; } +export interface __GetNetworkMetricsMethod__ { + name: 'getNetworkMetrics'; +} + // Accounts routes. export interface __GetAccountCongestionMethod__ { diff --git a/bindings/nodejs/lib/types/client/bridge/index.ts b/bindings/nodejs/lib/types/client/bridge/index.ts index 2bfb84fc57..560f6940e3 100644 --- a/bindings/nodejs/lib/types/client/bridge/index.ts +++ b/bindings/nodejs/lib/types/client/bridge/index.ts @@ -31,6 +31,7 @@ import type { __GetProtocolParametersMethod__, __GetHealthMethod__, __GetNodeInfoMethod__, + __GetNetworkMetricsMethod__, __GetBlockRawMethod__, __GetIncludedBlockMethod__, __GetIncludedBlockRawMethod__, @@ -98,6 +99,7 @@ export type __ClientMethods__ = | __GetProtocolParametersMethod__ | __GetHealthMethod__ | __GetNodeInfoMethod__ + | __GetNetworkMetricsMethod__ | __GetBlockRawMethod__ | __GetIncludedBlockMethod__ | __GetIncludedBlockRawMethod__ diff --git a/bindings/nodejs/lib/types/models/api/index.ts b/bindings/nodejs/lib/types/models/api/index.ts index dec65a7da7..e1c8aa9f6d 100644 --- a/bindings/nodejs/lib/types/models/api/index.ts +++ b/bindings/nodejs/lib/types/models/api/index.ts @@ -9,6 +9,7 @@ export * from './committee-response'; export * from './congestion-response'; export * from './issuance-response'; export * from './mana-rewards-response'; +export * from './network-metrics'; export * from './output-id-proof'; export * from './output-metadata-response'; export * from './output-response'; diff --git a/bindings/nodejs/lib/types/models/api/info/index.ts b/bindings/nodejs/lib/types/models/api/info/index.ts index db710a17d1..0a6f82288a 100644 --- a/bindings/nodejs/lib/types/models/api/info/index.ts +++ b/bindings/nodejs/lib/types/models/api/info/index.ts @@ -3,6 +3,5 @@ export * from './node-info'; export * from './node-info-base-token'; -export * from './node-info-metrics'; export * from './node-info-protocol'; export * from './node-info-status'; diff --git a/bindings/nodejs/lib/types/models/api/info/node-info.ts b/bindings/nodejs/lib/types/models/api/info/node-info.ts index 93a5c817d1..fbe4c1ecc3 100644 --- a/bindings/nodejs/lib/types/models/api/info/node-info.ts +++ b/bindings/nodejs/lib/types/models/api/info/node-info.ts @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import type { BaseTokenResponse } from './node-info-base-token'; -import type { MetricsResponse } from './node-info-metrics'; import type { ProtocolParametersResponse } from './node-info-protocol'; import type { StatusResponse } from './node-info-status'; /** @@ -21,10 +20,6 @@ export interface InfoResponse { * The status of the node. */ status: StatusResponse; - /** - * The metrics for the node. - */ - metrics: MetricsResponse; /** * The protocol parameters. */ diff --git a/bindings/nodejs/lib/types/models/api/info/node-info-metrics.ts b/bindings/nodejs/lib/types/models/api/network-metrics.ts similarity index 76% rename from bindings/nodejs/lib/types/models/api/info/node-info-metrics.ts rename to bindings/nodejs/lib/types/models/api/network-metrics.ts index 8700ba36ba..ab783806f8 100644 --- a/bindings/nodejs/lib/types/models/api/info/node-info-metrics.ts +++ b/bindings/nodejs/lib/types/models/api/network-metrics.ts @@ -1,10 +1,10 @@ -// Copyright 2023 IOTA Stiftung +// Copyright 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 /** - * Response from the /info endpoint. + * Metrics information about the network. */ -export interface MetricsResponse { +export interface NetworkMetricsResponse { /** * The current rate of new blocks per second. */ diff --git a/bindings/python/iota_sdk/client/_node_core_api.py b/bindings/python/iota_sdk/client/_node_core_api.py index 795dee0a43..f62b32525d 100644 --- a/bindings/python/iota_sdk/client/_node_core_api.py +++ b/bindings/python/iota_sdk/client/_node_core_api.py @@ -3,7 +3,7 @@ from typing import List, Optional, Union from abc import ABCMeta, abstractmethod -from iota_sdk.client.responses import InfoResponse, NodeInfoResponse, RoutesResponse, CongestionResponse, ManaRewardsResponse, CommitteeResponse, ValidatorResponse, ValidatorsResponse, IssuanceBlockHeaderResponse, BlockMetadataResponse, BlockWithMetadataResponse, OutputResponse, OutputWithMetadataResponse, TransactionMetadataResponse, UtxoChangesResponse, UtxoChangesFullResponse +from iota_sdk.client.responses import InfoResponse, NodeInfoResponse, NetworkMetricsResponse, RoutesResponse, CongestionResponse, ManaRewardsResponse, CommitteeResponse, ValidatorResponse, ValidatorsResponse, IssuanceBlockHeaderResponse, BlockMetadataResponse, BlockWithMetadataResponse, OutputResponse, OutputWithMetadataResponse, TransactionMetadataResponse, UtxoChangesResponse, UtxoChangesFullResponse from iota_sdk.types.block.block import Block from iota_sdk.types.block.id import BlockId from iota_sdk.types.common import HexStr, EpochIndex, SlotIndex @@ -82,9 +82,20 @@ def get_info(self, url: str, auth=None) -> InfoResponse: 'auth': auth })) + def get_network_metrics(self) -> NetworkMetricsResponse: + """Returns network metrics. + GET /api/core/v3/network/metrics + + Returns: + Network metrics. + """ + return NetworkMetricsResponse.from_dict( + self._call_method('getNetworkMetrics')) + # Accounts routes. - def get_account_congestion(self, account_id: HexStr, work_score: Optional[int] = None) -> CongestionResponse: + def get_account_congestion( + self, account_id: HexStr, work_score: Optional[int] = None) -> CongestionResponse: """Checks if the account is ready to issue a block. GET /api/core/v3/accounts/{bech32Address}/congestion """ @@ -112,7 +123,8 @@ def get_output_mana_rewards( # Validators routes. - def get_validators(self, page_size: Optional[int] = None, cursor: Optional[str] = None) -> ValidatorsResponse: + def get_validators( + self, page_size: Optional[int] = None, cursor: Optional[str] = None) -> ValidatorsResponse: """Returns information of all stakers (registered validators) and if they are active, ordered by their holding stake. GET /api/core/v3/validators """ @@ -131,7 +143,8 @@ def get_validator(self, account_id: HexStr) -> ValidatorResponse: # Committee routes. - def get_committee(self, epoch_index: Optional[EpochIndex] = None) -> CommitteeResponse: + def get_committee( + self, epoch_index: Optional[EpochIndex] = None) -> CommitteeResponse: """Returns the information of committee members at the given epoch index. If epoch index is not provided, the current committee members are returned. GET /api/core/v3/committee/?epochIndex diff --git a/bindings/python/iota_sdk/client/client.py b/bindings/python/iota_sdk/client/client.py index 3a0b684511..6f0f6ef10a 100644 --- a/bindings/python/iota_sdk/client/client.py +++ b/bindings/python/iota_sdk/client/client.py @@ -258,7 +258,8 @@ def get_node(self) -> Dict[str, Any]: def get_protocol_parameters(self) -> ProtocolParameters: """Gets the protocol parameters. """ - return ProtocolParameters.from_dict(self._call_method('getProtocolParameters')) + return ProtocolParameters.from_dict( + self._call_method('getProtocolParameters')) def get_network_id(self) -> int: """Gets the network id of the node we're connecting to. diff --git a/bindings/python/iota_sdk/client/responses.py b/bindings/python/iota_sdk/client/responses.py index 9d4db84d75..27f5d3e173 100644 --- a/bindings/python/iota_sdk/client/responses.py +++ b/bindings/python/iota_sdk/client/responses.py @@ -9,7 +9,7 @@ from iota_sdk.types.block.block import Block from iota_sdk.types.block.id import BlockId from iota_sdk.types.common import HexStr, json, EpochIndex, SlotIndex -from iota_sdk.types.node_info import BaseTokenResponse, MetricsResponse, StatusResponse, ProtocolParameters +from iota_sdk.types.node_info import BaseTokenResponse, StatusResponse, ProtocolParameters from iota_sdk.types.output import Output, deserialize_output from iota_sdk.types.output_id import OutputId, OutputWithId from iota_sdk.types.output_id_proof import OutputIdProof @@ -56,14 +56,12 @@ class InfoResponse: name: The name of the node (e.g. Hornet). version: The semantic version of the node. status: The status of the node. - metrics: Node metrics. protocol_parameters: Supported protocol versions by the node. base_token: Gives info about the base token the network uses. """ name: str version: str status: StatusResponse - metrics: MetricsResponse protocol_parameters: List[ProtocolParametersResponse] base_token: BaseTokenResponse @@ -82,8 +80,29 @@ class NodeInfoResponse: url: str +@json +@dataclass +class NetworkMetricsResponse: + """Network metrics. + + Attributes: + blocks_per_second: The current rate of new blocks per second. + confirmed_blocks_per_second: The current rate of confirmed blocks per second. + confirmation_rate: The ratio of confirmed blocks to new blocks of the last confirmed slot. + """ + blocks_per_second: float = field(metadata=config( + encoder=str + )) + confirmed_blocks_per_second: float = field(metadata=config( + encoder=str + )) + confirmation_rate: float = field(metadata=config( + encoder=str + )) + # Accounts routes responses + @json @dataclass class CongestionResponse: diff --git a/bindings/python/iota_sdk/types/node_info.py b/bindings/python/iota_sdk/types/node_info.py index 28fcd4b1cd..3f7ef621dd 100644 --- a/bindings/python/iota_sdk/types/node_info.py +++ b/bindings/python/iota_sdk/types/node_info.py @@ -46,27 +46,6 @@ class StatusResponse: pruning_epoch: EpochIndex -@json -@dataclass -class MetricsResponse: - """Node metrics. - - Attributes: - blocks_per_second: The current rate of new blocks per second. - confirmed_blocks_per_second: The current rate of confirmed blocks per second. - confirmation_rate: The ratio of confirmed blocks to new blocks of the last confirmed slot. - """ - blocks_per_second: float = field(metadata=config( - encoder=str - )) - confirmed_blocks_per_second: float = field(metadata=config( - encoder=str - )) - confirmation_rate: float = field(metadata=config( - encoder=str - )) - - @json @dataclass class StorageScoreParameters: diff --git a/bindings/python/tests/test_api_responses.py b/bindings/python/tests/test_api_responses.py index 2fc3a21c02..785f777667 100644 --- a/bindings/python/tests/test_api_responses.py +++ b/bindings/python/tests/test_api_responses.py @@ -3,7 +3,7 @@ from typing import Generic, TypeVar from json import load, loads, dumps -from iota_sdk import RoutesResponse, CongestionResponse, OutputWithMetadataResponse, ManaRewardsResponse, ValidatorsResponse, ValidatorResponse, InfoResponse, CommitteeResponse, IssuanceBlockHeaderResponse, Block, BlockMetadataResponse, BlockWithMetadataResponse, OutputMetadata, OutputResponse, TransactionMetadataResponse, SlotCommitment, UtxoChangesResponse, UtxoChangesFullResponse +from iota_sdk import RoutesResponse, CongestionResponse, OutputWithMetadataResponse, ManaRewardsResponse, ValidatorsResponse, ValidatorResponse, CommitteeResponse, IssuanceBlockHeaderResponse, Block, BlockMetadataResponse, BlockWithMetadataResponse, OutputMetadata, OutputResponse, TransactionMetadataResponse, SlotCommitment, UtxoChangesResponse, UtxoChangesFullResponse base_path = '../../sdk/tests/types/api/fixtures/' @@ -27,7 +27,8 @@ def test_api_response(cls_type: Generic[T], path: str): # GET /api/routes test_api_response(RoutesResponse, "get-routes-response-example.json") # GET /api/core/v3/info - test_api_response(InfoResponse, "get-info-response-example.json") + # TODO reenable when Metrics are split out of Info + # test_api_response(InfoResponse, "get-info-response-example.json") # GET /api/core/v3/accounts/{bech32Address}/congestion test_api_response(CongestionResponse, "get-congestion-estimate-response-example.json") diff --git a/sdk/src/client/node_api/core/routes.rs b/sdk/src/client/node_api/core/routes.rs index 798b73f61b..c7b09ce4da 100644 --- a/sdk/src/client/node_api/core/routes.rs +++ b/sdk/src/client/node_api/core/routes.rs @@ -17,9 +17,10 @@ use crate::{ types::{ api::core::{ BlockMetadataResponse, BlockWithMetadataResponse, CommitteeResponse, CongestionResponse, InfoResponse, - IssuanceBlockHeaderResponse, ManaRewardsResponse, OutputResponse, OutputWithMetadataResponse, - PermanodeInfoResponse, RoutesResponse, SubmitBlockResponse, TransactionMetadataResponse, - UtxoChangesFullResponse, UtxoChangesResponse, ValidatorResponse, ValidatorsResponse, + IssuanceBlockHeaderResponse, ManaRewardsResponse, NetworkMetricsResponse, OutputResponse, + OutputWithMetadataResponse, PermanodeInfoResponse, RoutesResponse, SubmitBlockResponse, + TransactionMetadataResponse, UtxoChangesFullResponse, UtxoChangesResponse, ValidatorResponse, + ValidatorsResponse, }, block::{ address::ToBech32Ext, @@ -87,6 +88,14 @@ impl ClientInner { pub async fn get_node_info(&self) -> Result { self.get_request(INFO_PATH, None, false).await } + + /// Returns network metrics. + /// GET /api/core/v3/network/metrics + pub async fn get_network_metrics(&self) -> Result { + const PATH: &str = "api/core/v3/network/metrics"; + + self.get_request(PATH, None, false).await + } } impl Client { diff --git a/sdk/src/types/api/core.rs b/sdk/src/types/api/core.rs index a943e6ff9a..c43a4fdfdd 100644 --- a/sdk/src/types/api/core.rs +++ b/sdk/src/types/api/core.rs @@ -40,7 +40,6 @@ pub struct InfoResponse { pub name: String, pub version: String, pub status: StatusResponse, - pub metrics: MetricsResponse, pub protocol_parameters: ProtocolParametersMap, pub base_token: BaseTokenResponse, } @@ -105,11 +104,10 @@ pub struct StatusResponse { pub pruning_epoch: EpochIndex, } -/// Returned in [`InfoResponse`]. -/// Metric information about the node. +/// Metrics information about the network. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct MetricsResponse { +pub struct NetworkMetricsResponse { #[serde(with = "string")] pub blocks_per_second: f64, #[serde(with = "string")] diff --git a/sdk/tests/types/api/core.rs b/sdk/tests/types/api/core.rs index 8b55420336..70e5ac525d 100644 --- a/sdk/tests/types/api/core.rs +++ b/sdk/tests/types/api/core.rs @@ -52,7 +52,8 @@ fn responses() { // GET /api/routes json_response::("get-routes-response-example.json").unwrap(); // GET /api/core/v3/info - json_response::("get-info-response-example.json").unwrap(); + // TODO reenable when Metrics are split out of Info + // json_response::("get-info-response-example.json").unwrap(); // GET /api/core/v3/accounts/{bech32Address}/congestion json_response::("get-congestion-estimate-response-example.json").unwrap(); // GET /api/core/v3/rewards/{outputId}