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 {
510 return Ok(Some(record));
511 }
512 match self.peer.try_get_block_record_by_height(height).await {
513 Ok(record) => Ok(Some(record)),
514 Err(peer_err) => {
515 if self.coinset_fallback_enabled {
516 self.coinset
517 .get_block_record_by_height_opt(height)
518 .await
519 .map_err(|ce| ChiaQueryError::AllSourcesFailed {
520 peer_error: Box::new(peer_err),
521 coinset_error: Some(Box::new(ce)),
522 })
523 } else {
524 Err(peer_err)
525 }
526 }
527 }
528 }
529
530 pub async fn get_coin_records_by_hint(
531 &self,
532 hint: &str,
533 start_height: Option<u32>,
534 end_height: Option<u32>,
535 include_spent_coins: bool,
536 ) -> Result<Vec<CoinRecord>, ChiaQueryError> {
537 self.peer_then_coinset(
538 self.peer.try_get_coin_records_by_hint(
539 hint,
540 start_height,
541 end_height,
542 include_spent_coins,
543 ),
544 self.peer.try_get_coin_records_by_hint(
545 hint,
546 start_height,
547 end_height,
548 include_spent_coins,
549 ),
550 self.coinset.get_coin_records_by_hint(
551 hint,
552 start_height,
553 end_height,
554 include_spent_coins,
555 ),
556 )
557 .await
558 }
559
560 pub async fn get_coin_records_by_hints(
561 &self,
562 hints: &[String],
563 start_height: Option<u32>,
564 end_height: Option<u32>,
565 include_spent_coins: bool,
566 ) -> Result<Vec<CoinRecord>, ChiaQueryError> {
567 self.peer_then_coinset(
568 self.peer.try_get_coin_records_by_hints(
569 hints,
570 start_height,
571 end_height,
572 include_spent_coins,
573 ),
574 self.peer.try_get_coin_records_by_hints(
575 hints,
576 start_height,
577 end_height,
578 include_spent_coins,
579 ),
580 self.coinset.get_coin_records_by_hints(
581 hints,
582 start_height,
583 end_height,
584 include_spent_coins,
585 ),
586 )
587 .await
588 }
589
590 pub async fn get_coin_records_by_names(
591 &self,
592 names: &[String],
593 start_height: Option<u32>,
594 end_height: Option<u32>,
595 include_spent_coins: bool,
596 ) -> Result<Vec<CoinRecord>, ChiaQueryError> {
597 self.peer_then_coinset(
598 self.peer.try_get_coin_records_by_names(names),
599 self.peer.try_get_coin_records_by_names(names),
600 self.coinset.get_coin_records_by_names(
601 names,
602 start_height,
603 end_height,
604 include_spent_coins,
605 ),
606 )
607 .await
608 }
609
610 pub async fn get_coin_records_by_parent_ids(
614 &self,
615 parent_ids: &[String],
616 start_height: Option<u32>,
617 end_height: Option<u32>,
618 include_spent_coins: bool,
619 ) -> Result<Vec<CoinRecord>, ChiaQueryError> {
620 let peer_attempt = async {
622 let mut all_records = Vec::new();
623 for parent_id in parent_ids {
624 let children = self.peer.try_get_children(parent_id).await?;
625 all_records.extend(children);
626 }
627 all_records.retain(|r| {
629 let height_ok = match (start_height, end_height) {
630 (Some(s), Some(e)) => {
631 r.confirmed_block_index >= s && r.confirmed_block_index <= e
632 }
633 (Some(s), None) => r.confirmed_block_index >= s,
634 (None, Some(e)) => r.confirmed_block_index <= e,
635 (None, None) => true,
636 };
637 let spent_ok = include_spent_coins || !r.spent;
638 height_ok && spent_ok
639 });
640 Ok(all_records)
641 };
642
643 match peer_attempt.await {
644 Ok(r) => Ok(r),
645 Err(peer_err) => {
646 if self.coinset_fallback_enabled {
647 self.coinset
648 .get_coin_records_by_parent_ids(
649 parent_ids,
650 start_height,
651 end_height,
652 include_spent_coins,
653 )
654 .await
655 .map_err(|ce| ChiaQueryError::AllSourcesFailed {
656 peer_error: Box::new(peer_err),
657 coinset_error: Some(Box::new(ce)),
658 })
659 } else {
660 Err(peer_err)
661 }
662 }
663 }
664 }
665
666 pub async fn get_coin_records_by_puzzle_hash(
667 &self,
668 puzzle_hash: &str,
669 start_height: Option<u32>,
670 end_height: Option<u32>,
671 include_spent_coins: bool,
672 ) -> Result<Vec<CoinRecord>, ChiaQueryError> {
673 self.peer_then_coinset(
674 self.peer.try_get_coin_records_by_puzzle_hash(
675 puzzle_hash,
676 start_height,
677 end_height,
678 include_spent_coins,
679 ),
680 self.peer.try_get_coin_records_by_puzzle_hash(
681 puzzle_hash,
682 start_height,
683 end_height,
684 include_spent_coins,
685 ),
686 self.coinset.get_coin_records_by_puzzle_hash(
687 puzzle_hash,
688 start_height,
689 end_height,
690 include_spent_coins,
691 ),
692 )
693 .await
694 }
695
696 pub async fn get_coin_records_by_puzzle_hashes(
697 &self,
698 puzzle_hashes: &[String],
699 start_height: Option<u32>,
700 end_height: Option<u32>,
701 include_spent_coins: bool,
702 ) -> Result<Vec<CoinRecord>, ChiaQueryError> {
703 self.peer_then_coinset(
704 self.peer.try_get_coin_records_by_puzzle_hashes(
705 puzzle_hashes,
706 start_height,
707 end_height,
708 include_spent_coins,
709 ),
710 self.peer.try_get_coin_records_by_puzzle_hashes(
711 puzzle_hashes,
712 start_height,
713 end_height,
714 include_spent_coins,
715 ),
716 self.coinset.get_coin_records_by_puzzle_hashes(
717 puzzle_hashes,
718 start_height,
719 end_height,
720 include_spent_coins,
721 ),
722 )
723 .await
724 }
725
726 pub async fn get_memos_by_coin_name(&self, name: &str) -> Result<Value, ChiaQueryError> {
728 self.require_coinset("get_memos_by_coin_name")?;
729 self.coinset.get_memos_by_coin_name(name).await
730 }
731
732 pub async fn get_puzzle_and_solution(
733 &self,
734 coin_id: &str,
735 height: Option<u32>,
736 ) -> Result<CoinSpend, ChiaQueryError> {
737 if let Some(h) = height {
738 self.peer_then_coinset(
739 self.peer.try_get_puzzle_and_solution(coin_id, h),
740 self.peer.try_get_puzzle_and_solution(coin_id, h),
741 self.coinset.get_puzzle_and_solution(coin_id, height),
742 )
743 .await
744 } else {
745 self.peer_then_coinset(
747 self.peer.try_get_puzzle_and_solution_auto(coin_id),
748 self.peer.try_get_puzzle_and_solution_auto(coin_id),
749 self.coinset.get_puzzle_and_solution(coin_id, None),
750 )
751 .await
752 }
753 }
754
755 pub async fn get_puzzle_and_solution_with_conditions(
758 &self,
759 coin_id: &str,
760 height: Option<u32>,
761 ) -> Result<CoinSpendWithConditions, ChiaQueryError> {
762 let spend = match self.get_puzzle_and_solution(coin_id, height).await {
764 Ok(s) => s,
765 Err(_) => {
766 if self.coinset_fallback_enabled {
767 return self
768 .coinset
769 .get_puzzle_and_solution_with_conditions(coin_id, height)
770 .await;
771 }
772 return Err(ChiaQueryError::PeerRejection(
773 "could not retrieve puzzle and solution".into(),
774 ));
775 }
776 };
777
778 let conditions = run_puzzle_conditions(&spend, self.peer.constants());
780 Ok(CoinSpendWithConditions {
781 coin_spend: spend,
782 conditions,
783 })
784 }
785
786 pub async fn push_tx(&self, bundle: &SpendBundle) -> Result<TxStatus, ChiaQueryError> {
787 self.peer_then_coinset(
788 self.peer.try_push_tx(bundle),
789 self.peer.try_push_tx(bundle),
790 self.coinset.push_tx(bundle),
791 )
792 .await
793 }
794}
795
796impl QueryRouter {
801 pub async fn get_fee_estimate(
802 &self,
803 spend_bundle: Option<&SpendBundle>,
804 target_times: Option<&[u64]>,
805 spend_count: Option<u64>,
806 ) -> Result<FeeEstimate, ChiaQueryError> {
807 let times = target_times.unwrap_or(&[60, 120, 300]);
808 self.peer_then_coinset(
809 self.peer.try_get_fee_estimate(times),
810 self.peer.try_get_fee_estimate(times),
811 self.coinset
812 .get_fee_estimate(spend_bundle, target_times, spend_count),
813 )
814 .await
815 }
816}
817
818impl QueryRouter {
823 pub async fn get_aggsig_additional_data(&self) -> Result<String, ChiaQueryError> {
826 Ok(self.peer.aggsig_additional_data())
827 }
828
829 pub async fn get_network_info(&self) -> Result<NetworkInfo, ChiaQueryError> {
832 Ok(self.peer.network_info())
833 }
834
835 pub async fn get_blockchain_state(&self) -> Result<BlockchainState, ChiaQueryError> {
838 if self.coinset_fallback_enabled {
840 if let Ok(state) = self.coinset.get_blockchain_state().await {
841 return Ok(state);
842 }
843 }
844 let peak = self.peer.peak_height();
846 if peak == 0 {
847 return Err(ChiaQueryError::PeerConnection(
848 "no peak observed from peers yet".into(),
849 ));
850 }
851 Ok(BlockchainState {
852 peak: Some(BlockRecord {
853 height: peak,
854 ..Default::default()
855 }),
856 sync: Some(SyncState {
857 synced: true,
858 sync_mode: false,
859 sync_progress_height: peak,
860 sync_tip_height: peak,
861 }),
862 ..Default::default()
863 })
864 }
865
866 pub async fn get_network_space(
867 &self,
868 newer_block_header_hash: &str,
869 older_block_header_hash: &str,
870 ) -> Result<u64, ChiaQueryError> {
871 self.require_coinset("get_network_space")?;
872 self.coinset
873 .get_network_space(newer_block_header_hash, older_block_header_hash)
874 .await
875 }
876}
877
878impl QueryRouter {
883 pub async fn get_all_mempool_items(
884 &self,
885 ) -> Result<HashMap<String, MempoolItem>, ChiaQueryError> {
886 self.require_coinset("get_all_mempool_items")?;
887 self.coinset.get_all_mempool_items().await
888 }
889
890 pub async fn get_all_mempool_tx_ids(&self) -> Result<Vec<String>, ChiaQueryError> {
891 self.require_coinset("get_all_mempool_tx_ids")?;
892 self.coinset.get_all_mempool_tx_ids().await
893 }
894
895 pub async fn get_mempool_item_by_tx_id(
896 &self,
897 tx_id: &str,
898 ) -> Result<MempoolItem, ChiaQueryError> {
899 self.require_coinset("get_mempool_item_by_tx_id")?;
900 self.coinset.get_mempool_item_by_tx_id(tx_id).await
901 }
902
903 pub async fn get_mempool_items_by_coin_name(
904 &self,
905 coin_name: &str,
906 include_spent_coins: Option<bool>,
907 ) -> Result<Vec<MempoolItem>, ChiaQueryError> {
908 self.require_coinset("get_mempool_items_by_coin_name")?;
909 self.coinset
910 .get_mempool_items_by_coin_name(coin_name, include_spent_coins)
911 .await
912 }
913}