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 Signing Error: Unable to sign claim: {error}")]
37	ClaimSigningError { error: String },
38
39	#[error("Cyclic Exit Transactions Error: The exit transactions for VTXO {vtxo} are cyclic")]
40	CyclicExitTransactions {
41		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
42		vtxo: VtxoId
43	},
44
45	#[error("Database Store Failure: Unable to update exit VTXO {vtxo_id} in the database: {error}")]
46	DatabaseVtxoStoreFailure {
47		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
48		vtxo_id: VtxoId,
49		error: String
50	},
51
52	#[error("Database Retrieval Failure: Unable to get child tx: {error}")]
53	DatabaseChildRetrievalFailure { error: String },
54
55	#[error("Dust Limit Error: The dust limit for a VTXO is {dust} but the balance is only {vtxo}")]
56	DustLimit {
57		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
58		vtxo: Amount,
59		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
60		dust: Amount
61	},
62
63	#[error("Exit Package Broadcast Failure: Unable to broadcast exit transaction package {txid}: {error}")]
64	ExitPackageBroadcastFailure {
65		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
66		txid: Txid,
67		error: String
68	},
69
70	#[error("Exit Package Finalize Failure: Unable to create exit transaction package: {error}")]
71	ExitPackageFinalizeFailure { error: String },
72
73	#[error("Exit Package Store Failure: Unable to store exit transaction package {txid}: {error}")]
74	ExitPackageStoreFailure {
75		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
76		txid: Txid,
77		error: String
78	},
79
80	#[error("Insufficient Confirmed Funds: {needed} is needed but only {available} is available")]
81	InsufficientConfirmedFunds {
82		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
83		needed: Amount,
84		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
85		available: Amount
86	},
87
88	#[error("Insufficient Fee Error: Your balance is {balance} but an estimated {total_fee} (fee rate of {fee_rate}) is required to exit the VTXO")]
89	InsufficientFeeToStart {
90		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
91		balance: Amount,
92		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
93		total_fee: Amount,
94		#[serde(rename = "fee_rate_kwu")]
95		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
96		fee_rate: FeeRate,
97	},
98
99	#[error("Internal Error: An unexpected problem occurred, {error}")]
100	InternalError { error: String },
101
102	#[error("Invalid Exit Transaction Status: Exit tx {txid} has an invalid status ({status}): {error}")]
103	InvalidExitTransactionStatus {
104		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
105		txid: Txid,
106		status: ExitTxStatus,
107		error: String
108	},
109
110	#[error("Invalid Wallet State: {error}")]
111	InvalidWalletState { error: String },
112
113	#[error("Missing Anchor Output: Malformed exit tx {txid}")]
114	MissingAnchorOutput { #[cfg_attr(feature = "utoipa", schema(value_type = String))] txid: Txid },
115
116	#[error("Missing VTXO Transaction: Couldn't find exit tx {txid}")]
117	MissingExitTransaction { #[cfg_attr(feature = "utoipa", schema(value_type = String))] txid: Txid },
118
119	#[error("Movement Registration Failure: {error}")]
120	MovementRegistrationFailure { error: String },
121
122	#[error("Tip Retrieval Failure: Unable to retrieve the blockchain tip height: {error}")]
123	TipRetrievalFailure { error: String },
124
125	#[error("Transaction Retrieval Failure: Unable to check the status of TX {txid}: {error}")]
126	TransactionRetrievalFailure { #[cfg_attr(feature = "utoipa", schema(value_type = String))] txid: Txid, error: String },
127
128	#[error("VTXO Not Spendable Error: Attempted to claim a VTXO which is not in a spendable state: {vtxo}")]
129	VtxoNotClaimable { #[cfg_attr(feature = "utoipa", schema(value_type = String))] vtxo: VtxoId },
130
131	#[error("VTXO ScriptPubKey Invalid: {error}")]
132	VtxoScriptPubKeyInvalid { error: String },
133}
134
135impl From<bark::exit::models::ExitError> for ExitError {
136	fn from(v: bark::exit::models::ExitError) -> Self {
137		match v {
138			bark::exit::models::ExitError::AncestorRetrievalFailure { txid, error } => {
139				ExitError::AncestorRetrievalFailure { txid, error }
140			},
141			bark::exit::models::ExitError::BlockRetrievalFailure { height, error } => {
142				ExitError::BlockRetrievalFailure { height, error }
143			},
144			bark::exit::models::ExitError::ClaimMissingInputs => {
145				ExitError::ClaimMissingInputs
146			},
147			bark::exit::models::ExitError::ClaimFeeExceedsOutput { needed, output } => {
148				ExitError::ClaimFeeExceedsOutput { needed, output }
149			},
150			bark::exit::models::ExitError::ClaimSigningError { error } => {
151				ExitError::ClaimSigningError { error }
152			},
153			bark::exit::models::ExitError::CyclicExitTransactions { vtxo } => {
154				ExitError::CyclicExitTransactions { vtxo }
155			},
156			bark::exit::models::ExitError::DatabaseVtxoStoreFailure { vtxo_id, error } => {
157				ExitError::DatabaseVtxoStoreFailure { vtxo_id, error }
158			},
159			bark::exit::models::ExitError::DatabaseChildRetrievalFailure { error } => {
160				ExitError::DatabaseChildRetrievalFailure { error }
161			},
162			bark::exit::models::ExitError::DustLimit { vtxo, dust } => {
163				ExitError::DustLimit { vtxo, dust }
164			},
165			bark::exit::models::ExitError::ExitPackageBroadcastFailure { txid, error } => {
166				ExitError::ExitPackageBroadcastFailure { txid, error }
167			},
168			bark::exit::models::ExitError::ExitPackageFinalizeFailure { error } => {
169				ExitError::ExitPackageFinalizeFailure { error }
170			},
171			bark::exit::models::ExitError::ExitPackageStoreFailure { txid, error } => {
172				ExitError::ExitPackageStoreFailure { txid, error }
173			},
174			bark::exit::models::ExitError::InsufficientConfirmedFunds { needed, available } => {
175				ExitError::InsufficientConfirmedFunds { needed, available }
176			},
177			bark::exit::models::ExitError::InsufficientFeeToStart { balance, total_fee, fee_rate } => {
178				ExitError::InsufficientFeeToStart { balance, total_fee, fee_rate }
179			},
180			bark::exit::models::ExitError::InternalError { error } => {
181				ExitError::InternalError { error }
182			},
183			bark::exit::models::ExitError::InvalidExitTransactionStatus { txid, status, error } => {
184				ExitError::InvalidExitTransactionStatus { txid, status: status.into(), error }
185			},
186			bark::exit::models::ExitError::InvalidWalletState { error } => {
187				ExitError::InvalidWalletState { error }
188			},
189			bark::exit::models::ExitError::MissingAnchorOutput { txid } => {
190				ExitError::MissingAnchorOutput { txid }
191			},
192			bark::exit::models::ExitError::MissingExitTransaction { txid } => {
193				ExitError::MissingExitTransaction { txid }
194			},
195			bark::exit::models::ExitError::MovementRegistrationFailure { error } => {
196				ExitError::MovementRegistrationFailure { error }
197			},
198			bark::exit::models::ExitError::TipRetrievalFailure { error } => {
199				ExitError::TipRetrievalFailure { error }
200			},
201			bark::exit::models::ExitError::TransactionRetrievalFailure { txid, error } => {
202				ExitError::TransactionRetrievalFailure { txid, error }
203			},
204			bark::exit::models::ExitError::VtxoNotClaimable { vtxo } => {
205				ExitError::VtxoNotClaimable { vtxo }
206			},
207			bark::exit::models::ExitError::VtxoScriptPubKeyInvalid { error } => {
208				ExitError::VtxoScriptPubKeyInvalid { error }
209			},
210		}
211	}
212}
213
214#[cfg(test)]
215mod test {
216	use super::*;
217	#[test]
218	fn json_roundtrip() {
219		let err = ExitError::InvalidWalletState { error: "none shall pass".into() };
220		let json = serde_json::to_string(&err).unwrap();
221		let err2 = serde_json::from_str::<ExitError>(&json).unwrap();
222		assert_eq!(err, err2);
223	}
224}