1
2use std::borrow::Borrow;
3use std::time::Duration;
4
5use bitcoin::secp256k1::PublicKey;
6use bitcoin::{Address, Amount, FeeRate, Txid, Wtxid, address};
7
8use ark::lightning::{PaymentHash, Preimage};
9use ark::rounds::RoundId;
10use ark::VtxoId;
11use bitcoin_ext::{BlockDelta, BlockHeight};
12#[cfg(feature = "utoipa")]
13use utoipa::ToSchema;
14
15use crate::exit::error::ExitError;
16use crate::exit::package::ExitTransactionPackage;
17use crate::exit::ExitState;
18use crate::primitives::{VtxoInfo, RecipientInfo};
19use crate::{WalletVtxoInfo, serde_utils};
20
21#[derive(Debug, Clone, Deserialize, Serialize)]
22#[cfg_attr(feature = "utoipa", derive(ToSchema))]
23pub struct ArkInfo {
24 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
26 pub network: bitcoin::Network,
27 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
29 pub server_pubkey: PublicKey,
30 #[serde(with = "serde_utils::duration")]
32 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
33 pub round_interval: Duration,
34 pub nb_round_nonces: usize,
36 pub vtxo_exit_delta: BlockDelta,
38 pub vtxo_expiry_delta: BlockDelta,
40 pub htlc_send_expiry_delta: BlockDelta,
42 pub htlc_expiry_delta: BlockDelta,
44 #[cfg_attr(feature = "utoipa", schema(value_type = u64))]
46 pub max_vtxo_amount: Option<Amount>,
47 pub max_arkoor_depth: u16,
49 pub required_board_confirmations: usize,
51 pub max_user_invoice_cltv_delta: u16,
54 pub min_board_amount: Amount,
56}
57
58impl<T: Borrow<ark::ArkInfo>> From<T> for ArkInfo {
59 fn from(v: T) -> Self {
60 let v = v.borrow();
61 ArkInfo {
62 network: v.network,
63 server_pubkey: v.server_pubkey,
64 round_interval: v.round_interval,
65 nb_round_nonces: v.nb_round_nonces,
66 vtxo_exit_delta: v.vtxo_exit_delta,
67 vtxo_expiry_delta: v.vtxo_expiry_delta,
68 htlc_send_expiry_delta: v.htlc_send_expiry_delta,
69 htlc_expiry_delta: v.htlc_expiry_delta,
70 max_vtxo_amount: v.max_vtxo_amount,
71 max_arkoor_depth: v.max_arkoor_depth,
72 required_board_confirmations: v.required_board_confirmations,
73 max_user_invoice_cltv_delta: v.max_user_invoice_cltv_delta,
74 min_board_amount: v.min_board_amount,
75 }
76 }
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
80#[cfg_attr(feature = "utoipa", derive(ToSchema))]
81pub struct LightningReceiveBalance {
82 #[serde(rename = "total_sat", with = "bitcoin::amount::serde::as_sat")]
83 #[cfg_attr(feature = "utoipa", schema(value_type = u64))]
84 pub total: Amount,
85 #[serde(rename = "claimable_sat", with = "bitcoin::amount::serde::as_sat")]
86 #[cfg_attr(feature = "utoipa", schema(value_type = u64))]
87 pub claimable: Amount,
88}
89
90#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
91#[cfg_attr(feature = "utoipa", derive(ToSchema))]
92pub struct Balance {
93 #[serde(rename = "spendable_sat", with = "bitcoin::amount::serde::as_sat")]
94 #[cfg_attr(feature = "utoipa", schema(value_type = u64))]
95 pub spendable: Amount,
96 #[serde(rename = "pending_lightning_send_sat", with = "bitcoin::amount::serde::as_sat")]
97 #[cfg_attr(feature = "utoipa", schema(value_type = u64))]
98 pub pending_lightning_send: Amount,
99 pub pending_lightning_receive: LightningReceiveBalance,
100 #[serde(rename = "pending_in_round_sat", with = "bitcoin::amount::serde::as_sat")]
101 #[cfg_attr(feature = "utoipa", schema(value_type = u64))]
102 pub pending_in_round: Amount,
103 #[serde(rename = "pending_board_sat", with = "bitcoin::amount::serde::as_sat")]
104 #[cfg_attr(feature = "utoipa", schema(value_type = u64))]
105 pub pending_board: Amount,
106 #[serde(
107 default,
108 rename = "pending_exit_sat",
109 with = "bitcoin::amount::serde::as_sat::opt",
110 skip_serializing_if = "Option::is_none",
111 )]
112 #[cfg_attr(feature = "utoipa", schema(value_type = u64, nullable=true))]
113 pub pending_exit: Option<Amount>,
114}
115
116#[derive(Debug, Clone, Deserialize, Serialize)]
117#[cfg_attr(feature = "utoipa", derive(ToSchema))]
118pub struct Config {
119 pub ark: String,
121 pub bitcoind: Option<String>,
123 pub bitcoind_cookie: Option<String>,
125 pub bitcoind_user: Option<String>,
127 pub bitcoind_pass: Option<String>,
129 pub esplora: Option<String>,
131 pub vtxo_refresh_expiry_threshold: BlockHeight,
133 #[serde(rename = "fallback_fee_rate_kvb", with = "serde_utils::fee_rate_sats_per_kvb")]
134 #[cfg_attr(feature = "utoipa", schema(value_type = u64, nullable = true))]
135 pub fallback_fee_rate: Option<FeeRate>,
136}
137
138#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
139#[cfg_attr(feature = "utoipa", derive(ToSchema))]
140pub struct ExitProgressResponse {
141 pub exits: Vec<ExitProgressStatus>,
143 pub done: bool,
145 pub claimable_height: Option<u32>,
147}
148
149#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
150#[cfg_attr(feature = "utoipa", derive(ToSchema))]
151pub struct ExitProgressStatus {
152 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
154 pub vtxo_id: VtxoId,
155 pub state: ExitState,
157 #[serde(default, skip_serializing_if = "Option::is_none")]
159 pub error: Option<ExitError>,
160}
161
162impl From<bark::exit::models::ExitProgressStatus> for ExitProgressStatus {
163 fn from(v: bark::exit::models::ExitProgressStatus) -> Self {
164 ExitProgressStatus {
165 vtxo_id: v.vtxo_id,
166 state: v.state.into(),
167 error: v.error.map(ExitError::from),
168 }
169 }
170}
171
172#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
173#[cfg_attr(feature = "utoipa", derive(ToSchema))]
174pub struct ExitTransactionStatus {
175 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
177 pub vtxo_id: VtxoId,
178 pub state: ExitState,
180 #[serde(default, skip_serializing_if = "Option::is_none")]
182 pub history: Option<Vec<ExitState>>,
183 #[serde(default, skip_serializing_if = "Vec::is_empty")]
185 pub transactions: Vec<ExitTransactionPackage>,
186}
187
188impl From<bark::exit::models::ExitTransactionStatus> for ExitTransactionStatus {
189 fn from(v: bark::exit::models::ExitTransactionStatus) -> Self {
190 ExitTransactionStatus {
191 vtxo_id: v.vtxo_id,
192 state: v.state.into(),
193 history: v.history.map(|h| h.into_iter().map(ExitState::from).collect()),
194 transactions: v.transactions.into_iter().map(ExitTransactionPackage::from).collect(),
195 }
196 }
197}
198
199#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
201#[cfg_attr(feature = "utoipa", derive(ToSchema))]
202pub struct Board {
203 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
207 pub funding_txid: Txid,
208 pub vtxos: Vec<VtxoInfo>,
213}
214
215impl From<bark::Board> for Board {
216 fn from(v: bark::Board) -> Self {
217 Board {
218 funding_txid: v.funding_txid,
219 vtxos: v.vtxos.into_iter().map(VtxoInfo::from).collect(),
220 }
221 }
222}
223
224#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
225#[cfg_attr(feature = "utoipa", derive(ToSchema))]
226pub struct Movement {
227 pub id: u32,
228 #[cfg_attr(feature = "utoipa", schema(value_type = u64))]
230 pub fees: Amount,
231 pub spends: Vec<VtxoInfo>,
233 pub receives: Vec<VtxoInfo>,
235 pub recipients: Vec<RecipientInfo>,
237 pub created_at: String,
239}
240
241
242pub mod onchain {
243 use super::*;
244
245 #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
246 #[cfg_attr(feature = "utoipa", derive(ToSchema))]
247 pub struct Send {
248 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
249 pub txid: Txid,
250 }
251
252 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
253 #[cfg_attr(feature = "utoipa", derive(ToSchema))]
254 pub struct Address {
255 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
256 pub address: bitcoin::Address<bitcoin::address::NetworkUnchecked>,
257 }
258
259 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
260 #[cfg_attr(feature = "utoipa", derive(ToSchema))]
261 pub struct OnchainBalance {
262 #[serde(rename="total_sat", with="bitcoin::amount::serde::as_sat")]
264 #[cfg_attr(feature = "utoipa", schema(value_type = u64))]
265 pub total: Amount,
266 #[serde(rename="trusted_spendable_sat", with="bitcoin::amount::serde::as_sat")]
271 #[cfg_attr(feature = "utoipa", schema(value_type = u64))]
272 pub trusted_spendable: Amount,
273 #[serde(rename="immature_sat", with="bitcoin::amount::serde::as_sat")]
275 #[cfg_attr(feature = "utoipa", schema(value_type = u64))]
276 pub immature: Amount,
277 #[serde(rename="trusted_pending_sat", with="bitcoin::amount::serde::as_sat")]
279 #[cfg_attr(feature = "utoipa", schema(value_type = u64))]
280 pub trusted_pending: Amount,
281 #[serde(rename="untrusted_pending_sat", with="bitcoin::amount::serde::as_sat")]
283 #[cfg_attr(feature = "utoipa", schema(value_type = u64))]
284 pub untrusted_pending: Amount,
285 #[serde(rename="confirmed_sat", with="bitcoin::amount::serde::as_sat")]
287 #[cfg_attr(feature = "utoipa", schema(value_type = u64))]
288 pub confirmed: Amount,
289 }
290}
291
292#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
295#[cfg_attr(feature = "utoipa", derive(ToSchema))]
296pub struct Offboard {
297 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
299 pub round: RoundId,
300 }
302
303impl From<bark::Offboard> for Offboard {
304 fn from(v: bark::Offboard) -> Self {
305 Offboard { round: v.round }
306 }
307}
308
309#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
311#[cfg_attr(feature = "utoipa", derive(ToSchema))]
312pub struct Refresh {
313 pub participate_round: bool,
317 #[cfg_attr(feature = "utoipa", schema(value_type = String, nullable = true))]
319 pub round: Option<RoundId>,
320}
321
322#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
323#[cfg_attr(feature = "utoipa", derive(ToSchema))]
324pub struct InvoiceInfo {
325 pub invoice: String,
327}
328
329#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
330#[cfg_attr(feature = "utoipa", derive(ToSchema))]
331pub struct LightningReceiveInfo {
332 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
334 pub payment_hash: PaymentHash,
335 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
337 pub payment_preimage: Preimage,
338 pub preimage_revealed_at: Option<chrono::DateTime<chrono::Utc>>,
340 pub invoice: String,
342 #[cfg_attr(feature = "utoipa", schema(value_type = Vec<WalletVtxoInfo>, nullable = true))]
346 pub htlc_vtxos: Option<Vec<WalletVtxoInfo>>,
347}
348
349impl From<bark::persist::models::LightningReceive> for LightningReceiveInfo {
350 fn from(v: bark::persist::models::LightningReceive) -> Self {
351 LightningReceiveInfo {
352 payment_hash: v.payment_hash,
353 payment_preimage: v.payment_preimage,
354 preimage_revealed_at: v.preimage_revealed_at.map(|ts| {
355 chrono::DateTime::from_timestamp_secs(ts as i64)
356 .expect("timestamp is valid")
357 }),
358 invoice: v.invoice.to_string(),
359 htlc_vtxos: v.htlc_vtxos.map(|vtxos| vtxos.into_iter()
360 .map(crate::primitives::WalletVtxoInfo::from).collect()),
361 }
362 }
363}
364
365#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
366#[cfg_attr(feature = "utoipa", derive(ToSchema))]
367pub struct InputScriptInfo {
368 pub hex: Option<Vec<u8>>,
369 pub asm: Option<String>,
370}
371
372impl From<hal::tx::InputScriptInfo> for InputScriptInfo {
373 fn from(v: hal::tx::InputScriptInfo) -> Self {
374 InputScriptInfo {
375 hex: v.hex.map(|hex| hex.bytes().to_vec()),
376 asm: v.asm,
377 }
378 }
379}
380
381#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
382#[cfg_attr(feature = "utoipa", derive(ToSchema))]
383pub struct InputInfo {
384 pub prevout: Option<String>,
385 #[cfg_attr(feature = "utoipa", schema(value_type = String, nullable = true))]
386 pub txid: Option<Txid>,
387 pub vout: Option<u32>,
388 pub script_sig: Option<InputScriptInfo>,
389 pub sequence: Option<u32>,
390 pub witness: Option<Vec<Vec<u8>>>,
391}
392
393impl From<hal::tx::InputInfo> for InputInfo {
394 fn from(v: hal::tx::InputInfo) -> Self {
395 InputInfo {
396 prevout: v.prevout,
397 txid: v.txid,
398 vout: v.vout,
399 script_sig: v.script_sig.map(InputScriptInfo::from),
400 sequence: v.sequence,
401 witness: v.witness.map(|witness| witness.into_iter().map(|witness| witness.bytes().to_vec()).collect()),
402 }
403 }
404}
405
406#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
407#[cfg_attr(feature = "utoipa", derive(ToSchema))]
408pub struct OutputScriptInfo {
409 pub hex: Option<Vec<u8>>,
410 pub asm: Option<String>,
411 #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
412 pub type_: Option<String>,
413 #[serde(skip_serializing_if = "Option::is_none")]
414 #[cfg_attr(feature = "utoipa", schema(value_type = String, nullable = true))]
415 pub address: Option<Address<address::NetworkUnchecked>>,
416}
417
418impl From<hal::tx::OutputScriptInfo> for OutputScriptInfo {
419 fn from(v: hal::tx::OutputScriptInfo) -> Self {
420 OutputScriptInfo {
421 hex: v.hex.map(|hex| hex.bytes().to_vec()),
422 asm: v.asm,
423 type_: v.type_,
424 address: v.address,
425 }
426 }
427}
428
429#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
430#[cfg_attr(feature = "utoipa", derive(ToSchema))]
431pub struct OutputInfo {
432 #[cfg_attr(feature = "utoipa", schema(value_type = u64, nullable = true))]
433 pub value: Option<Amount>,
434 pub script_pub_key: Option<OutputScriptInfo>,
435}
436
437impl From<hal::tx::OutputInfo> for OutputInfo {
438 fn from(v: hal::tx::OutputInfo) -> Self {
439 OutputInfo {
440 value: v.value,
441 script_pub_key: v.script_pub_key.map(OutputScriptInfo::from),
442 }
443 }
444}
445
446#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
447#[cfg_attr(feature = "utoipa", derive(ToSchema))]
448pub struct TransactionInfo {
449 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
450 pub txid: Option<Txid>,
451 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
452 pub wtxid: Option<Wtxid>,
453 pub size: Option<usize>,
454 pub weight: Option<usize>,
455 pub vsize: Option<usize>,
456 pub version: Option<i32>,
457 pub locktime: Option<u32>,
458 pub inputs: Option<Vec<InputInfo>>,
459 pub outputs: Option<Vec<OutputInfo>>,
460 pub total_output_value: Option<u64>,
461}
462
463impl From<hal::tx::TransactionInfo> for TransactionInfo {
464 fn from(v: hal::tx::TransactionInfo) -> Self {
465 TransactionInfo {
466 txid: v.txid,
467 wtxid: v.wtxid,
468 size: v.size,
469 weight: v.weight,
470 vsize: v.vsize,
471 version: v.version,
472 locktime: v.locktime,
473 inputs: v.inputs.map(|inputs| {
474 inputs.into_iter().map(InputInfo::from).collect()
475 }),
476 outputs: v.outputs.map(|outputs| {
477 outputs.into_iter().map(OutputInfo::from).collect()
478 }),
479 total_output_value: v.total_output_value,
480 }
481 }
482}
483
484#[cfg(test)]
485mod test {
486 use super::*;
487
488 #[test]
489 fn ark_info_fields() {
490 #[allow(unused)]
494 fn convert(j: ArkInfo) -> ark::ArkInfo {
495 ark::ArkInfo {
496 network: j.network,
497 server_pubkey: j.server_pubkey,
498 round_interval: j.round_interval,
499 nb_round_nonces: j.nb_round_nonces,
500 vtxo_exit_delta: j.vtxo_exit_delta,
501 vtxo_expiry_delta: j.vtxo_expiry_delta,
502 htlc_send_expiry_delta: j.htlc_send_expiry_delta,
503 htlc_expiry_delta: j.htlc_expiry_delta,
504 max_vtxo_amount: j.max_vtxo_amount,
505 max_arkoor_depth: j.max_arkoor_depth,
506 required_board_confirmations: j.required_board_confirmations,
507 max_user_invoice_cltv_delta: j.max_user_invoice_cltv_delta,
508 min_board_amount: j.min_board_amount,
509 }
510 }
511 }
512}