Skip to main content

bark_json/exit/
error.rs

1use bitcoin::{Amount, FeeRate, Txid};
2use thiserror::Error;
3
4use ark::VtxoId;
5use bitcoin_ext::BlockHeight;
6#[cfg(feature = "utoipa")]
7use utoipa::ToSchema;
8
9use crate::exit::states::ExitTxStatus;
10
11#[derive(Clone, Debug, Error, PartialEq, Eq, Deserialize, Serialize)]
12#[cfg_attr(feature = "utoipa", derive(ToSchema))]
13#[serde(tag = "type", rename_all = "kebab-case")]
14pub enum ExitError {
15	#[error("Transaction Retrieval Failure: Unable to retrieve ancestral data for TX {txid}: {error}")]
16	AncestorRetrievalFailure {
17		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
18		txid: Txid,
19		error: String
20	},
21
22	#[error("Block Retrieval Failure: Unable to retrieve a block at height {height}: {error}")]
23	BlockRetrievalFailure { height: BlockHeight, error: String },
24
25	#[error("Claim Missing Inputs: No inputs given to claim")]
26	ClaimMissingInputs,
27
28	#[error("Claim Fee Exceeds Output: Cost to claim exits was {needed}, but the total output was {output}")]
29	ClaimFeeExceedsOutput {
30		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
31		needed: Amount,
32		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
33		output: Amount,
34	},
35
36	#[error("Claim Missing Signable Clause: Couldn't find a signable clause for VTXO {vtxo}")]
37	ClaimMissingSignableClause {
38		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
39		vtxo: VtxoId,
40	},
41
42	#[error("Claim Signing Error: Unable to sign claim: {error}")]
43	ClaimSigningError { error: String },
44
45	#[error("Cyclic Exit Transactions Error: The exit transactions for VTXO {vtxo} are cyclic")]
46	CyclicExitTransactions {
47		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
48		vtxo: VtxoId
49	},
50
51	#[error("Database Store Failure: Unable to update exit VTXO {vtxo_id} in the database: {error}")]
52	DatabaseVtxoStoreFailure {
53		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
54		vtxo_id: VtxoId,
55		error: String
56	},
57
58	#[error("Database Retrieval Failure: Unable to get child tx: {error}")]
59	DatabaseChildRetrievalFailure { error: String },
60
61	#[error("Dust Limit Error: The dust limit for a VTXO is {dust} but the balance is only {vtxo}")]
62	DustLimit {
63		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
64		vtxo: Amount,
65		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
66		dust: Amount
67	},
68
69	#[error("Exit Package Broadcast Failure: Unable to broadcast exit transaction package {txid}: {error}")]
70	ExitPackageBroadcastFailure {
71		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
72		txid: Txid,
73		error: String
74	},
75
76	#[error("Exit Package Finalize Failure: Unable to create exit transaction package: {error}")]
77	ExitPackageFinalizeFailure { error: String },
78
79	#[error("Exit Package Store Failure: Unable to store exit transaction package {txid}: {error}")]
80	ExitPackageStoreFailure {
81		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
82		txid: Txid,
83		error: String
84	},
85
86	#[error("Insufficient Confirmed Funds: {needed} is needed but only {available} is available")]
87	InsufficientConfirmedFunds {
88		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
89		needed: Amount,
90		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
91		available: Amount
92	},
93
94	#[error("Insufficient Fee Error: Your balance is {balance} but an estimated {total_fee} (fee rate of {fee_rate}) is required to exit the VTXO")]
95	InsufficientFeeToStart {
96		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
97		balance: Amount,
98		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
99		total_fee: Amount,
100		#[serde(rename = "fee_rate_sat_per_kvb", with = "crate::serde_utils::fee_rate_sat_per_kvb")]
101		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
102		fee_rate: FeeRate,
103		#[deprecated(note = "use fee_rate_sat_per_kvb instead")]
104		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
105		fee_rate_kwu: u64,
106	},
107
108	#[error("Internal Error: An unexpected problem occurred, {error}")]
109	InternalError { error: String },
110
111	#[error("Invalid Exit Transaction Status: Exit tx {txid} has an invalid status ({status}): {error}")]
112	InvalidExitTransactionStatus {
113		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
114		txid: Txid,
115		status: ExitTxStatus,
116		error: String
117	},
118
119	#[error("Invalid LockTime ({tip}): {error}")]
120	InvalidLocktime { tip: BlockHeight, error: String },
121
122	#[error("Invalid Wallet State: {error}")]
123	InvalidWalletState { error: String },
124
125	#[error("Missing Anchor Output: Malformed exit tx {txid}")]
126	MissingAnchorOutput { #[cfg_attr(feature = "utoipa", schema(value_type = String))] txid: Txid },
127
128	#[error("Missing VTXO Transaction: Couldn't find exit tx {txid}")]
129	MissingExitTransaction { #[cfg_attr(feature = "utoipa", schema(value_type = String))] txid: Txid },
130
131	#[error("Movement Registration Failure: {error}")]
132	MovementRegistrationFailure { error: String },
133
134	#[error("Tip Retrieval Failure: Unable to retrieve the blockchain tip height: {error}")]
135	TipRetrievalFailure { error: String },
136
137	#[error("Transaction Retrieval Failure: Unable to check the status of TX {txid}: {error}")]
138	TransactionRetrievalFailure { #[cfg_attr(feature = "utoipa", schema(value_type = String))] txid: Txid, error: String },
139
140	#[error("VTXO Not Spendable Error: Attempted to claim a VTXO which is not in a spendable state: {vtxo}")]
141	VtxoNotClaimable { #[cfg_attr(feature = "utoipa", schema(value_type = String))] vtxo: VtxoId },
142
143	#[error("VTXO ScriptPubKey Invalid: {error}")]
144	VtxoScriptPubKeyInvalid { error: String },
145}
146
147impl From<bark::exit::ExitError> for ExitError {
148	fn from(v: bark::exit::ExitError) -> Self {
149		match v {
150			bark::exit::ExitError::AncestorRetrievalFailure { txid, error } => {
151				ExitError::AncestorRetrievalFailure { txid, error }
152			},
153			bark::exit::ExitError::BlockRetrievalFailure { height, error } => {
154				ExitError::BlockRetrievalFailure { height, error }
155			},
156			bark::exit::ExitError::ClaimMissingInputs => {
157				ExitError::ClaimMissingInputs
158			},
159			bark::exit::ExitError::ClaimFeeExceedsOutput { needed, output } => {
160				ExitError::ClaimFeeExceedsOutput { needed, output }
161			},
162			bark::exit::ExitError::ClaimMissingSignableClause { vtxo } => {
163				ExitError::ClaimMissingSignableClause { vtxo }
164			},
165			bark::exit::ExitError::ClaimSigningError { error } => {
166				ExitError::ClaimSigningError { error }
167			},
168			bark::exit::ExitError::CyclicExitTransactions { vtxo } => {
169				ExitError::CyclicExitTransactions { vtxo }
170			},
171			bark::exit::ExitError::DatabaseVtxoStoreFailure { vtxo_id, error } => {
172				ExitError::DatabaseVtxoStoreFailure { vtxo_id, error }
173			},
174			bark::exit::ExitError::DatabaseChildRetrievalFailure { error } => {
175				ExitError::DatabaseChildRetrievalFailure { error }
176			},
177			bark::exit::ExitError::DustLimit { vtxo, dust } => {
178				ExitError::DustLimit { vtxo, dust }
179			},
180			bark::exit::ExitError::ExitPackageBroadcastFailure { txid, error } => {
181				ExitError::ExitPackageBroadcastFailure { txid, error }
182			},
183			bark::exit::ExitError::ExitPackageFinalizeFailure { error } => {
184				ExitError::ExitPackageFinalizeFailure { error }
185			},
186			bark::exit::ExitError::ExitPackageStoreFailure { txid, error } => {
187				ExitError::ExitPackageStoreFailure { txid, error }
188			},
189			bark::exit::ExitError::InsufficientConfirmedFunds { needed, available } => {
190				ExitError::InsufficientConfirmedFunds { needed, available }
191			},
192			bark::exit::ExitError::InsufficientFeeToStart { balance, total_fee, fee_rate } => {
193				let fee_rate_kwu = fee_rate.to_sat_per_kwu();
194				ExitError::InsufficientFeeToStart { balance, total_fee, fee_rate, fee_rate_kwu }
195			},
196			bark::exit::ExitError::InternalError { error } => {
197				ExitError::InternalError { error }
198			},
199			bark::exit::ExitError::InvalidExitTransactionStatus { txid, status, error } => {
200				ExitError::InvalidExitTransactionStatus { txid, status: status.into(), error }
201			},
202			bark::exit::ExitError::InvalidLocktime { tip, error } => {
203				ExitError::InvalidLocktime { tip, error }
204			},
205			bark::exit::ExitError::InvalidWalletState { error } => {
206				ExitError::InvalidWalletState { error }
207			},
208			bark::exit::ExitError::MissingAnchorOutput { txid } => {
209				ExitError::MissingAnchorOutput { txid }
210			},
211			bark::exit::ExitError::MissingExitTransaction { txid } => {
212				ExitError::MissingExitTransaction { txid }
213			},
214			bark::exit::ExitError::MovementRegistrationFailure { error } => {
215				ExitError::MovementRegistrationFailure { error }
216			},
217			bark::exit::ExitError::TipRetrievalFailure { error } => {
218				ExitError::TipRetrievalFailure { error }
219			},
220			bark::exit::ExitError::TransactionRetrievalFailure { txid, error } => {
221				ExitError::TransactionRetrievalFailure { txid, error }
222			},
223			bark::exit::ExitError::VtxoNotClaimable { vtxo } => {
224				ExitError::VtxoNotClaimable { vtxo }
225			},
226			bark::exit::ExitError::VtxoScriptPubKeyInvalid { error } => {
227				ExitError::VtxoScriptPubKeyInvalid { error }
228			},
229		}
230	}
231}
232
233#[cfg(test)]
234mod test {
235	use super::*;
236	#[test]
237	fn json_roundtrip() {
238		let err = ExitError::InvalidWalletState { error: "none shall pass".into() };
239		let json = serde_json::to_string(&err).unwrap();
240		let err2 = serde_json::from_str::<ExitError>(&json).unwrap();
241		assert_eq!(err, err2);
242	}
243}