1use std::collections::HashMap;
6
7use chia_consensus::consensus_constants::ConsensusConstants;
8use chia_consensus::flags::DONT_VALIDATE_SIGNATURE;
9use serde_json::Value;
10
11use crate::coinset::CoinsetClient;
12use crate::peer::{OptAnswer, PeerBackend};
13use crate::types::*;
14
15#[cfg(test)]
16mod absence_tests;
17#[cfg(test)]
18mod presence_tests;
19
20fn run_puzzle_conditions(spend: &CoinSpend, constants: &ConsensusConstants) -> Vec<Condition> {
27 let flags = DONT_VALIDATE_SIGNATURE;
28 let Ok(puzzle_bytes) = crate::peer::translate::parse_hex(&spend.puzzle_reveal) else {
29 return Vec::new();
30 };
31 let Ok(solution_bytes) = crate::peer::translate::parse_hex(&spend.solution) else {
32 return Vec::new();
33 };
34
35 let mut allocator = chia_consensus::allocator::make_allocator(flags);
36
37 let Ok(puzzle_node) = clvmr::serde::node_from_bytes(&mut allocator, &puzzle_bytes) else {
38 return Vec::new();
39 };
40 let Ok(solution_node) = clvmr::serde::node_from_bytes(&mut allocator, &solution_bytes) else {
41 return Vec::new();
42 };
43
44 let dialect = clvmr::chia_dialect::ChiaDialect::new(flags);
45 match clvmr::run_program::run_program(
46 &mut allocator,
47 &dialect,
48 puzzle_node,
49 solution_node,
50 constants.max_block_cost_clvm,
51 ) {
52 Ok(clvmr::reduction::Reduction(_, output)) => {
53 crate::peer::block::parse_conditions_public(&allocator, output)
54 }
55 Err(_) => Vec::new(),
56 }
57}
58
59fn settle_uncorroborated_absence<T>(
69 coinset: Option<Result<Option<T>, ChiaQueryError>>,
70) -> Result<Option<T>, ChiaQueryError> {
71 match coinset {
72 None => Err(ChiaQueryError::UncorroboratedAbsence(
73 "one peer reported absence, no second peer was available, and the coinset fallback is \
74 disabled"
75 .into(),
76 )),
77 Some(Ok(None)) => Ok(None),
78 Some(Ok(Some(_))) => Err(ChiaQueryError::SourcesDisagree(
79 "a peer reports absent, the coinset API reports present".into(),
80 )),
81 Some(Err(e)) => Err(ChiaQueryError::UncorroboratedAbsence(format!(
82 "one peer reported absence and the coinset API could not corroborate it: {e}"
83 ))),
84 }
85}
86
87fn settle_uncorroborated_presence<T: ChainClaim>(
95 found: T,
96 coinset: Option<Result<Option<T>, ChiaQueryError>>,
97) -> Result<Option<T>, ChiaQueryError> {
98 match coinset {
99 None => Err(ChiaQueryError::UncorroboratedPresence(
100 "one peer produced a record, no second peer was available, and the coinset fallback \
101 is disabled"
102 .into(),
103 )),
104 Some(Ok(Some(other))) if other.chain_claim() == found.chain_claim() => Ok(Some(found)),
105 Some(Ok(Some(other))) => Err(ChiaQueryError::SourcesDisagree(format!(
106 "a peer claims `{}`, the coinset API claims `{}`",
107 found.chain_claim(),
108 other.chain_claim()
109 ))),
110 Some(Ok(None)) => Err(ChiaQueryError::SourcesDisagree(
111 "a peer reports present, the coinset API reports absent".into(),
112 )),
113 Some(Err(e)) => Err(ChiaQueryError::UncorroboratedPresence(format!(
114 "one peer produced a record and the coinset API could not corroborate it: {e}"
115 ))),
116 }
117}
118
119pub struct QueryRouter {
120 pub(crate) peer: PeerBackend,
121 pub(crate) coinset: CoinsetClient,
122 pub(crate) coinset_fallback_enabled: bool,
123}
124
125impl QueryRouter {
130 async fn peer_then_coinset<T>(
133 &self,
134 peer_fn: impl std::future::Future<Output = Result<T, ChiaQueryError>>,
135 peer_retry: impl std::future::Future<Output = Result<T, ChiaQueryError>>,
136 coinset_fn: impl std::future::Future<Output = Result<T, ChiaQueryError>>,
137 ) -> Result<T, ChiaQueryError> {
138 match peer_fn.await {
140 Ok(v) => return Ok(v),
141 Err(e) => log::debug!("peer attempt 1 failed: {e}"),
142 }
143
144 match peer_retry.await {
146 Ok(v) => Ok(v),
147 Err(peer_err) => {
148 if !self.coinset_fallback_enabled {
149 return Err(peer_err);
150 }
151 coinset_fn
153 .await
154 .map_err(|ce| ChiaQueryError::AllSourcesFailed {
155 peer_error: Box::new(peer_err),
156 coinset_error: Some(Box::new(ce)),
157 })
158 }
159 }
160 }
161
162 async fn peer_then_coinset_opt<T: ChainClaim>(
189 &self,
190 peer_fn: impl std::future::Future<Output = Result<OptAnswer<T>, ChiaQueryError>>,
191 peer_retry: impl std::future::Future<Output = Result<OptAnswer<T>, ChiaQueryError>>,
192 coinset_fn: impl std::future::Future<Output = Result<Option<T>, ChiaQueryError>>,
193 ) -> Result<Option<T>, ChiaQueryError> {
194 let first = match peer_fn.await {
195 Ok(answer) => Some(answer),
196 Err(e) => {
197 log::debug!("peer opt attempt 1 failed: {e}");
198 None
199 }
200 };
201
202 if let Some(answer) = first {
203 return self.settle_peer_answer(answer, coinset_fn).await;
204 }
205
206 match peer_retry.await {
207 Ok(answer) => self.settle_peer_answer(answer, coinset_fn).await,
208 Err(peer_err) => {
209 if !self.coinset_fallback_enabled {
210 return Err(peer_err);
211 }
212 coinset_fn
213 .await
214 .map_err(|ce| ChiaQueryError::AllSourcesFailed {
215 peer_error: Box::new(peer_err),
216 coinset_error: Some(Box::new(ce)),
217 })
218 }
219 }
220 }
221
222 async fn settle_peer_answer<T: ChainClaim>(
225 &self,
226 answer: OptAnswer<T>,
227 coinset_fn: impl std::future::Future<Output = Result<Option<T>, ChiaQueryError>>,
228 ) -> Result<Option<T>, ChiaQueryError> {
229 match answer {
230 OptAnswer::Found(v) => Ok(Some(v)),
231 OptAnswer::CorroboratedAbsent => Ok(None),
232 OptAnswer::UncorroboratedFound(v) => {
233 let coinset = if self.coinset_fallback_enabled {
234 Some(coinset_fn.await)
235 } else {
236 None
237 };
238 settle_uncorroborated_presence(v, coinset)
239 }
240 OptAnswer::UncorroboratedAbsent => {
241 let coinset = if self.coinset_fallback_enabled {
242 Some(coinset_fn.await)
243 } else {
244 None
245 };
246 settle_uncorroborated_absence(coinset)
247 }
248 }
249 }
250
251 fn require_coinset(&self, endpoint: &str) -> Result<(), ChiaQueryError> {
253 if !self.coinset_fallback_enabled {
254 Err(ChiaQueryError::UnsupportedWithoutCoinset(endpoint.into()))
255 } else {
256 Ok(())
257 }
258 }
259}
260
261impl QueryRouter {
266 pub async fn get_additions_and_removals(
270 &self,
271 header_hash: &str,
272 ) -> Result<AdditionsAndRemovals, ChiaQueryError> {
273 if let Ok(record) = self.get_block_record(header_hash).await {
275 match self
277 .peer
278 .try_get_additions_and_removals_from_block(record.height)
279 .await
280 {
281 Ok(r) => return Ok(r),
282 Err(e) => log::debug!("peer additions_and_removals failed: {e}"),
283 }
284 }
285 if self.coinset_fallback_enabled {
287 self.coinset.get_additions_and_removals(header_hash).await
288 } else {
289 Err(ChiaQueryError::UnsupportedWithoutCoinset(
290 "get_additions_and_removals".into(),
291 ))
292 }
293 }
294
295 pub async fn get_block(&self, header_hash: &str) -> Result<FullBlock, ChiaQueryError> {
296 if let Ok(record) = self.get_block_record(header_hash).await {
298 match self.peer.try_get_block_by_height(record.height).await {
299 Ok(b) => return Ok(b),
300 Err(e) => log::debug!("peer get_block failed: {e}"),
301 }
302 }
303 if self.coinset_fallback_enabled {
304 self.coinset.get_block(header_hash).await
305 } else {
306 Err(ChiaQueryError::UnsupportedWithoutCoinset(
307 "get_block".into(),
308 ))
309 }
310 }
311
312 pub async fn get_block_by_height(&self, height: u32) -> Result<FullBlock, ChiaQueryError> {
314 self.peer_then_coinset(
315 self.peer.try_get_block_by_height(height),
316 self.peer.try_get_block_by_height(height),
317 async {
318 let record = self.coinset.get_block_record_by_height(height).await?;
321 self.coinset.get_block(&record.header_hash).await
322 },
323 )
324 .await
325 }
326
327 pub async fn get_block_count_metrics(&self) -> Result<BlockCountMetrics, ChiaQueryError> {
328 self.require_coinset("get_block_count_metrics")?;
329 self.coinset.get_block_count_metrics().await
330 }
331
332 pub async fn get_block_record(&self, header_hash: &str) -> Result<BlockRecord, ChiaQueryError> {
333 self.require_coinset("get_block_record")?;
334 self.coinset.get_block_record(header_hash).await
335 }
336
337 pub async fn get_block_record_by_height(
340 &self,
341 height: u32,
342 ) -> Result<BlockRecord, ChiaQueryError> {
343 self.peer_then_coinset(
344 self.peer.try_get_block_record_by_height(height),
345 self.peer.try_get_block_record_by_height(height),
346 self.coinset.get_block_record_by_height(height),
347 )
348 .await
349 }
350
351 pub async fn get_block_records(
352 &self,
353 start: u32,
354 end: u32,
355 ) -> Result<Vec<BlockRecord>, ChiaQueryError> {
356 self.peer_then_coinset(
357 self.peer.try_get_block_records(start, end),
358 self.peer.try_get_block_records(start, end),
359 self.coinset.get_block_records(start, end),
360 )
361 .await
362 }
363
364 pub async fn get_block_spends(
367 &self,
368 header_hash: &str,
369 ) -> Result<Vec<CoinSpend>, ChiaQueryError> {
370 if let Ok(record) = self.get_block_record(header_hash).await {
372 match self
373 .peer
374 .try_get_block_spends_by_height(record.height)
375 .await
376 {
377 Ok(r) => return Ok(r),
378 Err(e) => log::debug!("peer block_spends failed: {e}"),
379 }
380 }
381 if self.coinset_fallback_enabled {
382 self.coinset.get_block_spends(header_hash).await
383 } else {
384 Err(ChiaQueryError::UnsupportedWithoutCoinset(
385 "get_block_spends".into(),
386 ))
387 }
388 }
389
390 pub async fn get_block_spends_with_conditions(
393 &self,
394 header_hash: &str,
395 ) -> Result<Vec<CoinSpendWithConditions>, ChiaQueryError> {
396 if let Ok(record) = self.get_block_record(header_hash).await {
397 match self
398 .peer
399 .try_get_block_spends_with_conditions(record.height)
400 .await
401 {
402 Ok(r) => return Ok(r),
403 Err(e) => log::debug!("peer block_spends_with_conditions failed: {e}"),
404 }
405 }
406 if self.coinset_fallback_enabled {
407 self.coinset
408 .get_block_spends_with_conditions(header_hash)
409 .await
410 } else {
411 Err(ChiaQueryError::UnsupportedWithoutCoinset(
412 "get_block_spends_with_conditions".into(),
413 ))
414 }
415 }
416
417 pub async fn get_blocks(
418 &self,
419 start: u32,
420 end: u32,
421 exclude_header_hash: bool,
422 exclude_reorged: bool,
423 ) -> Result<Vec<FullBlock>, ChiaQueryError> {
424 self.peer_then_coinset(
425 self.peer.try_get_blocks_range(start, end),
426 self.peer.try_get_blocks_range(start, end),
427 self.coinset
428 .get_blocks(start, end, exclude_header_hash, exclude_reorged),
429 )
430 .await
431 }
432
433 pub async fn get_unfinished_block_headers(
434 &self,
435 ) -> Result<Vec<UnfinishedBlockHeader>, ChiaQueryError> {
436 self.require_coinset("get_unfinished_block_headers")?;
437 self.coinset.get_unfinished_block_headers().await
438 }
439}
440
441impl QueryRouter {
446 pub async fn get_coin_record_by_name(&self, name: &str) -> Result<CoinRecord, ChiaQueryError> {
447 self.peer_then_coinset(
448 self.peer.try_get_coin_record_by_name(name),
449 self.peer.try_get_coin_record_by_name(name),
450 self.coinset.get_coin_record_by_name(name),
451 )
452 .await
453 }
454
455 pub async fn get_coin_record_by_name_opt(
460 &self,
461 name: &str,
462 ) -> Result<Option<CoinRecord>, ChiaQueryError> {
463 self.peer_then_coinset_opt(
464 self.peer.try_get_coin_record_by_name_opt(name),
465 self.peer.try_get_coin_record_by_name_opt(name),
466 self.coinset.get_coin_record_by_name_opt(name),
467 )
468 .await
469 }
470
471 pub async fn get_coin_spend_opt(
474 &self,
475 coin_id: &str,
476 ) -> Result<Option<CoinSpend>, ChiaQueryError> {
477 self.peer_then_coinset_opt(
478 self.peer.try_get_coin_spend_opt(coin_id),
479 self.peer.try_get_coin_spend_opt(coin_id),
480 self.coinset.get_puzzle_and_solution_opt(coin_id, None),
481 )
482 .await
483 }
484
485 pub async fn peak_height_opt(&self) -> Result<Option<u32>, ChiaQueryError> {
487 let state = self.get_blockchain_state().await?;
488 Ok(state.peak.map(|p| p.height))
489 }
490
491 pub async fn block_timestamp_opt(&self, height: u32) -> Result<Option<u64>, ChiaQueryError> {
494 let record = self.get_block_record_by_height_opt(height).await?;
495 Ok(record.and_then(|r| r.timestamp))
496 }
497
498 async fn get_block_record_by_height_opt(
500 &self,
501 height: u32,
502 ) -> Result<Option<BlockRecord>, ChiaQueryError> {
503 if let Ok(record) = self.peer.try_get_block_record_by_height(height).await {
506 return Ok(Some(record));
507 }
508 match self.peer.try_get_block_record_by_height(height).await {
509 Ok(record) => Ok(Some(record)),
510 Err(peer_err) => {
511 if self.coinset_fallback_enabled {
512 self.coinset
513 .get_block_record_by_height_opt(height)
514 .await
515 .map_err(|ce| ChiaQueryError::AllSourcesFailed {
516 peer_error: Box::new(peer_err),
517 coinset_error: Some(Box::new(ce)),
518 })
519 } else {
520 Err(peer_err)
521 }
522 }
523 }
524 }
525
526 pub async fn get_coin_records_by_hint(
527 &self,
528 hint: &str,
529 start_height: Option<u32>,
530 end_height: Option<u32>,
531 include_spent_coins: bool,
532 ) -> Result<Vec<CoinRecord>, ChiaQueryError> {
533 self.peer_then_coinset(
534 self.peer.try_get_coin_records_by_hint(
535 hint,
536 start_height,
537 end_height,
538 include_spent_coins,
539 ),
540 self.peer.try_get_coin_records_by_hint(
541 hint,
542 start_height,
543 end_height,
544 include_spent_coins,
545 ),
546 self.coinset.get_coin_records_by_hint(
547 hint,
548 start_height,
549 end_height,
550 include_spent_coins,
551 ),
552 )
553 .await
554 }
555
556 pub async fn get_coin_records_by_hints(
557 &self,
558 hints: &[String],
559 start_height: Option<u32>,
560 end_height: Option<u32>,
561 include_spent_coins: bool,
562 ) -> Result<Vec<CoinRecord>, ChiaQueryError> {
563 self.peer_then_coinset(
564 self.peer.try_get_coin_records_by_hints(
565 hints,
566 start_height,
567 end_height,
568 include_spent_coins,
569 ),
570 self.peer.try_get_coin_records_by_hints(
571 hints,
572 start_height,
573 end_height,
574 include_spent_coins,
575 ),
576 self.coinset.get_coin_records_by_hints(
577 hints,
578 start_height,
579 end_height,
580 include_spent_coins,
581 ),
582 )
583 .await
584 }
585
586 pub async fn get_coin_records_by_names(
587 &self,
588 names: &[String],
589 start_height: Option<u32>,
590 end_height: Option<u32>,
591 include_spent_coins: bool,
592 ) -> Result<Vec<CoinRecord>, ChiaQueryError> {
593 self.peer_then_coinset(
594 self.peer.try_get_coin_records_by_names(names),
595 self.peer.try_get_coin_records_by_names(names),
596 self.coinset.get_coin_records_by_names(
597 names,
598 start_height,
599 end_height,
600 include_spent_coins,
601 ),
602 )
603 .await
604 }
605
606 pub async fn get_coin_records_by_parent_ids(
610 &self,
611 parent_ids: &[String],
612 start_height: Option<u32>,
613 end_height: Option<u32>,
614 include_spent_coins: bool,
615 ) -> Result<Vec<CoinRecord>, ChiaQueryError> {
616 let peer_attempt = async {
618 let mut all_records = Vec::new();
619 for parent_id in parent_ids {
620 let children = self.peer.try_get_children(parent_id).await?;
621 all_records.extend(children);
622 }
623 all_records.retain(|r| {
625 let height_ok = match (start_height, end_height) {
626 (Some(s), Some(e)) => {
627 r.confirmed_block_index >= s && r.confirmed_block_index <= e
628 }
629 (Some(s), None) => r.confirmed_block_index >= s,
630 (None, Some(e)) => r.confirmed_block_index <= e,
631 (None, None) => true,
632 };
633 let spent_ok = include_spent_coins || !r.spent;
634 height_ok && spent_ok
635 });
636 Ok(all_records)
637 };
638
639 match peer_attempt.await {
640 Ok(r) => Ok(r),
641 Err(peer_err) => {
642 if self.coinset_fallback_enabled {
643 self.coinset
644 .get_coin_records_by_parent_ids(
645 parent_ids,
646 start_height,
647 end_height,
648 include_spent_coins,
649 )
650 .await
651 .map_err(|ce| ChiaQueryError::AllSourcesFailed {
652 peer_error: Box::new(peer_err),
653 coinset_error: Some(Box::new(ce)),
654 })
655 } else {
656 Err(peer_err)
657 }
658 }
659 }
660 }
661
662 pub async fn get_coin_records_by_puzzle_hash(
663 &self,
664 puzzle_hash: &str,
665 start_height: Option<u32>,
666 end_height: Option<u32>,
667 include_spent_coins: bool,
668 ) -> Result<Vec<CoinRecord>, ChiaQueryError> {
669 self.peer_then_coinset(
670 self.peer.try_get_coin_records_by_puzzle_hash(
671 puzzle_hash,
672 start_height,
673 end_height,
674 include_spent_coins,
675 ),
676 self.peer.try_get_coin_records_by_puzzle_hash(
677 puzzle_hash,
678 start_height,
679 end_height,
680 include_spent_coins,
681 ),
682 self.coinset.get_coin_records_by_puzzle_hash(
683 puzzle_hash,
684 start_height,
685 end_height,
686 include_spent_coins,
687 ),
688 )
689 .await
690 }
691
692 pub async fn get_coin_records_by_puzzle_hashes(
693 &self,
694 puzzle_hashes: &[String],
695 start_height: Option<u32>,
696 end_height: Option<u32>,
697 include_spent_coins: bool,
698 ) -> Result<Vec<CoinRecord>, ChiaQueryError> {
699 self.peer_then_coinset(
700 self.peer.try_get_coin_records_by_puzzle_hashes(
701 puzzle_hashes,
702 start_height,
703 end_height,
704 include_spent_coins,
705 ),
706 self.peer.try_get_coin_records_by_puzzle_hashes(
707 puzzle_hashes,
708 start_height,
709 end_height,
710 include_spent_coins,
711 ),
712 self.coinset.get_coin_records_by_puzzle_hashes(
713 puzzle_hashes,
714 start_height,
715 end_height,
716 include_spent_coins,
717 ),
718 )
719 .await
720 }
721
722 pub async fn get_memos_by_coin_name(&self, name: &str) -> Result<Value, ChiaQueryError> {
724 self.require_coinset("get_memos_by_coin_name")?;
725 self.coinset.get_memos_by_coin_name(name).await
726 }
727
728 pub async fn get_puzzle_and_solution(
729 &self,
730 coin_id: &str,
731 height: Option<u32>,
732 ) -> Result<CoinSpend, ChiaQueryError> {
733 if let Some(h) = height {
734 self.peer_then_coinset(
735 self.peer.try_get_puzzle_and_solution(coin_id, h),
736 self.peer.try_get_puzzle_and_solution(coin_id, h),
737 self.coinset.get_puzzle_and_solution(coin_id, height),
738 )
739 .await
740 } else {
741 self.peer_then_coinset(
743 self.peer.try_get_puzzle_and_solution_auto(coin_id),
744 self.peer.try_get_puzzle_and_solution_auto(coin_id),
745 self.coinset.get_puzzle_and_solution(coin_id, None),
746 )
747 .await
748 }
749 }
750
751 pub async fn get_puzzle_and_solution_with_conditions(
754 &self,
755 coin_id: &str,
756 height: Option<u32>,
757 ) -> Result<CoinSpendWithConditions, ChiaQueryError> {
758 let spend = match self.get_puzzle_and_solution(coin_id, height).await {
760 Ok(s) => s,
761 Err(_) => {
762 if self.coinset_fallback_enabled {
763 return self
764 .coinset
765 .get_puzzle_and_solution_with_conditions(coin_id, height)
766 .await;
767 }
768 return Err(ChiaQueryError::PeerRejection(
769 "could not retrieve puzzle and solution".into(),
770 ));
771 }
772 };
773
774 let conditions = run_puzzle_conditions(&spend, self.peer.constants());
776 Ok(CoinSpendWithConditions {
777 coin_spend: spend,
778 conditions,
779 })
780 }
781
782 pub async fn push_tx(&self, bundle: &SpendBundle) -> Result<TxStatus, ChiaQueryError> {
783 self.peer_then_coinset(
784 self.peer.try_push_tx(bundle),
785 self.peer.try_push_tx(bundle),
786 self.coinset.push_tx(bundle),
787 )
788 .await
789 }
790}
791
792impl QueryRouter {
797 pub async fn get_fee_estimate(
798 &self,
799 spend_bundle: Option<&SpendBundle>,
800 target_times: Option<&[u64]>,
801 spend_count: Option<u64>,
802 ) -> Result<FeeEstimate, ChiaQueryError> {
803 let times = target_times.unwrap_or(&[60, 120, 300]);
804 self.peer_then_coinset(
805 self.peer.try_get_fee_estimate(times),
806 self.peer.try_get_fee_estimate(times),
807 self.coinset
808 .get_fee_estimate(spend_bundle, target_times, spend_count),
809 )
810 .await
811 }
812}
813
814impl QueryRouter {
819 pub async fn get_aggsig_additional_data(&self) -> Result<String, ChiaQueryError> {
822 Ok(self.peer.aggsig_additional_data())
823 }
824
825 pub async fn get_network_info(&self) -> Result<NetworkInfo, ChiaQueryError> {
828 Ok(self.peer.network_info())
829 }
830
831 pub async fn get_blockchain_state(&self) -> Result<BlockchainState, ChiaQueryError> {
834 if self.coinset_fallback_enabled {
836 if let Ok(state) = self.coinset.get_blockchain_state().await {
837 return Ok(state);
838 }
839 }
840 let peak = self.peer.peak_height();
842 if peak == 0 {
843 return Err(ChiaQueryError::PeerConnection(
844 "no peak observed from peers yet".into(),
845 ));
846 }
847 Ok(BlockchainState {
848 peak: Some(BlockRecord {
849 height: peak,
850 ..Default::default()
851 }),
852 sync: Some(SyncState {
853 synced: true,
854 sync_mode: false,
855 sync_progress_height: peak,
856 sync_tip_height: peak,
857 }),
858 ..Default::default()
859 })
860 }
861
862 pub async fn get_network_space(
863 &self,
864 newer_block_header_hash: &str,
865 older_block_header_hash: &str,
866 ) -> Result<u64, ChiaQueryError> {
867 self.require_coinset("get_network_space")?;
868 self.coinset
869 .get_network_space(newer_block_header_hash, older_block_header_hash)
870 .await
871 }
872}
873
874impl QueryRouter {
879 pub async fn get_all_mempool_items(
880 &self,
881 ) -> Result<HashMap<String, MempoolItem>, ChiaQueryError> {
882 self.require_coinset("get_all_mempool_items")?;
883 self.coinset.get_all_mempool_items().await
884 }
885
886 pub async fn get_all_mempool_tx_ids(&self) -> Result<Vec<String>, ChiaQueryError> {
887 self.require_coinset("get_all_mempool_tx_ids")?;
888 self.coinset.get_all_mempool_tx_ids().await
889 }
890
891 pub async fn get_mempool_item_by_tx_id(
892 &self,
893 tx_id: &str,
894 ) -> Result<MempoolItem, ChiaQueryError> {
895 self.require_coinset("get_mempool_item_by_tx_id")?;
896 self.coinset.get_mempool_item_by_tx_id(tx_id).await
897 }
898
899 pub async fn get_mempool_items_by_coin_name(
900 &self,
901 coin_name: &str,
902 include_spent_coins: Option<bool>,
903 ) -> Result<Vec<MempoolItem>, ChiaQueryError> {
904 self.require_coinset("get_mempool_items_by_coin_name")?;
905 self.coinset
906 .get_mempool_items_by_coin_name(coin_name, include_spent_coins)
907 .await
908 }
909}