Skip to main content

bark_json/cli/
mod.rs

1#[cfg(feature = "onchain_bdk")]
2pub mod onchain;
3
4use std::borrow::Borrow;
5use std::collections::HashMap;
6use std::str::FromStr;
7use std::time::Duration;
8
9use anyhow::anyhow;
10use bitcoin::secp256k1::PublicKey;
11use bitcoin::{Amount, Txid, SignedAmount, ScriptBuf};
12use chrono::DateTime;
13#[cfg(feature = "utoipa")]
14use utoipa::ToSchema;
15
16use ark::VtxoId;
17use ark::lightning::{Invoice, Offer, PaymentHash, Preimage};
18use bark::lnurllib::lightning_address::LightningAddress;
19use bark::movement::MovementId;
20use bitcoin_ext::{AmountExt, BlockDelta};
21
22use crate::exit::error::ExitError;
23use crate::exit::package::ExitTransactionPackage;
24use crate::exit::ExitState;
25use crate::primitives::{TransactionInfo, WalletVtxoInfo};
26use crate::serde_utils;
27
28#[derive(Debug, Clone, Deserialize, Serialize)]
29#[cfg_attr(feature = "utoipa", derive(ToSchema))]
30pub struct ArkInfo {
31	/// The bitcoin network the server operates on
32	#[cfg_attr(feature = "utoipa", schema(value_type = String))]
33	pub network: bitcoin::Network,
34	/// The Ark server pubkey
35	#[cfg_attr(feature = "utoipa", schema(value_type = String))]
36	pub server_pubkey: PublicKey,
37	/// The pubkey used for blinding unified mailbox IDs
38	#[cfg_attr(feature = "utoipa", schema(value_type = String))]
39	pub mailbox_pubkey: PublicKey,
40	/// The interval between each round
41	#[serde(with = "serde_utils::duration")]
42	#[cfg_attr(feature = "utoipa", schema(value_type = String))]
43	pub round_interval: Duration,
44	/// Number of nonces per round
45	pub nb_round_nonces: usize,
46	/// Delta between exit confirmation and coins becoming spendable
47	pub vtxo_exit_delta: BlockDelta,
48	/// Expiration delta of the VTXO
49	pub vtxo_expiry_delta: BlockDelta,
50	/// The number of blocks after which an HTLC-send VTXO expires once granted.
51	pub htlc_send_expiry_delta: BlockDelta,
52	/// The number of blocks to keep between Lightning and Ark HTLCs expiries
53	pub htlc_expiry_delta: BlockDelta,
54	/// Maximum amount of a VTXO
55	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
56	pub max_vtxo_amount: Option<Amount>,
57	/// The number of confirmations required to register a board vtxo
58	pub required_board_confirmations: usize,
59	/// Maximum CLTV delta server will allow clients to request an
60	/// invoice generation with.
61	pub max_user_invoice_cltv_delta: u16,
62	/// Minimum amount for a board the server will cosign
63	#[serde(rename = "min_board_amount_sat", with = "bitcoin::amount::serde::as_sat")]
64	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
65	pub min_board_amount: Amount,
66	/// offboard feerate in sat per kvb
67	pub offboard_feerate_sat_per_kvb: u64,
68	/// fixed number of vb charged additinally for an offboard
69	/// this is charged after being multiplied with the offboard feerate
70	pub offboard_fixed_fee_vb: u64,
71	/// Indicates whether the Ark server requires clients to either
72	/// provide a VTXO ownership proof, or a lightning receive token
73	/// when preparing a lightning claim.
74	pub ln_receive_anti_dos_required: bool,
75}
76
77impl<T: Borrow<ark::ArkInfo>> From<T> for ArkInfo {
78	fn from(v: T) -> Self {
79		let v = v.borrow();
80	    ArkInfo {
81			network: v.network,
82			server_pubkey: v.server_pubkey,
83			mailbox_pubkey: v.mailbox_pubkey,
84			round_interval: v.round_interval,
85			nb_round_nonces: v.nb_round_nonces,
86			vtxo_exit_delta: v.vtxo_exit_delta,
87			vtxo_expiry_delta: v.vtxo_expiry_delta,
88			htlc_send_expiry_delta: v.htlc_send_expiry_delta,
89			htlc_expiry_delta: v.htlc_expiry_delta,
90			max_vtxo_amount: v.max_vtxo_amount,
91			required_board_confirmations: v.required_board_confirmations,
92			max_user_invoice_cltv_delta: v.max_user_invoice_cltv_delta,
93			min_board_amount: v.min_board_amount,
94			offboard_feerate_sat_per_kvb: v.offboard_feerate.to_sat_per_kwu() * 4,
95			offboard_fixed_fee_vb: v.offboard_fixed_fee_vb,
96			ln_receive_anti_dos_required: v.ln_receive_anti_dos_required,
97		}
98	}
99}
100
101#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
102#[cfg_attr(feature = "utoipa", derive(ToSchema))]
103pub struct Balance {
104	#[serde(rename = "spendable_sat", with = "bitcoin::amount::serde::as_sat")]
105	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
106	pub spendable: Amount,
107	#[serde(rename = "pending_lightning_send_sat", with = "bitcoin::amount::serde::as_sat")]
108	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
109	pub pending_lightning_send: Amount,
110	#[serde(rename = "claimable_lightning_receive_sat", with = "bitcoin::amount::serde::as_sat")]
111	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
112	pub claimable_lightning_receive: Amount,
113	#[serde(rename = "pending_in_round_sat", with = "bitcoin::amount::serde::as_sat")]
114	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
115	pub pending_in_round: Amount,
116	#[serde(rename = "pending_board_sat", with = "bitcoin::amount::serde::as_sat")]
117	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
118	pub pending_board: Amount,
119	#[serde(
120		default,
121		rename = "pending_exit_sat",
122		with = "bitcoin::amount::serde::as_sat::opt",
123		skip_serializing_if = "Option::is_none",
124	)]
125	#[cfg_attr(feature = "utoipa", schema(value_type = u64, nullable=true))]
126	pub pending_exit: Option<Amount>,
127}
128
129impl From<bark::Balance> for Balance {
130	fn from(v: bark::Balance) -> Self {
131		Balance {
132			spendable: v.spendable,
133			pending_in_round: v.pending_in_round,
134			pending_lightning_send: v.pending_lightning_send,
135			claimable_lightning_receive: v.claimable_lightning_receive,
136			pending_exit: v.pending_exit,
137			pending_board: v.pending_board,
138		}
139	}
140}
141
142#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
143#[cfg_attr(feature = "utoipa", derive(ToSchema))]
144pub struct ExitProgressResponse {
145	/// Status of each pending exit transaction
146	pub exits: Vec<ExitProgressStatus>,
147	/// Whether all transactions have been confirmed
148	pub done: bool,
149	/// Block height at which all exit outputs will be spendable
150	pub claimable_height: Option<u32>,
151}
152
153#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
154#[cfg_attr(feature = "utoipa", derive(ToSchema))]
155pub struct ExitProgressStatus {
156	/// The ID of the VTXO that is being unilaterally exited
157	#[cfg_attr(feature = "utoipa", schema(value_type = String))]
158	pub vtxo_id: VtxoId,
159	/// The current state of the exit transaction
160	pub state: ExitState,
161	/// Any error that occurred during the exit process
162	#[serde(default, skip_serializing_if = "Option::is_none")]
163	pub error: Option<ExitError>,
164}
165
166impl From<bark::exit::ExitProgressStatus> for ExitProgressStatus {
167	fn from(v: bark::exit::ExitProgressStatus) -> Self {
168		ExitProgressStatus {
169			vtxo_id: v.vtxo_id,
170			state: v.state.into(),
171			error: v.error.map(ExitError::from),
172		}
173	}
174}
175
176#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
177#[cfg_attr(feature = "utoipa", derive(ToSchema))]
178pub struct ExitTransactionStatus {
179	/// The ID of the VTXO that is being unilaterally exited
180	#[cfg_attr(feature = "utoipa", schema(value_type = String))]
181	pub vtxo_id: VtxoId,
182	/// The current state of the exit transaction
183	pub state: ExitState,
184	/// The history of each state the exit transaction has gone through
185	#[serde(default, skip_serializing_if = "Option::is_none")]
186	pub history: Option<Vec<ExitState>>,
187	/// Each exit transaction package required for the unilateral exit
188	#[serde(default, skip_serializing_if = "Vec::is_empty")]
189	pub transactions: Vec<ExitTransactionPackage>,
190}
191
192impl From<bark::exit::ExitTransactionStatus> for ExitTransactionStatus {
193	fn from(v: bark::exit::ExitTransactionStatus) -> Self {
194		ExitTransactionStatus {
195			vtxo_id: v.vtxo_id,
196			state: v.state.into(),
197			history: v.history.map(|h| h.into_iter().map(ExitState::from).collect()),
198			transactions: v.transactions.into_iter().map(ExitTransactionPackage::from).collect(),
199		}
200	}
201}
202
203/// Describes a completed transition of funds from onchain to offchain.
204#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
205#[cfg_attr(feature = "utoipa", derive(ToSchema))]
206pub struct PendingBoardInfo {
207	/// The funding transaction.
208	/// This is the transaction that has to be confirmed
209	/// onchain for the board to succeed.
210	pub funding_tx: TransactionInfo,
211	/// The IDs of the VTXOs that were created
212	/// in this board.
213	///
214	/// Currently, this is always a vector of length 1
215	#[cfg_attr(feature = "utoipa", schema(value_type = Vec<String>))]
216	pub vtxos: Vec<VtxoId>,
217	/// The amount of the board.
218	#[serde(rename = "amount_sat", with = "bitcoin::amount::serde::as_sat")]
219	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
220	pub amount: Amount,
221	/// The ID of the movement associated with this board.
222	pub movement_id: u32,
223}
224
225impl From<bark::persist::models::PendingBoard> for PendingBoardInfo {
226	fn from(v: bark::persist::models::PendingBoard) -> Self {
227		PendingBoardInfo {
228			funding_tx: v.funding_tx.into(),
229			vtxos: v.vtxos,
230			amount: v.amount,
231			movement_id: v.movement_id.0,
232		}
233	}
234}
235
236#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
237#[serde(rename_all = "kebab-case")]
238#[cfg_attr(feature = "utoipa", derive(ToSchema))]
239pub enum MovementStatus {
240	/// The default status of a new [Movement]. Should be treated as in-progress.
241	Pending,
242	/// The [Movement] has completed successfully.
243	Successful,
244	/// The [Movement] failed to complete due to an error. Note; this does not mean that VTXOs or
245	/// user funds didn't change, old VTXOs may be consumed and new ones produced.
246	Failed,
247	/// A [Movement] was canceled, either by the protocol (e.g., lightning payments) or by the
248	/// user.
249	Canceled,
250}
251
252impl From<bark::movement::MovementStatus> for MovementStatus {
253	fn from(v: bark::movement::MovementStatus) -> Self {
254		match v {
255			bark::movement::MovementStatus::Pending => Self::Pending,
256			bark::movement::MovementStatus::Successful => Self::Successful,
257			bark::movement::MovementStatus::Failed => Self::Failed,
258			bark::movement::MovementStatus::Canceled => Self::Canceled,
259		}
260	}
261}
262
263/// Describes an attempted movement of offchain funds within the [bark::Wallet].
264#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
265#[cfg_attr(feature = "utoipa", derive(ToSchema))]
266pub struct Movement {
267	/// The internal ID of the movement.
268	#[cfg_attr(feature = "utoipa", schema(value_type = u32))]
269	pub id: MovementId,
270	/// The status of the movement.
271	pub status: MovementStatus,
272	/// Contains information about the subsystem that created the movement as well as the purpose
273	/// of the movement.
274	pub subsystem: MovementSubsystem,
275	/// Miscellaneous metadata for the movement. This is JSON containing arbitrary information as
276	/// defined by the subsystem that created the movement.
277	#[serde(default, skip_serializing_if = "Option::is_none")]
278	pub metadata: Option<HashMap<String, serde_json::Value>>,
279	/// How much the movement was expected to increase or decrease the balance by. This is always an
280	/// estimate and often discounts any applicable fees.
281	#[serde(rename="intended_balance_sat", with="bitcoin::amount::serde::as_sat")]
282	#[cfg_attr(feature = "utoipa", schema(value_type = i64))]
283	pub intended_balance: SignedAmount,
284	/// How much the wallet balance actually changed by. Positive numbers indicate an increase and
285	/// negative numbers indicate a decrease. This is often inclusive of applicable fees, and it
286	/// should be the most accurate number.
287	#[serde(rename="effective_balance_sat", with="bitcoin::amount::serde::as_sat")]
288	#[cfg_attr(feature = "utoipa", schema(value_type = i64))]
289	pub effective_balance: SignedAmount,
290	/// How much the movement cost the user in offchain fees. If there are applicable onchain fees
291	/// they will not be included in this value but, depending on the subsystem, could be found in
292	/// the metadata.
293	#[serde(rename="offchain_fee_sat", with="bitcoin::amount::serde::as_sat")]
294	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
295	pub offchain_fee: Amount,
296	/// A list of external recipients that received funds from this movement.
297	pub sent_to: Vec<MovementDestination>,
298	/// Describes the means by which the wallet received funds in this movement. This could include
299	/// BOLT11 invoices or other useful data.
300	pub received_on: Vec<MovementDestination>,
301	/// A list of [Vtxo](ark::Vtxo) IDs that were consumed by this movement and
302	/// are either locked or unavailable.
303	#[cfg_attr(feature = "utoipa", schema(value_type = Vec<String>))]
304	pub input_vtxos: Vec<VtxoId>,
305	/// A list of IDs for new VTXOs that were produced as a result of this movement. Often change
306	/// VTXOs will be found here for outbound actions unless this was an inbound action.
307	#[cfg_attr(feature = "utoipa", schema(value_type = Vec<String>))]
308	pub output_vtxos: Vec<VtxoId>,
309	/// A list of IDs for VTXOs that were marked for unilateral exit as a result of this movement.
310	/// This could happen for many reasons, e.g. an unsuccessful lightning payment which can't be
311	/// revoked but is about to expire. VTXOs listed here will result in a reduction of spendable
312	/// balance due to the VTXOs being managed by the [bark::exit::Exit] system.
313	#[cfg_attr(feature = "utoipa", schema(value_type = Vec<String>))]
314	pub exited_vtxos: Vec<VtxoId>,
315	/// Contains the times at which the movement was created, updated and completed.
316	pub time: MovementTimestamp,
317}
318
319impl From<bark::movement::Movement> for Movement {
320	fn from(m: bark::movement::Movement) -> Self {
321		Movement {
322			id: m.id,
323			status: m.status.into(),
324			subsystem: MovementSubsystem::from(m.subsystem),
325			metadata: if m.metadata.is_empty() { None } else { Some(m.metadata) },
326			intended_balance: m.intended_balance,
327			effective_balance: m.effective_balance,
328			offchain_fee: m.offchain_fee,
329			sent_to: m.sent_to.into_iter().map(MovementDestination::from).collect(),
330			received_on: m.received_on.into_iter().map(MovementDestination::from).collect(),
331			input_vtxos: m.input_vtxos,
332			output_vtxos: m.output_vtxos,
333			exited_vtxos: m.exited_vtxos,
334			time: MovementTimestamp::from(m.time),
335		}
336	}
337}
338
339/// Describes a recipient of a movement. This could either be an external recipient in send actions
340/// or it could be the bark wallet itself.
341#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
342#[cfg_attr(feature = "utoipa", derive(ToSchema))]
343pub struct MovementDestination {
344	/// An address, invoice or any other identifier to distinguish the recipient.
345	pub destination: PaymentMethod,
346	/// How many sats the recipient received.
347	#[serde(rename="amount_sat", with="bitcoin::amount::serde::as_sat")]
348	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
349	pub amount: Amount,
350}
351
352impl From<bark::movement::MovementDestination> for MovementDestination {
353	fn from(d: bark::movement::MovementDestination) -> Self {
354		MovementDestination {
355			destination: PaymentMethod::from(d.destination),
356			amount: d.amount,
357		}
358	}
359}
360
361/// Provides a typed mechanism for describing the recipient in a [MovementDestination].
362/// This is a bark-json wrapper that serializes all payment methods as strings for utoipa
363/// compatibility.
364#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
365#[serde(tag = "type", content = "value", rename_all = "kebab-case")]
366pub enum PaymentMethod {
367	/// An [ark::Address] format for bark.
368	Ark(String),
369	/// An onchain [bitcoin::Address].
370	Bitcoin(String),
371	/// An onchain [bitcoin::ScriptBuf] output, typically used for non-address formats like
372	/// OP_RETURN.
373	OutputScript(String),
374	/// Any supported form of lightning invoice, e.g., BOLT11 and BOLT12.
375	Invoice(String),
376	/// A reusable BOLT12 offer for making lightning payments.
377	Offer(String),
378	/// A variant using an email-like lightning address format.
379	LightningAddress(String),
380	/// An alternative payment method that isn't native to bark.
381	Custom(String),
382}
383
384#[cfg(feature = "utoipa")]
385impl utoipa::PartialSchema for PaymentMethod {
386	fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
387		use utoipa::openapi::schema;
388
389		schema::ObjectBuilder::new()
390			.title(Some("PaymentMethod"))
391			.description(Some("A payment method with a type discriminator and string value"))
392			.property(
393				"type",
394				schema::ObjectBuilder::new()
395					.schema_type(schema::SchemaType::Type(schema::Type::String))
396					.enum_values(Some([
397						"ark",
398						"bitcoin",
399						"output-script",
400						"invoice",
401						"offer",
402						"lightning-address",
403						"custom",
404					]))
405					.description(Some("The type of payment method"))
406			)
407			.required("type")
408			.property(
409				"value",
410				schema::ObjectBuilder::new()
411					.schema_type(schema::SchemaType::Type(schema::Type::String))
412					.description(Some("The payment method value (address, invoice, etc.)"))
413			)
414			.required("value")
415			.into()
416	}
417}
418
419#[cfg(feature = "utoipa")]
420impl utoipa::ToSchema for PaymentMethod {
421	fn name() -> std::borrow::Cow<'static, str> {
422		std::borrow::Cow::Borrowed("PaymentMethod")
423	}
424}
425
426impl From<bark::movement::PaymentMethod> for PaymentMethod {
427	fn from(p: bark::movement::PaymentMethod) -> Self {
428		match p {
429			bark::movement::PaymentMethod::Ark(a) => Self::Ark(a.to_string()),
430			bark::movement::PaymentMethod::Bitcoin(b) => Self::Bitcoin(b.assume_checked().to_string()),
431			bark::movement::PaymentMethod::OutputScript(s) => Self::OutputScript(s.to_hex_string()),
432			bark::movement::PaymentMethod::Invoice(i) => Self::Invoice(i.to_string()),
433			bark::movement::PaymentMethod::Offer(o) => Self::Offer(o.to_string()),
434			bark::movement::PaymentMethod::LightningAddress(l) => Self::LightningAddress(l.to_string()),
435			bark::movement::PaymentMethod::Custom(c) => Self::Custom(c),
436		}
437	}
438}
439
440impl TryFrom<PaymentMethod> for bark::movement::PaymentMethod {
441	type Error = anyhow::Error;
442
443	fn try_from(p: PaymentMethod) -> Result<Self, Self::Error> {
444		match p {
445			PaymentMethod::Ark(a) => Ok(bark::movement::PaymentMethod::Ark(
446				ark::Address::from_str(&a)?,
447			)),
448			PaymentMethod::Bitcoin(b) => Ok(bark::movement::PaymentMethod::Bitcoin(
449				bitcoin::Address::from_str(&b)?,
450			)),
451			PaymentMethod::OutputScript(s) => Ok(bark::movement::PaymentMethod::OutputScript(
452				ScriptBuf::from_hex(&s)?,
453			)),
454			PaymentMethod::Invoice(i) => Ok(bark::movement::PaymentMethod::Invoice(
455				Invoice::from_str(&i)?,
456			)),
457			PaymentMethod::Offer(o) => Ok(bark::movement::PaymentMethod::Offer(
458				Offer::from_str(&o).map_err(|e| anyhow!("Failed to parse offer: {:?}", e))?,
459			)),
460			PaymentMethod::LightningAddress(l) => Ok(bark::movement::PaymentMethod::LightningAddress(
461				LightningAddress::from_str(&l)?,
462			)),
463			PaymentMethod::Custom(c) => Ok(bark::movement::PaymentMethod::Custom(c)),
464		}
465	}
466}
467
468/// Contains information about the subsystem that created the movement as well as the purpose
469/// of the movement.
470#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
471#[cfg_attr(feature = "utoipa", derive(ToSchema))]
472pub struct MovementSubsystem {
473	/// The name of the subsystem that created and manages the movement.
474	pub name: String,
475	/// The action responsible for registering the movement.
476	pub kind: String,
477}
478
479impl From<bark::movement::MovementSubsystem> for MovementSubsystem {
480	fn from(s: bark::movement::MovementSubsystem) -> Self {
481		MovementSubsystem {
482			name: s.name,
483			kind: s.kind,
484		}
485	}
486}
487
488/// Contains the times at which the movement was created, updated and completed.
489#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
490#[cfg_attr(feature = "utoipa", derive(ToSchema))]
491pub struct MovementTimestamp {
492	/// When the movement was first created.
493	pub created_at: DateTime<chrono::Local>,
494	/// When the movement was last updated.
495	pub updated_at: DateTime<chrono::Local>,
496	/// The action responsible for registering the movement.
497	#[serde(default, skip_serializing_if = "Option::is_none")]
498	pub completed_at: Option<DateTime<chrono::Local>>,
499}
500
501impl From<bark::movement::MovementTimestamp> for MovementTimestamp {
502	fn from(t: bark::movement::MovementTimestamp) -> Self {
503		MovementTimestamp {
504			created_at: t.created_at,
505			updated_at: t.updated_at,
506			completed_at: t.completed_at,
507		}
508	}
509}
510
511#[derive(Debug, Clone, Serialize, Deserialize)]
512#[serde(tag = "status", rename_all = "kebab-case")]
513#[cfg_attr(feature = "utoipa", derive(ToSchema))]
514pub enum RoundStatus {
515	/// Failed to sync round
516	SyncError {
517		error: String,
518	},
519	/// The round was successful and is fully confirmed
520	Confirmed {
521		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
522		funding_txid: Txid,
523	},
524	/// Round successful but not fully confirmed
525	Unconfirmed {
526		#[cfg_attr(feature = "utoipa", schema(value_type = String))]
527		funding_txid: Txid,
528	},
529	/// We have unsigned funding transactions that might confirm
530	Pending,
531	/// The round failed
532	Failed {
533		error: String,
534	},
535	/// The round canceled
536	Canceled,
537}
538
539impl RoundStatus {
540	/// Whether this is the final state and it won't change anymore
541	pub fn is_final(&self) -> bool {
542		match self {
543			Self::SyncError { .. } => false,
544			Self::Confirmed { .. } => true,
545			Self::Unconfirmed { .. } => false,
546			Self::Pending { .. } => false,
547			Self::Failed { .. } => true,
548			Self::Canceled => true,
549		}
550	}
551
552	/// Whether it looks like the round succeeded
553	pub fn is_success(&self) -> bool {
554		match self {
555			Self::SyncError { .. } => false,
556			Self::Confirmed { .. } => true,
557			Self::Unconfirmed { .. } => true,
558			Self::Pending { .. } => false,
559			Self::Failed { .. } => false,
560			Self::Canceled => false,
561		}
562	}
563}
564
565impl From<bark::round::RoundStatus> for RoundStatus {
566	fn from(s: bark::round::RoundStatus) -> Self {
567		match s {
568			bark::round::RoundStatus::Confirmed { funding_txid } => {
569				Self::Confirmed { funding_txid }
570			},
571			bark::round::RoundStatus::Unconfirmed { funding_txid } => {
572				Self::Unconfirmed { funding_txid }
573			},
574			bark::round::RoundStatus::Pending => Self::Pending,
575			bark::round::RoundStatus::Failed { error } => Self::Failed { error },
576			bark::round::RoundStatus::Canceled => Self::Canceled,
577		}
578	}
579}
580
581#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
582#[cfg_attr(feature = "utoipa", derive(ToSchema))]
583pub struct InvoiceInfo {
584	/// The invoice string
585	pub invoice: String,
586}
587
588#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
589#[cfg_attr(feature = "utoipa", derive(ToSchema))]
590pub struct OffboardResult {
591	/// The transaction id of the offboard transaction
592	#[cfg_attr(feature = "utoipa", schema(value_type = String))]
593	pub offboard_txid: Txid,
594}
595
596#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
597#[cfg_attr(feature = "utoipa", derive(ToSchema))]
598pub struct LightningReceiveInfo {
599	/// The amount of the lightning receive
600	#[serde(rename = "amount_sat", with = "bitcoin::amount::serde::as_sat")]
601	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
602	pub amount: Amount,
603	/// The payment hash linked to the lightning receive info
604	#[cfg_attr(feature = "utoipa", schema(value_type = String))]
605	pub payment_hash: PaymentHash,
606	/// The payment preimage linked to the lightning receive info
607	#[cfg_attr(feature = "utoipa", schema(value_type = String))]
608	pub payment_preimage: Preimage,
609	/// The timestamp at which the preimage was revealed
610	pub preimage_revealed_at: Option<chrono::DateTime<chrono::Local>>,
611	/// The timestamp at which the lightning receive was finished
612	pub finished_at: Option<chrono::DateTime<chrono::Local>>,
613	/// The invoice string
614	pub invoice: String,
615	/// The HTLC VTXOs granted by the server for the lightning receive
616	///
617	/// Only present if the lightning HTLC has been received by the server.
618	#[cfg_attr(feature = "utoipa", schema(value_type = Vec<WalletVtxoInfo>, nullable = true))]
619	pub htlc_vtxos: Option<Vec<WalletVtxoInfo>>,
620}
621
622impl From<bark::persist::models::LightningReceive> for LightningReceiveInfo {
623	fn from(v: bark::persist::models::LightningReceive) -> Self {
624		LightningReceiveInfo {
625			payment_hash: v.payment_hash,
626			payment_preimage: v.payment_preimage,
627			preimage_revealed_at: v.preimage_revealed_at,
628			invoice: v.invoice.to_string(),
629			htlc_vtxos: v.htlc_vtxos.map(|vtxos| vtxos.into_iter()
630				.map(crate::primitives::WalletVtxoInfo::from).collect()),
631			amount: v.invoice.amount_milli_satoshis().map(Amount::from_msat_floor)
632				.unwrap_or(Amount::ZERO),
633			finished_at: v.finished_at,
634		}
635	}
636}
637
638#[cfg(test)]
639mod test {
640	use bitcoin::FeeRate;
641	use super::*;
642
643	#[test]
644	fn ark_info_fields() {
645		//! the purpose of this test is to fail if we add a field to
646		//! ark::ArkInfo but we forgot to add it to the ArkInfo here
647
648		#[allow(unused)]
649		fn convert(j: ArkInfo) -> ark::ArkInfo {
650			ark::ArkInfo {
651				network: j.network,
652				server_pubkey: j.server_pubkey,
653				mailbox_pubkey: j.mailbox_pubkey,
654				round_interval: j.round_interval,
655				nb_round_nonces: j.nb_round_nonces,
656				vtxo_exit_delta: j.vtxo_exit_delta,
657				vtxo_expiry_delta: j.vtxo_expiry_delta,
658				htlc_send_expiry_delta: j.htlc_send_expiry_delta,
659				htlc_expiry_delta: j.htlc_expiry_delta,
660				max_vtxo_amount: j.max_vtxo_amount,
661				required_board_confirmations: j.required_board_confirmations,
662				max_user_invoice_cltv_delta: j.max_user_invoice_cltv_delta,
663				min_board_amount: j.min_board_amount,
664				offboard_feerate: FeeRate::from_sat_per_kwu(j.offboard_feerate_sat_per_kvb / 4),
665				offboard_fixed_fee_vb: j.offboard_fixed_fee_vb,
666				ln_receive_anti_dos_required: j.ln_receive_anti_dos_required,
667			}
668		}
669	}
670}
671