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