From 501adbec4e3c9ee501725d3551633c5dda1b373e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thoralf=20M=C3=BCller?= Date: Mon, 14 Aug 2023 19:15:29 +0200 Subject: [PATCH] Start taking it over from https://github.com/iotaledger/iota-sdk/pull/700 --- sdk/src/client/node_api/core/routes.rs | 12 ++-- sdk/src/types/api/core/response.rs | 70 ++++++++++++++----- sdk/src/types/block/semantic.rs | 3 + sdk/src/wallet/account/operations/reissue.rs | 20 +++--- .../operations/syncing/transactions.rs | 18 +++-- 5 files changed, 82 insertions(+), 41 deletions(-) diff --git a/sdk/src/client/node_api/core/routes.rs b/sdk/src/client/node_api/core/routes.rs index 8f1d38177b..7d22bf715f 100644 --- a/sdk/src/client/node_api/core/routes.rs +++ b/sdk/src/client/node_api/core/routes.rs @@ -142,7 +142,7 @@ impl ClientInner { } /// Finds a block by its ID and returns it as object. - /// GET /api/core/v3/blocks/{BlockId} + /// GET /api/core/v3/blocks/{blockId} pub async fn get_block(&self, block_id: &BlockId) -> Result { let path = &format!("api/core/v3/blocks/{block_id}"); @@ -160,7 +160,7 @@ impl ClientInner { } /// Finds a block by its ID and returns it as raw bytes. - /// GET /api/core/v3/blocks/{BlockId} + /// GET /api/core/v3/blocks/{blockId} pub async fn get_block_raw(&self, block_id: &BlockId) -> Result> { let path = &format!("api/core/v3/blocks/{block_id}"); @@ -172,7 +172,7 @@ impl ClientInner { } /// Returns the metadata of a block. - /// GET /api/core/v3/blocks/{BlockId}/metadata + /// GET /api/core/v3/blocks/{blockId}/metadata pub async fn get_block_metadata(&self, block_id: &BlockId) -> Result { let path = &format!("api/core/v3/blocks/{block_id}/metadata"); @@ -225,7 +225,7 @@ impl ClientInner { .await } - /// Returns the block that was included in the ledger for a given transaction ID, as object. + /// Returns the earliest block containing the transaction that get confirmed, as object. /// GET /api/core/v3/transactions/{transactionId}/included-block pub async fn get_included_block(&self, transaction_id: &TransactionId) -> Result { let path = &format!("api/core/v3/transactions/{transaction_id}/included-block"); @@ -243,7 +243,7 @@ impl ClientInner { )?) } - /// Returns the block that was included in the ledger for a given transaction ID, as object, as raw bytes. + /// Returns earliest the block containing the transaction that get confirmed, as raw bytes. /// GET /api/core/v3/transactions/{transactionId}/included-block pub async fn get_included_block_raw(&self, transaction_id: &TransactionId) -> Result> { let path = &format!("api/core/v3/transactions/{transaction_id}/included-block"); @@ -255,7 +255,7 @@ impl ClientInner { .await } - /// Returns the metadata of the block that was included in the ledger for a given TransactionId. + /// Returns the metadata of the earliest block containing the tx that get confirmed. /// GET /api/core/v3/transactions/{transactionId}/included-block/metadata pub async fn get_included_block_metadata(&self, transaction_id: &TransactionId) -> Result { let path = &format!("api/core/v3/transactions/{transaction_id}/included-block/metadata"); diff --git a/sdk/src/types/api/core/response.rs b/sdk/src/types/api/core/response.rs index a284dc045b..af6d016acc 100644 --- a/sdk/src/types/api/core/response.rs +++ b/sdk/src/types/api/core/response.rs @@ -6,6 +6,7 @@ use alloc::{string::String, vec::Vec}; use crate::types::block::{ output::{dto::OutputDto, OutputId, OutputMetadata, OutputWithMetadata}, protocol::ProtocolParameters, + semantic::ConflictReason, slot::SlotIndex, BlockId, }; @@ -117,20 +118,60 @@ pub struct SubmitBlockResponse { pub block_id: BlockId, } -/// Describes the ledger inclusion state of a transaction. +/// Describes the state of a block. #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(rename_all = "camelCase") )] -pub enum LedgerInclusionState { - Conflicting, - Included, - NoTransaction, +pub enum BlockState { + // Stored but not confirmed or contains not yet included transaction. + Pending, + // Confirmed with the first level of knowledge. + Confirmed, + // Included and cannot be reverted anymore. + Finalized, + // Rejected by the node, and user should reissue payload if it contains one. + Rejected, + // Not successfully issued due to failure reason. + Failed, } -/// Response of GET /api/core/v3/blocks/{block_id}/metadata. +/// Describes the state of a transaction. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +pub enum TransactionState { + // Stored but not confirmed or contains not yet included transaction. + Pending, + // Confirmed with the first level of knowledge. + Confirmed, + // Included and cannot be reverted anymore. + Finalized, + // The block is not successfully issued due to failure reason. + Failed, +} + +/// Describes the reason of a block state. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +#[non_exhaustive] +#[repr(u8)] +pub enum BlockStateReason { + Invalid = 1, + OrphanedCongestionControl = 2, + OrphanedNegativeManaBalance = 3, +} + +/// Response of GET /api/core/v3/blocks/{blockId}/metadata. /// Returns the metadata of a block. #[derive(Clone, Debug, Eq, PartialEq)] #[cfg_attr( @@ -140,22 +181,17 @@ pub enum LedgerInclusionState { )] pub struct BlockMetadataResponse { pub block_id: BlockId, - pub parents: Vec, - pub is_solid: bool, - #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] - pub referenced_by_milestone_index: Option, - #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] - pub milestone_index: Option, #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] - pub ledger_inclusion_state: Option, + pub block_state: Option, + pub strong_parents: Vec, #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] - pub conflict_reason: Option, + pub weak_parents: Option>, #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] - pub white_flag_index: Option, + pub shallow_like_parents: Option>, #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] - pub should_promote: Option, + pub tx_state: Option, #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] - pub should_reattach: Option, + pub tx_state_reason: Option, } /// Response of GET /api/core/v3/outputs/{output_id}. diff --git a/sdk/src/types/block/semantic.rs b/sdk/src/types/block/semantic.rs index f60cda7d9c..9878ca6926 100644 --- a/sdk/src/types/block/semantic.rs +++ b/sdk/src/types/block/semantic.rs @@ -45,12 +45,15 @@ impl std::error::Error for ConflictError {} #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[packable(unpack_error = ConflictError)] #[packable(tag_type = u8, with_error = ConflictError::InvalidConflict)] +// TODO may want to rename this to TransactionStateReason pub enum ConflictReason { /// The block has no conflict. None = 0, /// The referenced Utxo was already spent. InputUtxoAlreadySpent = 1, /// The referenced Utxo was already spent while confirming this milestone. + /// TODO weird + /// * `2` - denotes that the transaction is conflicting with another transaction. InputUtxoAlreadySpentInThisMilestone = 2, /// The referenced Utxo cannot be found. InputUtxoNotFound = 3, diff --git a/sdk/src/wallet/account/operations/reissue.rs b/sdk/src/wallet/account/operations/reissue.rs index 58232c0a2c..caab2011e0 100644 --- a/sdk/src/wallet/account/operations/reissue.rs +++ b/sdk/src/wallet/account/operations/reissue.rs @@ -4,7 +4,7 @@ use crate::{ client::{secret::SecretManage, Error as ClientError}, types::{ - api::core::response::LedgerInclusionState, + api::core::response::{BlockState, TransactionState}, block::{ payload::{transaction::TransactionId, Payload}, BlockId, @@ -86,18 +86,22 @@ where let mut conflicting = false; for (index, block_id_) in block_ids.clone().iter().enumerate() { let block_metadata = self.client().get_block_metadata(block_id_).await?; - if let Some(inclusion_state) = block_metadata.ledger_inclusion_state { - match inclusion_state { - LedgerInclusionState::Included | LedgerInclusionState::NoTransaction => { - return Ok(*block_id_); - } + if let Some(transaction_state) = block_metadata.tx_state { + match transaction_state { + TransactionState::Finalized => return Ok(*block_id_), // only set it as conflicting here and don't return, because another reissued block could // have the included transaction - LedgerInclusionState::Conflicting => conflicting = true, + // TODO: check if the comment above is still correct with IOTA 2.0 + TransactionState::Failed => conflicting = true, + // TODO: what to do when confirmed? + _ => {} }; } // Only reissue latest attachment of the block - if index == block_ids_len - 1 && block_metadata.should_reattach.unwrap_or(false) { + let should_reissue = block_metadata + .block_state + .map_or(false, |block_state| block_state == BlockState::Rejected); + if index == block_ids_len - 1 && should_reissue { let reissued_block = self .client() .finish_basic_block_builder( diff --git a/sdk/src/wallet/account/operations/syncing/transactions.rs b/sdk/src/wallet/account/operations/syncing/transactions.rs index 7c6eecaf59..ff5c9bf4af 100644 --- a/sdk/src/wallet/account/operations/syncing/transactions.rs +++ b/sdk/src/wallet/account/operations/syncing/transactions.rs @@ -4,7 +4,7 @@ use crate::{ client::secret::SecretManage, types::{ - api::core::response::LedgerInclusionState, + api::core::response::TransactionState, block::{input::Input, output::OutputId, payload::transaction::TransactionEssence, BlockId}, }, utils::unix_timestamp_now, @@ -102,9 +102,10 @@ where if let Some(block_id) = transaction.block_id { match self.client().get_block_metadata(&block_id).await { Ok(metadata) => { - if let Some(inclusion_state) = metadata.ledger_inclusion_state { - match inclusion_state { - LedgerInclusionState::Included => { + // TODO: use tx_state or block_state? + if let Some(tx_state) = metadata.tx_state { + match tx_state { + TransactionState::Finalized | TransactionState::Confirmed => { log::debug!( "[SYNC] confirmed transaction {transaction_id} in block {}", metadata.block_id @@ -118,7 +119,7 @@ where &mut spent_output_ids, ); } - LedgerInclusionState::Conflicting => { + TransactionState::Failed => { // try to get the included block, because maybe only this attachment is // conflicting because it got confirmed in another block if let Ok(included_block) = @@ -144,11 +145,8 @@ where ); } } - LedgerInclusionState::NoTransaction => { - unreachable!( - "We should only get the metadata for blocks with a transaction payload" - ) - } + // Do nothing, just need to wait a bit more + TransactionState::Pending => {} } } else { // no need to reissue if one input got spent