1use std::{
35 cmp,
36 collections::{HashMap, HashSet},
37 fmt,
38 ops::RangeInclusive,
39 sync::Arc,
40 time::Duration,
41};
42
43use chrono::Utc;
44use derive_getters::Getters;
45use derive_new::new;
46use futures::{future::OptionFuture, stream::FuturesOrdered, StreamExt, TryFutureExt};
47use hex::{FromHex, ToHex};
48use indexmap::IndexMap;
49use jsonrpsee::core::{async_trait, RpcResult as Result};
50use jsonrpsee_proc_macros::rpc;
51use jsonrpsee_types::{ErrorCode, ErrorObject};
52use tokio::{
53 sync::{broadcast, watch},
54 task::JoinHandle,
55};
56use tower::{Service, ServiceExt};
57use tracing::Instrument;
58
59use zcash_address::{unified::Encoding, TryFromAddress};
60use zcash_primitives::consensus::Parameters;
61
62use zebra_chain::{
63 amount::{self, Amount, NegativeAllowed, NonNegative},
64 block::{self, Block, Commitment, Height, SerializedBlock, TryIntoHeight},
65 chain_sync_status::ChainSyncStatus,
66 chain_tip::{ChainTip, NetworkChainTipHeightEstimator},
67 parameters::{
68 subsidy::{
69 block_subsidy, funding_stream_values, miner_subsidy, FundingStreamReceiver,
70 ParameterSubsidy,
71 },
72 ConsensusBranchId, Network, NetworkUpgrade, POW_AVERAGING_WINDOW,
73 },
74 primitives,
75 serialization::{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, ParameterCheckpoint, RouterError};
86use zebra_network::{address_book_peers::AddressBookPeers, PeerSocketAddr};
87use zebra_node_services::mempool;
88use zebra_state::{HashOrHeight, OutputLocation, ReadRequest, ReadResponse, TransactionLocation};
89
90use crate::{
91 config,
92 methods::types::validate_address::validate_address,
93 queue::Queue,
94 server::{
95 self,
96 error::{MapError, OkOrError},
97 },
98};
99
100pub(crate) mod hex_data;
101pub(crate) mod trees;
102pub(crate) mod types;
103
104use hex_data::HexData;
105use trees::{GetSubtreesByIndexResponse, GetTreestateResponse, SubtreeRpcData};
106use types::{
107 get_block_template::{
108 constants::{
109 DEFAULT_SOLUTION_RATE_WINDOW_SIZE, MEMPOOL_LONG_POLL_INTERVAL,
110 ZCASHD_FUNDING_STREAM_ORDER,
111 },
112 proposal::proposal_block_from_template,
113 BlockTemplateResponse, BlockTemplateTimeSource, GetBlockTemplateHandler,
114 GetBlockTemplateParameters, GetBlockTemplateResponse,
115 },
116 get_blockchain_info::GetBlockchainInfoBalance,
117 get_mining_info::GetMiningInfoResponse,
118 get_raw_mempool::{self, GetRawMempoolResponse},
119 long_poll::LongPollInput,
120 peer_info::PeerInfo,
121 submit_block::{SubmitBlockErrorResponse, SubmitBlockParameters, SubmitBlockResponse},
122 subsidy::GetBlockSubsidyResponse,
123 transaction::TransactionObject,
124 unified_address::ZListUnifiedReceiversResponse,
125 validate_address::ValidateAddressResponse,
126 z_validate_address::{ZValidateAddressResponse, ZValidateAddressType},
127};
128
129#[cfg(test)]
130mod tests;
131
132#[rpc(server)]
133pub trait Rpc {
135 #[method(name = "getinfo")]
150 async fn get_info(&self) -> Result<GetInfoResponse>;
151
152 #[method(name = "getblockchaininfo")]
163 async fn get_blockchain_info(&self) -> Result<GetBlockchainInfoResponse>;
164
165 #[method(name = "getaddressbalance")]
188 async fn get_address_balance(
189 &self,
190 address_strings: GetAddressBalanceRequest,
191 ) -> Result<GetAddressBalanceResponse>;
192
193 #[method(name = "sendrawtransaction")]
210 async fn send_raw_transaction(
211 &self,
212 raw_transaction_hex: String,
213 _allow_high_fees: Option<bool>,
214 ) -> Result<SendRawTransactionResponse>;
215
216 #[method(name = "getblock")]
236 async fn get_block(
237 &self,
238 hash_or_height: String,
239 verbosity: Option<u8>,
240 ) -> Result<GetBlockResponse>;
241
242 #[method(name = "getblockheader")]
260 async fn get_block_header(
261 &self,
262 hash_or_height: String,
263 verbose: Option<bool>,
264 ) -> Result<GetBlockHeaderResponse>;
265
266 #[method(name = "getbestblockhash")]
272 fn get_best_block_hash(&self) -> Result<GetBlockHashResponse>;
273
274 #[method(name = "getbestblockheightandhash")]
280 fn get_best_block_height_and_hash(&self) -> Result<GetBlockHeightAndHashResponse>;
281
282 #[method(name = "getrawmempool")]
292 async fn get_raw_mempool(&self, verbose: Option<bool>) -> Result<GetRawMempoolResponse>;
293
294 #[method(name = "z_gettreestate")]
311 async fn z_get_treestate(&self, hash_or_height: String) -> Result<GetTreestateResponse>;
312
313 #[method(name = "z_getsubtreesbyindex")]
332 async fn z_get_subtrees_by_index(
333 &self,
334 pool: String,
335 start_index: NoteCommitmentSubtreeIndex,
336 limit: Option<NoteCommitmentSubtreeIndex>,
337 ) -> Result<GetSubtreesByIndexResponse>;
338
339 #[method(name = "getrawtransaction")]
351 async fn get_raw_transaction(
352 &self,
353 txid: String,
354 verbose: Option<u8>,
355 block_hash: Option<String>,
356 ) -> Result<GetRawTransactionResponse>;
357
358 #[method(name = "getaddresstxids")]
376 async fn get_address_tx_ids(&self, request: GetAddressTxIdsRequest) -> Result<Vec<String>>;
377
378 #[method(name = "getaddressutxos")]
393 async fn get_address_utxos(
394 &self,
395 address_strings: AddressStrings,
396 ) -> Result<GetAddressUtxosResponse>;
397
398 #[method(name = "stop")]
409 fn stop(&self) -> Result<String>;
410
411 #[method(name = "getblockcount")]
418 fn get_block_count(&self) -> Result<u32>;
419
420 #[method(name = "getblockhash")]
436 async fn get_block_hash(&self, index: i32) -> Result<GetBlockHashResponse>;
437
438 #[method(name = "getblocktemplate")]
460 async fn get_block_template(
461 &self,
462 parameters: Option<GetBlockTemplateParameters>,
463 ) -> Result<GetBlockTemplateResponse>;
464
465 #[method(name = "submitblock")]
481 async fn submit_block(
482 &self,
483 hex_data: HexData,
484 _parameters: Option<SubmitBlockParameters>,
485 ) -> Result<SubmitBlockResponse>;
486
487 #[method(name = "getmininginfo")]
493 async fn get_mining_info(&self) -> Result<GetMiningInfoResponse>;
494
495 #[method(name = "getnetworksolps")]
506 async fn get_network_sol_ps(&self, num_blocks: Option<i32>, height: Option<i32>)
507 -> Result<u64>;
508
509 #[method(name = "getnetworkhashps")]
519 async fn get_network_hash_ps(
520 &self,
521 num_blocks: Option<i32>,
522 height: Option<i32>,
523 ) -> Result<u64> {
524 self.get_network_sol_ps(num_blocks, height).await
525 }
526
527 #[method(name = "getpeerinfo")]
533 async fn get_peer_info(&self) -> Result<Vec<PeerInfo>>;
534
535 #[method(name = "validateaddress")]
546 async fn validate_address(&self, address: String) -> Result<ValidateAddressResponse>;
547
548 #[method(name = "z_validateaddress")]
563 async fn z_validate_address(&self, address: String) -> Result<ZValidateAddressResponse>;
564
565 #[method(name = "getblocksubsidy")]
580 async fn get_block_subsidy(&self, height: Option<u32>) -> Result<GetBlockSubsidyResponse>;
581
582 #[method(name = "getdifficulty")]
588 async fn get_difficulty(&self) -> Result<f64>;
589
590 #[method(name = "z_listunifiedreceivers")]
604 async fn z_list_unified_receivers(
605 &self,
606 address: String,
607 ) -> Result<ZListUnifiedReceiversResponse>;
608
609 #[method(name = "invalidateblock")]
617 async fn invalidate_block(&self, block_hash: block::Hash) -> Result<()>;
618
619 #[method(name = "reconsiderblock")]
625 async fn reconsider_block(&self, block_hash: block::Hash) -> Result<Vec<block::Hash>>;
626
627 #[method(name = "generate")]
628 async fn generate(&self, num_blocks: u32) -> Result<Vec<GetBlockHashResponse>>;
642
643 #[method(name = "addnode")]
644 async fn add_node(&self, addr: PeerSocketAddr, command: AddNodeCommand) -> Result<()>;
659}
660
661#[derive(Clone)]
663pub struct RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
664where
665 Mempool: Service<
666 mempool::Request,
667 Response = mempool::Response,
668 Error = zebra_node_services::BoxError,
669 > + Clone
670 + Send
671 + Sync
672 + 'static,
673 Mempool::Future: Send,
674 State: Service<
675 zebra_state::Request,
676 Response = zebra_state::Response,
677 Error = zebra_state::BoxError,
678 > + Clone
679 + Send
680 + Sync
681 + 'static,
682 State::Future: Send,
683 ReadState: Service<
684 zebra_state::ReadRequest,
685 Response = zebra_state::ReadResponse,
686 Error = zebra_state::BoxError,
687 > + Clone
688 + Send
689 + Sync
690 + 'static,
691 ReadState::Future: Send,
692 Tip: ChainTip + Clone + Send + Sync + 'static,
693 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
694 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
695 + Clone
696 + Send
697 + Sync
698 + 'static,
699 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
700 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
701{
702 build_version: String,
706
707 user_agent: String,
709
710 network: Network,
712
713 debug_force_finished_sync: bool,
716
717 mempool: Mempool,
721
722 state: State,
724
725 read_state: ReadState,
727
728 latest_chain_tip: Tip,
730
731 queue_sender: broadcast::Sender<UnminedTx>,
735
736 address_book: AddressBook,
738
739 last_warn_error_log_rx: LoggedLastEvent,
741
742 gbt: GetBlockTemplateHandler<BlockVerifierRouter, SyncStatus>,
744}
745
746pub type LoggedLastEvent = watch::Receiver<Option<(String, tracing::Level, chrono::DateTime<Utc>)>>;
748
749impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus> fmt::Debug
750 for RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
751where
752 Mempool: Service<
753 mempool::Request,
754 Response = mempool::Response,
755 Error = zebra_node_services::BoxError,
756 > + Clone
757 + Send
758 + Sync
759 + 'static,
760 Mempool::Future: Send,
761 State: Service<
762 zebra_state::Request,
763 Response = zebra_state::Response,
764 Error = zebra_state::BoxError,
765 > + Clone
766 + Send
767 + Sync
768 + 'static,
769 State::Future: Send,
770 ReadState: Service<
771 zebra_state::ReadRequest,
772 Response = zebra_state::ReadResponse,
773 Error = zebra_state::BoxError,
774 > + Clone
775 + Send
776 + Sync
777 + 'static,
778 ReadState::Future: Send,
779 Tip: ChainTip + Clone + Send + Sync + 'static,
780 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
781 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
782 + Clone
783 + Send
784 + Sync
785 + 'static,
786 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
787 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
788{
789 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
790 f.debug_struct("RpcImpl")
792 .field("build_version", &self.build_version)
793 .field("user_agent", &self.user_agent)
794 .field("network", &self.network)
795 .field("debug_force_finished_sync", &self.debug_force_finished_sync)
796 .field("getblocktemplate", &self.gbt)
797 .finish()
798 }
799}
800
801impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
802 RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
803where
804 Mempool: Service<
805 mempool::Request,
806 Response = mempool::Response,
807 Error = zebra_node_services::BoxError,
808 > + Clone
809 + Send
810 + Sync
811 + 'static,
812 Mempool::Future: Send,
813 State: Service<
814 zebra_state::Request,
815 Response = zebra_state::Response,
816 Error = zebra_state::BoxError,
817 > + Clone
818 + Send
819 + Sync
820 + 'static,
821 State::Future: Send,
822 ReadState: Service<
823 zebra_state::ReadRequest,
824 Response = zebra_state::ReadResponse,
825 Error = zebra_state::BoxError,
826 > + Clone
827 + Send
828 + Sync
829 + 'static,
830 ReadState::Future: Send,
831 Tip: ChainTip + Clone + Send + Sync + 'static,
832 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
833 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
834 + Clone
835 + Send
836 + Sync
837 + 'static,
838 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
839 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
840{
841 #[allow(clippy::too_many_arguments)]
846 pub fn new<VersionString, UserAgentString>(
847 network: Network,
848 mining_config: config::mining::Config,
849 debug_force_finished_sync: bool,
850 build_version: VersionString,
851 user_agent: UserAgentString,
852 mempool: Mempool,
853 state: State,
854 read_state: ReadState,
855 block_verifier_router: BlockVerifierRouter,
856 sync_status: SyncStatus,
857 latest_chain_tip: Tip,
858 address_book: AddressBook,
859 last_warn_error_log_rx: LoggedLastEvent,
860 mined_block_sender: Option<watch::Sender<(block::Hash, block::Height)>>,
861 ) -> (Self, JoinHandle<()>)
862 where
863 VersionString: ToString + Clone + Send + 'static,
864 UserAgentString: ToString + Clone + Send + 'static,
865 {
866 let (runner, queue_sender) = Queue::start();
867
868 let mut build_version = build_version.to_string();
869 let user_agent = user_agent.to_string();
870
871 if !build_version.is_empty() && !build_version.starts_with('v') {
873 build_version.insert(0, 'v');
874 }
875
876 let gbt = GetBlockTemplateHandler::new(
877 &network,
878 mining_config.clone(),
879 block_verifier_router,
880 sync_status,
881 mined_block_sender,
882 );
883
884 let rpc_impl = RpcImpl {
885 build_version,
886 user_agent,
887 network: network.clone(),
888 debug_force_finished_sync,
889 mempool: mempool.clone(),
890 state: state.clone(),
891 read_state: read_state.clone(),
892 latest_chain_tip: latest_chain_tip.clone(),
893 queue_sender,
894 address_book,
895 last_warn_error_log_rx,
896 gbt,
897 };
898
899 let rpc_tx_queue_task_handle = tokio::spawn(
901 runner
902 .run(mempool, read_state, latest_chain_tip, network)
903 .in_current_span(),
904 );
905
906 (rpc_impl, rpc_tx_queue_task_handle)
907 }
908
909 pub fn network(&self) -> &Network {
911 &self.network
912 }
913}
914
915#[async_trait]
916impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus> RpcServer
917 for RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
918where
919 Mempool: Service<
920 mempool::Request,
921 Response = mempool::Response,
922 Error = zebra_node_services::BoxError,
923 > + Clone
924 + Send
925 + Sync
926 + 'static,
927 Mempool::Future: Send,
928 State: Service<
929 zebra_state::Request,
930 Response = zebra_state::Response,
931 Error = zebra_state::BoxError,
932 > + Clone
933 + Send
934 + Sync
935 + 'static,
936 State::Future: Send,
937 ReadState: Service<
938 zebra_state::ReadRequest,
939 Response = zebra_state::ReadResponse,
940 Error = zebra_state::BoxError,
941 > + Clone
942 + Send
943 + Sync
944 + 'static,
945 ReadState::Future: Send,
946 Tip: ChainTip + Clone + Send + Sync + 'static,
947 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
948 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
949 + Clone
950 + Send
951 + Sync
952 + 'static,
953 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
954 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
955{
956 async fn get_info(&self) -> Result<GetInfoResponse> {
957 let version = GetInfoResponse::version_from_string(&self.build_version)
958 .expect("invalid version string");
959
960 let connections = self.address_book.recently_live_peers(Utc::now()).len();
961
962 let last_error_recorded = self.last_warn_error_log_rx.borrow().clone();
963 let (last_error_log, _level, last_error_log_time) = last_error_recorded.unwrap_or((
964 GetInfoResponse::default().errors,
965 tracing::Level::INFO,
966 Utc::now(),
967 ));
968
969 let tip_height = self
970 .latest_chain_tip
971 .best_tip_height()
972 .unwrap_or(Height::MIN);
973 let testnet = self.network.is_a_test_network();
974
975 let pay_tx_fee = 0.0;
981
982 let relay_fee = zebra_chain::transaction::zip317::MIN_MEMPOOL_TX_FEE_RATE as f64
983 / (zebra_chain::amount::COIN as f64);
984 let difficulty = chain_tip_difficulty(self.network.clone(), self.read_state.clone(), true)
985 .await
986 .expect("should always be Ok when `should_use_default` is true");
987
988 let response = GetInfoResponse {
989 version,
990 build: self.build_version.clone(),
991 subversion: self.user_agent.clone(),
992 protocol_version: zebra_network::constants::CURRENT_NETWORK_PROTOCOL_VERSION.0,
993 blocks: tip_height.0,
994 connections,
995 proxy: None,
996 difficulty,
997 testnet,
998 pay_tx_fee,
999 relay_fee,
1000 errors: last_error_log,
1001 errors_timestamp: last_error_log_time.to_string(),
1002 };
1003
1004 Ok(response)
1005 }
1006
1007 #[allow(clippy::unwrap_in_result)]
1008 async fn get_blockchain_info(&self) -> Result<GetBlockchainInfoResponse> {
1009 let debug_force_finished_sync = self.debug_force_finished_sync;
1010 let network = &self.network;
1011
1012 let (usage_info_rsp, tip_pool_values_rsp, chain_tip_difficulty) = {
1013 use zebra_state::ReadRequest::*;
1014 let state_call = |request| self.read_state.clone().oneshot(request);
1015 tokio::join!(
1016 state_call(UsageInfo),
1017 state_call(TipPoolValues),
1018 chain_tip_difficulty(network.clone(), self.read_state.clone(), true)
1019 )
1020 };
1021
1022 let (size_on_disk, (tip_height, tip_hash), value_balance, difficulty) = {
1023 use zebra_state::ReadResponse::*;
1024
1025 let UsageInfo(size_on_disk) = usage_info_rsp.map_misc_error()? else {
1026 unreachable!("unmatched response to a TipPoolValues request")
1027 };
1028
1029 let (tip, value_balance) = match tip_pool_values_rsp {
1030 Ok(TipPoolValues {
1031 tip_height,
1032 tip_hash,
1033 value_balance,
1034 }) => ((tip_height, tip_hash), value_balance),
1035 Ok(_) => unreachable!("unmatched response to a TipPoolValues request"),
1036 Err(_) => ((Height::MIN, network.genesis_hash()), Default::default()),
1037 };
1038
1039 let difficulty = chain_tip_difficulty
1040 .expect("should always be Ok when `should_use_default` is true");
1041
1042 (size_on_disk, tip, value_balance, difficulty)
1043 };
1044
1045 let now = Utc::now();
1046 let (estimated_height, verification_progress) = self
1047 .latest_chain_tip
1048 .best_tip_height_and_block_time()
1049 .map(|(tip_height, tip_block_time)| {
1050 let height =
1051 NetworkChainTipHeightEstimator::new(tip_block_time, tip_height, network)
1052 .estimate_height_at(now);
1053
1054 let height =
1058 if tip_block_time > now || height < tip_height || debug_force_finished_sync {
1059 tip_height
1060 } else {
1061 height
1062 };
1063
1064 (height, f64::from(tip_height.0) / f64::from(height.0))
1065 })
1066 .unwrap_or((Height::MIN, 0.0));
1068
1069 let mut upgrades = IndexMap::new();
1073 for (activation_height, network_upgrade) in network.full_activation_list() {
1074 if let Some(branch_id) = network_upgrade.branch_id() {
1079 let status = if tip_height >= activation_height {
1081 NetworkUpgradeStatus::Active
1082 } else {
1083 NetworkUpgradeStatus::Pending
1084 };
1085
1086 let upgrade = NetworkUpgradeInfo {
1087 name: network_upgrade,
1088 activation_height,
1089 status,
1090 };
1091 upgrades.insert(ConsensusBranchIdHex(branch_id), upgrade);
1092 }
1093 }
1094
1095 let next_block_height =
1097 (tip_height + 1).expect("valid chain tips are a lot less than Height::MAX");
1098 let consensus = TipConsensusBranch {
1099 chain_tip: ConsensusBranchIdHex(
1100 NetworkUpgrade::current(network, tip_height)
1101 .branch_id()
1102 .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
1103 ),
1104 next_block: ConsensusBranchIdHex(
1105 NetworkUpgrade::current(network, next_block_height)
1106 .branch_id()
1107 .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
1108 ),
1109 };
1110
1111 let response = GetBlockchainInfoResponse {
1112 chain: network.bip70_network_name(),
1113 blocks: tip_height,
1114 best_block_hash: tip_hash,
1115 estimated_height,
1116 chain_supply: GetBlockchainInfoBalance::chain_supply(value_balance),
1117 value_pools: GetBlockchainInfoBalance::value_pools(value_balance, None),
1118 upgrades,
1119 consensus,
1120 headers: tip_height,
1121 difficulty,
1122 verification_progress,
1123 chain_work: 0,
1125 pruned: false,
1126 size_on_disk,
1127 commitments: 0,
1129 };
1130
1131 Ok(response)
1132 }
1133
1134 async fn get_address_balance(
1135 &self,
1136 address_strings: GetAddressBalanceRequest,
1137 ) -> Result<GetAddressBalanceResponse> {
1138 let valid_addresses = address_strings.valid_addresses()?;
1139
1140 let request = zebra_state::ReadRequest::AddressBalance(valid_addresses);
1141 let response = self
1142 .read_state
1143 .clone()
1144 .oneshot(request)
1145 .await
1146 .map_misc_error()?;
1147
1148 match response {
1149 zebra_state::ReadResponse::AddressBalance { balance, received } => {
1150 Ok(GetAddressBalanceResponse {
1151 balance: u64::from(balance),
1152 received,
1153 })
1154 }
1155 _ => unreachable!("Unexpected response from state service: {response:?}"),
1156 }
1157 }
1158
1159 async fn send_raw_transaction(
1161 &self,
1162 raw_transaction_hex: String,
1163 _allow_high_fees: Option<bool>,
1164 ) -> Result<SendRawTransactionResponse> {
1165 let mempool = self.mempool.clone();
1166 let queue_sender = self.queue_sender.clone();
1167
1168 let raw_transaction_bytes = Vec::from_hex(raw_transaction_hex)
1171 .map_error(server::error::LegacyCode::Deserialization)?;
1172 let raw_transaction = Transaction::zcash_deserialize(&*raw_transaction_bytes)
1173 .map_error(server::error::LegacyCode::Deserialization)?;
1174
1175 let transaction_hash = raw_transaction.hash();
1176
1177 let unmined_transaction = UnminedTx::from(raw_transaction.clone());
1179 let _ = queue_sender.send(unmined_transaction);
1180
1181 let transaction_parameter = mempool::Gossip::Tx(raw_transaction.into());
1182 let request = mempool::Request::Queue(vec![transaction_parameter]);
1183
1184 let response = mempool.oneshot(request).await.map_misc_error()?;
1185
1186 let mut queue_results = match response {
1187 mempool::Response::Queued(results) => results,
1188 _ => unreachable!("incorrect response variant from mempool service"),
1189 };
1190
1191 assert_eq!(
1192 queue_results.len(),
1193 1,
1194 "mempool service returned more results than expected"
1195 );
1196
1197 let queue_result = queue_results
1198 .pop()
1199 .expect("there should be exactly one item in Vec")
1200 .inspect_err(|err| tracing::debug!("sent transaction to mempool: {:?}", &err))
1201 .map_misc_error()?
1202 .await
1203 .map_misc_error()?;
1204
1205 tracing::debug!("sent transaction to mempool: {:?}", &queue_result);
1206
1207 queue_result
1208 .map(|_| SendRawTransactionResponse(transaction_hash))
1209 .map_error(server::error::LegacyCode::Verify)
1216 }
1217
1218 async fn get_block(
1227 &self,
1228 hash_or_height: String,
1229 verbosity: Option<u8>,
1230 ) -> Result<GetBlockResponse> {
1231 let verbosity = verbosity.unwrap_or(1);
1232 let network = self.network.clone();
1233 let original_hash_or_height = hash_or_height.clone();
1234
1235 let get_block_header_future = if matches!(verbosity, 1 | 2) {
1237 Some(self.get_block_header(original_hash_or_height.clone(), Some(true)))
1238 } else {
1239 None
1240 };
1241
1242 let hash_or_height =
1243 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1244 .map_error(server::error::LegacyCode::InvalidParameter)?;
1247
1248 if verbosity == 0 {
1249 let request = zebra_state::ReadRequest::Block(hash_or_height);
1250 let response = self
1251 .read_state
1252 .clone()
1253 .oneshot(request)
1254 .await
1255 .map_misc_error()?;
1256
1257 match response {
1258 zebra_state::ReadResponse::Block(Some(block)) => {
1259 Ok(GetBlockResponse::Raw(block.into()))
1260 }
1261 zebra_state::ReadResponse::Block(None) => {
1262 Err("Block not found").map_error(server::error::LegacyCode::InvalidParameter)
1263 }
1264 _ => unreachable!("unmatched response to a block request"),
1265 }
1266 } else if let Some(get_block_header_future) = get_block_header_future {
1267 let get_block_header_result: Result<GetBlockHeaderResponse> =
1268 get_block_header_future.await;
1269
1270 let GetBlockHeaderResponse::Object(block_header) = get_block_header_result? else {
1271 panic!("must return Object")
1272 };
1273
1274 let BlockHeaderObject {
1275 hash,
1276 confirmations,
1277 height,
1278 version,
1279 merkle_root,
1280 block_commitments,
1281 final_sapling_root,
1282 sapling_tree_size,
1283 time,
1284 nonce,
1285 solution,
1286 bits,
1287 difficulty,
1288 previous_block_hash,
1289 next_block_hash,
1290 } = *block_header;
1291
1292 let transactions_request = match verbosity {
1293 1 => zebra_state::ReadRequest::TransactionIdsForBlock(hash_or_height),
1294 2 => zebra_state::ReadRequest::BlockAndSize(hash_or_height),
1295 _other => panic!("get_block_header_fut should be none"),
1296 };
1297
1298 let hash_or_height = hash.into();
1303 let requests = vec![
1304 transactions_request,
1312 zebra_state::ReadRequest::OrchardTree(hash_or_height),
1314 zebra_state::ReadRequest::BlockInfo(previous_block_hash.into()),
1316 zebra_state::ReadRequest::BlockInfo(hash_or_height),
1317 ];
1318
1319 let mut futs = FuturesOrdered::new();
1320
1321 for request in requests {
1322 futs.push_back(self.read_state.clone().oneshot(request));
1323 }
1324
1325 let tx_ids_response = futs.next().await.expect("`futs` should not be empty");
1326 let (tx, size): (Vec<_>, Option<usize>) = match tx_ids_response.map_misc_error()? {
1327 zebra_state::ReadResponse::TransactionIdsForBlock(tx_ids) => (
1328 tx_ids
1329 .ok_or_misc_error("block not found")?
1330 .iter()
1331 .map(|tx_id| GetBlockTransaction::Hash(*tx_id))
1332 .collect(),
1333 None,
1334 ),
1335 zebra_state::ReadResponse::BlockAndSize(block_and_size) => {
1336 let (block, size) = block_and_size.ok_or_misc_error("Block not found")?;
1337 let block_time = block.header.time;
1338 let transactions =
1339 block
1340 .transactions
1341 .iter()
1342 .map(|tx| {
1343 GetBlockTransaction::Object(Box::new(
1344 TransactionObject::from_transaction(
1345 tx.clone(),
1346 Some(height),
1347 Some(confirmations.try_into().expect(
1348 "should be less than max block height, i32::MAX",
1349 )),
1350 &network,
1351 Some(block_time),
1352 Some(hash),
1353 Some(true),
1354 tx.hash(),
1355 ),
1356 ))
1357 })
1358 .collect();
1359 (transactions, Some(size))
1360 }
1361 _ => unreachable!("unmatched response to a transaction_ids_for_block request"),
1362 };
1363
1364 let orchard_tree_response = futs.next().await.expect("`futs` should not be empty");
1365 let zebra_state::ReadResponse::OrchardTree(orchard_tree) =
1366 orchard_tree_response.map_misc_error()?
1367 else {
1368 unreachable!("unmatched response to a OrchardTree request");
1369 };
1370
1371 let nu5_activation = NetworkUpgrade::Nu5.activation_height(&network);
1372
1373 let orchard_tree = orchard_tree.ok_or_misc_error("missing Orchard tree")?;
1375
1376 let final_orchard_root = match nu5_activation {
1377 Some(activation_height) if height >= activation_height => {
1378 Some(orchard_tree.root().into())
1379 }
1380 _other => None,
1381 };
1382
1383 let sapling = SaplingTrees {
1384 size: sapling_tree_size,
1385 };
1386
1387 let orchard_tree_size = orchard_tree.count();
1388 let orchard = OrchardTrees {
1389 size: orchard_tree_size,
1390 };
1391
1392 let trees = GetBlockTrees { sapling, orchard };
1393
1394 let block_info_response = futs.next().await.expect("`futs` should not be empty");
1395 let zebra_state::ReadResponse::BlockInfo(prev_block_info) =
1396 block_info_response.map_misc_error()?
1397 else {
1398 unreachable!("unmatched response to a BlockInfo request");
1399 };
1400 let block_info_response = futs.next().await.expect("`futs` should not be empty");
1401 let zebra_state::ReadResponse::BlockInfo(block_info) =
1402 block_info_response.map_misc_error()?
1403 else {
1404 unreachable!("unmatched response to a BlockInfo request");
1405 };
1406
1407 let delta = block_info.as_ref().and_then(|d| {
1408 let value_pools = d.value_pools().constrain::<NegativeAllowed>().ok()?;
1409 let prev_value_pools = prev_block_info
1410 .map(|d| d.value_pools().constrain::<NegativeAllowed>())
1411 .unwrap_or(Ok(ValueBalance::<NegativeAllowed>::zero()))
1412 .ok()?;
1413 (value_pools - prev_value_pools).ok()
1414 });
1415 let size = size.or(block_info.as_ref().map(|d| d.size() as usize));
1416
1417 Ok(GetBlockResponse::Object(Box::new(BlockObject {
1418 hash,
1419 confirmations,
1420 height: Some(height),
1421 version: Some(version),
1422 merkle_root: Some(merkle_root),
1423 time: Some(time),
1424 nonce: Some(nonce),
1425 solution: Some(solution),
1426 bits: Some(bits),
1427 difficulty: Some(difficulty),
1428 tx,
1429 trees,
1430 chain_supply: block_info
1431 .as_ref()
1432 .map(|d| GetBlockchainInfoBalance::chain_supply(*d.value_pools())),
1433 value_pools: block_info
1434 .map(|d| GetBlockchainInfoBalance::value_pools(*d.value_pools(), delta)),
1435 size: size.map(|size| size as i64),
1436 block_commitments: Some(block_commitments),
1437 final_sapling_root: Some(final_sapling_root),
1438 final_orchard_root,
1439 previous_block_hash: Some(previous_block_hash),
1440 next_block_hash,
1441 })))
1442 } else {
1443 Err("invalid verbosity value").map_error(server::error::LegacyCode::InvalidParameter)
1444 }
1445 }
1446
1447 async fn get_block_header(
1448 &self,
1449 hash_or_height: String,
1450 verbose: Option<bool>,
1451 ) -> Result<GetBlockHeaderResponse> {
1452 let verbose = verbose.unwrap_or(true);
1453 let network = self.network.clone();
1454
1455 let hash_or_height =
1456 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1457 .map_error(server::error::LegacyCode::InvalidParameter)?;
1460 let zebra_state::ReadResponse::BlockHeader {
1461 header,
1462 hash,
1463 height,
1464 next_block_hash,
1465 } = self
1466 .read_state
1467 .clone()
1468 .oneshot(zebra_state::ReadRequest::BlockHeader(hash_or_height))
1469 .await
1470 .map_err(|_| "block height not in best chain")
1471 .map_error(
1472 if hash_or_height.hash().is_some() {
1477 server::error::LegacyCode::InvalidAddressOrKey
1478 } else {
1479 server::error::LegacyCode::InvalidParameter
1480 },
1481 )?
1482 else {
1483 panic!("unexpected response to BlockHeader request")
1484 };
1485
1486 let response = if !verbose {
1487 GetBlockHeaderResponse::Raw(HexData(header.zcash_serialize_to_vec().map_misc_error()?))
1488 } else {
1489 let zebra_state::ReadResponse::SaplingTree(sapling_tree) = self
1490 .read_state
1491 .clone()
1492 .oneshot(zebra_state::ReadRequest::SaplingTree(hash_or_height))
1493 .await
1494 .map_misc_error()?
1495 else {
1496 panic!("unexpected response to SaplingTree request")
1497 };
1498
1499 let sapling_tree = sapling_tree.ok_or_misc_error("missing Sapling tree")?;
1501
1502 let zebra_state::ReadResponse::Depth(depth) = self
1503 .read_state
1504 .clone()
1505 .oneshot(zebra_state::ReadRequest::Depth(hash))
1506 .await
1507 .map_misc_error()?
1508 else {
1509 panic!("unexpected response to SaplingTree request")
1510 };
1511
1512 const NOT_IN_BEST_CHAIN_CONFIRMATIONS: i64 = -1;
1515
1516 let confirmations = depth
1519 .map(|depth| i64::from(depth) + 1)
1520 .unwrap_or(NOT_IN_BEST_CHAIN_CONFIRMATIONS);
1521
1522 let mut nonce = *header.nonce;
1523 nonce.reverse();
1524
1525 let sapling_activation = NetworkUpgrade::Sapling.activation_height(&network);
1526 let sapling_tree_size = sapling_tree.count();
1527 let final_sapling_root: [u8; 32] =
1528 if sapling_activation.is_some() && height >= sapling_activation.unwrap() {
1529 let mut root: [u8; 32] = sapling_tree.root().into();
1530 root.reverse();
1531 root
1532 } else {
1533 [0; 32]
1534 };
1535
1536 let difficulty = header.difficulty_threshold.relative_to_network(&network);
1537
1538 let block_commitments = match header.commitment(&network, height).expect(
1539 "Unexpected failure while parsing the blockcommitments field in get_block_header",
1540 ) {
1541 Commitment::PreSaplingReserved(bytes) => bytes,
1542 Commitment::FinalSaplingRoot(_) => final_sapling_root,
1543 Commitment::ChainHistoryActivationReserved => [0; 32],
1544 Commitment::ChainHistoryRoot(root) => root.bytes_in_display_order(),
1545 Commitment::ChainHistoryBlockTxAuthCommitment(hash) => {
1546 hash.bytes_in_display_order()
1547 }
1548 };
1549
1550 let block_header = BlockHeaderObject {
1551 hash,
1552 confirmations,
1553 height,
1554 version: header.version,
1555 merkle_root: header.merkle_root,
1556 block_commitments,
1557 final_sapling_root,
1558 sapling_tree_size,
1559 time: header.time.timestamp(),
1560 nonce,
1561 solution: header.solution,
1562 bits: header.difficulty_threshold,
1563 difficulty,
1564 previous_block_hash: header.previous_block_hash,
1565 next_block_hash,
1566 };
1567
1568 GetBlockHeaderResponse::Object(Box::new(block_header))
1569 };
1570
1571 Ok(response)
1572 }
1573
1574 fn get_best_block_hash(&self) -> Result<GetBlockHashResponse> {
1575 self.latest_chain_tip
1576 .best_tip_hash()
1577 .map(GetBlockHashResponse)
1578 .ok_or_misc_error("No blocks in state")
1579 }
1580
1581 fn get_best_block_height_and_hash(&self) -> Result<GetBlockHeightAndHashResponse> {
1582 self.latest_chain_tip
1583 .best_tip_height_and_hash()
1584 .map(|(height, hash)| GetBlockHeightAndHashResponse { height, hash })
1585 .ok_or_misc_error("No blocks in state")
1586 }
1587
1588 async fn get_raw_mempool(&self, verbose: Option<bool>) -> Result<GetRawMempoolResponse> {
1589 #[allow(unused)]
1590 let verbose = verbose.unwrap_or(false);
1591
1592 use zebra_chain::block::MAX_BLOCK_BYTES;
1593
1594 let mut mempool = self.mempool.clone();
1595
1596 let request = if verbose {
1597 mempool::Request::FullTransactions
1598 } else {
1599 mempool::Request::TransactionIds
1600 };
1601
1602 let response = mempool
1604 .ready()
1605 .and_then(|service| service.call(request))
1606 .await
1607 .map_misc_error()?;
1608
1609 match response {
1610 mempool::Response::FullTransactions {
1611 mut transactions,
1612 transaction_dependencies,
1613 last_seen_tip_hash: _,
1614 } => {
1615 if verbose {
1616 let map = transactions
1617 .iter()
1618 .map(|unmined_tx| {
1619 (
1620 unmined_tx.transaction.id.mined_id().encode_hex(),
1621 get_raw_mempool::MempoolObject::from_verified_unmined_tx(
1622 unmined_tx,
1623 &transactions,
1624 &transaction_dependencies,
1625 ),
1626 )
1627 })
1628 .collect::<HashMap<_, _>>();
1629 Ok(GetRawMempoolResponse::Verbose(map))
1630 } else {
1631 transactions.sort_by_cached_key(|tx| {
1636 cmp::Reverse((
1639 i64::from(tx.miner_fee) as u128 * MAX_BLOCK_BYTES as u128
1640 / tx.transaction.size as u128,
1641 tx.transaction.id.mined_id(),
1643 ))
1644 });
1645 let tx_ids: Vec<String> = transactions
1646 .iter()
1647 .map(|unmined_tx| unmined_tx.transaction.id.mined_id().encode_hex())
1648 .collect();
1649
1650 Ok(GetRawMempoolResponse::TxIds(tx_ids))
1651 }
1652 }
1653
1654 mempool::Response::TransactionIds(unmined_transaction_ids) => {
1655 let mut tx_ids: Vec<String> = unmined_transaction_ids
1656 .iter()
1657 .map(|id| id.mined_id().encode_hex())
1658 .collect();
1659
1660 tx_ids.sort();
1662
1663 Ok(GetRawMempoolResponse::TxIds(tx_ids))
1664 }
1665
1666 _ => unreachable!("unmatched response to a transactionids request"),
1667 }
1668 }
1669
1670 async fn get_raw_transaction(
1671 &self,
1672 txid: String,
1673 verbose: Option<u8>,
1674 block_hash: Option<String>,
1675 ) -> Result<GetRawTransactionResponse> {
1676 let mut mempool = self.mempool.clone();
1677 let verbose = verbose.unwrap_or(0) != 0;
1678
1679 let txid = transaction::Hash::from_hex(txid)
1682 .map_error(server::error::LegacyCode::InvalidAddressOrKey)?;
1683
1684 if block_hash.is_none() {
1686 match mempool
1687 .ready()
1688 .and_then(|service| {
1689 service.call(mempool::Request::TransactionsByMinedId([txid].into()))
1690 })
1691 .await
1692 .map_misc_error()?
1693 {
1694 mempool::Response::Transactions(txns) => {
1695 if let Some(tx) = txns.first() {
1696 return Ok(if verbose {
1697 GetRawTransactionResponse::Object(Box::new(
1698 TransactionObject::from_transaction(
1699 tx.transaction.clone(),
1700 None,
1701 None,
1702 &self.network,
1703 None,
1704 None,
1705 Some(false),
1706 txid,
1707 ),
1708 ))
1709 } else {
1710 let hex = tx.transaction.clone().into();
1711 GetRawTransactionResponse::Raw(hex)
1712 });
1713 }
1714 }
1715
1716 _ => unreachable!("unmatched response to a `TransactionsByMinedId` request"),
1717 };
1718 }
1719
1720 let txid = if let Some(block_hash) = block_hash {
1722 let block_hash = block::Hash::from_hex(block_hash)
1723 .map_error(server::error::LegacyCode::InvalidAddressOrKey)?;
1724 match self
1725 .read_state
1726 .clone()
1727 .oneshot(zebra_state::ReadRequest::TransactionIdsForBlock(
1728 block_hash.into(),
1729 ))
1730 .await
1731 .map_misc_error()?
1732 {
1733 zebra_state::ReadResponse::TransactionIdsForBlock(tx_ids) => *tx_ids
1734 .ok_or_error(
1735 server::error::LegacyCode::InvalidAddressOrKey,
1736 "block not found",
1737 )?
1738 .iter()
1739 .find(|id| **id == txid)
1740 .ok_or_error(
1741 server::error::LegacyCode::InvalidAddressOrKey,
1742 "txid not found",
1743 )?,
1744 _ => unreachable!("unmatched response to a `TransactionsByMinedId` request"),
1745 }
1746 } else {
1747 txid
1748 };
1749
1750 match self
1752 .read_state
1753 .clone()
1754 .oneshot(zebra_state::ReadRequest::Transaction(txid))
1755 .await
1756 .map_misc_error()?
1757 {
1758 zebra_state::ReadResponse::Transaction(Some(tx)) => Ok(if verbose {
1759 let block_hash = match self
1760 .read_state
1761 .clone()
1762 .oneshot(zebra_state::ReadRequest::BestChainBlockHash(tx.height))
1763 .await
1764 .map_misc_error()?
1765 {
1766 zebra_state::ReadResponse::BlockHash(block_hash) => block_hash,
1767 _ => unreachable!("unmatched response to a `TransactionsByMinedId` request"),
1768 };
1769
1770 GetRawTransactionResponse::Object(Box::new(TransactionObject::from_transaction(
1771 tx.tx.clone(),
1772 Some(tx.height),
1773 Some(tx.confirmations),
1774 &self.network,
1775 Some(tx.block_time),
1778 block_hash,
1779 Some(true),
1780 txid,
1781 )))
1782 } else {
1783 let hex = tx.tx.into();
1784 GetRawTransactionResponse::Raw(hex)
1785 }),
1786
1787 zebra_state::ReadResponse::Transaction(None) => {
1788 Err("No such mempool or main chain transaction")
1789 .map_error(server::error::LegacyCode::InvalidAddressOrKey)
1790 }
1791
1792 _ => unreachable!("unmatched response to a `Transaction` read request"),
1793 }
1794 }
1795
1796 async fn z_get_treestate(&self, hash_or_height: String) -> Result<GetTreestateResponse> {
1800 let mut read_state = self.read_state.clone();
1801 let network = self.network.clone();
1802
1803 let hash_or_height =
1804 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1805 .map_error(server::error::LegacyCode::InvalidParameter)?;
1808
1809 let block = match read_state
1818 .ready()
1819 .and_then(|service| service.call(zebra_state::ReadRequest::Block(hash_or_height)))
1820 .await
1821 .map_misc_error()?
1822 {
1823 zebra_state::ReadResponse::Block(Some(block)) => block,
1824 zebra_state::ReadResponse::Block(None) => {
1825 return Err("the requested block is not in the main chain")
1828 .map_error(server::error::LegacyCode::InvalidParameter);
1829 }
1830 _ => unreachable!("unmatched response to a block request"),
1831 };
1832
1833 let hash = hash_or_height
1834 .hash_or_else(|_| Some(block.hash()))
1835 .expect("block hash");
1836
1837 let height = hash_or_height
1838 .height_or_else(|_| block.coinbase_height())
1839 .expect("verified blocks have a coinbase height");
1840
1841 let time = u32::try_from(block.header.time.timestamp())
1842 .expect("Timestamps of valid blocks always fit into u32.");
1843
1844 let sapling_nu = zcash_primitives::consensus::NetworkUpgrade::Sapling;
1845 let sapling = if network.is_nu_active(sapling_nu, height.into()) {
1846 match read_state
1847 .ready()
1848 .and_then(|service| {
1849 service.call(zebra_state::ReadRequest::SaplingTree(hash.into()))
1850 })
1851 .await
1852 .map_misc_error()?
1853 {
1854 zebra_state::ReadResponse::SaplingTree(tree) => tree.map(|t| t.to_rpc_bytes()),
1855 _ => unreachable!("unmatched response to a Sapling tree request"),
1856 }
1857 } else {
1858 None
1859 };
1860
1861 let orchard_nu = zcash_primitives::consensus::NetworkUpgrade::Nu5;
1862 let orchard = if network.is_nu_active(orchard_nu, height.into()) {
1863 match read_state
1864 .ready()
1865 .and_then(|service| {
1866 service.call(zebra_state::ReadRequest::OrchardTree(hash.into()))
1867 })
1868 .await
1869 .map_misc_error()?
1870 {
1871 zebra_state::ReadResponse::OrchardTree(tree) => tree.map(|t| t.to_rpc_bytes()),
1872 _ => unreachable!("unmatched response to an Orchard tree request"),
1873 }
1874 } else {
1875 None
1876 };
1877
1878 Ok(GetTreestateResponse::from_parts(
1879 hash, height, time, sapling, orchard,
1880 ))
1881 }
1882
1883 async fn z_get_subtrees_by_index(
1884 &self,
1885 pool: String,
1886 start_index: NoteCommitmentSubtreeIndex,
1887 limit: Option<NoteCommitmentSubtreeIndex>,
1888 ) -> Result<GetSubtreesByIndexResponse> {
1889 let mut read_state = self.read_state.clone();
1890
1891 const POOL_LIST: &[&str] = &["sapling", "orchard"];
1892
1893 if pool == "sapling" {
1894 let request = zebra_state::ReadRequest::SaplingSubtrees { start_index, limit };
1895 let response = read_state
1896 .ready()
1897 .and_then(|service| service.call(request))
1898 .await
1899 .map_misc_error()?;
1900
1901 let subtrees = match response {
1902 zebra_state::ReadResponse::SaplingSubtrees(subtrees) => subtrees,
1903 _ => unreachable!("unmatched response to a subtrees request"),
1904 };
1905
1906 let subtrees = subtrees
1907 .values()
1908 .map(|subtree| SubtreeRpcData {
1909 root: subtree.root.encode_hex(),
1910 end_height: subtree.end_height,
1911 })
1912 .collect();
1913
1914 Ok(GetSubtreesByIndexResponse {
1915 pool,
1916 start_index,
1917 subtrees,
1918 })
1919 } else if pool == "orchard" {
1920 let request = zebra_state::ReadRequest::OrchardSubtrees { start_index, limit };
1921 let response = read_state
1922 .ready()
1923 .and_then(|service| service.call(request))
1924 .await
1925 .map_misc_error()?;
1926
1927 let subtrees = match response {
1928 zebra_state::ReadResponse::OrchardSubtrees(subtrees) => subtrees,
1929 _ => unreachable!("unmatched response to a subtrees request"),
1930 };
1931
1932 let subtrees = subtrees
1933 .values()
1934 .map(|subtree| SubtreeRpcData {
1935 root: subtree.root.encode_hex(),
1936 end_height: subtree.end_height,
1937 })
1938 .collect();
1939
1940 Ok(GetSubtreesByIndexResponse {
1941 pool,
1942 start_index,
1943 subtrees,
1944 })
1945 } else {
1946 Err(ErrorObject::owned(
1947 server::error::LegacyCode::Misc.into(),
1948 format!("invalid pool name, must be one of: {POOL_LIST:?}").as_str(),
1949 None::<()>,
1950 ))
1951 }
1952 }
1953
1954 async fn get_address_tx_ids(&self, request: GetAddressTxIdsRequest) -> Result<Vec<String>> {
1955 let mut read_state = self.read_state.clone();
1956 let latest_chain_tip = self.latest_chain_tip.clone();
1957
1958 let height_range = build_height_range(
1959 request.start,
1960 request.end,
1961 best_chain_tip_height(&latest_chain_tip)?,
1962 )?;
1963
1964 let valid_addresses = AddressStrings {
1965 addresses: request.addresses,
1966 }
1967 .valid_addresses()?;
1968
1969 let request = zebra_state::ReadRequest::TransactionIdsByAddresses {
1970 addresses: valid_addresses,
1971 height_range,
1972 };
1973 let response = read_state
1974 .ready()
1975 .and_then(|service| service.call(request))
1976 .await
1977 .map_misc_error()?;
1978
1979 let hashes = match response {
1980 zebra_state::ReadResponse::AddressesTransactionIds(hashes) => {
1981 let mut last_tx_location = TransactionLocation::from_usize(Height(0), 0);
1982
1983 hashes
1984 .iter()
1985 .map(|(tx_loc, tx_id)| {
1986 assert!(
1988 *tx_loc > last_tx_location,
1989 "Transactions were not in chain order:\n\
1990 {tx_loc:?} {tx_id:?} was after:\n\
1991 {last_tx_location:?}",
1992 );
1993
1994 last_tx_location = *tx_loc;
1995
1996 tx_id.to_string()
1997 })
1998 .collect()
1999 }
2000 _ => unreachable!("unmatched response to a TransactionsByAddresses request"),
2001 };
2002
2003 Ok(hashes)
2004 }
2005
2006 async fn get_address_utxos(
2007 &self,
2008 address_strings: AddressStrings,
2009 ) -> Result<GetAddressUtxosResponse> {
2010 let mut read_state = self.read_state.clone();
2011 let mut response_utxos = vec![];
2012
2013 let valid_addresses = address_strings.valid_addresses()?;
2014
2015 let request = zebra_state::ReadRequest::UtxosByAddresses(valid_addresses);
2017 let response = read_state
2018 .ready()
2019 .and_then(|service| service.call(request))
2020 .await
2021 .map_misc_error()?;
2022 let utxos = match response {
2023 zebra_state::ReadResponse::AddressUtxos(utxos) => utxos,
2024 _ => unreachable!("unmatched response to a UtxosByAddresses request"),
2025 };
2026
2027 let mut last_output_location = OutputLocation::from_usize(Height(0), 0, 0);
2028
2029 for utxo_data in utxos.utxos() {
2030 let address = utxo_data.0;
2031 let txid = *utxo_data.1;
2032 let height = utxo_data.2.height();
2033 let output_index = utxo_data.2.output_index();
2034 let script = utxo_data.3.lock_script.clone();
2035 let satoshis = u64::from(utxo_data.3.value);
2036
2037 let output_location = *utxo_data.2;
2038 assert!(
2040 output_location > last_output_location,
2041 "UTXOs were not in chain order:\n\
2042 {output_location:?} {address:?} {txid:?} was after:\n\
2043 {last_output_location:?}",
2044 );
2045
2046 let entry = Utxo {
2047 address,
2048 txid,
2049 output_index,
2050 script,
2051 satoshis,
2052 height,
2053 };
2054 response_utxos.push(entry);
2055
2056 last_output_location = output_location;
2057 }
2058
2059 Ok(response_utxos)
2060 }
2061
2062 fn stop(&self) -> Result<String> {
2063 #[cfg(not(target_os = "windows"))]
2064 if self.network.is_regtest() {
2065 match nix::sys::signal::raise(nix::sys::signal::SIGINT) {
2066 Ok(_) => Ok("Zebra server stopping".to_string()),
2067 Err(error) => Err(ErrorObject::owned(
2068 ErrorCode::InternalError.code(),
2069 format!("Failed to shut down: {error}").as_str(),
2070 None::<()>,
2071 )),
2072 }
2073 } else {
2074 Err(ErrorObject::borrowed(
2075 ErrorCode::MethodNotFound.code(),
2076 "stop is only available on regtest networks",
2077 None,
2078 ))
2079 }
2080 #[cfg(target_os = "windows")]
2081 Err(ErrorObject::borrowed(
2082 ErrorCode::MethodNotFound.code(),
2083 "stop is not available in windows targets",
2084 None,
2085 ))
2086 }
2087
2088 fn get_block_count(&self) -> Result<u32> {
2089 best_chain_tip_height(&self.latest_chain_tip).map(|height| height.0)
2090 }
2091
2092 async fn get_block_hash(&self, index: i32) -> Result<GetBlockHashResponse> {
2093 let mut read_state = self.read_state.clone();
2094 let latest_chain_tip = self.latest_chain_tip.clone();
2095
2096 let tip_height = best_chain_tip_height(&latest_chain_tip)?;
2098
2099 let height = height_from_signed_int(index, tip_height)?;
2100
2101 let request = zebra_state::ReadRequest::BestChainBlockHash(height);
2102 let response = read_state
2103 .ready()
2104 .and_then(|service| service.call(request))
2105 .await
2106 .map_error(server::error::LegacyCode::default())?;
2107
2108 match response {
2109 zebra_state::ReadResponse::BlockHash(Some(hash)) => Ok(GetBlockHashResponse(hash)),
2110 zebra_state::ReadResponse::BlockHash(None) => Err(ErrorObject::borrowed(
2111 server::error::LegacyCode::InvalidParameter.into(),
2112 "Block not found",
2113 None,
2114 )),
2115 _ => unreachable!("unmatched response to a block request"),
2116 }
2117 }
2118
2119 async fn get_block_template(
2120 &self,
2121 parameters: Option<GetBlockTemplateParameters>,
2122 ) -> Result<GetBlockTemplateResponse> {
2123 use types::get_block_template::{
2124 check_parameters, check_synced_to_tip, fetch_mempool_transactions,
2125 fetch_state_tip_and_local_time, validate_block_proposal,
2126 zip317::select_mempool_transactions,
2127 };
2128
2129 let network = self.network.clone();
2131 let extra_coinbase_data = self.gbt.extra_coinbase_data();
2132
2133 let mempool = self.mempool.clone();
2135 let mut latest_chain_tip = self.latest_chain_tip.clone();
2136 let sync_status = self.gbt.sync_status();
2137 let read_state = self.read_state.clone();
2138
2139 if let Some(HexData(block_proposal_bytes)) = parameters
2140 .as_ref()
2141 .and_then(GetBlockTemplateParameters::block_proposal_data)
2142 {
2143 return validate_block_proposal(
2144 self.gbt.block_verifier_router(),
2145 block_proposal_bytes,
2146 network,
2147 latest_chain_tip,
2148 sync_status,
2149 )
2150 .await;
2151 }
2152
2153 check_parameters(¶meters)?;
2155
2156 let client_long_poll_id = parameters.as_ref().and_then(|params| params.long_poll_id);
2157
2158 let miner_address = self
2159 .gbt
2160 .miner_address()
2161 .ok_or_misc_error("miner_address not configured")?;
2162
2163 let mut max_time_reached = false;
2167
2168 let (
2171 server_long_poll_id,
2172 chain_tip_and_local_time,
2173 mempool_txs,
2174 mempool_tx_deps,
2175 submit_old,
2176 ) = loop {
2177 check_synced_to_tip(&network, latest_chain_tip.clone(), sync_status.clone())?;
2183 latest_chain_tip.mark_best_tip_seen();
2191
2192 let chain_tip_and_local_time @ zebra_state::GetBlockTemplateChainInfo {
2199 tip_hash,
2200 tip_height,
2201 max_time,
2202 cur_time,
2203 ..
2204 } = fetch_state_tip_and_local_time(read_state.clone()).await?;
2205
2206 let Some((mempool_txs, mempool_tx_deps)) =
2217 fetch_mempool_transactions(mempool.clone(), tip_hash)
2218 .await?
2219 .or_else(|| client_long_poll_id.is_none().then(Default::default))
2223 else {
2224 continue;
2225 };
2226
2227 let server_long_poll_id = LongPollInput::new(
2229 tip_height,
2230 tip_hash,
2231 max_time,
2232 mempool_txs.iter().map(|tx| tx.transaction.id),
2233 )
2234 .generate_id();
2235
2236 if Some(&server_long_poll_id) != client_long_poll_id.as_ref() || max_time_reached {
2241 let mut submit_old = client_long_poll_id
2242 .as_ref()
2243 .map(|old_long_poll_id| server_long_poll_id.submit_old(old_long_poll_id));
2244
2245 if max_time_reached {
2250 submit_old = Some(false);
2251 }
2252
2253 break (
2254 server_long_poll_id,
2255 chain_tip_and_local_time,
2256 mempool_txs,
2257 mempool_tx_deps,
2258 submit_old,
2259 );
2260 }
2261
2262 let wait_for_mempool_request =
2272 tokio::time::sleep(Duration::from_secs(MEMPOOL_LONG_POLL_INTERVAL));
2273
2274 let mut wait_for_best_tip_change = latest_chain_tip.clone();
2277 let wait_for_best_tip_change = wait_for_best_tip_change.best_tip_changed();
2278
2279 let duration_until_max_time = max_time.saturating_duration_since(cur_time);
2291 let wait_for_max_time: OptionFuture<_> = if duration_until_max_time.seconds() > 0 {
2292 Some(tokio::time::sleep(duration_until_max_time.to_std()))
2293 } else {
2294 None
2295 }
2296 .into();
2297
2298 tokio::select! {
2305 biased;
2308
2309 _elapsed = wait_for_mempool_request => {
2311 tracing::debug!(
2312 ?max_time,
2313 ?cur_time,
2314 ?server_long_poll_id,
2315 ?client_long_poll_id,
2316 MEMPOOL_LONG_POLL_INTERVAL,
2317 "checking for a new mempool change after waiting a few seconds"
2318 );
2319 }
2320
2321 tip_changed_result = wait_for_best_tip_change => {
2323 match tip_changed_result {
2324 Ok(()) => {
2325 latest_chain_tip.mark_best_tip_seen();
2329
2330 let new_tip_hash = latest_chain_tip.best_tip_hash();
2331 if new_tip_hash == Some(tip_hash) {
2332 tracing::debug!(
2333 ?max_time,
2334 ?cur_time,
2335 ?server_long_poll_id,
2336 ?client_long_poll_id,
2337 ?tip_hash,
2338 ?tip_height,
2339 "ignoring spurious state change notification"
2340 );
2341
2342 tokio::time::sleep(Duration::from_secs(
2344 MEMPOOL_LONG_POLL_INTERVAL,
2345 )).await;
2346
2347 continue;
2348 }
2349
2350 tracing::debug!(
2351 ?max_time,
2352 ?cur_time,
2353 ?server_long_poll_id,
2354 ?client_long_poll_id,
2355 "returning from long poll because state has changed"
2356 );
2357 }
2358
2359 Err(recv_error) => {
2360 tracing::info!(
2362 ?recv_error,
2363 ?max_time,
2364 ?cur_time,
2365 ?server_long_poll_id,
2366 ?client_long_poll_id,
2367 "returning from long poll due to a state error.\
2368 Is Zebra shutting down?"
2369 );
2370
2371 return Err(recv_error).map_error(server::error::LegacyCode::default());
2372 }
2373 }
2374 }
2375
2376 Some(_elapsed) = wait_for_max_time => {
2379 tracing::info!(
2381 ?max_time,
2382 ?cur_time,
2383 ?server_long_poll_id,
2384 ?client_long_poll_id,
2385 "returning from long poll because max time was reached"
2386 );
2387
2388 max_time_reached = true;
2389 }
2390 }
2391 };
2392
2393 let next_block_height =
2401 (chain_tip_and_local_time.tip_height + 1).expect("tip is far below Height::MAX");
2402
2403 tracing::debug!(
2404 mempool_tx_hashes = ?mempool_txs
2405 .iter()
2406 .map(|tx| tx.transaction.id.mined_id())
2407 .collect::<Vec<_>>(),
2408 "selecting transactions for the template from the mempool"
2409 );
2410
2411 let mempool_txs = select_mempool_transactions(
2413 &network,
2414 next_block_height,
2415 &miner_address,
2416 mempool_txs,
2417 mempool_tx_deps,
2418 extra_coinbase_data.clone(),
2419 );
2420
2421 tracing::debug!(
2422 selected_mempool_tx_hashes = ?mempool_txs
2423 .iter()
2424 .map(|#[cfg(not(test))] tx, #[cfg(test)] (_, tx)| tx.transaction.id.mined_id())
2425 .collect::<Vec<_>>(),
2426 "selected transactions for the template from the mempool"
2427 );
2428
2429 let response = BlockTemplateResponse::new_internal(
2432 &network,
2433 &miner_address,
2434 &chain_tip_and_local_time,
2435 server_long_poll_id,
2436 mempool_txs,
2437 submit_old,
2438 extra_coinbase_data,
2439 );
2440
2441 Ok(response.into())
2442 }
2443
2444 async fn submit_block(
2445 &self,
2446 HexData(block_bytes): HexData,
2447 _parameters: Option<SubmitBlockParameters>,
2448 ) -> Result<SubmitBlockResponse> {
2449 let mut block_verifier_router = self.gbt.block_verifier_router();
2450
2451 let block: Block = match block_bytes.zcash_deserialize_into() {
2452 Ok(block_bytes) => block_bytes,
2453 Err(error) => {
2454 tracing::info!(
2455 ?error,
2456 "submit block failed: block bytes could not be deserialized into a structurally valid block"
2457 );
2458
2459 return Ok(SubmitBlockErrorResponse::Rejected.into());
2460 }
2461 };
2462
2463 let height = block
2464 .coinbase_height()
2465 .ok_or_error(0, "coinbase height not found")?;
2466 let block_hash = block.hash();
2467
2468 let block_verifier_router_response = block_verifier_router
2469 .ready()
2470 .await
2471 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?
2472 .call(zebra_consensus::Request::Commit(Arc::new(block)))
2473 .await;
2474
2475 let chain_error = match block_verifier_router_response {
2476 Ok(hash) => {
2483 tracing::info!(?hash, ?height, "submit block accepted");
2484
2485 self.gbt
2486 .advertise_mined_block(hash, height)
2487 .map_error_with_prefix(0, "failed to send mined block")?;
2488
2489 return Ok(SubmitBlockResponse::Accepted);
2490 }
2491
2492 Err(box_error) => {
2495 let error = box_error
2496 .downcast::<RouterError>()
2497 .map(|boxed_chain_error| *boxed_chain_error);
2498
2499 tracing::info!(
2500 ?error,
2501 ?block_hash,
2502 ?height,
2503 "submit block failed verification"
2504 );
2505
2506 error
2507 }
2508 };
2509
2510 let response = match chain_error {
2511 Ok(source) if source.is_duplicate_request() => SubmitBlockErrorResponse::Duplicate,
2512
2513 Ok(_verify_chain_error) => SubmitBlockErrorResponse::Rejected,
2529
2530 Err(_unknown_error_type) => SubmitBlockErrorResponse::Rejected,
2533 };
2534
2535 Ok(response.into())
2536 }
2537
2538 async fn get_mining_info(&self) -> Result<GetMiningInfoResponse> {
2539 let network = self.network.clone();
2540 let mut read_state = self.read_state.clone();
2541
2542 let chain_tip = self.latest_chain_tip.clone();
2543 let tip_height = chain_tip.best_tip_height().unwrap_or(Height(0)).0;
2544
2545 let mut current_block_tx = None;
2546 if tip_height > 0 {
2547 let mined_tx_ids = chain_tip.best_tip_mined_transaction_ids();
2548 current_block_tx =
2549 (!mined_tx_ids.is_empty()).then(|| mined_tx_ids.len().saturating_sub(1));
2550 }
2551
2552 let solution_rate_fut = self.get_network_sol_ps(None, None);
2553 let mut current_block_size = None;
2555 if tip_height > 0 {
2556 let request = zebra_state::ReadRequest::TipBlockSize;
2557 let response: zebra_state::ReadResponse = read_state
2558 .ready()
2559 .and_then(|service| service.call(request))
2560 .await
2561 .map_error(server::error::LegacyCode::default())?;
2562 current_block_size = match response {
2563 zebra_state::ReadResponse::TipBlockSize(Some(block_size)) => Some(block_size),
2564 _ => None,
2565 };
2566 }
2567
2568 Ok(GetMiningInfoResponse::new_internal(
2569 tip_height,
2570 current_block_size,
2571 current_block_tx,
2572 network,
2573 solution_rate_fut.await?,
2574 ))
2575 }
2576
2577 async fn get_network_sol_ps(
2578 &self,
2579 num_blocks: Option<i32>,
2580 height: Option<i32>,
2581 ) -> Result<u64> {
2582 let mut num_blocks = num_blocks.unwrap_or(DEFAULT_SOLUTION_RATE_WINDOW_SIZE);
2584 if num_blocks < 1 {
2586 num_blocks = i32::try_from(POW_AVERAGING_WINDOW).expect("fits in i32");
2587 }
2588 let num_blocks =
2589 usize::try_from(num_blocks).expect("just checked for negatives, i32 fits in usize");
2590
2591 let height = height.and_then(|height| height.try_into_height().ok());
2594
2595 let mut read_state = self.read_state.clone();
2596
2597 let request = ReadRequest::SolutionRate { num_blocks, height };
2598
2599 let response = read_state
2600 .ready()
2601 .and_then(|service| service.call(request))
2602 .await
2603 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
2604
2605 let solution_rate = match response {
2606 ReadResponse::SolutionRate(solution_rate) => solution_rate.unwrap_or(0),
2608
2609 _ => unreachable!("unmatched response to a solution rate request"),
2610 };
2611
2612 Ok(solution_rate
2613 .try_into()
2614 .expect("per-second solution rate always fits in u64"))
2615 }
2616
2617 async fn get_peer_info(&self) -> Result<Vec<PeerInfo>> {
2618 let address_book = self.address_book.clone();
2619 Ok(address_book
2620 .recently_live_peers(chrono::Utc::now())
2621 .into_iter()
2622 .map(PeerInfo::from)
2623 .collect())
2624 }
2625
2626 async fn validate_address(&self, raw_address: String) -> Result<ValidateAddressResponse> {
2627 let network = self.network.clone();
2628
2629 validate_address(network, raw_address)
2630 }
2631
2632 async fn z_validate_address(&self, raw_address: String) -> Result<ZValidateAddressResponse> {
2633 let network = self.network.clone();
2634
2635 let Ok(address) = raw_address.parse::<zcash_address::ZcashAddress>() else {
2636 return Ok(ZValidateAddressResponse::invalid());
2637 };
2638
2639 let address = match address.convert::<primitives::Address>() {
2640 Ok(address) => address,
2641 Err(err) => {
2642 tracing::debug!(?err, "conversion error");
2643 return Ok(ZValidateAddressResponse::invalid());
2644 }
2645 };
2646
2647 if address.network() == network.kind() {
2648 Ok(ZValidateAddressResponse {
2649 is_valid: true,
2650 address: Some(raw_address),
2651 address_type: Some(ZValidateAddressType::from(&address)),
2652 is_mine: Some(false),
2653 })
2654 } else {
2655 tracing::info!(
2656 ?network,
2657 address_network = ?address.network(),
2658 "invalid address network in z_validateaddress RPC: address is for {:?} but Zebra is on {:?}",
2659 address.network(),
2660 network
2661 );
2662
2663 Ok(ZValidateAddressResponse::invalid())
2664 }
2665 }
2666
2667 async fn get_block_subsidy(&self, height: Option<u32>) -> Result<GetBlockSubsidyResponse> {
2668 let latest_chain_tip = self.latest_chain_tip.clone();
2669 let network = self.network.clone();
2670
2671 let height = if let Some(height) = height {
2672 Height(height)
2673 } else {
2674 best_chain_tip_height(&latest_chain_tip)?
2675 };
2676
2677 if height < network.height_for_first_halving() {
2678 return Err(ErrorObject::borrowed(
2679 0,
2680 "Zebra does not support founders' reward subsidies, \
2681 use a block height that is after the first halving",
2682 None,
2683 ));
2684 }
2685
2686 let founders = Amount::zero();
2688
2689 let total_block_subsidy =
2690 block_subsidy(height, &network).map_error(server::error::LegacyCode::default())?;
2691 let miner_subsidy = miner_subsidy(height, &network, total_block_subsidy)
2692 .map_error(server::error::LegacyCode::default())?;
2693
2694 let (lockbox_streams, mut funding_streams): (Vec<_>, Vec<_>) =
2695 funding_stream_values(height, &network, total_block_subsidy)
2696 .map_error(server::error::LegacyCode::default())?
2697 .into_iter()
2698 .partition(|(receiver, _)| matches!(receiver, FundingStreamReceiver::Deferred));
2700
2701 let is_nu6 = NetworkUpgrade::current(&network, height) == NetworkUpgrade::Nu6;
2702
2703 let [lockbox_total, funding_streams_total]: [std::result::Result<
2704 Amount<NonNegative>,
2705 amount::Error,
2706 >; 2] = [&lockbox_streams, &funding_streams]
2707 .map(|streams| streams.iter().map(|&(_, amount)| amount).sum());
2708
2709 funding_streams.sort_by_key(|(receiver, _funding_stream)| {
2711 ZCASHD_FUNDING_STREAM_ORDER
2712 .iter()
2713 .position(|zcashd_receiver| zcashd_receiver == receiver)
2714 });
2715
2716 let [funding_streams, lockbox_streams]: [Vec<_>; 2] = [funding_streams, lockbox_streams]
2718 .map(|streams| {
2719 streams
2720 .into_iter()
2721 .map(|(receiver, value)| {
2722 let address = funding_stream_address(height, &network, receiver);
2723 types::subsidy::FundingStream::new_internal(
2724 is_nu6, receiver, value, address,
2725 )
2726 })
2727 .collect()
2728 });
2729
2730 Ok(GetBlockSubsidyResponse {
2731 miner: miner_subsidy.into(),
2732 founders: founders.into(),
2733 funding_streams,
2734 lockbox_streams,
2735 funding_streams_total: funding_streams_total
2736 .map_error(server::error::LegacyCode::default())?
2737 .into(),
2738 lockbox_total: lockbox_total
2739 .map_error(server::error::LegacyCode::default())?
2740 .into(),
2741 total_block_subsidy: total_block_subsidy.into(),
2742 })
2743 }
2744
2745 async fn get_difficulty(&self) -> Result<f64> {
2746 chain_tip_difficulty(self.network.clone(), self.read_state.clone(), false).await
2747 }
2748
2749 async fn z_list_unified_receivers(
2750 &self,
2751 address: String,
2752 ) -> Result<ZListUnifiedReceiversResponse> {
2753 use zcash_address::unified::Container;
2754
2755 let (network, unified_address): (
2756 zcash_protocol::consensus::NetworkType,
2757 zcash_address::unified::Address,
2758 ) = zcash_address::unified::Encoding::decode(address.clone().as_str())
2759 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
2760
2761 let mut p2pkh = None;
2762 let mut p2sh = None;
2763 let mut orchard = None;
2764 let mut sapling = None;
2765
2766 for item in unified_address.items() {
2767 match item {
2768 zcash_address::unified::Receiver::Orchard(_data) => {
2769 let addr = zcash_address::unified::Address::try_from_items(vec![item])
2770 .expect("using data already decoded as valid");
2771 orchard = Some(addr.encode(&network));
2772 }
2773 zcash_address::unified::Receiver::Sapling(data) => {
2774 let addr = zebra_chain::primitives::Address::try_from_sapling(network, data)
2775 .expect("using data already decoded as valid");
2776 sapling = Some(addr.payment_address().unwrap_or_default());
2777 }
2778 zcash_address::unified::Receiver::P2pkh(data) => {
2779 let addr =
2780 zebra_chain::primitives::Address::try_from_transparent_p2pkh(network, data)
2781 .expect("using data already decoded as valid");
2782 p2pkh = Some(addr.payment_address().unwrap_or_default());
2783 }
2784 zcash_address::unified::Receiver::P2sh(data) => {
2785 let addr =
2786 zebra_chain::primitives::Address::try_from_transparent_p2sh(network, data)
2787 .expect("using data already decoded as valid");
2788 p2sh = Some(addr.payment_address().unwrap_or_default());
2789 }
2790 _ => (),
2791 }
2792 }
2793
2794 Ok(ZListUnifiedReceiversResponse::new(
2795 orchard, sapling, p2pkh, p2sh,
2796 ))
2797 }
2798
2799 async fn invalidate_block(&self, block_hash: block::Hash) -> Result<()> {
2800 self.state
2801 .clone()
2802 .oneshot(zebra_state::Request::InvalidateBlock(block_hash))
2803 .await
2804 .map(|rsp| assert_eq!(rsp, zebra_state::Response::Invalidated(block_hash)))
2805 .map_misc_error()
2806 }
2807
2808 async fn reconsider_block(&self, block_hash: block::Hash) -> Result<Vec<block::Hash>> {
2809 self.state
2810 .clone()
2811 .oneshot(zebra_state::Request::ReconsiderBlock(block_hash))
2812 .await
2813 .map(|rsp| match rsp {
2814 zebra_state::Response::Reconsidered(block_hashes) => block_hashes,
2815 _ => unreachable!("unmatched response to a reconsider block request"),
2816 })
2817 .map_misc_error()
2818 }
2819
2820 async fn generate(&self, num_blocks: u32) -> Result<Vec<Hash>> {
2821 let rpc = self.clone();
2822 let network = self.network.clone();
2823
2824 if !network.disable_pow() {
2825 return Err(ErrorObject::borrowed(
2826 0,
2827 "generate is only supported on networks where PoW is disabled",
2828 None,
2829 ));
2830 }
2831
2832 let mut block_hashes = Vec::new();
2833 for _ in 0..num_blocks {
2834 let block_template = rpc
2835 .get_block_template(None)
2836 .await
2837 .map_error(server::error::LegacyCode::default())?;
2838
2839 let GetBlockTemplateResponse::TemplateMode(block_template) = block_template else {
2840 return Err(ErrorObject::borrowed(
2841 0,
2842 "error generating block template",
2843 None,
2844 ));
2845 };
2846
2847 let proposal_block = proposal_block_from_template(
2848 &block_template,
2849 BlockTemplateTimeSource::CurTime,
2850 &network,
2851 )
2852 .map_error(server::error::LegacyCode::default())?;
2853
2854 let hex_proposal_block = HexData(
2855 proposal_block
2856 .zcash_serialize_to_vec()
2857 .map_error(server::error::LegacyCode::default())?,
2858 );
2859
2860 rpc.submit_block(hex_proposal_block, None)
2861 .await
2862 .map_error(server::error::LegacyCode::default())?;
2863
2864 block_hashes.push(GetBlockHashResponse(proposal_block.hash()));
2865 }
2866
2867 Ok(block_hashes)
2868 }
2869
2870 async fn add_node(
2871 &self,
2872 addr: zebra_network::PeerSocketAddr,
2873 command: AddNodeCommand,
2874 ) -> Result<()> {
2875 if self.network.is_regtest() {
2876 match command {
2877 AddNodeCommand::Add => {
2878 tracing::info!(?addr, "adding peer address to the address book");
2879 if self.address_book.clone().add_peer(addr) {
2880 Ok(())
2881 } else {
2882 return Err(ErrorObject::owned(
2883 ErrorCode::InvalidParams.code(),
2884 format!("peer address was already present in the address book: {addr}"),
2885 None::<()>,
2886 ));
2887 }
2888 }
2889 }
2890 } else {
2891 return Err(ErrorObject::owned(
2892 ErrorCode::InvalidParams.code(),
2893 "addnode command is only supported on regtest",
2894 None::<()>,
2895 ));
2896 }
2897 }
2898}
2899
2900pub fn best_chain_tip_height<Tip>(latest_chain_tip: &Tip) -> Result<Height>
2905where
2906 Tip: ChainTip + Clone + Send + Sync + 'static,
2907{
2908 latest_chain_tip
2909 .best_tip_height()
2910 .ok_or_misc_error("No blocks in state")
2911}
2912
2913#[allow(clippy::too_many_arguments)]
2917#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
2918pub struct GetInfoResponse {
2919 #[getter(rename = "raw_version")]
2921 version: u64,
2922
2923 build: String,
2925
2926 subversion: String,
2928
2929 #[serde(rename = "protocolversion")]
2931 protocol_version: u32,
2932
2933 blocks: u32,
2935
2936 connections: usize,
2938
2939 #[serde(skip_serializing_if = "Option::is_none")]
2941 proxy: Option<String>,
2942
2943 difficulty: f64,
2945
2946 testnet: bool,
2948
2949 #[serde(rename = "paytxfee")]
2951 pay_tx_fee: f64,
2952
2953 #[serde(rename = "relayfee")]
2955 relay_fee: f64,
2956
2957 errors: String,
2959
2960 #[serde(rename = "errorstimestamp")]
2962 errors_timestamp: String,
2963}
2964
2965#[deprecated(note = "Use `GetInfoResponse` instead")]
2966pub use self::GetInfoResponse as GetInfo;
2967
2968impl Default for GetInfoResponse {
2969 fn default() -> Self {
2970 GetInfoResponse {
2971 version: 0,
2972 build: "some build version".to_string(),
2973 subversion: "some subversion".to_string(),
2974 protocol_version: 0,
2975 blocks: 0,
2976 connections: 0,
2977 proxy: None,
2978 difficulty: 0.0,
2979 testnet: false,
2980 pay_tx_fee: 0.0,
2981 relay_fee: 0.0,
2982 errors: "no errors".to_string(),
2983 errors_timestamp: "no errors timestamp".to_string(),
2984 }
2985 }
2986}
2987
2988impl GetInfoResponse {
2989 #[allow(clippy::too_many_arguments)]
2991 #[deprecated(note = "Use `GetInfoResponse::new` instead")]
2992 pub fn from_parts(
2993 version: u64,
2994 build: String,
2995 subversion: String,
2996 protocol_version: u32,
2997 blocks: u32,
2998 connections: usize,
2999 proxy: Option<String>,
3000 difficulty: f64,
3001 testnet: bool,
3002 pay_tx_fee: f64,
3003 relay_fee: f64,
3004 errors: String,
3005 errors_timestamp: String,
3006 ) -> Self {
3007 Self {
3008 version,
3009 build,
3010 subversion,
3011 protocol_version,
3012 blocks,
3013 connections,
3014 proxy,
3015 difficulty,
3016 testnet,
3017 pay_tx_fee,
3018 relay_fee,
3019 errors,
3020 errors_timestamp,
3021 }
3022 }
3023
3024 pub fn into_parts(
3026 self,
3027 ) -> (
3028 u64,
3029 String,
3030 String,
3031 u32,
3032 u32,
3033 usize,
3034 Option<String>,
3035 f64,
3036 bool,
3037 f64,
3038 f64,
3039 String,
3040 String,
3041 ) {
3042 (
3043 self.version,
3044 self.build,
3045 self.subversion,
3046 self.protocol_version,
3047 self.blocks,
3048 self.connections,
3049 self.proxy,
3050 self.difficulty,
3051 self.testnet,
3052 self.pay_tx_fee,
3053 self.relay_fee,
3054 self.errors,
3055 self.errors_timestamp,
3056 )
3057 }
3058
3059 fn version_from_string(build_string: &str) -> Option<u64> {
3061 let semver_version = semver::Version::parse(build_string.strip_prefix('v')?).ok()?;
3062 let build_number = semver_version
3063 .build
3064 .as_str()
3065 .split('.')
3066 .next()
3067 .and_then(|num_str| num_str.parse::<u64>().ok())
3068 .unwrap_or_default();
3069
3070 let version_number = 1_000_000 * semver_version.major
3072 + 10_000 * semver_version.minor
3073 + 100 * semver_version.patch
3074 + build_number;
3075
3076 Some(version_number)
3077 }
3078}
3079
3080#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters)]
3084pub struct GetBlockchainInfoResponse {
3085 chain: String,
3087
3088 #[getter(copy)]
3090 blocks: Height,
3091
3092 #[getter(copy)]
3095 headers: Height,
3096
3097 difficulty: f64,
3099
3100 #[serde(rename = "verificationprogress")]
3102 verification_progress: f64,
3103
3104 #[serde(rename = "chainwork")]
3106 chain_work: u64,
3107
3108 pruned: bool,
3110
3111 size_on_disk: u64,
3113
3114 commitments: u64,
3116
3117 #[serde(rename = "bestblockhash", with = "hex")]
3119 #[getter(copy)]
3120 best_block_hash: block::Hash,
3121
3122 #[serde(rename = "estimatedheight")]
3126 #[getter(copy)]
3127 estimated_height: Height,
3128
3129 #[serde(rename = "chainSupply")]
3131 chain_supply: GetBlockchainInfoBalance,
3132
3133 #[serde(rename = "valuePools")]
3135 value_pools: [GetBlockchainInfoBalance; 5],
3136
3137 upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
3139
3140 #[getter(copy)]
3142 consensus: TipConsensusBranch,
3143}
3144
3145impl Default for GetBlockchainInfoResponse {
3146 fn default() -> Self {
3147 Self {
3148 chain: "main".to_string(),
3149 blocks: Height(1),
3150 best_block_hash: block::Hash([0; 32]),
3151 estimated_height: Height(1),
3152 chain_supply: GetBlockchainInfoBalance::chain_supply(Default::default()),
3153 value_pools: GetBlockchainInfoBalance::zero_pools(),
3154 upgrades: IndexMap::new(),
3155 consensus: TipConsensusBranch {
3156 chain_tip: ConsensusBranchIdHex(ConsensusBranchId::default()),
3157 next_block: ConsensusBranchIdHex(ConsensusBranchId::default()),
3158 },
3159 headers: Height(1),
3160 difficulty: 0.0,
3161 verification_progress: 0.0,
3162 chain_work: 0,
3163 pruned: false,
3164 size_on_disk: 0,
3165 commitments: 0,
3166 }
3167 }
3168}
3169
3170impl GetBlockchainInfoResponse {
3171 #[allow(clippy::too_many_arguments)]
3175 pub fn new(
3176 chain: String,
3177 blocks: Height,
3178 best_block_hash: block::Hash,
3179 estimated_height: Height,
3180 chain_supply: GetBlockchainInfoBalance,
3181 value_pools: [GetBlockchainInfoBalance; 5],
3182 upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
3183 consensus: TipConsensusBranch,
3184 headers: Height,
3185 difficulty: f64,
3186 verification_progress: f64,
3187 chain_work: u64,
3188 pruned: bool,
3189 size_on_disk: u64,
3190 commitments: u64,
3191 ) -> Self {
3192 Self {
3193 chain,
3194 blocks,
3195 best_block_hash,
3196 estimated_height,
3197 chain_supply,
3198 value_pools,
3199 upgrades,
3200 consensus,
3201 headers,
3202 difficulty,
3203 verification_progress,
3204 chain_work,
3205 pruned,
3206 size_on_disk,
3207 commitments,
3208 }
3209 }
3210}
3211
3212#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
3217#[serde(from = "DAddressStrings")]
3218pub struct AddressStrings {
3219 addresses: Vec<String>,
3221}
3222
3223impl From<DAddressStrings> for AddressStrings {
3224 fn from(address_strings: DAddressStrings) -> Self {
3225 match address_strings {
3226 DAddressStrings::Addresses { addresses } => AddressStrings { addresses },
3227 DAddressStrings::Address(address) => AddressStrings {
3228 addresses: vec![address],
3229 },
3230 }
3231 }
3232}
3233
3234#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize)]
3236#[serde(untagged)]
3237enum DAddressStrings {
3238 Addresses { addresses: Vec<String> },
3240 Address(String),
3242}
3243
3244pub type GetAddressBalanceRequest = AddressStrings;
3246
3247impl AddressStrings {
3248 pub fn new(addresses: Vec<String>) -> AddressStrings {
3250 AddressStrings { addresses }
3251 }
3252
3253 #[deprecated(
3255 note = "Use `AddressStrings::new` instead. Validity will be checked by the server."
3256 )]
3257 pub fn new_valid(addresses: Vec<String>) -> Result<AddressStrings> {
3258 let address_strings = Self { addresses };
3259 address_strings.clone().valid_addresses()?;
3260 Ok(address_strings)
3261 }
3262
3263 pub fn valid_addresses(self) -> Result<HashSet<Address>> {
3267 let valid_addresses: HashSet<Address> = self
3270 .addresses
3271 .into_iter()
3272 .map(|address| {
3273 address
3274 .parse()
3275 .map_error(server::error::LegacyCode::InvalidAddressOrKey)
3276 })
3277 .collect::<Result<_>>()?;
3278
3279 Ok(valid_addresses)
3280 }
3281
3282 pub fn valid_address_strings(self) -> Result<Vec<String>> {
3286 self.clone().valid_addresses()?;
3287 Ok(self.addresses)
3288 }
3289}
3290
3291#[derive(
3293 Clone,
3294 Copy,
3295 Debug,
3296 Default,
3297 Eq,
3298 PartialEq,
3299 Hash,
3300 serde::Serialize,
3301 serde::Deserialize,
3302 Getters,
3303 new,
3304)]
3305pub struct GetAddressBalanceResponse {
3306 balance: u64,
3308 pub received: u64,
3310}
3311
3312#[deprecated(note = "Use `GetAddressBalanceResponse` instead.")]
3313pub use self::GetAddressBalanceResponse as AddressBalance;
3314
3315#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
3317pub struct ConsensusBranchIdHex(#[serde(with = "hex")] ConsensusBranchId);
3318
3319impl ConsensusBranchIdHex {
3320 pub fn new(consensus_branch_id: u32) -> Self {
3322 ConsensusBranchIdHex(consensus_branch_id.into())
3323 }
3324
3325 pub fn inner(&self) -> u32 {
3327 self.0.into()
3328 }
3329}
3330
3331#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3333pub struct NetworkUpgradeInfo {
3334 name: NetworkUpgrade,
3338
3339 #[serde(rename = "activationheight")]
3341 activation_height: Height,
3342
3343 status: NetworkUpgradeStatus,
3345}
3346
3347impl NetworkUpgradeInfo {
3348 pub fn from_parts(
3350 name: NetworkUpgrade,
3351 activation_height: Height,
3352 status: NetworkUpgradeStatus,
3353 ) -> Self {
3354 Self {
3355 name,
3356 activation_height,
3357 status,
3358 }
3359 }
3360
3361 pub fn into_parts(self) -> (NetworkUpgrade, Height, NetworkUpgradeStatus) {
3363 (self.name, self.activation_height, self.status)
3364 }
3365}
3366
3367#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3369pub enum NetworkUpgradeStatus {
3370 #[serde(rename = "active")]
3375 Active,
3376
3377 #[serde(rename = "disabled")]
3379 Disabled,
3380
3381 #[serde(rename = "pending")]
3383 Pending,
3384}
3385
3386#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3390pub struct TipConsensusBranch {
3391 #[serde(rename = "chaintip")]
3393 chain_tip: ConsensusBranchIdHex,
3394
3395 #[serde(rename = "nextblock")]
3397 next_block: ConsensusBranchIdHex,
3398}
3399
3400impl TipConsensusBranch {
3401 pub fn from_parts(chain_tip: u32, next_block: u32) -> Self {
3403 Self {
3404 chain_tip: ConsensusBranchIdHex::new(chain_tip),
3405 next_block: ConsensusBranchIdHex::new(next_block),
3406 }
3407 }
3408
3409 pub fn into_parts(self) -> (u32, u32) {
3411 (self.chain_tip.inner(), self.next_block.inner())
3412 }
3413}
3414
3415#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3421pub struct SendRawTransactionResponse(#[serde(with = "hex")] transaction::Hash);
3422
3423#[deprecated(note = "Use `SendRawTransactionResponse` instead")]
3424pub use self::SendRawTransactionResponse as SentTransactionHash;
3425
3426impl Default for SendRawTransactionResponse {
3427 fn default() -> Self {
3428 Self(transaction::Hash::from([0; 32]))
3429 }
3430}
3431
3432impl SendRawTransactionResponse {
3433 pub fn new(hash: transaction::Hash) -> Self {
3435 SendRawTransactionResponse(hash)
3436 }
3437
3438 #[deprecated(note = "Use `SentTransactionHash::hash` instead")]
3440 pub fn inner(&self) -> transaction::Hash {
3441 self.hash()
3442 }
3443
3444 pub fn hash(&self) -> transaction::Hash {
3446 self.0
3447 }
3448}
3449
3450#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3454#[serde(untagged)]
3455pub enum GetBlockResponse {
3456 Raw(#[serde(with = "hex")] SerializedBlock),
3458 Object(Box<BlockObject>),
3460}
3461
3462#[deprecated(note = "Use `GetBlockResponse` instead")]
3463pub use self::GetBlockResponse as GetBlock;
3464
3465impl Default for GetBlockResponse {
3466 fn default() -> Self {
3467 GetBlockResponse::Object(Box::new(BlockObject {
3468 hash: block::Hash([0; 32]),
3469 confirmations: 0,
3470 height: None,
3471 time: None,
3472 tx: Vec::new(),
3473 trees: GetBlockTrees::default(),
3474 size: None,
3475 version: None,
3476 merkle_root: None,
3477 block_commitments: None,
3478 final_sapling_root: None,
3479 final_orchard_root: None,
3480 nonce: None,
3481 bits: None,
3482 difficulty: None,
3483 chain_supply: None,
3484 value_pools: None,
3485 previous_block_hash: None,
3486 next_block_hash: None,
3487 solution: None,
3488 }))
3489 }
3490}
3491
3492#[allow(clippy::too_many_arguments)]
3494#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3495pub struct BlockObject {
3496 #[getter(copy)]
3498 #[serde(with = "hex")]
3499 hash: block::Hash,
3500
3501 confirmations: i64,
3504
3505 #[serde(skip_serializing_if = "Option::is_none")]
3507 #[getter(copy)]
3508 size: Option<i64>,
3509
3510 #[serde(skip_serializing_if = "Option::is_none")]
3512 #[getter(copy)]
3513 height: Option<Height>,
3514
3515 #[serde(skip_serializing_if = "Option::is_none")]
3517 #[getter(copy)]
3518 version: Option<u32>,
3519
3520 #[serde(with = "opthex", rename = "merkleroot")]
3522 #[serde(skip_serializing_if = "Option::is_none")]
3523 #[getter(copy)]
3524 merkle_root: Option<block::merkle::Root>,
3525
3526 #[serde(with = "opthex", rename = "blockcommitments")]
3529 #[serde(skip_serializing_if = "Option::is_none")]
3530 #[getter(copy)]
3531 block_commitments: Option<[u8; 32]>,
3532
3533 #[serde(with = "opthex", rename = "finalsaplingroot")]
3537 #[serde(skip_serializing_if = "Option::is_none")]
3538 #[getter(copy)]
3539 final_sapling_root: Option<[u8; 32]>,
3540
3541 #[serde(with = "opthex", rename = "finalorchardroot")]
3543 #[serde(skip_serializing_if = "Option::is_none")]
3544 #[getter(copy)]
3545 final_orchard_root: Option<[u8; 32]>,
3546
3547 tx: Vec<GetBlockTransaction>,
3552
3553 #[serde(skip_serializing_if = "Option::is_none")]
3555 #[getter(copy)]
3556 time: Option<i64>,
3557
3558 #[serde(with = "opthex")]
3560 #[serde(skip_serializing_if = "Option::is_none")]
3561 #[getter(copy)]
3562 nonce: Option<[u8; 32]>,
3563
3564 #[serde(with = "opthex")]
3567 #[serde(skip_serializing_if = "Option::is_none")]
3568 #[getter(copy)]
3569 solution: Option<Solution>,
3570
3571 #[serde(with = "opthex")]
3573 #[serde(skip_serializing_if = "Option::is_none")]
3574 #[getter(copy)]
3575 bits: Option<CompactDifficulty>,
3576
3577 #[serde(skip_serializing_if = "Option::is_none")]
3580 #[getter(copy)]
3581 difficulty: Option<f64>,
3582
3583 #[serde(rename = "chainSupply")]
3588 #[serde(skip_serializing_if = "Option::is_none")]
3589 chain_supply: Option<GetBlockchainInfoBalance>,
3590
3591 #[serde(rename = "valuePools")]
3593 #[serde(skip_serializing_if = "Option::is_none")]
3594 value_pools: Option<[GetBlockchainInfoBalance; 5]>,
3595
3596 #[getter(copy)]
3598 trees: GetBlockTrees,
3599
3600 #[serde(rename = "previousblockhash", skip_serializing_if = "Option::is_none")]
3602 #[serde(with = "opthex")]
3603 #[getter(copy)]
3604 previous_block_hash: Option<block::Hash>,
3605
3606 #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
3608 #[serde(with = "opthex")]
3609 #[getter(copy)]
3610 next_block_hash: Option<block::Hash>,
3611}
3612
3613#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3614#[serde(untagged)]
3615pub enum GetBlockTransaction {
3618 Hash(#[serde(with = "hex")] transaction::Hash),
3620 Object(Box<TransactionObject>),
3622}
3623
3624#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3628#[serde(untagged)]
3629pub enum GetBlockHeaderResponse {
3630 Raw(hex_data::HexData),
3632
3633 Object(Box<BlockHeaderObject>),
3635}
3636
3637#[deprecated(note = "Use `GetBlockHeaderResponse` instead")]
3638pub use self::GetBlockHeaderResponse as GetBlockHeader;
3639
3640#[allow(clippy::too_many_arguments)]
3641#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3642pub struct BlockHeaderObject {
3646 #[serde(with = "hex")]
3648 #[getter(copy)]
3649 hash: block::Hash,
3650
3651 confirmations: i64,
3654
3655 #[getter(copy)]
3657 height: Height,
3658
3659 version: u32,
3661
3662 #[serde(with = "hex", rename = "merkleroot")]
3664 #[getter(copy)]
3665 merkle_root: block::merkle::Root,
3666
3667 #[serde(with = "hex", rename = "blockcommitments")]
3670 #[getter(copy)]
3671 block_commitments: [u8; 32],
3672
3673 #[serde(with = "hex", rename = "finalsaplingroot")]
3675 #[getter(copy)]
3676 final_sapling_root: [u8; 32],
3677
3678 #[serde(skip)]
3681 sapling_tree_size: u64,
3682
3683 time: i64,
3685
3686 #[serde(with = "hex")]
3688 #[getter(copy)]
3689 nonce: [u8; 32],
3690
3691 #[serde(with = "hex")]
3693 #[getter(copy)]
3694 solution: Solution,
3695
3696 #[serde(with = "hex")]
3698 #[getter(copy)]
3699 bits: CompactDifficulty,
3700
3701 difficulty: f64,
3704
3705 #[serde(rename = "previousblockhash")]
3707 #[serde(with = "hex")]
3708 #[getter(copy)]
3709 previous_block_hash: block::Hash,
3710
3711 #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
3713 #[getter(copy)]
3714 #[serde(with = "opthex")]
3715 next_block_hash: Option<block::Hash>,
3716}
3717
3718#[deprecated(note = "Use `BlockHeaderObject` instead")]
3719pub use BlockHeaderObject as GetBlockHeaderObject;
3720
3721impl Default for GetBlockHeaderResponse {
3722 fn default() -> Self {
3723 GetBlockHeaderResponse::Object(Box::default())
3724 }
3725}
3726
3727impl Default for BlockHeaderObject {
3728 fn default() -> Self {
3729 let difficulty: ExpandedDifficulty = zebra_chain::work::difficulty::U256::one().into();
3730
3731 BlockHeaderObject {
3732 hash: block::Hash([0; 32]),
3733 confirmations: 0,
3734 height: Height::MIN,
3735 version: 4,
3736 merkle_root: block::merkle::Root([0; 32]),
3737 block_commitments: Default::default(),
3738 final_sapling_root: Default::default(),
3739 sapling_tree_size: Default::default(),
3740 time: 0,
3741 nonce: [0; 32],
3742 solution: Solution::for_proposal(),
3743 bits: difficulty.to_compact(),
3744 difficulty: 1.0,
3745 previous_block_hash: block::Hash([0; 32]),
3746 next_block_hash: Some(block::Hash([0; 32])),
3747 }
3748 }
3749}
3750
3751#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3757#[serde(transparent)]
3758pub struct GetBlockHashResponse(#[serde(with = "hex")] pub(crate) block::Hash);
3759
3760impl GetBlockHashResponse {
3761 pub fn new(hash: block::Hash) -> Self {
3763 GetBlockHashResponse(hash)
3764 }
3765
3766 pub fn hash(&self) -> block::Hash {
3768 self.0
3769 }
3770}
3771
3772#[deprecated(note = "Use `GetBlockHashResponse` instead")]
3773pub use self::GetBlockHashResponse as GetBlockHash;
3774
3775pub type Hash = GetBlockHashResponse;
3777
3778#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, Getters, new)]
3780pub struct GetBlockHeightAndHashResponse {
3781 #[getter(copy)]
3783 height: block::Height,
3784 #[getter(copy)]
3786 hash: block::Hash,
3787}
3788
3789#[deprecated(note = "Use `GetBlockHeightAndHashResponse` instead.")]
3790pub use GetBlockHeightAndHashResponse as GetBestBlockHeightAndHash;
3791
3792impl Default for GetBlockHeightAndHashResponse {
3793 fn default() -> Self {
3794 Self {
3795 height: block::Height::MIN,
3796 hash: block::Hash([0; 32]),
3797 }
3798 }
3799}
3800
3801impl Default for GetBlockHashResponse {
3802 fn default() -> Self {
3803 GetBlockHashResponse(block::Hash([0; 32]))
3804 }
3805}
3806
3807#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3811#[serde(untagged)]
3812pub enum GetRawTransactionResponse {
3813 Raw(#[serde(with = "hex")] SerializedTransaction),
3815 Object(Box<TransactionObject>),
3817}
3818
3819#[deprecated(note = "Use `GetRawTransactionResponse` instead")]
3820pub use self::GetRawTransactionResponse as GetRawTransaction;
3821
3822impl Default for GetRawTransactionResponse {
3823 fn default() -> Self {
3824 Self::Object(Box::default())
3825 }
3826}
3827
3828pub type GetAddressUtxosResponse = Vec<Utxo>;
3830
3831#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3835pub struct Utxo {
3836 address: transparent::Address,
3838
3839 #[serde(with = "hex")]
3841 #[getter(copy)]
3842 txid: transaction::Hash,
3843
3844 #[serde(rename = "outputIndex")]
3846 #[getter(copy)]
3847 output_index: OutputIndex,
3848
3849 #[serde(with = "hex")]
3851 script: transparent::Script,
3852
3853 satoshis: u64,
3855
3856 #[getter(copy)]
3860 height: Height,
3861}
3862
3863#[deprecated(note = "Use `Utxo` instead")]
3864pub use self::Utxo as GetAddressUtxos;
3865
3866impl Default for Utxo {
3867 fn default() -> Self {
3868 Self {
3869 address: transparent::Address::from_pub_key_hash(
3870 zebra_chain::parameters::NetworkKind::default(),
3871 [0u8; 20],
3872 ),
3873 txid: transaction::Hash::from([0; 32]),
3874 output_index: OutputIndex::from_u64(0),
3875 script: transparent::Script::new(&[0u8; 10]),
3876 satoshis: u64::default(),
3877 height: Height(0),
3878 }
3879 }
3880}
3881
3882impl Utxo {
3883 #[deprecated(note = "Use `Utxo::new` instead")]
3885 pub fn from_parts(
3886 address: transparent::Address,
3887 txid: transaction::Hash,
3888 output_index: OutputIndex,
3889 script: transparent::Script,
3890 satoshis: u64,
3891 height: Height,
3892 ) -> Self {
3893 Utxo {
3894 address,
3895 txid,
3896 output_index,
3897 script,
3898 satoshis,
3899 height,
3900 }
3901 }
3902
3903 pub fn into_parts(
3905 &self,
3906 ) -> (
3907 transparent::Address,
3908 transaction::Hash,
3909 OutputIndex,
3910 transparent::Script,
3911 u64,
3912 Height,
3913 ) {
3914 (
3915 self.address.clone(),
3916 self.txid,
3917 self.output_index,
3918 self.script.clone(),
3919 self.satoshis,
3920 self.height,
3921 )
3922 }
3923}
3924
3925#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, Getters, new)]
3929pub struct GetAddressTxIdsRequest {
3930 addresses: Vec<String>,
3932 start: Option<u32>,
3934 end: Option<u32>,
3936}
3937
3938impl GetAddressTxIdsRequest {
3939 #[deprecated(note = "Use `GetAddressTxIdsRequest::new` instead.")]
3941 pub fn from_parts(addresses: Vec<String>, start: u32, end: u32) -> Self {
3942 GetAddressTxIdsRequest {
3943 addresses,
3944 start: Some(start),
3945 end: Some(end),
3946 }
3947 }
3948
3949 pub fn into_parts(&self) -> (Vec<String>, u32, u32) {
3951 (
3952 self.addresses.clone(),
3953 self.start.unwrap_or(0),
3954 self.end.unwrap_or(0),
3955 )
3956 }
3957}
3958
3959#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3961pub struct GetBlockTrees {
3962 #[serde(skip_serializing_if = "SaplingTrees::is_empty")]
3963 sapling: SaplingTrees,
3964 #[serde(skip_serializing_if = "OrchardTrees::is_empty")]
3965 orchard: OrchardTrees,
3966}
3967
3968impl Default for GetBlockTrees {
3969 fn default() -> Self {
3970 GetBlockTrees {
3971 sapling: SaplingTrees { size: 0 },
3972 orchard: OrchardTrees { size: 0 },
3973 }
3974 }
3975}
3976
3977impl GetBlockTrees {
3978 pub fn new(sapling: u64, orchard: u64) -> Self {
3980 GetBlockTrees {
3981 sapling: SaplingTrees { size: sapling },
3982 orchard: OrchardTrees { size: orchard },
3983 }
3984 }
3985
3986 pub fn sapling(self) -> u64 {
3988 self.sapling.size
3989 }
3990
3991 pub fn orchard(self) -> u64 {
3993 self.orchard.size
3994 }
3995}
3996
3997#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3999pub struct SaplingTrees {
4000 size: u64,
4001}
4002
4003impl SaplingTrees {
4004 fn is_empty(&self) -> bool {
4005 self.size == 0
4006 }
4007}
4008
4009#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
4011pub struct OrchardTrees {
4012 size: u64,
4013}
4014
4015impl OrchardTrees {
4016 fn is_empty(&self) -> bool {
4017 self.size == 0
4018 }
4019}
4020
4021fn build_height_range(
4037 start: Option<u32>,
4038 end: Option<u32>,
4039 chain_height: Height,
4040) -> Result<RangeInclusive<Height>> {
4041 let start = Height(start.unwrap_or(0)).min(chain_height);
4044
4045 let end = match end {
4047 Some(0) | None => chain_height,
4048 Some(val) => Height(val).min(chain_height),
4049 };
4050
4051 if start > end {
4052 return Err(ErrorObject::owned(
4053 ErrorCode::InvalidParams.code(),
4054 format!("start {start:?} must be less than or equal to end {end:?}"),
4055 None::<()>,
4056 ));
4057 }
4058
4059 Ok(start..=end)
4060}
4061
4062#[allow(dead_code)]
4070pub fn height_from_signed_int(index: i32, tip_height: Height) -> Result<Height> {
4071 if index >= 0 {
4072 let height = index.try_into().expect("Positive i32 always fits in u32");
4073 if height > tip_height.0 {
4074 return Err(ErrorObject::borrowed(
4075 ErrorCode::InvalidParams.code(),
4076 "Provided index is greater than the current tip",
4077 None,
4078 ));
4079 }
4080 Ok(Height(height))
4081 } else {
4082 let height = i32::try_from(tip_height.0)
4084 .expect("tip height fits in i32, because Height::MAX fits in i32")
4085 .checked_add(index + 1);
4086
4087 let sanitized_height = match height {
4088 None => {
4089 return Err(ErrorObject::borrowed(
4090 ErrorCode::InvalidParams.code(),
4091 "Provided index is not valid",
4092 None,
4093 ));
4094 }
4095 Some(h) => {
4096 if h < 0 {
4097 return Err(ErrorObject::borrowed(
4098 ErrorCode::InvalidParams.code(),
4099 "Provided negative index ends up with a negative height",
4100 None,
4101 ));
4102 }
4103 let h: u32 = h.try_into().expect("Positive i32 always fits in u32");
4104 if h > tip_height.0 {
4105 return Err(ErrorObject::borrowed(
4106 ErrorCode::InvalidParams.code(),
4107 "Provided index is greater than the current tip",
4108 None,
4109 ));
4110 }
4111
4112 h
4113 }
4114 };
4115
4116 Ok(Height(sanitized_height))
4117 }
4118}
4119
4120pub mod opthex {
4122 use hex::{FromHex, ToHex};
4123 use serde::{de, Deserialize, Deserializer, Serializer};
4124
4125 #[allow(missing_docs)]
4126 pub fn serialize<S, T>(data: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
4127 where
4128 S: Serializer,
4129 T: ToHex,
4130 {
4131 match data {
4132 Some(data) => {
4133 let s = data.encode_hex::<String>();
4134 serializer.serialize_str(&s)
4135 }
4136 None => serializer.serialize_none(),
4137 }
4138 }
4139
4140 #[allow(missing_docs)]
4141 pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
4142 where
4143 D: Deserializer<'de>,
4144 T: FromHex,
4145 {
4146 let opt = Option::<String>::deserialize(deserializer)?;
4147 match opt {
4148 Some(s) => T::from_hex(&s)
4149 .map(Some)
4150 .map_err(|_e| de::Error::custom("failed to convert hex string")),
4151 None => Ok(None),
4152 }
4153 }
4154}
4155
4156pub mod arrayhex {
4158 use serde::{Deserializer, Serializer};
4159 use std::fmt;
4160
4161 #[allow(missing_docs)]
4162 pub fn serialize<S, const N: usize>(data: &[u8; N], serializer: S) -> Result<S::Ok, S::Error>
4163 where
4164 S: Serializer,
4165 {
4166 let hex_string = hex::encode(data);
4167 serializer.serialize_str(&hex_string)
4168 }
4169
4170 #[allow(missing_docs)]
4171 pub fn deserialize<'de, D, const N: usize>(deserializer: D) -> Result<[u8; N], D::Error>
4172 where
4173 D: Deserializer<'de>,
4174 {
4175 struct HexArrayVisitor<const N: usize>;
4176
4177 impl<const N: usize> serde::de::Visitor<'_> for HexArrayVisitor<N> {
4178 type Value = [u8; N];
4179
4180 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
4181 write!(formatter, "a hex string representing exactly {N} bytes")
4182 }
4183
4184 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
4185 where
4186 E: serde::de::Error,
4187 {
4188 let vec = hex::decode(v).map_err(E::custom)?;
4189 vec.clone().try_into().map_err(|_| {
4190 E::invalid_length(vec.len(), &format!("expected {N} bytes").as_str())
4191 })
4192 }
4193 }
4194
4195 deserializer.deserialize_str(HexArrayVisitor::<N>)
4196 }
4197}
4198
4199pub async fn chain_tip_difficulty<State>(
4201 network: Network,
4202 mut state: State,
4203 should_use_default: bool,
4204) -> Result<f64>
4205where
4206 State: Service<
4207 zebra_state::ReadRequest,
4208 Response = zebra_state::ReadResponse,
4209 Error = zebra_state::BoxError,
4210 > + Clone
4211 + Send
4212 + Sync
4213 + 'static,
4214 State::Future: Send,
4215{
4216 let request = ReadRequest::ChainInfo;
4217
4218 let response = state
4224 .ready()
4225 .and_then(|service| service.call(request))
4226 .await;
4227
4228 let response = match (should_use_default, response) {
4229 (_, Ok(res)) => res,
4230 (true, Err(_)) => {
4231 return Ok((U256::from(network.target_difficulty_limit()) >> 128).as_u128() as f64);
4232 }
4233 (false, Err(error)) => return Err(ErrorObject::owned(0, error.to_string(), None::<()>)),
4234 };
4235
4236 let chain_info = match response {
4237 ReadResponse::ChainInfo(info) => info,
4238 _ => unreachable!("unmatched response to a chain info request"),
4239 };
4240
4241 let pow_limit: U256 = network.target_difficulty_limit().into();
4264 let Some(difficulty) = chain_info.expected_difficulty.to_expanded() else {
4265 return Ok(0.0);
4266 };
4267
4268 let pow_limit = pow_limit >> 128;
4270 let difficulty = U256::from(difficulty) >> 128;
4271
4272 let pow_limit = pow_limit.as_u128() as f64;
4275 let difficulty = difficulty.as_u128() as f64;
4276
4277 Ok(pow_limit / difficulty)
4279}
4280
4281#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
4283pub enum AddNodeCommand {
4284 #[serde(rename = "add")]
4286 Add,
4287}