1use std::{
35 cmp,
36 collections::{HashMap, HashSet},
37 fmt,
38 ops::RangeInclusive,
39 sync::Arc,
40 time::Duration,
41};
42
43use chrono::Utc;
44use derive_getters::Getters;
45use derive_new::new;
46use futures::{future::OptionFuture, stream::FuturesOrdered, StreamExt, TryFutureExt};
47use hex::{FromHex, ToHex};
48use indexmap::IndexMap;
49use jsonrpsee::core::{async_trait, RpcResult as Result};
50use jsonrpsee_proc_macros::rpc;
51use jsonrpsee_types::{ErrorCode, ErrorObject};
52use tokio::{
53 sync::{broadcast, watch},
54 task::JoinHandle,
55};
56use tower::{Service, ServiceExt};
57use tracing::Instrument;
58
59use zcash_address::{unified::Encoding, TryFromAddress};
60use zcash_primitives::consensus::Parameters;
61
62use zebra_chain::{
63 amount::{self, Amount, NegativeAllowed, NonNegative},
64 block::{self, Block, Commitment, Height, SerializedBlock, TryIntoHeight},
65 chain_sync_status::ChainSyncStatus,
66 chain_tip::{ChainTip, NetworkChainTipHeightEstimator},
67 parameters::{
68 subsidy::{
69 block_subsidy, funding_stream_values, miner_subsidy, FundingStreamReceiver,
70 ParameterSubsidy,
71 },
72 ConsensusBranchId, Network, NetworkUpgrade, POW_AVERAGING_WINDOW,
73 },
74 serialization::{ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize},
75 subtree::NoteCommitmentSubtreeIndex,
76 transaction::{self, SerializedTransaction, Transaction, UnminedTx},
77 transparent::{self, Address, OutputIndex},
78 value_balance::ValueBalance,
79 work::{
80 difficulty::{CompactDifficulty, ExpandedDifficulty, ParameterDifficulty, U256},
81 equihash::Solution,
82 },
83};
84use zebra_consensus::{funding_stream_address, ParameterCheckpoint, RouterError};
85use zebra_network::{address_book_peers::AddressBookPeers, PeerSocketAddr};
86use zebra_node_services::mempool;
87use zebra_state::{HashOrHeight, OutputLocation, ReadRequest, ReadResponse, TransactionLocation};
88
89use crate::{
90 client::Treestate,
91 config,
92 methods::types::{validate_address::validate_address, z_validate_address::z_validate_address},
93 queue::Queue,
94 server::{
95 self,
96 error::{MapError, OkOrError},
97 },
98};
99
100pub(crate) mod hex_data;
101pub(crate) mod trees;
102pub(crate) mod types;
103
104use hex_data::HexData;
105use trees::{GetSubtreesByIndexResponse, GetTreestateResponse, SubtreeRpcData};
106use types::{
107 get_block_template::{
108 constants::{
109 DEFAULT_SOLUTION_RATE_WINDOW_SIZE, MEMPOOL_LONG_POLL_INTERVAL,
110 ZCASHD_FUNDING_STREAM_ORDER,
111 },
112 proposal::proposal_block_from_template,
113 BlockTemplateResponse, BlockTemplateTimeSource, GetBlockTemplateHandler,
114 GetBlockTemplateParameters, GetBlockTemplateResponse,
115 },
116 get_blockchain_info::GetBlockchainInfoBalance,
117 get_mempool_info::GetMempoolInfoResponse,
118 get_mining_info::GetMiningInfoResponse,
119 get_raw_mempool::{self, GetRawMempoolResponse},
120 long_poll::LongPollInput,
121 peer_info::PeerInfo,
122 submit_block::{SubmitBlockErrorResponse, SubmitBlockParameters, SubmitBlockResponse},
123 subsidy::GetBlockSubsidyResponse,
124 transaction::TransactionObject,
125 unified_address::ZListUnifiedReceiversResponse,
126 validate_address::ValidateAddressResponse,
127 z_validate_address::ZValidateAddressResponse,
128};
129
130#[cfg(test)]
131mod tests;
132
133#[rpc(server)]
134pub trait Rpc {
136 #[method(name = "getinfo")]
151 async fn get_info(&self) -> Result<GetInfoResponse>;
152
153 #[method(name = "getblockchaininfo")]
164 async fn get_blockchain_info(&self) -> Result<GetBlockchainInfoResponse>;
165
166 #[method(name = "getaddressbalance")]
189 async fn get_address_balance(
190 &self,
191 address_strings: GetAddressBalanceRequest,
192 ) -> Result<GetAddressBalanceResponse>;
193
194 #[method(name = "sendrawtransaction")]
211 async fn send_raw_transaction(
212 &self,
213 raw_transaction_hex: String,
214 _allow_high_fees: Option<bool>,
215 ) -> Result<SendRawTransactionResponse>;
216
217 #[method(name = "getblock")]
237 async fn get_block(
238 &self,
239 hash_or_height: String,
240 verbosity: Option<u8>,
241 ) -> Result<GetBlockResponse>;
242
243 #[method(name = "getblockheader")]
261 async fn get_block_header(
262 &self,
263 hash_or_height: String,
264 verbose: Option<bool>,
265 ) -> Result<GetBlockHeaderResponse>;
266
267 #[method(name = "getbestblockhash")]
273 fn get_best_block_hash(&self) -> Result<GetBlockHashResponse>;
274
275 #[method(name = "getbestblockheightandhash")]
281 fn get_best_block_height_and_hash(&self) -> Result<GetBlockHeightAndHashResponse>;
282
283 #[method(name = "getmempoolinfo")]
287 async fn get_mempool_info(&self) -> Result<GetMempoolInfoResponse>;
288
289 #[method(name = "getrawmempool")]
299 async fn get_raw_mempool(&self, verbose: Option<bool>) -> Result<GetRawMempoolResponse>;
300
301 #[method(name = "z_gettreestate")]
318 async fn z_get_treestate(&self, hash_or_height: String) -> Result<GetTreestateResponse>;
319
320 #[method(name = "z_getsubtreesbyindex")]
339 async fn z_get_subtrees_by_index(
340 &self,
341 pool: String,
342 start_index: NoteCommitmentSubtreeIndex,
343 limit: Option<NoteCommitmentSubtreeIndex>,
344 ) -> Result<GetSubtreesByIndexResponse>;
345
346 #[method(name = "getrawtransaction")]
358 async fn get_raw_transaction(
359 &self,
360 txid: String,
361 verbose: Option<u8>,
362 block_hash: Option<String>,
363 ) -> Result<GetRawTransactionResponse>;
364
365 #[method(name = "getaddresstxids")]
395 async fn get_address_tx_ids(&self, params: GetAddressTxIdsParams) -> Result<Vec<String>>;
396
397 #[method(name = "getaddressutxos")]
412 async fn get_address_utxos(
413 &self,
414 address_strings: AddressStrings,
415 ) -> Result<GetAddressUtxosResponse>;
416
417 #[method(name = "stop")]
428 fn stop(&self) -> Result<String>;
429
430 #[method(name = "getblockcount")]
437 fn get_block_count(&self) -> Result<u32>;
438
439 #[method(name = "getblockhash")]
455 async fn get_block_hash(&self, index: i32) -> Result<GetBlockHashResponse>;
456
457 #[method(name = "getblocktemplate")]
479 async fn get_block_template(
480 &self,
481 parameters: Option<GetBlockTemplateParameters>,
482 ) -> Result<GetBlockTemplateResponse>;
483
484 #[method(name = "submitblock")]
500 async fn submit_block(
501 &self,
502 hex_data: HexData,
503 _parameters: Option<SubmitBlockParameters>,
504 ) -> Result<SubmitBlockResponse>;
505
506 #[method(name = "getmininginfo")]
512 async fn get_mining_info(&self) -> Result<GetMiningInfoResponse>;
513
514 #[method(name = "getnetworksolps")]
525 async fn get_network_sol_ps(&self, num_blocks: Option<i32>, height: Option<i32>)
526 -> Result<u64>;
527
528 #[method(name = "getnetworkhashps")]
538 async fn get_network_hash_ps(
539 &self,
540 num_blocks: Option<i32>,
541 height: Option<i32>,
542 ) -> Result<u64> {
543 self.get_network_sol_ps(num_blocks, height).await
544 }
545
546 #[method(name = "getpeerinfo")]
552 async fn get_peer_info(&self) -> Result<Vec<PeerInfo>>;
553
554 #[method(name = "validateaddress")]
565 async fn validate_address(&self, address: String) -> Result<ValidateAddressResponse>;
566
567 #[method(name = "z_validateaddress")]
582 async fn z_validate_address(&self, address: String) -> Result<ZValidateAddressResponse>;
583
584 #[method(name = "getblocksubsidy")]
599 async fn get_block_subsidy(&self, height: Option<u32>) -> Result<GetBlockSubsidyResponse>;
600
601 #[method(name = "getdifficulty")]
607 async fn get_difficulty(&self) -> Result<f64>;
608
609 #[method(name = "z_listunifiedreceivers")]
623 async fn z_list_unified_receivers(
624 &self,
625 address: String,
626 ) -> Result<ZListUnifiedReceiversResponse>;
627
628 #[method(name = "invalidateblock")]
636 async fn invalidate_block(&self, block_hash: block::Hash) -> Result<()>;
637
638 #[method(name = "reconsiderblock")]
644 async fn reconsider_block(&self, block_hash: block::Hash) -> Result<Vec<block::Hash>>;
645
646 #[method(name = "generate")]
647 async fn generate(&self, num_blocks: u32) -> Result<Vec<GetBlockHashResponse>>;
661
662 #[method(name = "addnode")]
663 async fn add_node(&self, addr: PeerSocketAddr, command: AddNodeCommand) -> Result<()>;
678}
679
680#[derive(Clone)]
682pub struct RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
683where
684 Mempool: Service<
685 mempool::Request,
686 Response = mempool::Response,
687 Error = zebra_node_services::BoxError,
688 > + Clone
689 + Send
690 + Sync
691 + 'static,
692 Mempool::Future: Send,
693 State: Service<
694 zebra_state::Request,
695 Response = zebra_state::Response,
696 Error = zebra_state::BoxError,
697 > + Clone
698 + Send
699 + Sync
700 + 'static,
701 State::Future: Send,
702 ReadState: Service<
703 zebra_state::ReadRequest,
704 Response = zebra_state::ReadResponse,
705 Error = zebra_state::BoxError,
706 > + Clone
707 + Send
708 + Sync
709 + 'static,
710 ReadState::Future: Send,
711 Tip: ChainTip + Clone + Send + Sync + 'static,
712 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
713 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
714 + Clone
715 + Send
716 + Sync
717 + 'static,
718 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
719 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
720{
721 build_version: String,
725
726 user_agent: String,
728
729 network: Network,
731
732 debug_force_finished_sync: bool,
735
736 mempool: Mempool,
740
741 state: State,
743
744 read_state: ReadState,
746
747 latest_chain_tip: Tip,
749
750 queue_sender: broadcast::Sender<UnminedTx>,
754
755 address_book: AddressBook,
757
758 last_warn_error_log_rx: LoggedLastEvent,
760
761 gbt: GetBlockTemplateHandler<BlockVerifierRouter, SyncStatus>,
763}
764
765pub type LoggedLastEvent = watch::Receiver<Option<(String, tracing::Level, chrono::DateTime<Utc>)>>;
767
768impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus> fmt::Debug
769 for RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
770where
771 Mempool: Service<
772 mempool::Request,
773 Response = mempool::Response,
774 Error = zebra_node_services::BoxError,
775 > + Clone
776 + Send
777 + Sync
778 + 'static,
779 Mempool::Future: Send,
780 State: Service<
781 zebra_state::Request,
782 Response = zebra_state::Response,
783 Error = zebra_state::BoxError,
784 > + Clone
785 + Send
786 + Sync
787 + 'static,
788 State::Future: Send,
789 ReadState: Service<
790 zebra_state::ReadRequest,
791 Response = zebra_state::ReadResponse,
792 Error = zebra_state::BoxError,
793 > + Clone
794 + Send
795 + Sync
796 + 'static,
797 ReadState::Future: Send,
798 Tip: ChainTip + Clone + Send + Sync + 'static,
799 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
800 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
801 + Clone
802 + Send
803 + Sync
804 + 'static,
805 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
806 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
807{
808 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
809 f.debug_struct("RpcImpl")
811 .field("build_version", &self.build_version)
812 .field("user_agent", &self.user_agent)
813 .field("network", &self.network)
814 .field("debug_force_finished_sync", &self.debug_force_finished_sync)
815 .field("getblocktemplate", &self.gbt)
816 .finish()
817 }
818}
819
820impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
821 RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
822where
823 Mempool: Service<
824 mempool::Request,
825 Response = mempool::Response,
826 Error = zebra_node_services::BoxError,
827 > + Clone
828 + Send
829 + Sync
830 + 'static,
831 Mempool::Future: Send,
832 State: Service<
833 zebra_state::Request,
834 Response = zebra_state::Response,
835 Error = zebra_state::BoxError,
836 > + Clone
837 + Send
838 + Sync
839 + 'static,
840 State::Future: Send,
841 ReadState: Service<
842 zebra_state::ReadRequest,
843 Response = zebra_state::ReadResponse,
844 Error = zebra_state::BoxError,
845 > + Clone
846 + Send
847 + Sync
848 + 'static,
849 ReadState::Future: Send,
850 Tip: ChainTip + Clone + Send + Sync + 'static,
851 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
852 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
853 + Clone
854 + Send
855 + Sync
856 + 'static,
857 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
858 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
859{
860 #[allow(clippy::too_many_arguments)]
865 pub fn new<VersionString, UserAgentString>(
866 network: Network,
867 mining_config: config::mining::Config,
868 debug_force_finished_sync: bool,
869 build_version: VersionString,
870 user_agent: UserAgentString,
871 mempool: Mempool,
872 state: State,
873 read_state: ReadState,
874 block_verifier_router: BlockVerifierRouter,
875 sync_status: SyncStatus,
876 latest_chain_tip: Tip,
877 address_book: AddressBook,
878 last_warn_error_log_rx: LoggedLastEvent,
879 mined_block_sender: Option<watch::Sender<(block::Hash, block::Height)>>,
880 ) -> (Self, JoinHandle<()>)
881 where
882 VersionString: ToString + Clone + Send + 'static,
883 UserAgentString: ToString + Clone + Send + 'static,
884 {
885 let (runner, queue_sender) = Queue::start();
886
887 let mut build_version = build_version.to_string();
888 let user_agent = user_agent.to_string();
889
890 if !build_version.is_empty() && !build_version.starts_with('v') {
892 build_version.insert(0, 'v');
893 }
894
895 let gbt = GetBlockTemplateHandler::new(
896 &network,
897 mining_config.clone(),
898 block_verifier_router,
899 sync_status,
900 mined_block_sender,
901 );
902
903 let rpc_impl = RpcImpl {
904 build_version,
905 user_agent,
906 network: network.clone(),
907 debug_force_finished_sync,
908 mempool: mempool.clone(),
909 state: state.clone(),
910 read_state: read_state.clone(),
911 latest_chain_tip: latest_chain_tip.clone(),
912 queue_sender,
913 address_book,
914 last_warn_error_log_rx,
915 gbt,
916 };
917
918 let rpc_tx_queue_task_handle = tokio::spawn(
920 runner
921 .run(mempool, read_state, latest_chain_tip, network)
922 .in_current_span(),
923 );
924
925 (rpc_impl, rpc_tx_queue_task_handle)
926 }
927
928 pub fn network(&self) -> &Network {
930 &self.network
931 }
932}
933
934#[async_trait]
935impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus> RpcServer
936 for RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
937where
938 Mempool: Service<
939 mempool::Request,
940 Response = mempool::Response,
941 Error = zebra_node_services::BoxError,
942 > + Clone
943 + Send
944 + Sync
945 + 'static,
946 Mempool::Future: Send,
947 State: Service<
948 zebra_state::Request,
949 Response = zebra_state::Response,
950 Error = zebra_state::BoxError,
951 > + Clone
952 + Send
953 + Sync
954 + 'static,
955 State::Future: Send,
956 ReadState: Service<
957 zebra_state::ReadRequest,
958 Response = zebra_state::ReadResponse,
959 Error = zebra_state::BoxError,
960 > + Clone
961 + Send
962 + Sync
963 + 'static,
964 ReadState::Future: Send,
965 Tip: ChainTip + Clone + Send + Sync + 'static,
966 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
967 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
968 + Clone
969 + Send
970 + Sync
971 + 'static,
972 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
973 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
974{
975 async fn get_info(&self) -> Result<GetInfoResponse> {
976 let version = GetInfoResponse::version_from_string(&self.build_version)
977 .expect("invalid version string");
978
979 let connections = self.address_book.recently_live_peers(Utc::now()).len();
980
981 let last_error_recorded = self.last_warn_error_log_rx.borrow().clone();
982 let (last_error_log, _level, last_error_log_time) = last_error_recorded.unwrap_or((
983 GetInfoResponse::default().errors,
984 tracing::Level::INFO,
985 Utc::now(),
986 ));
987
988 let tip_height = self
989 .latest_chain_tip
990 .best_tip_height()
991 .unwrap_or(Height::MIN);
992 let testnet = self.network.is_a_test_network();
993
994 let pay_tx_fee = 0.0;
1000
1001 let relay_fee = zebra_chain::transaction::zip317::MIN_MEMPOOL_TX_FEE_RATE as f64
1002 / (zebra_chain::amount::COIN as f64);
1003 let difficulty = chain_tip_difficulty(self.network.clone(), self.read_state.clone(), true)
1004 .await
1005 .expect("should always be Ok when `should_use_default` is true");
1006
1007 let response = GetInfoResponse {
1008 version,
1009 build: self.build_version.clone(),
1010 subversion: self.user_agent.clone(),
1011 protocol_version: zebra_network::constants::CURRENT_NETWORK_PROTOCOL_VERSION.0,
1012 blocks: tip_height.0,
1013 connections,
1014 proxy: None,
1015 difficulty,
1016 testnet,
1017 pay_tx_fee,
1018 relay_fee,
1019 errors: last_error_log,
1020 errors_timestamp: last_error_log_time.to_string(),
1021 };
1022
1023 Ok(response)
1024 }
1025
1026 #[allow(clippy::unwrap_in_result)]
1027 async fn get_blockchain_info(&self) -> Result<GetBlockchainInfoResponse> {
1028 let debug_force_finished_sync = self.debug_force_finished_sync;
1029 let network = &self.network;
1030
1031 let (usage_info_rsp, tip_pool_values_rsp, chain_tip_difficulty) = {
1032 use zebra_state::ReadRequest::*;
1033 let state_call = |request| self.read_state.clone().oneshot(request);
1034 tokio::join!(
1035 state_call(UsageInfo),
1036 state_call(TipPoolValues),
1037 chain_tip_difficulty(network.clone(), self.read_state.clone(), true)
1038 )
1039 };
1040
1041 let (size_on_disk, (tip_height, tip_hash), value_balance, difficulty) = {
1042 use zebra_state::ReadResponse::*;
1043
1044 let UsageInfo(size_on_disk) = usage_info_rsp.map_misc_error()? else {
1045 unreachable!("unmatched response to a TipPoolValues request")
1046 };
1047
1048 let (tip, value_balance) = match tip_pool_values_rsp {
1049 Ok(TipPoolValues {
1050 tip_height,
1051 tip_hash,
1052 value_balance,
1053 }) => ((tip_height, tip_hash), value_balance),
1054 Ok(_) => unreachable!("unmatched response to a TipPoolValues request"),
1055 Err(_) => ((Height::MIN, network.genesis_hash()), Default::default()),
1056 };
1057
1058 let difficulty = chain_tip_difficulty
1059 .expect("should always be Ok when `should_use_default` is true");
1060
1061 (size_on_disk, tip, value_balance, difficulty)
1062 };
1063
1064 let now = Utc::now();
1065 let (estimated_height, verification_progress) = self
1066 .latest_chain_tip
1067 .best_tip_height_and_block_time()
1068 .map(|(tip_height, tip_block_time)| {
1069 let height =
1070 NetworkChainTipHeightEstimator::new(tip_block_time, tip_height, network)
1071 .estimate_height_at(now);
1072
1073 let height =
1077 if tip_block_time > now || height < tip_height || debug_force_finished_sync {
1078 tip_height
1079 } else {
1080 height
1081 };
1082
1083 (height, f64::from(tip_height.0) / f64::from(height.0))
1084 })
1085 .unwrap_or((Height::MIN, 0.0));
1087
1088 let mut upgrades = IndexMap::new();
1092 for (activation_height, network_upgrade) in network.full_activation_list() {
1093 if let Some(branch_id) = network_upgrade.branch_id() {
1098 let status = if tip_height >= activation_height {
1100 NetworkUpgradeStatus::Active
1101 } else {
1102 NetworkUpgradeStatus::Pending
1103 };
1104
1105 let upgrade = NetworkUpgradeInfo {
1106 name: network_upgrade,
1107 activation_height,
1108 status,
1109 };
1110 upgrades.insert(ConsensusBranchIdHex(branch_id), upgrade);
1111 }
1112 }
1113
1114 let next_block_height =
1116 (tip_height + 1).expect("valid chain tips are a lot less than Height::MAX");
1117 let consensus = TipConsensusBranch {
1118 chain_tip: ConsensusBranchIdHex(
1119 NetworkUpgrade::current(network, tip_height)
1120 .branch_id()
1121 .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
1122 ),
1123 next_block: ConsensusBranchIdHex(
1124 NetworkUpgrade::current(network, next_block_height)
1125 .branch_id()
1126 .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
1127 ),
1128 };
1129
1130 let response = GetBlockchainInfoResponse {
1131 chain: network.bip70_network_name(),
1132 blocks: tip_height,
1133 best_block_hash: tip_hash,
1134 estimated_height,
1135 chain_supply: GetBlockchainInfoBalance::chain_supply(value_balance),
1136 value_pools: GetBlockchainInfoBalance::value_pools(value_balance, None),
1137 upgrades,
1138 consensus,
1139 headers: tip_height,
1140 difficulty,
1141 verification_progress,
1142 chain_work: 0,
1144 pruned: false,
1145 size_on_disk,
1146 commitments: 0,
1148 };
1149
1150 Ok(response)
1151 }
1152
1153 async fn get_address_balance(
1154 &self,
1155 address_strings: GetAddressBalanceRequest,
1156 ) -> Result<GetAddressBalanceResponse> {
1157 let valid_addresses = address_strings.valid_addresses()?;
1158
1159 let request = zebra_state::ReadRequest::AddressBalance(valid_addresses);
1160 let response = self
1161 .read_state
1162 .clone()
1163 .oneshot(request)
1164 .await
1165 .map_misc_error()?;
1166
1167 match response {
1168 zebra_state::ReadResponse::AddressBalance { balance, received } => {
1169 Ok(GetAddressBalanceResponse {
1170 balance: u64::from(balance),
1171 received,
1172 })
1173 }
1174 _ => unreachable!("Unexpected response from state service: {response:?}"),
1175 }
1176 }
1177
1178 async fn send_raw_transaction(
1180 &self,
1181 raw_transaction_hex: String,
1182 _allow_high_fees: Option<bool>,
1183 ) -> Result<SendRawTransactionResponse> {
1184 let mempool = self.mempool.clone();
1185 let queue_sender = self.queue_sender.clone();
1186
1187 let raw_transaction_bytes = Vec::from_hex(raw_transaction_hex)
1190 .map_error(server::error::LegacyCode::Deserialization)?;
1191 let raw_transaction = Transaction::zcash_deserialize(&*raw_transaction_bytes)
1192 .map_error(server::error::LegacyCode::Deserialization)?;
1193
1194 let transaction_hash = raw_transaction.hash();
1195
1196 let unmined_transaction = UnminedTx::from(raw_transaction.clone());
1198 let _ = queue_sender.send(unmined_transaction);
1199
1200 let transaction_parameter = mempool::Gossip::Tx(raw_transaction.into());
1201 let request = mempool::Request::Queue(vec![transaction_parameter]);
1202
1203 let response = mempool.oneshot(request).await.map_misc_error()?;
1204
1205 let mut queue_results = match response {
1206 mempool::Response::Queued(results) => results,
1207 _ => unreachable!("incorrect response variant from mempool service"),
1208 };
1209
1210 assert_eq!(
1211 queue_results.len(),
1212 1,
1213 "mempool service returned more results than expected"
1214 );
1215
1216 let queue_result = queue_results
1217 .pop()
1218 .expect("there should be exactly one item in Vec")
1219 .inspect_err(|err| tracing::debug!("sent transaction to mempool: {:?}", &err))
1220 .map_misc_error()?
1221 .await
1222 .map_misc_error()?;
1223
1224 tracing::debug!("sent transaction to mempool: {:?}", &queue_result);
1225
1226 queue_result
1227 .map(|_| SendRawTransactionResponse(transaction_hash))
1228 .map_error(server::error::LegacyCode::Verify)
1235 }
1236
1237 async fn get_block(
1242 &self,
1243 hash_or_height: String,
1244 verbosity: Option<u8>,
1245 ) -> Result<GetBlockResponse> {
1246 let verbosity = verbosity.unwrap_or(1);
1247 let network = self.network.clone();
1248 let original_hash_or_height = hash_or_height.clone();
1249
1250 let get_block_header_future = if matches!(verbosity, 1 | 2) {
1252 Some(self.get_block_header(original_hash_or_height.clone(), Some(true)))
1253 } else {
1254 None
1255 };
1256
1257 let hash_or_height =
1258 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1259 .map_error(server::error::LegacyCode::InvalidParameter)?;
1262
1263 if verbosity == 0 {
1264 let request = zebra_state::ReadRequest::Block(hash_or_height);
1265 let response = self
1266 .read_state
1267 .clone()
1268 .oneshot(request)
1269 .await
1270 .map_misc_error()?;
1271
1272 match response {
1273 zebra_state::ReadResponse::Block(Some(block)) => {
1274 Ok(GetBlockResponse::Raw(block.into()))
1275 }
1276 zebra_state::ReadResponse::Block(None) => {
1277 Err("Block not found").map_error(server::error::LegacyCode::InvalidParameter)
1278 }
1279 _ => unreachable!("unmatched response to a block request"),
1280 }
1281 } else if let Some(get_block_header_future) = get_block_header_future {
1282 let get_block_header_result: Result<GetBlockHeaderResponse> =
1283 get_block_header_future.await;
1284
1285 let GetBlockHeaderResponse::Object(block_header) = get_block_header_result? else {
1286 panic!("must return Object")
1287 };
1288
1289 let BlockHeaderObject {
1290 hash,
1291 confirmations,
1292 height,
1293 version,
1294 merkle_root,
1295 block_commitments,
1296 final_sapling_root,
1297 sapling_tree_size,
1298 time,
1299 nonce,
1300 solution,
1301 bits,
1302 difficulty,
1303 previous_block_hash,
1304 next_block_hash,
1305 } = *block_header;
1306
1307 let transactions_request = match verbosity {
1308 1 => zebra_state::ReadRequest::TransactionIdsForBlock(hash_or_height),
1309 2 => zebra_state::ReadRequest::BlockAndSize(hash_or_height),
1310 _other => panic!("get_block_header_fut should be none"),
1311 };
1312
1313 let hash_or_height = hash.into();
1318 let requests = vec![
1319 transactions_request,
1327 zebra_state::ReadRequest::OrchardTree(hash_or_height),
1329 zebra_state::ReadRequest::BlockInfo(previous_block_hash.into()),
1331 zebra_state::ReadRequest::BlockInfo(hash_or_height),
1332 ];
1333
1334 let mut futs = FuturesOrdered::new();
1335
1336 for request in requests {
1337 futs.push_back(self.read_state.clone().oneshot(request));
1338 }
1339
1340 let tx_ids_response = futs.next().await.expect("`futs` should not be empty");
1341 let (tx, size): (Vec<_>, Option<usize>) = match tx_ids_response.map_misc_error()? {
1342 zebra_state::ReadResponse::TransactionIdsForBlock(tx_ids) => (
1343 tx_ids
1344 .ok_or_misc_error("block not found")?
1345 .iter()
1346 .map(|tx_id| GetBlockTransaction::Hash(*tx_id))
1347 .collect(),
1348 None,
1349 ),
1350 zebra_state::ReadResponse::BlockAndSize(block_and_size) => {
1351 let (block, size) = block_and_size.ok_or_misc_error("Block not found")?;
1352 let block_time = block.header.time;
1353 let transactions =
1354 block
1355 .transactions
1356 .iter()
1357 .map(|tx| {
1358 GetBlockTransaction::Object(Box::new(
1359 TransactionObject::from_transaction(
1360 tx.clone(),
1361 Some(height),
1362 Some(confirmations.try_into().expect(
1363 "should be less than max block height, i32::MAX",
1364 )),
1365 &network,
1366 Some(block_time),
1367 Some(hash),
1368 Some(true),
1369 tx.hash(),
1370 ),
1371 ))
1372 })
1373 .collect();
1374 (transactions, Some(size))
1375 }
1376 _ => unreachable!("unmatched response to a transaction_ids_for_block request"),
1377 };
1378
1379 let orchard_tree_response = futs.next().await.expect("`futs` should not be empty");
1380 let zebra_state::ReadResponse::OrchardTree(orchard_tree) =
1381 orchard_tree_response.map_misc_error()?
1382 else {
1383 unreachable!("unmatched response to a OrchardTree request");
1384 };
1385
1386 let nu5_activation = NetworkUpgrade::Nu5.activation_height(&network);
1387
1388 let orchard_tree = orchard_tree.ok_or_misc_error("missing Orchard tree")?;
1390
1391 let final_orchard_root = match nu5_activation {
1392 Some(activation_height) if height >= activation_height => {
1393 Some(orchard_tree.root().into())
1394 }
1395 _other => None,
1396 };
1397
1398 let sapling = SaplingTrees {
1399 size: sapling_tree_size,
1400 };
1401
1402 let orchard_tree_size = orchard_tree.count();
1403 let orchard = OrchardTrees {
1404 size: orchard_tree_size,
1405 };
1406
1407 let trees = GetBlockTrees { sapling, orchard };
1408
1409 let block_info_response = futs.next().await.expect("`futs` should not be empty");
1410 let zebra_state::ReadResponse::BlockInfo(prev_block_info) =
1411 block_info_response.map_misc_error()?
1412 else {
1413 unreachable!("unmatched response to a BlockInfo request");
1414 };
1415 let block_info_response = futs.next().await.expect("`futs` should not be empty");
1416 let zebra_state::ReadResponse::BlockInfo(block_info) =
1417 block_info_response.map_misc_error()?
1418 else {
1419 unreachable!("unmatched response to a BlockInfo request");
1420 };
1421
1422 let delta = block_info.as_ref().and_then(|d| {
1423 let value_pools = d.value_pools().constrain::<NegativeAllowed>().ok()?;
1424 let prev_value_pools = prev_block_info
1425 .map(|d| d.value_pools().constrain::<NegativeAllowed>())
1426 .unwrap_or(Ok(ValueBalance::<NegativeAllowed>::zero()))
1427 .ok()?;
1428 (value_pools - prev_value_pools).ok()
1429 });
1430 let size = size.or(block_info.as_ref().map(|d| d.size() as usize));
1431
1432 Ok(GetBlockResponse::Object(Box::new(BlockObject {
1433 hash,
1434 confirmations,
1435 height: Some(height),
1436 version: Some(version),
1437 merkle_root: Some(merkle_root),
1438 time: Some(time),
1439 nonce: Some(nonce),
1440 solution: Some(solution),
1441 bits: Some(bits),
1442 difficulty: Some(difficulty),
1443 tx,
1444 trees,
1445 chain_supply: block_info
1446 .as_ref()
1447 .map(|d| GetBlockchainInfoBalance::chain_supply(*d.value_pools())),
1448 value_pools: block_info
1449 .map(|d| GetBlockchainInfoBalance::value_pools(*d.value_pools(), delta)),
1450 size: size.map(|size| size as i64),
1451 block_commitments: Some(block_commitments),
1452 final_sapling_root: Some(final_sapling_root),
1453 final_orchard_root,
1454 previous_block_hash: Some(previous_block_hash),
1455 next_block_hash,
1456 })))
1457 } else {
1458 Err("invalid verbosity value").map_error(server::error::LegacyCode::InvalidParameter)
1459 }
1460 }
1461
1462 async fn get_block_header(
1463 &self,
1464 hash_or_height: String,
1465 verbose: Option<bool>,
1466 ) -> Result<GetBlockHeaderResponse> {
1467 let verbose = verbose.unwrap_or(true);
1468 let network = self.network.clone();
1469
1470 let hash_or_height =
1471 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1472 .map_error(server::error::LegacyCode::InvalidParameter)?;
1475 let zebra_state::ReadResponse::BlockHeader {
1476 header,
1477 hash,
1478 height,
1479 next_block_hash,
1480 } = self
1481 .read_state
1482 .clone()
1483 .oneshot(zebra_state::ReadRequest::BlockHeader(hash_or_height))
1484 .await
1485 .map_err(|_| "block height not in best chain")
1486 .map_error(
1487 if hash_or_height.hash().is_some() {
1492 server::error::LegacyCode::InvalidAddressOrKey
1493 } else {
1494 server::error::LegacyCode::InvalidParameter
1495 },
1496 )?
1497 else {
1498 panic!("unexpected response to BlockHeader request")
1499 };
1500
1501 let response = if !verbose {
1502 GetBlockHeaderResponse::Raw(HexData(header.zcash_serialize_to_vec().map_misc_error()?))
1503 } else {
1504 let zebra_state::ReadResponse::SaplingTree(sapling_tree) = self
1505 .read_state
1506 .clone()
1507 .oneshot(zebra_state::ReadRequest::SaplingTree(hash_or_height))
1508 .await
1509 .map_misc_error()?
1510 else {
1511 panic!("unexpected response to SaplingTree request")
1512 };
1513
1514 let sapling_tree = sapling_tree.ok_or_misc_error("missing Sapling tree")?;
1516
1517 let zebra_state::ReadResponse::Depth(depth) = self
1518 .read_state
1519 .clone()
1520 .oneshot(zebra_state::ReadRequest::Depth(hash))
1521 .await
1522 .map_misc_error()?
1523 else {
1524 panic!("unexpected response to SaplingTree request")
1525 };
1526
1527 const NOT_IN_BEST_CHAIN_CONFIRMATIONS: i64 = -1;
1530
1531 let confirmations = depth
1534 .map(|depth| i64::from(depth) + 1)
1535 .unwrap_or(NOT_IN_BEST_CHAIN_CONFIRMATIONS);
1536
1537 let mut nonce = *header.nonce;
1538 nonce.reverse();
1539
1540 let sapling_activation = NetworkUpgrade::Sapling.activation_height(&network);
1541 let sapling_tree_size = sapling_tree.count();
1542 let final_sapling_root: [u8; 32] =
1543 if sapling_activation.is_some() && height >= sapling_activation.unwrap() {
1544 let mut root: [u8; 32] = sapling_tree.root().into();
1545 root.reverse();
1546 root
1547 } else {
1548 [0; 32]
1549 };
1550
1551 let difficulty = header.difficulty_threshold.relative_to_network(&network);
1552
1553 let block_commitments = match header.commitment(&network, height).expect(
1554 "Unexpected failure while parsing the blockcommitments field in get_block_header",
1555 ) {
1556 Commitment::PreSaplingReserved(bytes) => bytes,
1557 Commitment::FinalSaplingRoot(_) => final_sapling_root,
1558 Commitment::ChainHistoryActivationReserved => [0; 32],
1559 Commitment::ChainHistoryRoot(root) => root.bytes_in_display_order(),
1560 Commitment::ChainHistoryBlockTxAuthCommitment(hash) => {
1561 hash.bytes_in_display_order()
1562 }
1563 };
1564
1565 let block_header = BlockHeaderObject {
1566 hash,
1567 confirmations,
1568 height,
1569 version: header.version,
1570 merkle_root: header.merkle_root,
1571 block_commitments,
1572 final_sapling_root,
1573 sapling_tree_size,
1574 time: header.time.timestamp(),
1575 nonce,
1576 solution: header.solution,
1577 bits: header.difficulty_threshold,
1578 difficulty,
1579 previous_block_hash: header.previous_block_hash,
1580 next_block_hash,
1581 };
1582
1583 GetBlockHeaderResponse::Object(Box::new(block_header))
1584 };
1585
1586 Ok(response)
1587 }
1588
1589 fn get_best_block_hash(&self) -> Result<GetBlockHashResponse> {
1590 self.latest_chain_tip
1591 .best_tip_hash()
1592 .map(GetBlockHashResponse)
1593 .ok_or_misc_error("No blocks in state")
1594 }
1595
1596 fn get_best_block_height_and_hash(&self) -> Result<GetBlockHeightAndHashResponse> {
1597 self.latest_chain_tip
1598 .best_tip_height_and_hash()
1599 .map(|(height, hash)| GetBlockHeightAndHashResponse { height, hash })
1600 .ok_or_misc_error("No blocks in state")
1601 }
1602
1603 async fn get_mempool_info(&self) -> Result<GetMempoolInfoResponse> {
1604 let mut mempool = self.mempool.clone();
1605
1606 let response = mempool
1607 .ready()
1608 .and_then(|service| service.call(mempool::Request::QueueStats))
1609 .await
1610 .map_misc_error()?;
1611
1612 if let mempool::Response::QueueStats {
1613 size,
1614 bytes,
1615 usage,
1616 fully_notified,
1617 } = response
1618 {
1619 Ok(GetMempoolInfoResponse {
1620 size,
1621 bytes,
1622 usage,
1623 fully_notified,
1624 })
1625 } else {
1626 unreachable!("unexpected response to QueueStats request")
1627 }
1628 }
1629
1630 async fn get_raw_mempool(&self, verbose: Option<bool>) -> Result<GetRawMempoolResponse> {
1631 #[allow(unused)]
1632 let verbose = verbose.unwrap_or(false);
1633
1634 use zebra_chain::block::MAX_BLOCK_BYTES;
1635
1636 let mut mempool = self.mempool.clone();
1637
1638 let request = if verbose {
1639 mempool::Request::FullTransactions
1640 } else {
1641 mempool::Request::TransactionIds
1642 };
1643
1644 let response = mempool
1646 .ready()
1647 .and_then(|service| service.call(request))
1648 .await
1649 .map_misc_error()?;
1650
1651 match response {
1652 mempool::Response::FullTransactions {
1653 mut transactions,
1654 transaction_dependencies,
1655 last_seen_tip_hash: _,
1656 } => {
1657 if verbose {
1658 let map = transactions
1659 .iter()
1660 .map(|unmined_tx| {
1661 (
1662 unmined_tx.transaction.id.mined_id().encode_hex(),
1663 get_raw_mempool::MempoolObject::from_verified_unmined_tx(
1664 unmined_tx,
1665 &transactions,
1666 &transaction_dependencies,
1667 ),
1668 )
1669 })
1670 .collect::<HashMap<_, _>>();
1671 Ok(GetRawMempoolResponse::Verbose(map))
1672 } else {
1673 transactions.sort_by_cached_key(|tx| {
1678 cmp::Reverse((
1681 i64::from(tx.miner_fee) as u128 * MAX_BLOCK_BYTES as u128
1682 / tx.transaction.size as u128,
1683 tx.transaction.id.mined_id(),
1685 ))
1686 });
1687 let tx_ids: Vec<String> = transactions
1688 .iter()
1689 .map(|unmined_tx| unmined_tx.transaction.id.mined_id().encode_hex())
1690 .collect();
1691
1692 Ok(GetRawMempoolResponse::TxIds(tx_ids))
1693 }
1694 }
1695
1696 mempool::Response::TransactionIds(unmined_transaction_ids) => {
1697 let mut tx_ids: Vec<String> = unmined_transaction_ids
1698 .iter()
1699 .map(|id| id.mined_id().encode_hex())
1700 .collect();
1701
1702 tx_ids.sort();
1704
1705 Ok(GetRawMempoolResponse::TxIds(tx_ids))
1706 }
1707
1708 _ => unreachable!("unmatched response to a transactionids request"),
1709 }
1710 }
1711
1712 async fn get_raw_transaction(
1713 &self,
1714 txid: String,
1715 verbose: Option<u8>,
1716 block_hash: Option<String>,
1717 ) -> Result<GetRawTransactionResponse> {
1718 let mut mempool = self.mempool.clone();
1719 let verbose = verbose.unwrap_or(0) != 0;
1720
1721 let txid = transaction::Hash::from_hex(txid)
1724 .map_error(server::error::LegacyCode::InvalidAddressOrKey)?;
1725
1726 if block_hash.is_none() {
1728 match mempool
1729 .ready()
1730 .and_then(|service| {
1731 service.call(mempool::Request::TransactionsByMinedId([txid].into()))
1732 })
1733 .await
1734 .map_misc_error()?
1735 {
1736 mempool::Response::Transactions(txns) => {
1737 if let Some(tx) = txns.first() {
1738 return Ok(if verbose {
1739 GetRawTransactionResponse::Object(Box::new(
1740 TransactionObject::from_transaction(
1741 tx.transaction.clone(),
1742 None,
1743 None,
1744 &self.network,
1745 None,
1746 None,
1747 Some(false),
1748 txid,
1749 ),
1750 ))
1751 } else {
1752 let hex = tx.transaction.clone().into();
1753 GetRawTransactionResponse::Raw(hex)
1754 });
1755 }
1756 }
1757
1758 _ => unreachable!("unmatched response to a `TransactionsByMinedId` request"),
1759 };
1760 }
1761
1762 let txid = if let Some(block_hash) = block_hash {
1764 let block_hash = block::Hash::from_hex(block_hash)
1765 .map_error(server::error::LegacyCode::InvalidAddressOrKey)?;
1766 match self
1767 .read_state
1768 .clone()
1769 .oneshot(zebra_state::ReadRequest::TransactionIdsForBlock(
1770 block_hash.into(),
1771 ))
1772 .await
1773 .map_misc_error()?
1774 {
1775 zebra_state::ReadResponse::TransactionIdsForBlock(tx_ids) => *tx_ids
1776 .ok_or_error(
1777 server::error::LegacyCode::InvalidAddressOrKey,
1778 "block not found",
1779 )?
1780 .iter()
1781 .find(|id| **id == txid)
1782 .ok_or_error(
1783 server::error::LegacyCode::InvalidAddressOrKey,
1784 "txid not found",
1785 )?,
1786 _ => unreachable!("unmatched response to a `TransactionsByMinedId` request"),
1787 }
1788 } else {
1789 txid
1790 };
1791
1792 match self
1794 .read_state
1795 .clone()
1796 .oneshot(zebra_state::ReadRequest::Transaction(txid))
1797 .await
1798 .map_misc_error()?
1799 {
1800 zebra_state::ReadResponse::Transaction(Some(tx)) => Ok(if verbose {
1801 let block_hash = match self
1802 .read_state
1803 .clone()
1804 .oneshot(zebra_state::ReadRequest::BestChainBlockHash(tx.height))
1805 .await
1806 .map_misc_error()?
1807 {
1808 zebra_state::ReadResponse::BlockHash(block_hash) => block_hash,
1809 _ => unreachable!("unmatched response to a `TransactionsByMinedId` request"),
1810 };
1811
1812 GetRawTransactionResponse::Object(Box::new(TransactionObject::from_transaction(
1813 tx.tx.clone(),
1814 Some(tx.height),
1815 Some(tx.confirmations),
1816 &self.network,
1817 Some(tx.block_time),
1820 block_hash,
1821 Some(true),
1822 txid,
1823 )))
1824 } else {
1825 let hex = tx.tx.into();
1826 GetRawTransactionResponse::Raw(hex)
1827 }),
1828
1829 zebra_state::ReadResponse::Transaction(None) => {
1830 Err("No such mempool or main chain transaction")
1831 .map_error(server::error::LegacyCode::InvalidAddressOrKey)
1832 }
1833
1834 _ => unreachable!("unmatched response to a `Transaction` read request"),
1835 }
1836 }
1837
1838 async fn z_get_treestate(&self, hash_or_height: String) -> Result<GetTreestateResponse> {
1839 let mut read_state = self.read_state.clone();
1840 let network = self.network.clone();
1841
1842 let hash_or_height =
1843 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1844 .map_error(server::error::LegacyCode::InvalidParameter)?;
1847
1848 let block = match read_state
1857 .ready()
1858 .and_then(|service| service.call(zebra_state::ReadRequest::Block(hash_or_height)))
1859 .await
1860 .map_misc_error()?
1861 {
1862 zebra_state::ReadResponse::Block(Some(block)) => block,
1863 zebra_state::ReadResponse::Block(None) => {
1864 return Err("the requested block is not in the main chain")
1867 .map_error(server::error::LegacyCode::InvalidParameter);
1868 }
1869 _ => unreachable!("unmatched response to a block request"),
1870 };
1871
1872 let hash = hash_or_height
1873 .hash_or_else(|_| Some(block.hash()))
1874 .expect("block hash");
1875
1876 let height = hash_or_height
1877 .height_or_else(|_| block.coinbase_height())
1878 .expect("verified blocks have a coinbase height");
1879
1880 let time = u32::try_from(block.header.time.timestamp())
1881 .expect("Timestamps of valid blocks always fit into u32.");
1882
1883 let sapling_nu = zcash_primitives::consensus::NetworkUpgrade::Sapling;
1884 let sapling = if network.is_nu_active(sapling_nu, height.into()) {
1885 match read_state
1886 .ready()
1887 .and_then(|service| {
1888 service.call(zebra_state::ReadRequest::SaplingTree(hash.into()))
1889 })
1890 .await
1891 .map_misc_error()?
1892 {
1893 zebra_state::ReadResponse::SaplingTree(tree) => {
1894 tree.map(|t| (t.to_rpc_bytes(), t.root().bytes_in_display_order().to_vec()))
1895 }
1896 _ => unreachable!("unmatched response to a Sapling tree request"),
1897 }
1898 } else {
1899 None
1900 };
1901 let (sapling_tree, sapling_root) =
1902 sapling.map_or((None, None), |(tree, root)| (Some(tree), Some(root)));
1903
1904 let orchard_nu = zcash_primitives::consensus::NetworkUpgrade::Nu5;
1905 let orchard = if network.is_nu_active(orchard_nu, height.into()) {
1906 match read_state
1907 .ready()
1908 .and_then(|service| {
1909 service.call(zebra_state::ReadRequest::OrchardTree(hash.into()))
1910 })
1911 .await
1912 .map_misc_error()?
1913 {
1914 zebra_state::ReadResponse::OrchardTree(tree) => {
1915 tree.map(|t| (t.to_rpc_bytes(), t.root().bytes_in_display_order().to_vec()))
1916 }
1917 _ => unreachable!("unmatched response to an Orchard tree request"),
1918 }
1919 } else {
1920 None
1921 };
1922 let (orchard_tree, orchard_root) =
1923 orchard.map_or((None, None), |(tree, root)| (Some(tree), Some(root)));
1924
1925 Ok(GetTreestateResponse::new(
1926 hash,
1927 height,
1928 time,
1929 None,
1932 Treestate::new(trees::Commitments::new(sapling_root, sapling_tree)),
1933 Treestate::new(trees::Commitments::new(orchard_root, orchard_tree)),
1934 ))
1935 }
1936
1937 async fn z_get_subtrees_by_index(
1938 &self,
1939 pool: String,
1940 start_index: NoteCommitmentSubtreeIndex,
1941 limit: Option<NoteCommitmentSubtreeIndex>,
1942 ) -> Result<GetSubtreesByIndexResponse> {
1943 let mut read_state = self.read_state.clone();
1944
1945 const POOL_LIST: &[&str] = &["sapling", "orchard"];
1946
1947 if pool == "sapling" {
1948 let request = zebra_state::ReadRequest::SaplingSubtrees { start_index, limit };
1949 let response = read_state
1950 .ready()
1951 .and_then(|service| service.call(request))
1952 .await
1953 .map_misc_error()?;
1954
1955 let subtrees = match response {
1956 zebra_state::ReadResponse::SaplingSubtrees(subtrees) => subtrees,
1957 _ => unreachable!("unmatched response to a subtrees request"),
1958 };
1959
1960 let subtrees = subtrees
1961 .values()
1962 .map(|subtree| SubtreeRpcData {
1963 root: subtree.root.encode_hex(),
1964 end_height: subtree.end_height,
1965 })
1966 .collect();
1967
1968 Ok(GetSubtreesByIndexResponse {
1969 pool,
1970 start_index,
1971 subtrees,
1972 })
1973 } else if pool == "orchard" {
1974 let request = zebra_state::ReadRequest::OrchardSubtrees { start_index, limit };
1975 let response = read_state
1976 .ready()
1977 .and_then(|service| service.call(request))
1978 .await
1979 .map_misc_error()?;
1980
1981 let subtrees = match response {
1982 zebra_state::ReadResponse::OrchardSubtrees(subtrees) => subtrees,
1983 _ => unreachable!("unmatched response to a subtrees request"),
1984 };
1985
1986 let subtrees = subtrees
1987 .values()
1988 .map(|subtree| SubtreeRpcData {
1989 root: subtree.root.encode_hex(),
1990 end_height: subtree.end_height,
1991 })
1992 .collect();
1993
1994 Ok(GetSubtreesByIndexResponse {
1995 pool,
1996 start_index,
1997 subtrees,
1998 })
1999 } else {
2000 Err(ErrorObject::owned(
2001 server::error::LegacyCode::Misc.into(),
2002 format!("invalid pool name, must be one of: {POOL_LIST:?}").as_str(),
2003 None::<()>,
2004 ))
2005 }
2006 }
2007
2008 async fn get_address_tx_ids(&self, params: GetAddressTxIdsParams) -> Result<Vec<String>> {
2009 let request = params.into_request();
2010
2011 let mut read_state = self.read_state.clone();
2012 let latest_chain_tip = self.latest_chain_tip.clone();
2013
2014 let height_range = build_height_range(
2015 request.start,
2016 request.end,
2017 best_chain_tip_height(&latest_chain_tip)?,
2018 )?;
2019
2020 let valid_addresses = AddressStrings {
2021 addresses: request.addresses,
2022 }
2023 .valid_addresses()?;
2024
2025 let request = zebra_state::ReadRequest::TransactionIdsByAddresses {
2026 addresses: valid_addresses,
2027 height_range,
2028 };
2029 let response = read_state
2030 .ready()
2031 .and_then(|service| service.call(request))
2032 .await
2033 .map_misc_error()?;
2034
2035 let hashes = match response {
2036 zebra_state::ReadResponse::AddressesTransactionIds(hashes) => {
2037 let mut last_tx_location = TransactionLocation::from_usize(Height(0), 0);
2038
2039 hashes
2040 .iter()
2041 .map(|(tx_loc, tx_id)| {
2042 assert!(
2044 *tx_loc > last_tx_location,
2045 "Transactions were not in chain order:\n\
2046 {tx_loc:?} {tx_id:?} was after:\n\
2047 {last_tx_location:?}",
2048 );
2049
2050 last_tx_location = *tx_loc;
2051
2052 tx_id.to_string()
2053 })
2054 .collect()
2055 }
2056 _ => unreachable!("unmatched response to a TransactionsByAddresses request"),
2057 };
2058
2059 Ok(hashes)
2060 }
2061
2062 async fn get_address_utxos(
2063 &self,
2064 address_strings: AddressStrings,
2065 ) -> Result<GetAddressUtxosResponse> {
2066 let mut read_state = self.read_state.clone();
2067 let mut response_utxos = vec![];
2068
2069 let valid_addresses = address_strings.valid_addresses()?;
2070
2071 let request = zebra_state::ReadRequest::UtxosByAddresses(valid_addresses);
2073 let response = read_state
2074 .ready()
2075 .and_then(|service| service.call(request))
2076 .await
2077 .map_misc_error()?;
2078 let utxos = match response {
2079 zebra_state::ReadResponse::AddressUtxos(utxos) => utxos,
2080 _ => unreachable!("unmatched response to a UtxosByAddresses request"),
2081 };
2082
2083 let mut last_output_location = OutputLocation::from_usize(Height(0), 0, 0);
2084
2085 for utxo_data in utxos.utxos() {
2086 let address = utxo_data.0;
2087 let txid = *utxo_data.1;
2088 let height = utxo_data.2.height();
2089 let output_index = utxo_data.2.output_index();
2090 let script = utxo_data.3.lock_script.clone();
2091 let satoshis = u64::from(utxo_data.3.value);
2092
2093 let output_location = *utxo_data.2;
2094 assert!(
2096 output_location > last_output_location,
2097 "UTXOs were not in chain order:\n\
2098 {output_location:?} {address:?} {txid:?} was after:\n\
2099 {last_output_location:?}",
2100 );
2101
2102 let entry = Utxo {
2103 address,
2104 txid,
2105 output_index,
2106 script,
2107 satoshis,
2108 height,
2109 };
2110 response_utxos.push(entry);
2111
2112 last_output_location = output_location;
2113 }
2114
2115 Ok(response_utxos)
2116 }
2117
2118 fn stop(&self) -> Result<String> {
2119 #[cfg(not(target_os = "windows"))]
2120 if self.network.is_regtest() {
2121 match nix::sys::signal::raise(nix::sys::signal::SIGINT) {
2122 Ok(_) => Ok("Zebra server stopping".to_string()),
2123 Err(error) => Err(ErrorObject::owned(
2124 ErrorCode::InternalError.code(),
2125 format!("Failed to shut down: {error}").as_str(),
2126 None::<()>,
2127 )),
2128 }
2129 } else {
2130 Err(ErrorObject::borrowed(
2131 ErrorCode::MethodNotFound.code(),
2132 "stop is only available on regtest networks",
2133 None,
2134 ))
2135 }
2136 #[cfg(target_os = "windows")]
2137 Err(ErrorObject::borrowed(
2138 ErrorCode::MethodNotFound.code(),
2139 "stop is not available in windows targets",
2140 None,
2141 ))
2142 }
2143
2144 fn get_block_count(&self) -> Result<u32> {
2145 best_chain_tip_height(&self.latest_chain_tip).map(|height| height.0)
2146 }
2147
2148 async fn get_block_hash(&self, index: i32) -> Result<GetBlockHashResponse> {
2149 let mut read_state = self.read_state.clone();
2150 let latest_chain_tip = self.latest_chain_tip.clone();
2151
2152 let tip_height = best_chain_tip_height(&latest_chain_tip)?;
2154
2155 let height = height_from_signed_int(index, tip_height)?;
2156
2157 let request = zebra_state::ReadRequest::BestChainBlockHash(height);
2158 let response = read_state
2159 .ready()
2160 .and_then(|service| service.call(request))
2161 .await
2162 .map_error(server::error::LegacyCode::default())?;
2163
2164 match response {
2165 zebra_state::ReadResponse::BlockHash(Some(hash)) => Ok(GetBlockHashResponse(hash)),
2166 zebra_state::ReadResponse::BlockHash(None) => Err(ErrorObject::borrowed(
2167 server::error::LegacyCode::InvalidParameter.into(),
2168 "Block not found",
2169 None,
2170 )),
2171 _ => unreachable!("unmatched response to a block request"),
2172 }
2173 }
2174
2175 async fn get_block_template(
2176 &self,
2177 parameters: Option<GetBlockTemplateParameters>,
2178 ) -> Result<GetBlockTemplateResponse> {
2179 use types::get_block_template::{
2180 check_parameters, check_synced_to_tip, fetch_mempool_transactions,
2181 fetch_state_tip_and_local_time, validate_block_proposal,
2182 zip317::select_mempool_transactions,
2183 };
2184
2185 let network = self.network.clone();
2187 let extra_coinbase_data = self.gbt.extra_coinbase_data();
2188
2189 let mempool = self.mempool.clone();
2191 let mut latest_chain_tip = self.latest_chain_tip.clone();
2192 let sync_status = self.gbt.sync_status();
2193 let read_state = self.read_state.clone();
2194
2195 if let Some(HexData(block_proposal_bytes)) = parameters
2196 .as_ref()
2197 .and_then(GetBlockTemplateParameters::block_proposal_data)
2198 {
2199 return validate_block_proposal(
2200 self.gbt.block_verifier_router(),
2201 block_proposal_bytes,
2202 network,
2203 latest_chain_tip,
2204 sync_status,
2205 )
2206 .await;
2207 }
2208
2209 check_parameters(¶meters)?;
2211
2212 let client_long_poll_id = parameters.as_ref().and_then(|params| params.long_poll_id);
2213
2214 let miner_address = self
2215 .gbt
2216 .miner_address()
2217 .ok_or_misc_error("miner_address not configured")?;
2218
2219 let mut max_time_reached = false;
2223
2224 let (
2227 server_long_poll_id,
2228 chain_tip_and_local_time,
2229 mempool_txs,
2230 mempool_tx_deps,
2231 submit_old,
2232 ) = loop {
2233 check_synced_to_tip(&network, latest_chain_tip.clone(), sync_status.clone())?;
2239 latest_chain_tip.mark_best_tip_seen();
2247
2248 let chain_tip_and_local_time @ zebra_state::GetBlockTemplateChainInfo {
2255 tip_hash,
2256 tip_height,
2257 max_time,
2258 cur_time,
2259 ..
2260 } = fetch_state_tip_and_local_time(read_state.clone()).await?;
2261
2262 let Some((mempool_txs, mempool_tx_deps)) =
2273 fetch_mempool_transactions(mempool.clone(), tip_hash)
2274 .await?
2275 .or_else(|| client_long_poll_id.is_none().then(Default::default))
2279 else {
2280 continue;
2281 };
2282
2283 let server_long_poll_id = LongPollInput::new(
2285 tip_height,
2286 tip_hash,
2287 max_time,
2288 mempool_txs.iter().map(|tx| tx.transaction.id),
2289 )
2290 .generate_id();
2291
2292 if Some(&server_long_poll_id) != client_long_poll_id.as_ref() || max_time_reached {
2297 let mut submit_old = client_long_poll_id
2298 .as_ref()
2299 .map(|old_long_poll_id| server_long_poll_id.submit_old(old_long_poll_id));
2300
2301 if max_time_reached {
2306 submit_old = Some(false);
2307 }
2308
2309 break (
2310 server_long_poll_id,
2311 chain_tip_and_local_time,
2312 mempool_txs,
2313 mempool_tx_deps,
2314 submit_old,
2315 );
2316 }
2317
2318 let wait_for_mempool_request =
2328 tokio::time::sleep(Duration::from_secs(MEMPOOL_LONG_POLL_INTERVAL));
2329
2330 let mut wait_for_best_tip_change = latest_chain_tip.clone();
2333 let wait_for_best_tip_change = wait_for_best_tip_change.best_tip_changed();
2334
2335 let duration_until_max_time = max_time.saturating_duration_since(cur_time);
2347 let wait_for_max_time: OptionFuture<_> = if duration_until_max_time.seconds() > 0 {
2348 Some(tokio::time::sleep(duration_until_max_time.to_std()))
2349 } else {
2350 None
2351 }
2352 .into();
2353
2354 tokio::select! {
2361 biased;
2364
2365 _elapsed = wait_for_mempool_request => {
2367 tracing::debug!(
2368 ?max_time,
2369 ?cur_time,
2370 ?server_long_poll_id,
2371 ?client_long_poll_id,
2372 MEMPOOL_LONG_POLL_INTERVAL,
2373 "checking for a new mempool change after waiting a few seconds"
2374 );
2375 }
2376
2377 tip_changed_result = wait_for_best_tip_change => {
2379 match tip_changed_result {
2380 Ok(()) => {
2381 latest_chain_tip.mark_best_tip_seen();
2385
2386 let new_tip_hash = latest_chain_tip.best_tip_hash();
2387 if new_tip_hash == Some(tip_hash) {
2388 tracing::debug!(
2389 ?max_time,
2390 ?cur_time,
2391 ?server_long_poll_id,
2392 ?client_long_poll_id,
2393 ?tip_hash,
2394 ?tip_height,
2395 "ignoring spurious state change notification"
2396 );
2397
2398 tokio::time::sleep(Duration::from_secs(
2400 MEMPOOL_LONG_POLL_INTERVAL,
2401 )).await;
2402
2403 continue;
2404 }
2405
2406 tracing::debug!(
2407 ?max_time,
2408 ?cur_time,
2409 ?server_long_poll_id,
2410 ?client_long_poll_id,
2411 "returning from long poll because state has changed"
2412 );
2413 }
2414
2415 Err(recv_error) => {
2416 tracing::info!(
2418 ?recv_error,
2419 ?max_time,
2420 ?cur_time,
2421 ?server_long_poll_id,
2422 ?client_long_poll_id,
2423 "returning from long poll due to a state error.\
2424 Is Zebra shutting down?"
2425 );
2426
2427 return Err(recv_error).map_error(server::error::LegacyCode::default());
2428 }
2429 }
2430 }
2431
2432 Some(_elapsed) = wait_for_max_time => {
2435 tracing::info!(
2437 ?max_time,
2438 ?cur_time,
2439 ?server_long_poll_id,
2440 ?client_long_poll_id,
2441 "returning from long poll because max time was reached"
2442 );
2443
2444 max_time_reached = true;
2445 }
2446 }
2447 };
2448
2449 let next_block_height =
2457 (chain_tip_and_local_time.tip_height + 1).expect("tip is far below Height::MAX");
2458
2459 tracing::debug!(
2460 mempool_tx_hashes = ?mempool_txs
2461 .iter()
2462 .map(|tx| tx.transaction.id.mined_id())
2463 .collect::<Vec<_>>(),
2464 "selecting transactions for the template from the mempool"
2465 );
2466
2467 let mempool_txs = select_mempool_transactions(
2469 &network,
2470 next_block_height,
2471 &miner_address,
2472 mempool_txs,
2473 mempool_tx_deps,
2474 extra_coinbase_data.clone(),
2475 );
2476
2477 tracing::debug!(
2478 selected_mempool_tx_hashes = ?mempool_txs
2479 .iter()
2480 .map(|#[cfg(not(test))] tx, #[cfg(test)] (_, tx)| tx.transaction.id.mined_id())
2481 .collect::<Vec<_>>(),
2482 "selected transactions for the template from the mempool"
2483 );
2484
2485 let response = BlockTemplateResponse::new_internal(
2488 &network,
2489 &miner_address,
2490 &chain_tip_and_local_time,
2491 server_long_poll_id,
2492 mempool_txs,
2493 submit_old,
2494 extra_coinbase_data,
2495 );
2496
2497 Ok(response.into())
2498 }
2499
2500 async fn submit_block(
2501 &self,
2502 HexData(block_bytes): HexData,
2503 _parameters: Option<SubmitBlockParameters>,
2504 ) -> Result<SubmitBlockResponse> {
2505 let mut block_verifier_router = self.gbt.block_verifier_router();
2506
2507 let block: Block = match block_bytes.zcash_deserialize_into() {
2508 Ok(block_bytes) => block_bytes,
2509 Err(error) => {
2510 tracing::info!(
2511 ?error,
2512 "submit block failed: block bytes could not be deserialized into a structurally valid block"
2513 );
2514
2515 return Ok(SubmitBlockErrorResponse::Rejected.into());
2516 }
2517 };
2518
2519 let height = block
2520 .coinbase_height()
2521 .ok_or_error(0, "coinbase height not found")?;
2522 let block_hash = block.hash();
2523
2524 let block_verifier_router_response = block_verifier_router
2525 .ready()
2526 .await
2527 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?
2528 .call(zebra_consensus::Request::Commit(Arc::new(block)))
2529 .await;
2530
2531 let chain_error = match block_verifier_router_response {
2532 Ok(hash) => {
2539 tracing::info!(?hash, ?height, "submit block accepted");
2540
2541 self.gbt
2542 .advertise_mined_block(hash, height)
2543 .map_error_with_prefix(0, "failed to send mined block")?;
2544
2545 return Ok(SubmitBlockResponse::Accepted);
2546 }
2547
2548 Err(box_error) => {
2551 let error = box_error
2552 .downcast::<RouterError>()
2553 .map(|boxed_chain_error| *boxed_chain_error);
2554
2555 tracing::info!(
2556 ?error,
2557 ?block_hash,
2558 ?height,
2559 "submit block failed verification"
2560 );
2561
2562 error
2563 }
2564 };
2565
2566 let response = match chain_error {
2567 Ok(source) if source.is_duplicate_request() => SubmitBlockErrorResponse::Duplicate,
2568
2569 Ok(_verify_chain_error) => SubmitBlockErrorResponse::Rejected,
2585
2586 Err(_unknown_error_type) => SubmitBlockErrorResponse::Rejected,
2589 };
2590
2591 Ok(response.into())
2592 }
2593
2594 async fn get_mining_info(&self) -> Result<GetMiningInfoResponse> {
2595 let network = self.network.clone();
2596 let mut read_state = self.read_state.clone();
2597
2598 let chain_tip = self.latest_chain_tip.clone();
2599 let tip_height = chain_tip.best_tip_height().unwrap_or(Height(0)).0;
2600
2601 let mut current_block_tx = None;
2602 if tip_height > 0 {
2603 let mined_tx_ids = chain_tip.best_tip_mined_transaction_ids();
2604 current_block_tx =
2605 (!mined_tx_ids.is_empty()).then(|| mined_tx_ids.len().saturating_sub(1));
2606 }
2607
2608 let solution_rate_fut = self.get_network_sol_ps(None, None);
2609 let mut current_block_size = None;
2611 if tip_height > 0 {
2612 let request = zebra_state::ReadRequest::TipBlockSize;
2613 let response: zebra_state::ReadResponse = read_state
2614 .ready()
2615 .and_then(|service| service.call(request))
2616 .await
2617 .map_error(server::error::LegacyCode::default())?;
2618 current_block_size = match response {
2619 zebra_state::ReadResponse::TipBlockSize(Some(block_size)) => Some(block_size),
2620 _ => None,
2621 };
2622 }
2623
2624 Ok(GetMiningInfoResponse::new_internal(
2625 tip_height,
2626 current_block_size,
2627 current_block_tx,
2628 network,
2629 solution_rate_fut.await?,
2630 ))
2631 }
2632
2633 async fn get_network_sol_ps(
2634 &self,
2635 num_blocks: Option<i32>,
2636 height: Option<i32>,
2637 ) -> Result<u64> {
2638 let mut num_blocks = num_blocks.unwrap_or(DEFAULT_SOLUTION_RATE_WINDOW_SIZE);
2640 if num_blocks < 1 {
2642 num_blocks = i32::try_from(POW_AVERAGING_WINDOW).expect("fits in i32");
2643 }
2644 let num_blocks =
2645 usize::try_from(num_blocks).expect("just checked for negatives, i32 fits in usize");
2646
2647 let height = height.and_then(|height| height.try_into_height().ok());
2650
2651 let mut read_state = self.read_state.clone();
2652
2653 let request = ReadRequest::SolutionRate { num_blocks, height };
2654
2655 let response = read_state
2656 .ready()
2657 .and_then(|service| service.call(request))
2658 .await
2659 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
2660
2661 let solution_rate = match response {
2662 ReadResponse::SolutionRate(solution_rate) => solution_rate.unwrap_or(0),
2664
2665 _ => unreachable!("unmatched response to a solution rate request"),
2666 };
2667
2668 Ok(solution_rate
2669 .try_into()
2670 .expect("per-second solution rate always fits in u64"))
2671 }
2672
2673 async fn get_peer_info(&self) -> Result<Vec<PeerInfo>> {
2674 let address_book = self.address_book.clone();
2675 Ok(address_book
2676 .recently_live_peers(chrono::Utc::now())
2677 .into_iter()
2678 .map(PeerInfo::from)
2679 .collect())
2680 }
2681
2682 async fn validate_address(&self, raw_address: String) -> Result<ValidateAddressResponse> {
2683 let network = self.network.clone();
2684
2685 validate_address(network, raw_address)
2686 }
2687
2688 async fn z_validate_address(&self, raw_address: String) -> Result<ZValidateAddressResponse> {
2689 let network = self.network.clone();
2690
2691 z_validate_address(network, raw_address)
2692 }
2693
2694 async fn get_block_subsidy(&self, height: Option<u32>) -> Result<GetBlockSubsidyResponse> {
2695 let latest_chain_tip = self.latest_chain_tip.clone();
2696 let network = self.network.clone();
2697
2698 let height = if let Some(height) = height {
2699 Height(height)
2700 } else {
2701 best_chain_tip_height(&latest_chain_tip)?
2702 };
2703
2704 if height < network.height_for_first_halving() {
2705 return Err(ErrorObject::borrowed(
2706 0,
2707 "Zebra does not support founders' reward subsidies, \
2708 use a block height that is after the first halving",
2709 None,
2710 ));
2711 }
2712
2713 let founders = Amount::zero();
2715
2716 let total_block_subsidy =
2717 block_subsidy(height, &network).map_error(server::error::LegacyCode::default())?;
2718 let miner_subsidy = miner_subsidy(height, &network, total_block_subsidy)
2719 .map_error(server::error::LegacyCode::default())?;
2720
2721 let (lockbox_streams, mut funding_streams): (Vec<_>, Vec<_>) =
2722 funding_stream_values(height, &network, total_block_subsidy)
2723 .map_error(server::error::LegacyCode::default())?
2724 .into_iter()
2725 .partition(|(receiver, _)| matches!(receiver, FundingStreamReceiver::Deferred));
2727
2728 let is_nu6 = NetworkUpgrade::current(&network, height) == NetworkUpgrade::Nu6;
2729
2730 let [lockbox_total, funding_streams_total]: [std::result::Result<
2731 Amount<NonNegative>,
2732 amount::Error,
2733 >; 2] = [&lockbox_streams, &funding_streams]
2734 .map(|streams| streams.iter().map(|&(_, amount)| amount).sum());
2735
2736 funding_streams.sort_by_key(|(receiver, _funding_stream)| {
2738 ZCASHD_FUNDING_STREAM_ORDER
2739 .iter()
2740 .position(|zcashd_receiver| zcashd_receiver == receiver)
2741 });
2742
2743 let [funding_streams, lockbox_streams]: [Vec<_>; 2] = [funding_streams, lockbox_streams]
2745 .map(|streams| {
2746 streams
2747 .into_iter()
2748 .map(|(receiver, value)| {
2749 let address = funding_stream_address(height, &network, receiver);
2750 types::subsidy::FundingStream::new_internal(
2751 is_nu6, receiver, value, address,
2752 )
2753 })
2754 .collect()
2755 });
2756
2757 Ok(GetBlockSubsidyResponse {
2758 miner: miner_subsidy.into(),
2759 founders: founders.into(),
2760 funding_streams,
2761 lockbox_streams,
2762 funding_streams_total: funding_streams_total
2763 .map_error(server::error::LegacyCode::default())?
2764 .into(),
2765 lockbox_total: lockbox_total
2766 .map_error(server::error::LegacyCode::default())?
2767 .into(),
2768 total_block_subsidy: total_block_subsidy.into(),
2769 })
2770 }
2771
2772 async fn get_difficulty(&self) -> Result<f64> {
2773 chain_tip_difficulty(self.network.clone(), self.read_state.clone(), false).await
2774 }
2775
2776 async fn z_list_unified_receivers(
2777 &self,
2778 address: String,
2779 ) -> Result<ZListUnifiedReceiversResponse> {
2780 use zcash_address::unified::Container;
2781
2782 let (network, unified_address): (
2783 zcash_protocol::consensus::NetworkType,
2784 zcash_address::unified::Address,
2785 ) = zcash_address::unified::Encoding::decode(address.clone().as_str())
2786 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
2787
2788 let mut p2pkh = None;
2789 let mut p2sh = None;
2790 let mut orchard = None;
2791 let mut sapling = None;
2792
2793 for item in unified_address.items() {
2794 match item {
2795 zcash_address::unified::Receiver::Orchard(_data) => {
2796 let addr = zcash_address::unified::Address::try_from_items(vec![item])
2797 .expect("using data already decoded as valid");
2798 orchard = Some(addr.encode(&network));
2799 }
2800 zcash_address::unified::Receiver::Sapling(data) => {
2801 let addr = zebra_chain::primitives::Address::try_from_sapling(network, data)
2802 .expect("using data already decoded as valid");
2803 sapling = Some(addr.payment_address().unwrap_or_default());
2804 }
2805 zcash_address::unified::Receiver::P2pkh(data) => {
2806 let addr =
2807 zebra_chain::primitives::Address::try_from_transparent_p2pkh(network, data)
2808 .expect("using data already decoded as valid");
2809 p2pkh = Some(addr.payment_address().unwrap_or_default());
2810 }
2811 zcash_address::unified::Receiver::P2sh(data) => {
2812 let addr =
2813 zebra_chain::primitives::Address::try_from_transparent_p2sh(network, data)
2814 .expect("using data already decoded as valid");
2815 p2sh = Some(addr.payment_address().unwrap_or_default());
2816 }
2817 _ => (),
2818 }
2819 }
2820
2821 Ok(ZListUnifiedReceiversResponse::new(
2822 orchard, sapling, p2pkh, p2sh,
2823 ))
2824 }
2825
2826 async fn invalidate_block(&self, block_hash: block::Hash) -> Result<()> {
2827 self.state
2828 .clone()
2829 .oneshot(zebra_state::Request::InvalidateBlock(block_hash))
2830 .await
2831 .map(|rsp| assert_eq!(rsp, zebra_state::Response::Invalidated(block_hash)))
2832 .map_misc_error()
2833 }
2834
2835 async fn reconsider_block(&self, block_hash: block::Hash) -> Result<Vec<block::Hash>> {
2836 self.state
2837 .clone()
2838 .oneshot(zebra_state::Request::ReconsiderBlock(block_hash))
2839 .await
2840 .map(|rsp| match rsp {
2841 zebra_state::Response::Reconsidered(block_hashes) => block_hashes,
2842 _ => unreachable!("unmatched response to a reconsider block request"),
2843 })
2844 .map_misc_error()
2845 }
2846
2847 async fn generate(&self, num_blocks: u32) -> Result<Vec<Hash>> {
2848 let rpc = self.clone();
2849 let network = self.network.clone();
2850
2851 if !network.disable_pow() {
2852 return Err(ErrorObject::borrowed(
2853 0,
2854 "generate is only supported on networks where PoW is disabled",
2855 None,
2856 ));
2857 }
2858
2859 let mut block_hashes = Vec::new();
2860 for _ in 0..num_blocks {
2861 let block_template = rpc
2862 .get_block_template(None)
2863 .await
2864 .map_error(server::error::LegacyCode::default())?;
2865
2866 let GetBlockTemplateResponse::TemplateMode(block_template) = block_template else {
2867 return Err(ErrorObject::borrowed(
2868 0,
2869 "error generating block template",
2870 None,
2871 ));
2872 };
2873
2874 let proposal_block = proposal_block_from_template(
2875 &block_template,
2876 BlockTemplateTimeSource::CurTime,
2877 &network,
2878 )
2879 .map_error(server::error::LegacyCode::default())?;
2880
2881 let hex_proposal_block = HexData(
2882 proposal_block
2883 .zcash_serialize_to_vec()
2884 .map_error(server::error::LegacyCode::default())?,
2885 );
2886
2887 rpc.submit_block(hex_proposal_block, None)
2888 .await
2889 .map_error(server::error::LegacyCode::default())?;
2890
2891 block_hashes.push(GetBlockHashResponse(proposal_block.hash()));
2892 }
2893
2894 Ok(block_hashes)
2895 }
2896
2897 async fn add_node(
2898 &self,
2899 addr: zebra_network::PeerSocketAddr,
2900 command: AddNodeCommand,
2901 ) -> Result<()> {
2902 if self.network.is_regtest() {
2903 match command {
2904 AddNodeCommand::Add => {
2905 tracing::info!(?addr, "adding peer address to the address book");
2906 if self.address_book.clone().add_peer(addr) {
2907 Ok(())
2908 } else {
2909 return Err(ErrorObject::owned(
2910 ErrorCode::InvalidParams.code(),
2911 format!("peer address was already present in the address book: {addr}"),
2912 None::<()>,
2913 ));
2914 }
2915 }
2916 }
2917 } else {
2918 return Err(ErrorObject::owned(
2919 ErrorCode::InvalidParams.code(),
2920 "addnode command is only supported on regtest",
2921 None::<()>,
2922 ));
2923 }
2924 }
2925}
2926
2927pub fn best_chain_tip_height<Tip>(latest_chain_tip: &Tip) -> Result<Height>
2932where
2933 Tip: ChainTip + Clone + Send + Sync + 'static,
2934{
2935 latest_chain_tip
2936 .best_tip_height()
2937 .ok_or_misc_error("No blocks in state")
2938}
2939
2940#[allow(clippy::too_many_arguments)]
2944#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
2945pub struct GetInfoResponse {
2946 #[getter(rename = "raw_version")]
2948 version: u64,
2949
2950 build: String,
2952
2953 subversion: String,
2955
2956 #[serde(rename = "protocolversion")]
2958 protocol_version: u32,
2959
2960 blocks: u32,
2962
2963 connections: usize,
2965
2966 #[serde(skip_serializing_if = "Option::is_none")]
2968 proxy: Option<String>,
2969
2970 difficulty: f64,
2972
2973 testnet: bool,
2975
2976 #[serde(rename = "paytxfee")]
2978 pay_tx_fee: f64,
2979
2980 #[serde(rename = "relayfee")]
2982 relay_fee: f64,
2983
2984 errors: String,
2986
2987 #[serde(rename = "errorstimestamp")]
2989 errors_timestamp: String,
2990}
2991
2992#[deprecated(note = "Use `GetInfoResponse` instead")]
2993pub use self::GetInfoResponse as GetInfo;
2994
2995impl Default for GetInfoResponse {
2996 fn default() -> Self {
2997 GetInfoResponse {
2998 version: 0,
2999 build: "some build version".to_string(),
3000 subversion: "some subversion".to_string(),
3001 protocol_version: 0,
3002 blocks: 0,
3003 connections: 0,
3004 proxy: None,
3005 difficulty: 0.0,
3006 testnet: false,
3007 pay_tx_fee: 0.0,
3008 relay_fee: 0.0,
3009 errors: "no errors".to_string(),
3010 errors_timestamp: "no errors timestamp".to_string(),
3011 }
3012 }
3013}
3014
3015impl GetInfoResponse {
3016 #[allow(clippy::too_many_arguments)]
3018 #[deprecated(note = "Use `GetInfoResponse::new` instead")]
3019 pub fn from_parts(
3020 version: u64,
3021 build: String,
3022 subversion: String,
3023 protocol_version: u32,
3024 blocks: u32,
3025 connections: usize,
3026 proxy: Option<String>,
3027 difficulty: f64,
3028 testnet: bool,
3029 pay_tx_fee: f64,
3030 relay_fee: f64,
3031 errors: String,
3032 errors_timestamp: String,
3033 ) -> Self {
3034 Self {
3035 version,
3036 build,
3037 subversion,
3038 protocol_version,
3039 blocks,
3040 connections,
3041 proxy,
3042 difficulty,
3043 testnet,
3044 pay_tx_fee,
3045 relay_fee,
3046 errors,
3047 errors_timestamp,
3048 }
3049 }
3050
3051 pub fn into_parts(
3053 self,
3054 ) -> (
3055 u64,
3056 String,
3057 String,
3058 u32,
3059 u32,
3060 usize,
3061 Option<String>,
3062 f64,
3063 bool,
3064 f64,
3065 f64,
3066 String,
3067 String,
3068 ) {
3069 (
3070 self.version,
3071 self.build,
3072 self.subversion,
3073 self.protocol_version,
3074 self.blocks,
3075 self.connections,
3076 self.proxy,
3077 self.difficulty,
3078 self.testnet,
3079 self.pay_tx_fee,
3080 self.relay_fee,
3081 self.errors,
3082 self.errors_timestamp,
3083 )
3084 }
3085
3086 fn version_from_string(build_string: &str) -> Option<u64> {
3088 let semver_version = semver::Version::parse(build_string.strip_prefix('v')?).ok()?;
3089 let build_number = semver_version
3090 .build
3091 .as_str()
3092 .split('.')
3093 .next()
3094 .and_then(|num_str| num_str.parse::<u64>().ok())
3095 .unwrap_or_default();
3096
3097 let version_number = 1_000_000 * semver_version.major
3099 + 10_000 * semver_version.minor
3100 + 100 * semver_version.patch
3101 + build_number;
3102
3103 Some(version_number)
3104 }
3105}
3106
3107pub type BlockchainValuePoolBalances = [GetBlockchainInfoBalance; 5];
3109
3110#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters)]
3114pub struct GetBlockchainInfoResponse {
3115 chain: String,
3117
3118 #[getter(copy)]
3120 blocks: Height,
3121
3122 #[getter(copy)]
3125 headers: Height,
3126
3127 difficulty: f64,
3129
3130 #[serde(rename = "verificationprogress")]
3132 verification_progress: f64,
3133
3134 #[serde(rename = "chainwork")]
3136 chain_work: u64,
3137
3138 pruned: bool,
3140
3141 size_on_disk: u64,
3143
3144 commitments: u64,
3146
3147 #[serde(rename = "bestblockhash", with = "hex")]
3149 #[getter(copy)]
3150 best_block_hash: block::Hash,
3151
3152 #[serde(rename = "estimatedheight")]
3156 #[getter(copy)]
3157 estimated_height: Height,
3158
3159 #[serde(rename = "chainSupply")]
3161 chain_supply: GetBlockchainInfoBalance,
3162
3163 #[serde(rename = "valuePools")]
3165 value_pools: BlockchainValuePoolBalances,
3166
3167 upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
3169
3170 #[getter(copy)]
3172 consensus: TipConsensusBranch,
3173}
3174
3175impl Default for GetBlockchainInfoResponse {
3176 fn default() -> Self {
3177 Self {
3178 chain: "main".to_string(),
3179 blocks: Height(1),
3180 best_block_hash: block::Hash([0; 32]),
3181 estimated_height: Height(1),
3182 chain_supply: GetBlockchainInfoBalance::chain_supply(Default::default()),
3183 value_pools: GetBlockchainInfoBalance::zero_pools(),
3184 upgrades: IndexMap::new(),
3185 consensus: TipConsensusBranch {
3186 chain_tip: ConsensusBranchIdHex(ConsensusBranchId::default()),
3187 next_block: ConsensusBranchIdHex(ConsensusBranchId::default()),
3188 },
3189 headers: Height(1),
3190 difficulty: 0.0,
3191 verification_progress: 0.0,
3192 chain_work: 0,
3193 pruned: false,
3194 size_on_disk: 0,
3195 commitments: 0,
3196 }
3197 }
3198}
3199
3200impl GetBlockchainInfoResponse {
3201 #[allow(clippy::too_many_arguments)]
3205 pub fn new(
3206 chain: String,
3207 blocks: Height,
3208 best_block_hash: block::Hash,
3209 estimated_height: Height,
3210 chain_supply: GetBlockchainInfoBalance,
3211 value_pools: BlockchainValuePoolBalances,
3212 upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
3213 consensus: TipConsensusBranch,
3214 headers: Height,
3215 difficulty: f64,
3216 verification_progress: f64,
3217 chain_work: u64,
3218 pruned: bool,
3219 size_on_disk: u64,
3220 commitments: u64,
3221 ) -> Self {
3222 Self {
3223 chain,
3224 blocks,
3225 best_block_hash,
3226 estimated_height,
3227 chain_supply,
3228 value_pools,
3229 upgrades,
3230 consensus,
3231 headers,
3232 difficulty,
3233 verification_progress,
3234 chain_work,
3235 pruned,
3236 size_on_disk,
3237 commitments,
3238 }
3239 }
3240}
3241
3242#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
3247#[serde(from = "DAddressStrings")]
3248pub struct AddressStrings {
3249 addresses: Vec<String>,
3251}
3252
3253impl From<DAddressStrings> for AddressStrings {
3254 fn from(address_strings: DAddressStrings) -> Self {
3255 match address_strings {
3256 DAddressStrings::Addresses { addresses } => AddressStrings { addresses },
3257 DAddressStrings::Address(address) => AddressStrings {
3258 addresses: vec![address],
3259 },
3260 }
3261 }
3262}
3263
3264#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize)]
3266#[serde(untagged)]
3267enum DAddressStrings {
3268 Addresses { addresses: Vec<String> },
3270 Address(String),
3272}
3273
3274pub type GetAddressBalanceRequest = AddressStrings;
3276
3277impl AddressStrings {
3278 pub fn new(addresses: Vec<String>) -> AddressStrings {
3280 AddressStrings { addresses }
3281 }
3282
3283 #[deprecated(
3285 note = "Use `AddressStrings::new` instead. Validity will be checked by the server."
3286 )]
3287 pub fn new_valid(addresses: Vec<String>) -> Result<AddressStrings> {
3288 let address_strings = Self { addresses };
3289 address_strings.clone().valid_addresses()?;
3290 Ok(address_strings)
3291 }
3292
3293 pub fn valid_addresses(self) -> Result<HashSet<Address>> {
3297 let valid_addresses: HashSet<Address> = self
3300 .addresses
3301 .into_iter()
3302 .map(|address| {
3303 address
3304 .parse()
3305 .map_error(server::error::LegacyCode::InvalidAddressOrKey)
3306 })
3307 .collect::<Result<_>>()?;
3308
3309 Ok(valid_addresses)
3310 }
3311
3312 pub fn valid_address_strings(self) -> Result<Vec<String>> {
3316 self.clone().valid_addresses()?;
3317 Ok(self.addresses)
3318 }
3319}
3320
3321#[derive(
3323 Clone,
3324 Copy,
3325 Debug,
3326 Default,
3327 Eq,
3328 PartialEq,
3329 Hash,
3330 serde::Serialize,
3331 serde::Deserialize,
3332 Getters,
3333 new,
3334)]
3335pub struct GetAddressBalanceResponse {
3336 balance: u64,
3338 pub received: u64,
3340}
3341
3342#[deprecated(note = "Use `GetAddressBalanceResponse` instead.")]
3343pub use self::GetAddressBalanceResponse as AddressBalance;
3344
3345#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
3347pub struct ConsensusBranchIdHex(#[serde(with = "hex")] ConsensusBranchId);
3348
3349impl ConsensusBranchIdHex {
3350 pub fn new(consensus_branch_id: u32) -> Self {
3352 ConsensusBranchIdHex(consensus_branch_id.into())
3353 }
3354
3355 pub fn inner(&self) -> u32 {
3357 self.0.into()
3358 }
3359}
3360
3361#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3363pub struct NetworkUpgradeInfo {
3364 name: NetworkUpgrade,
3368
3369 #[serde(rename = "activationheight")]
3371 activation_height: Height,
3372
3373 status: NetworkUpgradeStatus,
3375}
3376
3377impl NetworkUpgradeInfo {
3378 pub fn from_parts(
3380 name: NetworkUpgrade,
3381 activation_height: Height,
3382 status: NetworkUpgradeStatus,
3383 ) -> Self {
3384 Self {
3385 name,
3386 activation_height,
3387 status,
3388 }
3389 }
3390
3391 pub fn into_parts(self) -> (NetworkUpgrade, Height, NetworkUpgradeStatus) {
3393 (self.name, self.activation_height, self.status)
3394 }
3395}
3396
3397#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3399pub enum NetworkUpgradeStatus {
3400 #[serde(rename = "active")]
3405 Active,
3406
3407 #[serde(rename = "disabled")]
3409 Disabled,
3410
3411 #[serde(rename = "pending")]
3413 Pending,
3414}
3415
3416#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3420pub struct TipConsensusBranch {
3421 #[serde(rename = "chaintip")]
3423 chain_tip: ConsensusBranchIdHex,
3424
3425 #[serde(rename = "nextblock")]
3427 next_block: ConsensusBranchIdHex,
3428}
3429
3430impl TipConsensusBranch {
3431 pub fn from_parts(chain_tip: u32, next_block: u32) -> Self {
3433 Self {
3434 chain_tip: ConsensusBranchIdHex::new(chain_tip),
3435 next_block: ConsensusBranchIdHex::new(next_block),
3436 }
3437 }
3438
3439 pub fn into_parts(self) -> (u32, u32) {
3441 (self.chain_tip.inner(), self.next_block.inner())
3442 }
3443}
3444
3445#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3451pub struct SendRawTransactionResponse(#[serde(with = "hex")] transaction::Hash);
3452
3453#[deprecated(note = "Use `SendRawTransactionResponse` instead")]
3454pub use self::SendRawTransactionResponse as SentTransactionHash;
3455
3456impl Default for SendRawTransactionResponse {
3457 fn default() -> Self {
3458 Self(transaction::Hash::from([0; 32]))
3459 }
3460}
3461
3462impl SendRawTransactionResponse {
3463 pub fn new(hash: transaction::Hash) -> Self {
3465 SendRawTransactionResponse(hash)
3466 }
3467
3468 #[deprecated(note = "Use `SentTransactionHash::hash` instead")]
3470 pub fn inner(&self) -> transaction::Hash {
3471 self.hash()
3472 }
3473
3474 pub fn hash(&self) -> transaction::Hash {
3476 self.0
3477 }
3478}
3479
3480#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3484#[serde(untagged)]
3485pub enum GetBlockResponse {
3486 Raw(#[serde(with = "hex")] SerializedBlock),
3488 Object(Box<BlockObject>),
3490}
3491
3492#[deprecated(note = "Use `GetBlockResponse` instead")]
3493pub use self::GetBlockResponse as GetBlock;
3494
3495impl Default for GetBlockResponse {
3496 fn default() -> Self {
3497 GetBlockResponse::Object(Box::new(BlockObject {
3498 hash: block::Hash([0; 32]),
3499 confirmations: 0,
3500 height: None,
3501 time: None,
3502 tx: Vec::new(),
3503 trees: GetBlockTrees::default(),
3504 size: None,
3505 version: None,
3506 merkle_root: None,
3507 block_commitments: None,
3508 final_sapling_root: None,
3509 final_orchard_root: None,
3510 nonce: None,
3511 bits: None,
3512 difficulty: None,
3513 chain_supply: None,
3514 value_pools: None,
3515 previous_block_hash: None,
3516 next_block_hash: None,
3517 solution: None,
3518 }))
3519 }
3520}
3521
3522#[allow(clippy::too_many_arguments)]
3524#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3525pub struct BlockObject {
3526 #[getter(copy)]
3528 #[serde(with = "hex")]
3529 hash: block::Hash,
3530
3531 confirmations: i64,
3534
3535 #[serde(skip_serializing_if = "Option::is_none")]
3537 #[getter(copy)]
3538 size: Option<i64>,
3539
3540 #[serde(skip_serializing_if = "Option::is_none")]
3542 #[getter(copy)]
3543 height: Option<Height>,
3544
3545 #[serde(skip_serializing_if = "Option::is_none")]
3547 #[getter(copy)]
3548 version: Option<u32>,
3549
3550 #[serde(with = "opthex", rename = "merkleroot")]
3552 #[serde(skip_serializing_if = "Option::is_none")]
3553 #[getter(copy)]
3554 merkle_root: Option<block::merkle::Root>,
3555
3556 #[serde(with = "opthex", rename = "blockcommitments")]
3559 #[serde(skip_serializing_if = "Option::is_none")]
3560 #[getter(copy)]
3561 block_commitments: Option<[u8; 32]>,
3562
3563 #[serde(with = "opthex", rename = "finalsaplingroot")]
3567 #[serde(skip_serializing_if = "Option::is_none")]
3568 #[getter(copy)]
3569 final_sapling_root: Option<[u8; 32]>,
3570
3571 #[serde(with = "opthex", rename = "finalorchardroot")]
3573 #[serde(skip_serializing_if = "Option::is_none")]
3574 #[getter(copy)]
3575 final_orchard_root: Option<[u8; 32]>,
3576
3577 tx: Vec<GetBlockTransaction>,
3582
3583 #[serde(skip_serializing_if = "Option::is_none")]
3585 #[getter(copy)]
3586 time: Option<i64>,
3587
3588 #[serde(with = "opthex")]
3590 #[serde(skip_serializing_if = "Option::is_none")]
3591 #[getter(copy)]
3592 nonce: Option<[u8; 32]>,
3593
3594 #[serde(with = "opthex")]
3597 #[serde(skip_serializing_if = "Option::is_none")]
3598 #[getter(copy)]
3599 solution: Option<Solution>,
3600
3601 #[serde(with = "opthex")]
3603 #[serde(skip_serializing_if = "Option::is_none")]
3604 #[getter(copy)]
3605 bits: Option<CompactDifficulty>,
3606
3607 #[serde(skip_serializing_if = "Option::is_none")]
3610 #[getter(copy)]
3611 difficulty: Option<f64>,
3612
3613 #[serde(rename = "chainSupply")]
3618 #[serde(skip_serializing_if = "Option::is_none")]
3619 chain_supply: Option<GetBlockchainInfoBalance>,
3620
3621 #[serde(rename = "valuePools")]
3623 #[serde(skip_serializing_if = "Option::is_none")]
3624 value_pools: Option<BlockchainValuePoolBalances>,
3625
3626 #[getter(copy)]
3628 trees: GetBlockTrees,
3629
3630 #[serde(rename = "previousblockhash", skip_serializing_if = "Option::is_none")]
3632 #[serde(with = "opthex")]
3633 #[getter(copy)]
3634 previous_block_hash: Option<block::Hash>,
3635
3636 #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
3638 #[serde(with = "opthex")]
3639 #[getter(copy)]
3640 next_block_hash: Option<block::Hash>,
3641}
3642
3643#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3644#[serde(untagged)]
3645pub enum GetBlockTransaction {
3648 Hash(#[serde(with = "hex")] transaction::Hash),
3650 Object(Box<TransactionObject>),
3652}
3653
3654#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3658#[serde(untagged)]
3659pub enum GetBlockHeaderResponse {
3660 Raw(hex_data::HexData),
3662
3663 Object(Box<BlockHeaderObject>),
3665}
3666
3667#[deprecated(note = "Use `GetBlockHeaderResponse` instead")]
3668pub use self::GetBlockHeaderResponse as GetBlockHeader;
3669
3670#[allow(clippy::too_many_arguments)]
3671#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3672pub struct BlockHeaderObject {
3676 #[serde(with = "hex")]
3678 #[getter(copy)]
3679 hash: block::Hash,
3680
3681 confirmations: i64,
3684
3685 #[getter(copy)]
3687 height: Height,
3688
3689 version: u32,
3691
3692 #[serde(with = "hex", rename = "merkleroot")]
3694 #[getter(copy)]
3695 merkle_root: block::merkle::Root,
3696
3697 #[serde(with = "hex", rename = "blockcommitments")]
3700 #[getter(copy)]
3701 block_commitments: [u8; 32],
3702
3703 #[serde(with = "hex", rename = "finalsaplingroot")]
3705 #[getter(copy)]
3706 final_sapling_root: [u8; 32],
3707
3708 #[serde(skip)]
3711 sapling_tree_size: u64,
3712
3713 time: i64,
3715
3716 #[serde(with = "hex")]
3718 #[getter(copy)]
3719 nonce: [u8; 32],
3720
3721 #[serde(with = "hex")]
3723 #[getter(copy)]
3724 solution: Solution,
3725
3726 #[serde(with = "hex")]
3728 #[getter(copy)]
3729 bits: CompactDifficulty,
3730
3731 difficulty: f64,
3734
3735 #[serde(rename = "previousblockhash")]
3737 #[serde(with = "hex")]
3738 #[getter(copy)]
3739 previous_block_hash: block::Hash,
3740
3741 #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
3743 #[getter(copy)]
3744 #[serde(with = "opthex")]
3745 next_block_hash: Option<block::Hash>,
3746}
3747
3748#[deprecated(note = "Use `BlockHeaderObject` instead")]
3749pub use BlockHeaderObject as GetBlockHeaderObject;
3750
3751impl Default for GetBlockHeaderResponse {
3752 fn default() -> Self {
3753 GetBlockHeaderResponse::Object(Box::default())
3754 }
3755}
3756
3757impl Default for BlockHeaderObject {
3758 fn default() -> Self {
3759 let difficulty: ExpandedDifficulty = zebra_chain::work::difficulty::U256::one().into();
3760
3761 BlockHeaderObject {
3762 hash: block::Hash([0; 32]),
3763 confirmations: 0,
3764 height: Height::MIN,
3765 version: 4,
3766 merkle_root: block::merkle::Root([0; 32]),
3767 block_commitments: Default::default(),
3768 final_sapling_root: Default::default(),
3769 sapling_tree_size: Default::default(),
3770 time: 0,
3771 nonce: [0; 32],
3772 solution: Solution::for_proposal(),
3773 bits: difficulty.to_compact(),
3774 difficulty: 1.0,
3775 previous_block_hash: block::Hash([0; 32]),
3776 next_block_hash: Some(block::Hash([0; 32])),
3777 }
3778 }
3779}
3780
3781#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3787#[serde(transparent)]
3788pub struct GetBlockHashResponse(#[serde(with = "hex")] pub(crate) block::Hash);
3789
3790impl GetBlockHashResponse {
3791 pub fn new(hash: block::Hash) -> Self {
3793 GetBlockHashResponse(hash)
3794 }
3795
3796 pub fn hash(&self) -> block::Hash {
3798 self.0
3799 }
3800}
3801
3802#[deprecated(note = "Use `GetBlockHashResponse` instead")]
3803pub use self::GetBlockHashResponse as GetBlockHash;
3804
3805pub type Hash = GetBlockHashResponse;
3807
3808#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, Getters, new)]
3810pub struct GetBlockHeightAndHashResponse {
3811 #[getter(copy)]
3813 height: block::Height,
3814 #[getter(copy)]
3816 hash: block::Hash,
3817}
3818
3819#[deprecated(note = "Use `GetBlockHeightAndHashResponse` instead.")]
3820pub use GetBlockHeightAndHashResponse as GetBestBlockHeightAndHash;
3821
3822impl Default for GetBlockHeightAndHashResponse {
3823 fn default() -> Self {
3824 Self {
3825 height: block::Height::MIN,
3826 hash: block::Hash([0; 32]),
3827 }
3828 }
3829}
3830
3831impl Default for GetBlockHashResponse {
3832 fn default() -> Self {
3833 GetBlockHashResponse(block::Hash([0; 32]))
3834 }
3835}
3836
3837#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3841#[serde(untagged)]
3842pub enum GetRawTransactionResponse {
3843 Raw(#[serde(with = "hex")] SerializedTransaction),
3845 Object(Box<TransactionObject>),
3847}
3848
3849#[deprecated(note = "Use `GetRawTransactionResponse` instead")]
3850pub use self::GetRawTransactionResponse as GetRawTransaction;
3851
3852impl Default for GetRawTransactionResponse {
3853 fn default() -> Self {
3854 Self::Object(Box::default())
3855 }
3856}
3857
3858pub type GetAddressUtxosResponse = Vec<Utxo>;
3860
3861#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3865pub struct Utxo {
3866 address: transparent::Address,
3868
3869 #[serde(with = "hex")]
3871 #[getter(copy)]
3872 txid: transaction::Hash,
3873
3874 #[serde(rename = "outputIndex")]
3876 #[getter(copy)]
3877 output_index: OutputIndex,
3878
3879 #[serde(with = "hex")]
3881 script: transparent::Script,
3882
3883 satoshis: u64,
3885
3886 #[getter(copy)]
3890 height: Height,
3891}
3892
3893#[deprecated(note = "Use `Utxo` instead")]
3894pub use self::Utxo as GetAddressUtxos;
3895
3896impl Default for Utxo {
3897 fn default() -> Self {
3898 Self {
3899 address: transparent::Address::from_pub_key_hash(
3900 zebra_chain::parameters::NetworkKind::default(),
3901 [0u8; 20],
3902 ),
3903 txid: transaction::Hash::from([0; 32]),
3904 output_index: OutputIndex::from_u64(0),
3905 script: transparent::Script::new(&[0u8; 10]),
3906 satoshis: u64::default(),
3907 height: Height(0),
3908 }
3909 }
3910}
3911
3912impl Utxo {
3913 #[deprecated(note = "Use `Utxo::new` instead")]
3915 pub fn from_parts(
3916 address: transparent::Address,
3917 txid: transaction::Hash,
3918 output_index: OutputIndex,
3919 script: transparent::Script,
3920 satoshis: u64,
3921 height: Height,
3922 ) -> Self {
3923 Utxo {
3924 address,
3925 txid,
3926 output_index,
3927 script,
3928 satoshis,
3929 height,
3930 }
3931 }
3932
3933 pub fn into_parts(
3935 &self,
3936 ) -> (
3937 transparent::Address,
3938 transaction::Hash,
3939 OutputIndex,
3940 transparent::Script,
3941 u64,
3942 Height,
3943 ) {
3944 (
3945 self.address.clone(),
3946 self.txid,
3947 self.output_index,
3948 self.script.clone(),
3949 self.satoshis,
3950 self.height,
3951 )
3952 }
3953}
3954
3955#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, Getters, new)]
3959pub struct GetAddressTxIdsRequest {
3960 addresses: Vec<String>,
3962 start: Option<u32>,
3964 end: Option<u32>,
3966}
3967
3968impl GetAddressTxIdsRequest {
3969 #[deprecated(note = "Use `GetAddressTxIdsRequest::new` instead.")]
3971 pub fn from_parts(addresses: Vec<String>, start: u32, end: u32) -> Self {
3972 GetAddressTxIdsRequest {
3973 addresses,
3974 start: Some(start),
3975 end: Some(end),
3976 }
3977 }
3978
3979 pub fn into_parts(&self) -> (Vec<String>, u32, u32) {
3981 (
3982 self.addresses.clone(),
3983 self.start.unwrap_or(0),
3984 self.end.unwrap_or(0),
3985 )
3986 }
3987}
3988
3989#[derive(Debug, serde::Deserialize)]
3991#[serde(untagged)]
3992pub enum GetAddressTxIdsParams {
3993 Single(String),
3995 Object(GetAddressTxIdsRequest),
3997}
3998
3999impl GetAddressTxIdsParams {
4000 pub fn into_request(self) -> GetAddressTxIdsRequest {
4002 match self {
4003 GetAddressTxIdsParams::Single(addr) => GetAddressTxIdsRequest {
4004 addresses: vec![addr],
4005 start: None,
4006 end: None,
4007 },
4008 GetAddressTxIdsParams::Object(req) => req,
4009 }
4010 }
4011}
4012
4013#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
4015pub struct GetBlockTrees {
4016 #[serde(skip_serializing_if = "SaplingTrees::is_empty")]
4017 sapling: SaplingTrees,
4018 #[serde(skip_serializing_if = "OrchardTrees::is_empty")]
4019 orchard: OrchardTrees,
4020}
4021
4022impl Default for GetBlockTrees {
4023 fn default() -> Self {
4024 GetBlockTrees {
4025 sapling: SaplingTrees { size: 0 },
4026 orchard: OrchardTrees { size: 0 },
4027 }
4028 }
4029}
4030
4031impl GetBlockTrees {
4032 pub fn new(sapling: u64, orchard: u64) -> Self {
4034 GetBlockTrees {
4035 sapling: SaplingTrees { size: sapling },
4036 orchard: OrchardTrees { size: orchard },
4037 }
4038 }
4039
4040 pub fn sapling(self) -> u64 {
4042 self.sapling.size
4043 }
4044
4045 pub fn orchard(self) -> u64 {
4047 self.orchard.size
4048 }
4049}
4050
4051#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
4053pub struct SaplingTrees {
4054 size: u64,
4055}
4056
4057impl SaplingTrees {
4058 fn is_empty(&self) -> bool {
4059 self.size == 0
4060 }
4061}
4062
4063#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
4065pub struct OrchardTrees {
4066 size: u64,
4067}
4068
4069impl OrchardTrees {
4070 fn is_empty(&self) -> bool {
4071 self.size == 0
4072 }
4073}
4074
4075fn build_height_range(
4091 start: Option<u32>,
4092 end: Option<u32>,
4093 chain_height: Height,
4094) -> Result<RangeInclusive<Height>> {
4095 let start = Height(start.unwrap_or(0)).min(chain_height);
4098
4099 let end = match end {
4101 Some(0) | None => chain_height,
4102 Some(val) => Height(val).min(chain_height),
4103 };
4104
4105 if start > end {
4106 return Err(ErrorObject::owned(
4107 ErrorCode::InvalidParams.code(),
4108 format!("start {start:?} must be less than or equal to end {end:?}"),
4109 None::<()>,
4110 ));
4111 }
4112
4113 Ok(start..=end)
4114}
4115
4116#[allow(dead_code)]
4124pub fn height_from_signed_int(index: i32, tip_height: Height) -> Result<Height> {
4125 if index >= 0 {
4126 let height = index.try_into().expect("Positive i32 always fits in u32");
4127 if height > tip_height.0 {
4128 return Err(ErrorObject::borrowed(
4129 ErrorCode::InvalidParams.code(),
4130 "Provided index is greater than the current tip",
4131 None,
4132 ));
4133 }
4134 Ok(Height(height))
4135 } else {
4136 let height = i32::try_from(tip_height.0)
4138 .expect("tip height fits in i32, because Height::MAX fits in i32")
4139 .checked_add(index + 1);
4140
4141 let sanitized_height = match height {
4142 None => {
4143 return Err(ErrorObject::borrowed(
4144 ErrorCode::InvalidParams.code(),
4145 "Provided index is not valid",
4146 None,
4147 ));
4148 }
4149 Some(h) => {
4150 if h < 0 {
4151 return Err(ErrorObject::borrowed(
4152 ErrorCode::InvalidParams.code(),
4153 "Provided negative index ends up with a negative height",
4154 None,
4155 ));
4156 }
4157 let h: u32 = h.try_into().expect("Positive i32 always fits in u32");
4158 if h > tip_height.0 {
4159 return Err(ErrorObject::borrowed(
4160 ErrorCode::InvalidParams.code(),
4161 "Provided index is greater than the current tip",
4162 None,
4163 ));
4164 }
4165
4166 h
4167 }
4168 };
4169
4170 Ok(Height(sanitized_height))
4171 }
4172}
4173
4174pub mod opthex {
4176 use hex::{FromHex, ToHex};
4177 use serde::{de, Deserialize, Deserializer, Serializer};
4178
4179 #[allow(missing_docs)]
4180 pub fn serialize<S, T>(data: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
4181 where
4182 S: Serializer,
4183 T: ToHex,
4184 {
4185 match data {
4186 Some(data) => {
4187 let s = data.encode_hex::<String>();
4188 serializer.serialize_str(&s)
4189 }
4190 None => serializer.serialize_none(),
4191 }
4192 }
4193
4194 #[allow(missing_docs)]
4195 pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
4196 where
4197 D: Deserializer<'de>,
4198 T: FromHex,
4199 {
4200 let opt = Option::<String>::deserialize(deserializer)?;
4201 match opt {
4202 Some(s) => T::from_hex(&s)
4203 .map(Some)
4204 .map_err(|_e| de::Error::custom("failed to convert hex string")),
4205 None => Ok(None),
4206 }
4207 }
4208}
4209
4210pub mod arrayhex {
4212 use serde::{Deserializer, Serializer};
4213 use std::fmt;
4214
4215 #[allow(missing_docs)]
4216 pub fn serialize<S, const N: usize>(data: &[u8; N], serializer: S) -> Result<S::Ok, S::Error>
4217 where
4218 S: Serializer,
4219 {
4220 let hex_string = hex::encode(data);
4221 serializer.serialize_str(&hex_string)
4222 }
4223
4224 #[allow(missing_docs)]
4225 pub fn deserialize<'de, D, const N: usize>(deserializer: D) -> Result<[u8; N], D::Error>
4226 where
4227 D: Deserializer<'de>,
4228 {
4229 struct HexArrayVisitor<const N: usize>;
4230
4231 impl<const N: usize> serde::de::Visitor<'_> for HexArrayVisitor<N> {
4232 type Value = [u8; N];
4233
4234 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
4235 write!(formatter, "a hex string representing exactly {N} bytes")
4236 }
4237
4238 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
4239 where
4240 E: serde::de::Error,
4241 {
4242 let vec = hex::decode(v).map_err(E::custom)?;
4243 vec.clone().try_into().map_err(|_| {
4244 E::invalid_length(vec.len(), &format!("expected {N} bytes").as_str())
4245 })
4246 }
4247 }
4248
4249 deserializer.deserialize_str(HexArrayVisitor::<N>)
4250 }
4251}
4252
4253pub async fn chain_tip_difficulty<State>(
4255 network: Network,
4256 mut state: State,
4257 should_use_default: bool,
4258) -> Result<f64>
4259where
4260 State: Service<
4261 zebra_state::ReadRequest,
4262 Response = zebra_state::ReadResponse,
4263 Error = zebra_state::BoxError,
4264 > + Clone
4265 + Send
4266 + Sync
4267 + 'static,
4268 State::Future: Send,
4269{
4270 let request = ReadRequest::ChainInfo;
4271
4272 let response = state
4278 .ready()
4279 .and_then(|service| service.call(request))
4280 .await;
4281
4282 let response = match (should_use_default, response) {
4283 (_, Ok(res)) => res,
4284 (true, Err(_)) => {
4285 return Ok((U256::from(network.target_difficulty_limit()) >> 128).as_u128() as f64);
4286 }
4287 (false, Err(error)) => return Err(ErrorObject::owned(0, error.to_string(), None::<()>)),
4288 };
4289
4290 let chain_info = match response {
4291 ReadResponse::ChainInfo(info) => info,
4292 _ => unreachable!("unmatched response to a chain info request"),
4293 };
4294
4295 let pow_limit: U256 = network.target_difficulty_limit().into();
4318 let Some(difficulty) = chain_info.expected_difficulty.to_expanded() else {
4319 return Ok(0.0);
4320 };
4321
4322 let pow_limit = pow_limit >> 128;
4324 let difficulty = U256::from(difficulty) >> 128;
4325
4326 let pow_limit = pow_limit.as_u128() as f64;
4329 let difficulty = difficulty.as_u128() as f64;
4330
4331 Ok(pow_limit / difficulty)
4333}
4334
4335#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
4337pub enum AddNodeCommand {
4338 #[serde(rename = "add")]
4340 Add,
4341}