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 #[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 #[error("transaction could not be added to the mempool because it's full with transactions with higher priority")]
44 RejectMempoolIsFull,
45
46 #[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 #[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 #[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 #[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>;