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 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 }, 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 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 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 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 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 #[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 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 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, 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 #[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}