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