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