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
910#[async_trait]
911impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus> RpcServer
912 for RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
913where
914 Mempool: Service<
915 mempool::Request,
916 Response = mempool::Response,
917 Error = zebra_node_services::BoxError,
918 > + Clone
919 + Send
920 + Sync
921 + 'static,
922 Mempool::Future: Send,
923 State: Service<
924 zebra_state::Request,
925 Response = zebra_state::Response,
926 Error = zebra_state::BoxError,
927 > + Clone
928 + Send
929 + Sync
930 + 'static,
931 State::Future: Send,
932 ReadState: Service<
933 zebra_state::ReadRequest,
934 Response = zebra_state::ReadResponse,
935 Error = zebra_state::BoxError,
936 > + Clone
937 + Send
938 + Sync
939 + 'static,
940 ReadState::Future: Send,
941 Tip: ChainTip + Clone + Send + Sync + 'static,
942 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
943 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
944 + Clone
945 + Send
946 + Sync
947 + 'static,
948 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
949 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
950{
951 async fn get_info(&self) -> Result<GetInfoResponse> {
952 let version = GetInfoResponse::version_from_string(&self.build_version)
953 .expect("invalid version string");
954
955 let connections = self.address_book.recently_live_peers(Utc::now()).len();
956
957 let last_error_recorded = self.last_warn_error_log_rx.borrow().clone();
958 let (last_error_log, _level, last_error_log_time) = last_error_recorded.unwrap_or((
959 GetInfoResponse::default().errors,
960 tracing::Level::INFO,
961 Utc::now(),
962 ));
963
964 let tip_height = self
965 .latest_chain_tip
966 .best_tip_height()
967 .unwrap_or(Height::MIN);
968 let testnet = self.network.is_a_test_network();
969
970 let pay_tx_fee = 0.0;
976
977 let relay_fee = zebra_chain::transaction::zip317::MIN_MEMPOOL_TX_FEE_RATE as f64
978 / (zebra_chain::amount::COIN as f64);
979 let difficulty = chain_tip_difficulty(self.network.clone(), self.read_state.clone(), true)
980 .await
981 .expect("should always be Ok when `should_use_default` is true");
982
983 let response = GetInfoResponse {
984 version,
985 build: self.build_version.clone(),
986 subversion: self.user_agent.clone(),
987 protocol_version: zebra_network::constants::CURRENT_NETWORK_PROTOCOL_VERSION.0,
988 blocks: tip_height.0,
989 connections,
990 proxy: None,
991 difficulty,
992 testnet,
993 pay_tx_fee,
994 relay_fee,
995 errors: last_error_log,
996 errors_timestamp: last_error_log_time.to_string(),
997 };
998
999 Ok(response)
1000 }
1001
1002 #[allow(clippy::unwrap_in_result)]
1003 async fn get_blockchain_info(&self) -> Result<GetBlockchainInfoResponse> {
1004 let debug_force_finished_sync = self.debug_force_finished_sync;
1005 let network = &self.network;
1006
1007 let (usage_info_rsp, tip_pool_values_rsp, chain_tip_difficulty) = {
1008 use zebra_state::ReadRequest::*;
1009 let state_call = |request| self.read_state.clone().oneshot(request);
1010 tokio::join!(
1011 state_call(UsageInfo),
1012 state_call(TipPoolValues),
1013 chain_tip_difficulty(network.clone(), self.read_state.clone(), true)
1014 )
1015 };
1016
1017 let (size_on_disk, (tip_height, tip_hash), value_balance, difficulty) = {
1018 use zebra_state::ReadResponse::*;
1019
1020 let UsageInfo(size_on_disk) = usage_info_rsp.map_misc_error()? else {
1021 unreachable!("unmatched response to a TipPoolValues request")
1022 };
1023
1024 let (tip, value_balance) = match tip_pool_values_rsp {
1025 Ok(TipPoolValues {
1026 tip_height,
1027 tip_hash,
1028 value_balance,
1029 }) => ((tip_height, tip_hash), value_balance),
1030 Ok(_) => unreachable!("unmatched response to a TipPoolValues request"),
1031 Err(_) => ((Height::MIN, network.genesis_hash()), Default::default()),
1032 };
1033
1034 let difficulty = chain_tip_difficulty
1035 .expect("should always be Ok when `should_use_default` is true");
1036
1037 (size_on_disk, tip, value_balance, difficulty)
1038 };
1039
1040 let now = Utc::now();
1041 let (estimated_height, verification_progress) = self
1042 .latest_chain_tip
1043 .best_tip_height_and_block_time()
1044 .map(|(tip_height, tip_block_time)| {
1045 let height =
1046 NetworkChainTipHeightEstimator::new(tip_block_time, tip_height, network)
1047 .estimate_height_at(now);
1048
1049 let height =
1053 if tip_block_time > now || height < tip_height || debug_force_finished_sync {
1054 tip_height
1055 } else {
1056 height
1057 };
1058
1059 (height, f64::from(tip_height.0) / f64::from(height.0))
1060 })
1061 .unwrap_or((Height::MIN, 0.0));
1063
1064 let mut upgrades = IndexMap::new();
1068 for (activation_height, network_upgrade) in network.full_activation_list() {
1069 if let Some(branch_id) = network_upgrade.branch_id() {
1074 let status = if tip_height >= activation_height {
1076 NetworkUpgradeStatus::Active
1077 } else {
1078 NetworkUpgradeStatus::Pending
1079 };
1080
1081 let upgrade = NetworkUpgradeInfo {
1082 name: network_upgrade,
1083 activation_height,
1084 status,
1085 };
1086 upgrades.insert(ConsensusBranchIdHex(branch_id), upgrade);
1087 }
1088 }
1089
1090 let next_block_height =
1092 (tip_height + 1).expect("valid chain tips are a lot less than Height::MAX");
1093 let consensus = TipConsensusBranch {
1094 chain_tip: ConsensusBranchIdHex(
1095 NetworkUpgrade::current(network, tip_height)
1096 .branch_id()
1097 .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
1098 ),
1099 next_block: ConsensusBranchIdHex(
1100 NetworkUpgrade::current(network, next_block_height)
1101 .branch_id()
1102 .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
1103 ),
1104 };
1105
1106 let response = GetBlockchainInfoResponse {
1107 chain: network.bip70_network_name(),
1108 blocks: tip_height,
1109 best_block_hash: tip_hash,
1110 estimated_height,
1111 chain_supply: GetBlockchainInfoBalance::chain_supply(value_balance),
1112 value_pools: GetBlockchainInfoBalance::value_pools(value_balance, None),
1113 upgrades,
1114 consensus,
1115 headers: tip_height,
1116 difficulty,
1117 verification_progress,
1118 chain_work: 0,
1120 pruned: false,
1121 size_on_disk,
1122 commitments: 0,
1124 };
1125
1126 Ok(response)
1127 }
1128
1129 async fn get_address_balance(
1130 &self,
1131 address_strings: GetAddressBalanceRequest,
1132 ) -> Result<GetAddressBalanceResponse> {
1133 let valid_addresses = address_strings.valid_addresses()?;
1134
1135 let request = zebra_state::ReadRequest::AddressBalance(valid_addresses);
1136 let response = self
1137 .read_state
1138 .clone()
1139 .oneshot(request)
1140 .await
1141 .map_misc_error()?;
1142
1143 match response {
1144 zebra_state::ReadResponse::AddressBalance { balance, received } => {
1145 Ok(GetAddressBalanceResponse {
1146 balance: u64::from(balance),
1147 received,
1148 })
1149 }
1150 _ => unreachable!("Unexpected response from state service: {response:?}"),
1151 }
1152 }
1153
1154 async fn send_raw_transaction(
1156 &self,
1157 raw_transaction_hex: String,
1158 _allow_high_fees: Option<bool>,
1159 ) -> Result<SendRawTransactionResponse> {
1160 let mempool = self.mempool.clone();
1161 let queue_sender = self.queue_sender.clone();
1162
1163 let raw_transaction_bytes = Vec::from_hex(raw_transaction_hex)
1166 .map_error(server::error::LegacyCode::Deserialization)?;
1167 let raw_transaction = Transaction::zcash_deserialize(&*raw_transaction_bytes)
1168 .map_error(server::error::LegacyCode::Deserialization)?;
1169
1170 let transaction_hash = raw_transaction.hash();
1171
1172 let unmined_transaction = UnminedTx::from(raw_transaction.clone());
1174 let _ = queue_sender.send(unmined_transaction);
1175
1176 let transaction_parameter = mempool::Gossip::Tx(raw_transaction.into());
1177 let request = mempool::Request::Queue(vec![transaction_parameter]);
1178
1179 let response = mempool.oneshot(request).await.map_misc_error()?;
1180
1181 let mut queue_results = match response {
1182 mempool::Response::Queued(results) => results,
1183 _ => unreachable!("incorrect response variant from mempool service"),
1184 };
1185
1186 assert_eq!(
1187 queue_results.len(),
1188 1,
1189 "mempool service returned more results than expected"
1190 );
1191
1192 let queue_result = queue_results
1193 .pop()
1194 .expect("there should be exactly one item in Vec")
1195 .inspect_err(|err| tracing::debug!("sent transaction to mempool: {:?}", &err))
1196 .map_misc_error()?
1197 .await
1198 .map_misc_error()?;
1199
1200 tracing::debug!("sent transaction to mempool: {:?}", &queue_result);
1201
1202 queue_result
1203 .map(|_| SendRawTransactionResponse(transaction_hash))
1204 .map_error(server::error::LegacyCode::Verify)
1211 }
1212
1213 async fn get_block(
1222 &self,
1223 hash_or_height: String,
1224 verbosity: Option<u8>,
1225 ) -> Result<GetBlockResponse> {
1226 let verbosity = verbosity.unwrap_or(1);
1227 let network = self.network.clone();
1228 let original_hash_or_height = hash_or_height.clone();
1229
1230 let get_block_header_future = if matches!(verbosity, 1 | 2) {
1232 Some(self.get_block_header(original_hash_or_height.clone(), Some(true)))
1233 } else {
1234 None
1235 };
1236
1237 let hash_or_height =
1238 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1239 .map_error(server::error::LegacyCode::InvalidParameter)?;
1242
1243 if verbosity == 0 {
1244 let request = zebra_state::ReadRequest::Block(hash_or_height);
1245 let response = self
1246 .read_state
1247 .clone()
1248 .oneshot(request)
1249 .await
1250 .map_misc_error()?;
1251
1252 match response {
1253 zebra_state::ReadResponse::Block(Some(block)) => {
1254 Ok(GetBlockResponse::Raw(block.into()))
1255 }
1256 zebra_state::ReadResponse::Block(None) => {
1257 Err("Block not found").map_error(server::error::LegacyCode::InvalidParameter)
1258 }
1259 _ => unreachable!("unmatched response to a block request"),
1260 }
1261 } else if let Some(get_block_header_future) = get_block_header_future {
1262 let get_block_header_result: Result<GetBlockHeaderResponse> =
1263 get_block_header_future.await;
1264
1265 let GetBlockHeaderResponse::Object(block_header) = get_block_header_result? else {
1266 panic!("must return Object")
1267 };
1268
1269 let BlockHeaderObject {
1270 hash,
1271 confirmations,
1272 height,
1273 version,
1274 merkle_root,
1275 block_commitments,
1276 final_sapling_root,
1277 sapling_tree_size,
1278 time,
1279 nonce,
1280 solution,
1281 bits,
1282 difficulty,
1283 previous_block_hash,
1284 next_block_hash,
1285 } = *block_header;
1286
1287 let transactions_request = match verbosity {
1288 1 => zebra_state::ReadRequest::TransactionIdsForBlock(hash_or_height),
1289 2 => zebra_state::ReadRequest::BlockAndSize(hash_or_height),
1290 _other => panic!("get_block_header_fut should be none"),
1291 };
1292
1293 let hash_or_height = hash.into();
1298 let requests = vec![
1299 transactions_request,
1307 zebra_state::ReadRequest::OrchardTree(hash_or_height),
1309 zebra_state::ReadRequest::BlockInfo(previous_block_hash.into()),
1311 zebra_state::ReadRequest::BlockInfo(hash_or_height),
1312 ];
1313
1314 let mut futs = FuturesOrdered::new();
1315
1316 for request in requests {
1317 futs.push_back(self.read_state.clone().oneshot(request));
1318 }
1319
1320 let tx_ids_response = futs.next().await.expect("`futs` should not be empty");
1321 let (tx, size): (Vec<_>, Option<usize>) = match tx_ids_response.map_misc_error()? {
1322 zebra_state::ReadResponse::TransactionIdsForBlock(tx_ids) => (
1323 tx_ids
1324 .ok_or_misc_error("block not found")?
1325 .iter()
1326 .map(|tx_id| GetBlockTransaction::Hash(*tx_id))
1327 .collect(),
1328 None,
1329 ),
1330 zebra_state::ReadResponse::BlockAndSize(block_and_size) => {
1331 let (block, size) = block_and_size.ok_or_misc_error("Block not found")?;
1332 let block_time = block.header.time;
1333 let transactions =
1334 block
1335 .transactions
1336 .iter()
1337 .map(|tx| {
1338 GetBlockTransaction::Object(Box::new(
1339 TransactionObject::from_transaction(
1340 tx.clone(),
1341 Some(height),
1342 Some(confirmations.try_into().expect(
1343 "should be less than max block height, i32::MAX",
1344 )),
1345 &network,
1346 Some(block_time),
1347 Some(hash),
1348 Some(true),
1349 tx.hash(),
1350 ),
1351 ))
1352 })
1353 .collect();
1354 (transactions, Some(size))
1355 }
1356 _ => unreachable!("unmatched response to a transaction_ids_for_block request"),
1357 };
1358
1359 let orchard_tree_response = futs.next().await.expect("`futs` should not be empty");
1360 let zebra_state::ReadResponse::OrchardTree(orchard_tree) =
1361 orchard_tree_response.map_misc_error()?
1362 else {
1363 unreachable!("unmatched response to a OrchardTree request");
1364 };
1365
1366 let nu5_activation = NetworkUpgrade::Nu5.activation_height(&network);
1367
1368 let orchard_tree = orchard_tree.ok_or_misc_error("missing Orchard tree")?;
1370
1371 let final_orchard_root = match nu5_activation {
1372 Some(activation_height) if height >= activation_height => {
1373 Some(orchard_tree.root().into())
1374 }
1375 _other => None,
1376 };
1377
1378 let sapling = SaplingTrees {
1379 size: sapling_tree_size,
1380 };
1381
1382 let orchard_tree_size = orchard_tree.count();
1383 let orchard = OrchardTrees {
1384 size: orchard_tree_size,
1385 };
1386
1387 let trees = GetBlockTrees { sapling, orchard };
1388
1389 let block_info_response = futs.next().await.expect("`futs` should not be empty");
1390 let zebra_state::ReadResponse::BlockInfo(prev_block_info) =
1391 block_info_response.map_misc_error()?
1392 else {
1393 unreachable!("unmatched response to a BlockInfo request");
1394 };
1395 let block_info_response = futs.next().await.expect("`futs` should not be empty");
1396 let zebra_state::ReadResponse::BlockInfo(block_info) =
1397 block_info_response.map_misc_error()?
1398 else {
1399 unreachable!("unmatched response to a BlockInfo request");
1400 };
1401
1402 let delta = block_info.as_ref().and_then(|d| {
1403 let value_pools = d.value_pools().constrain::<NegativeAllowed>().ok()?;
1404 let prev_value_pools = prev_block_info
1405 .map(|d| d.value_pools().constrain::<NegativeAllowed>())
1406 .unwrap_or(Ok(ValueBalance::<NegativeAllowed>::zero()))
1407 .ok()?;
1408 (value_pools - prev_value_pools).ok()
1409 });
1410 let size = size.or(block_info.as_ref().map(|d| d.size() as usize));
1411
1412 Ok(GetBlockResponse::Object(Box::new(BlockObject {
1413 hash,
1414 confirmations,
1415 height: Some(height),
1416 version: Some(version),
1417 merkle_root: Some(merkle_root),
1418 time: Some(time),
1419 nonce: Some(nonce),
1420 solution: Some(solution),
1421 bits: Some(bits),
1422 difficulty: Some(difficulty),
1423 tx,
1424 trees,
1425 chain_supply: block_info
1426 .as_ref()
1427 .map(|d| GetBlockchainInfoBalance::chain_supply(*d.value_pools())),
1428 value_pools: block_info
1429 .map(|d| GetBlockchainInfoBalance::value_pools(*d.value_pools(), delta)),
1430 size: size.map(|size| size as i64),
1431 block_commitments: Some(block_commitments),
1432 final_sapling_root: Some(final_sapling_root),
1433 final_orchard_root,
1434 previous_block_hash: Some(previous_block_hash),
1435 next_block_hash,
1436 })))
1437 } else {
1438 Err("invalid verbosity value").map_error(server::error::LegacyCode::InvalidParameter)
1439 }
1440 }
1441
1442 async fn get_block_header(
1443 &self,
1444 hash_or_height: String,
1445 verbose: Option<bool>,
1446 ) -> Result<GetBlockHeaderResponse> {
1447 let verbose = verbose.unwrap_or(true);
1448 let network = self.network.clone();
1449
1450 let hash_or_height =
1451 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1452 .map_error(server::error::LegacyCode::InvalidParameter)?;
1455 let zebra_state::ReadResponse::BlockHeader {
1456 header,
1457 hash,
1458 height,
1459 next_block_hash,
1460 } = self
1461 .read_state
1462 .clone()
1463 .oneshot(zebra_state::ReadRequest::BlockHeader(hash_or_height))
1464 .await
1465 .map_err(|_| "block height not in best chain")
1466 .map_error(
1467 if hash_or_height.hash().is_some() {
1472 server::error::LegacyCode::InvalidAddressOrKey
1473 } else {
1474 server::error::LegacyCode::InvalidParameter
1475 },
1476 )?
1477 else {
1478 panic!("unexpected response to BlockHeader request")
1479 };
1480
1481 let response = if !verbose {
1482 GetBlockHeaderResponse::Raw(HexData(header.zcash_serialize_to_vec().map_misc_error()?))
1483 } else {
1484 let zebra_state::ReadResponse::SaplingTree(sapling_tree) = self
1485 .read_state
1486 .clone()
1487 .oneshot(zebra_state::ReadRequest::SaplingTree(hash_or_height))
1488 .await
1489 .map_misc_error()?
1490 else {
1491 panic!("unexpected response to SaplingTree request")
1492 };
1493
1494 let sapling_tree = sapling_tree.ok_or_misc_error("missing Sapling tree")?;
1496
1497 let zebra_state::ReadResponse::Depth(depth) = self
1498 .read_state
1499 .clone()
1500 .oneshot(zebra_state::ReadRequest::Depth(hash))
1501 .await
1502 .map_misc_error()?
1503 else {
1504 panic!("unexpected response to SaplingTree request")
1505 };
1506
1507 const NOT_IN_BEST_CHAIN_CONFIRMATIONS: i64 = -1;
1510
1511 let confirmations = depth
1514 .map(|depth| i64::from(depth) + 1)
1515 .unwrap_or(NOT_IN_BEST_CHAIN_CONFIRMATIONS);
1516
1517 let mut nonce = *header.nonce;
1518 nonce.reverse();
1519
1520 let sapling_activation = NetworkUpgrade::Sapling.activation_height(&network);
1521 let sapling_tree_size = sapling_tree.count();
1522 let final_sapling_root: [u8; 32] =
1523 if sapling_activation.is_some() && height >= sapling_activation.unwrap() {
1524 let mut root: [u8; 32] = sapling_tree.root().into();
1525 root.reverse();
1526 root
1527 } else {
1528 [0; 32]
1529 };
1530
1531 let difficulty = header.difficulty_threshold.relative_to_network(&network);
1532
1533 let block_commitments = match header.commitment(&network, height).expect(
1534 "Unexpected failure while parsing the blockcommitments field in get_block_header",
1535 ) {
1536 Commitment::PreSaplingReserved(bytes) => bytes,
1537 Commitment::FinalSaplingRoot(_) => final_sapling_root,
1538 Commitment::ChainHistoryActivationReserved => [0; 32],
1539 Commitment::ChainHistoryRoot(root) => root.bytes_in_display_order(),
1540 Commitment::ChainHistoryBlockTxAuthCommitment(hash) => {
1541 hash.bytes_in_display_order()
1542 }
1543 };
1544
1545 let block_header = BlockHeaderObject {
1546 hash,
1547 confirmations,
1548 height,
1549 version: header.version,
1550 merkle_root: header.merkle_root,
1551 block_commitments,
1552 final_sapling_root,
1553 sapling_tree_size,
1554 time: header.time.timestamp(),
1555 nonce,
1556 solution: header.solution,
1557 bits: header.difficulty_threshold,
1558 difficulty,
1559 previous_block_hash: header.previous_block_hash,
1560 next_block_hash,
1561 };
1562
1563 GetBlockHeaderResponse::Object(Box::new(block_header))
1564 };
1565
1566 Ok(response)
1567 }
1568
1569 fn get_best_block_hash(&self) -> Result<GetBlockHashResponse> {
1570 self.latest_chain_tip
1571 .best_tip_hash()
1572 .map(GetBlockHashResponse)
1573 .ok_or_misc_error("No blocks in state")
1574 }
1575
1576 fn get_best_block_height_and_hash(&self) -> Result<GetBlockHeightAndHashResponse> {
1577 self.latest_chain_tip
1578 .best_tip_height_and_hash()
1579 .map(|(height, hash)| GetBlockHeightAndHashResponse { height, hash })
1580 .ok_or_misc_error("No blocks in state")
1581 }
1582
1583 async fn get_raw_mempool(&self, verbose: Option<bool>) -> Result<GetRawMempoolResponse> {
1584 #[allow(unused)]
1585 let verbose = verbose.unwrap_or(false);
1586
1587 use zebra_chain::block::MAX_BLOCK_BYTES;
1588
1589 let mut mempool = self.mempool.clone();
1590
1591 let request = if verbose {
1592 mempool::Request::FullTransactions
1593 } else {
1594 mempool::Request::TransactionIds
1595 };
1596
1597 let response = mempool
1599 .ready()
1600 .and_then(|service| service.call(request))
1601 .await
1602 .map_misc_error()?;
1603
1604 match response {
1605 mempool::Response::FullTransactions {
1606 mut transactions,
1607 transaction_dependencies,
1608 last_seen_tip_hash: _,
1609 } => {
1610 if verbose {
1611 let map = transactions
1612 .iter()
1613 .map(|unmined_tx| {
1614 (
1615 unmined_tx.transaction.id.mined_id().encode_hex(),
1616 get_raw_mempool::MempoolObject::from_verified_unmined_tx(
1617 unmined_tx,
1618 &transactions,
1619 &transaction_dependencies,
1620 ),
1621 )
1622 })
1623 .collect::<HashMap<_, _>>();
1624 Ok(GetRawMempoolResponse::Verbose(map))
1625 } else {
1626 transactions.sort_by_cached_key(|tx| {
1631 cmp::Reverse((
1634 i64::from(tx.miner_fee) as u128 * MAX_BLOCK_BYTES as u128
1635 / tx.transaction.size as u128,
1636 tx.transaction.id.mined_id(),
1638 ))
1639 });
1640 let tx_ids: Vec<String> = transactions
1641 .iter()
1642 .map(|unmined_tx| unmined_tx.transaction.id.mined_id().encode_hex())
1643 .collect();
1644
1645 Ok(GetRawMempoolResponse::TxIds(tx_ids))
1646 }
1647 }
1648
1649 mempool::Response::TransactionIds(unmined_transaction_ids) => {
1650 let mut tx_ids: Vec<String> = unmined_transaction_ids
1651 .iter()
1652 .map(|id| id.mined_id().encode_hex())
1653 .collect();
1654
1655 tx_ids.sort();
1657
1658 Ok(GetRawMempoolResponse::TxIds(tx_ids))
1659 }
1660
1661 _ => unreachable!("unmatched response to a transactionids request"),
1662 }
1663 }
1664
1665 async fn get_raw_transaction(
1666 &self,
1667 txid: String,
1668 verbose: Option<u8>,
1669 block_hash: Option<String>,
1670 ) -> Result<GetRawTransactionResponse> {
1671 let mut mempool = self.mempool.clone();
1672 let verbose = verbose.unwrap_or(0) != 0;
1673
1674 let txid = transaction::Hash::from_hex(txid)
1677 .map_error(server::error::LegacyCode::InvalidAddressOrKey)?;
1678
1679 if block_hash.is_none() {
1681 match mempool
1682 .ready()
1683 .and_then(|service| {
1684 service.call(mempool::Request::TransactionsByMinedId([txid].into()))
1685 })
1686 .await
1687 .map_misc_error()?
1688 {
1689 mempool::Response::Transactions(txns) => {
1690 if let Some(tx) = txns.first() {
1691 return Ok(if verbose {
1692 GetRawTransactionResponse::Object(Box::new(
1693 TransactionObject::from_transaction(
1694 tx.transaction.clone(),
1695 None,
1696 None,
1697 &self.network,
1698 None,
1699 None,
1700 Some(false),
1701 txid,
1702 ),
1703 ))
1704 } else {
1705 let hex = tx.transaction.clone().into();
1706 GetRawTransactionResponse::Raw(hex)
1707 });
1708 }
1709 }
1710
1711 _ => unreachable!("unmatched response to a `TransactionsByMinedId` request"),
1712 };
1713 }
1714
1715 let txid = if let Some(block_hash) = block_hash {
1717 let block_hash = block::Hash::from_hex(block_hash)
1718 .map_error(server::error::LegacyCode::InvalidAddressOrKey)?;
1719 match self
1720 .read_state
1721 .clone()
1722 .oneshot(zebra_state::ReadRequest::TransactionIdsForBlock(
1723 block_hash.into(),
1724 ))
1725 .await
1726 .map_misc_error()?
1727 {
1728 zebra_state::ReadResponse::TransactionIdsForBlock(tx_ids) => *tx_ids
1729 .ok_or_error(
1730 server::error::LegacyCode::InvalidAddressOrKey,
1731 "block not found",
1732 )?
1733 .iter()
1734 .find(|id| **id == txid)
1735 .ok_or_error(
1736 server::error::LegacyCode::InvalidAddressOrKey,
1737 "txid not found",
1738 )?,
1739 _ => unreachable!("unmatched response to a `TransactionsByMinedId` request"),
1740 }
1741 } else {
1742 txid
1743 };
1744
1745 match self
1747 .read_state
1748 .clone()
1749 .oneshot(zebra_state::ReadRequest::Transaction(txid))
1750 .await
1751 .map_misc_error()?
1752 {
1753 zebra_state::ReadResponse::Transaction(Some(tx)) => Ok(if verbose {
1754 let block_hash = match self
1755 .read_state
1756 .clone()
1757 .oneshot(zebra_state::ReadRequest::BestChainBlockHash(tx.height))
1758 .await
1759 .map_misc_error()?
1760 {
1761 zebra_state::ReadResponse::BlockHash(block_hash) => block_hash,
1762 _ => unreachable!("unmatched response to a `TransactionsByMinedId` request"),
1763 };
1764
1765 GetRawTransactionResponse::Object(Box::new(TransactionObject::from_transaction(
1766 tx.tx.clone(),
1767 Some(tx.height),
1768 Some(tx.confirmations),
1769 &self.network,
1770 Some(tx.block_time),
1773 block_hash,
1774 Some(true),
1775 txid,
1776 )))
1777 } else {
1778 let hex = tx.tx.into();
1779 GetRawTransactionResponse::Raw(hex)
1780 }),
1781
1782 zebra_state::ReadResponse::Transaction(None) => {
1783 Err("No such mempool or main chain transaction")
1784 .map_error(server::error::LegacyCode::InvalidAddressOrKey)
1785 }
1786
1787 _ => unreachable!("unmatched response to a `Transaction` read request"),
1788 }
1789 }
1790
1791 async fn z_get_treestate(&self, hash_or_height: String) -> Result<GetTreestateResponse> {
1795 let mut read_state = self.read_state.clone();
1796 let network = self.network.clone();
1797
1798 let hash_or_height =
1799 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1800 .map_error(server::error::LegacyCode::InvalidParameter)?;
1803
1804 let block = match read_state
1813 .ready()
1814 .and_then(|service| service.call(zebra_state::ReadRequest::Block(hash_or_height)))
1815 .await
1816 .map_misc_error()?
1817 {
1818 zebra_state::ReadResponse::Block(Some(block)) => block,
1819 zebra_state::ReadResponse::Block(None) => {
1820 return Err("the requested block is not in the main chain")
1823 .map_error(server::error::LegacyCode::InvalidParameter);
1824 }
1825 _ => unreachable!("unmatched response to a block request"),
1826 };
1827
1828 let hash = hash_or_height
1829 .hash_or_else(|_| Some(block.hash()))
1830 .expect("block hash");
1831
1832 let height = hash_or_height
1833 .height_or_else(|_| block.coinbase_height())
1834 .expect("verified blocks have a coinbase height");
1835
1836 let time = u32::try_from(block.header.time.timestamp())
1837 .expect("Timestamps of valid blocks always fit into u32.");
1838
1839 let sapling_nu = zcash_primitives::consensus::NetworkUpgrade::Sapling;
1840 let sapling = if network.is_nu_active(sapling_nu, height.into()) {
1841 match read_state
1842 .ready()
1843 .and_then(|service| {
1844 service.call(zebra_state::ReadRequest::SaplingTree(hash.into()))
1845 })
1846 .await
1847 .map_misc_error()?
1848 {
1849 zebra_state::ReadResponse::SaplingTree(tree) => tree.map(|t| t.to_rpc_bytes()),
1850 _ => unreachable!("unmatched response to a Sapling tree request"),
1851 }
1852 } else {
1853 None
1854 };
1855
1856 let orchard_nu = zcash_primitives::consensus::NetworkUpgrade::Nu5;
1857 let orchard = if network.is_nu_active(orchard_nu, height.into()) {
1858 match read_state
1859 .ready()
1860 .and_then(|service| {
1861 service.call(zebra_state::ReadRequest::OrchardTree(hash.into()))
1862 })
1863 .await
1864 .map_misc_error()?
1865 {
1866 zebra_state::ReadResponse::OrchardTree(tree) => tree.map(|t| t.to_rpc_bytes()),
1867 _ => unreachable!("unmatched response to an Orchard tree request"),
1868 }
1869 } else {
1870 None
1871 };
1872
1873 Ok(GetTreestateResponse::from_parts(
1874 hash, height, time, sapling, orchard,
1875 ))
1876 }
1877
1878 async fn z_get_subtrees_by_index(
1879 &self,
1880 pool: String,
1881 start_index: NoteCommitmentSubtreeIndex,
1882 limit: Option<NoteCommitmentSubtreeIndex>,
1883 ) -> Result<GetSubtreesByIndexResponse> {
1884 let mut read_state = self.read_state.clone();
1885
1886 const POOL_LIST: &[&str] = &["sapling", "orchard"];
1887
1888 if pool == "sapling" {
1889 let request = zebra_state::ReadRequest::SaplingSubtrees { start_index, limit };
1890 let response = read_state
1891 .ready()
1892 .and_then(|service| service.call(request))
1893 .await
1894 .map_misc_error()?;
1895
1896 let subtrees = match response {
1897 zebra_state::ReadResponse::SaplingSubtrees(subtrees) => subtrees,
1898 _ => unreachable!("unmatched response to a subtrees request"),
1899 };
1900
1901 let subtrees = subtrees
1902 .values()
1903 .map(|subtree| SubtreeRpcData {
1904 root: subtree.root.encode_hex(),
1905 end_height: subtree.end_height,
1906 })
1907 .collect();
1908
1909 Ok(GetSubtreesByIndexResponse {
1910 pool,
1911 start_index,
1912 subtrees,
1913 })
1914 } else if pool == "orchard" {
1915 let request = zebra_state::ReadRequest::OrchardSubtrees { start_index, limit };
1916 let response = read_state
1917 .ready()
1918 .and_then(|service| service.call(request))
1919 .await
1920 .map_misc_error()?;
1921
1922 let subtrees = match response {
1923 zebra_state::ReadResponse::OrchardSubtrees(subtrees) => subtrees,
1924 _ => unreachable!("unmatched response to a subtrees request"),
1925 };
1926
1927 let subtrees = subtrees
1928 .values()
1929 .map(|subtree| SubtreeRpcData {
1930 root: subtree.root.encode_hex(),
1931 end_height: subtree.end_height,
1932 })
1933 .collect();
1934
1935 Ok(GetSubtreesByIndexResponse {
1936 pool,
1937 start_index,
1938 subtrees,
1939 })
1940 } else {
1941 Err(ErrorObject::owned(
1942 server::error::LegacyCode::Misc.into(),
1943 format!("invalid pool name, must be one of: {POOL_LIST:?}").as_str(),
1944 None::<()>,
1945 ))
1946 }
1947 }
1948
1949 async fn get_address_tx_ids(&self, request: GetAddressTxIdsRequest) -> Result<Vec<String>> {
1950 let mut read_state = self.read_state.clone();
1951 let latest_chain_tip = self.latest_chain_tip.clone();
1952
1953 let height_range = build_height_range(
1954 request.start,
1955 request.end,
1956 best_chain_tip_height(&latest_chain_tip)?,
1957 )?;
1958
1959 let valid_addresses = AddressStrings {
1960 addresses: request.addresses,
1961 }
1962 .valid_addresses()?;
1963
1964 let request = zebra_state::ReadRequest::TransactionIdsByAddresses {
1965 addresses: valid_addresses,
1966 height_range,
1967 };
1968 let response = read_state
1969 .ready()
1970 .and_then(|service| service.call(request))
1971 .await
1972 .map_misc_error()?;
1973
1974 let hashes = match response {
1975 zebra_state::ReadResponse::AddressesTransactionIds(hashes) => {
1976 let mut last_tx_location = TransactionLocation::from_usize(Height(0), 0);
1977
1978 hashes
1979 .iter()
1980 .map(|(tx_loc, tx_id)| {
1981 assert!(
1983 *tx_loc > last_tx_location,
1984 "Transactions were not in chain order:\n\
1985 {tx_loc:?} {tx_id:?} was after:\n\
1986 {last_tx_location:?}",
1987 );
1988
1989 last_tx_location = *tx_loc;
1990
1991 tx_id.to_string()
1992 })
1993 .collect()
1994 }
1995 _ => unreachable!("unmatched response to a TransactionsByAddresses request"),
1996 };
1997
1998 Ok(hashes)
1999 }
2000
2001 async fn get_address_utxos(
2002 &self,
2003 address_strings: AddressStrings,
2004 ) -> Result<GetAddressUtxosResponse> {
2005 let mut read_state = self.read_state.clone();
2006 let mut response_utxos = vec![];
2007
2008 let valid_addresses = address_strings.valid_addresses()?;
2009
2010 let request = zebra_state::ReadRequest::UtxosByAddresses(valid_addresses);
2012 let response = read_state
2013 .ready()
2014 .and_then(|service| service.call(request))
2015 .await
2016 .map_misc_error()?;
2017 let utxos = match response {
2018 zebra_state::ReadResponse::AddressUtxos(utxos) => utxos,
2019 _ => unreachable!("unmatched response to a UtxosByAddresses request"),
2020 };
2021
2022 let mut last_output_location = OutputLocation::from_usize(Height(0), 0, 0);
2023
2024 for utxo_data in utxos.utxos() {
2025 let address = utxo_data.0;
2026 let txid = *utxo_data.1;
2027 let height = utxo_data.2.height();
2028 let output_index = utxo_data.2.output_index();
2029 let script = utxo_data.3.lock_script.clone();
2030 let satoshis = u64::from(utxo_data.3.value);
2031
2032 let output_location = *utxo_data.2;
2033 assert!(
2035 output_location > last_output_location,
2036 "UTXOs were not in chain order:\n\
2037 {output_location:?} {address:?} {txid:?} was after:\n\
2038 {last_output_location:?}",
2039 );
2040
2041 let entry = Utxo {
2042 address,
2043 txid,
2044 output_index,
2045 script,
2046 satoshis,
2047 height,
2048 };
2049 response_utxos.push(entry);
2050
2051 last_output_location = output_location;
2052 }
2053
2054 Ok(response_utxos)
2055 }
2056
2057 fn stop(&self) -> Result<String> {
2058 #[cfg(not(target_os = "windows"))]
2059 if self.network.is_regtest() {
2060 match nix::sys::signal::raise(nix::sys::signal::SIGINT) {
2061 Ok(_) => Ok("Zebra server stopping".to_string()),
2062 Err(error) => Err(ErrorObject::owned(
2063 ErrorCode::InternalError.code(),
2064 format!("Failed to shut down: {error}").as_str(),
2065 None::<()>,
2066 )),
2067 }
2068 } else {
2069 Err(ErrorObject::borrowed(
2070 ErrorCode::MethodNotFound.code(),
2071 "stop is only available on regtest networks",
2072 None,
2073 ))
2074 }
2075 #[cfg(target_os = "windows")]
2076 Err(ErrorObject::borrowed(
2077 ErrorCode::MethodNotFound.code(),
2078 "stop is not available in windows targets",
2079 None,
2080 ))
2081 }
2082
2083 fn get_block_count(&self) -> Result<u32> {
2084 best_chain_tip_height(&self.latest_chain_tip).map(|height| height.0)
2085 }
2086
2087 async fn get_block_hash(&self, index: i32) -> Result<GetBlockHashResponse> {
2088 let mut read_state = self.read_state.clone();
2089 let latest_chain_tip = self.latest_chain_tip.clone();
2090
2091 let tip_height = best_chain_tip_height(&latest_chain_tip)?;
2093
2094 let height = height_from_signed_int(index, tip_height)?;
2095
2096 let request = zebra_state::ReadRequest::BestChainBlockHash(height);
2097 let response = read_state
2098 .ready()
2099 .and_then(|service| service.call(request))
2100 .await
2101 .map_error(server::error::LegacyCode::default())?;
2102
2103 match response {
2104 zebra_state::ReadResponse::BlockHash(Some(hash)) => Ok(GetBlockHashResponse(hash)),
2105 zebra_state::ReadResponse::BlockHash(None) => Err(ErrorObject::borrowed(
2106 server::error::LegacyCode::InvalidParameter.into(),
2107 "Block not found",
2108 None,
2109 )),
2110 _ => unreachable!("unmatched response to a block request"),
2111 }
2112 }
2113
2114 async fn get_block_template(
2115 &self,
2116 parameters: Option<GetBlockTemplateParameters>,
2117 ) -> Result<GetBlockTemplateResponse> {
2118 use types::get_block_template::{
2119 check_parameters, check_synced_to_tip, fetch_mempool_transactions,
2120 fetch_state_tip_and_local_time, validate_block_proposal,
2121 zip317::select_mempool_transactions,
2122 };
2123
2124 let network = self.network.clone();
2126 let extra_coinbase_data = self.gbt.extra_coinbase_data();
2127
2128 let mempool = self.mempool.clone();
2130 let mut latest_chain_tip = self.latest_chain_tip.clone();
2131 let sync_status = self.gbt.sync_status();
2132 let read_state = self.read_state.clone();
2133
2134 if let Some(HexData(block_proposal_bytes)) = parameters
2135 .as_ref()
2136 .and_then(GetBlockTemplateParameters::block_proposal_data)
2137 {
2138 return validate_block_proposal(
2139 self.gbt.block_verifier_router(),
2140 block_proposal_bytes,
2141 network,
2142 latest_chain_tip,
2143 sync_status,
2144 )
2145 .await;
2146 }
2147
2148 check_parameters(¶meters)?;
2150
2151 let client_long_poll_id = parameters.as_ref().and_then(|params| params.long_poll_id);
2152
2153 let miner_address = self
2154 .gbt
2155 .miner_address()
2156 .ok_or_misc_error("miner_address not configured")?;
2157
2158 let mut max_time_reached = false;
2162
2163 let (
2166 server_long_poll_id,
2167 chain_tip_and_local_time,
2168 mempool_txs,
2169 mempool_tx_deps,
2170 submit_old,
2171 ) = loop {
2172 check_synced_to_tip(&network, latest_chain_tip.clone(), sync_status.clone())?;
2178 latest_chain_tip.mark_best_tip_seen();
2186
2187 let chain_tip_and_local_time @ zebra_state::GetBlockTemplateChainInfo {
2194 tip_hash,
2195 tip_height,
2196 max_time,
2197 cur_time,
2198 ..
2199 } = fetch_state_tip_and_local_time(read_state.clone()).await?;
2200
2201 let Some((mempool_txs, mempool_tx_deps)) =
2212 fetch_mempool_transactions(mempool.clone(), tip_hash)
2213 .await?
2214 .or_else(|| client_long_poll_id.is_none().then(Default::default))
2218 else {
2219 continue;
2220 };
2221
2222 let server_long_poll_id = LongPollInput::new(
2224 tip_height,
2225 tip_hash,
2226 max_time,
2227 mempool_txs.iter().map(|tx| tx.transaction.id),
2228 )
2229 .generate_id();
2230
2231 if Some(&server_long_poll_id) != client_long_poll_id.as_ref() || max_time_reached {
2236 let mut submit_old = client_long_poll_id
2237 .as_ref()
2238 .map(|old_long_poll_id| server_long_poll_id.submit_old(old_long_poll_id));
2239
2240 if max_time_reached {
2245 submit_old = Some(false);
2246 }
2247
2248 break (
2249 server_long_poll_id,
2250 chain_tip_and_local_time,
2251 mempool_txs,
2252 mempool_tx_deps,
2253 submit_old,
2254 );
2255 }
2256
2257 let wait_for_mempool_request =
2267 tokio::time::sleep(Duration::from_secs(MEMPOOL_LONG_POLL_INTERVAL));
2268
2269 let mut wait_for_best_tip_change = latest_chain_tip.clone();
2272 let wait_for_best_tip_change = wait_for_best_tip_change.best_tip_changed();
2273
2274 let duration_until_max_time = max_time.saturating_duration_since(cur_time);
2286 let wait_for_max_time: OptionFuture<_> = if duration_until_max_time.seconds() > 0 {
2287 Some(tokio::time::sleep(duration_until_max_time.to_std()))
2288 } else {
2289 None
2290 }
2291 .into();
2292
2293 tokio::select! {
2300 biased;
2303
2304 _elapsed = wait_for_mempool_request => {
2306 tracing::debug!(
2307 ?max_time,
2308 ?cur_time,
2309 ?server_long_poll_id,
2310 ?client_long_poll_id,
2311 MEMPOOL_LONG_POLL_INTERVAL,
2312 "checking for a new mempool change after waiting a few seconds"
2313 );
2314 }
2315
2316 tip_changed_result = wait_for_best_tip_change => {
2318 match tip_changed_result {
2319 Ok(()) => {
2320 latest_chain_tip.mark_best_tip_seen();
2324
2325 let new_tip_hash = latest_chain_tip.best_tip_hash();
2326 if new_tip_hash == Some(tip_hash) {
2327 tracing::debug!(
2328 ?max_time,
2329 ?cur_time,
2330 ?server_long_poll_id,
2331 ?client_long_poll_id,
2332 ?tip_hash,
2333 ?tip_height,
2334 "ignoring spurious state change notification"
2335 );
2336
2337 tokio::time::sleep(Duration::from_secs(
2339 MEMPOOL_LONG_POLL_INTERVAL,
2340 )).await;
2341
2342 continue;
2343 }
2344
2345 tracing::debug!(
2346 ?max_time,
2347 ?cur_time,
2348 ?server_long_poll_id,
2349 ?client_long_poll_id,
2350 "returning from long poll because state has changed"
2351 );
2352 }
2353
2354 Err(recv_error) => {
2355 tracing::info!(
2357 ?recv_error,
2358 ?max_time,
2359 ?cur_time,
2360 ?server_long_poll_id,
2361 ?client_long_poll_id,
2362 "returning from long poll due to a state error.\
2363 Is Zebra shutting down?"
2364 );
2365
2366 return Err(recv_error).map_error(server::error::LegacyCode::default());
2367 }
2368 }
2369 }
2370
2371 Some(_elapsed) = wait_for_max_time => {
2374 tracing::info!(
2376 ?max_time,
2377 ?cur_time,
2378 ?server_long_poll_id,
2379 ?client_long_poll_id,
2380 "returning from long poll because max time was reached"
2381 );
2382
2383 max_time_reached = true;
2384 }
2385 }
2386 };
2387
2388 let next_block_height =
2396 (chain_tip_and_local_time.tip_height + 1).expect("tip is far below Height::MAX");
2397
2398 tracing::debug!(
2399 mempool_tx_hashes = ?mempool_txs
2400 .iter()
2401 .map(|tx| tx.transaction.id.mined_id())
2402 .collect::<Vec<_>>(),
2403 "selecting transactions for the template from the mempool"
2404 );
2405
2406 let mempool_txs = select_mempool_transactions(
2408 &network,
2409 next_block_height,
2410 &miner_address,
2411 mempool_txs,
2412 mempool_tx_deps,
2413 extra_coinbase_data.clone(),
2414 );
2415
2416 tracing::debug!(
2417 selected_mempool_tx_hashes = ?mempool_txs
2418 .iter()
2419 .map(|#[cfg(not(test))] tx, #[cfg(test)] (_, tx)| tx.transaction.id.mined_id())
2420 .collect::<Vec<_>>(),
2421 "selected transactions for the template from the mempool"
2422 );
2423
2424 let response = BlockTemplateResponse::new_internal(
2427 &network,
2428 &miner_address,
2429 &chain_tip_and_local_time,
2430 server_long_poll_id,
2431 mempool_txs,
2432 submit_old,
2433 extra_coinbase_data,
2434 );
2435
2436 Ok(response.into())
2437 }
2438
2439 async fn submit_block(
2440 &self,
2441 HexData(block_bytes): HexData,
2442 _parameters: Option<SubmitBlockParameters>,
2443 ) -> Result<SubmitBlockResponse> {
2444 let mut block_verifier_router = self.gbt.block_verifier_router();
2445
2446 let block: Block = match block_bytes.zcash_deserialize_into() {
2447 Ok(block_bytes) => block_bytes,
2448 Err(error) => {
2449 tracing::info!(
2450 ?error,
2451 "submit block failed: block bytes could not be deserialized into a structurally valid block"
2452 );
2453
2454 return Ok(SubmitBlockErrorResponse::Rejected.into());
2455 }
2456 };
2457
2458 let height = block
2459 .coinbase_height()
2460 .ok_or_error(0, "coinbase height not found")?;
2461 let block_hash = block.hash();
2462
2463 let block_verifier_router_response = block_verifier_router
2464 .ready()
2465 .await
2466 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?
2467 .call(zebra_consensus::Request::Commit(Arc::new(block)))
2468 .await;
2469
2470 let chain_error = match block_verifier_router_response {
2471 Ok(hash) => {
2478 tracing::info!(?hash, ?height, "submit block accepted");
2479
2480 self.gbt
2481 .advertise_mined_block(hash, height)
2482 .map_error_with_prefix(0, "failed to send mined block")?;
2483
2484 return Ok(SubmitBlockResponse::Accepted);
2485 }
2486
2487 Err(box_error) => {
2490 let error = box_error
2491 .downcast::<RouterError>()
2492 .map(|boxed_chain_error| *boxed_chain_error);
2493
2494 tracing::info!(
2495 ?error,
2496 ?block_hash,
2497 ?height,
2498 "submit block failed verification"
2499 );
2500
2501 error
2502 }
2503 };
2504
2505 let response = match chain_error {
2506 Ok(source) if source.is_duplicate_request() => SubmitBlockErrorResponse::Duplicate,
2507
2508 Ok(_verify_chain_error) => SubmitBlockErrorResponse::Rejected,
2524
2525 Err(_unknown_error_type) => SubmitBlockErrorResponse::Rejected,
2528 };
2529
2530 Ok(response.into())
2531 }
2532
2533 async fn get_mining_info(&self) -> Result<GetMiningInfoResponse> {
2534 let network = self.network.clone();
2535 let mut read_state = self.read_state.clone();
2536
2537 let chain_tip = self.latest_chain_tip.clone();
2538 let tip_height = chain_tip.best_tip_height().unwrap_or(Height(0)).0;
2539
2540 let mut current_block_tx = None;
2541 if tip_height > 0 {
2542 let mined_tx_ids = chain_tip.best_tip_mined_transaction_ids();
2543 current_block_tx =
2544 (!mined_tx_ids.is_empty()).then(|| mined_tx_ids.len().saturating_sub(1));
2545 }
2546
2547 let solution_rate_fut = self.get_network_sol_ps(None, None);
2548 let mut current_block_size = None;
2550 if tip_height > 0 {
2551 let request = zebra_state::ReadRequest::TipBlockSize;
2552 let response: zebra_state::ReadResponse = read_state
2553 .ready()
2554 .and_then(|service| service.call(request))
2555 .await
2556 .map_error(server::error::LegacyCode::default())?;
2557 current_block_size = match response {
2558 zebra_state::ReadResponse::TipBlockSize(Some(block_size)) => Some(block_size),
2559 _ => None,
2560 };
2561 }
2562
2563 Ok(GetMiningInfoResponse::new_internal(
2564 tip_height,
2565 current_block_size,
2566 current_block_tx,
2567 network,
2568 solution_rate_fut.await?,
2569 ))
2570 }
2571
2572 async fn get_network_sol_ps(
2573 &self,
2574 num_blocks: Option<i32>,
2575 height: Option<i32>,
2576 ) -> Result<u64> {
2577 let mut num_blocks = num_blocks.unwrap_or(DEFAULT_SOLUTION_RATE_WINDOW_SIZE);
2579 if num_blocks < 1 {
2581 num_blocks = i32::try_from(POW_AVERAGING_WINDOW).expect("fits in i32");
2582 }
2583 let num_blocks =
2584 usize::try_from(num_blocks).expect("just checked for negatives, i32 fits in usize");
2585
2586 let height = height.and_then(|height| height.try_into_height().ok());
2589
2590 let mut read_state = self.read_state.clone();
2591
2592 let request = ReadRequest::SolutionRate { num_blocks, height };
2593
2594 let response = read_state
2595 .ready()
2596 .and_then(|service| service.call(request))
2597 .await
2598 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
2599
2600 let solution_rate = match response {
2601 ReadResponse::SolutionRate(solution_rate) => solution_rate.unwrap_or(0),
2603
2604 _ => unreachable!("unmatched response to a solution rate request"),
2605 };
2606
2607 Ok(solution_rate
2608 .try_into()
2609 .expect("per-second solution rate always fits in u64"))
2610 }
2611
2612 async fn get_peer_info(&self) -> Result<Vec<PeerInfo>> {
2613 let address_book = self.address_book.clone();
2614 Ok(address_book
2615 .recently_live_peers(chrono::Utc::now())
2616 .into_iter()
2617 .map(PeerInfo::from)
2618 .collect())
2619 }
2620
2621 async fn validate_address(&self, raw_address: String) -> Result<ValidateAddressResponse> {
2622 let network = self.network.clone();
2623
2624 validate_address(network, raw_address)
2625 }
2626
2627 async fn z_validate_address(&self, raw_address: String) -> Result<ZValidateAddressResponse> {
2628 let network = self.network.clone();
2629
2630 let Ok(address) = raw_address.parse::<zcash_address::ZcashAddress>() else {
2631 return Ok(ZValidateAddressResponse::invalid());
2632 };
2633
2634 let address = match address.convert::<primitives::Address>() {
2635 Ok(address) => address,
2636 Err(err) => {
2637 tracing::debug!(?err, "conversion error");
2638 return Ok(ZValidateAddressResponse::invalid());
2639 }
2640 };
2641
2642 if address.network() == network.kind() {
2643 Ok(ZValidateAddressResponse {
2644 is_valid: true,
2645 address: Some(raw_address),
2646 address_type: Some(ZValidateAddressType::from(&address)),
2647 is_mine: Some(false),
2648 })
2649 } else {
2650 tracing::info!(
2651 ?network,
2652 address_network = ?address.network(),
2653 "invalid address network in z_validateaddress RPC: address is for {:?} but Zebra is on {:?}",
2654 address.network(),
2655 network
2656 );
2657
2658 Ok(ZValidateAddressResponse::invalid())
2659 }
2660 }
2661
2662 async fn get_block_subsidy(&self, height: Option<u32>) -> Result<GetBlockSubsidyResponse> {
2663 let latest_chain_tip = self.latest_chain_tip.clone();
2664 let network = self.network.clone();
2665
2666 let height = if let Some(height) = height {
2667 Height(height)
2668 } else {
2669 best_chain_tip_height(&latest_chain_tip)?
2670 };
2671
2672 if height < network.height_for_first_halving() {
2673 return Err(ErrorObject::borrowed(
2674 0,
2675 "Zebra does not support founders' reward subsidies, \
2676 use a block height that is after the first halving",
2677 None,
2678 ));
2679 }
2680
2681 let founders = Amount::zero();
2683
2684 let total_block_subsidy =
2685 block_subsidy(height, &network).map_error(server::error::LegacyCode::default())?;
2686 let miner_subsidy = miner_subsidy(height, &network, total_block_subsidy)
2687 .map_error(server::error::LegacyCode::default())?;
2688
2689 let (lockbox_streams, mut funding_streams): (Vec<_>, Vec<_>) =
2690 funding_stream_values(height, &network, total_block_subsidy)
2691 .map_error(server::error::LegacyCode::default())?
2692 .into_iter()
2693 .partition(|(receiver, _)| matches!(receiver, FundingStreamReceiver::Deferred));
2695
2696 let is_nu6 = NetworkUpgrade::current(&network, height) == NetworkUpgrade::Nu6;
2697
2698 let [lockbox_total, funding_streams_total]: [std::result::Result<
2699 Amount<NonNegative>,
2700 amount::Error,
2701 >; 2] = [&lockbox_streams, &funding_streams]
2702 .map(|streams| streams.iter().map(|&(_, amount)| amount).sum());
2703
2704 funding_streams.sort_by_key(|(receiver, _funding_stream)| {
2706 ZCASHD_FUNDING_STREAM_ORDER
2707 .iter()
2708 .position(|zcashd_receiver| zcashd_receiver == receiver)
2709 });
2710
2711 let [funding_streams, lockbox_streams]: [Vec<_>; 2] = [funding_streams, lockbox_streams]
2713 .map(|streams| {
2714 streams
2715 .into_iter()
2716 .map(|(receiver, value)| {
2717 let address = funding_stream_address(height, &network, receiver);
2718 types::subsidy::FundingStream::new_internal(
2719 is_nu6, receiver, value, address,
2720 )
2721 })
2722 .collect()
2723 });
2724
2725 Ok(GetBlockSubsidyResponse {
2726 miner: miner_subsidy.into(),
2727 founders: founders.into(),
2728 funding_streams,
2729 lockbox_streams,
2730 funding_streams_total: funding_streams_total
2731 .map_error(server::error::LegacyCode::default())?
2732 .into(),
2733 lockbox_total: lockbox_total
2734 .map_error(server::error::LegacyCode::default())?
2735 .into(),
2736 total_block_subsidy: total_block_subsidy.into(),
2737 })
2738 }
2739
2740 async fn get_difficulty(&self) -> Result<f64> {
2741 chain_tip_difficulty(self.network.clone(), self.read_state.clone(), false).await
2742 }
2743
2744 async fn z_list_unified_receivers(
2745 &self,
2746 address: String,
2747 ) -> Result<ZListUnifiedReceiversResponse> {
2748 use zcash_address::unified::Container;
2749
2750 let (network, unified_address): (
2751 zcash_protocol::consensus::NetworkType,
2752 zcash_address::unified::Address,
2753 ) = zcash_address::unified::Encoding::decode(address.clone().as_str())
2754 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
2755
2756 let mut p2pkh = None;
2757 let mut p2sh = None;
2758 let mut orchard = None;
2759 let mut sapling = None;
2760
2761 for item in unified_address.items() {
2762 match item {
2763 zcash_address::unified::Receiver::Orchard(_data) => {
2764 let addr = zcash_address::unified::Address::try_from_items(vec![item])
2765 .expect("using data already decoded as valid");
2766 orchard = Some(addr.encode(&network));
2767 }
2768 zcash_address::unified::Receiver::Sapling(data) => {
2769 let addr = zebra_chain::primitives::Address::try_from_sapling(network, data)
2770 .expect("using data already decoded as valid");
2771 sapling = Some(addr.payment_address().unwrap_or_default());
2772 }
2773 zcash_address::unified::Receiver::P2pkh(data) => {
2774 let addr =
2775 zebra_chain::primitives::Address::try_from_transparent_p2pkh(network, data)
2776 .expect("using data already decoded as valid");
2777 p2pkh = Some(addr.payment_address().unwrap_or_default());
2778 }
2779 zcash_address::unified::Receiver::P2sh(data) => {
2780 let addr =
2781 zebra_chain::primitives::Address::try_from_transparent_p2sh(network, data)
2782 .expect("using data already decoded as valid");
2783 p2sh = Some(addr.payment_address().unwrap_or_default());
2784 }
2785 _ => (),
2786 }
2787 }
2788
2789 Ok(ZListUnifiedReceiversResponse::new(
2790 orchard, sapling, p2pkh, p2sh,
2791 ))
2792 }
2793
2794 async fn invalidate_block(&self, block_hash: block::Hash) -> Result<()> {
2795 self.state
2796 .clone()
2797 .oneshot(zebra_state::Request::InvalidateBlock(block_hash))
2798 .await
2799 .map(|rsp| assert_eq!(rsp, zebra_state::Response::Invalidated(block_hash)))
2800 .map_misc_error()
2801 }
2802
2803 async fn reconsider_block(&self, block_hash: block::Hash) -> Result<Vec<block::Hash>> {
2804 self.state
2805 .clone()
2806 .oneshot(zebra_state::Request::ReconsiderBlock(block_hash))
2807 .await
2808 .map(|rsp| match rsp {
2809 zebra_state::Response::Reconsidered(block_hashes) => block_hashes,
2810 _ => unreachable!("unmatched response to a reconsider block request"),
2811 })
2812 .map_misc_error()
2813 }
2814
2815 async fn generate(&self, num_blocks: u32) -> Result<Vec<Hash>> {
2816 let rpc = self.clone();
2817 let network = self.network.clone();
2818
2819 if !network.disable_pow() {
2820 return Err(ErrorObject::borrowed(
2821 0,
2822 "generate is only supported on networks where PoW is disabled",
2823 None,
2824 ));
2825 }
2826
2827 let mut block_hashes = Vec::new();
2828 for _ in 0..num_blocks {
2829 let block_template = rpc
2830 .get_block_template(None)
2831 .await
2832 .map_error(server::error::LegacyCode::default())?;
2833
2834 let GetBlockTemplateResponse::TemplateMode(block_template) = block_template else {
2835 return Err(ErrorObject::borrowed(
2836 0,
2837 "error generating block template",
2838 None,
2839 ));
2840 };
2841
2842 let proposal_block =
2843 proposal_block_from_template(&block_template, BlockTemplateTimeSource::CurTime)
2844 .map_error(server::error::LegacyCode::default())?;
2845 let hex_proposal_block = HexData(
2846 proposal_block
2847 .zcash_serialize_to_vec()
2848 .map_error(server::error::LegacyCode::default())?,
2849 );
2850
2851 let _submit = rpc
2852 .submit_block(hex_proposal_block, None)
2853 .await
2854 .map_error(server::error::LegacyCode::default())?;
2855
2856 block_hashes.push(GetBlockHashResponse(proposal_block.hash()));
2857 }
2858
2859 Ok(block_hashes)
2860 }
2861
2862 async fn add_node(
2863 &self,
2864 addr: zebra_network::PeerSocketAddr,
2865 command: AddNodeCommand,
2866 ) -> Result<()> {
2867 if self.network.is_regtest() {
2868 match command {
2869 AddNodeCommand::Add => {
2870 tracing::info!(?addr, "adding peer address to the address book");
2871 if self.address_book.clone().add_peer(addr) {
2872 Ok(())
2873 } else {
2874 return Err(ErrorObject::owned(
2875 ErrorCode::InvalidParams.code(),
2876 format!("peer address was already present in the address book: {addr}"),
2877 None::<()>,
2878 ));
2879 }
2880 }
2881 }
2882 } else {
2883 return Err(ErrorObject::owned(
2884 ErrorCode::InvalidParams.code(),
2885 "addnode command is only supported on regtest",
2886 None::<()>,
2887 ));
2888 }
2889 }
2890}
2891
2892pub fn best_chain_tip_height<Tip>(latest_chain_tip: &Tip) -> Result<Height>
2897where
2898 Tip: ChainTip + Clone + Send + Sync + 'static,
2899{
2900 latest_chain_tip
2901 .best_tip_height()
2902 .ok_or_misc_error("No blocks in state")
2903}
2904
2905#[allow(clippy::too_many_arguments)]
2909#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
2910pub struct GetInfoResponse {
2911 #[getter(rename = "raw_version")]
2913 version: u64,
2914
2915 build: String,
2917
2918 subversion: String,
2920
2921 #[serde(rename = "protocolversion")]
2923 protocol_version: u32,
2924
2925 blocks: u32,
2927
2928 connections: usize,
2930
2931 #[serde(skip_serializing_if = "Option::is_none")]
2933 proxy: Option<String>,
2934
2935 difficulty: f64,
2937
2938 testnet: bool,
2940
2941 #[serde(rename = "paytxfee")]
2943 pay_tx_fee: f64,
2944
2945 #[serde(rename = "relayfee")]
2947 relay_fee: f64,
2948
2949 errors: String,
2951
2952 #[serde(rename = "errorstimestamp")]
2954 errors_timestamp: String,
2955}
2956
2957#[deprecated(note = "Use `GetInfoResponse` instead")]
2958pub use self::GetInfoResponse as GetInfo;
2959
2960impl Default for GetInfoResponse {
2961 fn default() -> Self {
2962 GetInfoResponse {
2963 version: 0,
2964 build: "some build version".to_string(),
2965 subversion: "some subversion".to_string(),
2966 protocol_version: 0,
2967 blocks: 0,
2968 connections: 0,
2969 proxy: None,
2970 difficulty: 0.0,
2971 testnet: false,
2972 pay_tx_fee: 0.0,
2973 relay_fee: 0.0,
2974 errors: "no errors".to_string(),
2975 errors_timestamp: "no errors timestamp".to_string(),
2976 }
2977 }
2978}
2979
2980impl GetInfoResponse {
2981 #[allow(clippy::too_many_arguments)]
2983 #[deprecated(note = "Use `GetInfoResponse::new` instead")]
2984 pub fn from_parts(
2985 version: u64,
2986 build: String,
2987 subversion: String,
2988 protocol_version: u32,
2989 blocks: u32,
2990 connections: usize,
2991 proxy: Option<String>,
2992 difficulty: f64,
2993 testnet: bool,
2994 pay_tx_fee: f64,
2995 relay_fee: f64,
2996 errors: String,
2997 errors_timestamp: String,
2998 ) -> Self {
2999 Self {
3000 version,
3001 build,
3002 subversion,
3003 protocol_version,
3004 blocks,
3005 connections,
3006 proxy,
3007 difficulty,
3008 testnet,
3009 pay_tx_fee,
3010 relay_fee,
3011 errors,
3012 errors_timestamp,
3013 }
3014 }
3015
3016 pub fn into_parts(
3018 self,
3019 ) -> (
3020 u64,
3021 String,
3022 String,
3023 u32,
3024 u32,
3025 usize,
3026 Option<String>,
3027 f64,
3028 bool,
3029 f64,
3030 f64,
3031 String,
3032 String,
3033 ) {
3034 (
3035 self.version,
3036 self.build,
3037 self.subversion,
3038 self.protocol_version,
3039 self.blocks,
3040 self.connections,
3041 self.proxy,
3042 self.difficulty,
3043 self.testnet,
3044 self.pay_tx_fee,
3045 self.relay_fee,
3046 self.errors,
3047 self.errors_timestamp,
3048 )
3049 }
3050
3051 fn version_from_string(build_string: &str) -> Option<u64> {
3053 let semver_version = semver::Version::parse(build_string.strip_prefix('v')?).ok()?;
3054 let build_number = semver_version
3055 .build
3056 .as_str()
3057 .split('.')
3058 .next()
3059 .and_then(|num_str| num_str.parse::<u64>().ok())
3060 .unwrap_or_default();
3061
3062 let version_number = 1_000_000 * semver_version.major
3064 + 10_000 * semver_version.minor
3065 + 100 * semver_version.patch
3066 + build_number;
3067
3068 Some(version_number)
3069 }
3070}
3071
3072#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters)]
3076pub struct GetBlockchainInfoResponse {
3077 chain: String,
3079
3080 #[getter(copy)]
3082 blocks: Height,
3083
3084 #[getter(copy)]
3087 headers: Height,
3088
3089 difficulty: f64,
3091
3092 #[serde(rename = "verificationprogress")]
3094 verification_progress: f64,
3095
3096 #[serde(rename = "chainwork")]
3098 chain_work: u64,
3099
3100 pruned: bool,
3102
3103 size_on_disk: u64,
3105
3106 commitments: u64,
3108
3109 #[serde(rename = "bestblockhash", with = "hex")]
3111 #[getter(copy)]
3112 best_block_hash: block::Hash,
3113
3114 #[serde(rename = "estimatedheight")]
3118 #[getter(copy)]
3119 estimated_height: Height,
3120
3121 #[serde(rename = "chainSupply")]
3123 chain_supply: GetBlockchainInfoBalance,
3124
3125 #[serde(rename = "valuePools")]
3127 value_pools: [GetBlockchainInfoBalance; 5],
3128
3129 upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
3131
3132 #[getter(copy)]
3134 consensus: TipConsensusBranch,
3135}
3136
3137impl Default for GetBlockchainInfoResponse {
3138 fn default() -> Self {
3139 Self {
3140 chain: "main".to_string(),
3141 blocks: Height(1),
3142 best_block_hash: block::Hash([0; 32]),
3143 estimated_height: Height(1),
3144 chain_supply: GetBlockchainInfoBalance::chain_supply(Default::default()),
3145 value_pools: GetBlockchainInfoBalance::zero_pools(),
3146 upgrades: IndexMap::new(),
3147 consensus: TipConsensusBranch {
3148 chain_tip: ConsensusBranchIdHex(ConsensusBranchId::default()),
3149 next_block: ConsensusBranchIdHex(ConsensusBranchId::default()),
3150 },
3151 headers: Height(1),
3152 difficulty: 0.0,
3153 verification_progress: 0.0,
3154 chain_work: 0,
3155 pruned: false,
3156 size_on_disk: 0,
3157 commitments: 0,
3158 }
3159 }
3160}
3161
3162impl GetBlockchainInfoResponse {
3163 #[allow(clippy::too_many_arguments)]
3167 pub fn new(
3168 chain: String,
3169 blocks: Height,
3170 best_block_hash: block::Hash,
3171 estimated_height: Height,
3172 chain_supply: GetBlockchainInfoBalance,
3173 value_pools: [GetBlockchainInfoBalance; 5],
3174 upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
3175 consensus: TipConsensusBranch,
3176 headers: Height,
3177 difficulty: f64,
3178 verification_progress: f64,
3179 chain_work: u64,
3180 pruned: bool,
3181 size_on_disk: u64,
3182 commitments: u64,
3183 ) -> Self {
3184 Self {
3185 chain,
3186 blocks,
3187 best_block_hash,
3188 estimated_height,
3189 chain_supply,
3190 value_pools,
3191 upgrades,
3192 consensus,
3193 headers,
3194 difficulty,
3195 verification_progress,
3196 chain_work,
3197 pruned,
3198 size_on_disk,
3199 commitments,
3200 }
3201 }
3202}
3203
3204#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
3209#[serde(from = "DAddressStrings")]
3210pub struct AddressStrings {
3211 addresses: Vec<String>,
3213}
3214
3215impl From<DAddressStrings> for AddressStrings {
3216 fn from(address_strings: DAddressStrings) -> Self {
3217 match address_strings {
3218 DAddressStrings::Addresses { addresses } => AddressStrings { addresses },
3219 DAddressStrings::Address(address) => AddressStrings {
3220 addresses: vec![address],
3221 },
3222 }
3223 }
3224}
3225
3226#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize)]
3228#[serde(untagged)]
3229enum DAddressStrings {
3230 Addresses { addresses: Vec<String> },
3232 Address(String),
3234}
3235
3236pub type GetAddressBalanceRequest = AddressStrings;
3238
3239impl AddressStrings {
3240 pub fn new(addresses: Vec<String>) -> AddressStrings {
3242 AddressStrings { addresses }
3243 }
3244
3245 #[deprecated(
3247 note = "Use `AddressStrings::new` instead. Validity will be checked by the server."
3248 )]
3249 pub fn new_valid(addresses: Vec<String>) -> Result<AddressStrings> {
3250 let address_strings = Self { addresses };
3251 address_strings.clone().valid_addresses()?;
3252 Ok(address_strings)
3253 }
3254
3255 pub fn valid_addresses(self) -> Result<HashSet<Address>> {
3259 let valid_addresses: HashSet<Address> = self
3262 .addresses
3263 .into_iter()
3264 .map(|address| {
3265 address
3266 .parse()
3267 .map_error(server::error::LegacyCode::InvalidAddressOrKey)
3268 })
3269 .collect::<Result<_>>()?;
3270
3271 Ok(valid_addresses)
3272 }
3273
3274 pub fn valid_address_strings(self) -> Result<Vec<String>> {
3278 self.clone().valid_addresses()?;
3279 Ok(self.addresses)
3280 }
3281}
3282
3283#[derive(
3285 Clone,
3286 Copy,
3287 Debug,
3288 Default,
3289 Eq,
3290 PartialEq,
3291 Hash,
3292 serde::Serialize,
3293 serde::Deserialize,
3294 Getters,
3295 new,
3296)]
3297pub struct GetAddressBalanceResponse {
3298 balance: u64,
3300 pub received: u64,
3302}
3303
3304#[deprecated(note = "Use `GetAddressBalanceResponse` instead.")]
3305pub use self::GetAddressBalanceResponse as AddressBalance;
3306
3307#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
3309pub struct ConsensusBranchIdHex(#[serde(with = "hex")] ConsensusBranchId);
3310
3311impl ConsensusBranchIdHex {
3312 pub fn new(consensus_branch_id: u32) -> Self {
3314 ConsensusBranchIdHex(consensus_branch_id.into())
3315 }
3316
3317 pub fn inner(&self) -> u32 {
3319 self.0.into()
3320 }
3321}
3322
3323#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3325pub struct NetworkUpgradeInfo {
3326 name: NetworkUpgrade,
3330
3331 #[serde(rename = "activationheight")]
3333 activation_height: Height,
3334
3335 status: NetworkUpgradeStatus,
3337}
3338
3339impl NetworkUpgradeInfo {
3340 pub fn from_parts(
3342 name: NetworkUpgrade,
3343 activation_height: Height,
3344 status: NetworkUpgradeStatus,
3345 ) -> Self {
3346 Self {
3347 name,
3348 activation_height,
3349 status,
3350 }
3351 }
3352
3353 pub fn into_parts(self) -> (NetworkUpgrade, Height, NetworkUpgradeStatus) {
3355 (self.name, self.activation_height, self.status)
3356 }
3357}
3358
3359#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3361pub enum NetworkUpgradeStatus {
3362 #[serde(rename = "active")]
3367 Active,
3368
3369 #[serde(rename = "disabled")]
3371 Disabled,
3372
3373 #[serde(rename = "pending")]
3375 Pending,
3376}
3377
3378#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3382pub struct TipConsensusBranch {
3383 #[serde(rename = "chaintip")]
3385 chain_tip: ConsensusBranchIdHex,
3386
3387 #[serde(rename = "nextblock")]
3389 next_block: ConsensusBranchIdHex,
3390}
3391
3392impl TipConsensusBranch {
3393 pub fn from_parts(chain_tip: u32, next_block: u32) -> Self {
3395 Self {
3396 chain_tip: ConsensusBranchIdHex::new(chain_tip),
3397 next_block: ConsensusBranchIdHex::new(next_block),
3398 }
3399 }
3400
3401 pub fn into_parts(self) -> (u32, u32) {
3403 (self.chain_tip.inner(), self.next_block.inner())
3404 }
3405}
3406
3407#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3413pub struct SendRawTransactionResponse(#[serde(with = "hex")] transaction::Hash);
3414
3415#[deprecated(note = "Use `SendRawTransactionResponse` instead")]
3416pub use self::SendRawTransactionResponse as SentTransactionHash;
3417
3418impl Default for SendRawTransactionResponse {
3419 fn default() -> Self {
3420 Self(transaction::Hash::from([0; 32]))
3421 }
3422}
3423
3424impl SendRawTransactionResponse {
3425 pub fn new(hash: transaction::Hash) -> Self {
3427 SendRawTransactionResponse(hash)
3428 }
3429
3430 #[deprecated(note = "Use `SentTransactionHash::hash` instead")]
3432 pub fn inner(&self) -> transaction::Hash {
3433 self.hash()
3434 }
3435
3436 pub fn hash(&self) -> transaction::Hash {
3438 self.0
3439 }
3440}
3441
3442#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3446#[serde(untagged)]
3447pub enum GetBlockResponse {
3448 Raw(#[serde(with = "hex")] SerializedBlock),
3450 Object(Box<BlockObject>),
3452}
3453
3454#[deprecated(note = "Use `GetBlockResponse` instead")]
3455pub use self::GetBlockResponse as GetBlock;
3456
3457impl Default for GetBlockResponse {
3458 fn default() -> Self {
3459 GetBlockResponse::Object(Box::new(BlockObject {
3460 hash: block::Hash([0; 32]),
3461 confirmations: 0,
3462 height: None,
3463 time: None,
3464 tx: Vec::new(),
3465 trees: GetBlockTrees::default(),
3466 size: None,
3467 version: None,
3468 merkle_root: None,
3469 block_commitments: None,
3470 final_sapling_root: None,
3471 final_orchard_root: None,
3472 nonce: None,
3473 bits: None,
3474 difficulty: None,
3475 chain_supply: None,
3476 value_pools: None,
3477 previous_block_hash: None,
3478 next_block_hash: None,
3479 solution: None,
3480 }))
3481 }
3482}
3483
3484#[allow(clippy::too_many_arguments)]
3486#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3487pub struct BlockObject {
3488 #[getter(copy)]
3490 #[serde(with = "hex")]
3491 hash: block::Hash,
3492
3493 confirmations: i64,
3496
3497 #[serde(skip_serializing_if = "Option::is_none")]
3499 #[getter(copy)]
3500 size: Option<i64>,
3501
3502 #[serde(skip_serializing_if = "Option::is_none")]
3504 #[getter(copy)]
3505 height: Option<Height>,
3506
3507 #[serde(skip_serializing_if = "Option::is_none")]
3509 #[getter(copy)]
3510 version: Option<u32>,
3511
3512 #[serde(with = "opthex", rename = "merkleroot")]
3514 #[serde(skip_serializing_if = "Option::is_none")]
3515 #[getter(copy)]
3516 merkle_root: Option<block::merkle::Root>,
3517
3518 #[serde(with = "opthex", rename = "blockcommitments")]
3521 #[serde(skip_serializing_if = "Option::is_none")]
3522 #[getter(copy)]
3523 block_commitments: Option<[u8; 32]>,
3524
3525 #[serde(with = "opthex", rename = "finalsaplingroot")]
3529 #[serde(skip_serializing_if = "Option::is_none")]
3530 #[getter(copy)]
3531 final_sapling_root: Option<[u8; 32]>,
3532
3533 #[serde(with = "opthex", rename = "finalorchardroot")]
3535 #[serde(skip_serializing_if = "Option::is_none")]
3536 #[getter(copy)]
3537 final_orchard_root: Option<[u8; 32]>,
3538
3539 tx: Vec<GetBlockTransaction>,
3544
3545 #[serde(skip_serializing_if = "Option::is_none")]
3547 #[getter(copy)]
3548 time: Option<i64>,
3549
3550 #[serde(with = "opthex")]
3552 #[serde(skip_serializing_if = "Option::is_none")]
3553 #[getter(copy)]
3554 nonce: Option<[u8; 32]>,
3555
3556 #[serde(with = "opthex")]
3559 #[serde(skip_serializing_if = "Option::is_none")]
3560 #[getter(copy)]
3561 solution: Option<Solution>,
3562
3563 #[serde(with = "opthex")]
3565 #[serde(skip_serializing_if = "Option::is_none")]
3566 #[getter(copy)]
3567 bits: Option<CompactDifficulty>,
3568
3569 #[serde(skip_serializing_if = "Option::is_none")]
3572 #[getter(copy)]
3573 difficulty: Option<f64>,
3574
3575 #[serde(rename = "chainSupply")]
3580 #[serde(skip_serializing_if = "Option::is_none")]
3581 chain_supply: Option<GetBlockchainInfoBalance>,
3582
3583 #[serde(rename = "valuePools")]
3585 #[serde(skip_serializing_if = "Option::is_none")]
3586 value_pools: Option<[GetBlockchainInfoBalance; 5]>,
3587
3588 #[getter(copy)]
3590 trees: GetBlockTrees,
3591
3592 #[serde(rename = "previousblockhash", skip_serializing_if = "Option::is_none")]
3594 #[serde(with = "opthex")]
3595 #[getter(copy)]
3596 previous_block_hash: Option<block::Hash>,
3597
3598 #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
3600 #[serde(with = "opthex")]
3601 #[getter(copy)]
3602 next_block_hash: Option<block::Hash>,
3603}
3604
3605#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3606#[serde(untagged)]
3607pub enum GetBlockTransaction {
3610 Hash(#[serde(with = "hex")] transaction::Hash),
3612 Object(Box<TransactionObject>),
3614}
3615
3616#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3620#[serde(untagged)]
3621pub enum GetBlockHeaderResponse {
3622 Raw(hex_data::HexData),
3624
3625 Object(Box<BlockHeaderObject>),
3627}
3628
3629#[deprecated(note = "Use `GetBlockHeaderResponse` instead")]
3630pub use self::GetBlockHeaderResponse as GetBlockHeader;
3631
3632#[allow(clippy::too_many_arguments)]
3633#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3634pub struct BlockHeaderObject {
3638 #[serde(with = "hex")]
3640 #[getter(copy)]
3641 hash: block::Hash,
3642
3643 confirmations: i64,
3646
3647 #[getter(copy)]
3649 height: Height,
3650
3651 version: u32,
3653
3654 #[serde(with = "hex", rename = "merkleroot")]
3656 #[getter(copy)]
3657 merkle_root: block::merkle::Root,
3658
3659 #[serde(with = "hex", rename = "blockcommitments")]
3662 #[getter(copy)]
3663 block_commitments: [u8; 32],
3664
3665 #[serde(with = "hex", rename = "finalsaplingroot")]
3667 #[getter(copy)]
3668 final_sapling_root: [u8; 32],
3669
3670 #[serde(skip)]
3673 sapling_tree_size: u64,
3674
3675 time: i64,
3677
3678 #[serde(with = "hex")]
3680 #[getter(copy)]
3681 nonce: [u8; 32],
3682
3683 #[serde(with = "hex")]
3685 #[getter(copy)]
3686 solution: Solution,
3687
3688 #[serde(with = "hex")]
3690 #[getter(copy)]
3691 bits: CompactDifficulty,
3692
3693 difficulty: f64,
3696
3697 #[serde(rename = "previousblockhash")]
3699 #[serde(with = "hex")]
3700 #[getter(copy)]
3701 previous_block_hash: block::Hash,
3702
3703 #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
3705 #[getter(copy)]
3706 #[serde(with = "opthex")]
3707 next_block_hash: Option<block::Hash>,
3708}
3709
3710#[deprecated(note = "Use `BlockHeaderObject` instead")]
3711pub use BlockHeaderObject as GetBlockHeaderObject;
3712
3713impl Default for GetBlockHeaderResponse {
3714 fn default() -> Self {
3715 GetBlockHeaderResponse::Object(Box::default())
3716 }
3717}
3718
3719impl Default for BlockHeaderObject {
3720 fn default() -> Self {
3721 let difficulty: ExpandedDifficulty = zebra_chain::work::difficulty::U256::one().into();
3722
3723 BlockHeaderObject {
3724 hash: block::Hash([0; 32]),
3725 confirmations: 0,
3726 height: Height::MIN,
3727 version: 4,
3728 merkle_root: block::merkle::Root([0; 32]),
3729 block_commitments: Default::default(),
3730 final_sapling_root: Default::default(),
3731 sapling_tree_size: Default::default(),
3732 time: 0,
3733 nonce: [0; 32],
3734 solution: Solution::for_proposal(),
3735 bits: difficulty.to_compact(),
3736 difficulty: 1.0,
3737 previous_block_hash: block::Hash([0; 32]),
3738 next_block_hash: Some(block::Hash([0; 32])),
3739 }
3740 }
3741}
3742
3743#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3749#[serde(transparent)]
3750pub struct GetBlockHashResponse(#[serde(with = "hex")] pub(crate) block::Hash);
3751
3752impl GetBlockHashResponse {
3753 pub fn new(hash: block::Hash) -> Self {
3755 GetBlockHashResponse(hash)
3756 }
3757
3758 pub fn hash(&self) -> block::Hash {
3760 self.0
3761 }
3762}
3763
3764#[deprecated(note = "Use `GetBlockHashResponse` instead")]
3765pub use self::GetBlockHashResponse as GetBlockHash;
3766
3767pub type Hash = GetBlockHashResponse;
3769
3770#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, Getters, new)]
3772pub struct GetBlockHeightAndHashResponse {
3773 #[getter(copy)]
3775 height: block::Height,
3776 #[getter(copy)]
3778 hash: block::Hash,
3779}
3780
3781#[deprecated(note = "Use `GetBlockHeightAndHashResponse` instead.")]
3782pub use GetBlockHeightAndHashResponse as GetBestBlockHeightAndHash;
3783
3784impl Default for GetBlockHeightAndHashResponse {
3785 fn default() -> Self {
3786 Self {
3787 height: block::Height::MIN,
3788 hash: block::Hash([0; 32]),
3789 }
3790 }
3791}
3792
3793impl Default for GetBlockHashResponse {
3794 fn default() -> Self {
3795 GetBlockHashResponse(block::Hash([0; 32]))
3796 }
3797}
3798
3799#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3803#[serde(untagged)]
3804pub enum GetRawTransactionResponse {
3805 Raw(#[serde(with = "hex")] SerializedTransaction),
3807 Object(Box<TransactionObject>),
3809}
3810
3811#[deprecated(note = "Use `GetRawTransactionResponse` instead")]
3812pub use self::GetRawTransactionResponse as GetRawTransaction;
3813
3814impl Default for GetRawTransactionResponse {
3815 fn default() -> Self {
3816 Self::Object(Box::default())
3817 }
3818}
3819
3820pub type GetAddressUtxosResponse = Vec<Utxo>;
3822
3823#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3827pub struct Utxo {
3828 address: transparent::Address,
3830
3831 #[serde(with = "hex")]
3833 #[getter(copy)]
3834 txid: transaction::Hash,
3835
3836 #[serde(rename = "outputIndex")]
3838 #[getter(copy)]
3839 output_index: OutputIndex,
3840
3841 #[serde(with = "hex")]
3843 script: transparent::Script,
3844
3845 satoshis: u64,
3847
3848 #[getter(copy)]
3852 height: Height,
3853}
3854
3855#[deprecated(note = "Use `Utxo` instead")]
3856pub use self::Utxo as GetAddressUtxos;
3857
3858impl Default for Utxo {
3859 fn default() -> Self {
3860 Self {
3861 address: transparent::Address::from_pub_key_hash(
3862 zebra_chain::parameters::NetworkKind::default(),
3863 [0u8; 20],
3864 ),
3865 txid: transaction::Hash::from([0; 32]),
3866 output_index: OutputIndex::from_u64(0),
3867 script: transparent::Script::new(&[0u8; 10]),
3868 satoshis: u64::default(),
3869 height: Height(0),
3870 }
3871 }
3872}
3873
3874impl Utxo {
3875 #[deprecated(note = "Use `Utxo::new` instead")]
3877 pub fn from_parts(
3878 address: transparent::Address,
3879 txid: transaction::Hash,
3880 output_index: OutputIndex,
3881 script: transparent::Script,
3882 satoshis: u64,
3883 height: Height,
3884 ) -> Self {
3885 Utxo {
3886 address,
3887 txid,
3888 output_index,
3889 script,
3890 satoshis,
3891 height,
3892 }
3893 }
3894
3895 pub fn into_parts(
3897 &self,
3898 ) -> (
3899 transparent::Address,
3900 transaction::Hash,
3901 OutputIndex,
3902 transparent::Script,
3903 u64,
3904 Height,
3905 ) {
3906 (
3907 self.address.clone(),
3908 self.txid,
3909 self.output_index,
3910 self.script.clone(),
3911 self.satoshis,
3912 self.height,
3913 )
3914 }
3915}
3916
3917#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, Getters, new)]
3921pub struct GetAddressTxIdsRequest {
3922 addresses: Vec<String>,
3924 start: Option<u32>,
3926 end: Option<u32>,
3928}
3929
3930impl GetAddressTxIdsRequest {
3931 #[deprecated(note = "Use `GetAddressTxIdsRequest::new` instead.")]
3933 pub fn from_parts(addresses: Vec<String>, start: u32, end: u32) -> Self {
3934 GetAddressTxIdsRequest {
3935 addresses,
3936 start: Some(start),
3937 end: Some(end),
3938 }
3939 }
3940
3941 pub fn into_parts(&self) -> (Vec<String>, u32, u32) {
3943 (
3944 self.addresses.clone(),
3945 self.start.unwrap_or(0),
3946 self.end.unwrap_or(0),
3947 )
3948 }
3949}
3950
3951#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3953pub struct GetBlockTrees {
3954 #[serde(skip_serializing_if = "SaplingTrees::is_empty")]
3955 sapling: SaplingTrees,
3956 #[serde(skip_serializing_if = "OrchardTrees::is_empty")]
3957 orchard: OrchardTrees,
3958}
3959
3960impl Default for GetBlockTrees {
3961 fn default() -> Self {
3962 GetBlockTrees {
3963 sapling: SaplingTrees { size: 0 },
3964 orchard: OrchardTrees { size: 0 },
3965 }
3966 }
3967}
3968
3969impl GetBlockTrees {
3970 pub fn new(sapling: u64, orchard: u64) -> Self {
3972 GetBlockTrees {
3973 sapling: SaplingTrees { size: sapling },
3974 orchard: OrchardTrees { size: orchard },
3975 }
3976 }
3977
3978 pub fn sapling(self) -> u64 {
3980 self.sapling.size
3981 }
3982
3983 pub fn orchard(self) -> u64 {
3985 self.orchard.size
3986 }
3987}
3988
3989#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3991pub struct SaplingTrees {
3992 size: u64,
3993}
3994
3995impl SaplingTrees {
3996 fn is_empty(&self) -> bool {
3997 self.size == 0
3998 }
3999}
4000
4001#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
4003pub struct OrchardTrees {
4004 size: u64,
4005}
4006
4007impl OrchardTrees {
4008 fn is_empty(&self) -> bool {
4009 self.size == 0
4010 }
4011}
4012
4013fn build_height_range(
4029 start: Option<u32>,
4030 end: Option<u32>,
4031 chain_height: Height,
4032) -> Result<RangeInclusive<Height>> {
4033 let start = Height(start.unwrap_or(0)).min(chain_height);
4036
4037 let end = match end {
4039 Some(0) | None => chain_height,
4040 Some(val) => Height(val).min(chain_height),
4041 };
4042
4043 if start > end {
4044 return Err(ErrorObject::owned(
4045 ErrorCode::InvalidParams.code(),
4046 format!("start {start:?} must be less than or equal to end {end:?}"),
4047 None::<()>,
4048 ));
4049 }
4050
4051 Ok(start..=end)
4052}
4053
4054#[allow(dead_code)]
4062pub fn height_from_signed_int(index: i32, tip_height: Height) -> Result<Height> {
4063 if index >= 0 {
4064 let height = index.try_into().expect("Positive i32 always fits in u32");
4065 if height > tip_height.0 {
4066 return Err(ErrorObject::borrowed(
4067 ErrorCode::InvalidParams.code(),
4068 "Provided index is greater than the current tip",
4069 None,
4070 ));
4071 }
4072 Ok(Height(height))
4073 } else {
4074 let height = i32::try_from(tip_height.0)
4076 .expect("tip height fits in i32, because Height::MAX fits in i32")
4077 .checked_add(index + 1);
4078
4079 let sanitized_height = match height {
4080 None => {
4081 return Err(ErrorObject::borrowed(
4082 ErrorCode::InvalidParams.code(),
4083 "Provided index is not valid",
4084 None,
4085 ));
4086 }
4087 Some(h) => {
4088 if h < 0 {
4089 return Err(ErrorObject::borrowed(
4090 ErrorCode::InvalidParams.code(),
4091 "Provided negative index ends up with a negative height",
4092 None,
4093 ));
4094 }
4095 let h: u32 = h.try_into().expect("Positive i32 always fits in u32");
4096 if h > tip_height.0 {
4097 return Err(ErrorObject::borrowed(
4098 ErrorCode::InvalidParams.code(),
4099 "Provided index is greater than the current tip",
4100 None,
4101 ));
4102 }
4103
4104 h
4105 }
4106 };
4107
4108 Ok(Height(sanitized_height))
4109 }
4110}
4111
4112pub mod opthex {
4114 use hex::{FromHex, ToHex};
4115 use serde::{de, Deserialize, Deserializer, Serializer};
4116
4117 #[allow(missing_docs)]
4118 pub fn serialize<S, T>(data: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
4119 where
4120 S: Serializer,
4121 T: ToHex,
4122 {
4123 match data {
4124 Some(data) => {
4125 let s = data.encode_hex::<String>();
4126 serializer.serialize_str(&s)
4127 }
4128 None => serializer.serialize_none(),
4129 }
4130 }
4131
4132 #[allow(missing_docs)]
4133 pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
4134 where
4135 D: Deserializer<'de>,
4136 T: FromHex,
4137 {
4138 let opt = Option::<String>::deserialize(deserializer)?;
4139 match opt {
4140 Some(s) => T::from_hex(&s)
4141 .map(Some)
4142 .map_err(|_e| de::Error::custom("failed to convert hex string")),
4143 None => Ok(None),
4144 }
4145 }
4146}
4147
4148pub mod arrayhex {
4150 use serde::{Deserializer, Serializer};
4151 use std::fmt;
4152
4153 #[allow(missing_docs)]
4154 pub fn serialize<S, const N: usize>(data: &[u8; N], serializer: S) -> Result<S::Ok, S::Error>
4155 where
4156 S: Serializer,
4157 {
4158 let hex_string = hex::encode(data);
4159 serializer.serialize_str(&hex_string)
4160 }
4161
4162 #[allow(missing_docs)]
4163 pub fn deserialize<'de, D, const N: usize>(deserializer: D) -> Result<[u8; N], D::Error>
4164 where
4165 D: Deserializer<'de>,
4166 {
4167 struct HexArrayVisitor<const N: usize>;
4168
4169 impl<const N: usize> serde::de::Visitor<'_> for HexArrayVisitor<N> {
4170 type Value = [u8; N];
4171
4172 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
4173 write!(formatter, "a hex string representing exactly {N} bytes")
4174 }
4175
4176 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
4177 where
4178 E: serde::de::Error,
4179 {
4180 let vec = hex::decode(v).map_err(E::custom)?;
4181 vec.clone().try_into().map_err(|_| {
4182 E::invalid_length(vec.len(), &format!("expected {N} bytes").as_str())
4183 })
4184 }
4185 }
4186
4187 deserializer.deserialize_str(HexArrayVisitor::<N>)
4188 }
4189}
4190
4191pub async fn chain_tip_difficulty<State>(
4193 network: Network,
4194 mut state: State,
4195 should_use_default: bool,
4196) -> Result<f64>
4197where
4198 State: Service<
4199 zebra_state::ReadRequest,
4200 Response = zebra_state::ReadResponse,
4201 Error = zebra_state::BoxError,
4202 > + Clone
4203 + Send
4204 + Sync
4205 + 'static,
4206 State::Future: Send,
4207{
4208 let request = ReadRequest::ChainInfo;
4209
4210 let response = state
4216 .ready()
4217 .and_then(|service| service.call(request))
4218 .await;
4219
4220 let response = match (should_use_default, response) {
4221 (_, Ok(res)) => res,
4222 (true, Err(_)) => {
4223 return Ok((U256::from(network.target_difficulty_limit()) >> 128).as_u128() as f64)
4224 }
4225 (false, Err(error)) => return Err(ErrorObject::owned(0, error.to_string(), None::<()>)),
4226 };
4227
4228 let chain_info = match response {
4229 ReadResponse::ChainInfo(info) => info,
4230 _ => unreachable!("unmatched response to a chain info request"),
4231 };
4232
4233 let pow_limit: U256 = network.target_difficulty_limit().into();
4256 let Some(difficulty) = chain_info.expected_difficulty.to_expanded() else {
4257 return Ok(0.0);
4258 };
4259
4260 let pow_limit = pow_limit >> 128;
4262 let difficulty = U256::from(difficulty) >> 128;
4263
4264 let pow_limit = pow_limit.as_u128() as f64;
4267 let difficulty = difficulty.as_u128() as f64;
4268
4269 Ok(pow_limit / difficulty)
4271}
4272
4273#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
4275pub enum AddNodeCommand {
4276 #[serde(rename = "add")]
4278 Add,
4279}