Skip to main content

kaspa_mining_errors/
mempool.rs

1use kaspa_consensus_core::{
2    errors::tx::TxRuleError,
3    tx::{TransactionId, TransactionOutpoint},
4};
5use thiserror::Error;
6
7#[derive(Error, Debug, Clone, PartialEq, Eq)]
8pub enum RuleError {
9    /// A consensus transaction rule error
10    ///
11    /// Note that following variants are converted:
12    ///
13    /// - TxRuleError::ImmatureCoinbaseSpend => RuleError::RejectImmatureSpend
14    /// - TxRuleError::MissingTxOutpoints => RuleError::RejectMissingOutpoint
15    #[error(transparent)]
16    RejectTxRule(TxRuleError),
17
18    #[error("at least one outpoint of the transaction is lacking a matching UTXO entry")]
19    RejectMissingOutpoint,
20
21    #[error("at least one outpoint of the transaction is impossible to have a matching UTXO entry")]
22    RejectImpossibleOutpoint,
23
24    #[error("transaction {0} was already accepted by the consensus")]
25    RejectAlreadyAccepted(TransactionId),
26
27    #[error("transaction {0} is already in the mempool")]
28    RejectDuplicate(TransactionId),
29
30    #[error("coinbase transaction {0} cannot be added to the mempool")]
31    RejectCoinbase(TransactionId),
32
33    #[error("output {0} already spent by transaction {1} in the mempool")]
34    RejectDoubleSpendInMempool(TransactionOutpoint, TransactionId),
35
36    #[error("replace by fee found no double spending transaction in the mempool")]
37    RejectRbfNoDoubleSpend,
38
39    #[error("replace by fee found more than one double spending transaction in the mempool")]
40    RejectRbfTooManyDoubleSpendingTransactions,
41
42    /// a transaction is rejected if the mempool is full
43    #[error("transaction could not be added to the mempool because it's full with transactions with higher priority")]
44    RejectMempoolIsFull,
45
46    /// An error emitted by mining\src\mempool\check_transaction_standard.rs
47    #[error("transaction {0} is not standard: {1}")]
48    RejectNonStandard(TransactionId, String),
49
50    #[error("transaction compute mass of {1} is larger than max allowed size of {2}")]
51    RejectComputeMass(TransactionId, u64, u64),
52
53    #[error("transaction transient (storage) mass of {1} is larger than max allowed size of {2}")]
54    RejectTransientMass(TransactionId, u64, u64),
55
56    #[error("transaction storage mass of {1} is larger than max allowed size of {2}")]
57    RejectStorageMass(TransactionId, u64, u64),
58
59    #[error("transaction gas of {1} is larger than max allowed per-lane gas of {2}")]
60    RejectGas(TransactionId, u64, u64),
61
62    #[error("one of the transaction inputs spends an immature UTXO: {0}")]
63    RejectImmatureSpend(TxRuleError),
64
65    #[error("transaction {0} doesn't exist in transaction pool")]
66    RejectMissingTransaction(TransactionId),
67
68    #[error("orphan transaction size of {0} bytes is larger than max allowed size of {1} bytes")]
69    RejectBadOrphanMass(u64, u64),
70
71    #[error("orphan transaction {0} is already in the orphan pool")]
72    RejectDuplicateOrphan(TransactionId),
73
74    #[error("orphan transaction {0} is double spending an input from already existing orphan {1}")]
75    RejectDoubleSpendOrphan(TransactionId, TransactionId),
76
77    #[error("transaction {0} is an orphan where orphan is disallowed")]
78    RejectDisallowedOrphan(TransactionId),
79
80    #[error("input No. {0} of {1} ({2}) doesn't exist in orphan_ids_by_previous_outpoint")]
81    RejectMissingOrphanOutpoint(usize, TransactionId, TransactionOutpoint),
82
83    #[error("transaction {0} doesn't exist in orphan pool")]
84    RejectMissingOrphanTransaction(TransactionId),
85
86    /// New behavior: a transaction is rejected if the orphan pool is full
87    #[error("number of high-priority transactions in orphan pool ({0}) has reached the maximum allowed ({1})")]
88    RejectOrphanPoolIsFull(usize, u64),
89
90    #[error("transactions in mempool form a cycle")]
91    RejectCycleInMempoolTransactions,
92
93    // TODO: This error is added for the tx_relay flow but is never constructed neither in the golang nor in this version. Discuss if it can be removed.
94    #[error("transaction {0} is invalid")]
95    RejectInvalid(TransactionId),
96
97    #[error("Rejected tx {0} from mempool due to incomputable storage mass")]
98    RejectStorageMassIncomputable(TransactionId),
99}
100
101impl From<NonStandardError> for RuleError {
102    fn from(item: NonStandardError) -> Self {
103        RuleError::RejectNonStandard(*item.transaction_id(), item.to_string())
104    }
105}
106
107impl From<TxRuleError> for RuleError {
108    fn from(item: TxRuleError) -> Self {
109        match item {
110            TxRuleError::ImmatureCoinbaseSpend(_, _, _, _, _) => RuleError::RejectImmatureSpend(item),
111            TxRuleError::MissingTxOutpoints => RuleError::RejectMissingOutpoint,
112            _ => RuleError::RejectTxRule(item),
113        }
114    }
115}
116
117pub type RuleResult<T> = std::result::Result<T, RuleError>;
118
119#[derive(Error, Debug, Clone, PartialEq, Eq)]
120pub enum NonStandardError {
121    // TODO: the three variants below are unreachable, nothing constructs them. Each corresponds to a
122    // rule that is now enforced elsewhere: transaction version, signature script length, per-lane
123    // gas and the zero-gas requirement for native and system subnetworks are all consensus rules,
124    // with per-lane gas additionally checked at mempool admission. Remove all three together with
125    // their `transaction_id` arms once no external consumer matches on them.
126    #[error("transaction version {1} is not in the valid range of {2}-{3}")]
127    RejectVersion(TransactionId, u16, u16, u16),
128
129    #[error("transaction gas of {1} is larger than max allowed per-lane gas of {2}")]
130    RejectGas(TransactionId, u64, u64),
131
132    #[error("transaction input #{1}: signature script size of {2} bytes is larger than the maximum allowed size of {3} bytes")]
133    RejectSignatureScriptSize(TransactionId, usize, u64, u64),
134
135    #[error("transaction output #{1}: the version of the scriptPublicKey is higher than the known version")]
136    RejectScriptPublicKeyVersion(TransactionId, usize),
137
138    #[error("transaction output #{1}: non-standard script form")]
139    RejectOutputScriptClass(TransactionId, usize),
140
141    #[error("transaction input {1}: non-standard script form")]
142    RejectInputScriptClass(TransactionId, usize),
143
144    #[error("transaction has {1} fees which is under the required amount of {2} for compute mass {3}")]
145    RejectInsufficientComputeFee(TransactionId, u64, u64, u64),
146
147    #[error(
148        "transaction has {1} fees which is under the required amount of {2} for normalized transient mass {3} (proportional to transaction byte size)"
149    )]
150    RejectInsufficientTransientFee(TransactionId, u64, u64, u64),
151
152    #[error("transaction input #{1} has {2} signature operations which is more than the allowed max amount of {3}")]
153    RejectSignatureCount(TransactionId, usize, u64, u16),
154}
155
156impl NonStandardError {
157    pub fn transaction_id(&self) -> &TransactionId {
158        match self {
159            NonStandardError::RejectVersion(id, _, _, _) => id,
160            NonStandardError::RejectGas(id, _, _) => id,
161            NonStandardError::RejectSignatureScriptSize(id, _, _, _) => id,
162            NonStandardError::RejectScriptPublicKeyVersion(id, _) => id,
163            NonStandardError::RejectOutputScriptClass(id, _) => id,
164            NonStandardError::RejectInputScriptClass(id, _) => id,
165            NonStandardError::RejectInsufficientComputeFee(id, _, _, _) => id,
166            NonStandardError::RejectInsufficientTransientFee(id, _, _, _) => id,
167            NonStandardError::RejectSignatureCount(id, _, _, _) => id,
168        }
169    }
170}
171
172pub type NonStandardResult<T> = std::result::Result<T, NonStandardError>;