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 rand::{rngs::OsRng, RngCore};
53use tokio::{
54 sync::{broadcast, mpsc, watch},
55 task::JoinHandle,
56};
57use tower::{Service, ServiceExt};
58use tracing::Instrument;
59
60use zcash_address::{unified::Encoding, TryFromAddress};
61use zcash_primitives::consensus::Parameters;
62
63use zebra_chain::{
64 amount::{self, Amount, NegativeAllowed, NonNegative},
65 block::{self, Block, Commitment, Height, SerializedBlock, TryIntoHeight},
66 chain_sync_status::ChainSyncStatus,
67 chain_tip::{ChainTip, NetworkChainTipHeightEstimator},
68 parameters::{
69 subsidy::{
70 block_subsidy, funding_stream_values, miner_subsidy, FundingStreamReceiver,
71 ParameterSubsidy,
72 },
73 ConsensusBranchId, Network, NetworkUpgrade, POW_AVERAGING_WINDOW,
74 },
75 serialization::{BytesInDisplayOrder, ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize},
76 subtree::NoteCommitmentSubtreeIndex,
77 transaction::{self, SerializedTransaction, Transaction, UnminedTx},
78 transparent::{self, Address, OutputIndex},
79 value_balance::ValueBalance,
80 work::{
81 difficulty::{CompactDifficulty, ExpandedDifficulty, ParameterDifficulty, U256},
82 equihash::Solution,
83 },
84};
85use zebra_consensus::{funding_stream_address, RouterError};
86use zebra_network::{address_book_peers::AddressBookPeers, types::PeerServices, PeerSocketAddr};
87use zebra_node_services::mempool;
88use zebra_state::{
89 AnyTx, HashOrHeight, OutputLocation, ReadRequest, ReadResponse, TransactionLocation,
90};
91
92use crate::{
93 client::Treestate,
94 config,
95 methods::types::{validate_address::validate_address, z_validate_address::z_validate_address},
96 queue::Queue,
97 server::{
98 self,
99 error::{MapError, OkOrError},
100 },
101};
102
103pub(crate) mod hex_data;
104pub(crate) mod trees;
105pub(crate) mod types;
106
107use hex_data::HexData;
108use trees::{GetSubtreesByIndexResponse, GetTreestateResponse, SubtreeRpcData};
109use types::{
110 get_block_template::{
111 constants::{
112 DEFAULT_SOLUTION_RATE_WINDOW_SIZE, MEMPOOL_LONG_POLL_INTERVAL,
113 ZCASHD_FUNDING_STREAM_ORDER,
114 },
115 proposal::proposal_block_from_template,
116 BlockTemplateResponse, BlockTemplateTimeSource, GetBlockTemplateHandler,
117 GetBlockTemplateParameters, GetBlockTemplateResponse,
118 },
119 get_blockchain_info::GetBlockchainInfoBalance,
120 get_mempool_info::GetMempoolInfoResponse,
121 get_mining_info::GetMiningInfoResponse,
122 get_raw_mempool::{self, GetRawMempoolResponse},
123 long_poll::LongPollInput,
124 network_info::{GetNetworkInfoResponse, NetworkInfo},
125 peer_info::PeerInfo,
126 submit_block::{SubmitBlockErrorResponse, SubmitBlockParameters, SubmitBlockResponse},
127 subsidy::GetBlockSubsidyResponse,
128 transaction::TransactionObject,
129 unified_address::ZListUnifiedReceiversResponse,
130 validate_address::ValidateAddressResponse,
131 z_validate_address::ZValidateAddressResponse,
132};
133
134#[cfg(test)]
135mod tests;
136
137#[rpc(server)]
138pub trait Rpc {
140 #[method(name = "getinfo")]
155 async fn get_info(&self) -> Result<GetInfoResponse>;
156
157 #[method(name = "getblockchaininfo")]
168 async fn get_blockchain_info(&self) -> Result<GetBlockchainInfoResponse>;
169
170 #[method(name = "getaddressbalance")]
193 async fn get_address_balance(
194 &self,
195 address_strings: GetAddressBalanceRequest,
196 ) -> Result<GetAddressBalanceResponse>;
197
198 #[method(name = "sendrawtransaction")]
215 async fn send_raw_transaction(
216 &self,
217 raw_transaction_hex: String,
218 _allow_high_fees: Option<bool>,
219 ) -> Result<SendRawTransactionResponse>;
220
221 #[method(name = "getblock")]
241 async fn get_block(
242 &self,
243 hash_or_height: String,
244 verbosity: Option<u8>,
245 ) -> Result<GetBlockResponse>;
246
247 #[method(name = "getblockheader")]
265 async fn get_block_header(
266 &self,
267 hash_or_height: String,
268 verbose: Option<bool>,
269 ) -> Result<GetBlockHeaderResponse>;
270
271 #[method(name = "getbestblockhash")]
277 fn get_best_block_hash(&self) -> Result<GetBlockHashResponse>;
278
279 #[method(name = "getbestblockheightandhash")]
285 fn get_best_block_height_and_hash(&self) -> Result<GetBlockHeightAndHashResponse>;
286
287 #[method(name = "getmempoolinfo")]
291 async fn get_mempool_info(&self) -> Result<GetMempoolInfoResponse>;
292
293 #[method(name = "getrawmempool")]
303 async fn get_raw_mempool(&self, verbose: Option<bool>) -> Result<GetRawMempoolResponse>;
304
305 #[method(name = "z_gettreestate")]
322 async fn z_get_treestate(&self, hash_or_height: String) -> Result<GetTreestateResponse>;
323
324 #[method(name = "z_getsubtreesbyindex")]
343 async fn z_get_subtrees_by_index(
344 &self,
345 pool: String,
346 start_index: NoteCommitmentSubtreeIndex,
347 limit: Option<NoteCommitmentSubtreeIndex>,
348 ) -> Result<GetSubtreesByIndexResponse>;
349
350 #[method(name = "getrawtransaction")]
362 async fn get_raw_transaction(
363 &self,
364 txid: String,
365 verbose: Option<u8>,
366 block_hash: Option<String>,
367 ) -> Result<GetRawTransactionResponse>;
368
369 #[method(name = "getaddresstxids")]
399 async fn get_address_tx_ids(&self, request: GetAddressTxIdsRequest) -> Result<Vec<String>>;
400
401 #[method(name = "getaddressutxos")]
420 async fn get_address_utxos(
421 &self,
422 request: GetAddressUtxosRequest,
423 ) -> Result<GetAddressUtxosResponse>;
424
425 #[method(name = "stop")]
436 fn stop(&self) -> Result<String>;
437
438 #[method(name = "getblockcount")]
445 fn get_block_count(&self) -> Result<u32>;
446
447 #[method(name = "getblockhash")]
463 async fn get_block_hash(&self, index: i32) -> Result<GetBlockHashResponse>;
464
465 #[method(name = "getblocktemplate")]
487 async fn get_block_template(
488 &self,
489 parameters: Option<GetBlockTemplateParameters>,
490 ) -> Result<GetBlockTemplateResponse>;
491
492 #[method(name = "submitblock")]
508 async fn submit_block(
509 &self,
510 hex_data: HexData,
511 _parameters: Option<SubmitBlockParameters>,
512 ) -> Result<SubmitBlockResponse>;
513
514 #[method(name = "getmininginfo")]
520 async fn get_mining_info(&self) -> Result<GetMiningInfoResponse>;
521
522 #[method(name = "getnetworksolps")]
533 async fn get_network_sol_ps(&self, num_blocks: Option<i32>, height: Option<i32>)
534 -> Result<u64>;
535
536 #[method(name = "getnetworkhashps")]
546 async fn get_network_hash_ps(
547 &self,
548 num_blocks: Option<i32>,
549 height: Option<i32>,
550 ) -> Result<u64> {
551 self.get_network_sol_ps(num_blocks, height).await
552 }
553
554 #[method(name = "getnetworkinfo")]
560 async fn get_network_info(&self) -> Result<GetNetworkInfoResponse>;
561
562 #[method(name = "getpeerinfo")]
568 async fn get_peer_info(&self) -> Result<Vec<PeerInfo>>;
569
570 #[method(name = "validateaddress")]
581 async fn validate_address(&self, address: String) -> Result<ValidateAddressResponse>;
582
583 #[method(name = "z_validateaddress")]
598 async fn z_validate_address(&self, address: String) -> Result<ZValidateAddressResponse>;
599
600 #[method(name = "getblocksubsidy")]
615 async fn get_block_subsidy(&self, height: Option<u32>) -> Result<GetBlockSubsidyResponse>;
616
617 #[method(name = "getdifficulty")]
623 async fn get_difficulty(&self) -> Result<f64>;
624
625 #[method(name = "z_listunifiedreceivers")]
639 async fn z_list_unified_receivers(
640 &self,
641 address: String,
642 ) -> Result<ZListUnifiedReceiversResponse>;
643
644 #[method(name = "invalidateblock")]
652 async fn invalidate_block(&self, block_hash: String) -> Result<()>;
653
654 #[method(name = "reconsiderblock")]
660 async fn reconsider_block(&self, block_hash: String) -> Result<Vec<block::Hash>>;
661
662 #[method(name = "generate")]
663 async fn generate(&self, num_blocks: u32) -> Result<Vec<GetBlockHashResponse>>;
677
678 #[method(name = "addnode")]
679 async fn add_node(&self, addr: PeerSocketAddr, command: AddNodeCommand) -> Result<()>;
694}
695
696#[derive(Clone)]
698pub struct RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
699where
700 Mempool: Service<
701 mempool::Request,
702 Response = mempool::Response,
703 Error = zebra_node_services::BoxError,
704 > + Clone
705 + Send
706 + Sync
707 + 'static,
708 Mempool::Future: Send,
709 State: Service<
710 zebra_state::Request,
711 Response = zebra_state::Response,
712 Error = zebra_state::BoxError,
713 > + Clone
714 + Send
715 + Sync
716 + 'static,
717 State::Future: Send,
718 ReadState: Service<
719 zebra_state::ReadRequest,
720 Response = zebra_state::ReadResponse,
721 Error = zebra_state::BoxError,
722 > + Clone
723 + Send
724 + Sync
725 + 'static,
726 ReadState::Future: Send,
727 Tip: ChainTip + Clone + Send + Sync + 'static,
728 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
729 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
730 + Clone
731 + Send
732 + Sync
733 + 'static,
734 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
735 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
736{
737 build_version: String,
741
742 user_agent: String,
744
745 network: Network,
747
748 debug_force_finished_sync: bool,
751
752 mempool: Mempool,
756
757 state: State,
759
760 read_state: ReadState,
762
763 latest_chain_tip: Tip,
765
766 queue_sender: broadcast::Sender<UnminedTx>,
770
771 address_book: AddressBook,
773
774 last_warn_error_log_rx: LoggedLastEvent,
776
777 gbt: GetBlockTemplateHandler<BlockVerifierRouter, SyncStatus>,
779}
780
781pub type LoggedLastEvent = watch::Receiver<Option<(String, tracing::Level, chrono::DateTime<Utc>)>>;
783
784impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus> fmt::Debug
785 for RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
786where
787 Mempool: Service<
788 mempool::Request,
789 Response = mempool::Response,
790 Error = zebra_node_services::BoxError,
791 > + Clone
792 + Send
793 + Sync
794 + 'static,
795 Mempool::Future: Send,
796 State: Service<
797 zebra_state::Request,
798 Response = zebra_state::Response,
799 Error = zebra_state::BoxError,
800 > + Clone
801 + Send
802 + Sync
803 + 'static,
804 State::Future: Send,
805 ReadState: Service<
806 zebra_state::ReadRequest,
807 Response = zebra_state::ReadResponse,
808 Error = zebra_state::BoxError,
809 > + Clone
810 + Send
811 + Sync
812 + 'static,
813 ReadState::Future: Send,
814 Tip: ChainTip + Clone + Send + Sync + 'static,
815 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
816 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
817 + Clone
818 + Send
819 + Sync
820 + 'static,
821 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
822 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
823{
824 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
825 f.debug_struct("RpcImpl")
827 .field("build_version", &self.build_version)
828 .field("user_agent", &self.user_agent)
829 .field("network", &self.network)
830 .field("debug_force_finished_sync", &self.debug_force_finished_sync)
831 .field("getblocktemplate", &self.gbt)
832 .finish()
833 }
834}
835
836impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
837 RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
838where
839 Mempool: Service<
840 mempool::Request,
841 Response = mempool::Response,
842 Error = zebra_node_services::BoxError,
843 > + Clone
844 + Send
845 + Sync
846 + 'static,
847 Mempool::Future: Send,
848 State: Service<
849 zebra_state::Request,
850 Response = zebra_state::Response,
851 Error = zebra_state::BoxError,
852 > + Clone
853 + Send
854 + Sync
855 + 'static,
856 State::Future: Send,
857 ReadState: Service<
858 zebra_state::ReadRequest,
859 Response = zebra_state::ReadResponse,
860 Error = zebra_state::BoxError,
861 > + Clone
862 + Send
863 + Sync
864 + 'static,
865 ReadState::Future: Send,
866 Tip: ChainTip + Clone + Send + Sync + 'static,
867 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
868 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
869 + Clone
870 + Send
871 + Sync
872 + 'static,
873 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
874 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
875{
876 #[allow(clippy::too_many_arguments)]
881 pub fn new<VersionString, UserAgentString>(
882 network: Network,
883 mining_config: config::mining::Config,
884 debug_force_finished_sync: bool,
885 build_version: VersionString,
886 user_agent: UserAgentString,
887 mempool: Mempool,
888 state: State,
889 read_state: ReadState,
890 block_verifier_router: BlockVerifierRouter,
891 sync_status: SyncStatus,
892 latest_chain_tip: Tip,
893 address_book: AddressBook,
894 last_warn_error_log_rx: LoggedLastEvent,
895 mined_block_sender: Option<mpsc::Sender<(block::Hash, block::Height)>>,
896 ) -> (Self, JoinHandle<()>)
897 where
898 VersionString: ToString + Clone + Send + 'static,
899 UserAgentString: ToString + Clone + Send + 'static,
900 {
901 let (runner, queue_sender) = Queue::start();
902
903 let mut build_version = build_version.to_string();
904 let user_agent = user_agent.to_string();
905
906 if !build_version.is_empty() && !build_version.starts_with('v') {
908 build_version.insert(0, 'v');
909 }
910
911 let gbt = GetBlockTemplateHandler::new(
912 &network,
913 mining_config.clone(),
914 block_verifier_router,
915 sync_status,
916 mined_block_sender,
917 );
918
919 let rpc_impl = RpcImpl {
920 build_version,
921 user_agent,
922 network: network.clone(),
923 debug_force_finished_sync,
924 mempool: mempool.clone(),
925 state: state.clone(),
926 read_state: read_state.clone(),
927 latest_chain_tip: latest_chain_tip.clone(),
928 queue_sender,
929 address_book,
930 last_warn_error_log_rx,
931 gbt,
932 };
933
934 let rpc_tx_queue_task_handle = tokio::spawn(
936 runner
937 .run(mempool, read_state, latest_chain_tip, network)
938 .in_current_span(),
939 );
940
941 (rpc_impl, rpc_tx_queue_task_handle)
942 }
943
944 pub fn network(&self) -> &Network {
946 &self.network
947 }
948}
949
950#[async_trait]
951impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus> RpcServer
952 for RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
953where
954 Mempool: Service<
955 mempool::Request,
956 Response = mempool::Response,
957 Error = zebra_node_services::BoxError,
958 > + Clone
959 + Send
960 + Sync
961 + 'static,
962 Mempool::Future: Send,
963 State: Service<
964 zebra_state::Request,
965 Response = zebra_state::Response,
966 Error = zebra_state::BoxError,
967 > + Clone
968 + Send
969 + Sync
970 + 'static,
971 State::Future: Send,
972 ReadState: Service<
973 zebra_state::ReadRequest,
974 Response = zebra_state::ReadResponse,
975 Error = zebra_state::BoxError,
976 > + Clone
977 + Send
978 + Sync
979 + 'static,
980 ReadState::Future: Send,
981 Tip: ChainTip + Clone + Send + Sync + 'static,
982 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
983 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
984 + Clone
985 + Send
986 + Sync
987 + 'static,
988 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
989 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
990{
991 async fn get_info(&self) -> Result<GetInfoResponse> {
992 let version = GetInfoResponse::version_from_string(&self.build_version)
993 .expect("invalid version string");
994
995 let connections = self.address_book.recently_live_peers(Utc::now()).len();
996
997 let last_error_recorded = self.last_warn_error_log_rx.borrow().clone();
998 let (last_error_log, _level, last_error_log_time) = last_error_recorded.unwrap_or((
999 GetInfoResponse::default().errors,
1000 tracing::Level::INFO,
1001 Utc::now(),
1002 ));
1003
1004 let tip_height = self
1005 .latest_chain_tip
1006 .best_tip_height()
1007 .unwrap_or(Height::MIN);
1008 let testnet = self.network.is_a_test_network();
1009
1010 let pay_tx_fee = 0.0;
1016
1017 let relay_fee = zebra_chain::transaction::zip317::MIN_MEMPOOL_TX_FEE_RATE as f64
1018 / (zebra_chain::amount::COIN as f64);
1019 let difficulty = chain_tip_difficulty(self.network.clone(), self.read_state.clone(), true)
1020 .await
1021 .expect("should always be Ok when `should_use_default` is true");
1022
1023 let response = GetInfoResponse {
1024 version,
1025 build: self.build_version.clone(),
1026 subversion: self.user_agent.clone(),
1027 protocol_version: zebra_network::constants::CURRENT_NETWORK_PROTOCOL_VERSION.0,
1028 blocks: tip_height.0,
1029 connections,
1030 proxy: None,
1031 difficulty,
1032 testnet,
1033 pay_tx_fee,
1034 relay_fee,
1035 errors: last_error_log,
1036 errors_timestamp: last_error_log_time.to_string(),
1037 };
1038
1039 Ok(response)
1040 }
1041
1042 #[allow(clippy::unwrap_in_result)]
1043 async fn get_blockchain_info(&self) -> Result<GetBlockchainInfoResponse> {
1044 let debug_force_finished_sync = self.debug_force_finished_sync;
1045 let network = &self.network;
1046
1047 let (usage_info_rsp, tip_pool_values_rsp, chain_tip_difficulty) = {
1048 use zebra_state::ReadRequest::*;
1049 let state_call = |request| self.read_state.clone().oneshot(request);
1050 tokio::join!(
1051 state_call(UsageInfo),
1052 state_call(TipPoolValues),
1053 chain_tip_difficulty(network.clone(), self.read_state.clone(), true)
1054 )
1055 };
1056
1057 let (size_on_disk, (tip_height, tip_hash), value_balance, difficulty) = {
1058 use zebra_state::ReadResponse::*;
1059
1060 let UsageInfo(size_on_disk) = usage_info_rsp.map_misc_error()? else {
1061 unreachable!("unmatched response to a TipPoolValues request")
1062 };
1063
1064 let (tip, value_balance) = match tip_pool_values_rsp {
1065 Ok(TipPoolValues {
1066 tip_height,
1067 tip_hash,
1068 value_balance,
1069 }) => ((tip_height, tip_hash), value_balance),
1070 Ok(_) => unreachable!("unmatched response to a TipPoolValues request"),
1071 Err(_) => ((Height::MIN, network.genesis_hash()), Default::default()),
1072 };
1073
1074 let difficulty = chain_tip_difficulty
1075 .expect("should always be Ok when `should_use_default` is true");
1076
1077 (size_on_disk, tip, value_balance, difficulty)
1078 };
1079
1080 let now = Utc::now();
1081 let (estimated_height, verification_progress) = self
1082 .latest_chain_tip
1083 .best_tip_height_and_block_time()
1084 .map(|(tip_height, tip_block_time)| {
1085 let height =
1086 NetworkChainTipHeightEstimator::new(tip_block_time, tip_height, network)
1087 .estimate_height_at(now);
1088
1089 let height =
1093 if tip_block_time > now || height < tip_height || debug_force_finished_sync {
1094 tip_height
1095 } else {
1096 height
1097 };
1098
1099 (height, f64::from(tip_height.0) / f64::from(height.0))
1100 })
1101 .unwrap_or((Height::MIN, 0.0));
1103
1104 let verification_progress = if network.is_regtest() {
1105 1.0
1106 } else {
1107 verification_progress
1108 };
1109
1110 let mut upgrades = IndexMap::new();
1114 for (activation_height, network_upgrade) in network.full_activation_list() {
1115 if let Some(branch_id) = network_upgrade.branch_id() {
1120 let status = if tip_height >= activation_height {
1122 NetworkUpgradeStatus::Active
1123 } else {
1124 NetworkUpgradeStatus::Pending
1125 };
1126
1127 let upgrade = NetworkUpgradeInfo {
1128 name: network_upgrade,
1129 activation_height,
1130 status,
1131 };
1132 upgrades.insert(ConsensusBranchIdHex(branch_id), upgrade);
1133 }
1134 }
1135
1136 let next_block_height =
1138 (tip_height + 1).expect("valid chain tips are a lot less than Height::MAX");
1139 let consensus = TipConsensusBranch {
1140 chain_tip: ConsensusBranchIdHex(
1141 NetworkUpgrade::current(network, tip_height)
1142 .branch_id()
1143 .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
1144 ),
1145 next_block: ConsensusBranchIdHex(
1146 NetworkUpgrade::current(network, next_block_height)
1147 .branch_id()
1148 .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
1149 ),
1150 };
1151
1152 let response = GetBlockchainInfoResponse {
1153 chain: network.bip70_network_name(),
1154 blocks: tip_height,
1155 best_block_hash: tip_hash,
1156 estimated_height,
1157 chain_supply: GetBlockchainInfoBalance::chain_supply(value_balance),
1158 value_pools: GetBlockchainInfoBalance::value_pools(value_balance, None),
1159 upgrades,
1160 consensus,
1161 headers: tip_height,
1162 difficulty,
1163 verification_progress,
1164 chain_work: 0,
1166 pruned: false,
1167 size_on_disk,
1168 commitments: 0,
1170 };
1171
1172 Ok(response)
1173 }
1174
1175 async fn get_address_balance(
1176 &self,
1177 address_strings: GetAddressBalanceRequest,
1178 ) -> Result<GetAddressBalanceResponse> {
1179 let valid_addresses = address_strings.valid_addresses()?;
1180
1181 let request = zebra_state::ReadRequest::AddressBalance(valid_addresses);
1182 let response = self
1183 .read_state
1184 .clone()
1185 .oneshot(request)
1186 .await
1187 .map_misc_error()?;
1188
1189 match response {
1190 zebra_state::ReadResponse::AddressBalance { balance, received } => {
1191 Ok(GetAddressBalanceResponse {
1192 balance: u64::from(balance),
1193 received,
1194 })
1195 }
1196 _ => unreachable!("Unexpected response from state service: {response:?}"),
1197 }
1198 }
1199
1200 async fn send_raw_transaction(
1202 &self,
1203 raw_transaction_hex: String,
1204 _allow_high_fees: Option<bool>,
1205 ) -> Result<SendRawTransactionResponse> {
1206 let mempool = self.mempool.clone();
1207 let queue_sender = self.queue_sender.clone();
1208
1209 let raw_transaction_bytes = Vec::from_hex(raw_transaction_hex)
1212 .map_error(server::error::LegacyCode::Deserialization)?;
1213 let raw_transaction = Transaction::zcash_deserialize(&*raw_transaction_bytes)
1214 .map_error(server::error::LegacyCode::Deserialization)?;
1215
1216 let transaction_hash = raw_transaction.hash();
1217
1218 let unmined_transaction = UnminedTx::from(raw_transaction.clone());
1220 let _ = queue_sender.send(unmined_transaction);
1221
1222 let transaction_parameter = mempool::Gossip::Tx(raw_transaction.into());
1223 let request = mempool::Request::Queue(vec![transaction_parameter]);
1224
1225 let response = mempool.oneshot(request).await.map_misc_error()?;
1226
1227 let mut queue_results = match response {
1228 mempool::Response::Queued(results) => results,
1229 _ => unreachable!("incorrect response variant from mempool service"),
1230 };
1231
1232 assert_eq!(
1233 queue_results.len(),
1234 1,
1235 "mempool service returned more results than expected"
1236 );
1237
1238 let queue_result = queue_results
1239 .pop()
1240 .expect("there should be exactly one item in Vec")
1241 .inspect_err(|err| tracing::debug!("sent transaction to mempool: {:?}", &err))
1242 .map_misc_error()?
1243 .await
1244 .map_misc_error()?;
1245
1246 tracing::debug!("sent transaction to mempool: {:?}", &queue_result);
1247
1248 queue_result
1249 .map(|_| SendRawTransactionResponse(transaction_hash))
1250 .map_error(server::error::LegacyCode::Verify)
1257 }
1258
1259 async fn get_block(
1264 &self,
1265 hash_or_height: String,
1266 verbosity: Option<u8>,
1267 ) -> Result<GetBlockResponse> {
1268 let verbosity = verbosity.unwrap_or(1);
1269 let network = self.network.clone();
1270 let original_hash_or_height = hash_or_height.clone();
1271
1272 let get_block_header_future = if matches!(verbosity, 1 | 2) {
1274 Some(self.get_block_header(original_hash_or_height.clone(), Some(true)))
1275 } else {
1276 None
1277 };
1278
1279 let hash_or_height =
1280 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1281 .map_error(server::error::LegacyCode::InvalidParameter)?;
1284
1285 if verbosity == 0 {
1286 let request = zebra_state::ReadRequest::Block(hash_or_height);
1287 let response = self
1288 .read_state
1289 .clone()
1290 .oneshot(request)
1291 .await
1292 .map_misc_error()?;
1293
1294 match response {
1295 zebra_state::ReadResponse::Block(Some(block)) => {
1296 Ok(GetBlockResponse::Raw(block.into()))
1297 }
1298 zebra_state::ReadResponse::Block(None) => {
1299 Err("Block not found").map_error(server::error::LegacyCode::InvalidParameter)
1300 }
1301 _ => unreachable!("unmatched response to a block request"),
1302 }
1303 } else if let Some(get_block_header_future) = get_block_header_future {
1304 let get_block_header_result: Result<GetBlockHeaderResponse> =
1305 get_block_header_future.await;
1306
1307 let GetBlockHeaderResponse::Object(block_header) = get_block_header_result? else {
1308 panic!("must return Object")
1309 };
1310
1311 let BlockHeaderObject {
1312 hash,
1313 confirmations,
1314 height,
1315 version,
1316 merkle_root,
1317 block_commitments,
1318 final_sapling_root,
1319 sapling_tree_size,
1320 time,
1321 nonce,
1322 solution,
1323 bits,
1324 difficulty,
1325 previous_block_hash,
1326 next_block_hash,
1327 } = *block_header;
1328
1329 let transactions_request = match verbosity {
1330 1 => zebra_state::ReadRequest::TransactionIdsForBlock(hash_or_height),
1331 2 => zebra_state::ReadRequest::BlockAndSize(hash_or_height),
1332 _other => panic!("get_block_header_fut should be none"),
1333 };
1334
1335 let hash_or_height = hash.into();
1340 let requests = vec![
1341 transactions_request,
1349 zebra_state::ReadRequest::OrchardTree(hash_or_height),
1351 zebra_state::ReadRequest::BlockInfo(previous_block_hash.into()),
1353 zebra_state::ReadRequest::BlockInfo(hash_or_height),
1354 ];
1355
1356 let mut futs = FuturesOrdered::new();
1357
1358 for request in requests {
1359 futs.push_back(self.read_state.clone().oneshot(request));
1360 }
1361
1362 let tx_ids_response = futs.next().await.expect("`futs` should not be empty");
1363 let (tx, size): (Vec<_>, Option<usize>) = match tx_ids_response.map_misc_error()? {
1364 zebra_state::ReadResponse::TransactionIdsForBlock(tx_ids) => (
1365 tx_ids
1366 .ok_or_misc_error("block not found")?
1367 .iter()
1368 .map(|tx_id| GetBlockTransaction::Hash(*tx_id))
1369 .collect(),
1370 None,
1371 ),
1372 zebra_state::ReadResponse::BlockAndSize(block_and_size) => {
1373 let (block, size) = block_and_size.ok_or_misc_error("Block not found")?;
1374 let block_time = block.header.time;
1375 let transactions =
1376 block
1377 .transactions
1378 .iter()
1379 .map(|tx| {
1380 GetBlockTransaction::Object(Box::new(
1381 TransactionObject::from_transaction(
1382 tx.clone(),
1383 Some(height),
1384 Some(confirmations.try_into().expect(
1385 "should be less than max block height, i32::MAX",
1386 )),
1387 &network,
1388 Some(block_time),
1389 Some(hash),
1390 Some(true),
1391 tx.hash(),
1392 ),
1393 ))
1394 })
1395 .collect();
1396 (transactions, Some(size))
1397 }
1398 _ => unreachable!("unmatched response to a transaction_ids_for_block request"),
1399 };
1400
1401 let orchard_tree_response = futs.next().await.expect("`futs` should not be empty");
1402 let zebra_state::ReadResponse::OrchardTree(orchard_tree) =
1403 orchard_tree_response.map_misc_error()?
1404 else {
1405 unreachable!("unmatched response to a OrchardTree request");
1406 };
1407
1408 let nu5_activation = NetworkUpgrade::Nu5.activation_height(&network);
1409
1410 let orchard_tree = orchard_tree.ok_or_misc_error("missing Orchard tree")?;
1412
1413 let final_orchard_root = match nu5_activation {
1414 Some(activation_height) if height >= activation_height => {
1415 Some(orchard_tree.root().into())
1416 }
1417 _other => None,
1418 };
1419
1420 let sapling = SaplingTrees {
1421 size: sapling_tree_size,
1422 };
1423
1424 let orchard_tree_size = orchard_tree.count();
1425 let orchard = OrchardTrees {
1426 size: orchard_tree_size,
1427 };
1428
1429 let trees = GetBlockTrees { sapling, orchard };
1430
1431 let block_info_response = futs.next().await.expect("`futs` should not be empty");
1432 let zebra_state::ReadResponse::BlockInfo(prev_block_info) =
1433 block_info_response.map_misc_error()?
1434 else {
1435 unreachable!("unmatched response to a BlockInfo request");
1436 };
1437 let block_info_response = futs.next().await.expect("`futs` should not be empty");
1438 let zebra_state::ReadResponse::BlockInfo(block_info) =
1439 block_info_response.map_misc_error()?
1440 else {
1441 unreachable!("unmatched response to a BlockInfo request");
1442 };
1443
1444 let delta = block_info.as_ref().and_then(|d| {
1445 let value_pools = d.value_pools().constrain::<NegativeAllowed>().ok()?;
1446 let prev_value_pools = prev_block_info
1447 .map(|d| d.value_pools().constrain::<NegativeAllowed>())
1448 .unwrap_or(Ok(ValueBalance::<NegativeAllowed>::zero()))
1449 .ok()?;
1450 (value_pools - prev_value_pools).ok()
1451 });
1452 let size = size.or(block_info.as_ref().map(|d| d.size() as usize));
1453
1454 Ok(GetBlockResponse::Object(Box::new(BlockObject {
1455 hash,
1456 confirmations,
1457 height: Some(height),
1458 version: Some(version),
1459 merkle_root: Some(merkle_root),
1460 time: Some(time),
1461 nonce: Some(nonce),
1462 solution: Some(solution),
1463 bits: Some(bits),
1464 difficulty: Some(difficulty),
1465 tx,
1466 trees,
1467 chain_supply: block_info
1468 .as_ref()
1469 .map(|d| GetBlockchainInfoBalance::chain_supply(*d.value_pools())),
1470 value_pools: block_info
1471 .map(|d| GetBlockchainInfoBalance::value_pools(*d.value_pools(), delta)),
1472 size: size.map(|size| size as i64),
1473 block_commitments: Some(block_commitments),
1474 final_sapling_root: Some(final_sapling_root),
1475 final_orchard_root,
1476 previous_block_hash: Some(previous_block_hash),
1477 next_block_hash,
1478 })))
1479 } else {
1480 Err("invalid verbosity value").map_error(server::error::LegacyCode::InvalidParameter)
1481 }
1482 }
1483
1484 async fn get_block_header(
1485 &self,
1486 hash_or_height: String,
1487 verbose: Option<bool>,
1488 ) -> Result<GetBlockHeaderResponse> {
1489 let verbose = verbose.unwrap_or(true);
1490 let network = self.network.clone();
1491
1492 let hash_or_height =
1493 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1494 .map_error(server::error::LegacyCode::InvalidParameter)?;
1497 let zebra_state::ReadResponse::BlockHeader {
1498 header,
1499 hash,
1500 height,
1501 next_block_hash,
1502 } = self
1503 .read_state
1504 .clone()
1505 .oneshot(zebra_state::ReadRequest::BlockHeader(hash_or_height))
1506 .await
1507 .map_err(|_| "block height not in best chain")
1508 .map_error(
1509 if hash_or_height.hash().is_some() {
1514 server::error::LegacyCode::InvalidAddressOrKey
1515 } else {
1516 server::error::LegacyCode::InvalidParameter
1517 },
1518 )?
1519 else {
1520 panic!("unexpected response to BlockHeader request")
1521 };
1522
1523 let response = if !verbose {
1524 GetBlockHeaderResponse::Raw(HexData(header.zcash_serialize_to_vec().map_misc_error()?))
1525 } else {
1526 let zebra_state::ReadResponse::SaplingTree(sapling_tree) = self
1527 .read_state
1528 .clone()
1529 .oneshot(zebra_state::ReadRequest::SaplingTree(hash_or_height))
1530 .await
1531 .map_misc_error()?
1532 else {
1533 panic!("unexpected response to SaplingTree request")
1534 };
1535
1536 let sapling_tree = sapling_tree.ok_or_misc_error("missing Sapling tree")?;
1538
1539 let zebra_state::ReadResponse::Depth(depth) = self
1540 .read_state
1541 .clone()
1542 .oneshot(zebra_state::ReadRequest::Depth(hash))
1543 .await
1544 .map_misc_error()?
1545 else {
1546 panic!("unexpected response to SaplingTree request")
1547 };
1548
1549 const NOT_IN_BEST_CHAIN_CONFIRMATIONS: i64 = -1;
1552
1553 let confirmations = depth
1556 .map(|depth| i64::from(depth) + 1)
1557 .unwrap_or(NOT_IN_BEST_CHAIN_CONFIRMATIONS);
1558
1559 let mut nonce = *header.nonce;
1560 nonce.reverse();
1561
1562 let sapling_activation = NetworkUpgrade::Sapling.activation_height(&network);
1563 let sapling_tree_size = sapling_tree.count();
1564 let final_sapling_root: [u8; 32] =
1565 if sapling_activation.is_some() && height >= sapling_activation.unwrap() {
1566 let mut root: [u8; 32] = sapling_tree.root().into();
1567 root.reverse();
1568 root
1569 } else {
1570 [0; 32]
1571 };
1572
1573 let difficulty = header.difficulty_threshold.relative_to_network(&network);
1574
1575 let block_commitments = match header.commitment(&network, height).expect(
1576 "Unexpected failure while parsing the blockcommitments field in get_block_header",
1577 ) {
1578 Commitment::PreSaplingReserved(bytes) => bytes,
1579 Commitment::FinalSaplingRoot(_) => final_sapling_root,
1580 Commitment::ChainHistoryActivationReserved => [0; 32],
1581 Commitment::ChainHistoryRoot(root) => root.bytes_in_display_order(),
1582 Commitment::ChainHistoryBlockTxAuthCommitment(hash) => {
1583 hash.bytes_in_display_order()
1584 }
1585 };
1586
1587 let block_header = BlockHeaderObject {
1588 hash,
1589 confirmations,
1590 height,
1591 version: header.version,
1592 merkle_root: header.merkle_root,
1593 block_commitments,
1594 final_sapling_root,
1595 sapling_tree_size,
1596 time: header.time.timestamp(),
1597 nonce,
1598 solution: header.solution,
1599 bits: header.difficulty_threshold,
1600 difficulty,
1601 previous_block_hash: header.previous_block_hash,
1602 next_block_hash,
1603 };
1604
1605 GetBlockHeaderResponse::Object(Box::new(block_header))
1606 };
1607
1608 Ok(response)
1609 }
1610
1611 fn get_best_block_hash(&self) -> Result<GetBlockHashResponse> {
1612 self.latest_chain_tip
1613 .best_tip_hash()
1614 .map(GetBlockHashResponse)
1615 .ok_or_misc_error("No blocks in state")
1616 }
1617
1618 fn get_best_block_height_and_hash(&self) -> Result<GetBlockHeightAndHashResponse> {
1619 self.latest_chain_tip
1620 .best_tip_height_and_hash()
1621 .map(|(height, hash)| GetBlockHeightAndHashResponse { height, hash })
1622 .ok_or_misc_error("No blocks in state")
1623 }
1624
1625 async fn get_mempool_info(&self) -> Result<GetMempoolInfoResponse> {
1626 let mut mempool = self.mempool.clone();
1627
1628 let response = mempool
1629 .ready()
1630 .and_then(|service| service.call(mempool::Request::QueueStats))
1631 .await
1632 .map_misc_error()?;
1633
1634 if let mempool::Response::QueueStats {
1635 size,
1636 bytes,
1637 usage,
1638 fully_notified,
1639 } = response
1640 {
1641 Ok(GetMempoolInfoResponse {
1642 size,
1643 bytes,
1644 usage,
1645 fully_notified,
1646 })
1647 } else {
1648 unreachable!("unexpected response to QueueStats request")
1649 }
1650 }
1651
1652 async fn get_raw_mempool(&self, verbose: Option<bool>) -> Result<GetRawMempoolResponse> {
1653 #[allow(unused)]
1654 let verbose = verbose.unwrap_or(false);
1655
1656 use zebra_chain::block::MAX_BLOCK_BYTES;
1657
1658 let mut mempool = self.mempool.clone();
1659
1660 let request = if verbose {
1661 mempool::Request::FullTransactions
1662 } else {
1663 mempool::Request::TransactionIds
1664 };
1665
1666 let response = mempool
1668 .ready()
1669 .and_then(|service| service.call(request))
1670 .await
1671 .map_misc_error()?;
1672
1673 match response {
1674 mempool::Response::FullTransactions {
1675 mut transactions,
1676 transaction_dependencies,
1677 last_seen_tip_hash: _,
1678 } => {
1679 if verbose {
1680 let map = transactions
1681 .iter()
1682 .map(|unmined_tx| {
1683 (
1684 unmined_tx.transaction.id.mined_id().encode_hex(),
1685 get_raw_mempool::MempoolObject::from_verified_unmined_tx(
1686 unmined_tx,
1687 &transactions,
1688 &transaction_dependencies,
1689 ),
1690 )
1691 })
1692 .collect::<HashMap<_, _>>();
1693 Ok(GetRawMempoolResponse::Verbose(map))
1694 } else {
1695 transactions.sort_by_cached_key(|tx| {
1700 cmp::Reverse((
1703 i64::from(tx.miner_fee) as u128 * MAX_BLOCK_BYTES as u128
1704 / tx.transaction.size as u128,
1705 tx.transaction.id.mined_id(),
1707 ))
1708 });
1709 let tx_ids: Vec<String> = transactions
1710 .iter()
1711 .map(|unmined_tx| unmined_tx.transaction.id.mined_id().encode_hex())
1712 .collect();
1713
1714 Ok(GetRawMempoolResponse::TxIds(tx_ids))
1715 }
1716 }
1717
1718 mempool::Response::TransactionIds(unmined_transaction_ids) => {
1719 let mut tx_ids: Vec<String> = unmined_transaction_ids
1720 .iter()
1721 .map(|id| id.mined_id().encode_hex())
1722 .collect();
1723
1724 tx_ids.sort();
1726
1727 Ok(GetRawMempoolResponse::TxIds(tx_ids))
1728 }
1729
1730 _ => unreachable!("unmatched response to a transactionids request"),
1731 }
1732 }
1733
1734 async fn get_raw_transaction(
1735 &self,
1736 txid: String,
1737 verbose: Option<u8>,
1738 block_hash: Option<String>,
1739 ) -> Result<GetRawTransactionResponse> {
1740 let mut mempool = self.mempool.clone();
1741 let verbose = verbose.unwrap_or(0) != 0;
1742
1743 let txid = transaction::Hash::from_hex(txid)
1746 .map_error(server::error::LegacyCode::InvalidAddressOrKey)?;
1747
1748 if block_hash.is_none() {
1750 match mempool
1751 .ready()
1752 .and_then(|service| {
1753 service.call(mempool::Request::TransactionsByMinedId([txid].into()))
1754 })
1755 .await
1756 .map_misc_error()?
1757 {
1758 mempool::Response::Transactions(txns) => {
1759 if let Some(tx) = txns.first() {
1760 return Ok(if verbose {
1761 GetRawTransactionResponse::Object(Box::new(
1762 TransactionObject::from_transaction(
1763 tx.transaction.clone(),
1764 None,
1765 None,
1766 &self.network,
1767 None,
1768 None,
1769 Some(false),
1770 txid,
1771 ),
1772 ))
1773 } else {
1774 let hex = tx.transaction.clone().into();
1775 GetRawTransactionResponse::Raw(hex)
1776 });
1777 }
1778 }
1779
1780 _ => unreachable!("unmatched response to a `TransactionsByMinedId` request"),
1781 };
1782 }
1783
1784 let txid = if let Some(block_hash) = block_hash {
1785 let block_hash = block::Hash::from_hex(block_hash)
1786 .map_error(server::error::LegacyCode::InvalidAddressOrKey)?;
1787 match self
1788 .read_state
1789 .clone()
1790 .oneshot(zebra_state::ReadRequest::AnyChainTransactionIdsForBlock(
1791 block_hash.into(),
1792 ))
1793 .await
1794 .map_misc_error()?
1795 {
1796 zebra_state::ReadResponse::AnyChainTransactionIdsForBlock(tx_ids) => *tx_ids
1797 .ok_or_error(
1798 server::error::LegacyCode::InvalidAddressOrKey,
1799 "block not found",
1800 )?
1801 .0
1802 .iter()
1803 .find(|id| **id == txid)
1804 .ok_or_error(
1805 server::error::LegacyCode::InvalidAddressOrKey,
1806 "txid not found",
1807 )?,
1808 _ => {
1809 unreachable!("unmatched response to a `AnyChainTransactionIdsForBlock` request")
1810 }
1811 }
1812 } else {
1813 txid
1814 };
1815
1816 match self
1818 .read_state
1819 .clone()
1820 .oneshot(zebra_state::ReadRequest::AnyChainTransaction(txid))
1821 .await
1822 .map_misc_error()?
1823 {
1824 zebra_state::ReadResponse::AnyChainTransaction(Some(tx)) => Ok(if verbose {
1825 match tx {
1826 AnyTx::Mined(tx) => {
1827 let block_hash = match self
1828 .read_state
1829 .clone()
1830 .oneshot(zebra_state::ReadRequest::BestChainBlockHash(tx.height))
1831 .await
1832 .map_misc_error()?
1833 {
1834 zebra_state::ReadResponse::BlockHash(block_hash) => block_hash,
1835 _ => {
1836 unreachable!("unmatched response to a `BestChainBlockHash` request")
1837 }
1838 };
1839
1840 GetRawTransactionResponse::Object(Box::new(
1841 TransactionObject::from_transaction(
1842 tx.tx.clone(),
1843 Some(tx.height),
1844 Some(tx.confirmations),
1845 &self.network,
1846 Some(tx.block_time),
1849 block_hash,
1850 Some(true),
1851 txid,
1852 ),
1853 ))
1854 }
1855 AnyTx::Side((tx, block_hash)) => GetRawTransactionResponse::Object(Box::new(
1856 TransactionObject::from_transaction(
1857 tx.clone(),
1858 None,
1859 None,
1860 &self.network,
1861 None,
1862 Some(block_hash),
1863 Some(false),
1864 txid,
1865 ),
1866 )),
1867 }
1868 } else {
1869 let tx: Arc<Transaction> = tx.into();
1870 let hex = tx.into();
1871 GetRawTransactionResponse::Raw(hex)
1872 }),
1873
1874 zebra_state::ReadResponse::AnyChainTransaction(None) => {
1875 Err("No such mempool or main chain transaction")
1876 .map_error(server::error::LegacyCode::InvalidAddressOrKey)
1877 }
1878
1879 _ => unreachable!("unmatched response to a `Transaction` read request"),
1880 }
1881 }
1882
1883 async fn z_get_treestate(&self, hash_or_height: String) -> Result<GetTreestateResponse> {
1884 let mut read_state = self.read_state.clone();
1885 let network = self.network.clone();
1886
1887 let hash_or_height =
1888 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1889 .map_error(server::error::LegacyCode::InvalidParameter)?;
1892
1893 let block = match read_state
1902 .ready()
1903 .and_then(|service| service.call(zebra_state::ReadRequest::Block(hash_or_height)))
1904 .await
1905 .map_misc_error()?
1906 {
1907 zebra_state::ReadResponse::Block(Some(block)) => block,
1908 zebra_state::ReadResponse::Block(None) => {
1909 return Err("the requested block is not in the main chain")
1912 .map_error(server::error::LegacyCode::InvalidParameter);
1913 }
1914 _ => unreachable!("unmatched response to a block request"),
1915 };
1916
1917 let hash = hash_or_height
1918 .hash_or_else(|_| Some(block.hash()))
1919 .expect("block hash");
1920
1921 let height = hash_or_height
1922 .height_or_else(|_| block.coinbase_height())
1923 .expect("verified blocks have a coinbase height");
1924
1925 let time = u32::try_from(block.header.time.timestamp())
1926 .expect("Timestamps of valid blocks always fit into u32.");
1927
1928 let sapling_nu = zcash_primitives::consensus::NetworkUpgrade::Sapling;
1929 let sapling = if network.is_nu_active(sapling_nu, height.into()) {
1930 match read_state
1931 .ready()
1932 .and_then(|service| {
1933 service.call(zebra_state::ReadRequest::SaplingTree(hash.into()))
1934 })
1935 .await
1936 .map_misc_error()?
1937 {
1938 zebra_state::ReadResponse::SaplingTree(tree) => {
1939 tree.map(|t| (t.to_rpc_bytes(), t.root().bytes_in_display_order().to_vec()))
1940 }
1941 _ => unreachable!("unmatched response to a Sapling tree request"),
1942 }
1943 } else {
1944 None
1945 };
1946 let (sapling_tree, sapling_root) =
1947 sapling.map_or((None, None), |(tree, root)| (Some(tree), Some(root)));
1948
1949 let orchard_nu = zcash_primitives::consensus::NetworkUpgrade::Nu5;
1950 let orchard = if network.is_nu_active(orchard_nu, height.into()) {
1951 match read_state
1952 .ready()
1953 .and_then(|service| {
1954 service.call(zebra_state::ReadRequest::OrchardTree(hash.into()))
1955 })
1956 .await
1957 .map_misc_error()?
1958 {
1959 zebra_state::ReadResponse::OrchardTree(tree) => {
1960 tree.map(|t| (t.to_rpc_bytes(), t.root().bytes_in_display_order().to_vec()))
1961 }
1962 _ => unreachable!("unmatched response to an Orchard tree request"),
1963 }
1964 } else {
1965 None
1966 };
1967 let (orchard_tree, orchard_root) =
1968 orchard.map_or((None, None), |(tree, root)| (Some(tree), Some(root)));
1969
1970 Ok(GetTreestateResponse::new(
1971 hash,
1972 height,
1973 time,
1974 None,
1977 Treestate::new(trees::Commitments::new(sapling_root, sapling_tree)),
1978 Treestate::new(trees::Commitments::new(orchard_root, orchard_tree)),
1979 ))
1980 }
1981
1982 async fn z_get_subtrees_by_index(
1983 &self,
1984 pool: String,
1985 start_index: NoteCommitmentSubtreeIndex,
1986 limit: Option<NoteCommitmentSubtreeIndex>,
1987 ) -> Result<GetSubtreesByIndexResponse> {
1988 let mut read_state = self.read_state.clone();
1989
1990 const POOL_LIST: &[&str] = &["sapling", "orchard"];
1991
1992 if pool == "sapling" {
1993 let request = zebra_state::ReadRequest::SaplingSubtrees { start_index, limit };
1994 let response = read_state
1995 .ready()
1996 .and_then(|service| service.call(request))
1997 .await
1998 .map_misc_error()?;
1999
2000 let subtrees = match response {
2001 zebra_state::ReadResponse::SaplingSubtrees(subtrees) => subtrees,
2002 _ => unreachable!("unmatched response to a subtrees request"),
2003 };
2004
2005 let subtrees = subtrees
2006 .values()
2007 .map(|subtree| SubtreeRpcData {
2008 root: subtree.root.to_bytes().encode_hex(),
2009 end_height: subtree.end_height,
2010 })
2011 .collect();
2012
2013 Ok(GetSubtreesByIndexResponse {
2014 pool,
2015 start_index,
2016 subtrees,
2017 })
2018 } else if pool == "orchard" {
2019 let request = zebra_state::ReadRequest::OrchardSubtrees { start_index, limit };
2020 let response = read_state
2021 .ready()
2022 .and_then(|service| service.call(request))
2023 .await
2024 .map_misc_error()?;
2025
2026 let subtrees = match response {
2027 zebra_state::ReadResponse::OrchardSubtrees(subtrees) => subtrees,
2028 _ => unreachable!("unmatched response to a subtrees request"),
2029 };
2030
2031 let subtrees = subtrees
2032 .values()
2033 .map(|subtree| SubtreeRpcData {
2034 root: subtree.root.encode_hex(),
2035 end_height: subtree.end_height,
2036 })
2037 .collect();
2038
2039 Ok(GetSubtreesByIndexResponse {
2040 pool,
2041 start_index,
2042 subtrees,
2043 })
2044 } else {
2045 Err(ErrorObject::owned(
2046 server::error::LegacyCode::Misc.into(),
2047 format!("invalid pool name, must be one of: {POOL_LIST:?}").as_str(),
2048 None::<()>,
2049 ))
2050 }
2051 }
2052
2053 async fn get_address_tx_ids(&self, request: GetAddressTxIdsRequest) -> Result<Vec<String>> {
2054 let mut read_state = self.read_state.clone();
2055 let latest_chain_tip = self.latest_chain_tip.clone();
2056
2057 let height_range = build_height_range(
2058 request.start,
2059 request.end,
2060 best_chain_tip_height(&latest_chain_tip)?,
2061 )?;
2062
2063 let valid_addresses = request.valid_addresses()?;
2064
2065 let request = zebra_state::ReadRequest::TransactionIdsByAddresses {
2066 addresses: valid_addresses,
2067 height_range,
2068 };
2069 let response = read_state
2070 .ready()
2071 .and_then(|service| service.call(request))
2072 .await
2073 .map_misc_error()?;
2074
2075 let hashes = match response {
2076 zebra_state::ReadResponse::AddressesTransactionIds(hashes) => {
2077 let mut last_tx_location = TransactionLocation::from_usize(Height(0), 0);
2078
2079 hashes
2080 .iter()
2081 .map(|(tx_loc, tx_id)| {
2082 assert!(
2084 *tx_loc > last_tx_location,
2085 "Transactions were not in chain order:\n\
2086 {tx_loc:?} {tx_id:?} was after:\n\
2087 {last_tx_location:?}",
2088 );
2089
2090 last_tx_location = *tx_loc;
2091
2092 tx_id.to_string()
2093 })
2094 .collect()
2095 }
2096 _ => unreachable!("unmatched response to a TransactionsByAddresses request"),
2097 };
2098
2099 Ok(hashes)
2100 }
2101
2102 async fn get_address_utxos(
2103 &self,
2104 utxos_request: GetAddressUtxosRequest,
2105 ) -> Result<GetAddressUtxosResponse> {
2106 let mut read_state = self.read_state.clone();
2107 let mut response_utxos = vec![];
2108
2109 let valid_addresses = utxos_request.valid_addresses()?;
2110
2111 let request = zebra_state::ReadRequest::UtxosByAddresses(valid_addresses);
2113 let response = read_state
2114 .ready()
2115 .and_then(|service| service.call(request))
2116 .await
2117 .map_misc_error()?;
2118 let utxos = match response {
2119 zebra_state::ReadResponse::AddressUtxos(utxos) => utxos,
2120 _ => unreachable!("unmatched response to a UtxosByAddresses request"),
2121 };
2122
2123 let mut last_output_location = OutputLocation::from_usize(Height(0), 0, 0);
2124
2125 for utxo_data in utxos.utxos() {
2126 let address = utxo_data.0;
2127 let txid = *utxo_data.1;
2128 let height = utxo_data.2.height();
2129 let output_index = utxo_data.2.output_index();
2130 let script = utxo_data.3.lock_script.clone();
2131 let satoshis = u64::from(utxo_data.3.value);
2132
2133 let output_location = *utxo_data.2;
2134 assert!(
2136 output_location > last_output_location,
2137 "UTXOs were not in chain order:\n\
2138 {output_location:?} {address:?} {txid:?} was after:\n\
2139 {last_output_location:?}",
2140 );
2141
2142 let entry = Utxo {
2143 address,
2144 txid,
2145 output_index,
2146 script,
2147 satoshis,
2148 height,
2149 };
2150 response_utxos.push(entry);
2151
2152 last_output_location = output_location;
2153 }
2154
2155 if !utxos_request.chain_info {
2156 Ok(GetAddressUtxosResponse::Utxos(response_utxos))
2157 } else {
2158 let (height, hash) = utxos
2159 .last_height_and_hash()
2160 .ok_or_misc_error("No blocks in state")?;
2161
2162 Ok(GetAddressUtxosResponse::UtxosAndChainInfo(
2163 GetAddressUtxosResponseObject {
2164 utxos: response_utxos,
2165 hash,
2166 height,
2167 },
2168 ))
2169 }
2170 }
2171
2172 fn stop(&self) -> Result<String> {
2173 #[cfg(not(target_os = "windows"))]
2174 if self.network.is_regtest() {
2175 match nix::sys::signal::raise(nix::sys::signal::SIGINT) {
2176 Ok(_) => Ok("Zebra server stopping".to_string()),
2177 Err(error) => Err(ErrorObject::owned(
2178 ErrorCode::InternalError.code(),
2179 format!("Failed to shut down: {error}").as_str(),
2180 None::<()>,
2181 )),
2182 }
2183 } else {
2184 Err(ErrorObject::borrowed(
2185 ErrorCode::MethodNotFound.code(),
2186 "stop is only available on regtest networks",
2187 None,
2188 ))
2189 }
2190 #[cfg(target_os = "windows")]
2191 Err(ErrorObject::borrowed(
2192 ErrorCode::MethodNotFound.code(),
2193 "stop is not available in windows targets",
2194 None,
2195 ))
2196 }
2197
2198 fn get_block_count(&self) -> Result<u32> {
2199 best_chain_tip_height(&self.latest_chain_tip).map(|height| height.0)
2200 }
2201
2202 async fn get_block_hash(&self, index: i32) -> Result<GetBlockHashResponse> {
2203 let mut read_state = self.read_state.clone();
2204 let latest_chain_tip = self.latest_chain_tip.clone();
2205
2206 let tip_height = best_chain_tip_height(&latest_chain_tip)?;
2208
2209 let height = height_from_signed_int(index, tip_height)?;
2210
2211 let request = zebra_state::ReadRequest::BestChainBlockHash(height);
2212 let response = read_state
2213 .ready()
2214 .and_then(|service| service.call(request))
2215 .await
2216 .map_error(server::error::LegacyCode::default())?;
2217
2218 match response {
2219 zebra_state::ReadResponse::BlockHash(Some(hash)) => Ok(GetBlockHashResponse(hash)),
2220 zebra_state::ReadResponse::BlockHash(None) => Err(ErrorObject::borrowed(
2221 server::error::LegacyCode::InvalidParameter.into(),
2222 "Block not found",
2223 None,
2224 )),
2225 _ => unreachable!("unmatched response to a block request"),
2226 }
2227 }
2228
2229 async fn get_block_template(
2230 &self,
2231 parameters: Option<GetBlockTemplateParameters>,
2232 ) -> Result<GetBlockTemplateResponse> {
2233 use types::get_block_template::{
2234 check_parameters, check_synced_to_tip, fetch_mempool_transactions,
2235 fetch_state_tip_and_local_time, validate_block_proposal,
2236 zip317::select_mempool_transactions,
2237 };
2238
2239 let network = self.network.clone();
2241 let extra_coinbase_data = self.gbt.extra_coinbase_data();
2242
2243 let mempool = self.mempool.clone();
2245 let mut latest_chain_tip = self.latest_chain_tip.clone();
2246 let sync_status = self.gbt.sync_status();
2247 let read_state = self.read_state.clone();
2248
2249 if let Some(HexData(block_proposal_bytes)) = parameters
2250 .as_ref()
2251 .and_then(GetBlockTemplateParameters::block_proposal_data)
2252 {
2253 return validate_block_proposal(
2254 self.gbt.block_verifier_router(),
2255 block_proposal_bytes,
2256 network,
2257 latest_chain_tip,
2258 sync_status,
2259 )
2260 .await;
2261 }
2262
2263 check_parameters(¶meters)?;
2265
2266 let client_long_poll_id = parameters.as_ref().and_then(|params| params.long_poll_id);
2267
2268 let miner_address = self
2269 .gbt
2270 .miner_address()
2271 .ok_or_misc_error("miner_address not configured")?;
2272
2273 let mut max_time_reached = false;
2277
2278 let (
2281 server_long_poll_id,
2282 chain_tip_and_local_time,
2283 mempool_txs,
2284 mempool_tx_deps,
2285 submit_old,
2286 ) = loop {
2287 check_synced_to_tip(&network, latest_chain_tip.clone(), sync_status.clone())?;
2293 latest_chain_tip.mark_best_tip_seen();
2301
2302 let chain_tip_and_local_time @ zebra_state::GetBlockTemplateChainInfo {
2309 tip_hash,
2310 tip_height,
2311 max_time,
2312 cur_time,
2313 ..
2314 } = fetch_state_tip_and_local_time(read_state.clone()).await?;
2315
2316 let Some((mempool_txs, mempool_tx_deps)) =
2327 fetch_mempool_transactions(mempool.clone(), tip_hash)
2328 .await?
2329 .or_else(|| client_long_poll_id.is_none().then(Default::default))
2333 else {
2334 continue;
2335 };
2336
2337 let server_long_poll_id = LongPollInput::new(
2339 tip_height,
2340 tip_hash,
2341 max_time,
2342 mempool_txs.iter().map(|tx| tx.transaction.id),
2343 )
2344 .generate_id();
2345
2346 if Some(&server_long_poll_id) != client_long_poll_id.as_ref() || max_time_reached {
2351 let mut submit_old = client_long_poll_id
2352 .as_ref()
2353 .map(|old_long_poll_id| server_long_poll_id.submit_old(old_long_poll_id));
2354
2355 if max_time_reached {
2360 submit_old = Some(false);
2361 }
2362
2363 break (
2364 server_long_poll_id,
2365 chain_tip_and_local_time,
2366 mempool_txs,
2367 mempool_tx_deps,
2368 submit_old,
2369 );
2370 }
2371
2372 let wait_for_mempool_request =
2382 tokio::time::sleep(Duration::from_secs(MEMPOOL_LONG_POLL_INTERVAL));
2383
2384 let mut wait_for_best_tip_change = latest_chain_tip.clone();
2387 let wait_for_best_tip_change = wait_for_best_tip_change.best_tip_changed();
2388
2389 let duration_until_max_time = max_time.saturating_duration_since(cur_time);
2401 let wait_for_max_time: OptionFuture<_> = if duration_until_max_time.seconds() > 0 {
2402 Some(tokio::time::sleep(duration_until_max_time.to_std()))
2403 } else {
2404 None
2405 }
2406 .into();
2407
2408 tokio::select! {
2415 biased;
2418
2419 _elapsed = wait_for_mempool_request => {
2421 tracing::debug!(
2422 ?max_time,
2423 ?cur_time,
2424 ?server_long_poll_id,
2425 ?client_long_poll_id,
2426 MEMPOOL_LONG_POLL_INTERVAL,
2427 "checking for a new mempool change after waiting a few seconds"
2428 );
2429 }
2430
2431 tip_changed_result = wait_for_best_tip_change => {
2433 match tip_changed_result {
2434 Ok(()) => {
2435 latest_chain_tip.mark_best_tip_seen();
2439
2440 let new_tip_hash = latest_chain_tip.best_tip_hash();
2441 if new_tip_hash == Some(tip_hash) {
2442 tracing::debug!(
2443 ?max_time,
2444 ?cur_time,
2445 ?server_long_poll_id,
2446 ?client_long_poll_id,
2447 ?tip_hash,
2448 ?tip_height,
2449 "ignoring spurious state change notification"
2450 );
2451
2452 tokio::time::sleep(Duration::from_secs(
2454 MEMPOOL_LONG_POLL_INTERVAL,
2455 )).await;
2456
2457 continue;
2458 }
2459
2460 tracing::debug!(
2461 ?max_time,
2462 ?cur_time,
2463 ?server_long_poll_id,
2464 ?client_long_poll_id,
2465 "returning from long poll because state has changed"
2466 );
2467 }
2468
2469 Err(recv_error) => {
2470 tracing::info!(
2472 ?recv_error,
2473 ?max_time,
2474 ?cur_time,
2475 ?server_long_poll_id,
2476 ?client_long_poll_id,
2477 "returning from long poll due to a state error.\
2478 Is Zebra shutting down?"
2479 );
2480
2481 return Err(recv_error).map_error(server::error::LegacyCode::default());
2482 }
2483 }
2484 }
2485
2486 Some(_elapsed) = wait_for_max_time => {
2489 tracing::info!(
2491 ?max_time,
2492 ?cur_time,
2493 ?server_long_poll_id,
2494 ?client_long_poll_id,
2495 "returning from long poll because max time was reached"
2496 );
2497
2498 max_time_reached = true;
2499 }
2500 }
2501 };
2502
2503 let next_block_height =
2511 (chain_tip_and_local_time.tip_height + 1).expect("tip is far below Height::MAX");
2512
2513 tracing::debug!(
2514 mempool_tx_hashes = ?mempool_txs
2515 .iter()
2516 .map(|tx| tx.transaction.id.mined_id())
2517 .collect::<Vec<_>>(),
2518 "selecting transactions for the template from the mempool"
2519 );
2520
2521 let mempool_txs = select_mempool_transactions(
2523 &network,
2524 next_block_height,
2525 &miner_address,
2526 mempool_txs,
2527 mempool_tx_deps,
2528 extra_coinbase_data.clone(),
2529 );
2530
2531 tracing::debug!(
2532 selected_mempool_tx_hashes = ?mempool_txs
2533 .iter()
2534 .map(|#[cfg(not(test))] tx, #[cfg(test)] (_, tx)| tx.transaction.id.mined_id())
2535 .collect::<Vec<_>>(),
2536 "selected transactions for the template from the mempool"
2537 );
2538
2539 let response = BlockTemplateResponse::new_internal(
2542 &network,
2543 &miner_address,
2544 &chain_tip_and_local_time,
2545 server_long_poll_id,
2546 mempool_txs,
2547 submit_old,
2548 extra_coinbase_data,
2549 );
2550
2551 Ok(response.into())
2552 }
2553
2554 async fn submit_block(
2555 &self,
2556 HexData(block_bytes): HexData,
2557 _parameters: Option<SubmitBlockParameters>,
2558 ) -> Result<SubmitBlockResponse> {
2559 let mut block_verifier_router = self.gbt.block_verifier_router();
2560
2561 let block: Block = match block_bytes.zcash_deserialize_into() {
2562 Ok(block_bytes) => block_bytes,
2563 Err(error) => {
2564 tracing::info!(
2565 ?error,
2566 "submit block failed: block bytes could not be deserialized into a structurally valid block"
2567 );
2568
2569 return Ok(SubmitBlockErrorResponse::Rejected.into());
2570 }
2571 };
2572
2573 let height = block
2574 .coinbase_height()
2575 .ok_or_error(0, "coinbase height not found")?;
2576 let block_hash = block.hash();
2577
2578 let block_verifier_router_response = block_verifier_router
2579 .ready()
2580 .await
2581 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?
2582 .call(zebra_consensus::Request::Commit(Arc::new(block)))
2583 .await;
2584
2585 let chain_error = match block_verifier_router_response {
2586 Ok(hash) => {
2593 tracing::info!(?hash, ?height, "submit block accepted");
2594
2595 self.gbt
2596 .advertise_mined_block(hash, height)
2597 .map_error_with_prefix(0, "failed to send mined block to gossip task")?;
2598
2599 return Ok(SubmitBlockResponse::Accepted);
2600 }
2601
2602 Err(box_error) => {
2605 let error = box_error
2606 .downcast::<RouterError>()
2607 .map(|boxed_chain_error| *boxed_chain_error);
2608
2609 tracing::info!(
2610 ?error,
2611 ?block_hash,
2612 ?height,
2613 "submit block failed verification"
2614 );
2615
2616 error
2617 }
2618 };
2619
2620 let response = match chain_error {
2621 Ok(source) if source.is_duplicate_request() => SubmitBlockErrorResponse::Duplicate,
2622
2623 Ok(_verify_chain_error) => SubmitBlockErrorResponse::Rejected,
2639
2640 Err(_unknown_error_type) => SubmitBlockErrorResponse::Rejected,
2643 };
2644
2645 Ok(response.into())
2646 }
2647
2648 async fn get_mining_info(&self) -> Result<GetMiningInfoResponse> {
2649 let network = self.network.clone();
2650 let mut read_state = self.read_state.clone();
2651
2652 let chain_tip = self.latest_chain_tip.clone();
2653 let tip_height = chain_tip.best_tip_height().unwrap_or(Height(0)).0;
2654
2655 let mut current_block_tx = None;
2656 if tip_height > 0 {
2657 let mined_tx_ids = chain_tip.best_tip_mined_transaction_ids();
2658 current_block_tx =
2659 (!mined_tx_ids.is_empty()).then(|| mined_tx_ids.len().saturating_sub(1));
2660 }
2661
2662 let solution_rate_fut = self.get_network_sol_ps(None, None);
2663 let mut current_block_size = None;
2665 if tip_height > 0 {
2666 let request = zebra_state::ReadRequest::TipBlockSize;
2667 let response: zebra_state::ReadResponse = read_state
2668 .ready()
2669 .and_then(|service| service.call(request))
2670 .await
2671 .map_error(server::error::LegacyCode::default())?;
2672 current_block_size = match response {
2673 zebra_state::ReadResponse::TipBlockSize(Some(block_size)) => Some(block_size),
2674 _ => None,
2675 };
2676 }
2677
2678 Ok(GetMiningInfoResponse::new_internal(
2679 tip_height,
2680 current_block_size,
2681 current_block_tx,
2682 network,
2683 solution_rate_fut.await?,
2684 ))
2685 }
2686
2687 async fn get_network_sol_ps(
2688 &self,
2689 num_blocks: Option<i32>,
2690 height: Option<i32>,
2691 ) -> Result<u64> {
2692 let mut num_blocks = num_blocks.unwrap_or(DEFAULT_SOLUTION_RATE_WINDOW_SIZE);
2694 if num_blocks < 1 {
2696 num_blocks = i32::try_from(POW_AVERAGING_WINDOW).expect("fits in i32");
2697 }
2698 let num_blocks =
2699 usize::try_from(num_blocks).expect("just checked for negatives, i32 fits in usize");
2700
2701 let height = height.and_then(|height| height.try_into_height().ok());
2704
2705 let mut read_state = self.read_state.clone();
2706
2707 let request = ReadRequest::SolutionRate { num_blocks, height };
2708
2709 let response = read_state
2710 .ready()
2711 .and_then(|service| service.call(request))
2712 .await
2713 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
2714
2715 let solution_rate = match response {
2716 ReadResponse::SolutionRate(solution_rate) => solution_rate.unwrap_or(0),
2718
2719 _ => unreachable!("unmatched response to a solution rate request"),
2720 };
2721
2722 Ok(solution_rate
2723 .try_into()
2724 .expect("per-second solution rate always fits in u64"))
2725 }
2726
2727 async fn get_network_info(&self) -> Result<GetNetworkInfoResponse> {
2728 let version = GetInfoResponse::version_from_string(&self.build_version)
2729 .expect("invalid version string");
2730
2731 let subversion = self.user_agent.clone();
2732
2733 let protocol_version = zebra_network::constants::CURRENT_NETWORK_PROTOCOL_VERSION.0;
2734
2735 let local_services = format!("{:016x}", PeerServices::NODE_NETWORK);
2737
2738 let timeoffset = 0;
2740
2741 let connections = self.address_book.recently_live_peers(Utc::now()).len();
2742
2743 let networks = vec![
2745 NetworkInfo::new("ipv4".to_string(), false, true, "".to_string(), false),
2746 NetworkInfo::new("ipv6".to_string(), false, true, "".to_string(), false),
2747 NetworkInfo::new("onion".to_string(), false, false, "".to_string(), false),
2748 ];
2749
2750 let relay_fee = zebra_chain::transaction::zip317::MIN_MEMPOOL_TX_FEE_RATE as f64
2751 / (zebra_chain::amount::COIN as f64);
2752
2753 let local_addresses = vec![];
2755
2756 let warnings = "".to_string();
2758
2759 let response = GetNetworkInfoResponse {
2760 version,
2761 subversion,
2762 protocol_version,
2763 local_services,
2764 timeoffset,
2765 connections,
2766 networks,
2767 relay_fee,
2768 local_addresses,
2769 warnings,
2770 };
2771
2772 Ok(response)
2773 }
2774
2775 async fn get_peer_info(&self) -> Result<Vec<PeerInfo>> {
2776 let address_book = self.address_book.clone();
2777 Ok(address_book
2778 .recently_live_peers(chrono::Utc::now())
2779 .into_iter()
2780 .map(PeerInfo::from)
2781 .collect())
2782 }
2783
2784 async fn validate_address(&self, raw_address: String) -> Result<ValidateAddressResponse> {
2785 let network = self.network.clone();
2786
2787 validate_address(network, raw_address)
2788 }
2789
2790 async fn z_validate_address(&self, raw_address: String) -> Result<ZValidateAddressResponse> {
2791 let network = self.network.clone();
2792
2793 z_validate_address(network, raw_address)
2794 }
2795
2796 async fn get_block_subsidy(&self, height: Option<u32>) -> Result<GetBlockSubsidyResponse> {
2797 let latest_chain_tip = self.latest_chain_tip.clone();
2798 let network = self.network.clone();
2799
2800 let height = if let Some(height) = height {
2801 Height(height)
2802 } else {
2803 best_chain_tip_height(&latest_chain_tip)?
2804 };
2805
2806 if height < network.height_for_first_halving() {
2807 return Err(ErrorObject::borrowed(
2808 0,
2809 "Zebra does not support founders' reward subsidies, \
2810 use a block height that is after the first halving",
2811 None,
2812 ));
2813 }
2814
2815 let founders = Amount::zero();
2817
2818 let total_block_subsidy =
2819 block_subsidy(height, &network).map_error(server::error::LegacyCode::default())?;
2820 let miner_subsidy = miner_subsidy(height, &network, total_block_subsidy)
2821 .map_error(server::error::LegacyCode::default())?;
2822
2823 let (lockbox_streams, mut funding_streams): (Vec<_>, Vec<_>) =
2824 funding_stream_values(height, &network, total_block_subsidy)
2825 .map_error(server::error::LegacyCode::default())?
2826 .into_iter()
2827 .partition(|(receiver, _)| matches!(receiver, FundingStreamReceiver::Deferred));
2829
2830 let is_nu6 = NetworkUpgrade::current(&network, height) == NetworkUpgrade::Nu6;
2831
2832 let [lockbox_total, funding_streams_total]: [std::result::Result<
2833 Amount<NonNegative>,
2834 amount::Error,
2835 >; 2] = [&lockbox_streams, &funding_streams]
2836 .map(|streams| streams.iter().map(|&(_, amount)| amount).sum());
2837
2838 funding_streams.sort_by_key(|(receiver, _funding_stream)| {
2840 ZCASHD_FUNDING_STREAM_ORDER
2841 .iter()
2842 .position(|zcashd_receiver| zcashd_receiver == receiver)
2843 });
2844
2845 let [funding_streams, lockbox_streams]: [Vec<_>; 2] = [funding_streams, lockbox_streams]
2847 .map(|streams| {
2848 streams
2849 .into_iter()
2850 .map(|(receiver, value)| {
2851 let address = funding_stream_address(height, &network, receiver);
2852 types::subsidy::FundingStream::new_internal(
2853 is_nu6, receiver, value, address,
2854 )
2855 })
2856 .collect()
2857 });
2858
2859 Ok(GetBlockSubsidyResponse {
2860 miner: miner_subsidy.into(),
2861 founders: founders.into(),
2862 funding_streams,
2863 lockbox_streams,
2864 funding_streams_total: funding_streams_total
2865 .map_error(server::error::LegacyCode::default())?
2866 .into(),
2867 lockbox_total: lockbox_total
2868 .map_error(server::error::LegacyCode::default())?
2869 .into(),
2870 total_block_subsidy: total_block_subsidy.into(),
2871 })
2872 }
2873
2874 async fn get_difficulty(&self) -> Result<f64> {
2875 chain_tip_difficulty(self.network.clone(), self.read_state.clone(), false).await
2876 }
2877
2878 async fn z_list_unified_receivers(
2879 &self,
2880 address: String,
2881 ) -> Result<ZListUnifiedReceiversResponse> {
2882 use zcash_address::unified::Container;
2883
2884 let (network, unified_address): (
2885 zcash_protocol::consensus::NetworkType,
2886 zcash_address::unified::Address,
2887 ) = zcash_address::unified::Encoding::decode(address.clone().as_str())
2888 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
2889
2890 let mut p2pkh = None;
2891 let mut p2sh = None;
2892 let mut orchard = None;
2893 let mut sapling = None;
2894
2895 for item in unified_address.items() {
2896 match item {
2897 zcash_address::unified::Receiver::Orchard(_data) => {
2898 let addr = zcash_address::unified::Address::try_from_items(vec![item])
2899 .expect("using data already decoded as valid");
2900 orchard = Some(addr.encode(&network));
2901 }
2902 zcash_address::unified::Receiver::Sapling(data) => {
2903 let addr = zebra_chain::primitives::Address::try_from_sapling(network, data)
2904 .expect("using data already decoded as valid");
2905 sapling = Some(addr.payment_address().unwrap_or_default());
2906 }
2907 zcash_address::unified::Receiver::P2pkh(data) => {
2908 let addr =
2909 zebra_chain::primitives::Address::try_from_transparent_p2pkh(network, data)
2910 .expect("using data already decoded as valid");
2911 p2pkh = Some(addr.payment_address().unwrap_or_default());
2912 }
2913 zcash_address::unified::Receiver::P2sh(data) => {
2914 let addr =
2915 zebra_chain::primitives::Address::try_from_transparent_p2sh(network, data)
2916 .expect("using data already decoded as valid");
2917 p2sh = Some(addr.payment_address().unwrap_or_default());
2918 }
2919 _ => (),
2920 }
2921 }
2922
2923 Ok(ZListUnifiedReceiversResponse::new(
2924 orchard, sapling, p2pkh, p2sh,
2925 ))
2926 }
2927
2928 async fn invalidate_block(&self, block_hash: String) -> Result<()> {
2929 let block_hash = block_hash
2930 .parse()
2931 .map_error(server::error::LegacyCode::InvalidParameter)?;
2932
2933 self.state
2934 .clone()
2935 .oneshot(zebra_state::Request::InvalidateBlock(block_hash))
2936 .await
2937 .map(|rsp| assert_eq!(rsp, zebra_state::Response::Invalidated(block_hash)))
2938 .map_misc_error()
2939 }
2940
2941 async fn reconsider_block(&self, block_hash: String) -> Result<Vec<block::Hash>> {
2942 let block_hash = block_hash
2943 .parse()
2944 .map_error(server::error::LegacyCode::InvalidParameter)?;
2945
2946 self.state
2947 .clone()
2948 .oneshot(zebra_state::Request::ReconsiderBlock(block_hash))
2949 .await
2950 .map(|rsp| match rsp {
2951 zebra_state::Response::Reconsidered(block_hashes) => block_hashes,
2952 _ => unreachable!("unmatched response to a reconsider block request"),
2953 })
2954 .map_misc_error()
2955 }
2956
2957 async fn generate(&self, num_blocks: u32) -> Result<Vec<Hash>> {
2958 let mut rpc = self.clone();
2959 let network = self.network.clone();
2960
2961 if !network.disable_pow() {
2962 return Err(ErrorObject::borrowed(
2963 0,
2964 "generate is only supported on networks where PoW is disabled",
2965 None,
2966 ));
2967 }
2968
2969 let mut block_hashes = Vec::new();
2970 for _ in 0..num_blocks {
2971 let mut extra_coinbase_data = [0u8; 32];
2976 OsRng.fill_bytes(&mut extra_coinbase_data);
2977 rpc.gbt
2978 .set_extra_coinbase_data(extra_coinbase_data.to_vec());
2979
2980 let block_template = rpc
2981 .get_block_template(None)
2982 .await
2983 .map_error(server::error::LegacyCode::default())?;
2984
2985 let GetBlockTemplateResponse::TemplateMode(block_template) = block_template else {
2986 return Err(ErrorObject::borrowed(
2987 0,
2988 "error generating block template",
2989 None,
2990 ));
2991 };
2992
2993 let proposal_block = proposal_block_from_template(
2994 &block_template,
2995 BlockTemplateTimeSource::CurTime,
2996 &network,
2997 )
2998 .map_error(server::error::LegacyCode::default())?;
2999
3000 let hex_proposal_block = HexData(
3001 proposal_block
3002 .zcash_serialize_to_vec()
3003 .map_error(server::error::LegacyCode::default())?,
3004 );
3005
3006 let r = rpc
3007 .submit_block(hex_proposal_block, None)
3008 .await
3009 .map_error(server::error::LegacyCode::default())?;
3010 match r {
3011 SubmitBlockResponse::Accepted => { }
3012 SubmitBlockResponse::ErrorResponse(response) => {
3013 return Err(ErrorObject::owned(
3014 server::error::LegacyCode::Misc.into(),
3015 format!("block was rejected: {:?}", response),
3016 None::<()>,
3017 ));
3018 }
3019 }
3020
3021 block_hashes.push(GetBlockHashResponse(proposal_block.hash()));
3022 }
3023
3024 Ok(block_hashes)
3025 }
3026
3027 async fn add_node(
3028 &self,
3029 addr: zebra_network::PeerSocketAddr,
3030 command: AddNodeCommand,
3031 ) -> Result<()> {
3032 if self.network.is_regtest() {
3033 match command {
3034 AddNodeCommand::Add => {
3035 tracing::info!(?addr, "adding peer address to the address book");
3036 if self.address_book.clone().add_peer(addr) {
3037 Ok(())
3038 } else {
3039 return Err(ErrorObject::owned(
3040 server::error::LegacyCode::ClientNodeAlreadyAdded.into(),
3041 format!("peer address was already present in the address book: {addr}"),
3042 None::<()>,
3043 ));
3044 }
3045 }
3046 }
3047 } else {
3048 return Err(ErrorObject::owned(
3049 ErrorCode::InvalidParams.code(),
3050 "addnode command is only supported on regtest",
3051 None::<()>,
3052 ));
3053 }
3054 }
3055}
3056
3057pub fn best_chain_tip_height<Tip>(latest_chain_tip: &Tip) -> Result<Height>
3062where
3063 Tip: ChainTip + Clone + Send + Sync + 'static,
3064{
3065 latest_chain_tip
3066 .best_tip_height()
3067 .ok_or_misc_error("No blocks in state")
3068}
3069
3070#[allow(clippy::too_many_arguments)]
3074#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3075pub struct GetInfoResponse {
3076 #[getter(rename = "raw_version")]
3078 version: u64,
3079
3080 build: String,
3082
3083 subversion: String,
3085
3086 #[serde(rename = "protocolversion")]
3088 protocol_version: u32,
3089
3090 blocks: u32,
3092
3093 connections: usize,
3095
3096 #[serde(skip_serializing_if = "Option::is_none")]
3098 proxy: Option<String>,
3099
3100 difficulty: f64,
3102
3103 testnet: bool,
3105
3106 #[serde(rename = "paytxfee")]
3108 pay_tx_fee: f64,
3109
3110 #[serde(rename = "relayfee")]
3112 relay_fee: f64,
3113
3114 errors: String,
3116
3117 #[serde(rename = "errorstimestamp")]
3119 errors_timestamp: String,
3120}
3121
3122#[deprecated(note = "Use `GetInfoResponse` instead")]
3123pub use self::GetInfoResponse as GetInfo;
3124
3125impl Default for GetInfoResponse {
3126 fn default() -> Self {
3127 GetInfoResponse {
3128 version: 0,
3129 build: "some build version".to_string(),
3130 subversion: "some subversion".to_string(),
3131 protocol_version: 0,
3132 blocks: 0,
3133 connections: 0,
3134 proxy: None,
3135 difficulty: 0.0,
3136 testnet: false,
3137 pay_tx_fee: 0.0,
3138 relay_fee: 0.0,
3139 errors: "no errors".to_string(),
3140 errors_timestamp: "no errors timestamp".to_string(),
3141 }
3142 }
3143}
3144
3145impl GetInfoResponse {
3146 #[allow(clippy::too_many_arguments)]
3148 #[deprecated(note = "Use `GetInfoResponse::new` instead")]
3149 pub fn from_parts(
3150 version: u64,
3151 build: String,
3152 subversion: String,
3153 protocol_version: u32,
3154 blocks: u32,
3155 connections: usize,
3156 proxy: Option<String>,
3157 difficulty: f64,
3158 testnet: bool,
3159 pay_tx_fee: f64,
3160 relay_fee: f64,
3161 errors: String,
3162 errors_timestamp: String,
3163 ) -> Self {
3164 Self {
3165 version,
3166 build,
3167 subversion,
3168 protocol_version,
3169 blocks,
3170 connections,
3171 proxy,
3172 difficulty,
3173 testnet,
3174 pay_tx_fee,
3175 relay_fee,
3176 errors,
3177 errors_timestamp,
3178 }
3179 }
3180
3181 pub fn into_parts(
3183 self,
3184 ) -> (
3185 u64,
3186 String,
3187 String,
3188 u32,
3189 u32,
3190 usize,
3191 Option<String>,
3192 f64,
3193 bool,
3194 f64,
3195 f64,
3196 String,
3197 String,
3198 ) {
3199 (
3200 self.version,
3201 self.build,
3202 self.subversion,
3203 self.protocol_version,
3204 self.blocks,
3205 self.connections,
3206 self.proxy,
3207 self.difficulty,
3208 self.testnet,
3209 self.pay_tx_fee,
3210 self.relay_fee,
3211 self.errors,
3212 self.errors_timestamp,
3213 )
3214 }
3215
3216 fn version_from_string(build_string: &str) -> Option<u64> {
3218 let semver_version = semver::Version::parse(build_string.strip_prefix('v')?).ok()?;
3219 let build_number = semver_version
3220 .build
3221 .as_str()
3222 .split('.')
3223 .next()
3224 .and_then(|num_str| num_str.parse::<u64>().ok())
3225 .unwrap_or_default();
3226
3227 let version_number = 1_000_000 * semver_version.major
3229 + 10_000 * semver_version.minor
3230 + 100 * semver_version.patch
3231 + build_number;
3232
3233 Some(version_number)
3234 }
3235}
3236
3237pub type BlockchainValuePoolBalances = [GetBlockchainInfoBalance; 5];
3239
3240#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters)]
3244pub struct GetBlockchainInfoResponse {
3245 chain: String,
3247
3248 #[getter(copy)]
3250 blocks: Height,
3251
3252 #[getter(copy)]
3255 headers: Height,
3256
3257 difficulty: f64,
3259
3260 #[serde(rename = "verificationprogress")]
3262 verification_progress: f64,
3263
3264 #[serde(rename = "chainwork")]
3266 chain_work: u64,
3267
3268 pruned: bool,
3270
3271 size_on_disk: u64,
3273
3274 commitments: u64,
3276
3277 #[serde(rename = "bestblockhash", with = "hex")]
3279 #[getter(copy)]
3280 best_block_hash: block::Hash,
3281
3282 #[serde(rename = "estimatedheight")]
3286 #[getter(copy)]
3287 estimated_height: Height,
3288
3289 #[serde(rename = "chainSupply")]
3291 chain_supply: GetBlockchainInfoBalance,
3292
3293 #[serde(rename = "valuePools")]
3295 value_pools: BlockchainValuePoolBalances,
3296
3297 upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
3299
3300 #[getter(copy)]
3302 consensus: TipConsensusBranch,
3303}
3304
3305impl Default for GetBlockchainInfoResponse {
3306 fn default() -> Self {
3307 Self {
3308 chain: "main".to_string(),
3309 blocks: Height(1),
3310 best_block_hash: block::Hash([0; 32]),
3311 estimated_height: Height(1),
3312 chain_supply: GetBlockchainInfoBalance::chain_supply(Default::default()),
3313 value_pools: GetBlockchainInfoBalance::zero_pools(),
3314 upgrades: IndexMap::new(),
3315 consensus: TipConsensusBranch {
3316 chain_tip: ConsensusBranchIdHex(ConsensusBranchId::default()),
3317 next_block: ConsensusBranchIdHex(ConsensusBranchId::default()),
3318 },
3319 headers: Height(1),
3320 difficulty: 0.0,
3321 verification_progress: 0.0,
3322 chain_work: 0,
3323 pruned: false,
3324 size_on_disk: 0,
3325 commitments: 0,
3326 }
3327 }
3328}
3329
3330impl GetBlockchainInfoResponse {
3331 #[allow(clippy::too_many_arguments)]
3335 pub fn new(
3336 chain: String,
3337 blocks: Height,
3338 best_block_hash: block::Hash,
3339 estimated_height: Height,
3340 chain_supply: GetBlockchainInfoBalance,
3341 value_pools: BlockchainValuePoolBalances,
3342 upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
3343 consensus: TipConsensusBranch,
3344 headers: Height,
3345 difficulty: f64,
3346 verification_progress: f64,
3347 chain_work: u64,
3348 pruned: bool,
3349 size_on_disk: u64,
3350 commitments: u64,
3351 ) -> Self {
3352 Self {
3353 chain,
3354 blocks,
3355 best_block_hash,
3356 estimated_height,
3357 chain_supply,
3358 value_pools,
3359 upgrades,
3360 consensus,
3361 headers,
3362 difficulty,
3363 verification_progress,
3364 chain_work,
3365 pruned,
3366 size_on_disk,
3367 commitments,
3368 }
3369 }
3370}
3371
3372#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
3374#[serde(from = "DGetAddressBalanceRequest")]
3375pub struct GetAddressBalanceRequest {
3376 addresses: Vec<String>,
3378}
3379
3380impl From<DGetAddressBalanceRequest> for GetAddressBalanceRequest {
3381 fn from(address_strings: DGetAddressBalanceRequest) -> Self {
3382 match address_strings {
3383 DGetAddressBalanceRequest::Addresses { addresses } => {
3384 GetAddressBalanceRequest { addresses }
3385 }
3386 DGetAddressBalanceRequest::Address(address) => GetAddressBalanceRequest {
3387 addresses: vec![address],
3388 },
3389 }
3390 }
3391}
3392
3393#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize)]
3395#[serde(untagged)]
3396enum DGetAddressBalanceRequest {
3397 Addresses { addresses: Vec<String> },
3399 Address(String),
3401}
3402
3403#[deprecated(note = "Use `GetAddressBalanceRequest` instead.")]
3405pub type AddressStrings = GetAddressBalanceRequest;
3406
3407trait ValidateAddresses {
3408 fn valid_addresses(&self) -> Result<HashSet<Address>> {
3412 let valid_addresses: HashSet<Address> = self
3415 .addresses()
3416 .iter()
3417 .map(|address| {
3418 address
3419 .parse()
3420 .map_error(server::error::LegacyCode::InvalidAddressOrKey)
3421 })
3422 .collect::<Result<_>>()?;
3423
3424 Ok(valid_addresses)
3425 }
3426
3427 fn addresses(&self) -> &[String];
3429}
3430
3431impl ValidateAddresses for GetAddressBalanceRequest {
3432 fn addresses(&self) -> &[String] {
3433 &self.addresses
3434 }
3435}
3436
3437impl GetAddressBalanceRequest {
3438 pub fn new(addresses: Vec<String>) -> GetAddressBalanceRequest {
3440 GetAddressBalanceRequest { addresses }
3441 }
3442
3443 #[deprecated(
3445 note = "Use `AddressStrings::new` instead. Validity will be checked by the server."
3446 )]
3447 pub fn new_valid(addresses: Vec<String>) -> Result<GetAddressBalanceRequest> {
3448 let req = Self { addresses };
3449 req.valid_addresses()?;
3450 Ok(req)
3451 }
3452}
3453
3454#[derive(
3456 Clone,
3457 Copy,
3458 Debug,
3459 Default,
3460 Eq,
3461 PartialEq,
3462 Hash,
3463 serde::Serialize,
3464 serde::Deserialize,
3465 Getters,
3466 new,
3467)]
3468pub struct GetAddressBalanceResponse {
3469 balance: u64,
3471 pub received: u64,
3473}
3474
3475#[deprecated(note = "Use `GetAddressBalanceResponse` instead.")]
3476pub use self::GetAddressBalanceResponse as AddressBalance;
3477
3478#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, Getters, new)]
3480#[serde(from = "DGetAddressUtxosRequest")]
3481pub struct GetAddressUtxosRequest {
3482 addresses: Vec<String>,
3484 #[serde(default)]
3486 #[serde(rename = "chainInfo")]
3487 chain_info: bool,
3488}
3489
3490impl From<DGetAddressUtxosRequest> for GetAddressUtxosRequest {
3491 fn from(request: DGetAddressUtxosRequest) -> Self {
3492 match request {
3493 DGetAddressUtxosRequest::Single(addr) => GetAddressUtxosRequest {
3494 addresses: vec![addr],
3495 chain_info: false,
3496 },
3497 DGetAddressUtxosRequest::Object {
3498 addresses,
3499 chain_info,
3500 } => GetAddressUtxosRequest {
3501 addresses,
3502 chain_info,
3503 },
3504 }
3505 }
3506}
3507
3508#[derive(Debug, serde::Deserialize)]
3510#[serde(untagged)]
3511enum DGetAddressUtxosRequest {
3512 Single(String),
3514 Object {
3516 addresses: Vec<String>,
3518 #[serde(default)]
3520 #[serde(rename = "chainInfo")]
3521 chain_info: bool,
3522 },
3523}
3524
3525impl ValidateAddresses for GetAddressUtxosRequest {
3526 fn addresses(&self) -> &[String] {
3527 &self.addresses
3528 }
3529}
3530
3531#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
3533pub struct ConsensusBranchIdHex(#[serde(with = "hex")] ConsensusBranchId);
3534
3535impl ConsensusBranchIdHex {
3536 pub fn new(consensus_branch_id: u32) -> Self {
3538 ConsensusBranchIdHex(consensus_branch_id.into())
3539 }
3540
3541 pub fn inner(&self) -> u32 {
3543 self.0.into()
3544 }
3545}
3546
3547#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3549pub struct NetworkUpgradeInfo {
3550 name: NetworkUpgrade,
3554
3555 #[serde(rename = "activationheight")]
3557 activation_height: Height,
3558
3559 status: NetworkUpgradeStatus,
3561}
3562
3563impl NetworkUpgradeInfo {
3564 pub fn from_parts(
3566 name: NetworkUpgrade,
3567 activation_height: Height,
3568 status: NetworkUpgradeStatus,
3569 ) -> Self {
3570 Self {
3571 name,
3572 activation_height,
3573 status,
3574 }
3575 }
3576
3577 pub fn into_parts(self) -> (NetworkUpgrade, Height, NetworkUpgradeStatus) {
3579 (self.name, self.activation_height, self.status)
3580 }
3581}
3582
3583#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3585pub enum NetworkUpgradeStatus {
3586 #[serde(rename = "active")]
3591 Active,
3592
3593 #[serde(rename = "disabled")]
3595 Disabled,
3596
3597 #[serde(rename = "pending")]
3599 Pending,
3600}
3601
3602#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3606pub struct TipConsensusBranch {
3607 #[serde(rename = "chaintip")]
3609 chain_tip: ConsensusBranchIdHex,
3610
3611 #[serde(rename = "nextblock")]
3613 next_block: ConsensusBranchIdHex,
3614}
3615
3616impl TipConsensusBranch {
3617 pub fn from_parts(chain_tip: u32, next_block: u32) -> Self {
3619 Self {
3620 chain_tip: ConsensusBranchIdHex::new(chain_tip),
3621 next_block: ConsensusBranchIdHex::new(next_block),
3622 }
3623 }
3624
3625 pub fn into_parts(self) -> (u32, u32) {
3627 (self.chain_tip.inner(), self.next_block.inner())
3628 }
3629}
3630
3631#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3637pub struct SendRawTransactionResponse(#[serde(with = "hex")] transaction::Hash);
3638
3639#[deprecated(note = "Use `SendRawTransactionResponse` instead")]
3640pub use self::SendRawTransactionResponse as SentTransactionHash;
3641
3642impl Default for SendRawTransactionResponse {
3643 fn default() -> Self {
3644 Self(transaction::Hash::from([0; 32]))
3645 }
3646}
3647
3648impl SendRawTransactionResponse {
3649 pub fn new(hash: transaction::Hash) -> Self {
3651 SendRawTransactionResponse(hash)
3652 }
3653
3654 #[deprecated(note = "Use `SentTransactionHash::hash` instead")]
3656 pub fn inner(&self) -> transaction::Hash {
3657 self.hash()
3658 }
3659
3660 pub fn hash(&self) -> transaction::Hash {
3662 self.0
3663 }
3664}
3665
3666#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3670#[serde(untagged)]
3671pub enum GetBlockResponse {
3672 Raw(#[serde(with = "hex")] SerializedBlock),
3674 Object(Box<BlockObject>),
3676}
3677
3678#[deprecated(note = "Use `GetBlockResponse` instead")]
3679pub use self::GetBlockResponse as GetBlock;
3680
3681impl Default for GetBlockResponse {
3682 fn default() -> Self {
3683 GetBlockResponse::Object(Box::new(BlockObject {
3684 hash: block::Hash([0; 32]),
3685 confirmations: 0,
3686 height: None,
3687 time: None,
3688 tx: Vec::new(),
3689 trees: GetBlockTrees::default(),
3690 size: None,
3691 version: None,
3692 merkle_root: None,
3693 block_commitments: None,
3694 final_sapling_root: None,
3695 final_orchard_root: None,
3696 nonce: None,
3697 bits: None,
3698 difficulty: None,
3699 chain_supply: None,
3700 value_pools: None,
3701 previous_block_hash: None,
3702 next_block_hash: None,
3703 solution: None,
3704 }))
3705 }
3706}
3707
3708#[allow(clippy::too_many_arguments)]
3710#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3711pub struct BlockObject {
3712 #[getter(copy)]
3714 #[serde(with = "hex")]
3715 hash: block::Hash,
3716
3717 confirmations: i64,
3720
3721 #[serde(skip_serializing_if = "Option::is_none")]
3723 #[getter(copy)]
3724 size: Option<i64>,
3725
3726 #[serde(skip_serializing_if = "Option::is_none")]
3728 #[getter(copy)]
3729 height: Option<Height>,
3730
3731 #[serde(skip_serializing_if = "Option::is_none")]
3733 #[getter(copy)]
3734 version: Option<u32>,
3735
3736 #[serde(with = "opthex", rename = "merkleroot")]
3738 #[serde(skip_serializing_if = "Option::is_none")]
3739 #[getter(copy)]
3740 merkle_root: Option<block::merkle::Root>,
3741
3742 #[serde(with = "opthex", rename = "blockcommitments")]
3745 #[serde(skip_serializing_if = "Option::is_none")]
3746 #[getter(copy)]
3747 block_commitments: Option<[u8; 32]>,
3748
3749 #[serde(with = "opthex", rename = "finalsaplingroot")]
3753 #[serde(skip_serializing_if = "Option::is_none")]
3754 #[getter(copy)]
3755 final_sapling_root: Option<[u8; 32]>,
3756
3757 #[serde(with = "opthex", rename = "finalorchardroot")]
3759 #[serde(skip_serializing_if = "Option::is_none")]
3760 #[getter(copy)]
3761 final_orchard_root: Option<[u8; 32]>,
3762
3763 tx: Vec<GetBlockTransaction>,
3768
3769 #[serde(skip_serializing_if = "Option::is_none")]
3771 #[getter(copy)]
3772 time: Option<i64>,
3773
3774 #[serde(with = "opthex")]
3776 #[serde(skip_serializing_if = "Option::is_none")]
3777 #[getter(copy)]
3778 nonce: Option<[u8; 32]>,
3779
3780 #[serde(with = "opthex")]
3783 #[serde(skip_serializing_if = "Option::is_none")]
3784 #[getter(copy)]
3785 solution: Option<Solution>,
3786
3787 #[serde(with = "opthex")]
3789 #[serde(skip_serializing_if = "Option::is_none")]
3790 #[getter(copy)]
3791 bits: Option<CompactDifficulty>,
3792
3793 #[serde(skip_serializing_if = "Option::is_none")]
3796 #[getter(copy)]
3797 difficulty: Option<f64>,
3798
3799 #[serde(rename = "chainSupply")]
3804 #[serde(skip_serializing_if = "Option::is_none")]
3805 chain_supply: Option<GetBlockchainInfoBalance>,
3806
3807 #[serde(rename = "valuePools")]
3809 #[serde(skip_serializing_if = "Option::is_none")]
3810 value_pools: Option<BlockchainValuePoolBalances>,
3811
3812 #[getter(copy)]
3814 trees: GetBlockTrees,
3815
3816 #[serde(rename = "previousblockhash", skip_serializing_if = "Option::is_none")]
3818 #[serde(with = "opthex")]
3819 #[getter(copy)]
3820 previous_block_hash: Option<block::Hash>,
3821
3822 #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
3824 #[serde(with = "opthex")]
3825 #[getter(copy)]
3826 next_block_hash: Option<block::Hash>,
3827}
3828
3829#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3830#[serde(untagged)]
3831pub enum GetBlockTransaction {
3834 Hash(#[serde(with = "hex")] transaction::Hash),
3836 Object(Box<TransactionObject>),
3838}
3839
3840#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3844#[serde(untagged)]
3845pub enum GetBlockHeaderResponse {
3846 Raw(hex_data::HexData),
3848
3849 Object(Box<BlockHeaderObject>),
3851}
3852
3853#[deprecated(note = "Use `GetBlockHeaderResponse` instead")]
3854pub use self::GetBlockHeaderResponse as GetBlockHeader;
3855
3856#[allow(clippy::too_many_arguments)]
3857#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3858pub struct BlockHeaderObject {
3862 #[serde(with = "hex")]
3864 #[getter(copy)]
3865 hash: block::Hash,
3866
3867 confirmations: i64,
3870
3871 #[getter(copy)]
3873 height: Height,
3874
3875 version: u32,
3877
3878 #[serde(with = "hex", rename = "merkleroot")]
3880 #[getter(copy)]
3881 merkle_root: block::merkle::Root,
3882
3883 #[serde(with = "hex", rename = "blockcommitments")]
3886 #[getter(copy)]
3887 block_commitments: [u8; 32],
3888
3889 #[serde(with = "hex", rename = "finalsaplingroot")]
3891 #[getter(copy)]
3892 final_sapling_root: [u8; 32],
3893
3894 #[serde(skip)]
3897 sapling_tree_size: u64,
3898
3899 time: i64,
3901
3902 #[serde(with = "hex")]
3904 #[getter(copy)]
3905 nonce: [u8; 32],
3906
3907 #[serde(with = "hex")]
3909 #[getter(copy)]
3910 solution: Solution,
3911
3912 #[serde(with = "hex")]
3914 #[getter(copy)]
3915 bits: CompactDifficulty,
3916
3917 difficulty: f64,
3920
3921 #[serde(rename = "previousblockhash")]
3923 #[serde(with = "hex")]
3924 #[getter(copy)]
3925 previous_block_hash: block::Hash,
3926
3927 #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
3929 #[getter(copy)]
3930 #[serde(with = "opthex")]
3931 next_block_hash: Option<block::Hash>,
3932}
3933
3934#[deprecated(note = "Use `BlockHeaderObject` instead")]
3935pub use BlockHeaderObject as GetBlockHeaderObject;
3936
3937impl Default for GetBlockHeaderResponse {
3938 fn default() -> Self {
3939 GetBlockHeaderResponse::Object(Box::default())
3940 }
3941}
3942
3943impl Default for BlockHeaderObject {
3944 fn default() -> Self {
3945 let difficulty: ExpandedDifficulty = zebra_chain::work::difficulty::U256::one().into();
3946
3947 BlockHeaderObject {
3948 hash: block::Hash([0; 32]),
3949 confirmations: 0,
3950 height: Height::MIN,
3951 version: 4,
3952 merkle_root: block::merkle::Root([0; 32]),
3953 block_commitments: Default::default(),
3954 final_sapling_root: Default::default(),
3955 sapling_tree_size: Default::default(),
3956 time: 0,
3957 nonce: [0; 32],
3958 solution: Solution::for_proposal(),
3959 bits: difficulty.to_compact(),
3960 difficulty: 1.0,
3961 previous_block_hash: block::Hash([0; 32]),
3962 next_block_hash: Some(block::Hash([0; 32])),
3963 }
3964 }
3965}
3966
3967#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3973#[serde(transparent)]
3974pub struct GetBlockHashResponse(#[serde(with = "hex")] pub(crate) block::Hash);
3975
3976impl GetBlockHashResponse {
3977 pub fn new(hash: block::Hash) -> Self {
3979 GetBlockHashResponse(hash)
3980 }
3981
3982 pub fn hash(&self) -> block::Hash {
3984 self.0
3985 }
3986}
3987
3988#[deprecated(note = "Use `GetBlockHashResponse` instead")]
3989pub use self::GetBlockHashResponse as GetBlockHash;
3990
3991pub type Hash = GetBlockHashResponse;
3993
3994#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, Getters, new)]
3996pub struct GetBlockHeightAndHashResponse {
3997 #[getter(copy)]
3999 height: block::Height,
4000 #[getter(copy)]
4002 hash: block::Hash,
4003}
4004
4005#[deprecated(note = "Use `GetBlockHeightAndHashResponse` instead.")]
4006pub use GetBlockHeightAndHashResponse as GetBestBlockHeightAndHash;
4007
4008impl Default for GetBlockHeightAndHashResponse {
4009 fn default() -> Self {
4010 Self {
4011 height: block::Height::MIN,
4012 hash: block::Hash([0; 32]),
4013 }
4014 }
4015}
4016
4017impl Default for GetBlockHashResponse {
4018 fn default() -> Self {
4019 GetBlockHashResponse(block::Hash([0; 32]))
4020 }
4021}
4022
4023#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
4027#[serde(untagged)]
4028pub enum GetRawTransactionResponse {
4029 Raw(#[serde(with = "hex")] SerializedTransaction),
4031 Object(Box<TransactionObject>),
4033}
4034
4035#[deprecated(note = "Use `GetRawTransactionResponse` instead")]
4036pub use self::GetRawTransactionResponse as GetRawTransaction;
4037
4038impl Default for GetRawTransactionResponse {
4039 fn default() -> Self {
4040 Self::Object(Box::default())
4041 }
4042}
4043
4044#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
4046#[serde(untagged)]
4047pub enum GetAddressUtxosResponse {
4048 Utxos(Vec<Utxo>),
4050 UtxosAndChainInfo(GetAddressUtxosResponseObject),
4052}
4053
4054#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
4056pub struct GetAddressUtxosResponseObject {
4057 utxos: Vec<Utxo>,
4058 #[serde(with = "hex")]
4059 #[getter(copy)]
4060 hash: block::Hash,
4061 #[getter(copy)]
4062 height: block::Height,
4063}
4064
4065#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
4069pub struct Utxo {
4070 address: transparent::Address,
4072
4073 #[serde(with = "hex")]
4075 #[getter(copy)]
4076 txid: transaction::Hash,
4077
4078 #[serde(rename = "outputIndex")]
4080 #[getter(copy)]
4081 output_index: OutputIndex,
4082
4083 #[serde(with = "hex")]
4085 script: transparent::Script,
4086
4087 satoshis: u64,
4089
4090 #[getter(copy)]
4094 height: Height,
4095}
4096
4097#[deprecated(note = "Use `Utxo` instead")]
4098pub use self::Utxo as GetAddressUtxos;
4099
4100impl Default for Utxo {
4101 fn default() -> Self {
4102 Self {
4103 address: transparent::Address::from_pub_key_hash(
4104 zebra_chain::parameters::NetworkKind::default(),
4105 [0u8; 20],
4106 ),
4107 txid: transaction::Hash::from([0; 32]),
4108 output_index: OutputIndex::from_u64(0),
4109 script: transparent::Script::new(&[0u8; 10]),
4110 satoshis: u64::default(),
4111 height: Height(0),
4112 }
4113 }
4114}
4115
4116impl Utxo {
4117 #[deprecated(note = "Use `Utxo::new` instead")]
4119 pub fn from_parts(
4120 address: transparent::Address,
4121 txid: transaction::Hash,
4122 output_index: OutputIndex,
4123 script: transparent::Script,
4124 satoshis: u64,
4125 height: Height,
4126 ) -> Self {
4127 Utxo {
4128 address,
4129 txid,
4130 output_index,
4131 script,
4132 satoshis,
4133 height,
4134 }
4135 }
4136
4137 pub fn into_parts(
4139 &self,
4140 ) -> (
4141 transparent::Address,
4142 transaction::Hash,
4143 OutputIndex,
4144 transparent::Script,
4145 u64,
4146 Height,
4147 ) {
4148 (
4149 self.address.clone(),
4150 self.txid,
4151 self.output_index,
4152 self.script.clone(),
4153 self.satoshis,
4154 self.height,
4155 )
4156 }
4157}
4158
4159#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, Getters, new)]
4163#[serde(from = "DGetAddressTxIdsRequest")]
4164pub struct GetAddressTxIdsRequest {
4165 addresses: Vec<String>,
4168 start: Option<u32>,
4170 end: Option<u32>,
4172}
4173
4174impl GetAddressTxIdsRequest {
4175 #[deprecated(note = "Use `GetAddressTxIdsRequest::new` instead.")]
4177 pub fn from_parts(addresses: Vec<String>, start: u32, end: u32) -> Self {
4178 GetAddressTxIdsRequest {
4179 addresses,
4180 start: Some(start),
4181 end: Some(end),
4182 }
4183 }
4184
4185 pub fn into_parts(&self) -> (Vec<String>, u32, u32) {
4187 (
4188 self.addresses.clone(),
4189 self.start.unwrap_or(0),
4190 self.end.unwrap_or(0),
4191 )
4192 }
4193}
4194
4195impl From<DGetAddressTxIdsRequest> for GetAddressTxIdsRequest {
4196 fn from(request: DGetAddressTxIdsRequest) -> Self {
4197 match request {
4198 DGetAddressTxIdsRequest::Single(addr) => GetAddressTxIdsRequest {
4199 addresses: vec![addr],
4200 start: None,
4201 end: None,
4202 },
4203 DGetAddressTxIdsRequest::Object {
4204 addresses,
4205 start,
4206 end,
4207 } => GetAddressTxIdsRequest {
4208 addresses,
4209 start,
4210 end,
4211 },
4212 }
4213 }
4214}
4215
4216#[derive(Debug, serde::Deserialize)]
4218#[serde(untagged)]
4219enum DGetAddressTxIdsRequest {
4220 Single(String),
4222 Object {
4224 addresses: Vec<String>,
4226 start: Option<u32>,
4228 end: Option<u32>,
4230 },
4231}
4232
4233impl ValidateAddresses for GetAddressTxIdsRequest {
4234 fn addresses(&self) -> &[String] {
4235 &self.addresses
4236 }
4237}
4238
4239#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
4241pub struct GetBlockTrees {
4242 #[serde(skip_serializing_if = "SaplingTrees::is_empty")]
4243 sapling: SaplingTrees,
4244 #[serde(skip_serializing_if = "OrchardTrees::is_empty")]
4245 orchard: OrchardTrees,
4246}
4247
4248impl Default for GetBlockTrees {
4249 fn default() -> Self {
4250 GetBlockTrees {
4251 sapling: SaplingTrees { size: 0 },
4252 orchard: OrchardTrees { size: 0 },
4253 }
4254 }
4255}
4256
4257impl GetBlockTrees {
4258 pub fn new(sapling: u64, orchard: u64) -> Self {
4260 GetBlockTrees {
4261 sapling: SaplingTrees { size: sapling },
4262 orchard: OrchardTrees { size: orchard },
4263 }
4264 }
4265
4266 pub fn sapling(self) -> u64 {
4268 self.sapling.size
4269 }
4270
4271 pub fn orchard(self) -> u64 {
4273 self.orchard.size
4274 }
4275}
4276
4277#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
4279pub struct SaplingTrees {
4280 size: u64,
4281}
4282
4283impl SaplingTrees {
4284 fn is_empty(&self) -> bool {
4285 self.size == 0
4286 }
4287}
4288
4289#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
4291pub struct OrchardTrees {
4292 size: u64,
4293}
4294
4295impl OrchardTrees {
4296 fn is_empty(&self) -> bool {
4297 self.size == 0
4298 }
4299}
4300
4301fn build_height_range(
4317 start: Option<u32>,
4318 end: Option<u32>,
4319 chain_height: Height,
4320) -> Result<RangeInclusive<Height>> {
4321 let start = Height(start.unwrap_or(0)).min(chain_height);
4324
4325 let end = match end {
4327 Some(0) | None => chain_height,
4328 Some(val) => Height(val).min(chain_height),
4329 };
4330
4331 if start > end {
4332 return Err(ErrorObject::owned(
4333 ErrorCode::InvalidParams.code(),
4334 format!("start {start:?} must be less than or equal to end {end:?}"),
4335 None::<()>,
4336 ));
4337 }
4338
4339 Ok(start..=end)
4340}
4341
4342#[allow(dead_code)]
4350pub fn height_from_signed_int(index: i32, tip_height: Height) -> Result<Height> {
4351 if index >= 0 {
4352 let height = index.try_into().expect("Positive i32 always fits in u32");
4353 if height > tip_height.0 {
4354 return Err(ErrorObject::borrowed(
4355 ErrorCode::InvalidParams.code(),
4356 "Provided index is greater than the current tip",
4357 None,
4358 ));
4359 }
4360 Ok(Height(height))
4361 } else {
4362 let height = i32::try_from(tip_height.0)
4364 .expect("tip height fits in i32, because Height::MAX fits in i32")
4365 .checked_add(index + 1);
4366
4367 let sanitized_height = match height {
4368 None => {
4369 return Err(ErrorObject::borrowed(
4370 ErrorCode::InvalidParams.code(),
4371 "Provided index is not valid",
4372 None,
4373 ));
4374 }
4375 Some(h) => {
4376 if h < 0 {
4377 return Err(ErrorObject::borrowed(
4378 ErrorCode::InvalidParams.code(),
4379 "Provided negative index ends up with a negative height",
4380 None,
4381 ));
4382 }
4383 let h: u32 = h.try_into().expect("Positive i32 always fits in u32");
4384 if h > tip_height.0 {
4385 return Err(ErrorObject::borrowed(
4386 ErrorCode::InvalidParams.code(),
4387 "Provided index is greater than the current tip",
4388 None,
4389 ));
4390 }
4391
4392 h
4393 }
4394 };
4395
4396 Ok(Height(sanitized_height))
4397 }
4398}
4399
4400pub mod opthex {
4402 use hex::{FromHex, ToHex};
4403 use serde::{de, Deserialize, Deserializer, Serializer};
4404
4405 #[allow(missing_docs)]
4406 pub fn serialize<S, T>(data: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
4407 where
4408 S: Serializer,
4409 T: ToHex,
4410 {
4411 match data {
4412 Some(data) => {
4413 let s = data.encode_hex::<String>();
4414 serializer.serialize_str(&s)
4415 }
4416 None => serializer.serialize_none(),
4417 }
4418 }
4419
4420 #[allow(missing_docs)]
4421 pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
4422 where
4423 D: Deserializer<'de>,
4424 T: FromHex,
4425 {
4426 let opt = Option::<String>::deserialize(deserializer)?;
4427 match opt {
4428 Some(s) => T::from_hex(&s)
4429 .map(Some)
4430 .map_err(|_e| de::Error::custom("failed to convert hex string")),
4431 None => Ok(None),
4432 }
4433 }
4434}
4435
4436pub mod arrayhex {
4438 use serde::{Deserializer, Serializer};
4439 use std::fmt;
4440
4441 #[allow(missing_docs)]
4442 pub fn serialize<S, const N: usize>(data: &[u8; N], serializer: S) -> Result<S::Ok, S::Error>
4443 where
4444 S: Serializer,
4445 {
4446 let hex_string = hex::encode(data);
4447 serializer.serialize_str(&hex_string)
4448 }
4449
4450 #[allow(missing_docs)]
4451 pub fn deserialize<'de, D, const N: usize>(deserializer: D) -> Result<[u8; N], D::Error>
4452 where
4453 D: Deserializer<'de>,
4454 {
4455 struct HexArrayVisitor<const N: usize>;
4456
4457 impl<const N: usize> serde::de::Visitor<'_> for HexArrayVisitor<N> {
4458 type Value = [u8; N];
4459
4460 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
4461 write!(formatter, "a hex string representing exactly {N} bytes")
4462 }
4463
4464 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
4465 where
4466 E: serde::de::Error,
4467 {
4468 let vec = hex::decode(v).map_err(E::custom)?;
4469 vec.clone().try_into().map_err(|_| {
4470 E::invalid_length(vec.len(), &format!("expected {N} bytes").as_str())
4471 })
4472 }
4473 }
4474
4475 deserializer.deserialize_str(HexArrayVisitor::<N>)
4476 }
4477}
4478
4479pub async fn chain_tip_difficulty<State>(
4481 network: Network,
4482 mut state: State,
4483 should_use_default: bool,
4484) -> Result<f64>
4485where
4486 State: Service<
4487 zebra_state::ReadRequest,
4488 Response = zebra_state::ReadResponse,
4489 Error = zebra_state::BoxError,
4490 > + Clone
4491 + Send
4492 + Sync
4493 + 'static,
4494 State::Future: Send,
4495{
4496 let request = ReadRequest::ChainInfo;
4497
4498 let response = state
4504 .ready()
4505 .and_then(|service| service.call(request))
4506 .await;
4507
4508 let response = match (should_use_default, response) {
4509 (_, Ok(res)) => res,
4510 (true, Err(_)) => {
4511 return Ok((U256::from(network.target_difficulty_limit()) >> 128).as_u128() as f64);
4512 }
4513 (false, Err(error)) => return Err(ErrorObject::owned(0, error.to_string(), None::<()>)),
4514 };
4515
4516 let chain_info = match response {
4517 ReadResponse::ChainInfo(info) => info,
4518 _ => unreachable!("unmatched response to a chain info request"),
4519 };
4520
4521 let pow_limit: U256 = network.target_difficulty_limit().into();
4544 let Some(difficulty) = chain_info.expected_difficulty.to_expanded() else {
4545 return Ok(0.0);
4546 };
4547
4548 let pow_limit = pow_limit >> 128;
4550 let difficulty = U256::from(difficulty) >> 128;
4551
4552 let pow_limit = pow_limit.as_u128() as f64;
4555 let difficulty = difficulty.as_u128() as f64;
4556
4557 Ok(pow_limit / difficulty)
4559}
4560
4561#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
4563pub enum AddNodeCommand {
4564 #[serde(rename = "add")]
4566 Add,
4567}