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