sui_types/
quorum_driver_types.rs

1// Copyright (c) 2021, Facebook, Inc. and its affiliates
2// Copyright (c) Mysten Labs, Inc.
3// SPDX-License-Identifier: Apache-2.0
4
5use std::collections::BTreeMap;
6use std::time::Duration;
7
8use crate::base_types::{AuthorityName, EpochId, ObjectRef, TransactionDigest};
9use crate::committee::StakeUnit;
10use crate::crypto::{AuthorityStrongQuorumSignInfo, ConciseAuthorityPublicKeyBytes};
11use crate::effects::{
12    CertifiedTransactionEffects, TransactionEffects, TransactionEvents,
13    VerifiedCertifiedTransactionEffects,
14};
15use crate::error::{ErrorCategory, SuiError};
16use crate::messages_checkpoint::CheckpointSequenceNumber;
17use crate::object::Object;
18use crate::transaction::{Transaction, VerifiedTransaction};
19use serde::{Deserialize, Serialize};
20use strum::AsRefStr;
21use thiserror::Error;
22
23pub type QuorumDriverResult = Result<QuorumDriverResponse, QuorumDriverError>;
24
25pub type QuorumDriverEffectsQueueResult =
26    Result<(Transaction, QuorumDriverResponse), (TransactionDigest, QuorumDriverError)>;
27
28pub const NON_RECOVERABLE_ERROR_MSG: &str =
29    "Transaction has non recoverable errors from at least 1/3 of validators";
30
31/// Client facing errors regarding transaction submission via Quorum Driver.
32/// Every invariant needs detailed documents to instruct client handling.
33#[derive(Eq, PartialEq, Clone, Debug, Error, Hash, AsRefStr)]
34pub enum QuorumDriverError {
35    #[error("QuorumDriver internal error: {0}.")]
36    QuorumDriverInternalError(SuiError),
37    #[error("Invalid user signature: {0}.")]
38    InvalidUserSignature(SuiError),
39    #[error(
40        "Failed to sign transaction by a quorum of validators because of locked objects: {conflicting_txes:?}"
41    )]
42    ObjectsDoubleUsed {
43        conflicting_txes: BTreeMap<TransactionDigest, (Vec<(AuthorityName, ObjectRef)>, StakeUnit)>,
44    },
45    #[error("Transaction timed out before reaching finality")]
46    TimeoutBeforeFinality,
47    #[error(
48        "Transaction timed out before reaching finality. Last recorded retriable error: {last_error}"
49    )]
50    TimeoutBeforeFinalityWithErrors {
51        last_error: String,
52        attempts: u32,
53        timeout: Duration,
54    },
55    #[error(
56        "Transaction failed to reach finality with transient error after {total_attempts} attempts."
57    )]
58    FailedWithTransientErrorAfterMaximumAttempts { total_attempts: u32 },
59    #[error("{NON_RECOVERABLE_ERROR_MSG}: {errors:?}.")]
60    NonRecoverableTransactionError { errors: GroupedErrors },
61    #[error(
62        "Transaction is not processed because {overloaded_stake} of validators by stake are overloaded with certificates pending execution."
63    )]
64    SystemOverload {
65        overloaded_stake: StakeUnit,
66        errors: GroupedErrors,
67    },
68    #[error(
69        "Transaction is not processed because {overload_stake} of validators are overloaded and asked client to retry after {retry_after_secs}."
70    )]
71    SystemOverloadRetryAfter {
72        overload_stake: StakeUnit,
73        errors: GroupedErrors,
74        retry_after_secs: u64,
75    },
76    #[error("Transaction is already finalized but with different user signatures")]
77    TxAlreadyFinalizedWithDifferentUserSignatures,
78
79    // Wrapped error from Transaction Driver.
80    #[error("Transaction processing failed. Details: {details}")]
81    TransactionFailed {
82        category: ErrorCategory,
83        details: String,
84    },
85}
86
87impl QuorumDriverError {
88    pub fn is_retriable(&self) -> bool {
89        match self {
90            QuorumDriverError::QuorumDriverInternalError { .. } => false,
91            QuorumDriverError::InvalidUserSignature { .. } => false,
92            QuorumDriverError::ObjectsDoubleUsed { .. } => false,
93            QuorumDriverError::TimeoutBeforeFinality => true,
94            QuorumDriverError::TimeoutBeforeFinalityWithErrors { .. } => true,
95            QuorumDriverError::FailedWithTransientErrorAfterMaximumAttempts { .. } => true,
96            QuorumDriverError::NonRecoverableTransactionError { .. } => false,
97            QuorumDriverError::SystemOverload { .. } => true,
98            QuorumDriverError::SystemOverloadRetryAfter { .. } => true,
99            QuorumDriverError::TxAlreadyFinalizedWithDifferentUserSignatures => false,
100            QuorumDriverError::TransactionFailed { category, .. } => {
101                category.is_submission_retriable()
102            }
103        }
104    }
105}
106
107pub type GroupedErrors = Vec<(SuiError, StakeUnit, Vec<ConciseAuthorityPublicKeyBytes>)>;
108
109#[derive(Serialize, Deserialize, Clone, Debug, schemars::JsonSchema)]
110pub enum ExecuteTransactionRequestType {
111    WaitForEffectsCert,
112    WaitForLocalExecution,
113}
114
115#[derive(Debug)]
116pub enum TransactionType {
117    SingleWriter, // Txes that only use owned objects and/or immutable objects
118    SharedObject, // Txes that use at least one shared object
119}
120
121/// Proof of finality of transaction effects.
122#[derive(Serialize, Deserialize, Clone, Debug)]
123pub enum EffectsFinalityInfo {
124    /// Effects are certified by a quorum of validators.
125    Certified(AuthorityStrongQuorumSignInfo),
126
127    /// Effects are included in a checkpoint.
128    Checkpointed(EpochId, CheckpointSequenceNumber),
129
130    /// A quorum of validators have acknowledged effects.
131    QuorumExecuted(EpochId),
132}
133
134/// When requested to execute a transaction with WaitForLocalExecution,
135/// TransactionOrchestrator attempts to execute this transaction locally
136/// after it is finalized. This value represents whether the transaction
137/// is confirmed to be executed on this node before the response returns.
138pub type IsTransactionExecutedLocally = bool;
139
140#[derive(Serialize, Deserialize, Clone, Debug)]
141pub enum ExecuteTransactionResponse {
142    EffectsCert(
143        Box<(
144            FinalizedEffects,
145            TransactionEvents,
146            IsTransactionExecutedLocally,
147        )>,
148    ),
149}
150
151#[derive(Clone, Debug)]
152pub struct QuorumDriverRequest {
153    pub transaction: VerifiedTransaction,
154}
155
156#[derive(Debug, Clone)]
157pub struct QuorumDriverResponse {
158    pub effects_cert: VerifiedCertifiedTransactionEffects,
159    // pub events: TransactionEvents,
160    pub events: Option<TransactionEvents>,
161    // Input objects will only be populated in the happy path
162    pub input_objects: Option<Vec<Object>>,
163    // Output objects will only be populated in the happy path
164    pub output_objects: Option<Vec<Object>>,
165    pub auxiliary_data: Option<Vec<u8>>,
166}
167
168#[derive(Serialize, Deserialize, Clone, Debug)]
169pub struct ExecuteTransactionRequestV3 {
170    pub transaction: Transaction,
171
172    pub include_events: bool,
173    pub include_input_objects: bool,
174    pub include_output_objects: bool,
175    pub include_auxiliary_data: bool,
176}
177
178impl ExecuteTransactionRequestV3 {
179    pub fn new_v2<T: Into<Transaction>>(transaction: T) -> Self {
180        Self {
181            transaction: transaction.into(),
182            include_events: true,
183            include_input_objects: false,
184            include_output_objects: false,
185            include_auxiliary_data: false,
186        }
187    }
188}
189
190#[derive(Serialize, Deserialize, Clone, Debug)]
191pub struct ExecuteTransactionResponseV3 {
192    pub effects: FinalizedEffects,
193
194    pub events: Option<TransactionEvents>,
195    // Input objects will only be populated in the happy path
196    pub input_objects: Option<Vec<Object>>,
197    // Output objects will only be populated in the happy path
198    pub output_objects: Option<Vec<Object>>,
199    pub auxiliary_data: Option<Vec<u8>>,
200}
201
202#[derive(Serialize, Deserialize, Clone, Debug)]
203pub struct FinalizedEffects {
204    pub effects: TransactionEffects,
205    pub finality_info: EffectsFinalityInfo,
206}
207
208impl FinalizedEffects {
209    pub fn new_from_effects_cert(effects_cert: CertifiedTransactionEffects) -> Self {
210        let (data, sig) = effects_cert.into_data_and_sig();
211        Self {
212            effects: data,
213            finality_info: EffectsFinalityInfo::Certified(sig),
214        }
215    }
216
217    pub fn epoch(&self) -> EpochId {
218        match &self.finality_info {
219            EffectsFinalityInfo::Certified(cert) => cert.epoch,
220            EffectsFinalityInfo::Checkpointed(epoch, _) => *epoch,
221            EffectsFinalityInfo::QuorumExecuted(epoch) => *epoch,
222        }
223    }
224
225    pub fn data(&self) -> &TransactionEffects {
226        &self.effects
227    }
228}