1use std::{
35 cmp,
36 collections::{HashMap, HashSet},
37 fmt,
38 ops::RangeInclusive,
39 sync::Arc,
40 time::Duration,
41};
42
43use chrono::Utc;
44use derive_getters::Getters;
45use derive_new::new;
46use futures::{future::OptionFuture, stream::FuturesOrdered, StreamExt, TryFutureExt};
47use hex::{FromHex, ToHex};
48use indexmap::IndexMap;
49use jsonrpsee::core::{async_trait, RpcResult as Result};
50use jsonrpsee_proc_macros::rpc;
51use jsonrpsee_types::{ErrorCode, ErrorObject};
52use tokio::{
53 sync::{broadcast, watch},
54 task::JoinHandle,
55};
56use tower::{Service, ServiceExt};
57use tracing::Instrument;
58
59use zcash_address::{unified::Encoding, TryFromAddress};
60use zcash_primitives::consensus::Parameters;
61
62use zebra_chain::{
63 amount::{self, Amount, NegativeAllowed, NonNegative},
64 block::{self, Block, Commitment, Height, SerializedBlock, TryIntoHeight},
65 chain_sync_status::ChainSyncStatus,
66 chain_tip::{ChainTip, NetworkChainTipHeightEstimator},
67 parameters::{
68 subsidy::{
69 block_subsidy, funding_stream_values, miner_subsidy, FundingStreamReceiver,
70 ParameterSubsidy,
71 },
72 ConsensusBranchId, Network, NetworkUpgrade, POW_AVERAGING_WINDOW,
73 },
74 serialization::{ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize},
75 subtree::NoteCommitmentSubtreeIndex,
76 transaction::{self, SerializedTransaction, Transaction, UnminedTx},
77 transparent::{self, Address, OutputIndex},
78 value_balance::ValueBalance,
79 work::{
80 difficulty::{CompactDifficulty, ExpandedDifficulty, ParameterDifficulty, U256},
81 equihash::Solution,
82 },
83};
84use zebra_consensus::{funding_stream_address, RouterError};
85use zebra_network::{address_book_peers::AddressBookPeers, PeerSocketAddr};
86use zebra_node_services::mempool;
87use zebra_state::{HashOrHeight, OutputLocation, ReadRequest, ReadResponse, TransactionLocation};
88
89use crate::{
90 client::Treestate,
91 config,
92 methods::types::{validate_address::validate_address, z_validate_address::z_validate_address},
93 queue::Queue,
94 server::{
95 self,
96 error::{MapError, OkOrError},
97 },
98};
99
100pub(crate) mod hex_data;
101pub(crate) mod trees;
102pub(crate) mod types;
103
104use hex_data::HexData;
105use trees::{GetSubtreesByIndexResponse, GetTreestateResponse, SubtreeRpcData};
106use types::{
107 get_block_template::{
108 constants::{
109 DEFAULT_SOLUTION_RATE_WINDOW_SIZE, MEMPOOL_LONG_POLL_INTERVAL,
110 ZCASHD_FUNDING_STREAM_ORDER,
111 },
112 proposal::proposal_block_from_template,
113 BlockTemplateResponse, BlockTemplateTimeSource, GetBlockTemplateHandler,
114 GetBlockTemplateParameters, GetBlockTemplateResponse,
115 },
116 get_blockchain_info::GetBlockchainInfoBalance,
117 get_mempool_info::GetMempoolInfoResponse,
118 get_mining_info::GetMiningInfoResponse,
119 get_raw_mempool::{self, GetRawMempoolResponse},
120 long_poll::LongPollInput,
121 peer_info::PeerInfo,
122 submit_block::{SubmitBlockErrorResponse, SubmitBlockParameters, SubmitBlockResponse},
123 subsidy::GetBlockSubsidyResponse,
124 transaction::TransactionObject,
125 unified_address::ZListUnifiedReceiversResponse,
126 validate_address::ValidateAddressResponse,
127 z_validate_address::ZValidateAddressResponse,
128};
129
130#[cfg(test)]
131mod tests;
132
133#[rpc(server)]
134pub trait Rpc {
136 #[method(name = "getinfo")]
151 async fn get_info(&self) -> Result<GetInfoResponse>;
152
153 #[method(name = "getblockchaininfo")]
164 async fn get_blockchain_info(&self) -> Result<GetBlockchainInfoResponse>;
165
166 #[method(name = "getaddressbalance")]
189 async fn get_address_balance(
190 &self,
191 address_strings: GetAddressBalanceRequest,
192 ) -> Result<GetAddressBalanceResponse>;
193
194 #[method(name = "sendrawtransaction")]
211 async fn send_raw_transaction(
212 &self,
213 raw_transaction_hex: String,
214 _allow_high_fees: Option<bool>,
215 ) -> Result<SendRawTransactionResponse>;
216
217 #[method(name = "getblock")]
237 async fn get_block(
238 &self,
239 hash_or_height: String,
240 verbosity: Option<u8>,
241 ) -> Result<GetBlockResponse>;
242
243 #[method(name = "getblockheader")]
261 async fn get_block_header(
262 &self,
263 hash_or_height: String,
264 verbose: Option<bool>,
265 ) -> Result<GetBlockHeaderResponse>;
266
267 #[method(name = "getbestblockhash")]
273 fn get_best_block_hash(&self) -> Result<GetBlockHashResponse>;
274
275 #[method(name = "getbestblockheightandhash")]
281 fn get_best_block_height_and_hash(&self) -> Result<GetBlockHeightAndHashResponse>;
282
283 #[method(name = "getmempoolinfo")]
287 async fn get_mempool_info(&self) -> Result<GetMempoolInfoResponse>;
288
289 #[method(name = "getrawmempool")]
299 async fn get_raw_mempool(&self, verbose: Option<bool>) -> Result<GetRawMempoolResponse>;
300
301 #[method(name = "z_gettreestate")]
318 async fn z_get_treestate(&self, hash_or_height: String) -> Result<GetTreestateResponse>;
319
320 #[method(name = "z_getsubtreesbyindex")]
339 async fn z_get_subtrees_by_index(
340 &self,
341 pool: String,
342 start_index: NoteCommitmentSubtreeIndex,
343 limit: Option<NoteCommitmentSubtreeIndex>,
344 ) -> Result<GetSubtreesByIndexResponse>;
345
346 #[method(name = "getrawtransaction")]
358 async fn get_raw_transaction(
359 &self,
360 txid: String,
361 verbose: Option<u8>,
362 block_hash: Option<String>,
363 ) -> Result<GetRawTransactionResponse>;
364
365 #[method(name = "getaddresstxids")]
395 async fn get_address_tx_ids(&self, request: GetAddressTxIdsRequest) -> Result<Vec<String>>;
396
397 #[method(name = "getaddressutxos")]
416 async fn get_address_utxos(
417 &self,
418 request: GetAddressUtxosRequest,
419 ) -> Result<GetAddressUtxosResponse>;
420
421 #[method(name = "stop")]
432 fn stop(&self) -> Result<String>;
433
434 #[method(name = "getblockcount")]
441 fn get_block_count(&self) -> Result<u32>;
442
443 #[method(name = "getblockhash")]
459 async fn get_block_hash(&self, index: i32) -> Result<GetBlockHashResponse>;
460
461 #[method(name = "getblocktemplate")]
483 async fn get_block_template(
484 &self,
485 parameters: Option<GetBlockTemplateParameters>,
486 ) -> Result<GetBlockTemplateResponse>;
487
488 #[method(name = "submitblock")]
504 async fn submit_block(
505 &self,
506 hex_data: HexData,
507 _parameters: Option<SubmitBlockParameters>,
508 ) -> Result<SubmitBlockResponse>;
509
510 #[method(name = "getmininginfo")]
516 async fn get_mining_info(&self) -> Result<GetMiningInfoResponse>;
517
518 #[method(name = "getnetworksolps")]
529 async fn get_network_sol_ps(&self, num_blocks: Option<i32>, height: Option<i32>)
530 -> Result<u64>;
531
532 #[method(name = "getnetworkhashps")]
542 async fn get_network_hash_ps(
543 &self,
544 num_blocks: Option<i32>,
545 height: Option<i32>,
546 ) -> Result<u64> {
547 self.get_network_sol_ps(num_blocks, height).await
548 }
549
550 #[method(name = "getpeerinfo")]
556 async fn get_peer_info(&self) -> Result<Vec<PeerInfo>>;
557
558 #[method(name = "validateaddress")]
569 async fn validate_address(&self, address: String) -> Result<ValidateAddressResponse>;
570
571 #[method(name = "z_validateaddress")]
586 async fn z_validate_address(&self, address: String) -> Result<ZValidateAddressResponse>;
587
588 #[method(name = "getblocksubsidy")]
603 async fn get_block_subsidy(&self, height: Option<u32>) -> Result<GetBlockSubsidyResponse>;
604
605 #[method(name = "getdifficulty")]
611 async fn get_difficulty(&self) -> Result<f64>;
612
613 #[method(name = "z_listunifiedreceivers")]
627 async fn z_list_unified_receivers(
628 &self,
629 address: String,
630 ) -> Result<ZListUnifiedReceiversResponse>;
631
632 #[method(name = "invalidateblock")]
640 async fn invalidate_block(&self, block_hash: block::Hash) -> Result<()>;
641
642 #[method(name = "reconsiderblock")]
648 async fn reconsider_block(&self, block_hash: block::Hash) -> Result<Vec<block::Hash>>;
649
650 #[method(name = "generate")]
651 async fn generate(&self, num_blocks: u32) -> Result<Vec<GetBlockHashResponse>>;
665
666 #[method(name = "addnode")]
667 async fn add_node(&self, addr: PeerSocketAddr, command: AddNodeCommand) -> Result<()>;
682}
683
684#[derive(Clone)]
686pub struct RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
687where
688 Mempool: Service<
689 mempool::Request,
690 Response = mempool::Response,
691 Error = zebra_node_services::BoxError,
692 > + Clone
693 + Send
694 + Sync
695 + 'static,
696 Mempool::Future: Send,
697 State: Service<
698 zebra_state::Request,
699 Response = zebra_state::Response,
700 Error = zebra_state::BoxError,
701 > + Clone
702 + Send
703 + Sync
704 + 'static,
705 State::Future: Send,
706 ReadState: Service<
707 zebra_state::ReadRequest,
708 Response = zebra_state::ReadResponse,
709 Error = zebra_state::BoxError,
710 > + Clone
711 + Send
712 + Sync
713 + 'static,
714 ReadState::Future: Send,
715 Tip: ChainTip + Clone + Send + Sync + 'static,
716 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
717 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
718 + Clone
719 + Send
720 + Sync
721 + 'static,
722 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
723 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
724{
725 build_version: String,
729
730 user_agent: String,
732
733 network: Network,
735
736 debug_force_finished_sync: bool,
739
740 mempool: Mempool,
744
745 state: State,
747
748 read_state: ReadState,
750
751 latest_chain_tip: Tip,
753
754 queue_sender: broadcast::Sender<UnminedTx>,
758
759 address_book: AddressBook,
761
762 last_warn_error_log_rx: LoggedLastEvent,
764
765 gbt: GetBlockTemplateHandler<BlockVerifierRouter, SyncStatus>,
767}
768
769pub type LoggedLastEvent = watch::Receiver<Option<(String, tracing::Level, chrono::DateTime<Utc>)>>;
771
772impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus> fmt::Debug
773 for RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
774where
775 Mempool: Service<
776 mempool::Request,
777 Response = mempool::Response,
778 Error = zebra_node_services::BoxError,
779 > + Clone
780 + Send
781 + Sync
782 + 'static,
783 Mempool::Future: Send,
784 State: Service<
785 zebra_state::Request,
786 Response = zebra_state::Response,
787 Error = zebra_state::BoxError,
788 > + Clone
789 + Send
790 + Sync
791 + 'static,
792 State::Future: Send,
793 ReadState: Service<
794 zebra_state::ReadRequest,
795 Response = zebra_state::ReadResponse,
796 Error = zebra_state::BoxError,
797 > + Clone
798 + Send
799 + Sync
800 + 'static,
801 ReadState::Future: Send,
802 Tip: ChainTip + Clone + Send + Sync + 'static,
803 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
804 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
805 + Clone
806 + Send
807 + Sync
808 + 'static,
809 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
810 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
811{
812 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
813 f.debug_struct("RpcImpl")
815 .field("build_version", &self.build_version)
816 .field("user_agent", &self.user_agent)
817 .field("network", &self.network)
818 .field("debug_force_finished_sync", &self.debug_force_finished_sync)
819 .field("getblocktemplate", &self.gbt)
820 .finish()
821 }
822}
823
824impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
825 RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
826where
827 Mempool: Service<
828 mempool::Request,
829 Response = mempool::Response,
830 Error = zebra_node_services::BoxError,
831 > + Clone
832 + Send
833 + Sync
834 + 'static,
835 Mempool::Future: Send,
836 State: Service<
837 zebra_state::Request,
838 Response = zebra_state::Response,
839 Error = zebra_state::BoxError,
840 > + Clone
841 + Send
842 + Sync
843 + 'static,
844 State::Future: Send,
845 ReadState: Service<
846 zebra_state::ReadRequest,
847 Response = zebra_state::ReadResponse,
848 Error = zebra_state::BoxError,
849 > + Clone
850 + Send
851 + Sync
852 + 'static,
853 ReadState::Future: Send,
854 Tip: ChainTip + Clone + Send + Sync + 'static,
855 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
856 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
857 + Clone
858 + Send
859 + Sync
860 + 'static,
861 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
862 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
863{
864 #[allow(clippy::too_many_arguments)]
869 pub fn new<VersionString, UserAgentString>(
870 network: Network,
871 mining_config: config::mining::Config,
872 debug_force_finished_sync: bool,
873 build_version: VersionString,
874 user_agent: UserAgentString,
875 mempool: Mempool,
876 state: State,
877 read_state: ReadState,
878 block_verifier_router: BlockVerifierRouter,
879 sync_status: SyncStatus,
880 latest_chain_tip: Tip,
881 address_book: AddressBook,
882 last_warn_error_log_rx: LoggedLastEvent,
883 mined_block_sender: Option<watch::Sender<(block::Hash, block::Height)>>,
884 ) -> (Self, JoinHandle<()>)
885 where
886 VersionString: ToString + Clone + Send + 'static,
887 UserAgentString: ToString + Clone + Send + 'static,
888 {
889 let (runner, queue_sender) = Queue::start();
890
891 let mut build_version = build_version.to_string();
892 let user_agent = user_agent.to_string();
893
894 if !build_version.is_empty() && !build_version.starts_with('v') {
896 build_version.insert(0, 'v');
897 }
898
899 let gbt = GetBlockTemplateHandler::new(
900 &network,
901 mining_config.clone(),
902 block_verifier_router,
903 sync_status,
904 mined_block_sender,
905 );
906
907 let rpc_impl = RpcImpl {
908 build_version,
909 user_agent,
910 network: network.clone(),
911 debug_force_finished_sync,
912 mempool: mempool.clone(),
913 state: state.clone(),
914 read_state: read_state.clone(),
915 latest_chain_tip: latest_chain_tip.clone(),
916 queue_sender,
917 address_book,
918 last_warn_error_log_rx,
919 gbt,
920 };
921
922 let rpc_tx_queue_task_handle = tokio::spawn(
924 runner
925 .run(mempool, read_state, latest_chain_tip, network)
926 .in_current_span(),
927 );
928
929 (rpc_impl, rpc_tx_queue_task_handle)
930 }
931
932 pub fn network(&self) -> &Network {
934 &self.network
935 }
936}
937
938#[async_trait]
939impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus> RpcServer
940 for RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
941where
942 Mempool: Service<
943 mempool::Request,
944 Response = mempool::Response,
945 Error = zebra_node_services::BoxError,
946 > + Clone
947 + Send
948 + Sync
949 + 'static,
950 Mempool::Future: Send,
951 State: Service<
952 zebra_state::Request,
953 Response = zebra_state::Response,
954 Error = zebra_state::BoxError,
955 > + Clone
956 + Send
957 + Sync
958 + 'static,
959 State::Future: Send,
960 ReadState: Service<
961 zebra_state::ReadRequest,
962 Response = zebra_state::ReadResponse,
963 Error = zebra_state::BoxError,
964 > + Clone
965 + Send
966 + Sync
967 + 'static,
968 ReadState::Future: Send,
969 Tip: ChainTip + Clone + Send + Sync + 'static,
970 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
971 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
972 + Clone
973 + Send
974 + Sync
975 + 'static,
976 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
977 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
978{
979 async fn get_info(&self) -> Result<GetInfoResponse> {
980 let version = GetInfoResponse::version_from_string(&self.build_version)
981 .expect("invalid version string");
982
983 let connections = self.address_book.recently_live_peers(Utc::now()).len();
984
985 let last_error_recorded = self.last_warn_error_log_rx.borrow().clone();
986 let (last_error_log, _level, last_error_log_time) = last_error_recorded.unwrap_or((
987 GetInfoResponse::default().errors,
988 tracing::Level::INFO,
989 Utc::now(),
990 ));
991
992 let tip_height = self
993 .latest_chain_tip
994 .best_tip_height()
995 .unwrap_or(Height::MIN);
996 let testnet = self.network.is_a_test_network();
997
998 let pay_tx_fee = 0.0;
1004
1005 let relay_fee = zebra_chain::transaction::zip317::MIN_MEMPOOL_TX_FEE_RATE as f64
1006 / (zebra_chain::amount::COIN as f64);
1007 let difficulty = chain_tip_difficulty(self.network.clone(), self.read_state.clone(), true)
1008 .await
1009 .expect("should always be Ok when `should_use_default` is true");
1010
1011 let response = GetInfoResponse {
1012 version,
1013 build: self.build_version.clone(),
1014 subversion: self.user_agent.clone(),
1015 protocol_version: zebra_network::constants::CURRENT_NETWORK_PROTOCOL_VERSION.0,
1016 blocks: tip_height.0,
1017 connections,
1018 proxy: None,
1019 difficulty,
1020 testnet,
1021 pay_tx_fee,
1022 relay_fee,
1023 errors: last_error_log,
1024 errors_timestamp: last_error_log_time.to_string(),
1025 };
1026
1027 Ok(response)
1028 }
1029
1030 #[allow(clippy::unwrap_in_result)]
1031 async fn get_blockchain_info(&self) -> Result<GetBlockchainInfoResponse> {
1032 let debug_force_finished_sync = self.debug_force_finished_sync;
1033 let network = &self.network;
1034
1035 let (usage_info_rsp, tip_pool_values_rsp, chain_tip_difficulty) = {
1036 use zebra_state::ReadRequest::*;
1037 let state_call = |request| self.read_state.clone().oneshot(request);
1038 tokio::join!(
1039 state_call(UsageInfo),
1040 state_call(TipPoolValues),
1041 chain_tip_difficulty(network.clone(), self.read_state.clone(), true)
1042 )
1043 };
1044
1045 let (size_on_disk, (tip_height, tip_hash), value_balance, difficulty) = {
1046 use zebra_state::ReadResponse::*;
1047
1048 let UsageInfo(size_on_disk) = usage_info_rsp.map_misc_error()? else {
1049 unreachable!("unmatched response to a TipPoolValues request")
1050 };
1051
1052 let (tip, value_balance) = match tip_pool_values_rsp {
1053 Ok(TipPoolValues {
1054 tip_height,
1055 tip_hash,
1056 value_balance,
1057 }) => ((tip_height, tip_hash), value_balance),
1058 Ok(_) => unreachable!("unmatched response to a TipPoolValues request"),
1059 Err(_) => ((Height::MIN, network.genesis_hash()), Default::default()),
1060 };
1061
1062 let difficulty = chain_tip_difficulty
1063 .expect("should always be Ok when `should_use_default` is true");
1064
1065 (size_on_disk, tip, value_balance, difficulty)
1066 };
1067
1068 let now = Utc::now();
1069 let (estimated_height, verification_progress) = self
1070 .latest_chain_tip
1071 .best_tip_height_and_block_time()
1072 .map(|(tip_height, tip_block_time)| {
1073 let height =
1074 NetworkChainTipHeightEstimator::new(tip_block_time, tip_height, network)
1075 .estimate_height_at(now);
1076
1077 let height =
1081 if tip_block_time > now || height < tip_height || debug_force_finished_sync {
1082 tip_height
1083 } else {
1084 height
1085 };
1086
1087 (height, f64::from(tip_height.0) / f64::from(height.0))
1088 })
1089 .unwrap_or((Height::MIN, 0.0));
1091
1092 let verification_progress = if network.is_regtest() {
1093 1.0
1094 } else {
1095 verification_progress
1096 };
1097
1098 let mut upgrades = IndexMap::new();
1102 for (activation_height, network_upgrade) in network.full_activation_list() {
1103 if let Some(branch_id) = network_upgrade.branch_id() {
1108 let status = if tip_height >= activation_height {
1110 NetworkUpgradeStatus::Active
1111 } else {
1112 NetworkUpgradeStatus::Pending
1113 };
1114
1115 let upgrade = NetworkUpgradeInfo {
1116 name: network_upgrade,
1117 activation_height,
1118 status,
1119 };
1120 upgrades.insert(ConsensusBranchIdHex(branch_id), upgrade);
1121 }
1122 }
1123
1124 let next_block_height =
1126 (tip_height + 1).expect("valid chain tips are a lot less than Height::MAX");
1127 let consensus = TipConsensusBranch {
1128 chain_tip: ConsensusBranchIdHex(
1129 NetworkUpgrade::current(network, tip_height)
1130 .branch_id()
1131 .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
1132 ),
1133 next_block: ConsensusBranchIdHex(
1134 NetworkUpgrade::current(network, next_block_height)
1135 .branch_id()
1136 .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
1137 ),
1138 };
1139
1140 let response = GetBlockchainInfoResponse {
1141 chain: network.bip70_network_name(),
1142 blocks: tip_height,
1143 best_block_hash: tip_hash,
1144 estimated_height,
1145 chain_supply: GetBlockchainInfoBalance::chain_supply(value_balance),
1146 value_pools: GetBlockchainInfoBalance::value_pools(value_balance, None),
1147 upgrades,
1148 consensus,
1149 headers: tip_height,
1150 difficulty,
1151 verification_progress,
1152 chain_work: 0,
1154 pruned: false,
1155 size_on_disk,
1156 commitments: 0,
1158 };
1159
1160 Ok(response)
1161 }
1162
1163 async fn get_address_balance(
1164 &self,
1165 address_strings: GetAddressBalanceRequest,
1166 ) -> Result<GetAddressBalanceResponse> {
1167 let valid_addresses = address_strings.valid_addresses()?;
1168
1169 let request = zebra_state::ReadRequest::AddressBalance(valid_addresses);
1170 let response = self
1171 .read_state
1172 .clone()
1173 .oneshot(request)
1174 .await
1175 .map_misc_error()?;
1176
1177 match response {
1178 zebra_state::ReadResponse::AddressBalance { balance, received } => {
1179 Ok(GetAddressBalanceResponse {
1180 balance: u64::from(balance),
1181 received,
1182 })
1183 }
1184 _ => unreachable!("Unexpected response from state service: {response:?}"),
1185 }
1186 }
1187
1188 async fn send_raw_transaction(
1190 &self,
1191 raw_transaction_hex: String,
1192 _allow_high_fees: Option<bool>,
1193 ) -> Result<SendRawTransactionResponse> {
1194 let mempool = self.mempool.clone();
1195 let queue_sender = self.queue_sender.clone();
1196
1197 let raw_transaction_bytes = Vec::from_hex(raw_transaction_hex)
1200 .map_error(server::error::LegacyCode::Deserialization)?;
1201 let raw_transaction = Transaction::zcash_deserialize(&*raw_transaction_bytes)
1202 .map_error(server::error::LegacyCode::Deserialization)?;
1203
1204 let transaction_hash = raw_transaction.hash();
1205
1206 let unmined_transaction = UnminedTx::from(raw_transaction.clone());
1208 let _ = queue_sender.send(unmined_transaction);
1209
1210 let transaction_parameter = mempool::Gossip::Tx(raw_transaction.into());
1211 let request = mempool::Request::Queue(vec![transaction_parameter]);
1212
1213 let response = mempool.oneshot(request).await.map_misc_error()?;
1214
1215 let mut queue_results = match response {
1216 mempool::Response::Queued(results) => results,
1217 _ => unreachable!("incorrect response variant from mempool service"),
1218 };
1219
1220 assert_eq!(
1221 queue_results.len(),
1222 1,
1223 "mempool service returned more results than expected"
1224 );
1225
1226 let queue_result = queue_results
1227 .pop()
1228 .expect("there should be exactly one item in Vec")
1229 .inspect_err(|err| tracing::debug!("sent transaction to mempool: {:?}", &err))
1230 .map_misc_error()?
1231 .await
1232 .map_misc_error()?;
1233
1234 tracing::debug!("sent transaction to mempool: {:?}", &queue_result);
1235
1236 queue_result
1237 .map(|_| SendRawTransactionResponse(transaction_hash))
1238 .map_error(server::error::LegacyCode::Verify)
1245 }
1246
1247 async fn get_block(
1252 &self,
1253 hash_or_height: String,
1254 verbosity: Option<u8>,
1255 ) -> Result<GetBlockResponse> {
1256 let verbosity = verbosity.unwrap_or(1);
1257 let network = self.network.clone();
1258 let original_hash_or_height = hash_or_height.clone();
1259
1260 let get_block_header_future = if matches!(verbosity, 1 | 2) {
1262 Some(self.get_block_header(original_hash_or_height.clone(), Some(true)))
1263 } else {
1264 None
1265 };
1266
1267 let hash_or_height =
1268 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1269 .map_error(server::error::LegacyCode::InvalidParameter)?;
1272
1273 if verbosity == 0 {
1274 let request = zebra_state::ReadRequest::Block(hash_or_height);
1275 let response = self
1276 .read_state
1277 .clone()
1278 .oneshot(request)
1279 .await
1280 .map_misc_error()?;
1281
1282 match response {
1283 zebra_state::ReadResponse::Block(Some(block)) => {
1284 Ok(GetBlockResponse::Raw(block.into()))
1285 }
1286 zebra_state::ReadResponse::Block(None) => {
1287 Err("Block not found").map_error(server::error::LegacyCode::InvalidParameter)
1288 }
1289 _ => unreachable!("unmatched response to a block request"),
1290 }
1291 } else if let Some(get_block_header_future) = get_block_header_future {
1292 let get_block_header_result: Result<GetBlockHeaderResponse> =
1293 get_block_header_future.await;
1294
1295 let GetBlockHeaderResponse::Object(block_header) = get_block_header_result? else {
1296 panic!("must return Object")
1297 };
1298
1299 let BlockHeaderObject {
1300 hash,
1301 confirmations,
1302 height,
1303 version,
1304 merkle_root,
1305 block_commitments,
1306 final_sapling_root,
1307 sapling_tree_size,
1308 time,
1309 nonce,
1310 solution,
1311 bits,
1312 difficulty,
1313 previous_block_hash,
1314 next_block_hash,
1315 } = *block_header;
1316
1317 let transactions_request = match verbosity {
1318 1 => zebra_state::ReadRequest::TransactionIdsForBlock(hash_or_height),
1319 2 => zebra_state::ReadRequest::BlockAndSize(hash_or_height),
1320 _other => panic!("get_block_header_fut should be none"),
1321 };
1322
1323 let hash_or_height = hash.into();
1328 let requests = vec![
1329 transactions_request,
1337 zebra_state::ReadRequest::OrchardTree(hash_or_height),
1339 zebra_state::ReadRequest::BlockInfo(previous_block_hash.into()),
1341 zebra_state::ReadRequest::BlockInfo(hash_or_height),
1342 ];
1343
1344 let mut futs = FuturesOrdered::new();
1345
1346 for request in requests {
1347 futs.push_back(self.read_state.clone().oneshot(request));
1348 }
1349
1350 let tx_ids_response = futs.next().await.expect("`futs` should not be empty");
1351 let (tx, size): (Vec<_>, Option<usize>) = match tx_ids_response.map_misc_error()? {
1352 zebra_state::ReadResponse::TransactionIdsForBlock(tx_ids) => (
1353 tx_ids
1354 .ok_or_misc_error("block not found")?
1355 .iter()
1356 .map(|tx_id| GetBlockTransaction::Hash(*tx_id))
1357 .collect(),
1358 None,
1359 ),
1360 zebra_state::ReadResponse::BlockAndSize(block_and_size) => {
1361 let (block, size) = block_and_size.ok_or_misc_error("Block not found")?;
1362 let block_time = block.header.time;
1363 let transactions =
1364 block
1365 .transactions
1366 .iter()
1367 .map(|tx| {
1368 GetBlockTransaction::Object(Box::new(
1369 TransactionObject::from_transaction(
1370 tx.clone(),
1371 Some(height),
1372 Some(confirmations.try_into().expect(
1373 "should be less than max block height, i32::MAX",
1374 )),
1375 &network,
1376 Some(block_time),
1377 Some(hash),
1378 Some(true),
1379 tx.hash(),
1380 ),
1381 ))
1382 })
1383 .collect();
1384 (transactions, Some(size))
1385 }
1386 _ => unreachable!("unmatched response to a transaction_ids_for_block request"),
1387 };
1388
1389 let orchard_tree_response = futs.next().await.expect("`futs` should not be empty");
1390 let zebra_state::ReadResponse::OrchardTree(orchard_tree) =
1391 orchard_tree_response.map_misc_error()?
1392 else {
1393 unreachable!("unmatched response to a OrchardTree request");
1394 };
1395
1396 let nu5_activation = NetworkUpgrade::Nu5.activation_height(&network);
1397
1398 let orchard_tree = orchard_tree.ok_or_misc_error("missing Orchard tree")?;
1400
1401 let final_orchard_root = match nu5_activation {
1402 Some(activation_height) if height >= activation_height => {
1403 Some(orchard_tree.root().into())
1404 }
1405 _other => None,
1406 };
1407
1408 let sapling = SaplingTrees {
1409 size: sapling_tree_size,
1410 };
1411
1412 let orchard_tree_size = orchard_tree.count();
1413 let orchard = OrchardTrees {
1414 size: orchard_tree_size,
1415 };
1416
1417 let trees = GetBlockTrees { sapling, orchard };
1418
1419 let block_info_response = futs.next().await.expect("`futs` should not be empty");
1420 let zebra_state::ReadResponse::BlockInfo(prev_block_info) =
1421 block_info_response.map_misc_error()?
1422 else {
1423 unreachable!("unmatched response to a BlockInfo request");
1424 };
1425 let block_info_response = futs.next().await.expect("`futs` should not be empty");
1426 let zebra_state::ReadResponse::BlockInfo(block_info) =
1427 block_info_response.map_misc_error()?
1428 else {
1429 unreachable!("unmatched response to a BlockInfo request");
1430 };
1431
1432 let delta = block_info.as_ref().and_then(|d| {
1433 let value_pools = d.value_pools().constrain::<NegativeAllowed>().ok()?;
1434 let prev_value_pools = prev_block_info
1435 .map(|d| d.value_pools().constrain::<NegativeAllowed>())
1436 .unwrap_or(Ok(ValueBalance::<NegativeAllowed>::zero()))
1437 .ok()?;
1438 (value_pools - prev_value_pools).ok()
1439 });
1440 let size = size.or(block_info.as_ref().map(|d| d.size() as usize));
1441
1442 Ok(GetBlockResponse::Object(Box::new(BlockObject {
1443 hash,
1444 confirmations,
1445 height: Some(height),
1446 version: Some(version),
1447 merkle_root: Some(merkle_root),
1448 time: Some(time),
1449 nonce: Some(nonce),
1450 solution: Some(solution),
1451 bits: Some(bits),
1452 difficulty: Some(difficulty),
1453 tx,
1454 trees,
1455 chain_supply: block_info
1456 .as_ref()
1457 .map(|d| GetBlockchainInfoBalance::chain_supply(*d.value_pools())),
1458 value_pools: block_info
1459 .map(|d| GetBlockchainInfoBalance::value_pools(*d.value_pools(), delta)),
1460 size: size.map(|size| size as i64),
1461 block_commitments: Some(block_commitments),
1462 final_sapling_root: Some(final_sapling_root),
1463 final_orchard_root,
1464 previous_block_hash: Some(previous_block_hash),
1465 next_block_hash,
1466 })))
1467 } else {
1468 Err("invalid verbosity value").map_error(server::error::LegacyCode::InvalidParameter)
1469 }
1470 }
1471
1472 async fn get_block_header(
1473 &self,
1474 hash_or_height: String,
1475 verbose: Option<bool>,
1476 ) -> Result<GetBlockHeaderResponse> {
1477 let verbose = verbose.unwrap_or(true);
1478 let network = self.network.clone();
1479
1480 let hash_or_height =
1481 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1482 .map_error(server::error::LegacyCode::InvalidParameter)?;
1485 let zebra_state::ReadResponse::BlockHeader {
1486 header,
1487 hash,
1488 height,
1489 next_block_hash,
1490 } = self
1491 .read_state
1492 .clone()
1493 .oneshot(zebra_state::ReadRequest::BlockHeader(hash_or_height))
1494 .await
1495 .map_err(|_| "block height not in best chain")
1496 .map_error(
1497 if hash_or_height.hash().is_some() {
1502 server::error::LegacyCode::InvalidAddressOrKey
1503 } else {
1504 server::error::LegacyCode::InvalidParameter
1505 },
1506 )?
1507 else {
1508 panic!("unexpected response to BlockHeader request")
1509 };
1510
1511 let response = if !verbose {
1512 GetBlockHeaderResponse::Raw(HexData(header.zcash_serialize_to_vec().map_misc_error()?))
1513 } else {
1514 let zebra_state::ReadResponse::SaplingTree(sapling_tree) = self
1515 .read_state
1516 .clone()
1517 .oneshot(zebra_state::ReadRequest::SaplingTree(hash_or_height))
1518 .await
1519 .map_misc_error()?
1520 else {
1521 panic!("unexpected response to SaplingTree request")
1522 };
1523
1524 let sapling_tree = sapling_tree.ok_or_misc_error("missing Sapling tree")?;
1526
1527 let zebra_state::ReadResponse::Depth(depth) = self
1528 .read_state
1529 .clone()
1530 .oneshot(zebra_state::ReadRequest::Depth(hash))
1531 .await
1532 .map_misc_error()?
1533 else {
1534 panic!("unexpected response to SaplingTree request")
1535 };
1536
1537 const NOT_IN_BEST_CHAIN_CONFIRMATIONS: i64 = -1;
1540
1541 let confirmations = depth
1544 .map(|depth| i64::from(depth) + 1)
1545 .unwrap_or(NOT_IN_BEST_CHAIN_CONFIRMATIONS);
1546
1547 let mut nonce = *header.nonce;
1548 nonce.reverse();
1549
1550 let sapling_activation = NetworkUpgrade::Sapling.activation_height(&network);
1551 let sapling_tree_size = sapling_tree.count();
1552 let final_sapling_root: [u8; 32] =
1553 if sapling_activation.is_some() && height >= sapling_activation.unwrap() {
1554 let mut root: [u8; 32] = sapling_tree.root().into();
1555 root.reverse();
1556 root
1557 } else {
1558 [0; 32]
1559 };
1560
1561 let difficulty = header.difficulty_threshold.relative_to_network(&network);
1562
1563 let block_commitments = match header.commitment(&network, height).expect(
1564 "Unexpected failure while parsing the blockcommitments field in get_block_header",
1565 ) {
1566 Commitment::PreSaplingReserved(bytes) => bytes,
1567 Commitment::FinalSaplingRoot(_) => final_sapling_root,
1568 Commitment::ChainHistoryActivationReserved => [0; 32],
1569 Commitment::ChainHistoryRoot(root) => root.bytes_in_display_order(),
1570 Commitment::ChainHistoryBlockTxAuthCommitment(hash) => {
1571 hash.bytes_in_display_order()
1572 }
1573 };
1574
1575 let block_header = BlockHeaderObject {
1576 hash,
1577 confirmations,
1578 height,
1579 version: header.version,
1580 merkle_root: header.merkle_root,
1581 block_commitments,
1582 final_sapling_root,
1583 sapling_tree_size,
1584 time: header.time.timestamp(),
1585 nonce,
1586 solution: header.solution,
1587 bits: header.difficulty_threshold,
1588 difficulty,
1589 previous_block_hash: header.previous_block_hash,
1590 next_block_hash,
1591 };
1592
1593 GetBlockHeaderResponse::Object(Box::new(block_header))
1594 };
1595
1596 Ok(response)
1597 }
1598
1599 fn get_best_block_hash(&self) -> Result<GetBlockHashResponse> {
1600 self.latest_chain_tip
1601 .best_tip_hash()
1602 .map(GetBlockHashResponse)
1603 .ok_or_misc_error("No blocks in state")
1604 }
1605
1606 fn get_best_block_height_and_hash(&self) -> Result<GetBlockHeightAndHashResponse> {
1607 self.latest_chain_tip
1608 .best_tip_height_and_hash()
1609 .map(|(height, hash)| GetBlockHeightAndHashResponse { height, hash })
1610 .ok_or_misc_error("No blocks in state")
1611 }
1612
1613 async fn get_mempool_info(&self) -> Result<GetMempoolInfoResponse> {
1614 let mut mempool = self.mempool.clone();
1615
1616 let response = mempool
1617 .ready()
1618 .and_then(|service| service.call(mempool::Request::QueueStats))
1619 .await
1620 .map_misc_error()?;
1621
1622 if let mempool::Response::QueueStats {
1623 size,
1624 bytes,
1625 usage,
1626 fully_notified,
1627 } = response
1628 {
1629 Ok(GetMempoolInfoResponse {
1630 size,
1631 bytes,
1632 usage,
1633 fully_notified,
1634 })
1635 } else {
1636 unreachable!("unexpected response to QueueStats request")
1637 }
1638 }
1639
1640 async fn get_raw_mempool(&self, verbose: Option<bool>) -> Result<GetRawMempoolResponse> {
1641 #[allow(unused)]
1642 let verbose = verbose.unwrap_or(false);
1643
1644 use zebra_chain::block::MAX_BLOCK_BYTES;
1645
1646 let mut mempool = self.mempool.clone();
1647
1648 let request = if verbose {
1649 mempool::Request::FullTransactions
1650 } else {
1651 mempool::Request::TransactionIds
1652 };
1653
1654 let response = mempool
1656 .ready()
1657 .and_then(|service| service.call(request))
1658 .await
1659 .map_misc_error()?;
1660
1661 match response {
1662 mempool::Response::FullTransactions {
1663 mut transactions,
1664 transaction_dependencies,
1665 last_seen_tip_hash: _,
1666 } => {
1667 if verbose {
1668 let map = transactions
1669 .iter()
1670 .map(|unmined_tx| {
1671 (
1672 unmined_tx.transaction.id.mined_id().encode_hex(),
1673 get_raw_mempool::MempoolObject::from_verified_unmined_tx(
1674 unmined_tx,
1675 &transactions,
1676 &transaction_dependencies,
1677 ),
1678 )
1679 })
1680 .collect::<HashMap<_, _>>();
1681 Ok(GetRawMempoolResponse::Verbose(map))
1682 } else {
1683 transactions.sort_by_cached_key(|tx| {
1688 cmp::Reverse((
1691 i64::from(tx.miner_fee) as u128 * MAX_BLOCK_BYTES as u128
1692 / tx.transaction.size as u128,
1693 tx.transaction.id.mined_id(),
1695 ))
1696 });
1697 let tx_ids: Vec<String> = transactions
1698 .iter()
1699 .map(|unmined_tx| unmined_tx.transaction.id.mined_id().encode_hex())
1700 .collect();
1701
1702 Ok(GetRawMempoolResponse::TxIds(tx_ids))
1703 }
1704 }
1705
1706 mempool::Response::TransactionIds(unmined_transaction_ids) => {
1707 let mut tx_ids: Vec<String> = unmined_transaction_ids
1708 .iter()
1709 .map(|id| id.mined_id().encode_hex())
1710 .collect();
1711
1712 tx_ids.sort();
1714
1715 Ok(GetRawMempoolResponse::TxIds(tx_ids))
1716 }
1717
1718 _ => unreachable!("unmatched response to a transactionids request"),
1719 }
1720 }
1721
1722 async fn get_raw_transaction(
1723 &self,
1724 txid: String,
1725 verbose: Option<u8>,
1726 block_hash: Option<String>,
1727 ) -> Result<GetRawTransactionResponse> {
1728 let mut mempool = self.mempool.clone();
1729 let verbose = verbose.unwrap_or(0) != 0;
1730
1731 let txid = transaction::Hash::from_hex(txid)
1734 .map_error(server::error::LegacyCode::InvalidAddressOrKey)?;
1735
1736 if block_hash.is_none() {
1738 match mempool
1739 .ready()
1740 .and_then(|service| {
1741 service.call(mempool::Request::TransactionsByMinedId([txid].into()))
1742 })
1743 .await
1744 .map_misc_error()?
1745 {
1746 mempool::Response::Transactions(txns) => {
1747 if let Some(tx) = txns.first() {
1748 return Ok(if verbose {
1749 GetRawTransactionResponse::Object(Box::new(
1750 TransactionObject::from_transaction(
1751 tx.transaction.clone(),
1752 None,
1753 None,
1754 &self.network,
1755 None,
1756 None,
1757 Some(false),
1758 txid,
1759 ),
1760 ))
1761 } else {
1762 let hex = tx.transaction.clone().into();
1763 GetRawTransactionResponse::Raw(hex)
1764 });
1765 }
1766 }
1767
1768 _ => unreachable!("unmatched response to a `TransactionsByMinedId` request"),
1769 };
1770 }
1771
1772 let txid = if let Some(block_hash) = block_hash {
1774 let block_hash = block::Hash::from_hex(block_hash)
1775 .map_error(server::error::LegacyCode::InvalidAddressOrKey)?;
1776 match self
1777 .read_state
1778 .clone()
1779 .oneshot(zebra_state::ReadRequest::TransactionIdsForBlock(
1780 block_hash.into(),
1781 ))
1782 .await
1783 .map_misc_error()?
1784 {
1785 zebra_state::ReadResponse::TransactionIdsForBlock(tx_ids) => *tx_ids
1786 .ok_or_error(
1787 server::error::LegacyCode::InvalidAddressOrKey,
1788 "block not found",
1789 )?
1790 .iter()
1791 .find(|id| **id == txid)
1792 .ok_or_error(
1793 server::error::LegacyCode::InvalidAddressOrKey,
1794 "txid not found",
1795 )?,
1796 _ => unreachable!("unmatched response to a `TransactionsByMinedId` request"),
1797 }
1798 } else {
1799 txid
1800 };
1801
1802 match self
1804 .read_state
1805 .clone()
1806 .oneshot(zebra_state::ReadRequest::Transaction(txid))
1807 .await
1808 .map_misc_error()?
1809 {
1810 zebra_state::ReadResponse::Transaction(Some(tx)) => Ok(if verbose {
1811 let block_hash = match self
1812 .read_state
1813 .clone()
1814 .oneshot(zebra_state::ReadRequest::BestChainBlockHash(tx.height))
1815 .await
1816 .map_misc_error()?
1817 {
1818 zebra_state::ReadResponse::BlockHash(block_hash) => block_hash,
1819 _ => unreachable!("unmatched response to a `TransactionsByMinedId` request"),
1820 };
1821
1822 GetRawTransactionResponse::Object(Box::new(TransactionObject::from_transaction(
1823 tx.tx.clone(),
1824 Some(tx.height),
1825 Some(tx.confirmations),
1826 &self.network,
1827 Some(tx.block_time),
1830 block_hash,
1831 Some(true),
1832 txid,
1833 )))
1834 } else {
1835 let hex = tx.tx.into();
1836 GetRawTransactionResponse::Raw(hex)
1837 }),
1838
1839 zebra_state::ReadResponse::Transaction(None) => {
1840 Err("No such mempool or main chain transaction")
1841 .map_error(server::error::LegacyCode::InvalidAddressOrKey)
1842 }
1843
1844 _ => unreachable!("unmatched response to a `Transaction` read request"),
1845 }
1846 }
1847
1848 async fn z_get_treestate(&self, hash_or_height: String) -> Result<GetTreestateResponse> {
1849 let mut read_state = self.read_state.clone();
1850 let network = self.network.clone();
1851
1852 let hash_or_height =
1853 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1854 .map_error(server::error::LegacyCode::InvalidParameter)?;
1857
1858 let block = match read_state
1867 .ready()
1868 .and_then(|service| service.call(zebra_state::ReadRequest::Block(hash_or_height)))
1869 .await
1870 .map_misc_error()?
1871 {
1872 zebra_state::ReadResponse::Block(Some(block)) => block,
1873 zebra_state::ReadResponse::Block(None) => {
1874 return Err("the requested block is not in the main chain")
1877 .map_error(server::error::LegacyCode::InvalidParameter);
1878 }
1879 _ => unreachable!("unmatched response to a block request"),
1880 };
1881
1882 let hash = hash_or_height
1883 .hash_or_else(|_| Some(block.hash()))
1884 .expect("block hash");
1885
1886 let height = hash_or_height
1887 .height_or_else(|_| block.coinbase_height())
1888 .expect("verified blocks have a coinbase height");
1889
1890 let time = u32::try_from(block.header.time.timestamp())
1891 .expect("Timestamps of valid blocks always fit into u32.");
1892
1893 let sapling_nu = zcash_primitives::consensus::NetworkUpgrade::Sapling;
1894 let sapling = if network.is_nu_active(sapling_nu, height.into()) {
1895 match read_state
1896 .ready()
1897 .and_then(|service| {
1898 service.call(zebra_state::ReadRequest::SaplingTree(hash.into()))
1899 })
1900 .await
1901 .map_misc_error()?
1902 {
1903 zebra_state::ReadResponse::SaplingTree(tree) => {
1904 tree.map(|t| (t.to_rpc_bytes(), t.root().bytes_in_display_order().to_vec()))
1905 }
1906 _ => unreachable!("unmatched response to a Sapling tree request"),
1907 }
1908 } else {
1909 None
1910 };
1911 let (sapling_tree, sapling_root) =
1912 sapling.map_or((None, None), |(tree, root)| (Some(tree), Some(root)));
1913
1914 let orchard_nu = zcash_primitives::consensus::NetworkUpgrade::Nu5;
1915 let orchard = if network.is_nu_active(orchard_nu, height.into()) {
1916 match read_state
1917 .ready()
1918 .and_then(|service| {
1919 service.call(zebra_state::ReadRequest::OrchardTree(hash.into()))
1920 })
1921 .await
1922 .map_misc_error()?
1923 {
1924 zebra_state::ReadResponse::OrchardTree(tree) => {
1925 tree.map(|t| (t.to_rpc_bytes(), t.root().bytes_in_display_order().to_vec()))
1926 }
1927 _ => unreachable!("unmatched response to an Orchard tree request"),
1928 }
1929 } else {
1930 None
1931 };
1932 let (orchard_tree, orchard_root) =
1933 orchard.map_or((None, None), |(tree, root)| (Some(tree), Some(root)));
1934
1935 Ok(GetTreestateResponse::new(
1936 hash,
1937 height,
1938 time,
1939 None,
1942 Treestate::new(trees::Commitments::new(sapling_root, sapling_tree)),
1943 Treestate::new(trees::Commitments::new(orchard_root, orchard_tree)),
1944 ))
1945 }
1946
1947 async fn z_get_subtrees_by_index(
1948 &self,
1949 pool: String,
1950 start_index: NoteCommitmentSubtreeIndex,
1951 limit: Option<NoteCommitmentSubtreeIndex>,
1952 ) -> Result<GetSubtreesByIndexResponse> {
1953 let mut read_state = self.read_state.clone();
1954
1955 const POOL_LIST: &[&str] = &["sapling", "orchard"];
1956
1957 if pool == "sapling" {
1958 let request = zebra_state::ReadRequest::SaplingSubtrees { start_index, limit };
1959 let response = read_state
1960 .ready()
1961 .and_then(|service| service.call(request))
1962 .await
1963 .map_misc_error()?;
1964
1965 let subtrees = match response {
1966 zebra_state::ReadResponse::SaplingSubtrees(subtrees) => subtrees,
1967 _ => unreachable!("unmatched response to a subtrees request"),
1968 };
1969
1970 let subtrees = subtrees
1971 .values()
1972 .map(|subtree| SubtreeRpcData {
1973 root: subtree.root.to_bytes().encode_hex(),
1974 end_height: subtree.end_height,
1975 })
1976 .collect();
1977
1978 Ok(GetSubtreesByIndexResponse {
1979 pool,
1980 start_index,
1981 subtrees,
1982 })
1983 } else if pool == "orchard" {
1984 let request = zebra_state::ReadRequest::OrchardSubtrees { start_index, limit };
1985 let response = read_state
1986 .ready()
1987 .and_then(|service| service.call(request))
1988 .await
1989 .map_misc_error()?;
1990
1991 let subtrees = match response {
1992 zebra_state::ReadResponse::OrchardSubtrees(subtrees) => subtrees,
1993 _ => unreachable!("unmatched response to a subtrees request"),
1994 };
1995
1996 let subtrees = subtrees
1997 .values()
1998 .map(|subtree| SubtreeRpcData {
1999 root: subtree.root.encode_hex(),
2000 end_height: subtree.end_height,
2001 })
2002 .collect();
2003
2004 Ok(GetSubtreesByIndexResponse {
2005 pool,
2006 start_index,
2007 subtrees,
2008 })
2009 } else {
2010 Err(ErrorObject::owned(
2011 server::error::LegacyCode::Misc.into(),
2012 format!("invalid pool name, must be one of: {POOL_LIST:?}").as_str(),
2013 None::<()>,
2014 ))
2015 }
2016 }
2017
2018 async fn get_address_tx_ids(&self, request: GetAddressTxIdsRequest) -> Result<Vec<String>> {
2019 let mut read_state = self.read_state.clone();
2020 let latest_chain_tip = self.latest_chain_tip.clone();
2021
2022 let height_range = build_height_range(
2023 request.start,
2024 request.end,
2025 best_chain_tip_height(&latest_chain_tip)?,
2026 )?;
2027
2028 let valid_addresses = request.valid_addresses()?;
2029
2030 let request = zebra_state::ReadRequest::TransactionIdsByAddresses {
2031 addresses: valid_addresses,
2032 height_range,
2033 };
2034 let response = read_state
2035 .ready()
2036 .and_then(|service| service.call(request))
2037 .await
2038 .map_misc_error()?;
2039
2040 let hashes = match response {
2041 zebra_state::ReadResponse::AddressesTransactionIds(hashes) => {
2042 let mut last_tx_location = TransactionLocation::from_usize(Height(0), 0);
2043
2044 hashes
2045 .iter()
2046 .map(|(tx_loc, tx_id)| {
2047 assert!(
2049 *tx_loc > last_tx_location,
2050 "Transactions were not in chain order:\n\
2051 {tx_loc:?} {tx_id:?} was after:\n\
2052 {last_tx_location:?}",
2053 );
2054
2055 last_tx_location = *tx_loc;
2056
2057 tx_id.to_string()
2058 })
2059 .collect()
2060 }
2061 _ => unreachable!("unmatched response to a TransactionsByAddresses request"),
2062 };
2063
2064 Ok(hashes)
2065 }
2066
2067 async fn get_address_utxos(
2068 &self,
2069 utxos_request: GetAddressUtxosRequest,
2070 ) -> Result<GetAddressUtxosResponse> {
2071 let mut read_state = self.read_state.clone();
2072 let mut response_utxos = vec![];
2073
2074 let valid_addresses = utxos_request.valid_addresses()?;
2075
2076 let request = zebra_state::ReadRequest::UtxosByAddresses(valid_addresses);
2078 let response = read_state
2079 .ready()
2080 .and_then(|service| service.call(request))
2081 .await
2082 .map_misc_error()?;
2083 let utxos = match response {
2084 zebra_state::ReadResponse::AddressUtxos(utxos) => utxos,
2085 _ => unreachable!("unmatched response to a UtxosByAddresses request"),
2086 };
2087
2088 let mut last_output_location = OutputLocation::from_usize(Height(0), 0, 0);
2089
2090 for utxo_data in utxos.utxos() {
2091 let address = utxo_data.0;
2092 let txid = *utxo_data.1;
2093 let height = utxo_data.2.height();
2094 let output_index = utxo_data.2.output_index();
2095 let script = utxo_data.3.lock_script.clone();
2096 let satoshis = u64::from(utxo_data.3.value);
2097
2098 let output_location = *utxo_data.2;
2099 assert!(
2101 output_location > last_output_location,
2102 "UTXOs were not in chain order:\n\
2103 {output_location:?} {address:?} {txid:?} was after:\n\
2104 {last_output_location:?}",
2105 );
2106
2107 let entry = Utxo {
2108 address,
2109 txid,
2110 output_index,
2111 script,
2112 satoshis,
2113 height,
2114 };
2115 response_utxos.push(entry);
2116
2117 last_output_location = output_location;
2118 }
2119
2120 if !utxos_request.chain_info {
2121 Ok(GetAddressUtxosResponse::Utxos(response_utxos))
2122 } else {
2123 let (height, hash) = utxos
2124 .last_height_and_hash()
2125 .ok_or_misc_error("No blocks in state")?;
2126
2127 Ok(GetAddressUtxosResponse::UtxosAndChainInfo(
2128 GetAddressUtxosResponseObject {
2129 utxos: response_utxos,
2130 hash,
2131 height,
2132 },
2133 ))
2134 }
2135 }
2136
2137 fn stop(&self) -> Result<String> {
2138 #[cfg(not(target_os = "windows"))]
2139 if self.network.is_regtest() {
2140 match nix::sys::signal::raise(nix::sys::signal::SIGINT) {
2141 Ok(_) => Ok("Zebra server stopping".to_string()),
2142 Err(error) => Err(ErrorObject::owned(
2143 ErrorCode::InternalError.code(),
2144 format!("Failed to shut down: {error}").as_str(),
2145 None::<()>,
2146 )),
2147 }
2148 } else {
2149 Err(ErrorObject::borrowed(
2150 ErrorCode::MethodNotFound.code(),
2151 "stop is only available on regtest networks",
2152 None,
2153 ))
2154 }
2155 #[cfg(target_os = "windows")]
2156 Err(ErrorObject::borrowed(
2157 ErrorCode::MethodNotFound.code(),
2158 "stop is not available in windows targets",
2159 None,
2160 ))
2161 }
2162
2163 fn get_block_count(&self) -> Result<u32> {
2164 best_chain_tip_height(&self.latest_chain_tip).map(|height| height.0)
2165 }
2166
2167 async fn get_block_hash(&self, index: i32) -> Result<GetBlockHashResponse> {
2168 let mut read_state = self.read_state.clone();
2169 let latest_chain_tip = self.latest_chain_tip.clone();
2170
2171 let tip_height = best_chain_tip_height(&latest_chain_tip)?;
2173
2174 let height = height_from_signed_int(index, tip_height)?;
2175
2176 let request = zebra_state::ReadRequest::BestChainBlockHash(height);
2177 let response = read_state
2178 .ready()
2179 .and_then(|service| service.call(request))
2180 .await
2181 .map_error(server::error::LegacyCode::default())?;
2182
2183 match response {
2184 zebra_state::ReadResponse::BlockHash(Some(hash)) => Ok(GetBlockHashResponse(hash)),
2185 zebra_state::ReadResponse::BlockHash(None) => Err(ErrorObject::borrowed(
2186 server::error::LegacyCode::InvalidParameter.into(),
2187 "Block not found",
2188 None,
2189 )),
2190 _ => unreachable!("unmatched response to a block request"),
2191 }
2192 }
2193
2194 async fn get_block_template(
2195 &self,
2196 parameters: Option<GetBlockTemplateParameters>,
2197 ) -> Result<GetBlockTemplateResponse> {
2198 use types::get_block_template::{
2199 check_parameters, check_synced_to_tip, fetch_mempool_transactions,
2200 fetch_state_tip_and_local_time, validate_block_proposal,
2201 zip317::select_mempool_transactions,
2202 };
2203
2204 let network = self.network.clone();
2206 let extra_coinbase_data = self.gbt.extra_coinbase_data();
2207
2208 let mempool = self.mempool.clone();
2210 let mut latest_chain_tip = self.latest_chain_tip.clone();
2211 let sync_status = self.gbt.sync_status();
2212 let read_state = self.read_state.clone();
2213
2214 if let Some(HexData(block_proposal_bytes)) = parameters
2215 .as_ref()
2216 .and_then(GetBlockTemplateParameters::block_proposal_data)
2217 {
2218 return validate_block_proposal(
2219 self.gbt.block_verifier_router(),
2220 block_proposal_bytes,
2221 network,
2222 latest_chain_tip,
2223 sync_status,
2224 )
2225 .await;
2226 }
2227
2228 check_parameters(¶meters)?;
2230
2231 let client_long_poll_id = parameters.as_ref().and_then(|params| params.long_poll_id);
2232
2233 let miner_address = self
2234 .gbt
2235 .miner_address()
2236 .ok_or_misc_error("miner_address not configured")?;
2237
2238 let mut max_time_reached = false;
2242
2243 let (
2246 server_long_poll_id,
2247 chain_tip_and_local_time,
2248 mempool_txs,
2249 mempool_tx_deps,
2250 submit_old,
2251 ) = loop {
2252 check_synced_to_tip(&network, latest_chain_tip.clone(), sync_status.clone())?;
2258 latest_chain_tip.mark_best_tip_seen();
2266
2267 let chain_tip_and_local_time @ zebra_state::GetBlockTemplateChainInfo {
2274 tip_hash,
2275 tip_height,
2276 max_time,
2277 cur_time,
2278 ..
2279 } = fetch_state_tip_and_local_time(read_state.clone()).await?;
2280
2281 let Some((mempool_txs, mempool_tx_deps)) =
2292 fetch_mempool_transactions(mempool.clone(), tip_hash)
2293 .await?
2294 .or_else(|| client_long_poll_id.is_none().then(Default::default))
2298 else {
2299 continue;
2300 };
2301
2302 let server_long_poll_id = LongPollInput::new(
2304 tip_height,
2305 tip_hash,
2306 max_time,
2307 mempool_txs.iter().map(|tx| tx.transaction.id),
2308 )
2309 .generate_id();
2310
2311 if Some(&server_long_poll_id) != client_long_poll_id.as_ref() || max_time_reached {
2316 let mut submit_old = client_long_poll_id
2317 .as_ref()
2318 .map(|old_long_poll_id| server_long_poll_id.submit_old(old_long_poll_id));
2319
2320 if max_time_reached {
2325 submit_old = Some(false);
2326 }
2327
2328 break (
2329 server_long_poll_id,
2330 chain_tip_and_local_time,
2331 mempool_txs,
2332 mempool_tx_deps,
2333 submit_old,
2334 );
2335 }
2336
2337 let wait_for_mempool_request =
2347 tokio::time::sleep(Duration::from_secs(MEMPOOL_LONG_POLL_INTERVAL));
2348
2349 let mut wait_for_best_tip_change = latest_chain_tip.clone();
2352 let wait_for_best_tip_change = wait_for_best_tip_change.best_tip_changed();
2353
2354 let duration_until_max_time = max_time.saturating_duration_since(cur_time);
2366 let wait_for_max_time: OptionFuture<_> = if duration_until_max_time.seconds() > 0 {
2367 Some(tokio::time::sleep(duration_until_max_time.to_std()))
2368 } else {
2369 None
2370 }
2371 .into();
2372
2373 tokio::select! {
2380 biased;
2383
2384 _elapsed = wait_for_mempool_request => {
2386 tracing::debug!(
2387 ?max_time,
2388 ?cur_time,
2389 ?server_long_poll_id,
2390 ?client_long_poll_id,
2391 MEMPOOL_LONG_POLL_INTERVAL,
2392 "checking for a new mempool change after waiting a few seconds"
2393 );
2394 }
2395
2396 tip_changed_result = wait_for_best_tip_change => {
2398 match tip_changed_result {
2399 Ok(()) => {
2400 latest_chain_tip.mark_best_tip_seen();
2404
2405 let new_tip_hash = latest_chain_tip.best_tip_hash();
2406 if new_tip_hash == Some(tip_hash) {
2407 tracing::debug!(
2408 ?max_time,
2409 ?cur_time,
2410 ?server_long_poll_id,
2411 ?client_long_poll_id,
2412 ?tip_hash,
2413 ?tip_height,
2414 "ignoring spurious state change notification"
2415 );
2416
2417 tokio::time::sleep(Duration::from_secs(
2419 MEMPOOL_LONG_POLL_INTERVAL,
2420 )).await;
2421
2422 continue;
2423 }
2424
2425 tracing::debug!(
2426 ?max_time,
2427 ?cur_time,
2428 ?server_long_poll_id,
2429 ?client_long_poll_id,
2430 "returning from long poll because state has changed"
2431 );
2432 }
2433
2434 Err(recv_error) => {
2435 tracing::info!(
2437 ?recv_error,
2438 ?max_time,
2439 ?cur_time,
2440 ?server_long_poll_id,
2441 ?client_long_poll_id,
2442 "returning from long poll due to a state error.\
2443 Is Zebra shutting down?"
2444 );
2445
2446 return Err(recv_error).map_error(server::error::LegacyCode::default());
2447 }
2448 }
2449 }
2450
2451 Some(_elapsed) = wait_for_max_time => {
2454 tracing::info!(
2456 ?max_time,
2457 ?cur_time,
2458 ?server_long_poll_id,
2459 ?client_long_poll_id,
2460 "returning from long poll because max time was reached"
2461 );
2462
2463 max_time_reached = true;
2464 }
2465 }
2466 };
2467
2468 let next_block_height =
2476 (chain_tip_and_local_time.tip_height + 1).expect("tip is far below Height::MAX");
2477
2478 tracing::debug!(
2479 mempool_tx_hashes = ?mempool_txs
2480 .iter()
2481 .map(|tx| tx.transaction.id.mined_id())
2482 .collect::<Vec<_>>(),
2483 "selecting transactions for the template from the mempool"
2484 );
2485
2486 let mempool_txs = select_mempool_transactions(
2488 &network,
2489 next_block_height,
2490 &miner_address,
2491 mempool_txs,
2492 mempool_tx_deps,
2493 extra_coinbase_data.clone(),
2494 );
2495
2496 tracing::debug!(
2497 selected_mempool_tx_hashes = ?mempool_txs
2498 .iter()
2499 .map(|#[cfg(not(test))] tx, #[cfg(test)] (_, tx)| tx.transaction.id.mined_id())
2500 .collect::<Vec<_>>(),
2501 "selected transactions for the template from the mempool"
2502 );
2503
2504 let response = BlockTemplateResponse::new_internal(
2507 &network,
2508 &miner_address,
2509 &chain_tip_and_local_time,
2510 server_long_poll_id,
2511 mempool_txs,
2512 submit_old,
2513 extra_coinbase_data,
2514 );
2515
2516 Ok(response.into())
2517 }
2518
2519 async fn submit_block(
2520 &self,
2521 HexData(block_bytes): HexData,
2522 _parameters: Option<SubmitBlockParameters>,
2523 ) -> Result<SubmitBlockResponse> {
2524 let mut block_verifier_router = self.gbt.block_verifier_router();
2525
2526 let block: Block = match block_bytes.zcash_deserialize_into() {
2527 Ok(block_bytes) => block_bytes,
2528 Err(error) => {
2529 tracing::info!(
2530 ?error,
2531 "submit block failed: block bytes could not be deserialized into a structurally valid block"
2532 );
2533
2534 return Ok(SubmitBlockErrorResponse::Rejected.into());
2535 }
2536 };
2537
2538 let height = block
2539 .coinbase_height()
2540 .ok_or_error(0, "coinbase height not found")?;
2541 let block_hash = block.hash();
2542
2543 let block_verifier_router_response = block_verifier_router
2544 .ready()
2545 .await
2546 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?
2547 .call(zebra_consensus::Request::Commit(Arc::new(block)))
2548 .await;
2549
2550 let chain_error = match block_verifier_router_response {
2551 Ok(hash) => {
2558 tracing::info!(?hash, ?height, "submit block accepted");
2559
2560 self.gbt
2561 .advertise_mined_block(hash, height)
2562 .map_error_with_prefix(0, "failed to send mined block")?;
2563
2564 return Ok(SubmitBlockResponse::Accepted);
2565 }
2566
2567 Err(box_error) => {
2570 let error = box_error
2571 .downcast::<RouterError>()
2572 .map(|boxed_chain_error| *boxed_chain_error);
2573
2574 tracing::info!(
2575 ?error,
2576 ?block_hash,
2577 ?height,
2578 "submit block failed verification"
2579 );
2580
2581 error
2582 }
2583 };
2584
2585 let response = match chain_error {
2586 Ok(source) if source.is_duplicate_request() => SubmitBlockErrorResponse::Duplicate,
2587
2588 Ok(_verify_chain_error) => SubmitBlockErrorResponse::Rejected,
2604
2605 Err(_unknown_error_type) => SubmitBlockErrorResponse::Rejected,
2608 };
2609
2610 Ok(response.into())
2611 }
2612
2613 async fn get_mining_info(&self) -> Result<GetMiningInfoResponse> {
2614 let network = self.network.clone();
2615 let mut read_state = self.read_state.clone();
2616
2617 let chain_tip = self.latest_chain_tip.clone();
2618 let tip_height = chain_tip.best_tip_height().unwrap_or(Height(0)).0;
2619
2620 let mut current_block_tx = None;
2621 if tip_height > 0 {
2622 let mined_tx_ids = chain_tip.best_tip_mined_transaction_ids();
2623 current_block_tx =
2624 (!mined_tx_ids.is_empty()).then(|| mined_tx_ids.len().saturating_sub(1));
2625 }
2626
2627 let solution_rate_fut = self.get_network_sol_ps(None, None);
2628 let mut current_block_size = None;
2630 if tip_height > 0 {
2631 let request = zebra_state::ReadRequest::TipBlockSize;
2632 let response: zebra_state::ReadResponse = read_state
2633 .ready()
2634 .and_then(|service| service.call(request))
2635 .await
2636 .map_error(server::error::LegacyCode::default())?;
2637 current_block_size = match response {
2638 zebra_state::ReadResponse::TipBlockSize(Some(block_size)) => Some(block_size),
2639 _ => None,
2640 };
2641 }
2642
2643 Ok(GetMiningInfoResponse::new_internal(
2644 tip_height,
2645 current_block_size,
2646 current_block_tx,
2647 network,
2648 solution_rate_fut.await?,
2649 ))
2650 }
2651
2652 async fn get_network_sol_ps(
2653 &self,
2654 num_blocks: Option<i32>,
2655 height: Option<i32>,
2656 ) -> Result<u64> {
2657 let mut num_blocks = num_blocks.unwrap_or(DEFAULT_SOLUTION_RATE_WINDOW_SIZE);
2659 if num_blocks < 1 {
2661 num_blocks = i32::try_from(POW_AVERAGING_WINDOW).expect("fits in i32");
2662 }
2663 let num_blocks =
2664 usize::try_from(num_blocks).expect("just checked for negatives, i32 fits in usize");
2665
2666 let height = height.and_then(|height| height.try_into_height().ok());
2669
2670 let mut read_state = self.read_state.clone();
2671
2672 let request = ReadRequest::SolutionRate { num_blocks, height };
2673
2674 let response = read_state
2675 .ready()
2676 .and_then(|service| service.call(request))
2677 .await
2678 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
2679
2680 let solution_rate = match response {
2681 ReadResponse::SolutionRate(solution_rate) => solution_rate.unwrap_or(0),
2683
2684 _ => unreachable!("unmatched response to a solution rate request"),
2685 };
2686
2687 Ok(solution_rate
2688 .try_into()
2689 .expect("per-second solution rate always fits in u64"))
2690 }
2691
2692 async fn get_peer_info(&self) -> Result<Vec<PeerInfo>> {
2693 let address_book = self.address_book.clone();
2694 Ok(address_book
2695 .recently_live_peers(chrono::Utc::now())
2696 .into_iter()
2697 .map(PeerInfo::from)
2698 .collect())
2699 }
2700
2701 async fn validate_address(&self, raw_address: String) -> Result<ValidateAddressResponse> {
2702 let network = self.network.clone();
2703
2704 validate_address(network, raw_address)
2705 }
2706
2707 async fn z_validate_address(&self, raw_address: String) -> Result<ZValidateAddressResponse> {
2708 let network = self.network.clone();
2709
2710 z_validate_address(network, raw_address)
2711 }
2712
2713 async fn get_block_subsidy(&self, height: Option<u32>) -> Result<GetBlockSubsidyResponse> {
2714 let latest_chain_tip = self.latest_chain_tip.clone();
2715 let network = self.network.clone();
2716
2717 let height = if let Some(height) = height {
2718 Height(height)
2719 } else {
2720 best_chain_tip_height(&latest_chain_tip)?
2721 };
2722
2723 if height < network.height_for_first_halving() {
2724 return Err(ErrorObject::borrowed(
2725 0,
2726 "Zebra does not support founders' reward subsidies, \
2727 use a block height that is after the first halving",
2728 None,
2729 ));
2730 }
2731
2732 let founders = Amount::zero();
2734
2735 let total_block_subsidy =
2736 block_subsidy(height, &network).map_error(server::error::LegacyCode::default())?;
2737 let miner_subsidy = miner_subsidy(height, &network, total_block_subsidy)
2738 .map_error(server::error::LegacyCode::default())?;
2739
2740 let (lockbox_streams, mut funding_streams): (Vec<_>, Vec<_>) =
2741 funding_stream_values(height, &network, total_block_subsidy)
2742 .map_error(server::error::LegacyCode::default())?
2743 .into_iter()
2744 .partition(|(receiver, _)| matches!(receiver, FundingStreamReceiver::Deferred));
2746
2747 let is_nu6 = NetworkUpgrade::current(&network, height) == NetworkUpgrade::Nu6;
2748
2749 let [lockbox_total, funding_streams_total]: [std::result::Result<
2750 Amount<NonNegative>,
2751 amount::Error,
2752 >; 2] = [&lockbox_streams, &funding_streams]
2753 .map(|streams| streams.iter().map(|&(_, amount)| amount).sum());
2754
2755 funding_streams.sort_by_key(|(receiver, _funding_stream)| {
2757 ZCASHD_FUNDING_STREAM_ORDER
2758 .iter()
2759 .position(|zcashd_receiver| zcashd_receiver == receiver)
2760 });
2761
2762 let [funding_streams, lockbox_streams]: [Vec<_>; 2] = [funding_streams, lockbox_streams]
2764 .map(|streams| {
2765 streams
2766 .into_iter()
2767 .map(|(receiver, value)| {
2768 let address = funding_stream_address(height, &network, receiver);
2769 types::subsidy::FundingStream::new_internal(
2770 is_nu6, receiver, value, address,
2771 )
2772 })
2773 .collect()
2774 });
2775
2776 Ok(GetBlockSubsidyResponse {
2777 miner: miner_subsidy.into(),
2778 founders: founders.into(),
2779 funding_streams,
2780 lockbox_streams,
2781 funding_streams_total: funding_streams_total
2782 .map_error(server::error::LegacyCode::default())?
2783 .into(),
2784 lockbox_total: lockbox_total
2785 .map_error(server::error::LegacyCode::default())?
2786 .into(),
2787 total_block_subsidy: total_block_subsidy.into(),
2788 })
2789 }
2790
2791 async fn get_difficulty(&self) -> Result<f64> {
2792 chain_tip_difficulty(self.network.clone(), self.read_state.clone(), false).await
2793 }
2794
2795 async fn z_list_unified_receivers(
2796 &self,
2797 address: String,
2798 ) -> Result<ZListUnifiedReceiversResponse> {
2799 use zcash_address::unified::Container;
2800
2801 let (network, unified_address): (
2802 zcash_protocol::consensus::NetworkType,
2803 zcash_address::unified::Address,
2804 ) = zcash_address::unified::Encoding::decode(address.clone().as_str())
2805 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
2806
2807 let mut p2pkh = None;
2808 let mut p2sh = None;
2809 let mut orchard = None;
2810 let mut sapling = None;
2811
2812 for item in unified_address.items() {
2813 match item {
2814 zcash_address::unified::Receiver::Orchard(_data) => {
2815 let addr = zcash_address::unified::Address::try_from_items(vec![item])
2816 .expect("using data already decoded as valid");
2817 orchard = Some(addr.encode(&network));
2818 }
2819 zcash_address::unified::Receiver::Sapling(data) => {
2820 let addr = zebra_chain::primitives::Address::try_from_sapling(network, data)
2821 .expect("using data already decoded as valid");
2822 sapling = Some(addr.payment_address().unwrap_or_default());
2823 }
2824 zcash_address::unified::Receiver::P2pkh(data) => {
2825 let addr =
2826 zebra_chain::primitives::Address::try_from_transparent_p2pkh(network, data)
2827 .expect("using data already decoded as valid");
2828 p2pkh = Some(addr.payment_address().unwrap_or_default());
2829 }
2830 zcash_address::unified::Receiver::P2sh(data) => {
2831 let addr =
2832 zebra_chain::primitives::Address::try_from_transparent_p2sh(network, data)
2833 .expect("using data already decoded as valid");
2834 p2sh = Some(addr.payment_address().unwrap_or_default());
2835 }
2836 _ => (),
2837 }
2838 }
2839
2840 Ok(ZListUnifiedReceiversResponse::new(
2841 orchard, sapling, p2pkh, p2sh,
2842 ))
2843 }
2844
2845 async fn invalidate_block(&self, block_hash: block::Hash) -> Result<()> {
2846 self.state
2847 .clone()
2848 .oneshot(zebra_state::Request::InvalidateBlock(block_hash))
2849 .await
2850 .map(|rsp| assert_eq!(rsp, zebra_state::Response::Invalidated(block_hash)))
2851 .map_misc_error()
2852 }
2853
2854 async fn reconsider_block(&self, block_hash: block::Hash) -> Result<Vec<block::Hash>> {
2855 self.state
2856 .clone()
2857 .oneshot(zebra_state::Request::ReconsiderBlock(block_hash))
2858 .await
2859 .map(|rsp| match rsp {
2860 zebra_state::Response::Reconsidered(block_hashes) => block_hashes,
2861 _ => unreachable!("unmatched response to a reconsider block request"),
2862 })
2863 .map_misc_error()
2864 }
2865
2866 async fn generate(&self, num_blocks: u32) -> Result<Vec<Hash>> {
2867 let rpc = self.clone();
2868 let network = self.network.clone();
2869
2870 if !network.disable_pow() {
2871 return Err(ErrorObject::borrowed(
2872 0,
2873 "generate is only supported on networks where PoW is disabled",
2874 None,
2875 ));
2876 }
2877
2878 let mut block_hashes = Vec::new();
2879 for _ in 0..num_blocks {
2880 let block_template = rpc
2881 .get_block_template(None)
2882 .await
2883 .map_error(server::error::LegacyCode::default())?;
2884
2885 let GetBlockTemplateResponse::TemplateMode(block_template) = block_template else {
2886 return Err(ErrorObject::borrowed(
2887 0,
2888 "error generating block template",
2889 None,
2890 ));
2891 };
2892
2893 let proposal_block = proposal_block_from_template(
2894 &block_template,
2895 BlockTemplateTimeSource::CurTime,
2896 &network,
2897 )
2898 .map_error(server::error::LegacyCode::default())?;
2899
2900 let hex_proposal_block = HexData(
2901 proposal_block
2902 .zcash_serialize_to_vec()
2903 .map_error(server::error::LegacyCode::default())?,
2904 );
2905
2906 rpc.submit_block(hex_proposal_block, None)
2907 .await
2908 .map_error(server::error::LegacyCode::default())?;
2909
2910 block_hashes.push(GetBlockHashResponse(proposal_block.hash()));
2911 }
2912
2913 Ok(block_hashes)
2914 }
2915
2916 async fn add_node(
2917 &self,
2918 addr: zebra_network::PeerSocketAddr,
2919 command: AddNodeCommand,
2920 ) -> Result<()> {
2921 if self.network.is_regtest() {
2922 match command {
2923 AddNodeCommand::Add => {
2924 tracing::info!(?addr, "adding peer address to the address book");
2925 if self.address_book.clone().add_peer(addr) {
2926 Ok(())
2927 } else {
2928 return Err(ErrorObject::owned(
2929 ErrorCode::InvalidParams.code(),
2930 format!("peer address was already present in the address book: {addr}"),
2931 None::<()>,
2932 ));
2933 }
2934 }
2935 }
2936 } else {
2937 return Err(ErrorObject::owned(
2938 ErrorCode::InvalidParams.code(),
2939 "addnode command is only supported on regtest",
2940 None::<()>,
2941 ));
2942 }
2943 }
2944}
2945
2946pub fn best_chain_tip_height<Tip>(latest_chain_tip: &Tip) -> Result<Height>
2951where
2952 Tip: ChainTip + Clone + Send + Sync + 'static,
2953{
2954 latest_chain_tip
2955 .best_tip_height()
2956 .ok_or_misc_error("No blocks in state")
2957}
2958
2959#[allow(clippy::too_many_arguments)]
2963#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
2964pub struct GetInfoResponse {
2965 #[getter(rename = "raw_version")]
2967 version: u64,
2968
2969 build: String,
2971
2972 subversion: String,
2974
2975 #[serde(rename = "protocolversion")]
2977 protocol_version: u32,
2978
2979 blocks: u32,
2981
2982 connections: usize,
2984
2985 #[serde(skip_serializing_if = "Option::is_none")]
2987 proxy: Option<String>,
2988
2989 difficulty: f64,
2991
2992 testnet: bool,
2994
2995 #[serde(rename = "paytxfee")]
2997 pay_tx_fee: f64,
2998
2999 #[serde(rename = "relayfee")]
3001 relay_fee: f64,
3002
3003 errors: String,
3005
3006 #[serde(rename = "errorstimestamp")]
3008 errors_timestamp: String,
3009}
3010
3011#[deprecated(note = "Use `GetInfoResponse` instead")]
3012pub use self::GetInfoResponse as GetInfo;
3013
3014impl Default for GetInfoResponse {
3015 fn default() -> Self {
3016 GetInfoResponse {
3017 version: 0,
3018 build: "some build version".to_string(),
3019 subversion: "some subversion".to_string(),
3020 protocol_version: 0,
3021 blocks: 0,
3022 connections: 0,
3023 proxy: None,
3024 difficulty: 0.0,
3025 testnet: false,
3026 pay_tx_fee: 0.0,
3027 relay_fee: 0.0,
3028 errors: "no errors".to_string(),
3029 errors_timestamp: "no errors timestamp".to_string(),
3030 }
3031 }
3032}
3033
3034impl GetInfoResponse {
3035 #[allow(clippy::too_many_arguments)]
3037 #[deprecated(note = "Use `GetInfoResponse::new` instead")]
3038 pub fn from_parts(
3039 version: u64,
3040 build: String,
3041 subversion: String,
3042 protocol_version: u32,
3043 blocks: u32,
3044 connections: usize,
3045 proxy: Option<String>,
3046 difficulty: f64,
3047 testnet: bool,
3048 pay_tx_fee: f64,
3049 relay_fee: f64,
3050 errors: String,
3051 errors_timestamp: String,
3052 ) -> Self {
3053 Self {
3054 version,
3055 build,
3056 subversion,
3057 protocol_version,
3058 blocks,
3059 connections,
3060 proxy,
3061 difficulty,
3062 testnet,
3063 pay_tx_fee,
3064 relay_fee,
3065 errors,
3066 errors_timestamp,
3067 }
3068 }
3069
3070 pub fn into_parts(
3072 self,
3073 ) -> (
3074 u64,
3075 String,
3076 String,
3077 u32,
3078 u32,
3079 usize,
3080 Option<String>,
3081 f64,
3082 bool,
3083 f64,
3084 f64,
3085 String,
3086 String,
3087 ) {
3088 (
3089 self.version,
3090 self.build,
3091 self.subversion,
3092 self.protocol_version,
3093 self.blocks,
3094 self.connections,
3095 self.proxy,
3096 self.difficulty,
3097 self.testnet,
3098 self.pay_tx_fee,
3099 self.relay_fee,
3100 self.errors,
3101 self.errors_timestamp,
3102 )
3103 }
3104
3105 fn version_from_string(build_string: &str) -> Option<u64> {
3107 let semver_version = semver::Version::parse(build_string.strip_prefix('v')?).ok()?;
3108 let build_number = semver_version
3109 .build
3110 .as_str()
3111 .split('.')
3112 .next()
3113 .and_then(|num_str| num_str.parse::<u64>().ok())
3114 .unwrap_or_default();
3115
3116 let version_number = 1_000_000 * semver_version.major
3118 + 10_000 * semver_version.minor
3119 + 100 * semver_version.patch
3120 + build_number;
3121
3122 Some(version_number)
3123 }
3124}
3125
3126pub type BlockchainValuePoolBalances = [GetBlockchainInfoBalance; 5];
3128
3129#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters)]
3133pub struct GetBlockchainInfoResponse {
3134 chain: String,
3136
3137 #[getter(copy)]
3139 blocks: Height,
3140
3141 #[getter(copy)]
3144 headers: Height,
3145
3146 difficulty: f64,
3148
3149 #[serde(rename = "verificationprogress")]
3151 verification_progress: f64,
3152
3153 #[serde(rename = "chainwork")]
3155 chain_work: u64,
3156
3157 pruned: bool,
3159
3160 size_on_disk: u64,
3162
3163 commitments: u64,
3165
3166 #[serde(rename = "bestblockhash", with = "hex")]
3168 #[getter(copy)]
3169 best_block_hash: block::Hash,
3170
3171 #[serde(rename = "estimatedheight")]
3175 #[getter(copy)]
3176 estimated_height: Height,
3177
3178 #[serde(rename = "chainSupply")]
3180 chain_supply: GetBlockchainInfoBalance,
3181
3182 #[serde(rename = "valuePools")]
3184 value_pools: BlockchainValuePoolBalances,
3185
3186 upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
3188
3189 #[getter(copy)]
3191 consensus: TipConsensusBranch,
3192}
3193
3194impl Default for GetBlockchainInfoResponse {
3195 fn default() -> Self {
3196 Self {
3197 chain: "main".to_string(),
3198 blocks: Height(1),
3199 best_block_hash: block::Hash([0; 32]),
3200 estimated_height: Height(1),
3201 chain_supply: GetBlockchainInfoBalance::chain_supply(Default::default()),
3202 value_pools: GetBlockchainInfoBalance::zero_pools(),
3203 upgrades: IndexMap::new(),
3204 consensus: TipConsensusBranch {
3205 chain_tip: ConsensusBranchIdHex(ConsensusBranchId::default()),
3206 next_block: ConsensusBranchIdHex(ConsensusBranchId::default()),
3207 },
3208 headers: Height(1),
3209 difficulty: 0.0,
3210 verification_progress: 0.0,
3211 chain_work: 0,
3212 pruned: false,
3213 size_on_disk: 0,
3214 commitments: 0,
3215 }
3216 }
3217}
3218
3219impl GetBlockchainInfoResponse {
3220 #[allow(clippy::too_many_arguments)]
3224 pub fn new(
3225 chain: String,
3226 blocks: Height,
3227 best_block_hash: block::Hash,
3228 estimated_height: Height,
3229 chain_supply: GetBlockchainInfoBalance,
3230 value_pools: BlockchainValuePoolBalances,
3231 upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
3232 consensus: TipConsensusBranch,
3233 headers: Height,
3234 difficulty: f64,
3235 verification_progress: f64,
3236 chain_work: u64,
3237 pruned: bool,
3238 size_on_disk: u64,
3239 commitments: u64,
3240 ) -> Self {
3241 Self {
3242 chain,
3243 blocks,
3244 best_block_hash,
3245 estimated_height,
3246 chain_supply,
3247 value_pools,
3248 upgrades,
3249 consensus,
3250 headers,
3251 difficulty,
3252 verification_progress,
3253 chain_work,
3254 pruned,
3255 size_on_disk,
3256 commitments,
3257 }
3258 }
3259}
3260
3261#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
3263#[serde(from = "DGetAddressBalanceRequest")]
3264pub struct GetAddressBalanceRequest {
3265 addresses: Vec<String>,
3267}
3268
3269impl From<DGetAddressBalanceRequest> for GetAddressBalanceRequest {
3270 fn from(address_strings: DGetAddressBalanceRequest) -> Self {
3271 match address_strings {
3272 DGetAddressBalanceRequest::Addresses { addresses } => {
3273 GetAddressBalanceRequest { addresses }
3274 }
3275 DGetAddressBalanceRequest::Address(address) => GetAddressBalanceRequest {
3276 addresses: vec![address],
3277 },
3278 }
3279 }
3280}
3281
3282#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize)]
3284#[serde(untagged)]
3285enum DGetAddressBalanceRequest {
3286 Addresses { addresses: Vec<String> },
3288 Address(String),
3290}
3291
3292#[deprecated(note = "Use `GetAddressBalanceRequest` instead.")]
3294pub type AddressStrings = GetAddressBalanceRequest;
3295
3296trait ValidateAddresses {
3297 fn valid_addresses(&self) -> Result<HashSet<Address>> {
3301 let valid_addresses: HashSet<Address> = self
3304 .addresses()
3305 .iter()
3306 .map(|address| {
3307 address
3308 .parse()
3309 .map_error(server::error::LegacyCode::InvalidAddressOrKey)
3310 })
3311 .collect::<Result<_>>()?;
3312
3313 Ok(valid_addresses)
3314 }
3315
3316 fn addresses(&self) -> &[String];
3318}
3319
3320impl ValidateAddresses for GetAddressBalanceRequest {
3321 fn addresses(&self) -> &[String] {
3322 &self.addresses
3323 }
3324}
3325
3326impl GetAddressBalanceRequest {
3327 pub fn new(addresses: Vec<String>) -> GetAddressBalanceRequest {
3329 GetAddressBalanceRequest { addresses }
3330 }
3331
3332 #[deprecated(
3334 note = "Use `AddressStrings::new` instead. Validity will be checked by the server."
3335 )]
3336 pub fn new_valid(addresses: Vec<String>) -> Result<GetAddressBalanceRequest> {
3337 let req = Self { addresses };
3338 req.valid_addresses()?;
3339 Ok(req)
3340 }
3341}
3342
3343#[derive(
3345 Clone,
3346 Copy,
3347 Debug,
3348 Default,
3349 Eq,
3350 PartialEq,
3351 Hash,
3352 serde::Serialize,
3353 serde::Deserialize,
3354 Getters,
3355 new,
3356)]
3357pub struct GetAddressBalanceResponse {
3358 balance: u64,
3360 pub received: u64,
3362}
3363
3364#[deprecated(note = "Use `GetAddressBalanceResponse` instead.")]
3365pub use self::GetAddressBalanceResponse as AddressBalance;
3366
3367#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, Getters, new)]
3369#[serde(from = "DGetAddressUtxosRequest")]
3370pub struct GetAddressUtxosRequest {
3371 addresses: Vec<String>,
3373 #[serde(default)]
3375 #[serde(rename = "chainInfo")]
3376 chain_info: bool,
3377}
3378
3379impl From<DGetAddressUtxosRequest> for GetAddressUtxosRequest {
3380 fn from(request: DGetAddressUtxosRequest) -> Self {
3381 match request {
3382 DGetAddressUtxosRequest::Single(addr) => GetAddressUtxosRequest {
3383 addresses: vec![addr],
3384 chain_info: false,
3385 },
3386 DGetAddressUtxosRequest::Object {
3387 addresses,
3388 chain_info,
3389 } => GetAddressUtxosRequest {
3390 addresses,
3391 chain_info,
3392 },
3393 }
3394 }
3395}
3396
3397#[derive(Debug, serde::Deserialize)]
3399#[serde(untagged)]
3400enum DGetAddressUtxosRequest {
3401 Single(String),
3403 Object {
3405 addresses: Vec<String>,
3407 #[serde(default)]
3409 #[serde(rename = "chainInfo")]
3410 chain_info: bool,
3411 },
3412}
3413
3414impl ValidateAddresses for GetAddressUtxosRequest {
3415 fn addresses(&self) -> &[String] {
3416 &self.addresses
3417 }
3418}
3419
3420#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
3422pub struct ConsensusBranchIdHex(#[serde(with = "hex")] ConsensusBranchId);
3423
3424impl ConsensusBranchIdHex {
3425 pub fn new(consensus_branch_id: u32) -> Self {
3427 ConsensusBranchIdHex(consensus_branch_id.into())
3428 }
3429
3430 pub fn inner(&self) -> u32 {
3432 self.0.into()
3433 }
3434}
3435
3436#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3438pub struct NetworkUpgradeInfo {
3439 name: NetworkUpgrade,
3443
3444 #[serde(rename = "activationheight")]
3446 activation_height: Height,
3447
3448 status: NetworkUpgradeStatus,
3450}
3451
3452impl NetworkUpgradeInfo {
3453 pub fn from_parts(
3455 name: NetworkUpgrade,
3456 activation_height: Height,
3457 status: NetworkUpgradeStatus,
3458 ) -> Self {
3459 Self {
3460 name,
3461 activation_height,
3462 status,
3463 }
3464 }
3465
3466 pub fn into_parts(self) -> (NetworkUpgrade, Height, NetworkUpgradeStatus) {
3468 (self.name, self.activation_height, self.status)
3469 }
3470}
3471
3472#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3474pub enum NetworkUpgradeStatus {
3475 #[serde(rename = "active")]
3480 Active,
3481
3482 #[serde(rename = "disabled")]
3484 Disabled,
3485
3486 #[serde(rename = "pending")]
3488 Pending,
3489}
3490
3491#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3495pub struct TipConsensusBranch {
3496 #[serde(rename = "chaintip")]
3498 chain_tip: ConsensusBranchIdHex,
3499
3500 #[serde(rename = "nextblock")]
3502 next_block: ConsensusBranchIdHex,
3503}
3504
3505impl TipConsensusBranch {
3506 pub fn from_parts(chain_tip: u32, next_block: u32) -> Self {
3508 Self {
3509 chain_tip: ConsensusBranchIdHex::new(chain_tip),
3510 next_block: ConsensusBranchIdHex::new(next_block),
3511 }
3512 }
3513
3514 pub fn into_parts(self) -> (u32, u32) {
3516 (self.chain_tip.inner(), self.next_block.inner())
3517 }
3518}
3519
3520#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3526pub struct SendRawTransactionResponse(#[serde(with = "hex")] transaction::Hash);
3527
3528#[deprecated(note = "Use `SendRawTransactionResponse` instead")]
3529pub use self::SendRawTransactionResponse as SentTransactionHash;
3530
3531impl Default for SendRawTransactionResponse {
3532 fn default() -> Self {
3533 Self(transaction::Hash::from([0; 32]))
3534 }
3535}
3536
3537impl SendRawTransactionResponse {
3538 pub fn new(hash: transaction::Hash) -> Self {
3540 SendRawTransactionResponse(hash)
3541 }
3542
3543 #[deprecated(note = "Use `SentTransactionHash::hash` instead")]
3545 pub fn inner(&self) -> transaction::Hash {
3546 self.hash()
3547 }
3548
3549 pub fn hash(&self) -> transaction::Hash {
3551 self.0
3552 }
3553}
3554
3555#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3559#[serde(untagged)]
3560pub enum GetBlockResponse {
3561 Raw(#[serde(with = "hex")] SerializedBlock),
3563 Object(Box<BlockObject>),
3565}
3566
3567#[deprecated(note = "Use `GetBlockResponse` instead")]
3568pub use self::GetBlockResponse as GetBlock;
3569
3570impl Default for GetBlockResponse {
3571 fn default() -> Self {
3572 GetBlockResponse::Object(Box::new(BlockObject {
3573 hash: block::Hash([0; 32]),
3574 confirmations: 0,
3575 height: None,
3576 time: None,
3577 tx: Vec::new(),
3578 trees: GetBlockTrees::default(),
3579 size: None,
3580 version: None,
3581 merkle_root: None,
3582 block_commitments: None,
3583 final_sapling_root: None,
3584 final_orchard_root: None,
3585 nonce: None,
3586 bits: None,
3587 difficulty: None,
3588 chain_supply: None,
3589 value_pools: None,
3590 previous_block_hash: None,
3591 next_block_hash: None,
3592 solution: None,
3593 }))
3594 }
3595}
3596
3597#[allow(clippy::too_many_arguments)]
3599#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3600pub struct BlockObject {
3601 #[getter(copy)]
3603 #[serde(with = "hex")]
3604 hash: block::Hash,
3605
3606 confirmations: i64,
3609
3610 #[serde(skip_serializing_if = "Option::is_none")]
3612 #[getter(copy)]
3613 size: Option<i64>,
3614
3615 #[serde(skip_serializing_if = "Option::is_none")]
3617 #[getter(copy)]
3618 height: Option<Height>,
3619
3620 #[serde(skip_serializing_if = "Option::is_none")]
3622 #[getter(copy)]
3623 version: Option<u32>,
3624
3625 #[serde(with = "opthex", rename = "merkleroot")]
3627 #[serde(skip_serializing_if = "Option::is_none")]
3628 #[getter(copy)]
3629 merkle_root: Option<block::merkle::Root>,
3630
3631 #[serde(with = "opthex", rename = "blockcommitments")]
3634 #[serde(skip_serializing_if = "Option::is_none")]
3635 #[getter(copy)]
3636 block_commitments: Option<[u8; 32]>,
3637
3638 #[serde(with = "opthex", rename = "finalsaplingroot")]
3642 #[serde(skip_serializing_if = "Option::is_none")]
3643 #[getter(copy)]
3644 final_sapling_root: Option<[u8; 32]>,
3645
3646 #[serde(with = "opthex", rename = "finalorchardroot")]
3648 #[serde(skip_serializing_if = "Option::is_none")]
3649 #[getter(copy)]
3650 final_orchard_root: Option<[u8; 32]>,
3651
3652 tx: Vec<GetBlockTransaction>,
3657
3658 #[serde(skip_serializing_if = "Option::is_none")]
3660 #[getter(copy)]
3661 time: Option<i64>,
3662
3663 #[serde(with = "opthex")]
3665 #[serde(skip_serializing_if = "Option::is_none")]
3666 #[getter(copy)]
3667 nonce: Option<[u8; 32]>,
3668
3669 #[serde(with = "opthex")]
3672 #[serde(skip_serializing_if = "Option::is_none")]
3673 #[getter(copy)]
3674 solution: Option<Solution>,
3675
3676 #[serde(with = "opthex")]
3678 #[serde(skip_serializing_if = "Option::is_none")]
3679 #[getter(copy)]
3680 bits: Option<CompactDifficulty>,
3681
3682 #[serde(skip_serializing_if = "Option::is_none")]
3685 #[getter(copy)]
3686 difficulty: Option<f64>,
3687
3688 #[serde(rename = "chainSupply")]
3693 #[serde(skip_serializing_if = "Option::is_none")]
3694 chain_supply: Option<GetBlockchainInfoBalance>,
3695
3696 #[serde(rename = "valuePools")]
3698 #[serde(skip_serializing_if = "Option::is_none")]
3699 value_pools: Option<BlockchainValuePoolBalances>,
3700
3701 #[getter(copy)]
3703 trees: GetBlockTrees,
3704
3705 #[serde(rename = "previousblockhash", skip_serializing_if = "Option::is_none")]
3707 #[serde(with = "opthex")]
3708 #[getter(copy)]
3709 previous_block_hash: Option<block::Hash>,
3710
3711 #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
3713 #[serde(with = "opthex")]
3714 #[getter(copy)]
3715 next_block_hash: Option<block::Hash>,
3716}
3717
3718#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3719#[serde(untagged)]
3720pub enum GetBlockTransaction {
3723 Hash(#[serde(with = "hex")] transaction::Hash),
3725 Object(Box<TransactionObject>),
3727}
3728
3729#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3733#[serde(untagged)]
3734pub enum GetBlockHeaderResponse {
3735 Raw(hex_data::HexData),
3737
3738 Object(Box<BlockHeaderObject>),
3740}
3741
3742#[deprecated(note = "Use `GetBlockHeaderResponse` instead")]
3743pub use self::GetBlockHeaderResponse as GetBlockHeader;
3744
3745#[allow(clippy::too_many_arguments)]
3746#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3747pub struct BlockHeaderObject {
3751 #[serde(with = "hex")]
3753 #[getter(copy)]
3754 hash: block::Hash,
3755
3756 confirmations: i64,
3759
3760 #[getter(copy)]
3762 height: Height,
3763
3764 version: u32,
3766
3767 #[serde(with = "hex", rename = "merkleroot")]
3769 #[getter(copy)]
3770 merkle_root: block::merkle::Root,
3771
3772 #[serde(with = "hex", rename = "blockcommitments")]
3775 #[getter(copy)]
3776 block_commitments: [u8; 32],
3777
3778 #[serde(with = "hex", rename = "finalsaplingroot")]
3780 #[getter(copy)]
3781 final_sapling_root: [u8; 32],
3782
3783 #[serde(skip)]
3786 sapling_tree_size: u64,
3787
3788 time: i64,
3790
3791 #[serde(with = "hex")]
3793 #[getter(copy)]
3794 nonce: [u8; 32],
3795
3796 #[serde(with = "hex")]
3798 #[getter(copy)]
3799 solution: Solution,
3800
3801 #[serde(with = "hex")]
3803 #[getter(copy)]
3804 bits: CompactDifficulty,
3805
3806 difficulty: f64,
3809
3810 #[serde(rename = "previousblockhash")]
3812 #[serde(with = "hex")]
3813 #[getter(copy)]
3814 previous_block_hash: block::Hash,
3815
3816 #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
3818 #[getter(copy)]
3819 #[serde(with = "opthex")]
3820 next_block_hash: Option<block::Hash>,
3821}
3822
3823#[deprecated(note = "Use `BlockHeaderObject` instead")]
3824pub use BlockHeaderObject as GetBlockHeaderObject;
3825
3826impl Default for GetBlockHeaderResponse {
3827 fn default() -> Self {
3828 GetBlockHeaderResponse::Object(Box::default())
3829 }
3830}
3831
3832impl Default for BlockHeaderObject {
3833 fn default() -> Self {
3834 let difficulty: ExpandedDifficulty = zebra_chain::work::difficulty::U256::one().into();
3835
3836 BlockHeaderObject {
3837 hash: block::Hash([0; 32]),
3838 confirmations: 0,
3839 height: Height::MIN,
3840 version: 4,
3841 merkle_root: block::merkle::Root([0; 32]),
3842 block_commitments: Default::default(),
3843 final_sapling_root: Default::default(),
3844 sapling_tree_size: Default::default(),
3845 time: 0,
3846 nonce: [0; 32],
3847 solution: Solution::for_proposal(),
3848 bits: difficulty.to_compact(),
3849 difficulty: 1.0,
3850 previous_block_hash: block::Hash([0; 32]),
3851 next_block_hash: Some(block::Hash([0; 32])),
3852 }
3853 }
3854}
3855
3856#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3862#[serde(transparent)]
3863pub struct GetBlockHashResponse(#[serde(with = "hex")] pub(crate) block::Hash);
3864
3865impl GetBlockHashResponse {
3866 pub fn new(hash: block::Hash) -> Self {
3868 GetBlockHashResponse(hash)
3869 }
3870
3871 pub fn hash(&self) -> block::Hash {
3873 self.0
3874 }
3875}
3876
3877#[deprecated(note = "Use `GetBlockHashResponse` instead")]
3878pub use self::GetBlockHashResponse as GetBlockHash;
3879
3880pub type Hash = GetBlockHashResponse;
3882
3883#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, Getters, new)]
3885pub struct GetBlockHeightAndHashResponse {
3886 #[getter(copy)]
3888 height: block::Height,
3889 #[getter(copy)]
3891 hash: block::Hash,
3892}
3893
3894#[deprecated(note = "Use `GetBlockHeightAndHashResponse` instead.")]
3895pub use GetBlockHeightAndHashResponse as GetBestBlockHeightAndHash;
3896
3897impl Default for GetBlockHeightAndHashResponse {
3898 fn default() -> Self {
3899 Self {
3900 height: block::Height::MIN,
3901 hash: block::Hash([0; 32]),
3902 }
3903 }
3904}
3905
3906impl Default for GetBlockHashResponse {
3907 fn default() -> Self {
3908 GetBlockHashResponse(block::Hash([0; 32]))
3909 }
3910}
3911
3912#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3916#[serde(untagged)]
3917pub enum GetRawTransactionResponse {
3918 Raw(#[serde(with = "hex")] SerializedTransaction),
3920 Object(Box<TransactionObject>),
3922}
3923
3924#[deprecated(note = "Use `GetRawTransactionResponse` instead")]
3925pub use self::GetRawTransactionResponse as GetRawTransaction;
3926
3927impl Default for GetRawTransactionResponse {
3928 fn default() -> Self {
3929 Self::Object(Box::default())
3930 }
3931}
3932
3933#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3935#[serde(untagged)]
3936pub enum GetAddressUtxosResponse {
3937 Utxos(Vec<Utxo>),
3939 UtxosAndChainInfo(GetAddressUtxosResponseObject),
3941}
3942
3943#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3945pub struct GetAddressUtxosResponseObject {
3946 utxos: Vec<Utxo>,
3947 #[serde(with = "hex")]
3948 #[getter(copy)]
3949 hash: block::Hash,
3950 #[getter(copy)]
3951 height: block::Height,
3952}
3953
3954#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3958pub struct Utxo {
3959 address: transparent::Address,
3961
3962 #[serde(with = "hex")]
3964 #[getter(copy)]
3965 txid: transaction::Hash,
3966
3967 #[serde(rename = "outputIndex")]
3969 #[getter(copy)]
3970 output_index: OutputIndex,
3971
3972 #[serde(with = "hex")]
3974 script: transparent::Script,
3975
3976 satoshis: u64,
3978
3979 #[getter(copy)]
3983 height: Height,
3984}
3985
3986#[deprecated(note = "Use `Utxo` instead")]
3987pub use self::Utxo as GetAddressUtxos;
3988
3989impl Default for Utxo {
3990 fn default() -> Self {
3991 Self {
3992 address: transparent::Address::from_pub_key_hash(
3993 zebra_chain::parameters::NetworkKind::default(),
3994 [0u8; 20],
3995 ),
3996 txid: transaction::Hash::from([0; 32]),
3997 output_index: OutputIndex::from_u64(0),
3998 script: transparent::Script::new(&[0u8; 10]),
3999 satoshis: u64::default(),
4000 height: Height(0),
4001 }
4002 }
4003}
4004
4005impl Utxo {
4006 #[deprecated(note = "Use `Utxo::new` instead")]
4008 pub fn from_parts(
4009 address: transparent::Address,
4010 txid: transaction::Hash,
4011 output_index: OutputIndex,
4012 script: transparent::Script,
4013 satoshis: u64,
4014 height: Height,
4015 ) -> Self {
4016 Utxo {
4017 address,
4018 txid,
4019 output_index,
4020 script,
4021 satoshis,
4022 height,
4023 }
4024 }
4025
4026 pub fn into_parts(
4028 &self,
4029 ) -> (
4030 transparent::Address,
4031 transaction::Hash,
4032 OutputIndex,
4033 transparent::Script,
4034 u64,
4035 Height,
4036 ) {
4037 (
4038 self.address.clone(),
4039 self.txid,
4040 self.output_index,
4041 self.script.clone(),
4042 self.satoshis,
4043 self.height,
4044 )
4045 }
4046}
4047
4048#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, Getters, new)]
4052#[serde(from = "DGetAddressTxIdsRequest")]
4053pub struct GetAddressTxIdsRequest {
4054 addresses: Vec<String>,
4057 start: Option<u32>,
4059 end: Option<u32>,
4061}
4062
4063impl GetAddressTxIdsRequest {
4064 #[deprecated(note = "Use `GetAddressTxIdsRequest::new` instead.")]
4066 pub fn from_parts(addresses: Vec<String>, start: u32, end: u32) -> Self {
4067 GetAddressTxIdsRequest {
4068 addresses,
4069 start: Some(start),
4070 end: Some(end),
4071 }
4072 }
4073
4074 pub fn into_parts(&self) -> (Vec<String>, u32, u32) {
4076 (
4077 self.addresses.clone(),
4078 self.start.unwrap_or(0),
4079 self.end.unwrap_or(0),
4080 )
4081 }
4082}
4083
4084impl From<DGetAddressTxIdsRequest> for GetAddressTxIdsRequest {
4085 fn from(request: DGetAddressTxIdsRequest) -> Self {
4086 match request {
4087 DGetAddressTxIdsRequest::Single(addr) => GetAddressTxIdsRequest {
4088 addresses: vec![addr],
4089 start: None,
4090 end: None,
4091 },
4092 DGetAddressTxIdsRequest::Object {
4093 addresses,
4094 start,
4095 end,
4096 } => GetAddressTxIdsRequest {
4097 addresses,
4098 start,
4099 end,
4100 },
4101 }
4102 }
4103}
4104
4105#[derive(Debug, serde::Deserialize)]
4107#[serde(untagged)]
4108enum DGetAddressTxIdsRequest {
4109 Single(String),
4111 Object {
4113 addresses: Vec<String>,
4115 start: Option<u32>,
4117 end: Option<u32>,
4119 },
4120}
4121
4122impl ValidateAddresses for GetAddressTxIdsRequest {
4123 fn addresses(&self) -> &[String] {
4124 &self.addresses
4125 }
4126}
4127
4128#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
4130pub struct GetBlockTrees {
4131 #[serde(skip_serializing_if = "SaplingTrees::is_empty")]
4132 sapling: SaplingTrees,
4133 #[serde(skip_serializing_if = "OrchardTrees::is_empty")]
4134 orchard: OrchardTrees,
4135}
4136
4137impl Default for GetBlockTrees {
4138 fn default() -> Self {
4139 GetBlockTrees {
4140 sapling: SaplingTrees { size: 0 },
4141 orchard: OrchardTrees { size: 0 },
4142 }
4143 }
4144}
4145
4146impl GetBlockTrees {
4147 pub fn new(sapling: u64, orchard: u64) -> Self {
4149 GetBlockTrees {
4150 sapling: SaplingTrees { size: sapling },
4151 orchard: OrchardTrees { size: orchard },
4152 }
4153 }
4154
4155 pub fn sapling(self) -> u64 {
4157 self.sapling.size
4158 }
4159
4160 pub fn orchard(self) -> u64 {
4162 self.orchard.size
4163 }
4164}
4165
4166#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
4168pub struct SaplingTrees {
4169 size: u64,
4170}
4171
4172impl SaplingTrees {
4173 fn is_empty(&self) -> bool {
4174 self.size == 0
4175 }
4176}
4177
4178#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
4180pub struct OrchardTrees {
4181 size: u64,
4182}
4183
4184impl OrchardTrees {
4185 fn is_empty(&self) -> bool {
4186 self.size == 0
4187 }
4188}
4189
4190fn build_height_range(
4206 start: Option<u32>,
4207 end: Option<u32>,
4208 chain_height: Height,
4209) -> Result<RangeInclusive<Height>> {
4210 let start = Height(start.unwrap_or(0)).min(chain_height);
4213
4214 let end = match end {
4216 Some(0) | None => chain_height,
4217 Some(val) => Height(val).min(chain_height),
4218 };
4219
4220 if start > end {
4221 return Err(ErrorObject::owned(
4222 ErrorCode::InvalidParams.code(),
4223 format!("start {start:?} must be less than or equal to end {end:?}"),
4224 None::<()>,
4225 ));
4226 }
4227
4228 Ok(start..=end)
4229}
4230
4231#[allow(dead_code)]
4239pub fn height_from_signed_int(index: i32, tip_height: Height) -> Result<Height> {
4240 if index >= 0 {
4241 let height = index.try_into().expect("Positive i32 always fits in u32");
4242 if height > tip_height.0 {
4243 return Err(ErrorObject::borrowed(
4244 ErrorCode::InvalidParams.code(),
4245 "Provided index is greater than the current tip",
4246 None,
4247 ));
4248 }
4249 Ok(Height(height))
4250 } else {
4251 let height = i32::try_from(tip_height.0)
4253 .expect("tip height fits in i32, because Height::MAX fits in i32")
4254 .checked_add(index + 1);
4255
4256 let sanitized_height = match height {
4257 None => {
4258 return Err(ErrorObject::borrowed(
4259 ErrorCode::InvalidParams.code(),
4260 "Provided index is not valid",
4261 None,
4262 ));
4263 }
4264 Some(h) => {
4265 if h < 0 {
4266 return Err(ErrorObject::borrowed(
4267 ErrorCode::InvalidParams.code(),
4268 "Provided negative index ends up with a negative height",
4269 None,
4270 ));
4271 }
4272 let h: u32 = h.try_into().expect("Positive i32 always fits in u32");
4273 if h > tip_height.0 {
4274 return Err(ErrorObject::borrowed(
4275 ErrorCode::InvalidParams.code(),
4276 "Provided index is greater than the current tip",
4277 None,
4278 ));
4279 }
4280
4281 h
4282 }
4283 };
4284
4285 Ok(Height(sanitized_height))
4286 }
4287}
4288
4289pub mod opthex {
4291 use hex::{FromHex, ToHex};
4292 use serde::{de, Deserialize, Deserializer, Serializer};
4293
4294 #[allow(missing_docs)]
4295 pub fn serialize<S, T>(data: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
4296 where
4297 S: Serializer,
4298 T: ToHex,
4299 {
4300 match data {
4301 Some(data) => {
4302 let s = data.encode_hex::<String>();
4303 serializer.serialize_str(&s)
4304 }
4305 None => serializer.serialize_none(),
4306 }
4307 }
4308
4309 #[allow(missing_docs)]
4310 pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
4311 where
4312 D: Deserializer<'de>,
4313 T: FromHex,
4314 {
4315 let opt = Option::<String>::deserialize(deserializer)?;
4316 match opt {
4317 Some(s) => T::from_hex(&s)
4318 .map(Some)
4319 .map_err(|_e| de::Error::custom("failed to convert hex string")),
4320 None => Ok(None),
4321 }
4322 }
4323}
4324
4325pub mod arrayhex {
4327 use serde::{Deserializer, Serializer};
4328 use std::fmt;
4329
4330 #[allow(missing_docs)]
4331 pub fn serialize<S, const N: usize>(data: &[u8; N], serializer: S) -> Result<S::Ok, S::Error>
4332 where
4333 S: Serializer,
4334 {
4335 let hex_string = hex::encode(data);
4336 serializer.serialize_str(&hex_string)
4337 }
4338
4339 #[allow(missing_docs)]
4340 pub fn deserialize<'de, D, const N: usize>(deserializer: D) -> Result<[u8; N], D::Error>
4341 where
4342 D: Deserializer<'de>,
4343 {
4344 struct HexArrayVisitor<const N: usize>;
4345
4346 impl<const N: usize> serde::de::Visitor<'_> for HexArrayVisitor<N> {
4347 type Value = [u8; N];
4348
4349 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
4350 write!(formatter, "a hex string representing exactly {N} bytes")
4351 }
4352
4353 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
4354 where
4355 E: serde::de::Error,
4356 {
4357 let vec = hex::decode(v).map_err(E::custom)?;
4358 vec.clone().try_into().map_err(|_| {
4359 E::invalid_length(vec.len(), &format!("expected {N} bytes").as_str())
4360 })
4361 }
4362 }
4363
4364 deserializer.deserialize_str(HexArrayVisitor::<N>)
4365 }
4366}
4367
4368pub async fn chain_tip_difficulty<State>(
4370 network: Network,
4371 mut state: State,
4372 should_use_default: bool,
4373) -> Result<f64>
4374where
4375 State: Service<
4376 zebra_state::ReadRequest,
4377 Response = zebra_state::ReadResponse,
4378 Error = zebra_state::BoxError,
4379 > + Clone
4380 + Send
4381 + Sync
4382 + 'static,
4383 State::Future: Send,
4384{
4385 let request = ReadRequest::ChainInfo;
4386
4387 let response = state
4393 .ready()
4394 .and_then(|service| service.call(request))
4395 .await;
4396
4397 let response = match (should_use_default, response) {
4398 (_, Ok(res)) => res,
4399 (true, Err(_)) => {
4400 return Ok((U256::from(network.target_difficulty_limit()) >> 128).as_u128() as f64);
4401 }
4402 (false, Err(error)) => return Err(ErrorObject::owned(0, error.to_string(), None::<()>)),
4403 };
4404
4405 let chain_info = match response {
4406 ReadResponse::ChainInfo(info) => info,
4407 _ => unreachable!("unmatched response to a chain info request"),
4408 };
4409
4410 let pow_limit: U256 = network.target_difficulty_limit().into();
4433 let Some(difficulty) = chain_info.expected_difficulty.to_expanded() else {
4434 return Ok(0.0);
4435 };
4436
4437 let pow_limit = pow_limit >> 128;
4439 let difficulty = U256::from(difficulty) >> 128;
4440
4441 let pow_limit = pow_limit.as_u128() as f64;
4444 let difficulty = difficulty.as_u128() as f64;
4445
4446 Ok(pow_limit / difficulty)
4448}
4449
4450#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
4452pub enum AddNodeCommand {
4453 #[serde(rename = "add")]
4455 Add,
4456}