clementine_core/rpc/
verifier.rs

1use std::str::FromStr;
2
3use super::clementine::{
4    self, clementine_verifier_server::ClementineVerifier, Empty, NonceGenRequest, NonceGenResponse,
5    OperatorParams, OptimisticPayoutParams, PartialSig, RawTxWithRbfInfo, VergenResponse,
6    VerifierDepositFinalizeParams, VerifierDepositSignParams, VerifierParams,
7};
8use super::error;
9use super::parser::ParserError;
10use crate::builder::transaction::sign::{create_and_sign_txs, TransactionRequestData};
11use crate::builder::transaction::ContractContext;
12use crate::citrea::CitreaClientT;
13use crate::compatibility::ActorWithConfig;
14use crate::constants::RESTART_BACKGROUND_TASKS_TIMEOUT;
15use crate::errors::ResultExt as _;
16use crate::rpc::clementine::{CompatibilityParamsRpc, VerifierDepositFinalizeResponse};
17use crate::utils::{get_vergen_response, monitor_standalone_task, timed_request};
18use crate::verifier::VerifierServer;
19use crate::{constants, fetch_next_optional_message_from_stream};
20use crate::{
21    fetch_next_message_from_stream,
22    rpc::parser::{self},
23};
24use alloy::primitives::PrimitiveSignature;
25use bitcoin::Witness;
26use clementine::verifier_deposit_finalize_params::Params;
27use eyre::Context as _;
28use secp256k1::musig::AggregatedNonce;
29use tokio::sync::mpsc::{self};
30use tokio_stream::wrappers::ReceiverStream;
31use tonic::{async_trait, Request, Response, Status, Streaming};
32
33#[async_trait]
34impl<C> ClementineVerifier for VerifierServer<C>
35where
36    C: CitreaClientT,
37{
38    async fn get_compatibility_params(
39        &self,
40        _request: Request<Empty>,
41    ) -> Result<Response<CompatibilityParamsRpc>, Status> {
42        let params = self.verifier.get_compatibility_params()?;
43        Ok(Response::new(params.try_into().map_to_status()?))
44    }
45
46    async fn vergen(&self, _request: Request<Empty>) -> Result<Response<VergenResponse>, Status> {
47        tracing::info!("Vergen rpc called");
48        Ok(Response::new(get_vergen_response()))
49    }
50
51    async fn restart_background_tasks(
52        &self,
53        _request: tonic::Request<super::Empty>,
54    ) -> std::result::Result<tonic::Response<super::Empty>, tonic::Status> {
55        tracing::info!("Restarting background tasks rpc called");
56        // because start_background_tasks uses a RwLock, we set a timeout to be safe
57        timed_request(
58            RESTART_BACKGROUND_TASKS_TIMEOUT,
59            "Restarting background tasks",
60            self.start_background_tasks(),
61        )
62        .await?;
63        tracing::info!("Restarting background tasks rpc completed");
64        Ok(Response::new(Empty {}))
65    }
66
67    async fn optimistic_payout_sign(
68        &self,
69        request: Request<OptimisticPayoutParams>,
70    ) -> Result<Response<PartialSig>, Status> {
71        tracing::info!("Optimistic payout sign rpc called");
72        let params = request.into_inner();
73        let agg_nonce = AggregatedNonce::from_byte_array(
74            params
75                .agg_nonce
76                .as_slice()
77                .try_into()
78                .map_err(|_| Status::invalid_argument("agg_nonce must be exactly 66 bytes"))?,
79        )
80        .map_err(|e| Status::invalid_argument(format!("Invalid musigagg nonce: {e}")))?;
81        let nonce_session_id = params
82            .nonce_gen
83            .ok_or(Status::invalid_argument(
84                "Nonce params not found for optimistic payout",
85            ))?
86            .id
87            .parse::<u128>()
88            .map_err(|e| Status::invalid_argument(format!("Invalid nonce session id: {e}")))?;
89
90        let opt_withdraw_params = params.opt_withdrawal.ok_or(Status::invalid_argument(
91            "Withdrawal params not found for optimistic payout",
92        ))?;
93
94        tracing::info!(
95            "Parsed optimistic payout rpc params: {:?}",
96            opt_withdraw_params
97        );
98
99        let verification_signature_str = opt_withdraw_params.verification_signature.clone();
100        let withdrawal_params = opt_withdraw_params
101            .withdrawal
102            .ok_or(Status::invalid_argument(
103                "Withdrawal params not found for optimistic payout",
104            ))?;
105        let (withdrawal_id, input_signature, input_outpoint, output_script_pubkey, output_amount) =
106            parser::operator::parse_withdrawal_sig_params(withdrawal_params)?;
107
108        let verification_signature = verification_signature_str
109            .map(|sig| {
110                PrimitiveSignature::from_str(&sig).map_err(|e| {
111                    Status::invalid_argument(format!("Invalid verification signature: {e}"))
112                })
113            })
114            .transpose()?;
115
116        let partial_sig = self
117            .verifier
118            .sign_optimistic_payout(
119                nonce_session_id,
120                agg_nonce,
121                withdrawal_id,
122                input_signature,
123                input_outpoint,
124                output_script_pubkey,
125                output_amount,
126                verification_signature,
127            )
128            .await?;
129        tracing::info!("Optimistic payout sign rpc completed successfully");
130        Ok(Response::new(partial_sig.into()))
131    }
132
133    async fn internal_create_watchtower_challenge(
134        &self,
135        request: tonic::Request<super::TransactionRequest>,
136    ) -> std::result::Result<tonic::Response<super::RawTxWithRbfInfo>, tonic::Status> {
137        tracing::warn!(
138            "Internal create watchtower challenge rpc called with request: {:?}",
139            request
140        );
141        let transaction_request = request.into_inner();
142        let transaction_data: TransactionRequestData = transaction_request.try_into()?;
143
144        let (_tx_type, signed_tx, rbf_info) = self
145            .verifier
146            .create_watchtower_challenge(
147                transaction_data,
148                &{
149                    let challenge_bytes = self
150                        .verifier
151                        .config
152                        .protocol_paramset()
153                        .watchtower_challenge_bytes;
154                    let mut challenge = vec![0u8; challenge_bytes];
155                    for (step, i) in (0..challenge_bytes).step_by(32).enumerate() {
156                        if i < challenge_bytes {
157                            challenge[i] = step as u8;
158                        }
159                    }
160                    challenge
161                }, // dummy challenge with 1u8, 2u8 every 32 bytes
162                None,
163            )
164            .await?;
165
166        Ok(Response::new(RawTxWithRbfInfo {
167            raw_tx: bitcoin::consensus::serialize(&signed_tx),
168            rbf_info: Some(rbf_info.into()),
169        }))
170    }
171    type NonceGenStream = ReceiverStream<Result<NonceGenResponse, Status>>;
172    type DepositSignStream = ReceiverStream<Result<PartialSig, Status>>;
173
174    async fn get_params(&self, _: Request<Empty>) -> Result<Response<VerifierParams>, Status> {
175        tracing::info!("Verifier get params rpc called");
176        let params: VerifierParams = (&self.verifier).try_into()?;
177
178        Ok(Response::new(params))
179    }
180
181    async fn set_operator(
182        &self,
183        req: Request<Streaming<OperatorParams>>,
184    ) -> Result<Response<Empty>, Status> {
185        let mut in_stream = req.into_inner();
186        tracing::info!("set_operator rpc called");
187        let (collateral_funding_outpoint, operator_xonly_pk, wallet_reimburse_address) =
188            parser::operator::parse_details(&mut in_stream).await?;
189        tracing::info!("Parsed set_operator rpc params for operator xonly pk: {}, collateral funding outpoint: {}, wallet reimburse address: {:?}", operator_xonly_pk, collateral_funding_outpoint, wallet_reimburse_address);
190
191        // check if address is valid
192        let wallet_reimburse_address_checked = wallet_reimburse_address
193            .clone()
194            .require_network(self.verifier.config.protocol_paramset().network)
195            .map_err(|e| {
196                Status::invalid_argument(format!(
197                    "Invalid operator reimbursement address: {:?} for bitcoin network {:?} for operator {:?}. ParseError: {}",
198                    wallet_reimburse_address,
199                    self.verifier.config.protocol_paramset().network,
200                    operator_xonly_pk,
201                    e
202                ))
203            })?;
204
205        let mut operator_kickoff_winternitz_public_keys = Vec::new();
206        // we need num_round_txs + 1 because the last round includes reimburse generators of previous round
207        for _ in 0..self.verifier.config.get_num_kickoff_winternitz_pks() {
208            operator_kickoff_winternitz_public_keys
209                .push(parser::operator::parse_winternitz_public_keys(&mut in_stream).await?);
210        }
211
212        let mut unspent_kickoff_sigs =
213            Vec::with_capacity(self.verifier.config.get_num_unspent_kickoff_sigs());
214        for _ in 0..self.verifier.config.get_num_unspent_kickoff_sigs() {
215            unspent_kickoff_sigs.push(parser::operator::parse_schnorr_sig(&mut in_stream).await?);
216        }
217
218        if in_stream.message().await?.is_some() {
219            return Err(Status::invalid_argument(
220                "Expected end of stream, got more messages in set_operator",
221            ));
222        }
223
224        self.verifier
225            .set_operator(
226                collateral_funding_outpoint,
227                operator_xonly_pk,
228                wallet_reimburse_address_checked,
229                operator_kickoff_winternitz_public_keys,
230                unspent_kickoff_sigs,
231            )
232            .await?;
233
234        tracing::info!(
235            "Set operator rpc completed successfully for operator xonly pk: {}",
236            operator_xonly_pk
237        );
238        Ok(Response::new(Empty {}))
239    }
240
241    async fn nonce_gen(
242        &self,
243        req: Request<NonceGenRequest>,
244    ) -> Result<Response<Self::NonceGenStream>, Status> {
245        let num_nonces = req.into_inner().num_nonces;
246        tracing::info!(
247            "Verifier nonce gen rpc called with num_nonces: {}",
248            num_nonces
249        );
250        let (session_id, pub_nonces) = self.verifier.nonce_gen(num_nonces).await?;
251
252        let (tx, rx) = mpsc::channel(pub_nonces.len() + 1);
253        let monitor_sender = tx.clone();
254
255        let handle = tokio::spawn(async move {
256            let nonce_gen_first_response = clementine::NonceGenFirstResponse {
257                id: session_id.to_string(),
258                num_nonces,
259            };
260            let session_id: NonceGenResponse = nonce_gen_first_response.into();
261            tx.send(Ok(session_id)).await.map_err(|e| {
262                Status::aborted(format!("Failed to send nonce gen first response: {e}"))
263            })?;
264
265            for pub_nonce in &pub_nonces {
266                let pub_nonce: NonceGenResponse = pub_nonce.into();
267                tx.send(Ok(pub_nonce)).await.map_err(|e| {
268                    Status::aborted(format!("Failed to send nonce gen response: {e}"))
269                })?;
270            }
271
272            Ok::<(), Status>(())
273        });
274        monitor_standalone_task(handle, "Verifier nonce_gen", monitor_sender);
275
276        Ok(Response::new(ReceiverStream::new(rx)))
277    }
278
279    async fn deposit_sign(
280        &self,
281        req: Request<Streaming<VerifierDepositSignParams>>,
282    ) -> Result<Response<Self::DepositSignStream>, Status> {
283        let mut in_stream = req.into_inner();
284        let verifier = self.verifier.clone();
285        tracing::info!("Verifier deposit sign rpc called");
286
287        let (tx, rx) = mpsc::channel(constants::DEFAULT_CHANNEL_SIZE);
288        let out_stream: Self::DepositSignStream = ReceiverStream::new(rx);
289
290        let (param_tx, mut param_rx) = mpsc::channel(1);
291        let (agg_nonce_tx, agg_nonce_rx) = mpsc::channel(constants::DEFAULT_CHANNEL_SIZE);
292        let config = self.verifier.config.clone();
293
294        // Send incoming data to deposit sign job.
295        let handle = tokio::spawn(async move {
296            let params = fetch_next_message_from_stream!(in_stream, params)?;
297            let (deposit_data, session_id) = match params {
298                clementine::verifier_deposit_sign_params::Params::DepositSignFirstParam(
299                    deposit_sign_session,
300                ) => parser::verifier::parse_deposit_sign_session(
301                    deposit_sign_session,
302                    &verifier.signer.public_key,
303                )?,
304                _ => return Err(Status::invalid_argument("Expected DepositOutpoint")),
305            };
306
307            let mut received_agg_nonces = 0;
308            let needed_agg_nonces = config.get_num_required_nofn_sigs(&deposit_data);
309
310            param_tx
311                .send((deposit_data, session_id))
312                .await
313                .map_err(error::output_stream_ended_prematurely)?;
314
315            while let Some(result) =
316                fetch_next_optional_message_from_stream!(&mut in_stream, params)
317            {
318                let agg_nonce = match result {
319                    clementine::verifier_deposit_sign_params::Params::AggNonce(agg_nonce) => {
320                        AggregatedNonce::from_byte_array(
321                            agg_nonce.as_slice().try_into().map_err(|_| {
322                                ParserError::RPCParamMalformed("AggNonce".to_string())
323                            })?,
324                        )
325                        .map_err(|_| ParserError::RPCParamMalformed("AggNonce".to_string()))?
326                    }
327                    _ => return Err(Status::invalid_argument("Expected AggNonce")),
328                };
329
330                agg_nonce_tx
331                    .send(agg_nonce)
332                    .await
333                    .map_err(error::output_stream_ended_prematurely)?;
334
335                received_agg_nonces += 1;
336                if received_agg_nonces == needed_agg_nonces {
337                    break;
338                }
339            }
340            Ok(())
341        });
342        monitor_standalone_task(handle, "Verifier deposit data receiver", tx.clone());
343
344        // Start partial sig job and return partial sig responses.
345        let tx_for_monitor = tx.clone();
346        let handle = tokio::spawn(async move {
347            let (deposit_data, session_id) = param_rx
348                .recv()
349                .await
350                .ok_or(error::expected_msg_got_none("parameters")())?;
351
352            tracing::info!("Called deposit_sign for deposit data: {:?}", deposit_data,);
353
354            let mut partial_sig_receiver = verifier
355                .deposit_sign(deposit_data.clone(), session_id, agg_nonce_rx)
356                .await?;
357
358            let mut nonce_idx = 0;
359            let num_required_sigs = verifier.config.get_num_required_nofn_sigs(&deposit_data);
360            while let Some(partial_sig_result) = partial_sig_receiver.recv().await {
361                match partial_sig_result {
362                    Ok(partial_sig) => {
363                        tx.send(Ok(PartialSig {
364                            partial_sig: partial_sig.serialize().to_vec(),
365                        }))
366                        .await
367                        .map_err(|e| {
368                            Status::aborted(format!(
369                                "Error sending partial sig, stream ended prematurely: {e}"
370                            ))
371                        })?;
372                    }
373                    Err(e) => {
374                        tx
375                            .send(Err(e.into()))
376                            .await
377                            .map_err(|send_err| {
378                                Status::aborted(format!(
379                                    "Error forwarding partial sig error, stream ended prematurely: {send_err}"
380                                ))
381                            })?;
382                        break;
383                    }
384                }
385
386                nonce_idx += 1;
387                tracing::trace!(
388                    "Verifier {:?} signed and sent sighash {} of {} through rpc deposit_sign",
389                    verifier.signer.public_key,
390                    nonce_idx,
391                    num_required_sigs
392                );
393                if nonce_idx == num_required_sigs {
394                    break;
395                }
396            }
397
398            Ok::<(), Status>(())
399        });
400        monitor_standalone_task(handle, "Verifier deposit signature sender", tx_for_monitor);
401
402        Ok(Response::new(out_stream))
403    }
404
405    /// Function to finalize the deposit. Verifier will check the validity of the both nofn signatures and
406    /// operator signatures. It will receive data from the stream in this order -> nofn sigs, movetx agg nonce, operator sigs.
407    /// If everything is correct, it will partially sign the move tx and send it to aggregator.
408    async fn deposit_finalize(
409        &self,
410        req: Request<Streaming<VerifierDepositFinalizeParams>>,
411    ) -> Result<Response<VerifierDepositFinalizeResponse>, Status> {
412        let mut in_stream = req.into_inner();
413        tracing::info!("deposit finalize rpc called");
414
415        let (sig_tx, sig_rx) = mpsc::channel(constants::DEFAULT_CHANNEL_SIZE);
416        let (agg_nonce_tx, agg_nonce_rx) = mpsc::channel(1);
417        let (operator_sig_tx, operator_sig_rx) = mpsc::channel(constants::DEFAULT_CHANNEL_SIZE);
418
419        let params = fetch_next_message_from_stream!(in_stream, params)?;
420        let (deposit_data, session_id) = match params {
421            Params::DepositSignFirstParam(deposit_sign_session) => {
422                parser::verifier::parse_deposit_sign_session(
423                    deposit_sign_session,
424                    &self.verifier.signer.public_key,
425                )?
426            }
427            _ => Err(Status::internal("Expected DepositOutpoint"))?,
428        };
429        tracing::info!(
430            "deposit_data received in deposit_finalize, {:?}",
431            deposit_data
432        );
433        let deposit_outpoint = deposit_data.get_deposit_outpoint();
434
435        // Start deposit finalize job.
436        let verifier = self.verifier.clone();
437        let mut dep_data = deposit_data.clone();
438        let deposit_finalize_handle = tokio::spawn(async move {
439            verifier
440                .deposit_finalize(
441                    &mut dep_data,
442                    session_id,
443                    sig_rx,
444                    agg_nonce_rx,
445                    operator_sig_rx,
446                )
447                .await
448        });
449
450        // Start parsing inputs and send them to deposit finalize job.
451        let verifier = self.verifier.clone();
452        let sig_handle = tokio::spawn(async move {
453            let num_required_nofn_sigs = verifier.config.get_num_required_nofn_sigs(&deposit_data);
454            tracing::debug!(
455                "Needed nofn sigs for deposit {:?}: {}",
456                deposit_data,
457                num_required_nofn_sigs
458            );
459            let mut nonce_idx = 0;
460            while let Some(sig) =
461                parser::verifier::parse_next_deposit_finalize_param_schnorr_sig(&mut in_stream)
462                    .await
463                    .wrap_err_with(|| {
464                        format!(
465                            "While waiting for the {}th signature out of {}",
466                            nonce_idx + 1,
467                            num_required_nofn_sigs
468                        )
469                    })
470                    .map_to_status()?
471            {
472                tracing::trace!(
473                    "Received full nofn sig {} in deposit_finalize()",
474                    nonce_idx + 1
475                );
476                sig_tx
477                    .send(sig)
478                    .await
479                    .map_err(error::output_stream_ended_prematurely)?;
480                tracing::debug!(
481                    "Sent full nofn sig {} to src/verifier in deposit_finalize()",
482                    nonce_idx + 1
483                );
484                nonce_idx += 1;
485                if nonce_idx == num_required_nofn_sigs {
486                    break;
487                }
488            }
489            if nonce_idx < num_required_nofn_sigs {
490                let err_msg = format!(
491                    "Insufficient N-of-N signatures received: got {nonce_idx}, expected {num_required_nofn_sigs}",
492                );
493                tracing::error!("{err_msg}");
494                return Err(Status::invalid_argument(err_msg));
495            }
496
497            let move_tx_agg_nonce =
498                parser::verifier::parse_deposit_finalize_param_move_tx_agg_nonce(&mut in_stream)
499                    .await?;
500            agg_nonce_tx
501                .send(move_tx_agg_nonce)
502                .await
503                .map_err(error::output_stream_ended_prematurely)?;
504
505            let emergency_stop_agg_nonce =
506                parser::verifier::parse_deposit_finalize_param_emergency_stop_agg_nonce(
507                    &mut in_stream,
508                )
509                .await?;
510            agg_nonce_tx
511                .send(emergency_stop_agg_nonce)
512                .await
513                .map_err(error::output_stream_ended_prematurely)?;
514
515            let num_required_op_sigs = verifier
516                .config
517                .get_num_required_operator_sigs(&deposit_data);
518            let num_operators = deposit_data.get_num_operators();
519            let num_required_total_op_sigs = num_required_op_sigs * num_operators;
520            let mut total_op_sig_count = 0;
521            for _ in 0..num_operators {
522                let mut op_sig_count = 0;
523
524                while let Some(operator_sig) =
525                    parser::verifier::parse_next_deposit_finalize_param_schnorr_sig(&mut in_stream)
526                        .await?
527                {
528                    tracing::trace!(
529                        "Received full operator sig {} in deposit_finalize()",
530                        op_sig_count + 1
531                    );
532                    operator_sig_tx
533                        .send(operator_sig)
534                        .await
535                        .map_err(error::output_stream_ended_prematurely)?;
536                    tracing::trace!(
537                        "Sent full operator sig {} to src/verifier in deposit_finalize()",
538                        op_sig_count + 1
539                    );
540
541                    op_sig_count += 1;
542                    total_op_sig_count += 1;
543                    if op_sig_count == num_required_op_sigs {
544                        break;
545                    }
546                }
547            }
548
549            if total_op_sig_count < num_required_total_op_sigs {
550                let err_msg = format!(
551                    "Insufficient operator signatures received: got {total_op_sig_count}, expected {num_required_total_op_sigs}",
552                );
553                tracing::error!("{err_msg}");
554                return Err(Status::invalid_argument(err_msg));
555            }
556
557            Ok::<(), Status>(())
558        });
559
560        sig_handle.await.map_err(|e| {
561            Status::internal(format!("Deposit sign thread failed to finish: {e}").as_str())
562        })??;
563
564        let partial_sig = deposit_finalize_handle.await.map_err(|e| {
565            Status::internal(format!("Deposit finalize thread failed to finish: {e}").as_str())
566        })??;
567
568        let response = VerifierDepositFinalizeResponse {
569            move_to_vault_partial_sig: partial_sig.0.serialize().to_vec(),
570            emergency_stop_partial_sig: partial_sig.1.serialize().to_vec(),
571        };
572
573        tracing::info!(
574            "deposit finalize rpc completed successfully for deposit outpoint: {:?}",
575            deposit_outpoint
576        );
577
578        Ok(Response::new(response))
579    }
580
581    async fn set_operator_keys(
582        &self,
583        request: tonic::Request<super::OperatorKeysWithDeposit>,
584    ) -> std::result::Result<tonic::Response<super::Empty>, tonic::Status> {
585        tracing::info!("set_operator_keys rpc called");
586        let data = request.into_inner();
587        let (deposit_data, op_keys, operator_xonly_pk) =
588            parser::verifier::parse_op_keys_with_deposit(data)?;
589        tracing::info!(
590            "Parsed set_operator_keys rpc params, operator xonly pk: {:?}, deposit data: {:?}",
591            operator_xonly_pk,
592            deposit_data
593        );
594        self.verifier
595            .set_operator_keys(deposit_data, op_keys, operator_xonly_pk)
596            .await?;
597        Ok(Response::new(Empty {}))
598    }
599
600    async fn internal_create_signed_txs(
601        &self,
602        request: tonic::Request<super::TransactionRequest>,
603    ) -> std::result::Result<tonic::Response<super::SignedTxsWithType>, tonic::Status> {
604        let transaction_request = request.into_inner();
605        let transaction_data: TransactionRequestData = transaction_request.try_into()?;
606        tracing::warn!(
607            "Called internal_create_signed_txs with transaction data: {:?}",
608            transaction_data
609        );
610        let (_, deposit_data) = self
611            .verifier
612            .db
613            .get_deposit_data(None, transaction_data.deposit_outpoint)
614            .await?
615            .ok_or(Status::invalid_argument("Deposit not found in database"))?;
616        let context = ContractContext::new_context_for_kickoff(
617            transaction_data.kickoff_data,
618            deposit_data,
619            self.verifier.config.protocol_paramset(),
620        );
621        let raw_txs = create_and_sign_txs(
622            self.verifier.db.clone(),
623            &self.verifier.signer,
624            self.verifier.config.clone(),
625            context,
626            None, // empty blockhash, will not sign this
627            None,
628        )
629        .await?;
630
631        Ok(Response::new(raw_txs.into()))
632    }
633
634    async fn internal_handle_kickoff(
635        &self,
636        request: Request<clementine::Txid>,
637    ) -> Result<Response<Empty>, Status> {
638        let txid = request.into_inner();
639        let txid = bitcoin::Txid::try_from(txid).map_err(|e| {
640            Status::invalid_argument(format!("Failed to convert txid to bitcoin::Txid: {e}"))
641        })?;
642        tracing::warn!(
643            "Called internal_handle_kickoff for kickoff txid: {:?}",
644            txid
645        );
646        let mut dbtx = self.verifier.db.begin_transaction().await?;
647        let kickoff_data = self
648            .verifier
649            .db
650            .get_deposit_data_with_kickoff_txid(None, txid)
651            .await?;
652        if let Some((deposit_data, kickoff_id)) = kickoff_data {
653            self.verifier
654                .handle_kickoff(
655                    &mut dbtx,
656                    Witness::new(),
657                    deposit_data,
658                    kickoff_id,
659                    false,
660                    txid,
661                )
662                .await?;
663        } else {
664            return Err(Status::not_found("Kickoff txid not found"));
665        }
666        dbtx.commit()
667            .await
668            .wrap_err("Failed to commit transaction")
669            .map_to_status()?;
670        Ok(Response::new(Empty {}))
671    }
672
673    async fn debug_tx(
674        &self,
675        request: tonic::Request<super::TxDebugRequest>,
676    ) -> std::result::Result<tonic::Response<super::TxDebugInfo>, tonic::Status> {
677        #[cfg(not(feature = "automation"))]
678        {
679            Err(tonic::Status::unimplemented(
680                "Automation is not enabled, TxSender is not running.",
681            ))
682        }
683
684        // Get debug info from tx_sender
685        #[cfg(feature = "automation")]
686        {
687            let tx_id = request.into_inner().tx_id;
688            tracing::info!("Called debug_tx for tx sender try to send id: {:?}", tx_id);
689            match self.verifier.tx_sender.debug_tx(tx_id).await {
690                Ok(debug_info) => Ok(tonic::Response::new(debug_info)),
691                Err(e) => Err(tonic::Status::internal(format!(
692                    "Failed to debug TX {tx_id}: {e}",
693                ))),
694            }
695        }
696    }
697
698    async fn get_current_status(
699        &self,
700        _request: Request<Empty>,
701    ) -> Result<Response<clementine::EntityStatus>, Status> {
702        tracing::debug!("Called get_current_status rpc");
703        let status = self.get_current_status().await?;
704        Ok(Response::new(status))
705    }
706}