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