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