1use std::{
10 cmp,
11 collections::{HashMap, HashSet},
12 fmt,
13 ops::RangeInclusive,
14 sync::Arc,
15 time::Duration,
16};
17
18use chrono::Utc;
19use futures::{future::OptionFuture, stream::FuturesOrdered, StreamExt, TryFutureExt};
20use hex::{FromHex, ToHex};
21use hex_data::HexData;
22use indexmap::IndexMap;
23use jsonrpsee::core::{async_trait, RpcResult as Result};
24use jsonrpsee_proc_macros::rpc;
25use jsonrpsee_types::{ErrorCode, ErrorObject};
26use tokio::{
27 sync::{broadcast, watch},
28 task::JoinHandle,
29};
30use tower::{Service, ServiceExt};
31use tracing::Instrument;
32
33use zcash_address::{unified::Encoding, TryFromAddress};
34use zcash_primitives::consensus::Parameters;
35
36use zebra_chain::{
37 amount::{self, Amount, NonNegative},
38 block::{self, Block, Commitment, Height, SerializedBlock, TryIntoHeight},
39 chain_sync_status::ChainSyncStatus,
40 chain_tip::{ChainTip, NetworkChainTipHeightEstimator},
41 parameters::{
42 subsidy::{FundingStreamReceiver, ParameterSubsidy},
43 ConsensusBranchId, Network, NetworkUpgrade, POW_AVERAGING_WINDOW,
44 },
45 primitives,
46 serialization::{ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize},
47 subtree::NoteCommitmentSubtreeIndex,
48 transaction::{self, SerializedTransaction, Transaction, UnminedTx},
49 transparent::{self, Address},
50 work::{
51 difficulty::{CompactDifficulty, ExpandedDifficulty, ParameterDifficulty, U256},
52 equihash::Solution,
53 },
54};
55use zebra_consensus::{
56 block_subsidy, funding_stream_address, funding_stream_values, miner_subsidy,
57 ParameterCheckpoint, RouterError,
58};
59use zebra_network::address_book_peers::AddressBookPeers;
60use zebra_node_services::mempool;
61use zebra_state::{
62 HashOrHeight, OutputIndex, OutputLocation, ReadRequest, ReadResponse, TransactionLocation,
63};
64
65use crate::{
66 config,
67 methods::trees::{GetSubtrees, GetTreestate, SubtreeRpcData},
68 queue::Queue,
69 server::{
70 self,
71 error::{MapError, OkOrError},
72 },
73};
74
75use types::{
76 get_block_template::{
77 self, constants::MEMPOOL_LONG_POLL_INTERVAL, proposal::proposal_block_from_template,
78 GetBlockTemplate, GetBlockTemplateHandler, ZCASHD_FUNDING_STREAM_ORDER,
79 },
80 get_blockchain_info, get_mining_info,
81 get_raw_mempool::{self, GetRawMempool},
82 long_poll::LongPollInput,
83 peer_info::PeerInfo,
84 submit_block,
85 subsidy::BlockSubsidy,
86 transaction::TransactionObject,
87 unified_address, validate_address, z_validate_address,
88};
89
90pub mod hex_data;
91pub mod trees;
92pub mod types;
93
94#[cfg(test)]
95mod tests;
96
97#[rpc(server)]
98pub trait Rpc {
100 #[method(name = "getinfo")]
115 async fn get_info(&self) -> Result<GetInfo>;
116
117 #[method(name = "getblockchaininfo")]
128 async fn get_blockchain_info(&self) -> Result<GetBlockChainInfo>;
129
130 #[method(name = "getaddressbalance")]
153 async fn get_address_balance(&self, address_strings: AddressStrings) -> Result<AddressBalance>;
154
155 #[method(name = "sendrawtransaction")]
172 async fn send_raw_transaction(
173 &self,
174 raw_transaction_hex: String,
175 _allow_high_fees: Option<bool>,
176 ) -> Result<SentTransactionHash>;
177
178 #[method(name = "getblock")]
198 async fn get_block(&self, hash_or_height: String, verbosity: Option<u8>) -> Result<GetBlock>;
199
200 #[method(name = "getblockheader")]
218 async fn get_block_header(
219 &self,
220 hash_or_height: String,
221 verbose: Option<bool>,
222 ) -> Result<GetBlockHeader>;
223
224 #[method(name = "getbestblockhash")]
230 fn get_best_block_hash(&self) -> Result<GetBlockHash>;
231
232 #[method(name = "getbestblockheightandhash")]
238 fn get_best_block_height_and_hash(&self) -> Result<GetBlockHeightAndHash>;
239
240 #[method(name = "getrawmempool")]
250 async fn get_raw_mempool(&self, verbose: Option<bool>) -> Result<GetRawMempool>;
251
252 #[method(name = "z_gettreestate")]
269 async fn z_get_treestate(&self, hash_or_height: String) -> Result<GetTreestate>;
270
271 #[method(name = "z_getsubtreesbyindex")]
290 async fn z_get_subtrees_by_index(
291 &self,
292 pool: String,
293 start_index: NoteCommitmentSubtreeIndex,
294 limit: Option<NoteCommitmentSubtreeIndex>,
295 ) -> Result<GetSubtrees>;
296
297 #[method(name = "getrawtransaction")]
317 async fn get_raw_transaction(
318 &self,
319 txid: String,
320 verbose: Option<u8>,
321 ) -> Result<GetRawTransaction>;
322
323 #[method(name = "getaddresstxids")]
341 async fn get_address_tx_ids(&self, request: GetAddressTxIdsRequest) -> Result<Vec<String>>;
342
343 #[method(name = "getaddressutxos")]
358 async fn get_address_utxos(
359 &self,
360 address_strings: AddressStrings,
361 ) -> Result<Vec<GetAddressUtxos>>;
362
363 #[method(name = "stop")]
374 fn stop(&self) -> Result<String>;
375
376 #[method(name = "getblockcount")]
383 fn get_block_count(&self) -> Result<u32>;
384
385 #[method(name = "getblockhash")]
401 async fn get_block_hash(&self, index: i32) -> Result<GetBlockHash>;
402
403 #[method(name = "getblocktemplate")]
425 async fn get_block_template(
426 &self,
427 parameters: Option<get_block_template::parameters::JsonParameters>,
428 ) -> Result<get_block_template::Response>;
429
430 #[method(name = "submitblock")]
446 async fn submit_block(
447 &self,
448 hex_data: HexData,
449 _parameters: Option<submit_block::JsonParameters>,
450 ) -> Result<submit_block::Response>;
451
452 #[method(name = "getmininginfo")]
458 async fn get_mining_info(&self) -> Result<get_mining_info::Response>;
459
460 #[method(name = "getnetworksolps")]
471 async fn get_network_sol_ps(&self, num_blocks: Option<i32>, height: Option<i32>)
472 -> Result<u64>;
473
474 #[method(name = "getnetworkhashps")]
484 async fn get_network_hash_ps(
485 &self,
486 num_blocks: Option<i32>,
487 height: Option<i32>,
488 ) -> Result<u64> {
489 self.get_network_sol_ps(num_blocks, height).await
490 }
491
492 #[method(name = "getpeerinfo")]
498 async fn get_peer_info(&self) -> Result<Vec<PeerInfo>>;
499
500 #[method(name = "validateaddress")]
511 async fn validate_address(&self, address: String) -> Result<validate_address::Response>;
512
513 #[method(name = "z_validateaddress")]
528 async fn z_validate_address(&self, address: String) -> Result<z_validate_address::Response>;
529
530 #[method(name = "getblocksubsidy")]
545 async fn get_block_subsidy(&self, height: Option<u32>) -> Result<BlockSubsidy>;
546
547 #[method(name = "getdifficulty")]
553 async fn get_difficulty(&self) -> Result<f64>;
554
555 #[method(name = "z_listunifiedreceivers")]
569 async fn z_list_unified_receivers(&self, address: String) -> Result<unified_address::Response>;
570
571 #[method(name = "generate")]
572 async fn generate(&self, num_blocks: u32) -> Result<Vec<GetBlockHash>>;
586}
587
588#[derive(Clone)]
590pub struct RpcImpl<Mempool, State, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
591where
592 Mempool: Service<
593 mempool::Request,
594 Response = mempool::Response,
595 Error = zebra_node_services::BoxError,
596 > + Clone
597 + Send
598 + Sync
599 + 'static,
600 Mempool::Future: Send,
601 State: Service<
602 zebra_state::ReadRequest,
603 Response = zebra_state::ReadResponse,
604 Error = zebra_state::BoxError,
605 > + Clone
606 + Send
607 + Sync
608 + 'static,
609 State::Future: Send,
610 Tip: ChainTip + Clone + Send + Sync + 'static,
611 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
612 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
613 + Clone
614 + Send
615 + Sync
616 + 'static,
617 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
618 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
619{
620 build_version: String,
624
625 user_agent: String,
627
628 network: Network,
630
631 debug_force_finished_sync: bool,
634
635 #[allow(dead_code)]
637 debug_like_zcashd: bool,
638
639 mempool: Mempool,
643
644 state: State,
646
647 latest_chain_tip: Tip,
649
650 queue_sender: broadcast::Sender<UnminedTx>,
654
655 address_book: AddressBook,
657
658 last_warn_error_log_rx: LoggedLastEvent,
660
661 gbt: GetBlockTemplateHandler<BlockVerifierRouter, SyncStatus>,
663}
664
665pub type LoggedLastEvent = watch::Receiver<Option<(String, tracing::Level, chrono::DateTime<Utc>)>>;
667
668impl<Mempool, State, Tip, AddressBook, BlockVerifierRouter, SyncStatus> fmt::Debug
669 for RpcImpl<Mempool, State, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
670where
671 Mempool: Service<
672 mempool::Request,
673 Response = mempool::Response,
674 Error = zebra_node_services::BoxError,
675 > + Clone
676 + Send
677 + Sync
678 + 'static,
679 Mempool::Future: Send,
680 State: Service<
681 zebra_state::ReadRequest,
682 Response = zebra_state::ReadResponse,
683 Error = zebra_state::BoxError,
684 > + Clone
685 + Send
686 + Sync
687 + 'static,
688 State::Future: Send,
689 Tip: ChainTip + Clone + Send + Sync + 'static,
690 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
691 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
692 + Clone
693 + Send
694 + Sync
695 + 'static,
696 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
697 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
698{
699 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
700 f.debug_struct("RpcImpl")
702 .field("build_version", &self.build_version)
703 .field("user_agent", &self.user_agent)
704 .field("network", &self.network)
705 .field("debug_force_finished_sync", &self.debug_force_finished_sync)
706 .field("debug_like_zcashd", &self.debug_like_zcashd)
707 .field("getblocktemplate", &self.gbt)
708 .finish()
709 }
710}
711
712impl<Mempool, State, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
713 RpcImpl<Mempool, State, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
714where
715 Mempool: Service<
716 mempool::Request,
717 Response = mempool::Response,
718 Error = zebra_node_services::BoxError,
719 > + Clone
720 + Send
721 + Sync
722 + 'static,
723 Mempool::Future: Send,
724 State: Service<
725 zebra_state::ReadRequest,
726 Response = zebra_state::ReadResponse,
727 Error = zebra_state::BoxError,
728 > + Clone
729 + Send
730 + Sync
731 + 'static,
732 State::Future: Send,
733 Tip: ChainTip + Clone + Send + Sync + 'static,
734 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
735 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
736 + Clone
737 + Send
738 + Sync
739 + 'static,
740 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
741 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
742{
743 #[allow(clippy::too_many_arguments)]
748 pub fn new<VersionString, UserAgentString>(
749 network: Network,
750 mining_config: config::mining::Config,
751 debug_force_finished_sync: bool,
752 build_version: VersionString,
753 user_agent: UserAgentString,
754 mempool: Mempool,
755 state: State,
756 block_verifier_router: BlockVerifierRouter,
757 sync_status: SyncStatus,
758 latest_chain_tip: Tip,
759 address_book: AddressBook,
760 last_warn_error_log_rx: LoggedLastEvent,
761 mined_block_sender: Option<watch::Sender<(block::Hash, block::Height)>>,
762 ) -> (Self, JoinHandle<()>)
763 where
764 VersionString: ToString + Clone + Send + 'static,
765 UserAgentString: ToString + Clone + Send + 'static,
766 {
767 let (runner, queue_sender) = Queue::start();
768
769 let mut build_version = build_version.to_string();
770 let user_agent = user_agent.to_string();
771
772 if !build_version.is_empty() && !build_version.starts_with('v') {
774 build_version.insert(0, 'v');
775 }
776
777 let gbt = GetBlockTemplateHandler::new(
778 &network,
779 mining_config.clone(),
780 block_verifier_router,
781 sync_status,
782 mined_block_sender,
783 );
784
785 let rpc_impl = RpcImpl {
786 build_version,
787 user_agent,
788 network: network.clone(),
789 debug_force_finished_sync,
790 debug_like_zcashd: mining_config.debug_like_zcashd,
791 mempool: mempool.clone(),
792 state: state.clone(),
793 latest_chain_tip: latest_chain_tip.clone(),
794 queue_sender,
795 address_book,
796 last_warn_error_log_rx,
797 gbt,
798 };
799
800 let rpc_tx_queue_task_handle = tokio::spawn(
802 runner
803 .run(mempool, state, latest_chain_tip, network)
804 .in_current_span(),
805 );
806
807 (rpc_impl, rpc_tx_queue_task_handle)
808 }
809}
810
811#[async_trait]
812impl<Mempool, State, Tip, AddressBook, BlockVerifierRouter, SyncStatus> RpcServer
813 for RpcImpl<Mempool, State, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
814where
815 Mempool: Service<
816 mempool::Request,
817 Response = mempool::Response,
818 Error = zebra_node_services::BoxError,
819 > + Clone
820 + Send
821 + Sync
822 + 'static,
823 Mempool::Future: Send,
824 State: Service<
825 zebra_state::ReadRequest,
826 Response = zebra_state::ReadResponse,
827 Error = zebra_state::BoxError,
828 > + Clone
829 + Send
830 + Sync
831 + 'static,
832 State::Future: Send,
833 Tip: ChainTip + Clone + Send + Sync + 'static,
834 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
835 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
836 + Clone
837 + Send
838 + Sync
839 + 'static,
840 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
841 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
842{
843 async fn get_info(&self) -> Result<GetInfo> {
844 let version = GetInfo::version(&self.build_version).expect("invalid version string");
845
846 let connections = self.address_book.recently_live_peers(Utc::now()).len();
847
848 let last_error_recorded = self.last_warn_error_log_rx.borrow().clone();
849 let (last_error_log, _level, last_error_log_time) = last_error_recorded.unwrap_or((
850 GetInfo::default().errors,
851 tracing::Level::INFO,
852 Utc::now(),
853 ));
854
855 let tip_height = self
856 .latest_chain_tip
857 .best_tip_height()
858 .unwrap_or(Height::MIN);
859 let testnet = self.network.is_a_test_network();
860
861 let pay_tx_fee = 0.0;
867
868 let relay_fee = zebra_chain::transaction::zip317::MIN_MEMPOOL_TX_FEE_RATE as f64
869 / (zebra_chain::amount::COIN as f64);
870 let difficulty = chain_tip_difficulty(self.network.clone(), self.state.clone(), true)
871 .await
872 .expect("should always be Ok when `should_use_default` is true");
873
874 let response = GetInfo {
875 version,
876 build: self.build_version.clone(),
877 subversion: self.user_agent.clone(),
878 protocol_version: zebra_network::constants::CURRENT_NETWORK_PROTOCOL_VERSION.0,
879 blocks: tip_height.0,
880 connections,
881 proxy: None,
882 difficulty,
883 testnet,
884 pay_tx_fee,
885 relay_fee,
886 errors: last_error_log,
887 errors_timestamp: last_error_log_time.to_string(),
888 };
889
890 Ok(response)
891 }
892
893 #[allow(clippy::unwrap_in_result)]
894 async fn get_blockchain_info(&self) -> Result<GetBlockChainInfo> {
895 let debug_force_finished_sync = self.debug_force_finished_sync;
896 let network = &self.network;
897
898 let (usage_info_rsp, tip_pool_values_rsp, chain_tip_difficulty) = {
899 use zebra_state::ReadRequest::*;
900 let state_call = |request| self.state.clone().oneshot(request);
901 tokio::join!(
902 state_call(UsageInfo),
903 state_call(TipPoolValues),
904 chain_tip_difficulty(network.clone(), self.state.clone(), true)
905 )
906 };
907
908 let (size_on_disk, (tip_height, tip_hash), value_balance, difficulty) = {
909 use zebra_state::ReadResponse::*;
910
911 let UsageInfo(size_on_disk) = usage_info_rsp.map_misc_error()? else {
912 unreachable!("unmatched response to a TipPoolValues request")
913 };
914
915 let (tip, value_balance) = match tip_pool_values_rsp {
916 Ok(TipPoolValues {
917 tip_height,
918 tip_hash,
919 value_balance,
920 }) => ((tip_height, tip_hash), value_balance),
921 Ok(_) => unreachable!("unmatched response to a TipPoolValues request"),
922 Err(_) => ((Height::MIN, network.genesis_hash()), Default::default()),
923 };
924
925 let difficulty = chain_tip_difficulty
926 .expect("should always be Ok when `should_use_default` is true");
927
928 (size_on_disk, tip, value_balance, difficulty)
929 };
930
931 let now = Utc::now();
932 let (estimated_height, verification_progress) = self
933 .latest_chain_tip
934 .best_tip_height_and_block_time()
935 .map(|(tip_height, tip_block_time)| {
936 let height =
937 NetworkChainTipHeightEstimator::new(tip_block_time, tip_height, network)
938 .estimate_height_at(now);
939
940 let height =
944 if tip_block_time > now || height < tip_height || debug_force_finished_sync {
945 tip_height
946 } else {
947 height
948 };
949
950 (height, f64::from(tip_height.0) / f64::from(height.0))
951 })
952 .unwrap_or((Height::MIN, 0.0));
954
955 let mut upgrades = IndexMap::new();
959 for (activation_height, network_upgrade) in network.full_activation_list() {
960 if let Some(branch_id) = network_upgrade.branch_id() {
965 let status = if tip_height >= activation_height {
967 NetworkUpgradeStatus::Active
968 } else {
969 NetworkUpgradeStatus::Pending
970 };
971
972 let upgrade = NetworkUpgradeInfo {
973 name: network_upgrade,
974 activation_height,
975 status,
976 };
977 upgrades.insert(ConsensusBranchIdHex(branch_id), upgrade);
978 }
979 }
980
981 let next_block_height =
983 (tip_height + 1).expect("valid chain tips are a lot less than Height::MAX");
984 let consensus = TipConsensusBranch {
985 chain_tip: ConsensusBranchIdHex(
986 NetworkUpgrade::current(network, tip_height)
987 .branch_id()
988 .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
989 ),
990 next_block: ConsensusBranchIdHex(
991 NetworkUpgrade::current(network, next_block_height)
992 .branch_id()
993 .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
994 ),
995 };
996
997 let response = GetBlockChainInfo {
998 chain: network.bip70_network_name(),
999 blocks: tip_height,
1000 best_block_hash: tip_hash,
1001 estimated_height,
1002 chain_supply: get_blockchain_info::Balance::chain_supply(value_balance),
1003 value_pools: get_blockchain_info::Balance::value_pools(value_balance),
1004 upgrades,
1005 consensus,
1006 headers: tip_height,
1007 difficulty,
1008 verification_progress,
1009 chain_work: 0,
1011 pruned: false,
1012 size_on_disk,
1013 commitments: 0,
1015 };
1016
1017 Ok(response)
1018 }
1019
1020 async fn get_address_balance(&self, address_strings: AddressStrings) -> Result<AddressBalance> {
1021 let state = self.state.clone();
1022
1023 let valid_addresses = address_strings.valid_addresses()?;
1024
1025 let request = zebra_state::ReadRequest::AddressBalance(valid_addresses);
1026 let response = state.oneshot(request).await.map_misc_error()?;
1027
1028 match response {
1029 zebra_state::ReadResponse::AddressBalance(balance) => Ok(AddressBalance {
1030 balance: u64::from(balance),
1031 }),
1032 _ => unreachable!("Unexpected response from state service: {response:?}"),
1033 }
1034 }
1035
1036 async fn send_raw_transaction(
1038 &self,
1039 raw_transaction_hex: String,
1040 _allow_high_fees: Option<bool>,
1041 ) -> Result<SentTransactionHash> {
1042 let mempool = self.mempool.clone();
1043 let queue_sender = self.queue_sender.clone();
1044
1045 let raw_transaction_bytes = Vec::from_hex(raw_transaction_hex)
1048 .map_error(server::error::LegacyCode::Deserialization)?;
1049 let raw_transaction = Transaction::zcash_deserialize(&*raw_transaction_bytes)
1050 .map_error(server::error::LegacyCode::Deserialization)?;
1051
1052 let transaction_hash = raw_transaction.hash();
1053
1054 let unmined_transaction = UnminedTx::from(raw_transaction.clone());
1056 let _ = queue_sender.send(unmined_transaction);
1057
1058 let transaction_parameter = mempool::Gossip::Tx(raw_transaction.into());
1059 let request = mempool::Request::Queue(vec![transaction_parameter]);
1060
1061 let response = mempool.oneshot(request).await.map_misc_error()?;
1062
1063 let mut queue_results = match response {
1064 mempool::Response::Queued(results) => results,
1065 _ => unreachable!("incorrect response variant from mempool service"),
1066 };
1067
1068 assert_eq!(
1069 queue_results.len(),
1070 1,
1071 "mempool service returned more results than expected"
1072 );
1073
1074 let queue_result = queue_results
1075 .pop()
1076 .expect("there should be exactly one item in Vec")
1077 .inspect_err(|err| tracing::debug!("sent transaction to mempool: {:?}", &err))
1078 .map_misc_error()?
1079 .await
1080 .map_misc_error()?;
1081
1082 tracing::debug!("sent transaction to mempool: {:?}", &queue_result);
1083
1084 queue_result
1085 .map(|_| SentTransactionHash(transaction_hash))
1086 .map_error(server::error::LegacyCode::Verify)
1093 }
1094
1095 async fn get_block(&self, hash_or_height: String, verbosity: Option<u8>) -> Result<GetBlock> {
1104 let mut state = self.state.clone();
1105 let verbosity = verbosity.unwrap_or(1);
1106 let network = self.network.clone();
1107 let original_hash_or_height = hash_or_height.clone();
1108
1109 let get_block_header_future = if matches!(verbosity, 1 | 2) {
1111 Some(self.get_block_header(original_hash_or_height.clone(), Some(true)))
1112 } else {
1113 None
1114 };
1115
1116 let hash_or_height =
1117 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1118 .map_error(server::error::LegacyCode::InvalidParameter)?;
1121
1122 if verbosity == 0 {
1123 let request = zebra_state::ReadRequest::Block(hash_or_height);
1124 let response = state
1125 .ready()
1126 .and_then(|service| service.call(request))
1127 .await
1128 .map_misc_error()?;
1129
1130 match response {
1131 zebra_state::ReadResponse::Block(Some(block)) => Ok(GetBlock::Raw(block.into())),
1132 zebra_state::ReadResponse::Block(None) => {
1133 Err("Block not found").map_error(server::error::LegacyCode::InvalidParameter)
1134 }
1135 _ => unreachable!("unmatched response to a block request"),
1136 }
1137 } else if let Some(get_block_header_future) = get_block_header_future {
1138 let get_block_header_result: Result<GetBlockHeader> = get_block_header_future.await;
1139
1140 let GetBlockHeader::Object(block_header) = get_block_header_result? else {
1141 panic!("must return Object")
1142 };
1143
1144 let GetBlockHeaderObject {
1145 hash,
1146 confirmations,
1147 height,
1148 version,
1149 merkle_root,
1150 block_commitments,
1151 final_sapling_root,
1152 sapling_tree_size,
1153 time,
1154 nonce,
1155 solution,
1156 bits,
1157 difficulty,
1158 previous_block_hash,
1159 next_block_hash,
1160 } = *block_header;
1161
1162 let transactions_request = match verbosity {
1163 1 => zebra_state::ReadRequest::TransactionIdsForBlock(hash_or_height),
1164 2 => zebra_state::ReadRequest::BlockAndSize(hash_or_height),
1165 _other => panic!("get_block_header_fut should be none"),
1166 };
1167
1168 let hash_or_height = hash.0.into();
1173 let requests = vec![
1174 transactions_request,
1182 zebra_state::ReadRequest::OrchardTree(hash_or_height),
1184 ];
1185
1186 let mut futs = FuturesOrdered::new();
1187
1188 for request in requests {
1189 futs.push_back(state.clone().oneshot(request));
1190 }
1191
1192 let tx_ids_response = futs.next().await.expect("`futs` should not be empty");
1193 let (tx, size): (Vec<_>, Option<usize>) = match tx_ids_response.map_misc_error()? {
1194 zebra_state::ReadResponse::TransactionIdsForBlock(tx_ids) => (
1195 tx_ids
1196 .ok_or_misc_error("block not found")?
1197 .iter()
1198 .map(|tx_id| GetBlockTransaction::Hash(*tx_id))
1199 .collect(),
1200 None,
1201 ),
1202 zebra_state::ReadResponse::BlockAndSize(block_and_size) => {
1203 let (block, size) = block_and_size.ok_or_misc_error("Block not found")?;
1204 let block_time = block.header.time;
1205 let transactions =
1206 block
1207 .transactions
1208 .iter()
1209 .map(|tx| {
1210 GetBlockTransaction::Object(Box::new(
1211 TransactionObject::from_transaction(
1212 tx.clone(),
1213 Some(height),
1214 Some(confirmations.try_into().expect(
1215 "should be less than max block height, i32::MAX",
1216 )),
1217 &network,
1218 Some(block_time),
1219 ),
1220 ))
1221 })
1222 .collect();
1223 (transactions, Some(size))
1224 }
1225 _ => unreachable!("unmatched response to a transaction_ids_for_block request"),
1226 };
1227
1228 let orchard_tree_response = futs.next().await.expect("`futs` should not be empty");
1229 let zebra_state::ReadResponse::OrchardTree(orchard_tree) =
1230 orchard_tree_response.map_misc_error()?
1231 else {
1232 unreachable!("unmatched response to a OrchardTree request");
1233 };
1234
1235 let nu5_activation = NetworkUpgrade::Nu5.activation_height(&network);
1236
1237 let orchard_tree = orchard_tree.ok_or_misc_error("missing Orchard tree")?;
1239
1240 let final_orchard_root = match nu5_activation {
1241 Some(activation_height) if height >= activation_height => {
1242 Some(orchard_tree.root().into())
1243 }
1244 _other => None,
1245 };
1246
1247 let sapling = SaplingTrees {
1248 size: sapling_tree_size,
1249 };
1250
1251 let orchard_tree_size = orchard_tree.count();
1252 let orchard = OrchardTrees {
1253 size: orchard_tree_size,
1254 };
1255
1256 let trees = GetBlockTrees { sapling, orchard };
1257
1258 Ok(GetBlock::Object {
1259 hash,
1260 confirmations,
1261 height: Some(height),
1262 version: Some(version),
1263 merkle_root: Some(merkle_root),
1264 time: Some(time),
1265 nonce: Some(nonce),
1266 solution: Some(solution),
1267 bits: Some(bits),
1268 difficulty: Some(difficulty),
1269 tx,
1270 trees,
1271 size: size.map(|size| size as i64),
1272 block_commitments: Some(block_commitments),
1273 final_sapling_root: Some(final_sapling_root),
1274 final_orchard_root,
1275 previous_block_hash: Some(previous_block_hash),
1276 next_block_hash,
1277 })
1278 } else {
1279 Err("invalid verbosity value").map_error(server::error::LegacyCode::InvalidParameter)
1280 }
1281 }
1282
1283 async fn get_block_header(
1284 &self,
1285 hash_or_height: String,
1286 verbose: Option<bool>,
1287 ) -> Result<GetBlockHeader> {
1288 let state = self.state.clone();
1289 let verbose = verbose.unwrap_or(true);
1290 let network = self.network.clone();
1291
1292 let hash_or_height =
1293 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1294 .map_error(server::error::LegacyCode::InvalidParameter)?;
1297 let zebra_state::ReadResponse::BlockHeader {
1298 header,
1299 hash,
1300 height,
1301 next_block_hash,
1302 } = state
1303 .clone()
1304 .oneshot(zebra_state::ReadRequest::BlockHeader(hash_or_height))
1305 .await
1306 .map_err(|_| "block height not in best chain")
1307 .map_error(
1308 if hash_or_height.hash().is_some() {
1313 server::error::LegacyCode::InvalidAddressOrKey
1314 } else {
1315 server::error::LegacyCode::InvalidParameter
1316 },
1317 )?
1318 else {
1319 panic!("unexpected response to BlockHeader request")
1320 };
1321
1322 let response = if !verbose {
1323 GetBlockHeader::Raw(HexData(header.zcash_serialize_to_vec().map_misc_error()?))
1324 } else {
1325 let zebra_state::ReadResponse::SaplingTree(sapling_tree) = state
1326 .clone()
1327 .oneshot(zebra_state::ReadRequest::SaplingTree(hash_or_height))
1328 .await
1329 .map_misc_error()?
1330 else {
1331 panic!("unexpected response to SaplingTree request")
1332 };
1333
1334 let sapling_tree = sapling_tree.ok_or_misc_error("missing Sapling tree")?;
1336
1337 let zebra_state::ReadResponse::Depth(depth) = state
1338 .clone()
1339 .oneshot(zebra_state::ReadRequest::Depth(hash))
1340 .await
1341 .map_misc_error()?
1342 else {
1343 panic!("unexpected response to SaplingTree request")
1344 };
1345
1346 const NOT_IN_BEST_CHAIN_CONFIRMATIONS: i64 = -1;
1349
1350 let confirmations = depth
1353 .map(|depth| i64::from(depth) + 1)
1354 .unwrap_or(NOT_IN_BEST_CHAIN_CONFIRMATIONS);
1355
1356 let mut nonce = *header.nonce;
1357 nonce.reverse();
1358
1359 let sapling_activation = NetworkUpgrade::Sapling.activation_height(&network);
1360 let sapling_tree_size = sapling_tree.count();
1361 let final_sapling_root: [u8; 32] =
1362 if sapling_activation.is_some() && height >= sapling_activation.unwrap() {
1363 let mut root: [u8; 32] = sapling_tree.root().into();
1364 root.reverse();
1365 root
1366 } else {
1367 [0; 32]
1368 };
1369
1370 let difficulty = header.difficulty_threshold.relative_to_network(&network);
1371
1372 let block_commitments = match header.commitment(&network, height).expect(
1373 "Unexpected failure while parsing the blockcommitments field in get_block_header",
1374 ) {
1375 Commitment::PreSaplingReserved(bytes) => bytes,
1376 Commitment::FinalSaplingRoot(_) => final_sapling_root,
1377 Commitment::ChainHistoryActivationReserved => [0; 32],
1378 Commitment::ChainHistoryRoot(root) => root.bytes_in_display_order(),
1379 Commitment::ChainHistoryBlockTxAuthCommitment(hash) => {
1380 hash.bytes_in_display_order()
1381 }
1382 };
1383
1384 let block_header = GetBlockHeaderObject {
1385 hash: GetBlockHash(hash),
1386 confirmations,
1387 height,
1388 version: header.version,
1389 merkle_root: header.merkle_root,
1390 block_commitments,
1391 final_sapling_root,
1392 sapling_tree_size,
1393 time: header.time.timestamp(),
1394 nonce,
1395 solution: header.solution,
1396 bits: header.difficulty_threshold,
1397 difficulty,
1398 previous_block_hash: GetBlockHash(header.previous_block_hash),
1399 next_block_hash: next_block_hash.map(GetBlockHash),
1400 };
1401
1402 GetBlockHeader::Object(Box::new(block_header))
1403 };
1404
1405 Ok(response)
1406 }
1407
1408 fn get_best_block_hash(&self) -> Result<GetBlockHash> {
1409 self.latest_chain_tip
1410 .best_tip_hash()
1411 .map(GetBlockHash)
1412 .ok_or_misc_error("No blocks in state")
1413 }
1414
1415 fn get_best_block_height_and_hash(&self) -> Result<GetBlockHeightAndHash> {
1416 self.latest_chain_tip
1417 .best_tip_height_and_hash()
1418 .map(|(height, hash)| GetBlockHeightAndHash { height, hash })
1419 .ok_or_misc_error("No blocks in state")
1420 }
1421
1422 async fn get_raw_mempool(&self, verbose: Option<bool>) -> Result<GetRawMempool> {
1423 #[allow(unused)]
1424 let verbose = verbose.unwrap_or(false);
1425
1426 use zebra_chain::block::MAX_BLOCK_BYTES;
1427
1428 let should_use_zcashd_order = self.debug_like_zcashd;
1430
1431 let mut mempool = self.mempool.clone();
1432
1433 let request = if should_use_zcashd_order || verbose {
1434 mempool::Request::FullTransactions
1435 } else {
1436 mempool::Request::TransactionIds
1437 };
1438
1439 let response = mempool
1441 .ready()
1442 .and_then(|service| service.call(request))
1443 .await
1444 .map_misc_error()?;
1445
1446 match response {
1447 mempool::Response::FullTransactions {
1448 mut transactions,
1449 transaction_dependencies,
1450 last_seen_tip_hash: _,
1451 } => {
1452 if verbose {
1453 let map = transactions
1454 .iter()
1455 .map(|unmined_tx| {
1456 (
1457 unmined_tx.transaction.id.mined_id().encode_hex(),
1458 get_raw_mempool::MempoolObject::from_verified_unmined_tx(
1459 unmined_tx,
1460 &transactions,
1461 &transaction_dependencies,
1462 ),
1463 )
1464 })
1465 .collect::<HashMap<_, _>>();
1466 Ok(GetRawMempool::Verbose(map))
1467 } else {
1468 transactions.sort_by_cached_key(|tx| {
1473 cmp::Reverse((
1476 i64::from(tx.miner_fee) as u128 * MAX_BLOCK_BYTES as u128
1477 / tx.transaction.size as u128,
1478 tx.transaction.id.mined_id(),
1480 ))
1481 });
1482 let tx_ids: Vec<String> = transactions
1483 .iter()
1484 .map(|unmined_tx| unmined_tx.transaction.id.mined_id().encode_hex())
1485 .collect();
1486
1487 Ok(GetRawMempool::TxIds(tx_ids))
1488 }
1489 }
1490
1491 mempool::Response::TransactionIds(unmined_transaction_ids) => {
1492 let mut tx_ids: Vec<String> = unmined_transaction_ids
1493 .iter()
1494 .map(|id| id.mined_id().encode_hex())
1495 .collect();
1496
1497 tx_ids.sort();
1499
1500 Ok(GetRawMempool::TxIds(tx_ids))
1501 }
1502
1503 _ => unreachable!("unmatched response to a transactionids request"),
1504 }
1505 }
1506
1507 async fn get_raw_transaction(
1508 &self,
1509 txid: String,
1510 verbose: Option<u8>,
1511 ) -> Result<GetRawTransaction> {
1512 let mut state = self.state.clone();
1513 let mut mempool = self.mempool.clone();
1514 let verbose = verbose.unwrap_or(0) != 0;
1515
1516 let txid = transaction::Hash::from_hex(txid)
1519 .map_error(server::error::LegacyCode::InvalidAddressOrKey)?;
1520
1521 match mempool
1523 .ready()
1524 .and_then(|service| {
1525 service.call(mempool::Request::TransactionsByMinedId([txid].into()))
1526 })
1527 .await
1528 .map_misc_error()?
1529 {
1530 mempool::Response::Transactions(txns) => {
1531 if let Some(tx) = txns.first() {
1532 return Ok(if verbose {
1533 GetRawTransaction::Object(Box::new(TransactionObject::from_transaction(
1534 tx.transaction.clone(),
1535 None,
1536 None,
1537 &self.network,
1538 None,
1539 )))
1540 } else {
1541 let hex = tx.transaction.clone().into();
1542 GetRawTransaction::Raw(hex)
1543 });
1544 }
1545 }
1546
1547 _ => unreachable!("unmatched response to a `TransactionsByMinedId` request"),
1548 };
1549
1550 match state
1552 .ready()
1553 .and_then(|service| service.call(zebra_state::ReadRequest::Transaction(txid)))
1554 .await
1555 .map_misc_error()?
1556 {
1557 zebra_state::ReadResponse::Transaction(Some(tx)) => Ok(if verbose {
1558 GetRawTransaction::Object(Box::new(TransactionObject::from_transaction(
1559 tx.tx.clone(),
1560 Some(tx.height),
1561 Some(tx.confirmations),
1562 &self.network,
1563 Some(tx.block_time),
1566 )))
1567 } else {
1568 let hex = tx.tx.into();
1569 GetRawTransaction::Raw(hex)
1570 }),
1571
1572 zebra_state::ReadResponse::Transaction(None) => {
1573 Err("No such mempool or main chain transaction")
1574 .map_error(server::error::LegacyCode::InvalidAddressOrKey)
1575 }
1576
1577 _ => unreachable!("unmatched response to a `Transaction` read request"),
1578 }
1579 }
1580
1581 async fn z_get_treestate(&self, hash_or_height: String) -> Result<GetTreestate> {
1585 let mut state = self.state.clone();
1586 let network = self.network.clone();
1587
1588 let hash_or_height =
1589 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1590 .map_error(server::error::LegacyCode::InvalidParameter)?;
1593
1594 let block = match state
1603 .ready()
1604 .and_then(|service| service.call(zebra_state::ReadRequest::Block(hash_or_height)))
1605 .await
1606 .map_misc_error()?
1607 {
1608 zebra_state::ReadResponse::Block(Some(block)) => block,
1609 zebra_state::ReadResponse::Block(None) => {
1610 return Err("the requested block is not in the main chain")
1613 .map_error(server::error::LegacyCode::InvalidParameter);
1614 }
1615 _ => unreachable!("unmatched response to a block request"),
1616 };
1617
1618 let hash = hash_or_height
1619 .hash_or_else(|_| Some(block.hash()))
1620 .expect("block hash");
1621
1622 let height = hash_or_height
1623 .height_or_else(|_| block.coinbase_height())
1624 .expect("verified blocks have a coinbase height");
1625
1626 let time = u32::try_from(block.header.time.timestamp())
1627 .expect("Timestamps of valid blocks always fit into u32.");
1628
1629 let sapling_nu = zcash_primitives::consensus::NetworkUpgrade::Sapling;
1630 let sapling = if network.is_nu_active(sapling_nu, height.into()) {
1631 match state
1632 .ready()
1633 .and_then(|service| {
1634 service.call(zebra_state::ReadRequest::SaplingTree(hash.into()))
1635 })
1636 .await
1637 .map_misc_error()?
1638 {
1639 zebra_state::ReadResponse::SaplingTree(tree) => tree.map(|t| t.to_rpc_bytes()),
1640 _ => unreachable!("unmatched response to a Sapling tree request"),
1641 }
1642 } else {
1643 None
1644 };
1645
1646 let orchard_nu = zcash_primitives::consensus::NetworkUpgrade::Nu5;
1647 let orchard = if network.is_nu_active(orchard_nu, height.into()) {
1648 match state
1649 .ready()
1650 .and_then(|service| {
1651 service.call(zebra_state::ReadRequest::OrchardTree(hash.into()))
1652 })
1653 .await
1654 .map_misc_error()?
1655 {
1656 zebra_state::ReadResponse::OrchardTree(tree) => tree.map(|t| t.to_rpc_bytes()),
1657 _ => unreachable!("unmatched response to an Orchard tree request"),
1658 }
1659 } else {
1660 None
1661 };
1662
1663 Ok(GetTreestate::from_parts(
1664 hash, height, time, sapling, orchard,
1665 ))
1666 }
1667
1668 async fn z_get_subtrees_by_index(
1669 &self,
1670 pool: String,
1671 start_index: NoteCommitmentSubtreeIndex,
1672 limit: Option<NoteCommitmentSubtreeIndex>,
1673 ) -> Result<GetSubtrees> {
1674 let mut state = self.state.clone();
1675
1676 const POOL_LIST: &[&str] = &["sapling", "orchard"];
1677
1678 if pool == "sapling" {
1679 let request = zebra_state::ReadRequest::SaplingSubtrees { start_index, limit };
1680 let response = state
1681 .ready()
1682 .and_then(|service| service.call(request))
1683 .await
1684 .map_misc_error()?;
1685
1686 let subtrees = match response {
1687 zebra_state::ReadResponse::SaplingSubtrees(subtrees) => subtrees,
1688 _ => unreachable!("unmatched response to a subtrees request"),
1689 };
1690
1691 let subtrees = subtrees
1692 .values()
1693 .map(|subtree| SubtreeRpcData {
1694 root: subtree.root.encode_hex(),
1695 end_height: subtree.end_height,
1696 })
1697 .collect();
1698
1699 Ok(GetSubtrees {
1700 pool,
1701 start_index,
1702 subtrees,
1703 })
1704 } else if pool == "orchard" {
1705 let request = zebra_state::ReadRequest::OrchardSubtrees { start_index, limit };
1706 let response = state
1707 .ready()
1708 .and_then(|service| service.call(request))
1709 .await
1710 .map_misc_error()?;
1711
1712 let subtrees = match response {
1713 zebra_state::ReadResponse::OrchardSubtrees(subtrees) => subtrees,
1714 _ => unreachable!("unmatched response to a subtrees request"),
1715 };
1716
1717 let subtrees = subtrees
1718 .values()
1719 .map(|subtree| SubtreeRpcData {
1720 root: subtree.root.encode_hex(),
1721 end_height: subtree.end_height,
1722 })
1723 .collect();
1724
1725 Ok(GetSubtrees {
1726 pool,
1727 start_index,
1728 subtrees,
1729 })
1730 } else {
1731 Err(ErrorObject::owned(
1732 server::error::LegacyCode::Misc.into(),
1733 format!("invalid pool name, must be one of: {:?}", POOL_LIST).as_str(),
1734 None::<()>,
1735 ))
1736 }
1737 }
1738
1739 async fn get_address_tx_ids(&self, request: GetAddressTxIdsRequest) -> Result<Vec<String>> {
1740 let mut state = self.state.clone();
1741 let latest_chain_tip = self.latest_chain_tip.clone();
1742
1743 let height_range = build_height_range(
1744 request.start,
1745 request.end,
1746 best_chain_tip_height(&latest_chain_tip)?,
1747 )?;
1748
1749 let valid_addresses = AddressStrings {
1750 addresses: request.addresses,
1751 }
1752 .valid_addresses()?;
1753
1754 let request = zebra_state::ReadRequest::TransactionIdsByAddresses {
1755 addresses: valid_addresses,
1756 height_range,
1757 };
1758 let response = state
1759 .ready()
1760 .and_then(|service| service.call(request))
1761 .await
1762 .map_misc_error()?;
1763
1764 let hashes = match response {
1765 zebra_state::ReadResponse::AddressesTransactionIds(hashes) => {
1766 let mut last_tx_location = TransactionLocation::from_usize(Height(0), 0);
1767
1768 hashes
1769 .iter()
1770 .map(|(tx_loc, tx_id)| {
1771 assert!(
1773 *tx_loc > last_tx_location,
1774 "Transactions were not in chain order:\n\
1775 {tx_loc:?} {tx_id:?} was after:\n\
1776 {last_tx_location:?}",
1777 );
1778
1779 last_tx_location = *tx_loc;
1780
1781 tx_id.to_string()
1782 })
1783 .collect()
1784 }
1785 _ => unreachable!("unmatched response to a TransactionsByAddresses request"),
1786 };
1787
1788 Ok(hashes)
1789 }
1790
1791 async fn get_address_utxos(
1792 &self,
1793 address_strings: AddressStrings,
1794 ) -> Result<Vec<GetAddressUtxos>> {
1795 let mut state = self.state.clone();
1796 let mut response_utxos = vec![];
1797
1798 let valid_addresses = address_strings.valid_addresses()?;
1799
1800 let request = zebra_state::ReadRequest::UtxosByAddresses(valid_addresses);
1802 let response = state
1803 .ready()
1804 .and_then(|service| service.call(request))
1805 .await
1806 .map_misc_error()?;
1807 let utxos = match response {
1808 zebra_state::ReadResponse::AddressUtxos(utxos) => utxos,
1809 _ => unreachable!("unmatched response to a UtxosByAddresses request"),
1810 };
1811
1812 let mut last_output_location = OutputLocation::from_usize(Height(0), 0, 0);
1813
1814 for utxo_data in utxos.utxos() {
1815 let address = utxo_data.0;
1816 let txid = *utxo_data.1;
1817 let height = utxo_data.2.height();
1818 let output_index = utxo_data.2.output_index();
1819 let script = utxo_data.3.lock_script.clone();
1820 let satoshis = u64::from(utxo_data.3.value);
1821
1822 let output_location = *utxo_data.2;
1823 assert!(
1825 output_location > last_output_location,
1826 "UTXOs were not in chain order:\n\
1827 {output_location:?} {address:?} {txid:?} was after:\n\
1828 {last_output_location:?}",
1829 );
1830
1831 let entry = GetAddressUtxos {
1832 address,
1833 txid,
1834 output_index,
1835 script,
1836 satoshis,
1837 height,
1838 };
1839 response_utxos.push(entry);
1840
1841 last_output_location = output_location;
1842 }
1843
1844 Ok(response_utxos)
1845 }
1846
1847 fn stop(&self) -> Result<String> {
1848 #[cfg(not(target_os = "windows"))]
1849 if self.network.is_regtest() {
1850 match nix::sys::signal::raise(nix::sys::signal::SIGINT) {
1851 Ok(_) => Ok("Zebra server stopping".to_string()),
1852 Err(error) => Err(ErrorObject::owned(
1853 ErrorCode::InternalError.code(),
1854 format!("Failed to shut down: {}", error).as_str(),
1855 None::<()>,
1856 )),
1857 }
1858 } else {
1859 Err(ErrorObject::borrowed(
1860 ErrorCode::MethodNotFound.code(),
1861 "stop is only available on regtest networks",
1862 None,
1863 ))
1864 }
1865 #[cfg(target_os = "windows")]
1866 Err(ErrorObject::borrowed(
1867 ErrorCode::MethodNotFound.code(),
1868 "stop is not available in windows targets",
1869 None,
1870 ))
1871 }
1872
1873 fn get_block_count(&self) -> Result<u32> {
1874 best_chain_tip_height(&self.latest_chain_tip).map(|height| height.0)
1875 }
1876
1877 async fn get_block_hash(&self, index: i32) -> Result<GetBlockHash> {
1878 let mut state = self.state.clone();
1879 let latest_chain_tip = self.latest_chain_tip.clone();
1880
1881 let tip_height = best_chain_tip_height(&latest_chain_tip)?;
1883
1884 let height = height_from_signed_int(index, tip_height)?;
1885
1886 let request = zebra_state::ReadRequest::BestChainBlockHash(height);
1887 let response = state
1888 .ready()
1889 .and_then(|service| service.call(request))
1890 .await
1891 .map_error(server::error::LegacyCode::default())?;
1892
1893 match response {
1894 zebra_state::ReadResponse::BlockHash(Some(hash)) => Ok(GetBlockHash(hash)),
1895 zebra_state::ReadResponse::BlockHash(None) => Err(ErrorObject::borrowed(
1896 server::error::LegacyCode::InvalidParameter.into(),
1897 "Block not found",
1898 None,
1899 )),
1900 _ => unreachable!("unmatched response to a block request"),
1901 }
1902 }
1903
1904 async fn get_block_template(
1905 &self,
1906 parameters: Option<get_block_template::JsonParameters>,
1907 ) -> Result<get_block_template::Response> {
1908 let network = self.network.clone();
1910 let miner_address = self.gbt.miner_address();
1911 let debug_like_zcashd = self.debug_like_zcashd;
1912 let extra_coinbase_data = self.gbt.extra_coinbase_data();
1913
1914 let mempool = self.mempool.clone();
1916 let mut latest_chain_tip = self.latest_chain_tip.clone();
1917 let sync_status = self.gbt.sync_status();
1918 let state = self.state.clone();
1919
1920 if let Some(HexData(block_proposal_bytes)) = parameters
1921 .as_ref()
1922 .and_then(get_block_template::JsonParameters::block_proposal_data)
1923 {
1924 return get_block_template::validate_block_proposal(
1925 self.gbt.block_verifier_router(),
1926 block_proposal_bytes,
1927 network,
1928 latest_chain_tip,
1929 sync_status,
1930 )
1931 .await;
1932 }
1933
1934 get_block_template::check_parameters(¶meters)?;
1936
1937 let client_long_poll_id = parameters.as_ref().and_then(|params| params.long_poll_id);
1938
1939 let miner_address = get_block_template::check_miner_address(miner_address)?;
1944
1945 let mut max_time_reached = false;
1949
1950 let (
1953 server_long_poll_id,
1954 chain_tip_and_local_time,
1955 mempool_txs,
1956 mempool_tx_deps,
1957 submit_old,
1958 ) = loop {
1959 get_block_template::check_synced_to_tip(
1965 &network,
1966 latest_chain_tip.clone(),
1967 sync_status.clone(),
1968 )?;
1969 latest_chain_tip.mark_best_tip_seen();
1977
1978 let chain_tip_and_local_time @ zebra_state::GetBlockTemplateChainInfo {
1985 tip_hash,
1986 tip_height,
1987 max_time,
1988 cur_time,
1989 ..
1990 } = get_block_template::fetch_state_tip_and_local_time(state.clone()).await?;
1991
1992 let Some((mempool_txs, mempool_tx_deps)) =
2003 get_block_template::fetch_mempool_transactions(mempool.clone(), tip_hash)
2004 .await?
2005 .or_else(|| client_long_poll_id.is_none().then(Default::default))
2009 else {
2010 continue;
2011 };
2012
2013 let server_long_poll_id = LongPollInput::new(
2015 tip_height,
2016 tip_hash,
2017 max_time,
2018 mempool_txs.iter().map(|tx| tx.transaction.id),
2019 )
2020 .generate_id();
2021
2022 if Some(&server_long_poll_id) != client_long_poll_id.as_ref() || max_time_reached {
2027 let mut submit_old = client_long_poll_id
2028 .as_ref()
2029 .map(|old_long_poll_id| server_long_poll_id.submit_old(old_long_poll_id));
2030
2031 if max_time_reached {
2036 submit_old = Some(false);
2037 }
2038
2039 break (
2040 server_long_poll_id,
2041 chain_tip_and_local_time,
2042 mempool_txs,
2043 mempool_tx_deps,
2044 submit_old,
2045 );
2046 }
2047
2048 let wait_for_mempool_request =
2058 tokio::time::sleep(Duration::from_secs(MEMPOOL_LONG_POLL_INTERVAL));
2059
2060 let mut wait_for_best_tip_change = latest_chain_tip.clone();
2063 let wait_for_best_tip_change = wait_for_best_tip_change.best_tip_changed();
2064
2065 let duration_until_max_time = max_time.saturating_duration_since(cur_time);
2077 let wait_for_max_time: OptionFuture<_> = if duration_until_max_time.seconds() > 0 {
2078 Some(tokio::time::sleep(duration_until_max_time.to_std()))
2079 } else {
2080 None
2081 }
2082 .into();
2083
2084 tokio::select! {
2091 biased;
2094
2095 _elapsed = wait_for_mempool_request => {
2097 tracing::debug!(
2098 ?max_time,
2099 ?cur_time,
2100 ?server_long_poll_id,
2101 ?client_long_poll_id,
2102 MEMPOOL_LONG_POLL_INTERVAL,
2103 "checking for a new mempool change after waiting a few seconds"
2104 );
2105 }
2106
2107 tip_changed_result = wait_for_best_tip_change => {
2109 match tip_changed_result {
2110 Ok(()) => {
2111 latest_chain_tip.mark_best_tip_seen();
2115
2116 let new_tip_hash = latest_chain_tip.best_tip_hash();
2117 if new_tip_hash == Some(tip_hash) {
2118 tracing::debug!(
2119 ?max_time,
2120 ?cur_time,
2121 ?server_long_poll_id,
2122 ?client_long_poll_id,
2123 ?tip_hash,
2124 ?tip_height,
2125 "ignoring spurious state change notification"
2126 );
2127
2128 tokio::time::sleep(Duration::from_secs(
2130 MEMPOOL_LONG_POLL_INTERVAL,
2131 )).await;
2132
2133 continue;
2134 }
2135
2136 tracing::debug!(
2137 ?max_time,
2138 ?cur_time,
2139 ?server_long_poll_id,
2140 ?client_long_poll_id,
2141 "returning from long poll because state has changed"
2142 );
2143 }
2144
2145 Err(recv_error) => {
2146 tracing::info!(
2148 ?recv_error,
2149 ?max_time,
2150 ?cur_time,
2151 ?server_long_poll_id,
2152 ?client_long_poll_id,
2153 "returning from long poll due to a state error.\
2154 Is Zebra shutting down?"
2155 );
2156
2157 return Err(recv_error).map_error(server::error::LegacyCode::default());
2158 }
2159 }
2160 }
2161
2162 Some(_elapsed) = wait_for_max_time => {
2165 tracing::info!(
2167 ?max_time,
2168 ?cur_time,
2169 ?server_long_poll_id,
2170 ?client_long_poll_id,
2171 "returning from long poll because max time was reached"
2172 );
2173
2174 max_time_reached = true;
2175 }
2176 }
2177 };
2178
2179 let next_block_height =
2187 (chain_tip_and_local_time.tip_height + 1).expect("tip is far below Height::MAX");
2188
2189 tracing::debug!(
2190 mempool_tx_hashes = ?mempool_txs
2191 .iter()
2192 .map(|tx| tx.transaction.id.mined_id())
2193 .collect::<Vec<_>>(),
2194 "selecting transactions for the template from the mempool"
2195 );
2196
2197 let mempool_txs = get_block_template::zip317::select_mempool_transactions(
2199 &network,
2200 next_block_height,
2201 &miner_address,
2202 mempool_txs,
2203 mempool_tx_deps,
2204 debug_like_zcashd,
2205 extra_coinbase_data.clone(),
2206 );
2207
2208 tracing::debug!(
2209 selected_mempool_tx_hashes = ?mempool_txs
2210 .iter()
2211 .map(|#[cfg(not(test))] tx, #[cfg(test)] (_, tx)| tx.transaction.id.mined_id())
2212 .collect::<Vec<_>>(),
2213 "selected transactions for the template from the mempool"
2214 );
2215
2216 let response = GetBlockTemplate::new(
2219 &network,
2220 &miner_address,
2221 &chain_tip_and_local_time,
2222 server_long_poll_id,
2223 mempool_txs,
2224 submit_old,
2225 debug_like_zcashd,
2226 extra_coinbase_data,
2227 );
2228
2229 Ok(response.into())
2230 }
2231
2232 async fn submit_block(
2233 &self,
2234 HexData(block_bytes): HexData,
2235 _parameters: Option<submit_block::JsonParameters>,
2236 ) -> Result<submit_block::Response> {
2237 let mut block_verifier_router = self.gbt.block_verifier_router();
2238
2239 let block: Block = match block_bytes.zcash_deserialize_into() {
2240 Ok(block_bytes) => block_bytes,
2241 Err(error) => {
2242 tracing::info!(
2243 ?error,
2244 "submit block failed: block bytes could not be deserialized into a structurally valid block"
2245 );
2246
2247 return Ok(submit_block::ErrorResponse::Rejected.into());
2248 }
2249 };
2250
2251 let height = block
2252 .coinbase_height()
2253 .ok_or_error(0, "coinbase height not found")?;
2254 let block_hash = block.hash();
2255
2256 let block_verifier_router_response = block_verifier_router
2257 .ready()
2258 .await
2259 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?
2260 .call(zebra_consensus::Request::Commit(Arc::new(block)))
2261 .await;
2262
2263 let chain_error = match block_verifier_router_response {
2264 Ok(hash) => {
2271 tracing::info!(?hash, ?height, "submit block accepted");
2272
2273 self.gbt
2274 .advertise_mined_block(hash, height)
2275 .map_error_with_prefix(0, "failed to send mined block")?;
2276
2277 return Ok(submit_block::Response::Accepted);
2278 }
2279
2280 Err(box_error) => {
2283 let error = box_error
2284 .downcast::<RouterError>()
2285 .map(|boxed_chain_error| *boxed_chain_error);
2286
2287 tracing::info!(
2288 ?error,
2289 ?block_hash,
2290 ?height,
2291 "submit block failed verification"
2292 );
2293
2294 error
2295 }
2296 };
2297
2298 let response = match chain_error {
2299 Ok(source) if source.is_duplicate_request() => submit_block::ErrorResponse::Duplicate,
2300
2301 Ok(_verify_chain_error) => submit_block::ErrorResponse::Rejected,
2317
2318 Err(_unknown_error_type) => submit_block::ErrorResponse::Rejected,
2321 };
2322
2323 Ok(response.into())
2324 }
2325
2326 async fn get_mining_info(&self) -> Result<get_mining_info::Response> {
2327 let network = self.network.clone();
2328 let mut state = self.state.clone();
2329
2330 let chain_tip = self.latest_chain_tip.clone();
2331 let tip_height = chain_tip.best_tip_height().unwrap_or(Height(0)).0;
2332
2333 let mut current_block_tx = None;
2334 if tip_height > 0 {
2335 let mined_tx_ids = chain_tip.best_tip_mined_transaction_ids();
2336 current_block_tx =
2337 (!mined_tx_ids.is_empty()).then(|| mined_tx_ids.len().saturating_sub(1));
2338 }
2339
2340 let solution_rate_fut = self.get_network_sol_ps(None, None);
2341 let mut current_block_size = None;
2343 if tip_height > 0 {
2344 let request = zebra_state::ReadRequest::TipBlockSize;
2345 let response: zebra_state::ReadResponse = state
2346 .ready()
2347 .and_then(|service| service.call(request))
2348 .await
2349 .map_error(server::error::LegacyCode::default())?;
2350 current_block_size = match response {
2351 zebra_state::ReadResponse::TipBlockSize(Some(block_size)) => Some(block_size),
2352 _ => None,
2353 };
2354 }
2355
2356 Ok(get_mining_info::Response::new(
2357 tip_height,
2358 current_block_size,
2359 current_block_tx,
2360 network,
2361 solution_rate_fut.await?,
2362 ))
2363 }
2364
2365 async fn get_network_sol_ps(
2366 &self,
2367 num_blocks: Option<i32>,
2368 height: Option<i32>,
2369 ) -> Result<u64> {
2370 let mut num_blocks =
2372 num_blocks.unwrap_or(get_block_template::DEFAULT_SOLUTION_RATE_WINDOW_SIZE);
2373 if num_blocks < 1 {
2375 num_blocks = i32::try_from(POW_AVERAGING_WINDOW).expect("fits in i32");
2376 }
2377 let num_blocks =
2378 usize::try_from(num_blocks).expect("just checked for negatives, i32 fits in usize");
2379
2380 let height = height.and_then(|height| height.try_into_height().ok());
2383
2384 let mut state = self.state.clone();
2385
2386 let request = ReadRequest::SolutionRate { num_blocks, height };
2387
2388 let response = state
2389 .ready()
2390 .and_then(|service| service.call(request))
2391 .await
2392 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
2393
2394 let solution_rate = match response {
2395 ReadResponse::SolutionRate(solution_rate) => solution_rate.unwrap_or(0),
2397
2398 _ => unreachable!("unmatched response to a solution rate request"),
2399 };
2400
2401 Ok(solution_rate
2402 .try_into()
2403 .expect("per-second solution rate always fits in u64"))
2404 }
2405
2406 async fn get_peer_info(&self) -> Result<Vec<PeerInfo>> {
2407 let address_book = self.address_book.clone();
2408 Ok(address_book
2409 .recently_live_peers(chrono::Utc::now())
2410 .into_iter()
2411 .map(PeerInfo::from)
2412 .collect())
2413 }
2414
2415 async fn validate_address(&self, raw_address: String) -> Result<validate_address::Response> {
2416 let network = self.network.clone();
2417
2418 let Ok(address) = raw_address.parse::<zcash_address::ZcashAddress>() else {
2419 return Ok(validate_address::Response::invalid());
2420 };
2421
2422 let address = match address.convert::<primitives::Address>() {
2423 Ok(address) => address,
2424 Err(err) => {
2425 tracing::debug!(?err, "conversion error");
2426 return Ok(validate_address::Response::invalid());
2427 }
2428 };
2429
2430 if !address.is_transparent() {
2432 return Ok(validate_address::Response::invalid());
2433 }
2434
2435 if address.network() == network.kind() {
2436 Ok(validate_address::Response {
2437 address: Some(raw_address),
2438 is_valid: true,
2439 is_script: Some(address.is_script_hash()),
2440 })
2441 } else {
2442 tracing::info!(
2443 ?network,
2444 address_network = ?address.network(),
2445 "invalid address in validateaddress RPC: Zebra's configured network must match address network"
2446 );
2447
2448 Ok(validate_address::Response::invalid())
2449 }
2450 }
2451
2452 async fn z_validate_address(
2453 &self,
2454 raw_address: String,
2455 ) -> Result<z_validate_address::Response> {
2456 let network = self.network.clone();
2457
2458 let Ok(address) = raw_address.parse::<zcash_address::ZcashAddress>() else {
2459 return Ok(z_validate_address::Response::invalid());
2460 };
2461
2462 let address = match address.convert::<primitives::Address>() {
2463 Ok(address) => address,
2464 Err(err) => {
2465 tracing::debug!(?err, "conversion error");
2466 return Ok(z_validate_address::Response::invalid());
2467 }
2468 };
2469
2470 if address.network() == network.kind() {
2471 Ok(z_validate_address::Response {
2472 is_valid: true,
2473 address: Some(raw_address),
2474 address_type: Some(z_validate_address::AddressType::from(&address)),
2475 is_mine: Some(false),
2476 })
2477 } else {
2478 tracing::info!(
2479 ?network,
2480 address_network = ?address.network(),
2481 "invalid address network in z_validateaddress RPC: address is for {:?} but Zebra is on {:?}",
2482 address.network(),
2483 network
2484 );
2485
2486 Ok(z_validate_address::Response::invalid())
2487 }
2488 }
2489
2490 async fn get_block_subsidy(&self, height: Option<u32>) -> Result<BlockSubsidy> {
2491 let latest_chain_tip = self.latest_chain_tip.clone();
2492 let network = self.network.clone();
2493
2494 let height = if let Some(height) = height {
2495 Height(height)
2496 } else {
2497 best_chain_tip_height(&latest_chain_tip)?
2498 };
2499
2500 if height < network.height_for_first_halving() {
2501 return Err(ErrorObject::borrowed(
2502 0,
2503 "Zebra does not support founders' reward subsidies, \
2504 use a block height that is after the first halving",
2505 None,
2506 ));
2507 }
2508
2509 let founders = Amount::zero();
2511
2512 let total_block_subsidy =
2513 block_subsidy(height, &network).map_error(server::error::LegacyCode::default())?;
2514 let miner_subsidy = miner_subsidy(height, &network, total_block_subsidy)
2515 .map_error(server::error::LegacyCode::default())?;
2516
2517 let (lockbox_streams, mut funding_streams): (Vec<_>, Vec<_>) =
2518 funding_stream_values(height, &network, total_block_subsidy)
2519 .map_error(server::error::LegacyCode::default())?
2520 .into_iter()
2521 .partition(|(receiver, _)| matches!(receiver, FundingStreamReceiver::Deferred));
2523
2524 let is_nu6 = NetworkUpgrade::current(&network, height) == NetworkUpgrade::Nu6;
2525
2526 let [lockbox_total, funding_streams_total]: [std::result::Result<
2527 Amount<NonNegative>,
2528 amount::Error,
2529 >; 2] = [&lockbox_streams, &funding_streams]
2530 .map(|streams| streams.iter().map(|&(_, amount)| amount).sum());
2531
2532 funding_streams.sort_by_key(|(receiver, _funding_stream)| {
2534 ZCASHD_FUNDING_STREAM_ORDER
2535 .iter()
2536 .position(|zcashd_receiver| zcashd_receiver == receiver)
2537 });
2538
2539 let [funding_streams, lockbox_streams]: [Vec<_>; 2] = [funding_streams, lockbox_streams]
2541 .map(|streams| {
2542 streams
2543 .into_iter()
2544 .map(|(receiver, value)| {
2545 let address = funding_stream_address(height, &network, receiver);
2546 types::subsidy::FundingStream::new(is_nu6, receiver, value, address)
2547 })
2548 .collect()
2549 });
2550
2551 Ok(BlockSubsidy {
2552 miner: miner_subsidy.into(),
2553 founders: founders.into(),
2554 funding_streams,
2555 lockbox_streams,
2556 funding_streams_total: funding_streams_total
2557 .map_error(server::error::LegacyCode::default())?
2558 .into(),
2559 lockbox_total: lockbox_total
2560 .map_error(server::error::LegacyCode::default())?
2561 .into(),
2562 total_block_subsidy: total_block_subsidy.into(),
2563 })
2564 }
2565
2566 async fn get_difficulty(&self) -> Result<f64> {
2567 chain_tip_difficulty(self.network.clone(), self.state.clone(), false).await
2568 }
2569
2570 async fn z_list_unified_receivers(&self, address: String) -> Result<unified_address::Response> {
2571 use zcash_address::unified::Container;
2572
2573 let (network, unified_address): (
2574 zcash_protocol::consensus::NetworkType,
2575 zcash_address::unified::Address,
2576 ) = zcash_address::unified::Encoding::decode(address.clone().as_str())
2577 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
2578
2579 let mut p2pkh = String::new();
2580 let mut p2sh = String::new();
2581 let mut orchard = String::new();
2582 let mut sapling = String::new();
2583
2584 for item in unified_address.items() {
2585 match item {
2586 zcash_address::unified::Receiver::Orchard(_data) => {
2587 let addr = zcash_address::unified::Address::try_from_items(vec![item])
2588 .expect("using data already decoded as valid");
2589 orchard = addr.encode(&network);
2590 }
2591 zcash_address::unified::Receiver::Sapling(data) => {
2592 let addr = zebra_chain::primitives::Address::try_from_sapling(network, data)
2593 .expect("using data already decoded as valid");
2594 sapling = addr.payment_address().unwrap_or_default();
2595 }
2596 zcash_address::unified::Receiver::P2pkh(data) => {
2597 let addr =
2598 zebra_chain::primitives::Address::try_from_transparent_p2pkh(network, data)
2599 .expect("using data already decoded as valid");
2600 p2pkh = addr.payment_address().unwrap_or_default();
2601 }
2602 zcash_address::unified::Receiver::P2sh(data) => {
2603 let addr =
2604 zebra_chain::primitives::Address::try_from_transparent_p2sh(network, data)
2605 .expect("using data already decoded as valid");
2606 p2sh = addr.payment_address().unwrap_or_default();
2607 }
2608 _ => (),
2609 }
2610 }
2611
2612 Ok(unified_address::Response::new(
2613 orchard, sapling, p2pkh, p2sh,
2614 ))
2615 }
2616
2617 async fn generate(&self, num_blocks: u32) -> Result<Vec<GetBlockHash>> {
2618 let rpc = self.clone();
2619 let network = self.network.clone();
2620
2621 if !network.disable_pow() {
2622 return Err(ErrorObject::borrowed(
2623 0,
2624 "generate is only supported on networks where PoW is disabled",
2625 None,
2626 ));
2627 }
2628
2629 let mut block_hashes = Vec::new();
2630 for _ in 0..num_blocks {
2631 let block_template = rpc
2632 .get_block_template(None)
2633 .await
2634 .map_error(server::error::LegacyCode::default())?;
2635
2636 let get_block_template::Response::TemplateMode(block_template) = block_template else {
2637 return Err(ErrorObject::borrowed(
2638 0,
2639 "error generating block template",
2640 None,
2641 ));
2642 };
2643
2644 let proposal_block = proposal_block_from_template(
2645 &block_template,
2646 get_block_template::TimeSource::CurTime,
2647 NetworkUpgrade::current(&network, Height(block_template.height)),
2648 )
2649 .map_error(server::error::LegacyCode::default())?;
2650 let hex_proposal_block = HexData(
2651 proposal_block
2652 .zcash_serialize_to_vec()
2653 .map_error(server::error::LegacyCode::default())?,
2654 );
2655
2656 let _submit = rpc
2657 .submit_block(hex_proposal_block, None)
2658 .await
2659 .map_error(server::error::LegacyCode::default())?;
2660
2661 block_hashes.push(GetBlockHash(proposal_block.hash()));
2662 }
2663
2664 Ok(block_hashes)
2665 }
2666}
2667
2668pub fn best_chain_tip_height<Tip>(latest_chain_tip: &Tip) -> Result<Height>
2673where
2674 Tip: ChainTip + Clone + Send + Sync + 'static,
2675{
2676 latest_chain_tip
2677 .best_tip_height()
2678 .ok_or_misc_error("No blocks in state")
2679}
2680
2681#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
2685pub struct GetInfo {
2686 version: u64,
2688
2689 build: String,
2691
2692 subversion: String,
2694
2695 #[serde(rename = "protocolversion")]
2697 protocol_version: u32,
2698
2699 blocks: u32,
2701
2702 connections: usize,
2704
2705 #[serde(skip_serializing_if = "Option::is_none")]
2707 proxy: Option<String>,
2708
2709 difficulty: f64,
2711
2712 testnet: bool,
2714
2715 #[serde(rename = "paytxfee")]
2717 pay_tx_fee: f64,
2718
2719 #[serde(rename = "relayfee")]
2721 relay_fee: f64,
2722
2723 errors: String,
2725
2726 #[serde(rename = "errorstimestamp")]
2728 errors_timestamp: String,
2729}
2730
2731impl Default for GetInfo {
2732 fn default() -> Self {
2733 GetInfo {
2734 version: 0,
2735 build: "some build version".to_string(),
2736 subversion: "some subversion".to_string(),
2737 protocol_version: 0,
2738 blocks: 0,
2739 connections: 0,
2740 proxy: None,
2741 difficulty: 0.0,
2742 testnet: false,
2743 pay_tx_fee: 0.0,
2744 relay_fee: 0.0,
2745 errors: "no errors".to_string(),
2746 errors_timestamp: "no errors timestamp".to_string(),
2747 }
2748 }
2749}
2750
2751impl GetInfo {
2752 #[allow(clippy::too_many_arguments)]
2754 pub fn from_parts(
2755 version: u64,
2756 build: String,
2757 subversion: String,
2758 protocol_version: u32,
2759 blocks: u32,
2760 connections: usize,
2761 proxy: Option<String>,
2762 difficulty: f64,
2763 testnet: bool,
2764 pay_tx_fee: f64,
2765 relay_fee: f64,
2766 errors: String,
2767 errors_timestamp: String,
2768 ) -> Self {
2769 Self {
2770 version,
2771 build,
2772 subversion,
2773 protocol_version,
2774 blocks,
2775 connections,
2776 proxy,
2777 difficulty,
2778 testnet,
2779 pay_tx_fee,
2780 relay_fee,
2781 errors,
2782 errors_timestamp,
2783 }
2784 }
2785
2786 pub fn into_parts(
2788 self,
2789 ) -> (
2790 u64,
2791 String,
2792 String,
2793 u32,
2794 u32,
2795 usize,
2796 Option<String>,
2797 f64,
2798 bool,
2799 f64,
2800 f64,
2801 String,
2802 String,
2803 ) {
2804 (
2805 self.version,
2806 self.build,
2807 self.subversion,
2808 self.protocol_version,
2809 self.blocks,
2810 self.connections,
2811 self.proxy,
2812 self.difficulty,
2813 self.testnet,
2814 self.pay_tx_fee,
2815 self.relay_fee,
2816 self.errors,
2817 self.errors_timestamp,
2818 )
2819 }
2820
2821 pub fn version(build_string: &str) -> Option<u64> {
2823 let semver_version = semver::Version::parse(build_string.strip_prefix('v')?).ok()?;
2824 let build_number = semver_version
2825 .build
2826 .as_str()
2827 .split('.')
2828 .next()
2829 .and_then(|num_str| num_str.parse::<u64>().ok())
2830 .unwrap_or_default();
2831
2832 let version_number = 1_000_000 * semver_version.major
2834 + 10_000 * semver_version.minor
2835 + 100 * semver_version.patch
2836 + build_number;
2837
2838 Some(version_number)
2839 }
2840}
2841
2842#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
2846pub struct GetBlockChainInfo {
2847 chain: String,
2849
2850 blocks: Height,
2852
2853 headers: Height,
2856
2857 difficulty: f64,
2859
2860 #[serde(rename = "verificationprogress")]
2862 verification_progress: f64,
2863
2864 #[serde(rename = "chainwork")]
2866 chain_work: u64,
2867
2868 pruned: bool,
2870
2871 size_on_disk: u64,
2873
2874 commitments: u64,
2876
2877 #[serde(rename = "bestblockhash", with = "hex")]
2879 best_block_hash: block::Hash,
2880
2881 #[serde(rename = "estimatedheight")]
2885 estimated_height: Height,
2886
2887 #[serde(rename = "chainSupply")]
2889 chain_supply: get_blockchain_info::Balance,
2890
2891 #[serde(rename = "valuePools")]
2893 value_pools: [get_blockchain_info::Balance; 5],
2894
2895 upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
2897
2898 consensus: TipConsensusBranch,
2900}
2901
2902impl Default for GetBlockChainInfo {
2903 fn default() -> Self {
2904 GetBlockChainInfo {
2905 chain: "main".to_string(),
2906 blocks: Height(1),
2907 best_block_hash: block::Hash([0; 32]),
2908 estimated_height: Height(1),
2909 chain_supply: get_blockchain_info::Balance::chain_supply(Default::default()),
2910 value_pools: get_blockchain_info::Balance::zero_pools(),
2911 upgrades: IndexMap::new(),
2912 consensus: TipConsensusBranch {
2913 chain_tip: ConsensusBranchIdHex(ConsensusBranchId::default()),
2914 next_block: ConsensusBranchIdHex(ConsensusBranchId::default()),
2915 },
2916 headers: Height(1),
2917 difficulty: 0.0,
2918 verification_progress: 0.0,
2919 chain_work: 0,
2920 pruned: false,
2921 size_on_disk: 0,
2922 commitments: 0,
2923 }
2924 }
2925}
2926
2927impl GetBlockChainInfo {
2928 #[allow(clippy::too_many_arguments)]
2930 pub fn new(
2931 chain: String,
2932 blocks: Height,
2933 best_block_hash: block::Hash,
2934 estimated_height: Height,
2935 chain_supply: get_blockchain_info::Balance,
2936 value_pools: [get_blockchain_info::Balance; 5],
2937 upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
2938 consensus: TipConsensusBranch,
2939 headers: Height,
2940 difficulty: f64,
2941 verification_progress: f64,
2942 chain_work: u64,
2943 pruned: bool,
2944 size_on_disk: u64,
2945 commitments: u64,
2946 ) -> Self {
2947 Self {
2948 chain,
2949 blocks,
2950 best_block_hash,
2951 estimated_height,
2952 chain_supply,
2953 value_pools,
2954 upgrades,
2955 consensus,
2956 headers,
2957 difficulty,
2958 verification_progress,
2959 chain_work,
2960 pruned,
2961 size_on_disk,
2962 commitments,
2963 }
2964 }
2965
2966 pub fn chain(&self) -> String {
2968 self.chain.clone()
2969 }
2970
2971 pub fn blocks(&self) -> Height {
2973 self.blocks
2974 }
2975
2976 pub fn best_block_hash(&self) -> &block::Hash {
2978 &self.best_block_hash
2979 }
2980
2981 pub fn estimated_height(&self) -> Height {
2987 self.estimated_height
2988 }
2989
2990 pub fn value_pools(&self) -> &[get_blockchain_info::Balance; 5] {
2992 &self.value_pools
2993 }
2994
2995 pub fn upgrades(&self) -> &IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo> {
2997 &self.upgrades
2998 }
2999
3000 pub fn consensus(&self) -> &TipConsensusBranch {
3002 &self.consensus
3003 }
3004}
3005
3006#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
3011pub struct AddressStrings {
3012 addresses: Vec<String>,
3014}
3015
3016impl AddressStrings {
3017 #[cfg(test)]
3019 pub fn new(addresses: Vec<String>) -> AddressStrings {
3020 AddressStrings { addresses }
3021 }
3022
3023 pub fn new_valid(addresses: Vec<String>) -> Result<AddressStrings> {
3025 let address_strings = Self { addresses };
3026 address_strings.clone().valid_addresses()?;
3027 Ok(address_strings)
3028 }
3029
3030 pub fn valid_addresses(self) -> Result<HashSet<Address>> {
3034 let valid_addresses: HashSet<Address> = self
3037 .addresses
3038 .into_iter()
3039 .map(|address| {
3040 address
3041 .parse()
3042 .map_error(server::error::LegacyCode::InvalidAddressOrKey)
3043 })
3044 .collect::<Result<_>>()?;
3045
3046 Ok(valid_addresses)
3047 }
3048
3049 pub fn valid_address_strings(self) -> Result<Vec<String>> {
3053 self.clone().valid_addresses()?;
3054 Ok(self.addresses)
3055 }
3056}
3057
3058#[derive(
3060 Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize,
3061)]
3062pub struct AddressBalance {
3063 pub balance: u64,
3065}
3066
3067#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
3069pub struct ConsensusBranchIdHex(#[serde(with = "hex")] ConsensusBranchId);
3070
3071impl ConsensusBranchIdHex {
3072 pub fn new(consensus_branch_id: u32) -> Self {
3074 ConsensusBranchIdHex(consensus_branch_id.into())
3075 }
3076
3077 pub fn inner(&self) -> u32 {
3079 self.0.into()
3080 }
3081}
3082
3083#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3085pub struct NetworkUpgradeInfo {
3086 name: NetworkUpgrade,
3090
3091 #[serde(rename = "activationheight")]
3093 activation_height: Height,
3094
3095 status: NetworkUpgradeStatus,
3097}
3098
3099impl NetworkUpgradeInfo {
3100 pub fn from_parts(
3102 name: NetworkUpgrade,
3103 activation_height: Height,
3104 status: NetworkUpgradeStatus,
3105 ) -> Self {
3106 Self {
3107 name,
3108 activation_height,
3109 status,
3110 }
3111 }
3112
3113 pub fn into_parts(self) -> (NetworkUpgrade, Height, NetworkUpgradeStatus) {
3115 (self.name, self.activation_height, self.status)
3116 }
3117}
3118
3119#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3121pub enum NetworkUpgradeStatus {
3122 #[serde(rename = "active")]
3127 Active,
3128
3129 #[serde(rename = "disabled")]
3131 Disabled,
3132
3133 #[serde(rename = "pending")]
3135 Pending,
3136}
3137
3138#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3142pub struct TipConsensusBranch {
3143 #[serde(rename = "chaintip")]
3145 chain_tip: ConsensusBranchIdHex,
3146
3147 #[serde(rename = "nextblock")]
3149 next_block: ConsensusBranchIdHex,
3150}
3151
3152impl TipConsensusBranch {
3153 pub fn from_parts(chain_tip: u32, next_block: u32) -> Self {
3155 Self {
3156 chain_tip: ConsensusBranchIdHex::new(chain_tip),
3157 next_block: ConsensusBranchIdHex::new(next_block),
3158 }
3159 }
3160
3161 pub fn into_parts(self) -> (u32, u32) {
3163 (self.chain_tip.inner(), self.next_block.inner())
3164 }
3165}
3166
3167#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3173pub struct SentTransactionHash(#[serde(with = "hex")] transaction::Hash);
3174
3175impl Default for SentTransactionHash {
3176 fn default() -> Self {
3177 Self(transaction::Hash::from([0; 32]))
3178 }
3179}
3180
3181impl SentTransactionHash {
3182 pub fn new(hash: transaction::Hash) -> Self {
3184 SentTransactionHash(hash)
3185 }
3186
3187 pub fn inner(&self) -> transaction::Hash {
3189 self.0
3190 }
3191}
3192
3193#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3197#[serde(untagged)]
3198#[allow(clippy::large_enum_variant)] pub enum GetBlock {
3200 Raw(#[serde(with = "hex")] SerializedBlock),
3202 Object {
3204 hash: GetBlockHash,
3206
3207 confirmations: i64,
3210
3211 #[serde(skip_serializing_if = "Option::is_none")]
3213 size: Option<i64>,
3214
3215 #[serde(skip_serializing_if = "Option::is_none")]
3217 height: Option<Height>,
3218
3219 #[serde(skip_serializing_if = "Option::is_none")]
3221 version: Option<u32>,
3222
3223 #[serde(with = "opthex", rename = "merkleroot")]
3225 #[serde(skip_serializing_if = "Option::is_none")]
3226 merkle_root: Option<block::merkle::Root>,
3227
3228 #[serde(with = "opthex", rename = "blockcommitments")]
3231 #[serde(skip_serializing_if = "Option::is_none")]
3232 block_commitments: Option<[u8; 32]>,
3233
3234 #[serde(with = "opthex", rename = "finalsaplingroot")]
3238 #[serde(skip_serializing_if = "Option::is_none")]
3239 final_sapling_root: Option<[u8; 32]>,
3240
3241 #[serde(with = "opthex", rename = "finalorchardroot")]
3243 #[serde(skip_serializing_if = "Option::is_none")]
3244 final_orchard_root: Option<[u8; 32]>,
3245
3246 tx: Vec<GetBlockTransaction>,
3251
3252 #[serde(skip_serializing_if = "Option::is_none")]
3254 time: Option<i64>,
3255
3256 #[serde(with = "opthex")]
3258 #[serde(skip_serializing_if = "Option::is_none")]
3259 nonce: Option<[u8; 32]>,
3260
3261 #[serde(with = "opthex")]
3264 #[serde(skip_serializing_if = "Option::is_none")]
3265 solution: Option<Solution>,
3266
3267 #[serde(with = "opthex")]
3269 #[serde(skip_serializing_if = "Option::is_none")]
3270 bits: Option<CompactDifficulty>,
3271
3272 #[serde(skip_serializing_if = "Option::is_none")]
3275 difficulty: Option<f64>,
3276
3277 trees: GetBlockTrees,
3284
3285 #[serde(rename = "previousblockhash", skip_serializing_if = "Option::is_none")]
3287 previous_block_hash: Option<GetBlockHash>,
3288
3289 #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
3291 next_block_hash: Option<GetBlockHash>,
3292 },
3293}
3294
3295impl Default for GetBlock {
3296 fn default() -> Self {
3297 GetBlock::Object {
3298 hash: GetBlockHash::default(),
3299 confirmations: 0,
3300 height: None,
3301 time: None,
3302 tx: Vec::new(),
3303 trees: GetBlockTrees::default(),
3304 size: None,
3305 version: None,
3306 merkle_root: None,
3307 block_commitments: None,
3308 final_sapling_root: None,
3309 final_orchard_root: None,
3310 nonce: None,
3311 bits: None,
3312 difficulty: None,
3313 previous_block_hash: None,
3314 next_block_hash: None,
3315 solution: None,
3316 }
3317 }
3318}
3319
3320#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3321#[serde(untagged)]
3322pub enum GetBlockTransaction {
3325 Hash(#[serde(with = "hex")] transaction::Hash),
3327 Object(Box<TransactionObject>),
3329}
3330
3331#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3335#[serde(untagged)]
3336pub enum GetBlockHeader {
3337 Raw(hex_data::HexData),
3339
3340 Object(Box<GetBlockHeaderObject>),
3342}
3343
3344#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3345pub struct GetBlockHeaderObject {
3349 pub hash: GetBlockHash,
3351
3352 pub confirmations: i64,
3355
3356 pub height: Height,
3358
3359 pub version: u32,
3361
3362 #[serde(with = "hex", rename = "merkleroot")]
3364 pub merkle_root: block::merkle::Root,
3365
3366 #[serde(with = "hex", rename = "blockcommitments")]
3369 pub block_commitments: [u8; 32],
3370
3371 #[serde(with = "hex", rename = "finalsaplingroot")]
3373 pub final_sapling_root: [u8; 32],
3374
3375 #[serde(skip)]
3378 pub sapling_tree_size: u64,
3379
3380 pub time: i64,
3382
3383 #[serde(with = "hex")]
3385 pub nonce: [u8; 32],
3386
3387 #[serde(with = "hex")]
3389 pub solution: Solution,
3390
3391 #[serde(with = "hex")]
3393 pub bits: CompactDifficulty,
3394
3395 pub difficulty: f64,
3398
3399 #[serde(rename = "previousblockhash")]
3401 pub previous_block_hash: GetBlockHash,
3402
3403 #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
3405 pub next_block_hash: Option<GetBlockHash>,
3406}
3407
3408impl Default for GetBlockHeader {
3409 fn default() -> Self {
3410 GetBlockHeader::Object(Box::default())
3411 }
3412}
3413
3414impl Default for GetBlockHeaderObject {
3415 fn default() -> Self {
3416 let difficulty: ExpandedDifficulty = zebra_chain::work::difficulty::U256::one().into();
3417
3418 GetBlockHeaderObject {
3419 hash: GetBlockHash::default(),
3420 confirmations: 0,
3421 height: Height::MIN,
3422 version: 4,
3423 merkle_root: block::merkle::Root([0; 32]),
3424 block_commitments: Default::default(),
3425 final_sapling_root: Default::default(),
3426 sapling_tree_size: Default::default(),
3427 time: 0,
3428 nonce: [0; 32],
3429 solution: Solution::for_proposal(),
3430 bits: difficulty.to_compact(),
3431 difficulty: 1.0,
3432 previous_block_hash: Default::default(),
3433 next_block_hash: Default::default(),
3434 }
3435 }
3436}
3437
3438#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3444#[serde(transparent)]
3445pub struct GetBlockHash(#[serde(with = "hex")] pub block::Hash);
3446
3447#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3449pub struct GetBlockHeightAndHash {
3450 pub height: block::Height,
3452 pub hash: block::Hash,
3454}
3455
3456impl Default for GetBlockHeightAndHash {
3457 fn default() -> Self {
3458 Self {
3459 height: block::Height::MIN,
3460 hash: block::Hash([0; 32]),
3461 }
3462 }
3463}
3464
3465impl Default for GetBlockHash {
3466 fn default() -> Self {
3467 GetBlockHash(block::Hash([0; 32]))
3468 }
3469}
3470
3471#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3475#[serde(untagged)]
3476pub enum GetRawTransaction {
3477 Raw(#[serde(with = "hex")] SerializedTransaction),
3479 Object(Box<TransactionObject>),
3481}
3482
3483impl Default for GetRawTransaction {
3484 fn default() -> Self {
3485 Self::Object(Box::default())
3486 }
3487}
3488
3489#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3493pub struct GetAddressUtxos {
3494 address: transparent::Address,
3496
3497 #[serde(with = "hex")]
3499 txid: transaction::Hash,
3500
3501 #[serde(rename = "outputIndex")]
3503 output_index: OutputIndex,
3504
3505 #[serde(with = "hex")]
3507 script: transparent::Script,
3508
3509 satoshis: u64,
3511
3512 height: Height,
3516}
3517
3518impl Default for GetAddressUtxos {
3519 fn default() -> Self {
3520 Self {
3521 address: transparent::Address::from_pub_key_hash(
3522 zebra_chain::parameters::NetworkKind::default(),
3523 [0u8; 20],
3524 ),
3525 txid: transaction::Hash::from([0; 32]),
3526 output_index: OutputIndex::from_u64(0),
3527 script: transparent::Script::new(&[0u8; 10]),
3528 satoshis: u64::default(),
3529 height: Height(0),
3530 }
3531 }
3532}
3533
3534impl GetAddressUtxos {
3535 pub fn from_parts(
3537 address: transparent::Address,
3538 txid: transaction::Hash,
3539 output_index: OutputIndex,
3540 script: transparent::Script,
3541 satoshis: u64,
3542 height: Height,
3543 ) -> Self {
3544 GetAddressUtxos {
3545 address,
3546 txid,
3547 output_index,
3548 script,
3549 satoshis,
3550 height,
3551 }
3552 }
3553
3554 pub fn into_parts(
3556 &self,
3557 ) -> (
3558 transparent::Address,
3559 transaction::Hash,
3560 OutputIndex,
3561 transparent::Script,
3562 u64,
3563 Height,
3564 ) {
3565 (
3566 self.address.clone(),
3567 self.txid,
3568 self.output_index,
3569 self.script.clone(),
3570 self.satoshis,
3571 self.height,
3572 )
3573 }
3574}
3575
3576#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3580pub struct GetAddressTxIdsRequest {
3581 addresses: Vec<String>,
3583 start: Option<u32>,
3585 end: Option<u32>,
3587}
3588
3589impl GetAddressTxIdsRequest {
3590 pub fn from_parts(addresses: Vec<String>, start: u32, end: u32) -> Self {
3592 GetAddressTxIdsRequest {
3593 addresses,
3594 start: Some(start),
3595 end: Some(end),
3596 }
3597 }
3598 pub fn into_parts(&self) -> (Vec<String>, u32, u32) {
3600 (
3601 self.addresses.clone(),
3602 self.start.unwrap_or(0),
3603 self.end.unwrap_or(0),
3604 )
3605 }
3606}
3607
3608#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3610pub struct GetBlockTrees {
3611 #[serde(skip_serializing_if = "SaplingTrees::is_empty")]
3612 sapling: SaplingTrees,
3613 #[serde(skip_serializing_if = "OrchardTrees::is_empty")]
3614 orchard: OrchardTrees,
3615}
3616
3617impl Default for GetBlockTrees {
3618 fn default() -> Self {
3619 GetBlockTrees {
3620 sapling: SaplingTrees { size: 0 },
3621 orchard: OrchardTrees { size: 0 },
3622 }
3623 }
3624}
3625
3626impl GetBlockTrees {
3627 pub fn new(sapling: u64, orchard: u64) -> Self {
3629 GetBlockTrees {
3630 sapling: SaplingTrees { size: sapling },
3631 orchard: OrchardTrees { size: orchard },
3632 }
3633 }
3634
3635 pub fn sapling(self) -> u64 {
3637 self.sapling.size
3638 }
3639
3640 pub fn orchard(self) -> u64 {
3642 self.orchard.size
3643 }
3644}
3645
3646#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3648pub struct SaplingTrees {
3649 size: u64,
3650}
3651
3652impl SaplingTrees {
3653 fn is_empty(&self) -> bool {
3654 self.size == 0
3655 }
3656}
3657
3658#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3660pub struct OrchardTrees {
3661 size: u64,
3662}
3663
3664impl OrchardTrees {
3665 fn is_empty(&self) -> bool {
3666 self.size == 0
3667 }
3668}
3669
3670fn build_height_range(
3686 start: Option<u32>,
3687 end: Option<u32>,
3688 chain_height: Height,
3689) -> Result<RangeInclusive<Height>> {
3690 let start = Height(start.unwrap_or(0)).min(chain_height);
3693
3694 let end = match end {
3696 Some(0) | None => chain_height,
3697 Some(val) => Height(val).min(chain_height),
3698 };
3699
3700 if start > end {
3701 return Err(ErrorObject::owned(
3702 ErrorCode::InvalidParams.code(),
3703 format!("start {start:?} must be less than or equal to end {end:?}"),
3704 None::<()>,
3705 ));
3706 }
3707
3708 Ok(start..=end)
3709}
3710
3711#[allow(dead_code)]
3719pub fn height_from_signed_int(index: i32, tip_height: Height) -> Result<Height> {
3720 if index >= 0 {
3721 let height = index.try_into().expect("Positive i32 always fits in u32");
3722 if height > tip_height.0 {
3723 return Err(ErrorObject::borrowed(
3724 ErrorCode::InvalidParams.code(),
3725 "Provided index is greater than the current tip",
3726 None,
3727 ));
3728 }
3729 Ok(Height(height))
3730 } else {
3731 let height = i32::try_from(tip_height.0)
3733 .expect("tip height fits in i32, because Height::MAX fits in i32")
3734 .checked_add(index + 1);
3735
3736 let sanitized_height = match height {
3737 None => {
3738 return Err(ErrorObject::borrowed(
3739 ErrorCode::InvalidParams.code(),
3740 "Provided index is not valid",
3741 None,
3742 ));
3743 }
3744 Some(h) => {
3745 if h < 0 {
3746 return Err(ErrorObject::borrowed(
3747 ErrorCode::InvalidParams.code(),
3748 "Provided negative index ends up with a negative height",
3749 None,
3750 ));
3751 }
3752 let h: u32 = h.try_into().expect("Positive i32 always fits in u32");
3753 if h > tip_height.0 {
3754 return Err(ErrorObject::borrowed(
3755 ErrorCode::InvalidParams.code(),
3756 "Provided index is greater than the current tip",
3757 None,
3758 ));
3759 }
3760
3761 h
3762 }
3763 };
3764
3765 Ok(Height(sanitized_height))
3766 }
3767}
3768
3769pub mod opthex {
3771 use hex::{FromHex, ToHex};
3772 use serde::{de, Deserialize, Deserializer, Serializer};
3773
3774 #[allow(missing_docs)]
3775 pub fn serialize<S, T>(data: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
3776 where
3777 S: Serializer,
3778 T: ToHex,
3779 {
3780 match data {
3781 Some(data) => {
3782 let s = data.encode_hex::<String>();
3783 serializer.serialize_str(&s)
3784 }
3785 None => serializer.serialize_none(),
3786 }
3787 }
3788
3789 #[allow(missing_docs)]
3790 pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
3791 where
3792 D: Deserializer<'de>,
3793 T: FromHex,
3794 {
3795 let opt = Option::<String>::deserialize(deserializer)?;
3796 match opt {
3797 Some(s) => T::from_hex(&s)
3798 .map(Some)
3799 .map_err(|_e| de::Error::custom("failed to convert hex string")),
3800 None => Ok(None),
3801 }
3802 }
3803}
3804
3805pub mod arrayhex {
3807 use serde::{Deserializer, Serializer};
3808 use std::fmt;
3809
3810 #[allow(missing_docs)]
3811 pub fn serialize<S, const N: usize>(data: &[u8; N], serializer: S) -> Result<S::Ok, S::Error>
3812 where
3813 S: Serializer,
3814 {
3815 let hex_string = hex::encode(data);
3816 serializer.serialize_str(&hex_string)
3817 }
3818
3819 #[allow(missing_docs)]
3820 pub fn deserialize<'de, D, const N: usize>(deserializer: D) -> Result<[u8; N], D::Error>
3821 where
3822 D: Deserializer<'de>,
3823 {
3824 struct HexArrayVisitor<const N: usize>;
3825
3826 impl<const N: usize> serde::de::Visitor<'_> for HexArrayVisitor<N> {
3827 type Value = [u8; N];
3828
3829 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
3830 write!(formatter, "a hex string representing exactly {} bytes", N)
3831 }
3832
3833 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
3834 where
3835 E: serde::de::Error,
3836 {
3837 let vec = hex::decode(v).map_err(E::custom)?;
3838 vec.clone().try_into().map_err(|_| {
3839 E::invalid_length(vec.len(), &format!("expected {} bytes", N).as_str())
3840 })
3841 }
3842 }
3843
3844 deserializer.deserialize_str(HexArrayVisitor::<N>)
3845 }
3846}
3847
3848pub async fn chain_tip_difficulty<State>(
3850 network: Network,
3851 mut state: State,
3852 should_use_default: bool,
3853) -> Result<f64>
3854where
3855 State: Service<
3856 zebra_state::ReadRequest,
3857 Response = zebra_state::ReadResponse,
3858 Error = zebra_state::BoxError,
3859 > + Clone
3860 + Send
3861 + Sync
3862 + 'static,
3863 State::Future: Send,
3864{
3865 let request = ReadRequest::ChainInfo;
3866
3867 let response = state
3873 .ready()
3874 .and_then(|service| service.call(request))
3875 .await;
3876
3877 let response = match (should_use_default, response) {
3878 (_, Ok(res)) => res,
3879 (true, Err(_)) => {
3880 return Ok((U256::from(network.target_difficulty_limit()) >> 128).as_u128() as f64)
3881 }
3882 (false, Err(error)) => return Err(ErrorObject::owned(0, error.to_string(), None::<()>)),
3883 };
3884
3885 let chain_info = match response {
3886 ReadResponse::ChainInfo(info) => info,
3887 _ => unreachable!("unmatched response to a chain info request"),
3888 };
3889
3890 let pow_limit: U256 = network.target_difficulty_limit().into();
3913 let Some(difficulty) = chain_info.expected_difficulty.to_expanded() else {
3914 return Ok(0.0);
3915 };
3916
3917 let pow_limit = pow_limit >> 128;
3919 let difficulty = U256::from(difficulty) >> 128;
3920
3921 let pow_limit = pow_limit.as_u128() as f64;
3924 let difficulty = difficulty.as_u128() as f64;
3925
3926 Ok(pow_limit / difficulty)
3928}