Skip to main content

iota_types/
transaction_driver_types.rs

1// Copyright (c) 2021, Facebook, Inc. and its affiliates
2// Copyright (c) Mysten Labs, Inc.
3// Modifications Copyright (c) 2024 IOTA Stiftung
4// SPDX-License-Identifier: Apache-2.0
5
6use std::{collections::BTreeMap, time::Duration};
7
8use iota_sdk_types::{ObjectReference, TransactionDigest, TransactionEffects, TransactionEvents};
9use serde::{Deserialize, Serialize};
10use strum::AsRefStr;
11use thiserror::Error;
12
13use crate::{
14    base_types::{AuthorityName, EpochId},
15    committee::StakeUnit,
16    crypto::{AuthorityStrongQuorumSignInfo, ConciseAuthorityPublicKeyBytes},
17    effects::{CertifiedTransactionEffects, VerifiedCertifiedTransactionEffects},
18    error::{ErrorCategory, IotaError},
19    messages_checkpoint::CheckpointSequenceNumber,
20    object::Object,
21    transaction::{TransactionEnvelope, VerifiedTransaction},
22};
23
24pub type TransactionDriverResult = Result<TransactionDriverResponse, TransactionSubmissionError>;
25
26pub type TransactionDriverEffectsQueueResult = Result<
27    (TransactionEnvelope, TransactionDriverResponse),
28    (TransactionDigest, TransactionSubmissionError),
29>;
30
31pub const NON_RECOVERABLE_ERROR_MSG: &str =
32    "Transaction has non recoverable errors from at least 1/3 of validators";
33
34/// Client facing errors regarding transaction submission via Transaction
35/// Driver. Every invariant needs detailed documents to instruct client
36/// handling.
37#[derive(Eq, PartialEq, Clone, Debug, Error, Hash, AsRefStr)]
38pub enum TransactionSubmissionError {
39    #[error("TransactionDriver internal error: {0}.")]
40    TransactionDriverInternalError(IotaError),
41    #[error("Invalid user signature: {0}.")]
42    InvalidUserSignature(IotaError),
43    #[error(
44        "Failed to sign transaction by a quorum of validators because of locked objects: {conflicting_txes:?}"
45    )]
46    ObjectsDoubleUsed {
47        conflicting_txes:
48            BTreeMap<TransactionDigest, (Vec<(AuthorityName, ObjectReference)>, StakeUnit)>,
49    },
50    #[error("Transaction timed out before reaching finality")]
51    TimeoutBeforeFinality,
52    #[error(
53        "Transaction timed out before reaching finality. Last recorded retriable error: {last_error}"
54    )]
55    TimeoutBeforeFinalityWithErrors {
56        last_error: String,
57        attempts: u32,
58        timeout: Duration,
59    },
60    #[error(
61        "Transaction failed to reach finality with transient error after {total_attempts} attempts."
62    )]
63    FailedWithTransientErrorAfterMaximumAttempts { total_attempts: u32 },
64    #[error("{NON_RECOVERABLE_ERROR_MSG}: {errors:?}.")]
65    NonRecoverableTransactionError { errors: GroupedErrors },
66    #[error(
67        "Transaction is not processed because {overloaded_stake} of validators by stake are overloaded with certificates pending execution."
68    )]
69    SystemOverload {
70        overloaded_stake: StakeUnit,
71        errors: GroupedErrors,
72    },
73    #[error(
74        "Transaction is not processed because {overload_stake} of validators are overloaded and asked client to retry after {retry_after_secs}."
75    )]
76    SystemOverloadRetryAfter {
77        overload_stake: StakeUnit,
78        errors: GroupedErrors,
79        retry_after_secs: u64,
80    },
81    #[error("Transaction is already finalized but with different user signatures")]
82    TxAlreadyFinalizedWithDifferentUserSignatures,
83
84    #[error("Transaction processing failed. Details: {details}")]
85    TransactionFailed {
86        category: ErrorCategory,
87        details: String,
88    },
89}
90
91impl TransactionSubmissionError {
92    pub fn is_retriable(&self) -> bool {
93        match self {
94            Self::TransactionDriverInternalError { .. } => false,
95            Self::InvalidUserSignature { .. } => false,
96            Self::ObjectsDoubleUsed { .. } => false,
97            Self::TimeoutBeforeFinality => true,
98            Self::TimeoutBeforeFinalityWithErrors { .. } => true,
99            Self::FailedWithTransientErrorAfterMaximumAttempts { .. } => true,
100            Self::NonRecoverableTransactionError { .. } => false,
101            Self::SystemOverload { .. } => true,
102            Self::SystemOverloadRetryAfter { .. } => true,
103            Self::TxAlreadyFinalizedWithDifferentUserSignatures => false,
104            Self::TransactionFailed { category, .. } => category.is_submission_retriable(),
105        }
106    }
107}
108
109pub type GroupedErrors = Vec<(IotaError, StakeUnit, Vec<ConciseAuthorityPublicKeyBytes>)>;
110
111#[derive(Debug)]
112pub enum TransactionType {
113    SingleWriter, // Txes that only use owned objects and/or immutable objects
114    SharedObject, // Txes that use at least one shared object
115}
116
117#[derive(Clone, Debug)]
118pub struct TransactionDriverRequest {
119    pub transaction: VerifiedTransaction,
120}
121
122#[derive(Debug, Clone)]
123pub struct TransactionDriverResponse {
124    pub effects_cert: VerifiedCertifiedTransactionEffects,
125    pub events: Option<TransactionEvents>,
126    // Input objects will only be populated in the happy path
127    pub input_objects: Option<Vec<Object>>,
128    // Output objects will only be populated in the happy path
129    pub output_objects: Option<Vec<Object>>,
130    pub auxiliary_data: Option<Vec<u8>>,
131}
132
133/// Proof of finality of transaction effects.
134#[derive(Serialize, Deserialize, Clone, Debug)]
135pub enum EffectsFinalityInfo {
136    /// Effects are certified by a quorum of validators.
137    Certified(AuthorityStrongQuorumSignInfo),
138
139    /// Effects are included in a checkpoint.
140    Checkpointed(EpochId, CheckpointSequenceNumber),
141
142    /// A quorum of validators have acknowledged effects.
143    QuorumExecuted(EpochId),
144
145    /// Effects from a single validator without quorum certification.
146    /// The caller MUST wait for local checkpoint execution before returning
147    /// these to the client, as they have not been certified by a quorum.
148    UncertifiedSingleValidator(EpochId),
149}
150
151#[derive(Serialize, Deserialize, Clone, Debug)]
152pub struct FinalizedEffects {
153    pub effects: TransactionEffects,
154    pub finality_info: EffectsFinalityInfo,
155}
156
157impl FinalizedEffects {
158    pub fn new_from_effects_cert(effects_cert: CertifiedTransactionEffects) -> Self {
159        let (data, sig) = effects_cert.into_data_and_sig();
160        Self {
161            effects: data,
162            finality_info: EffectsFinalityInfo::Certified(sig),
163        }
164    }
165
166    pub fn epoch(&self) -> EpochId {
167        match &self.finality_info {
168            EffectsFinalityInfo::Certified(cert) => cert.epoch,
169            EffectsFinalityInfo::Checkpointed(epoch, _)
170            | EffectsFinalityInfo::QuorumExecuted(epoch)
171            | EffectsFinalityInfo::UncertifiedSingleValidator(epoch) => *epoch,
172        }
173    }
174
175    pub fn data(&self) -> &TransactionEffects {
176        &self.effects
177    }
178}