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