From eb726f71c2a393e6f8db0927d7b4055744d16f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thoralf=20M=C3=BCller?= Date: Tue, 5 Dec 2023 10:39:48 +0100 Subject: [PATCH 1/8] Add /api/core/v3/outputs/{outputId}/full route --- sdk/Cargo.toml | 5 ++ .../node_api_core/12_get_output_full.rs | 44 ++++++++++ sdk/src/client/node_api/core/mod.rs | 15 ---- sdk/src/client/node_api/core/routes.rs | 10 ++- sdk/src/types/api/core.rs | 84 ++++++++++++++++++- sdk/src/types/block/output/mod.rs | 45 +++++++--- sdk/src/wallet/operations/syncing/outputs.rs | 2 + sdk/src/wallet/types/mod.rs | 4 +- 8 files changed, 180 insertions(+), 29 deletions(-) create mode 100644 sdk/examples/client/node_api_core/12_get_output_full.rs diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index e9bb13942f..250f10bb85 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -453,6 +453,11 @@ name = "node_api_core_get_output_metadata" path = "examples/client/node_api_core/11_get_output_metadata.rs" required-features = ["client"] +[[example]] +name = "node_api_core_get_output_full" +path = "examples/client/node_api_core/12_get_output_full.rs" +required-features = ["client"] + [[example]] name = "node_api_core_get_included_block" path = "examples/client/node_api_core/15_get_included_block.rs" diff --git a/sdk/examples/client/node_api_core/12_get_output_full.rs b/sdk/examples/client/node_api_core/12_get_output_full.rs new file mode 100644 index 0000000000..fe82758ea1 --- /dev/null +++ b/sdk/examples/client/node_api_core/12_get_output_full.rs @@ -0,0 +1,44 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +//! Find an output with its metadata, by its identifier by querying the `/api/core/v3/outputs/{outputId}/full` node +//! endpoint. +//! +//! Make sure to provide a somewhat recent output id to make this example run successfully! +//! +//! Rename `.env.example` to `.env` first, then run the command: +//! ```sh +//! cargo run --release --example node_api_core_get_output_full [NODE URL] +//! ``` + +use iota_sdk::{ + client::{Client, Result}, + types::block::output::OutputId, +}; + +#[tokio::main] +async fn main() -> Result<()> { + // If not provided we use the default node from the `.env` file. + dotenvy::dotenv().ok(); + + // Take the node URL from command line argument or use one from env as default. + let node_url = std::env::args() + .nth(2) + .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)?.finish().await?; + + // Take the output id from the command line, or panic. + let output_id = std::env::args() + .nth(1) + .expect("missing example argument: OUTPUT ID") + .parse::()?; + + // Get the output with its metadata. + let output_with_metadata = client.get_output_with_metadata(&output_id).await?; + + println!("{output_with_metadata:?}"); + + Ok(()) +} diff --git a/sdk/src/client/node_api/core/mod.rs b/sdk/src/client/node_api/core/mod.rs index 7f31339855..e243916590 100644 --- a/sdk/src/client/node_api/core/mod.rs +++ b/sdk/src/client/node_api/core/mod.rs @@ -5,27 +5,12 @@ pub mod routes; -use packable::PackableExt; - use crate::{ client::{node_api::error::Error as NodeApiError, Client, Error, Result}, types::block::output::{Output, OutputId, OutputMetadata, OutputWithMetadata}, }; impl Client { - // Finds output and its metadata by output ID. - /// GET /api/core/v3/outputs/{outputId} - /// + GET /api/core/v3/outputs/{outputId}/metadata - pub async fn get_output_with_metadata(&self, output_id: &OutputId) -> Result { - let output = Output::unpack_verified( - self.get_output_raw(output_id).await?, - &self.get_protocol_parameters().await?, - )?; - let metadata = self.get_output_metadata(output_id).await?; - - Ok(OutputWithMetadata::new(output, metadata)) - } - /// Requests outputs by their output ID in parallel. pub async fn get_outputs(&self, output_ids: &[OutputId]) -> Result> { futures::future::try_join_all(output_ids.iter().map(|id| self.get_output(id))).await diff --git a/sdk/src/client/node_api/core/routes.rs b/sdk/src/client/node_api/core/routes.rs index aace690693..80c09664dd 100644 --- a/sdk/src/client/node_api/core/routes.rs +++ b/sdk/src/client/node_api/core/routes.rs @@ -22,7 +22,7 @@ use crate::{ }, block::{ address::ToBech32Ext, - output::{AccountId, Output, OutputId, OutputMetadata}, + output::{AccountId, Output, OutputId, OutputMetadata, OutputWithMetadata}, payload::signed_transaction::TransactionId, slot::{EpochIndex, SlotCommitment, SlotCommitmentId, SlotIndex}, Block, BlockDto, BlockId, @@ -256,6 +256,14 @@ impl ClientInner { self.get_request(path, None, false, true).await } + /// Finds an output with its metadata by output ID. + /// GET /api/core/v3/outputs/{outputId}/full + pub async fn get_output_with_metadata(&self, output_id: &OutputId) -> Result { + let path = &format!("api/core/v3/outputs/{output_id}/full"); + + self.get_request(path, None, false, true).await + } + /// Returns the earliest confirmed block containing the transaction with the given ID. /// GET /api/core/v3/transactions/{transactionId}/included-block pub async fn get_included_block(&self, transaction_id: &TransactionId) -> Result { diff --git a/sdk/src/types/api/core.rs b/sdk/src/types/api/core.rs index 34b6472ad8..faa033bd2f 100644 --- a/sdk/src/types/api/core.rs +++ b/sdk/src/types/api/core.rs @@ -9,6 +9,7 @@ use alloc::{ }; use serde::{Deserialize, Serialize}; +use serde_json::Value; use crate::{ types::block::{ @@ -21,7 +22,7 @@ use crate::{ slot::{EpochIndex, SlotCommitment, SlotCommitmentId, SlotIndex}, BlockDto, BlockId, }, - utils::serde::{option_string, string}, + utils::serde::{option_string, prefix_hex_bytes, string}, }; /// Response of GET /api/core/v3/info. @@ -528,3 +529,84 @@ pub struct UtxoChangesResponse { pub created_outputs: Vec, pub consumed_outputs: Vec, } + +/// Contains the generic [`Output`] with associated [`OutputIdProof`]. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct OutputResponse { + pub output: Output, + pub output_id_proof: OutputIdProof, +} + +/// The proof of the output identifier. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct OutputIdProof { + pub slot: SlotIndex, + pub output_index: u16, + pub transaction_commitment: String, + pub output_commitment_proof: OutputCommitmentProof, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +#[serde(untagged)] +pub enum OutputCommitmentProof { + HashableNode(HashableNode), + LeafHash(LeafHash), + ValueHash(ValueHash), +} + +impl<'de> Deserialize<'de> for OutputCommitmentProof { + fn deserialize>(d: D) -> Result { + let value = Value::deserialize(d)?; + Ok( + match value + .get("type") + .and_then(Value::as_u64) + .ok_or_else(|| serde::de::Error::custom("invalid output commitment proof type"))? + as u8 + { + 0 => Self::HashableNode( + serde_json::from_value::(value) + .map_err(|e| serde::de::Error::custom(format!("cannot deserialize hashable node: {e}")))?, + ), + 1 => Self::LeafHash( + serde_json::from_value::(value) + .map_err(|e| serde::de::Error::custom(format!("cannot deserialize leaf hash: {e}")))?, + ), + 2 => Self::ValueHash( + serde_json::from_value::(value) + .map_err(|e| serde::de::Error::custom(format!("cannot deserialize value hash: {e}")))?, + ), + _ => return Err(serde::de::Error::custom("invalid output commitment proof")), + }, + ) + } +} + +/// The proof of the output commitment. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct HashableNode { + #[serde(rename = "type")] + pub kind: u8, + pub l: Box, + pub r: Box, +} + +/// Leaf Hash contains the hash of a leaf in the tree. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct LeafHash { + #[serde(rename = "type")] + pub kind: u8, + #[serde(with = "prefix_hex_bytes")] + pub hash: [u8; 32], +} + +/// Value Hash contains the hash of the value for which the proof is being computed. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct ValueHash { + #[serde(rename = "type")] + pub kind: u8, + #[serde(with = "prefix_hex_bytes")] + pub hash: [u8; 32], +} diff --git a/sdk/src/types/block/output/mod.rs b/sdk/src/types/block/output/mod.rs index d4f41e337a..06cec2e5e2 100644 --- a/sdk/src/types/block/output/mod.rs +++ b/sdk/src/types/block/output/mod.rs @@ -52,12 +52,15 @@ pub(crate) use self::{ output_id::OutputIndex, unlock_condition::AddressUnlockCondition, }; -use crate::types::block::{ - address::Address, - protocol::{CommittableAgeRange, ProtocolParameters, WorkScore, WorkScoreParameters}, - semantic::SemanticValidationContext, - slot::SlotIndex, - Error, +use crate::types::{ + api::core::OutputIdProof, + block::{ + address::Address, + protocol::{CommittableAgeRange, ProtocolParameters, WorkScore, WorkScoreParameters}, + semantic::SemanticValidationContext, + slot::SlotIndex, + Error, + }, }; /// The maximum number of outputs of a transaction. @@ -75,17 +78,27 @@ pub enum OutputBuilderAmount { MinimumAmount(StorageScoreParameters), } -/// Contains the generic [`Output`] with associated [`OutputMetadata`]. +/// Contains the generic [`Output`] with associated [`OutputIdProof`] and [`OutputMetadata`]. #[derive(Clone, Debug)] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] pub struct OutputWithMetadata { - pub(crate) output: Output, - pub(crate) metadata: OutputMetadata, + pub output: Output, + pub output_id_proof: OutputIdProof, + pub metadata: OutputMetadata, } impl OutputWithMetadata { /// Creates a new [`OutputWithMetadata`]. - pub fn new(output: Output, metadata: OutputMetadata) -> Self { - Self { output, metadata } + pub fn new(output: Output, output_id_proof: OutputIdProof, metadata: OutputMetadata) -> Self { + Self { + output, + output_id_proof, + metadata, + } } /// Returns the [`Output`]. @@ -98,6 +111,16 @@ impl OutputWithMetadata { self.output } + /// Returns the [`OutputIdProof`]. + pub fn output_id_proof(&self) -> &OutputIdProof { + &self.output_id_proof + } + + /// Consumes self and returns the [`OutputIdProof`]. + pub fn into_output_id_proof(self) -> OutputIdProof { + self.output_id_proof + } + /// Returns the [`OutputMetadata`]. pub fn metadata(&self) -> &OutputMetadata { &self.metadata diff --git a/sdk/src/wallet/operations/syncing/outputs.rs b/sdk/src/wallet/operations/syncing/outputs.rs index 0e18fc8c9c..647d8a4a43 100644 --- a/sdk/src/wallet/operations/syncing/outputs.rs +++ b/sdk/src/wallet/operations/syncing/outputs.rs @@ -60,6 +60,7 @@ where output_id: output_with_meta.metadata().output_id().to_owned(), metadata: *output_with_meta.metadata(), output: output_with_meta.output().clone(), + output_id_proof: output_with_meta.output_id_proof().clone(), is_spent: output_with_meta.metadata().is_spent(), address: associated_address.address.inner.clone(), network_id, @@ -91,6 +92,7 @@ where unspent_outputs.push((output_id, output_data.clone())); outputs.push(OutputWithMetadata::new( output_data.output.clone(), + output_data.output_id_proof.clone(), output_data.metadata, )); } diff --git a/sdk/src/wallet/types/mod.rs b/sdk/src/wallet/types/mod.rs index 39c2c5721d..3e68d9f0c5 100644 --- a/sdk/src/wallet/types/mod.rs +++ b/sdk/src/wallet/types/mod.rs @@ -19,7 +19,7 @@ pub use self::{ use crate::{ client::secret::types::InputSigningData, types::{ - api::core::OutputWithMetadataResponse, + api::core::{OutputIdProof, OutputWithMetadataResponse}, block::{ address::Address, output::{Output, OutputId, OutputMetadata}, @@ -43,6 +43,8 @@ pub struct OutputData { pub metadata: OutputMetadata, /// The actual Output pub output: Output, + /// The OutputIdProof + pub output_id_proof: OutputIdProof, /// If an output is spent pub is_spent: bool, /// Associated wallet address. From fc941f14c40c0b50c91c5cd55a33e0ee3cd1d0ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thoralf=20M=C3=BCller?= Date: Tue, 5 Dec 2023 15:37:45 +0100 Subject: [PATCH 2/8] Fix test --- sdk/tests/wallet/events.rs | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/sdk/tests/wallet/events.rs b/sdk/tests/wallet/events.rs index 65710e679b..d55d2d6225 100644 --- a/sdk/tests/wallet/events.rs +++ b/sdk/tests/wallet/events.rs @@ -3,15 +3,19 @@ use iota_sdk::{ client::api::PreparedTransactionDataDto, - types::block::{ - address::{Address, Bech32Address, Ed25519Address}, - input::{Input, UtxoInput}, - output::{unlock_condition::AddressUnlockCondition, BasicOutput, Output}, - payload::signed_transaction::{Transaction, TransactionHash, TransactionId}, - protocol::protocol_parameters, - rand::{ - mana::rand_mana_allotment, - output::{rand_basic_output, rand_output_metadata}, + types::{ + api::core::{LeafHash, OutputCommitmentProof, OutputIdProof}, + block::{ + address::{Address, Bech32Address, Ed25519Address}, + input::{Input, UtxoInput}, + output::{unlock_condition::AddressUnlockCondition, BasicOutput, Output}, + payload::signed_transaction::{Transaction, TransactionHash, TransactionId}, + protocol::protocol_parameters, + rand::{ + mana::rand_mana_allotment, + output::{rand_basic_output, rand_output_metadata}, + }, + slot::SlotIndex, }, }, wallet::{ @@ -48,6 +52,15 @@ fn wallet_events_serde() { output_id: TransactionHash::null().into_transaction_id(0).into_output_id(0), metadata: rand_output_metadata(), output: Output::from(rand_basic_output(1_813_620_509_061_365)), + output_id_proof: OutputIdProof { + slot: SlotIndex(1), + output_index: 0, + transaction_commitment: "0x".to_string(), + output_commitment_proof: OutputCommitmentProof::LeafHash(LeafHash { + kind: 1, + hash: [0u8; 32], + }), + }, is_spent: false, address: Address::Ed25519(Ed25519Address::new([0; Ed25519Address::LENGTH])), network_id: 42, From 81bd5f05f92dee90ec91b7f7050fb829b4a12219 Mon Sep 17 00:00:00 2001 From: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> Date: Wed, 6 Dec 2023 17:21:10 +0100 Subject: [PATCH 3/8] Update sdk/src/wallet/types/mod.rs Co-authored-by: Thibault Martinez --- sdk/src/wallet/types/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/src/wallet/types/mod.rs b/sdk/src/wallet/types/mod.rs index 3e68d9f0c5..e08b3fcd02 100644 --- a/sdk/src/wallet/types/mod.rs +++ b/sdk/src/wallet/types/mod.rs @@ -43,7 +43,7 @@ pub struct OutputData { pub metadata: OutputMetadata, /// The actual Output pub output: Output, - /// The OutputIdProof + /// The output ID proof pub output_id_proof: OutputIdProof, /// If an output is spent pub is_spent: bool, From fd27ebe80d5b97457ba946e7f1de7db13412991d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thoralf=20M=C3=BCller?= Date: Wed, 6 Dec 2023 17:22:34 +0100 Subject: [PATCH 4/8] Comment OutputResponse --- sdk/src/types/api/core.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/sdk/src/types/api/core.rs b/sdk/src/types/api/core.rs index faa033bd2f..52ba161bcc 100644 --- a/sdk/src/types/api/core.rs +++ b/sdk/src/types/api/core.rs @@ -530,13 +530,14 @@ pub struct UtxoChangesResponse { pub consumed_outputs: Vec, } -/// Contains the generic [`Output`] with associated [`OutputIdProof`]. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct OutputResponse { - pub output: Output, - pub output_id_proof: OutputIdProof, -} +// TODO use for outputs route https://github.com/iotaledger/iota-sdk/issues/1686 +// /// Contains the generic [`Output`] with associated [`OutputIdProof`]. +// #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +// #[serde(rename_all = "camelCase")] +// pub struct OutputResponse { +// pub output: Output, +// pub output_id_proof: OutputIdProof, +// } /// The proof of the output identifier. #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] From b9929e72119a09c48781f7582f3c1999c1b1d422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thoralf=20M=C3=BCller?= Date: Thu, 7 Dec 2023 10:44:31 +0100 Subject: [PATCH 5/8] Fix comment --- sdk/src/types/api/core.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/src/types/api/core.rs b/sdk/src/types/api/core.rs index 52ba161bcc..f273dba7e8 100644 --- a/sdk/src/types/api/core.rs +++ b/sdk/src/types/api/core.rs @@ -585,7 +585,7 @@ impl<'de> Deserialize<'de> for OutputCommitmentProof { } } -/// The proof of the output commitment. +/// Node contains the hashes of the left and right children of a node in the tree. #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct HashableNode { #[serde(rename = "type")] From e14881f16ebe2ed89677d6d679e4c48e9f9ca3c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thoralf=20M=C3=BCller?= Date: Fri, 8 Dec 2023 11:47:07 +0100 Subject: [PATCH 6/8] Import format --- sdk/src/types/api/core.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/src/types/api/core.rs b/sdk/src/types/api/core.rs index f273dba7e8..ea8b859d6c 100644 --- a/sdk/src/types/api/core.rs +++ b/sdk/src/types/api/core.rs @@ -4,6 +4,7 @@ use alloc::{ boxed::Box, collections::{BTreeMap, BTreeSet}, + format, string::String, vec::Vec, }; From 8ca4054375a44f98abdaa081fa42248948b8aad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thoralf=20M=C3=BCller?= Date: Fri, 8 Dec 2023 15:19:03 +0100 Subject: [PATCH 7/8] Move OutputIdProof to block types --- sdk/src/types/api/core.rs | 77 +--------------- sdk/src/types/block/output/mod.rs | 17 ++-- sdk/src/types/block/output/output_id_proof.rs | 90 +++++++++++++++++++ .../operations/syncing/addresses/outputs.rs | 5 +- sdk/src/wallet/operations/syncing/mod.rs | 2 +- sdk/src/wallet/types/mod.rs | 4 +- 6 files changed, 103 insertions(+), 92 deletions(-) create mode 100644 sdk/src/types/block/output/output_id_proof.rs diff --git a/sdk/src/types/api/core.rs b/sdk/src/types/api/core.rs index ea8b859d6c..a6e093e80d 100644 --- a/sdk/src/types/api/core.rs +++ b/sdk/src/types/api/core.rs @@ -4,13 +4,11 @@ use alloc::{ boxed::Box, collections::{BTreeMap, BTreeSet}, - format, string::String, vec::Vec, }; use serde::{Deserialize, Serialize}; -use serde_json::Value; use crate::{ types::block::{ @@ -23,7 +21,7 @@ use crate::{ slot::{EpochIndex, SlotCommitment, SlotCommitmentId, SlotIndex}, BlockDto, BlockId, }, - utils::serde::{option_string, prefix_hex_bytes, string}, + utils::serde::{option_string, string}, }; /// Response of GET /api/core/v3/info. @@ -539,76 +537,3 @@ pub struct UtxoChangesResponse { // pub output: Output, // pub output_id_proof: OutputIdProof, // } - -/// The proof of the output identifier. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct OutputIdProof { - pub slot: SlotIndex, - pub output_index: u16, - pub transaction_commitment: String, - pub output_commitment_proof: OutputCommitmentProof, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -#[serde(untagged)] -pub enum OutputCommitmentProof { - HashableNode(HashableNode), - LeafHash(LeafHash), - ValueHash(ValueHash), -} - -impl<'de> Deserialize<'de> for OutputCommitmentProof { - fn deserialize>(d: D) -> Result { - let value = Value::deserialize(d)?; - Ok( - match value - .get("type") - .and_then(Value::as_u64) - .ok_or_else(|| serde::de::Error::custom("invalid output commitment proof type"))? - as u8 - { - 0 => Self::HashableNode( - serde_json::from_value::(value) - .map_err(|e| serde::de::Error::custom(format!("cannot deserialize hashable node: {e}")))?, - ), - 1 => Self::LeafHash( - serde_json::from_value::(value) - .map_err(|e| serde::de::Error::custom(format!("cannot deserialize leaf hash: {e}")))?, - ), - 2 => Self::ValueHash( - serde_json::from_value::(value) - .map_err(|e| serde::de::Error::custom(format!("cannot deserialize value hash: {e}")))?, - ), - _ => return Err(serde::de::Error::custom("invalid output commitment proof")), - }, - ) - } -} - -/// Node contains the hashes of the left and right children of a node in the tree. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -pub struct HashableNode { - #[serde(rename = "type")] - pub kind: u8, - pub l: Box, - pub r: Box, -} - -/// Leaf Hash contains the hash of a leaf in the tree. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -pub struct LeafHash { - #[serde(rename = "type")] - pub kind: u8, - #[serde(with = "prefix_hex_bytes")] - pub hash: [u8; 32], -} - -/// Value Hash contains the hash of the value for which the proof is being computed. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -pub struct ValueHash { - #[serde(rename = "type")] - pub kind: u8, - #[serde(with = "prefix_hex_bytes")] - pub hash: [u8; 32], -} diff --git a/sdk/src/types/block/output/mod.rs b/sdk/src/types/block/output/mod.rs index 06cec2e5e2..01945ecf00 100644 --- a/sdk/src/types/block/output/mod.rs +++ b/sdk/src/types/block/output/mod.rs @@ -7,6 +7,7 @@ mod delegation; mod metadata; mod native_token; mod output_id; +mod output_id_proof; mod state_transition; mod storage_score; mod token_scheme; @@ -50,17 +51,15 @@ pub(crate) use self::{ feature::{MetadataFeatureLength, TagFeatureLength}, native_token::NativeTokenCount, output_id::OutputIndex, + output_id_proof::OutputIdProof, unlock_condition::AddressUnlockCondition, }; -use crate::types::{ - api::core::OutputIdProof, - block::{ - address::Address, - protocol::{CommittableAgeRange, ProtocolParameters, WorkScore, WorkScoreParameters}, - semantic::SemanticValidationContext, - slot::SlotIndex, - Error, - }, +use crate::types::block::{ + address::Address, + protocol::{CommittableAgeRange, ProtocolParameters, WorkScore, WorkScoreParameters}, + semantic::SemanticValidationContext, + slot::SlotIndex, + Error, }; /// The maximum number of outputs of a transaction. diff --git a/sdk/src/types/block/output/output_id_proof.rs b/sdk/src/types/block/output/output_id_proof.rs new file mode 100644 index 0000000000..7f7bab6933 --- /dev/null +++ b/sdk/src/types/block/output/output_id_proof.rs @@ -0,0 +1,90 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use alloc::{boxed::Box, string::String}; + +#[cfg(feature = "serde")] +use {crate::utils::serde::prefix_hex_bytes, alloc::format, serde::de::Deserialize, serde_json::Value}; + +use crate::types::block::slot::SlotIndex; + +/// The proof of the output identifier. +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +pub struct OutputIdProof { + pub slot: SlotIndex, + pub output_index: u16, + pub transaction_commitment: String, + pub output_commitment_proof: OutputCommitmentProof, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(untagged))] +pub enum OutputCommitmentProof { + HashableNode(HashableNode), + LeafHash(LeafHash), + ValueHash(ValueHash), +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for OutputCommitmentProof { + fn deserialize>(d: D) -> Result { + let value = Value::deserialize(d)?; + Ok( + match value + .get("type") + .and_then(Value::as_u64) + .ok_or_else(|| serde::de::Error::custom("invalid output commitment proof type"))? + as u8 + { + 0 => Self::HashableNode( + serde_json::from_value::(value) + .map_err(|e| serde::de::Error::custom(format!("cannot deserialize hashable node: {e}")))?, + ), + 1 => Self::LeafHash( + serde_json::from_value::(value) + .map_err(|e| serde::de::Error::custom(format!("cannot deserialize leaf hash: {e}")))?, + ), + 2 => Self::ValueHash( + serde_json::from_value::(value) + .map_err(|e| serde::de::Error::custom(format!("cannot deserialize value hash: {e}")))?, + ), + _ => return Err(serde::de::Error::custom("invalid output commitment proof")), + }, + ) + } +} + +/// Node contains the hashes of the left and right children of a node in the tree. +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct HashableNode { + #[cfg_attr(feature = "serde", serde(rename = "type"))] + pub kind: u8, + pub l: Box, + pub r: Box, +} + +/// Leaf Hash contains the hash of a leaf in the tree. +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct LeafHash { + #[cfg_attr(feature = "serde", serde(rename = "type"))] + pub kind: u8, + #[cfg_attr(feature = "serde", serde(with = "prefix_hex_bytes"))] + pub hash: [u8; 32], +} + +/// Value Hash contains the hash of the value for which the proof is being computed. +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct ValueHash { + #[cfg_attr(feature = "serde", serde(rename = "type"))] + pub kind: u8, + #[cfg_attr(feature = "serde", serde(with = "prefix_hex_bytes"))] + pub hash: [u8; 32], +} diff --git a/sdk/src/wallet/operations/syncing/addresses/outputs.rs b/sdk/src/wallet/operations/syncing/addresses/outputs.rs index ee4640127b..1a3424fbd1 100644 --- a/sdk/src/wallet/operations/syncing/addresses/outputs.rs +++ b/sdk/src/wallet/operations/syncing/addresses/outputs.rs @@ -1,13 +1,10 @@ // Copyright 2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use std::collections::HashMap; - use instant::Instant; use crate::{ client::secret::SecretManage, - types::block::address::Address, wallet::{ constants::PARALLEL_REQUESTS_AMOUNT, task, @@ -25,7 +22,7 @@ where pub(crate) async fn get_outputs_from_address_output_ids( &self, addresses_with_unspent_outputs: Vec, - ) -> crate::wallet::Result<(Vec<(AddressWithUnspentOutputs, Vec)>)> { + ) -> crate::wallet::Result)>> { log::debug!("[SYNC] start get_outputs_from_address_output_ids"); let address_outputs_start_time = Instant::now(); diff --git a/sdk/src/wallet/operations/syncing/mod.rs b/sdk/src/wallet/operations/syncing/mod.rs index b4a0ca8379..b915ebf936 100644 --- a/sdk/src/wallet/operations/syncing/mod.rs +++ b/sdk/src/wallet/operations/syncing/mod.rs @@ -13,7 +13,7 @@ pub use self::options::SyncOptions; use crate::{ client::secret::SecretManage, types::block::{ - address::{AccountAddress, Address, Bech32Address, NftAddress, ToBech32Ext}, + address::{AccountAddress, Address, Bech32Address, NftAddress}, output::{FoundryId, Output, OutputId, OutputMetadata}, }, wallet::{ diff --git a/sdk/src/wallet/types/mod.rs b/sdk/src/wallet/types/mod.rs index b86f1ac41d..9c25c02b50 100644 --- a/sdk/src/wallet/types/mod.rs +++ b/sdk/src/wallet/types/mod.rs @@ -19,9 +19,9 @@ pub use self::{ use crate::{ client::secret::types::InputSigningData, types::{ - api::core::{OutputIdProof, OutputWithMetadataResponse}, + api::core::OutputWithMetadataResponse, block::{ - output::{Output, OutputId, OutputMetadata}, + output::{Output, OutputId, OutputIdProof, OutputMetadata}, payload::signed_transaction::{dto::SignedTransactionPayloadDto, SignedTransactionPayload, TransactionId}, protocol::{CommittableAgeRange, ProtocolParameters}, slot::SlotIndex, From ea3dd8009dcd481aa4fe0f36af50e72bb2679c4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thoralf=20M=C3=BCller?= Date: Fri, 8 Dec 2023 15:51:23 +0100 Subject: [PATCH 8/8] Exports imports --- sdk/src/types/block/output/mod.rs | 2 +- sdk/tests/wallet/events.rs | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/sdk/src/types/block/output/mod.rs b/sdk/src/types/block/output/mod.rs index 01945ecf00..501a2b93d1 100644 --- a/sdk/src/types/block/output/mod.rs +++ b/sdk/src/types/block/output/mod.rs @@ -42,6 +42,7 @@ pub use self::{ native_token::{NativeToken, NativeTokens, NativeTokensBuilder, TokenId}, nft::{NftId, NftOutput, NftOutputBuilder}, output_id::OutputId, + output_id_proof::{HashableNode, LeafHash, OutputCommitmentProof, OutputIdProof, ValueHash}, state_transition::{StateTransitionError, StateTransitionVerifier}, storage_score::{StorageScore, StorageScoreParameters}, token_scheme::{SimpleTokenScheme, TokenScheme}, @@ -51,7 +52,6 @@ pub(crate) use self::{ feature::{MetadataFeatureLength, TagFeatureLength}, native_token::NativeTokenCount, output_id::OutputIndex, - output_id_proof::OutputIdProof, unlock_condition::AddressUnlockCondition, }; use crate::types::block::{ diff --git a/sdk/tests/wallet/events.rs b/sdk/tests/wallet/events.rs index df87c82ae5..d30eedb89f 100644 --- a/sdk/tests/wallet/events.rs +++ b/sdk/tests/wallet/events.rs @@ -3,20 +3,20 @@ use iota_sdk::{ client::api::PreparedTransactionDataDto, - types::{ - api::core::{LeafHash, OutputCommitmentProof, OutputIdProof}, - block::{ - address::{Address, Bech32Address, Ed25519Address}, - input::{Input, UtxoInput}, - output::{unlock_condition::AddressUnlockCondition, BasicOutput, Output}, - payload::signed_transaction::{Transaction, TransactionHash, TransactionId}, - protocol::protocol_parameters, - rand::{ - mana::rand_mana_allotment, - output::{rand_basic_output, rand_output_metadata}, - }, - slot::SlotIndex, + types::block::{ + address::{Address, Bech32Address, Ed25519Address}, + input::{Input, UtxoInput}, + output::{ + unlock_condition::AddressUnlockCondition, BasicOutput, LeafHash, Output, OutputCommitmentProof, + OutputIdProof, }, + payload::signed_transaction::{Transaction, TransactionHash, TransactionId}, + protocol::protocol_parameters, + rand::{ + mana::rand_mana_allotment, + output::{rand_basic_output, rand_output_metadata}, + }, + slot::SlotIndex, }, wallet::{ events::types::{