Skip to main content

bark_json/exit/
error.rs

1use bitcoin::{Amount, Txid};
2use thiserror::Error;
3
4use ark::VtxoId;
5use bitcoin_ext::BlockHeight;
6#[cfg(feature = "utoipa")]
7use utoipa::ToSchema;
8
9use crate::exit::ExitStateKind;
10use crate::exit::states::ExitTxStatus;
11
12#[derive(Clone, Debug, Error, PartialEq, Eq, Deserialize, Serialize)]
13#[cfg_attr(feature = "utoipa", derive(ToSchema))]
14#[serde(tag = "type", rename_all = "kebab-case")]
15pub enum ExitError {
16	#[error("Transaction Retrieval Failure: Unable to retrieve ancestral data for TX {txid}: {error}")]
17	AncestorRetrievalFailure {
18		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
19		txid: Txid,
20		error: String
21	},
22
23	#[error("Block Retrieval Failure: Unable to retrieve a block at height {height}: {error}")]
24	BlockRetrievalFailure {
25		#[cfg_attr(feature = "utoipa", schema(value_type = u32))]
26		height: BlockHeight,
27		error: String
28	},
29
30	#[error("Cannot Cancel Exit: The exit for VTXO {vtxo} can no longer be canceled (state: {state})")]
31	CannotCancelExit {
32		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
33		vtxo: VtxoId,
34		state: ExitStateKind,
35	},
36
37	#[error("Claim Missing Inputs: No inputs given to claim")]
38	ClaimMissingInputs,
39
40	#[error("Claim Fee Exceeds Output: Cost to claim exits was {needed}, but the total output was {output}")]
41	ClaimFeeExceedsOutput {
42		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
43		needed: Amount,
44		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
45		output: Amount,
46	},
47
48	#[error("Claim Missing Signable Clause: Couldn't find a signable clause for VTXO {vtxo}")]
49	ClaimMissingSignableClause {
50		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
51		vtxo: VtxoId,
52	},
53
54	#[error("Claim Signing Error: Unable to sign claim: {error}")]
55	ClaimSigningError { error: String },
56
57	#[error("Cyclic Exit Transactions Error: The exit transactions for VTXO {vtxo} are cyclic")]
58	CyclicExitTransactions {
59		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
60		vtxo: VtxoId
61	},
62
63	#[error("Database Store Failure: Unable to update exit VTXO {vtxo_id} in the database: {error}")]
64	DatabaseVtxoStoreFailure {
65		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
66		vtxo_id: VtxoId,
67		error: String
68	},
69
70	#[error("Database Retrieval Failure: Unable to get child tx: {error}")]
71	DatabaseChildRetrievalFailure { error: String },
72
73	#[error("Database Store Failure: Unable to store child tx: {error}")]
74	DatabaseChildStoreFailure { error: String },
75
76	#[error("Dust Limit Error: The dust limit for a VTXO is {dust} but vtxo {vtxo} is only {amount}")]
77	DustLimit {
78		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
79		vtxo: VtxoId,
80		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
81		amount: Amount,
82		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
83		dust: Amount
84	},
85
86	#[error("Exit Package Broadcast Failure: Unable to broadcast exit transaction package {txid}: {error}")]
87	ExitPackageBroadcastFailure {
88		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
89		txid: Txid,
90		error: String
91	},
92
93	#[error("Exit Package Finalize Failure: Unable to create exit transaction package: {error}")]
94	ExitPackageFinalizeFailure { error: String },
95
96	#[error("Exit Package Store Failure: Unable to store exit transaction package {txid}: {error}")]
97	ExitPackageStoreFailure {
98		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
99		txid: Txid,
100		error: String
101	},
102
103	#[error("Exit Tx Already Broadcast: Cannot cancel the exit for VTXO {vtxo}, its final exit tx {txid} has already been broadcast")]
104	ExitTxAlreadyBroadcast {
105		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
106		vtxo: VtxoId,
107		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
108		txid: Txid,
109	},
110
111	#[error("Insufficient Confirmed Funds: {needed} is needed but only {available} is available")]
112	InsufficientConfirmedFunds {
113		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
114		needed: Amount,
115		#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
116		available: Amount
117	},
118
119	#[error("Internal Error: An unexpected problem occurred, {error}")]
120	InternalError { error: String },
121
122	#[error("Invalid Exit Transaction Status: Exit tx {txid} has an invalid status ({status}): {error}")]
123	InvalidExitTransactionStatus {
124		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
125		txid: Txid,
126		status: ExitTxStatus,
127		error: String
128	},
129
130	#[error("Invalid Fee Margin: {margin} must be finite and non-negative and keep the fee in range")]
131	InvalidFeeMargin { margin: String },
132
133	#[error("Invalid LockTime ({tip}): {error}")]
134	InvalidLocktime {
135		#[cfg_attr(feature = "utoipa", schema(value_type = u32))]
136		tip: BlockHeight,
137		error: String
138	},
139
140	#[error("Invalid Wallet State: {error}")]
141	InvalidWalletState { error: String },
142
143	#[error("Missing Anchor Output: Malformed exit tx {txid}")]
144	MissingAnchorOutput { #[cfg_attr(feature = "utoipa", schema(value_type = String))] txid: Txid },
145
146	#[error("Missing VTXO Transaction: Couldn't find exit tx {txid}")]
147	MissingExitTransaction { #[cfg_attr(feature = "utoipa", schema(value_type = String))] txid: Txid },
148
149	#[error("Non-Standard VTXO {vtxo}: exit chain is not relayable: {error}")]
150	NonStandardVtxo {
151		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
152		vtxo: VtxoId,
153		error: String,
154	},
155
156	#[error("Movement Registration Failure: {error}")]
157	MovementRegistrationFailure { error: String },
158
159	#[error("Not Exiting: VTXO {vtxo} has no unilateral exit")]
160	NotExiting { #[cfg_attr(feature = "utoipa", schema(value_type = String))] vtxo: VtxoId },
161
162	#[error("Tip Retrieval Failure: Unable to retrieve the blockchain tip height: {error}")]
163	TipRetrievalFailure { error: String },
164
165	#[error("Transaction Retrieval Failure: Unable to check the status of TX {txid}: {error}")]
166	TransactionRetrievalFailure { #[cfg_attr(feature = "utoipa", schema(value_type = String))] txid: Txid, error: String },
167
168	#[error("VTXO Not Spendable Error: Attempted to claim a VTXO which is not in a spendable state: {vtxo}")]
169	VtxoNotClaimable { #[cfg_attr(feature = "utoipa", schema(value_type = String))] vtxo: VtxoId },
170
171	#[error("Unknown VTXO: {vtxo} is not known to this wallet")]
172	UnknownVtxo { #[cfg_attr(feature = "utoipa", schema(value_type = String))] vtxo: VtxoId },
173
174	#[error("VTXO Already Exited: {vtxo} has already completed its unilateral exit")]
175	VtxoAlreadyExited { #[cfg_attr(feature = "utoipa", schema(value_type = String))] vtxo: VtxoId },
176
177	#[error("VTXO Already Spent: {vtxo} has already been spent and can no longer be exited")]
178	VtxoAlreadySpent { #[cfg_attr(feature = "utoipa", schema(value_type = String))] vtxo: VtxoId },
179
180	#[error("VTXO Swept: an output the exit chain of {vtxo} needs was spent on chain, so it can \
181		no longer be exited")]
182	VtxoSwept { #[cfg_attr(feature = "utoipa", schema(value_type = String))] vtxo: VtxoId },
183
184	#[error("VTXO ScriptPubKey Invalid: {error}")]
185	VtxoScriptPubKeyInvalid { error: String },
186}
187
188impl From<bark::exit::ExitError> for ExitError {
189	fn from(v: bark::exit::ExitError) -> Self {
190		match v {
191			bark::exit::ExitError::AncestorRetrievalFailure { txid, error } => {
192				ExitError::AncestorRetrievalFailure { txid, error }
193			},
194			bark::exit::ExitError::BlockRetrievalFailure { height, error } => {
195				ExitError::BlockRetrievalFailure { height, error }
196			},
197			bark::exit::ExitError::ClaimMissingInputs => {
198				ExitError::ClaimMissingInputs
199			},
200			bark::exit::ExitError::ClaimFeeExceedsOutput { needed, output } => {
201				ExitError::ClaimFeeExceedsOutput { needed, output }
202			},
203			bark::exit::ExitError::ClaimMissingSignableClause { vtxo } => {
204				ExitError::ClaimMissingSignableClause { vtxo }
205			},
206			bark::exit::ExitError::ClaimSigningError { error } => {
207				ExitError::ClaimSigningError { error }
208			},
209			bark::exit::ExitError::CyclicExitTransactions { vtxo } => {
210				ExitError::CyclicExitTransactions { vtxo }
211			},
212			bark::exit::ExitError::DatabaseVtxoStoreFailure { vtxo_id, error } => {
213				ExitError::DatabaseVtxoStoreFailure { vtxo_id, error }
214			},
215			bark::exit::ExitError::DatabaseChildRetrievalFailure { error } => {
216				ExitError::DatabaseChildRetrievalFailure { error }
217			},
218			bark::exit::ExitError::DatabaseChildStoreFailure { error } => {
219				ExitError::DatabaseChildStoreFailure { error }
220			},
221			bark::exit::ExitError::DustLimit { vtxo, amount, dust } => {
222				ExitError::DustLimit { vtxo, amount, dust }
223			},
224			bark::exit::ExitError::ExitPackageBroadcastFailure { txid, error } => {
225				ExitError::ExitPackageBroadcastFailure { txid, error: error.to_string() }
226			},
227			bark::exit::ExitError::ExitPackageFinalizeFailure { error } => {
228				ExitError::ExitPackageFinalizeFailure { error }
229			},
230			bark::exit::ExitError::ExitPackageStoreFailure { txid, error } => {
231				ExitError::ExitPackageStoreFailure { txid, error }
232			},
233			bark::exit::ExitError::ExitTxAlreadyBroadcast { vtxo, txid } => {
234				ExitError::ExitTxAlreadyBroadcast { vtxo, txid }
235			},
236			bark::exit::ExitError::InsufficientConfirmedFunds { needed, available } => {
237				ExitError::InsufficientConfirmedFunds { needed, available }
238			},
239			bark::exit::ExitError::InternalError { error } => {
240				ExitError::InternalError { error }
241			},
242			bark::exit::ExitError::InvalidExitTransactionStatus { txid, status, error } => {
243				ExitError::InvalidExitTransactionStatus { txid, status: status.into(), error }
244			},
245			bark::exit::ExitError::InvalidFeeMargin { margin } => {
246				ExitError::InvalidFeeMargin { margin }
247			},
248			bark::exit::ExitError::InvalidLocktime { tip, error } => {
249				ExitError::InvalidLocktime { tip, error }
250			},
251			bark::exit::ExitError::InvalidWalletState { error } => {
252				ExitError::InvalidWalletState { error }
253			},
254			bark::exit::ExitError::MissingAnchorOutput { txid } => {
255				ExitError::MissingAnchorOutput { txid }
256			},
257			bark::exit::ExitError::MissingExitTransaction { txid } => {
258				ExitError::MissingExitTransaction { txid }
259			},
260			bark::exit::ExitError::NonStandardVtxo { vtxo, error } => {
261				ExitError::NonStandardVtxo { vtxo, error: error.to_string() }
262			},
263			bark::exit::ExitError::MovementRegistrationFailure { error } => {
264				ExitError::MovementRegistrationFailure { error }
265			},
266			bark::exit::ExitError::CannotCancelExit { vtxo, state } => {
267				ExitError::CannotCancelExit { vtxo, state: state.into() }
268			},
269			bark::exit::ExitError::NotExiting { vtxo } => {
270				ExitError::NotExiting { vtxo }
271			},
272			bark::exit::ExitError::TipRetrievalFailure { error } => {
273				ExitError::TipRetrievalFailure { error }
274			},
275			bark::exit::ExitError::TransactionRetrievalFailure { txid, error } => {
276				ExitError::TransactionRetrievalFailure { txid, error }
277			},
278			bark::exit::ExitError::VtxoNotClaimable { vtxo } => {
279				ExitError::VtxoNotClaimable { vtxo }
280			},
281			bark::exit::ExitError::UnknownVtxo { vtxo } => {
282				ExitError::UnknownVtxo { vtxo }
283			},
284			bark::exit::ExitError::VtxoAlreadyExited { vtxo } => {
285				ExitError::VtxoAlreadyExited { vtxo }
286			},
287			bark::exit::ExitError::VtxoAlreadySpent { vtxo } => {
288				ExitError::VtxoAlreadySpent { vtxo }
289			},
290			bark::exit::ExitError::VtxoSwept { vtxo } => {
291				ExitError::VtxoSwept { vtxo }
292			},
293			bark::exit::ExitError::VtxoScriptPubKeyInvalid { error } => {
294				ExitError::VtxoScriptPubKeyInvalid { error }
295			},
296		}
297	}
298}
299
300#[cfg(test)]
301mod test {
302	use super::*;
303	#[test]
304	fn json_roundtrip() {
305		let err = ExitError::InvalidWalletState { error: "none shall pass".into() };
306		let json = serde_json::to_string(&err).unwrap();
307		let err2 = serde_json::from_str::<ExitError>(&json).unwrap();
308		assert_eq!(err, err2);
309	}
310}