clementine_core/task/
payout_checker.rs1use eyre::OptionExt;
2use tokio::time::Duration;
3use tonic::async_trait;
4
5use crate::{citrea::CitreaClientT, database::Database, errors::BridgeError, operator::Operator};
6
7use super::{Task, TaskVariant};
8
9pub const PAYOUT_CHECKER_POLL_DELAY: Duration = if cfg!(test) {
10 Duration::from_millis(250)
11} else {
12 Duration::from_secs(60)
13};
14
15#[derive(Debug, Clone)]
16pub struct PayoutCheckerTask<C: CitreaClientT> {
17 db: Database,
18 operator: Operator<C>,
19}
20
21impl<C> PayoutCheckerTask<C>
22where
23 C: CitreaClientT,
24{
25 pub fn new(db: Database, operator: Operator<C>) -> Self {
26 Self { db, operator }
27 }
28}
29
30#[async_trait]
31impl<C> Task for PayoutCheckerTask<C>
32where
33 C: CitreaClientT,
34{
35 type Output = bool;
36 const VARIANT: TaskVariant = TaskVariant::PayoutChecker;
37
38 async fn run_once(&mut self) -> Result<Self::Output, BridgeError> {
39 let mut dbtx = self.db.begin_transaction().await?;
40 let unhandled_payout = self
41 .db
42 .get_first_unhandled_payout_by_operator_xonly_pk(
43 Some(&mut dbtx),
44 self.operator.signer.xonly_public_key,
45 )
46 .await?;
47
48 if unhandled_payout.is_none() {
49 return Ok(false);
50 }
51
52 let (citrea_idx, move_to_vault_txid, payout_tx_blockhash) =
53 unhandled_payout.expect("Must be Some");
54
55 tracing::info!(
56 "Unhandled payout found for withdrawal {}, move_txid: {}",
57 citrea_idx,
58 move_to_vault_txid
59 );
60
61 let deposit_data = self
62 .db
63 .get_deposit_data_with_move_tx(Some(&mut dbtx), move_to_vault_txid)
64 .await?;
65 if deposit_data.is_none() {
66 return Err(eyre::eyre!("Deposit data not found").into());
67 }
68
69 let deposit_data = deposit_data.expect("Must be Some");
70
71 let kickoff_txid = self
72 .operator
73 .handle_finalized_payout(
74 &mut dbtx,
75 deposit_data.get_deposit_outpoint(),
76 payout_tx_blockhash,
77 )
78 .await?;
79
80 let (_, payout_block_height) = self
82 .operator
83 .db
84 .get_block_info_from_hash(Some(&mut dbtx), payout_tx_blockhash)
85 .await?
86 .ok_or_eyre("Couldn't find payout blockhash in bitcoin sync")?;
87
88 let _ = self
89 .operator
90 .citrea_client
91 .fetch_validate_and_store_lcp(
92 payout_block_height as u64,
93 citrea_idx,
94 &self.operator.db,
95 Some(&mut dbtx),
96 self.operator.config.protocol_paramset(),
97 )
98 .await?;
99
100 #[cfg(feature = "automation")]
101 self.operator.end_round(&mut dbtx).await?;
102
103 self.db
104 .mark_payout_handled(Some(&mut dbtx), citrea_idx, kickoff_txid)
105 .await?;
106
107 dbtx.commit().await?;
108
109 Ok(true)
110 }
111}