use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use strum::AsRefStr;
use thiserror::Error;
use crate::{
base_types::{AuthorityName, EpochId, ObjectRef, TransactionDigest},
committee::StakeUnit,
crypto::{AuthorityStrongQuorumSignInfo, ConciseAuthorityPublicKeyBytes},
effects::{
CertifiedTransactionEffects, TransactionEffects, TransactionEvents,
VerifiedCertifiedTransactionEffects,
},
error::IotaError,
messages_checkpoint::CheckpointSequenceNumber,
object::Object,
transaction::Transaction,
};
pub type QuorumDriverResult = Result<QuorumDriverResponse, QuorumDriverError>;
pub type QuorumDriverEffectsQueueResult =
Result<(Transaction, QuorumDriverResponse), (TransactionDigest, QuorumDriverError)>;
pub const NON_RECOVERABLE_ERROR_MSG: &str =
"Transaction has non recoverable errors from at least 1/3 of validators";
#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Error, Hash, AsRefStr)]
pub enum QuorumDriverError {
#[error("QuorumDriver internal error: {0:?}.")]
QuorumDriverInternal(IotaError),
#[error("Invalid user signature: {0:?}.")]
InvalidUserSignature(IotaError),
#[error(
"Failed to sign transaction by a quorum of validators because of locked objects: {:?}, retried a conflicting transaction {:?}, success: {:?}",
conflicting_txes,
retried_tx,
retried_tx_success
)]
ObjectsDoubleUsed {
conflicting_txes: BTreeMap<TransactionDigest, (Vec<(AuthorityName, ObjectRef)>, StakeUnit)>,
retried_tx: Option<TransactionDigest>,
retried_tx_success: Option<bool>,
},
#[error("Transaction timed out before reaching finality")]
TimeoutBeforeFinality,
#[error(
"Transaction failed to reach finality with transient error after {total_attempts} attempts."
)]
FailedWithTransientErrorAfterMaximumAttempts { total_attempts: u32 },
#[error("{NON_RECOVERABLE_ERROR_MSG}: {errors:?}.")]
NonRecoverableTransactionError { errors: GroupedErrors },
#[error(
"Transaction is not processed because {overloaded_stake} of validators by stake are overloaded with certificates pending execution."
)]
SystemOverload {
overloaded_stake: StakeUnit,
errors: GroupedErrors,
},
#[error("Transaction is already finalized but with different user signatures")]
TxAlreadyFinalizedWithDifferentUserSignatures,
#[error(
"Transaction is not processed because {overload_stake} of validators are overloaded and asked client to retry after {retry_after_secs}."
)]
SystemOverloadRetryAfter {
overload_stake: StakeUnit,
errors: GroupedErrors,
retry_after_secs: u64,
},
}
pub type GroupedErrors = Vec<(IotaError, StakeUnit, Vec<ConciseAuthorityPublicKeyBytes>)>;
#[derive(Serialize, Deserialize, Clone, Debug, schemars::JsonSchema)]
pub enum ExecuteTransactionRequestType {
WaitForEffectsCert,
WaitForLocalExecution,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum EffectsFinalityInfo {
Certified(AuthorityStrongQuorumSignInfo),
Checkpointed(EpochId, CheckpointSequenceNumber),
}
pub type IsTransactionExecutedLocally = bool;
#[derive(Debug, Clone)]
pub struct QuorumDriverResponse {
pub effects_cert: VerifiedCertifiedTransactionEffects,
pub events: Option<TransactionEvents>,
pub input_objects: Option<Vec<Object>>,
pub output_objects: Option<Vec<Object>>,
pub auxiliary_data: Option<Vec<u8>>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ExecuteTransactionRequestV1 {
pub transaction: Transaction,
pub include_events: bool,
pub include_input_objects: bool,
pub include_output_objects: bool,
pub include_auxiliary_data: bool,
}
impl ExecuteTransactionRequestV1 {
pub fn new<T: Into<Transaction>>(transaction: T) -> Self {
Self {
transaction: transaction.into(),
include_events: true,
include_input_objects: false,
include_output_objects: false,
include_auxiliary_data: false,
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ExecuteTransactionResponseV1 {
pub effects: FinalizedEffects,
pub events: Option<TransactionEvents>,
pub input_objects: Option<Vec<Object>>,
pub output_objects: Option<Vec<Object>>,
pub auxiliary_data: Option<Vec<u8>>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct FinalizedEffects {
pub effects: TransactionEffects,
pub finality_info: EffectsFinalityInfo,
}
impl FinalizedEffects {
pub fn new_from_effects_cert(effects_cert: CertifiedTransactionEffects) -> Self {
let (data, sig) = effects_cert.into_data_and_sig();
Self {
effects: data,
finality_info: EffectsFinalityInfo::Certified(sig),
}
}
pub fn epoch(&self) -> EpochId {
match &self.finality_info {
EffectsFinalityInfo::Certified(cert) => cert.epoch,
EffectsFinalityInfo::Checkpointed(epoch, _) => *epoch,
}
}
}