1use std::{
35 cmp,
36 collections::{HashMap, HashSet},
37 fmt,
38 ops::RangeInclusive,
39 sync::Arc,
40 time::Duration,
41};
42
43use chrono::Utc;
44use derive_getters::Getters;
45use derive_new::new;
46use futures::{future::OptionFuture, stream::FuturesOrdered, StreamExt, TryFutureExt};
47use hex::{FromHex, ToHex};
48use indexmap::IndexMap;
49use jsonrpsee::core::{async_trait, RpcResult as Result};
50use jsonrpsee_proc_macros::rpc;
51use jsonrpsee_types::{ErrorCode, ErrorObject};
52use rand::{rngs::OsRng, RngCore};
53use tokio::{
54 sync::{broadcast, mpsc, watch},
55 task::JoinHandle,
56};
57use tower::ServiceExt;
58use tracing::Instrument;
59
60use zcash_address::{unified::Encoding, TryFromAddress};
61use zcash_primitives::consensus::Parameters;
62
63use zebra_chain::{
64 amount::{self, Amount, NegativeAllowed, NonNegative},
65 block::{self, Block, Commitment, Height, SerializedBlock, TryIntoHeight},
66 chain_sync_status::ChainSyncStatus,
67 chain_tip::{ChainTip, NetworkChainTipHeightEstimator},
68 parameters::{
69 subsidy::{
70 block_subsidy, funding_stream_values, miner_subsidy, FundingStreamReceiver,
71 ParameterSubsidy,
72 },
73 ConsensusBranchId, Network, NetworkUpgrade, POW_AVERAGING_WINDOW,
74 },
75 serialization::{BytesInDisplayOrder, ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize},
76 subtree::NoteCommitmentSubtreeIndex,
77 transaction::{self, SerializedTransaction, Transaction, UnminedTx},
78 transparent::{self, Address, OutputIndex},
79 value_balance::ValueBalance,
80 work::{
81 difficulty::{CompactDifficulty, ExpandedDifficulty, ParameterDifficulty, U256},
82 equihash::Solution,
83 },
84};
85use zebra_consensus::{
86 funding_stream_address, router::service_trait::BlockVerifierService, RouterError,
87};
88use zebra_network::{address_book_peers::AddressBookPeers, types::PeerServices, PeerSocketAddr};
89use zebra_node_services::mempool::{self, MempoolService};
90use zebra_state::{
91 AnyTx, HashOrHeight, OutputLocation, ReadRequest, ReadResponse, ReadState as ReadStateService,
92 State as StateService, TransactionLocation,
93};
94
95use crate::{
96 client::Treestate,
97 config,
98 methods::types::{validate_address::validate_address, z_validate_address::z_validate_address},
99 queue::Queue,
100 server::{
101 self,
102 error::{MapError, OkOrError},
103 },
104};
105
106pub(crate) mod hex_data;
107pub(crate) mod trees;
108pub(crate) mod types;
109
110use hex_data::HexData;
111use trees::{GetSubtreesByIndexResponse, GetTreestateResponse, SubtreeRpcData};
112use types::{
113 get_block_template::{
114 constants::{
115 DEFAULT_SOLUTION_RATE_WINDOW_SIZE, MEMPOOL_LONG_POLL_INTERVAL,
116 ZCASHD_FUNDING_STREAM_ORDER,
117 },
118 proposal::proposal_block_from_template,
119 BlockTemplateResponse, BlockTemplateTimeSource, GetBlockTemplateHandler,
120 GetBlockTemplateParameters, GetBlockTemplateResponse,
121 },
122 get_blockchain_info::GetBlockchainInfoBalance,
123 get_mempool_info::GetMempoolInfoResponse,
124 get_mining_info::GetMiningInfoResponse,
125 get_raw_mempool::{self, GetRawMempoolResponse},
126 long_poll::LongPollInput,
127 network_info::{GetNetworkInfoResponse, NetworkInfo},
128 peer_info::PeerInfo,
129 submit_block::{SubmitBlockErrorResponse, SubmitBlockParameters, SubmitBlockResponse},
130 subsidy::GetBlockSubsidyResponse,
131 transaction::TransactionObject,
132 unified_address::ZListUnifiedReceiversResponse,
133 validate_address::ValidateAddressResponse,
134 z_validate_address::ZValidateAddressResponse,
135};
136
137#[cfg(test)]
138mod tests;
139
140#[rpc(server)]
141pub trait Rpc {
143 #[method(name = "getinfo")]
158 async fn get_info(&self) -> Result<GetInfoResponse>;
159
160 #[method(name = "getblockchaininfo")]
171 async fn get_blockchain_info(&self) -> Result<GetBlockchainInfoResponse>;
172
173 #[method(name = "getaddressbalance")]
196 async fn get_address_balance(
197 &self,
198 address_strings: GetAddressBalanceRequest,
199 ) -> Result<GetAddressBalanceResponse>;
200
201 #[method(name = "sendrawtransaction")]
218 async fn send_raw_transaction(
219 &self,
220 raw_transaction_hex: String,
221 _allow_high_fees: Option<bool>,
222 ) -> Result<SendRawTransactionResponse>;
223
224 #[method(name = "getblock")]
244 async fn get_block(
245 &self,
246 hash_or_height: String,
247 verbosity: Option<u8>,
248 ) -> Result<GetBlockResponse>;
249
250 #[method(name = "getblockheader")]
268 async fn get_block_header(
269 &self,
270 hash_or_height: String,
271 verbose: Option<bool>,
272 ) -> Result<GetBlockHeaderResponse>;
273
274 #[method(name = "getbestblockhash")]
280 fn get_best_block_hash(&self) -> Result<GetBlockHashResponse>;
281
282 #[method(name = "getbestblockheightandhash")]
288 fn get_best_block_height_and_hash(&self) -> Result<GetBlockHeightAndHashResponse>;
289
290 #[method(name = "getmempoolinfo")]
294 async fn get_mempool_info(&self) -> Result<GetMempoolInfoResponse>;
295
296 #[method(name = "getrawmempool")]
306 async fn get_raw_mempool(&self, verbose: Option<bool>) -> Result<GetRawMempoolResponse>;
307
308 #[method(name = "z_gettreestate")]
325 async fn z_get_treestate(&self, hash_or_height: String) -> Result<GetTreestateResponse>;
326
327 #[method(name = "z_getsubtreesbyindex")]
346 async fn z_get_subtrees_by_index(
347 &self,
348 pool: String,
349 start_index: NoteCommitmentSubtreeIndex,
350 limit: Option<NoteCommitmentSubtreeIndex>,
351 ) -> Result<GetSubtreesByIndexResponse>;
352
353 #[method(name = "getrawtransaction")]
365 async fn get_raw_transaction(
366 &self,
367 txid: String,
368 verbose: Option<u8>,
369 block_hash: Option<String>,
370 ) -> Result<GetRawTransactionResponse>;
371
372 #[method(name = "getaddresstxids")]
402 async fn get_address_tx_ids(&self, request: GetAddressTxIdsRequest) -> Result<Vec<String>>;
403
404 #[method(name = "getaddressutxos")]
423 async fn get_address_utxos(
424 &self,
425 request: GetAddressUtxosRequest,
426 ) -> Result<GetAddressUtxosResponse>;
427
428 #[method(name = "stop")]
439 fn stop(&self) -> Result<String>;
440
441 #[method(name = "getblockcount")]
448 fn get_block_count(&self) -> Result<u32>;
449
450 #[method(name = "getblockhash")]
466 async fn get_block_hash(&self, index: i32) -> Result<GetBlockHashResponse>;
467
468 #[method(name = "getblocktemplate")]
490 async fn get_block_template(
491 &self,
492 parameters: Option<GetBlockTemplateParameters>,
493 ) -> Result<GetBlockTemplateResponse>;
494
495 #[method(name = "submitblock")]
511 async fn submit_block(
512 &self,
513 hex_data: HexData,
514 _parameters: Option<SubmitBlockParameters>,
515 ) -> Result<SubmitBlockResponse>;
516
517 #[method(name = "getmininginfo")]
523 async fn get_mining_info(&self) -> Result<GetMiningInfoResponse>;
524
525 #[method(name = "getnetworksolps")]
536 async fn get_network_sol_ps(&self, num_blocks: Option<i32>, height: Option<i32>)
537 -> Result<u64>;
538
539 #[method(name = "getnetworkhashps")]
549 async fn get_network_hash_ps(
550 &self,
551 num_blocks: Option<i32>,
552 height: Option<i32>,
553 ) -> Result<u64> {
554 self.get_network_sol_ps(num_blocks, height).await
555 }
556
557 #[method(name = "getnetworkinfo")]
563 async fn get_network_info(&self) -> Result<GetNetworkInfoResponse>;
564
565 #[method(name = "getpeerinfo")]
571 async fn get_peer_info(&self) -> Result<Vec<PeerInfo>>;
572
573 #[method(name = "validateaddress")]
584 async fn validate_address(&self, address: String) -> Result<ValidateAddressResponse>;
585
586 #[method(name = "z_validateaddress")]
601 async fn z_validate_address(&self, address: String) -> Result<ZValidateAddressResponse>;
602
603 #[method(name = "getblocksubsidy")]
618 async fn get_block_subsidy(&self, height: Option<u32>) -> Result<GetBlockSubsidyResponse>;
619
620 #[method(name = "getdifficulty")]
626 async fn get_difficulty(&self) -> Result<f64>;
627
628 #[method(name = "z_listunifiedreceivers")]
642 async fn z_list_unified_receivers(
643 &self,
644 address: String,
645 ) -> Result<ZListUnifiedReceiversResponse>;
646
647 #[method(name = "invalidateblock")]
655 async fn invalidate_block(&self, block_hash: String) -> Result<()>;
656
657 #[method(name = "reconsiderblock")]
663 async fn reconsider_block(&self, block_hash: String) -> Result<Vec<block::Hash>>;
664
665 #[method(name = "generate")]
666 async fn generate(&self, num_blocks: u32) -> Result<Vec<GetBlockHashResponse>>;
680
681 #[method(name = "addnode")]
682 async fn add_node(&self, addr: PeerSocketAddr, command: AddNodeCommand) -> Result<()>;
697}
698
699#[derive(Clone)]
701pub struct RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
702where
703 Mempool: MempoolService,
704 State: StateService,
705 ReadState: ReadStateService,
706 Tip: ChainTip + Clone + Send + Sync + 'static,
707 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
708 BlockVerifierRouter: BlockVerifierService,
709 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
710{
711 build_version: String,
715
716 user_agent: String,
718
719 network: Network,
721
722 debug_force_finished_sync: bool,
725
726 mempool: Mempool,
730
731 state: State,
733
734 read_state: ReadState,
736
737 latest_chain_tip: Tip,
739
740 queue_sender: broadcast::Sender<UnminedTx>,
744
745 address_book: AddressBook,
747
748 last_warn_error_log_rx: LoggedLastEvent,
750
751 gbt: GetBlockTemplateHandler<BlockVerifierRouter, SyncStatus>,
753}
754
755pub type LoggedLastEvent = watch::Receiver<Option<(String, tracing::Level, chrono::DateTime<Utc>)>>;
757
758impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus> fmt::Debug
759 for RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
760where
761 Mempool: MempoolService,
762 State: StateService,
763 ReadState: ReadStateService,
764 Tip: ChainTip + Clone + Send + Sync + 'static,
765 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
766 BlockVerifierRouter: BlockVerifierService,
767 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
768{
769 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
770 f.debug_struct("RpcImpl")
772 .field("build_version", &self.build_version)
773 .field("user_agent", &self.user_agent)
774 .field("network", &self.network)
775 .field("debug_force_finished_sync", &self.debug_force_finished_sync)
776 .field("getblocktemplate", &self.gbt)
777 .finish()
778 }
779}
780
781impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
782 RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
783where
784 Mempool: MempoolService,
785 State: StateService,
786 ReadState: ReadStateService,
787 Tip: ChainTip + Clone + Send + Sync + 'static,
788 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
789 BlockVerifierRouter: BlockVerifierService,
790 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
791{
792 #[allow(clippy::too_many_arguments)]
797 pub fn new<VersionString, UserAgentString>(
798 network: Network,
799 mining_config: config::mining::Config,
800 debug_force_finished_sync: bool,
801 build_version: VersionString,
802 user_agent: UserAgentString,
803 mempool: Mempool,
804 state: State,
805 read_state: ReadState,
806 block_verifier_router: BlockVerifierRouter,
807 sync_status: SyncStatus,
808 latest_chain_tip: Tip,
809 address_book: AddressBook,
810 last_warn_error_log_rx: LoggedLastEvent,
811 mined_block_sender: Option<mpsc::Sender<(block::Hash, block::Height)>>,
812 ) -> (Self, JoinHandle<()>)
813 where
814 VersionString: ToString + Clone + Send + 'static,
815 UserAgentString: ToString + Clone + Send + 'static,
816 {
817 let (runner, queue_sender) = Queue::start();
818
819 let mut build_version = build_version.to_string();
820 let user_agent = user_agent.to_string();
821
822 if !build_version.is_empty() && !build_version.starts_with('v') {
824 build_version.insert(0, 'v');
825 }
826
827 let gbt = GetBlockTemplateHandler::new(
828 &network,
829 mining_config.clone(),
830 block_verifier_router,
831 sync_status,
832 mined_block_sender,
833 );
834
835 let rpc_impl = RpcImpl {
836 build_version,
837 user_agent,
838 network: network.clone(),
839 debug_force_finished_sync,
840 mempool: mempool.clone(),
841 state: state.clone(),
842 read_state: read_state.clone(),
843 latest_chain_tip: latest_chain_tip.clone(),
844 queue_sender,
845 address_book,
846 last_warn_error_log_rx,
847 gbt,
848 };
849
850 let rpc_tx_queue_task_handle = tokio::spawn(
852 runner
853 .run(mempool, read_state, latest_chain_tip, network)
854 .in_current_span(),
855 );
856
857 (rpc_impl, rpc_tx_queue_task_handle)
858 }
859
860 pub fn network(&self) -> &Network {
862 &self.network
863 }
864}
865
866#[async_trait]
867impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus> RpcServer
868 for RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
869where
870 Mempool: MempoolService,
871 State: StateService,
872 ReadState: ReadStateService,
873 Tip: ChainTip + Clone + Send + Sync + 'static,
874 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
875 BlockVerifierRouter: BlockVerifierService,
876 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
877{
878 async fn get_info(&self) -> Result<GetInfoResponse> {
879 let version = GetInfoResponse::version_from_string(&self.build_version)
880 .expect("invalid version string");
881
882 let connections = self.address_book.recently_live_peers(Utc::now()).len();
883
884 let last_error_recorded = self.last_warn_error_log_rx.borrow().clone();
885 let (last_error_log, _level, last_error_log_time) = last_error_recorded.unwrap_or((
886 GetInfoResponse::default().errors,
887 tracing::Level::INFO,
888 Utc::now(),
889 ));
890
891 let tip_height = self
892 .latest_chain_tip
893 .best_tip_height()
894 .unwrap_or(Height::MIN);
895 let testnet = self.network.is_a_test_network();
896
897 let pay_tx_fee = 0.0;
903
904 let relay_fee = zebra_chain::transaction::zip317::MIN_MEMPOOL_TX_FEE_RATE as f64
905 / (zebra_chain::amount::COIN as f64);
906 let difficulty = chain_tip_difficulty(self.network.clone(), self.read_state.clone(), true)
907 .await
908 .expect("should always be Ok when `should_use_default` is true");
909
910 let response = GetInfoResponse {
911 version,
912 build: self.build_version.clone(),
913 subversion: self.user_agent.clone(),
914 protocol_version: zebra_network::constants::CURRENT_NETWORK_PROTOCOL_VERSION.0,
915 blocks: tip_height.0,
916 connections,
917 proxy: None,
918 difficulty,
919 testnet,
920 pay_tx_fee,
921 relay_fee,
922 errors: last_error_log,
923 errors_timestamp: last_error_log_time.to_string(),
924 };
925
926 Ok(response)
927 }
928
929 #[allow(clippy::unwrap_in_result)]
930 async fn get_blockchain_info(&self) -> Result<GetBlockchainInfoResponse> {
931 let debug_force_finished_sync = self.debug_force_finished_sync;
932 let network = &self.network;
933
934 let (usage_info_rsp, tip_pool_values_rsp, chain_tip_difficulty) = {
935 use zebra_state::ReadRequest::*;
936 let state_call = |request| self.read_state.clone().oneshot(request);
937 tokio::join!(
938 state_call(UsageInfo),
939 state_call(TipPoolValues),
940 chain_tip_difficulty(network.clone(), self.read_state.clone(), true)
941 )
942 };
943
944 let (size_on_disk, (tip_height, tip_hash), value_balance, difficulty) = {
945 use zebra_state::ReadResponse::*;
946
947 let UsageInfo(size_on_disk) = usage_info_rsp.map_misc_error()? else {
948 unreachable!("unmatched response to a TipPoolValues request")
949 };
950
951 let (tip, value_balance) = match tip_pool_values_rsp {
952 Ok(TipPoolValues {
953 tip_height,
954 tip_hash,
955 value_balance,
956 }) => ((tip_height, tip_hash), value_balance),
957 Ok(_) => unreachable!("unmatched response to a TipPoolValues request"),
958 Err(_) => ((Height::MIN, network.genesis_hash()), Default::default()),
959 };
960
961 let difficulty = chain_tip_difficulty
962 .expect("should always be Ok when `should_use_default` is true");
963
964 (size_on_disk, tip, value_balance, difficulty)
965 };
966
967 let now = Utc::now();
968 let (estimated_height, verification_progress) = self
969 .latest_chain_tip
970 .best_tip_height_and_block_time()
971 .map(|(tip_height, tip_block_time)| {
972 let height =
973 NetworkChainTipHeightEstimator::new(tip_block_time, tip_height, network)
974 .estimate_height_at(now);
975
976 let height =
980 if tip_block_time > now || height < tip_height || debug_force_finished_sync {
981 tip_height
982 } else {
983 height
984 };
985
986 (height, f64::from(tip_height.0) / f64::from(height.0))
987 })
988 .unwrap_or((Height::MIN, 0.0));
990
991 let verification_progress = if network.is_regtest() {
992 1.0
993 } else {
994 verification_progress
995 };
996
997 let mut upgrades = IndexMap::new();
1001 for (activation_height, network_upgrade) in network.full_activation_list() {
1002 if let Some(branch_id) = network_upgrade.branch_id() {
1007 let status = if tip_height >= activation_height {
1009 NetworkUpgradeStatus::Active
1010 } else {
1011 NetworkUpgradeStatus::Pending
1012 };
1013
1014 let upgrade = NetworkUpgradeInfo {
1015 name: network_upgrade,
1016 activation_height,
1017 status,
1018 };
1019 upgrades.insert(ConsensusBranchIdHex(branch_id), upgrade);
1020 }
1021 }
1022
1023 let next_block_height =
1025 (tip_height + 1).expect("valid chain tips are a lot less than Height::MAX");
1026 let consensus = TipConsensusBranch {
1027 chain_tip: ConsensusBranchIdHex(
1028 NetworkUpgrade::current(network, tip_height)
1029 .branch_id()
1030 .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
1031 ),
1032 next_block: ConsensusBranchIdHex(
1033 NetworkUpgrade::current(network, next_block_height)
1034 .branch_id()
1035 .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
1036 ),
1037 };
1038
1039 let response = GetBlockchainInfoResponse {
1040 chain: network.bip70_network_name(),
1041 blocks: tip_height,
1042 best_block_hash: tip_hash,
1043 estimated_height,
1044 chain_supply: GetBlockchainInfoBalance::chain_supply(value_balance),
1045 value_pools: GetBlockchainInfoBalance::value_pools(value_balance, None),
1046 upgrades,
1047 consensus,
1048 headers: tip_height,
1049 difficulty,
1050 verification_progress,
1051 chain_work: 0,
1053 pruned: false,
1054 size_on_disk,
1055 commitments: 0,
1057 };
1058
1059 Ok(response)
1060 }
1061
1062 async fn get_address_balance(
1063 &self,
1064 address_strings: GetAddressBalanceRequest,
1065 ) -> Result<GetAddressBalanceResponse> {
1066 let valid_addresses = address_strings.valid_addresses()?;
1067
1068 let request = zebra_state::ReadRequest::AddressBalance(valid_addresses);
1069 let response = self
1070 .read_state
1071 .clone()
1072 .oneshot(request)
1073 .await
1074 .map_misc_error()?;
1075
1076 match response {
1077 zebra_state::ReadResponse::AddressBalance { balance, received } => {
1078 Ok(GetAddressBalanceResponse {
1079 balance: u64::from(balance),
1080 received,
1081 })
1082 }
1083 _ => unreachable!("Unexpected response from state service: {response:?}"),
1084 }
1085 }
1086
1087 async fn send_raw_transaction(
1089 &self,
1090 raw_transaction_hex: String,
1091 _allow_high_fees: Option<bool>,
1092 ) -> Result<SendRawTransactionResponse> {
1093 let mempool = self.mempool.clone();
1094 let queue_sender = self.queue_sender.clone();
1095
1096 let raw_transaction_bytes = Vec::from_hex(raw_transaction_hex)
1099 .map_error(server::error::LegacyCode::Deserialization)?;
1100 let raw_transaction = Transaction::zcash_deserialize(&*raw_transaction_bytes)
1101 .map_error(server::error::LegacyCode::Deserialization)?;
1102
1103 let transaction_hash = raw_transaction.hash();
1104
1105 let unmined_transaction = UnminedTx::from(raw_transaction.clone());
1107 let _ = queue_sender.send(unmined_transaction);
1108
1109 let transaction_parameter = mempool::Gossip::Tx(raw_transaction.into());
1110 let request = mempool::Request::Queue(vec![transaction_parameter]);
1111
1112 let response = mempool.oneshot(request).await.map_misc_error()?;
1113
1114 let mut queue_results = match response {
1115 mempool::Response::Queued(results) => results,
1116 _ => unreachable!("incorrect response variant from mempool service"),
1117 };
1118
1119 assert_eq!(
1120 queue_results.len(),
1121 1,
1122 "mempool service returned more results than expected"
1123 );
1124
1125 let queue_result = queue_results
1126 .pop()
1127 .expect("there should be exactly one item in Vec")
1128 .inspect_err(|err| tracing::debug!("sent transaction to mempool: {:?}", &err))
1129 .map_misc_error()?
1130 .await
1131 .map_misc_error()?;
1132
1133 tracing::debug!("sent transaction to mempool: {:?}", &queue_result);
1134
1135 queue_result
1136 .map(|_| SendRawTransactionResponse(transaction_hash))
1137 .map_error(server::error::LegacyCode::Verify)
1144 }
1145
1146 async fn get_block(
1151 &self,
1152 hash_or_height: String,
1153 verbosity: Option<u8>,
1154 ) -> Result<GetBlockResponse> {
1155 let verbosity = verbosity.unwrap_or(1);
1156 let network = self.network.clone();
1157 let original_hash_or_height = hash_or_height.clone();
1158
1159 let get_block_header_future = if matches!(verbosity, 1 | 2) {
1161 Some(self.get_block_header(original_hash_or_height.clone(), Some(true)))
1162 } else {
1163 None
1164 };
1165
1166 let hash_or_height =
1167 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1168 .map_error(server::error::LegacyCode::InvalidParameter)?;
1171
1172 if verbosity == 0 {
1173 let request = zebra_state::ReadRequest::Block(hash_or_height);
1174 let response = self
1175 .read_state
1176 .clone()
1177 .oneshot(request)
1178 .await
1179 .map_misc_error()?;
1180
1181 match response {
1182 zebra_state::ReadResponse::Block(Some(block)) => {
1183 Ok(GetBlockResponse::Raw(block.into()))
1184 }
1185 zebra_state::ReadResponse::Block(None) => {
1186 Err("Block not found").map_error(server::error::LegacyCode::InvalidParameter)
1187 }
1188 _ => unreachable!("unmatched response to a block request"),
1189 }
1190 } else if let Some(get_block_header_future) = get_block_header_future {
1191 let get_block_header_result: Result<GetBlockHeaderResponse> =
1192 get_block_header_future.await;
1193
1194 let GetBlockHeaderResponse::Object(block_header) = get_block_header_result? else {
1195 panic!("must return Object")
1196 };
1197
1198 let BlockHeaderObject {
1199 hash,
1200 confirmations,
1201 height,
1202 version,
1203 merkle_root,
1204 block_commitments,
1205 final_sapling_root,
1206 sapling_tree_size,
1207 time,
1208 nonce,
1209 solution,
1210 bits,
1211 difficulty,
1212 previous_block_hash,
1213 next_block_hash,
1214 } = *block_header;
1215
1216 let transactions_request = match verbosity {
1217 1 => zebra_state::ReadRequest::TransactionIdsForBlock(hash_or_height),
1218 2 => zebra_state::ReadRequest::BlockAndSize(hash_or_height),
1219 _other => panic!("get_block_header_fut should be none"),
1220 };
1221
1222 let hash_or_height = hash.into();
1227 let requests = vec![
1228 transactions_request,
1236 zebra_state::ReadRequest::OrchardTree(hash_or_height),
1238 zebra_state::ReadRequest::BlockInfo(previous_block_hash.into()),
1240 zebra_state::ReadRequest::BlockInfo(hash_or_height),
1241 ];
1242
1243 let mut futs = FuturesOrdered::new();
1244
1245 for request in requests {
1246 futs.push_back(self.read_state.clone().oneshot(request));
1247 }
1248
1249 let tx_ids_response = futs.next().await.expect("`futs` should not be empty");
1250 let (tx, size): (Vec<_>, Option<usize>) = match tx_ids_response.map_misc_error()? {
1251 zebra_state::ReadResponse::TransactionIdsForBlock(tx_ids) => (
1252 tx_ids
1253 .ok_or_misc_error("block not found")?
1254 .iter()
1255 .map(|tx_id| GetBlockTransaction::Hash(*tx_id))
1256 .collect(),
1257 None,
1258 ),
1259 zebra_state::ReadResponse::BlockAndSize(block_and_size) => {
1260 let (block, size) = block_and_size.ok_or_misc_error("Block not found")?;
1261 let block_time = block.header.time;
1262 let transactions =
1263 block
1264 .transactions
1265 .iter()
1266 .map(|tx| {
1267 GetBlockTransaction::Object(Box::new(
1268 TransactionObject::from_transaction(
1269 tx.clone(),
1270 Some(height),
1271 Some(confirmations.try_into().expect(
1272 "should be less than max block height, i32::MAX",
1273 )),
1274 &network,
1275 Some(block_time),
1276 Some(hash),
1277 Some(true),
1278 tx.hash(),
1279 ),
1280 ))
1281 })
1282 .collect();
1283 (transactions, Some(size))
1284 }
1285 _ => unreachable!("unmatched response to a transaction_ids_for_block request"),
1286 };
1287
1288 let orchard_tree_response = futs.next().await.expect("`futs` should not be empty");
1289 let zebra_state::ReadResponse::OrchardTree(orchard_tree) =
1290 orchard_tree_response.map_misc_error()?
1291 else {
1292 unreachable!("unmatched response to a OrchardTree request");
1293 };
1294
1295 let nu5_activation = NetworkUpgrade::Nu5.activation_height(&network);
1296
1297 let orchard_tree = orchard_tree.ok_or_misc_error("missing Orchard tree")?;
1299
1300 let final_orchard_root = match nu5_activation {
1301 Some(activation_height) if height >= activation_height => {
1302 Some(orchard_tree.root().into())
1303 }
1304 _other => None,
1305 };
1306
1307 let sapling = SaplingTrees {
1308 size: sapling_tree_size,
1309 };
1310
1311 let orchard_tree_size = orchard_tree.count();
1312 let orchard = OrchardTrees {
1313 size: orchard_tree_size,
1314 };
1315
1316 let trees = GetBlockTrees { sapling, orchard };
1317
1318 let block_info_response = futs.next().await.expect("`futs` should not be empty");
1319 let zebra_state::ReadResponse::BlockInfo(prev_block_info) =
1320 block_info_response.map_misc_error()?
1321 else {
1322 unreachable!("unmatched response to a BlockInfo request");
1323 };
1324 let block_info_response = futs.next().await.expect("`futs` should not be empty");
1325 let zebra_state::ReadResponse::BlockInfo(block_info) =
1326 block_info_response.map_misc_error()?
1327 else {
1328 unreachable!("unmatched response to a BlockInfo request");
1329 };
1330
1331 let delta = block_info.as_ref().and_then(|d| {
1332 let value_pools = d.value_pools().constrain::<NegativeAllowed>().ok()?;
1333 let prev_value_pools = prev_block_info
1334 .map(|d| d.value_pools().constrain::<NegativeAllowed>())
1335 .unwrap_or(Ok(ValueBalance::<NegativeAllowed>::zero()))
1336 .ok()?;
1337 (value_pools - prev_value_pools).ok()
1338 });
1339 let size = size.or(block_info.as_ref().map(|d| d.size() as usize));
1340
1341 Ok(GetBlockResponse::Object(Box::new(BlockObject {
1342 hash,
1343 confirmations,
1344 height: Some(height),
1345 version: Some(version),
1346 merkle_root: Some(merkle_root),
1347 time: Some(time),
1348 nonce: Some(nonce),
1349 solution: Some(solution),
1350 bits: Some(bits),
1351 difficulty: Some(difficulty),
1352 tx,
1353 trees,
1354 chain_supply: block_info
1355 .as_ref()
1356 .map(|d| GetBlockchainInfoBalance::chain_supply(*d.value_pools())),
1357 value_pools: block_info
1358 .map(|d| GetBlockchainInfoBalance::value_pools(*d.value_pools(), delta)),
1359 size: size.map(|size| size as i64),
1360 block_commitments: Some(block_commitments),
1361 final_sapling_root: Some(final_sapling_root),
1362 final_orchard_root,
1363 previous_block_hash: Some(previous_block_hash),
1364 next_block_hash,
1365 })))
1366 } else {
1367 Err("invalid verbosity value").map_error(server::error::LegacyCode::InvalidParameter)
1368 }
1369 }
1370
1371 async fn get_block_header(
1372 &self,
1373 hash_or_height: String,
1374 verbose: Option<bool>,
1375 ) -> Result<GetBlockHeaderResponse> {
1376 let verbose = verbose.unwrap_or(true);
1377 let network = self.network.clone();
1378
1379 let hash_or_height =
1380 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1381 .map_error(server::error::LegacyCode::InvalidParameter)?;
1384 let zebra_state::ReadResponse::BlockHeader {
1385 header,
1386 hash,
1387 height,
1388 next_block_hash,
1389 } = self
1390 .read_state
1391 .clone()
1392 .oneshot(zebra_state::ReadRequest::BlockHeader(hash_or_height))
1393 .await
1394 .map_err(|_| "block height not in best chain")
1395 .map_error(
1396 if hash_or_height.hash().is_some() {
1401 server::error::LegacyCode::InvalidAddressOrKey
1402 } else {
1403 server::error::LegacyCode::InvalidParameter
1404 },
1405 )?
1406 else {
1407 panic!("unexpected response to BlockHeader request")
1408 };
1409
1410 let response = if !verbose {
1411 GetBlockHeaderResponse::Raw(HexData(header.zcash_serialize_to_vec().map_misc_error()?))
1412 } else {
1413 let zebra_state::ReadResponse::SaplingTree(sapling_tree) = self
1414 .read_state
1415 .clone()
1416 .oneshot(zebra_state::ReadRequest::SaplingTree(hash_or_height))
1417 .await
1418 .map_misc_error()?
1419 else {
1420 panic!("unexpected response to SaplingTree request")
1421 };
1422
1423 let sapling_tree = sapling_tree.ok_or_misc_error("missing Sapling tree")?;
1425
1426 let zebra_state::ReadResponse::Depth(depth) = self
1427 .read_state
1428 .clone()
1429 .oneshot(zebra_state::ReadRequest::Depth(hash))
1430 .await
1431 .map_misc_error()?
1432 else {
1433 panic!("unexpected response to SaplingTree request")
1434 };
1435
1436 const NOT_IN_BEST_CHAIN_CONFIRMATIONS: i64 = -1;
1439
1440 let confirmations = depth
1443 .map(|depth| i64::from(depth) + 1)
1444 .unwrap_or(NOT_IN_BEST_CHAIN_CONFIRMATIONS);
1445
1446 let mut nonce = *header.nonce;
1447 nonce.reverse();
1448
1449 let sapling_activation = NetworkUpgrade::Sapling.activation_height(&network);
1450 let sapling_tree_size = sapling_tree.count();
1451 let final_sapling_root: [u8; 32] =
1452 if sapling_activation.is_some() && height >= sapling_activation.unwrap() {
1453 let mut root: [u8; 32] = sapling_tree.root().into();
1454 root.reverse();
1455 root
1456 } else {
1457 [0; 32]
1458 };
1459
1460 let difficulty = header.difficulty_threshold.relative_to_network(&network);
1461
1462 let block_commitments = match header.commitment(&network, height).expect(
1463 "Unexpected failure while parsing the blockcommitments field in get_block_header",
1464 ) {
1465 Commitment::PreSaplingReserved(bytes) => bytes,
1466 Commitment::FinalSaplingRoot(_) => final_sapling_root,
1467 Commitment::ChainHistoryActivationReserved => [0; 32],
1468 Commitment::ChainHistoryRoot(root) => root.bytes_in_display_order(),
1469 Commitment::ChainHistoryBlockTxAuthCommitment(hash) => {
1470 hash.bytes_in_display_order()
1471 }
1472 };
1473
1474 let block_header = BlockHeaderObject {
1475 hash,
1476 confirmations,
1477 height,
1478 version: header.version,
1479 merkle_root: header.merkle_root,
1480 block_commitments,
1481 final_sapling_root,
1482 sapling_tree_size,
1483 time: header.time.timestamp(),
1484 nonce,
1485 solution: header.solution,
1486 bits: header.difficulty_threshold,
1487 difficulty,
1488 previous_block_hash: header.previous_block_hash,
1489 next_block_hash,
1490 };
1491
1492 GetBlockHeaderResponse::Object(Box::new(block_header))
1493 };
1494
1495 Ok(response)
1496 }
1497
1498 fn get_best_block_hash(&self) -> Result<GetBlockHashResponse> {
1499 self.latest_chain_tip
1500 .best_tip_hash()
1501 .map(GetBlockHashResponse)
1502 .ok_or_misc_error("No blocks in state")
1503 }
1504
1505 fn get_best_block_height_and_hash(&self) -> Result<GetBlockHeightAndHashResponse> {
1506 self.latest_chain_tip
1507 .best_tip_height_and_hash()
1508 .map(|(height, hash)| GetBlockHeightAndHashResponse { height, hash })
1509 .ok_or_misc_error("No blocks in state")
1510 }
1511
1512 async fn get_mempool_info(&self) -> Result<GetMempoolInfoResponse> {
1513 let mut mempool = self.mempool.clone();
1514
1515 let response = mempool
1516 .ready()
1517 .and_then(|service| service.call(mempool::Request::QueueStats))
1518 .await
1519 .map_misc_error()?;
1520
1521 if let mempool::Response::QueueStats {
1522 size,
1523 bytes,
1524 usage,
1525 fully_notified,
1526 } = response
1527 {
1528 Ok(GetMempoolInfoResponse {
1529 size,
1530 bytes,
1531 usage,
1532 fully_notified,
1533 })
1534 } else {
1535 unreachable!("unexpected response to QueueStats request")
1536 }
1537 }
1538
1539 async fn get_raw_mempool(&self, verbose: Option<bool>) -> Result<GetRawMempoolResponse> {
1540 #[allow(unused)]
1541 let verbose = verbose.unwrap_or(false);
1542
1543 use zebra_chain::block::MAX_BLOCK_BYTES;
1544
1545 let mut mempool = self.mempool.clone();
1546
1547 let request = if verbose {
1548 mempool::Request::FullTransactions
1549 } else {
1550 mempool::Request::TransactionIds
1551 };
1552
1553 let response = mempool
1555 .ready()
1556 .and_then(|service| service.call(request))
1557 .await
1558 .map_misc_error()?;
1559
1560 match response {
1561 mempool::Response::FullTransactions {
1562 mut transactions,
1563 transaction_dependencies,
1564 last_seen_tip_hash: _,
1565 } => {
1566 if verbose {
1567 let map = transactions
1568 .iter()
1569 .map(|unmined_tx| {
1570 (
1571 unmined_tx.transaction.id.mined_id().encode_hex(),
1572 get_raw_mempool::MempoolObject::from_verified_unmined_tx(
1573 unmined_tx,
1574 &transactions,
1575 &transaction_dependencies,
1576 ),
1577 )
1578 })
1579 .collect::<HashMap<_, _>>();
1580 Ok(GetRawMempoolResponse::Verbose(map))
1581 } else {
1582 transactions.sort_by_cached_key(|tx| {
1587 cmp::Reverse((
1590 i64::from(tx.miner_fee) as u128 * MAX_BLOCK_BYTES as u128
1591 / tx.transaction.size as u128,
1592 tx.transaction.id.mined_id(),
1594 ))
1595 });
1596 let tx_ids: Vec<String> = transactions
1597 .iter()
1598 .map(|unmined_tx| unmined_tx.transaction.id.mined_id().encode_hex())
1599 .collect();
1600
1601 Ok(GetRawMempoolResponse::TxIds(tx_ids))
1602 }
1603 }
1604
1605 mempool::Response::TransactionIds(unmined_transaction_ids) => {
1606 let mut tx_ids: Vec<String> = unmined_transaction_ids
1607 .iter()
1608 .map(|id| id.mined_id().encode_hex())
1609 .collect();
1610
1611 tx_ids.sort();
1613
1614 Ok(GetRawMempoolResponse::TxIds(tx_ids))
1615 }
1616
1617 _ => unreachable!("unmatched response to a transactionids request"),
1618 }
1619 }
1620
1621 async fn get_raw_transaction(
1622 &self,
1623 txid: String,
1624 verbose: Option<u8>,
1625 block_hash: Option<String>,
1626 ) -> Result<GetRawTransactionResponse> {
1627 let mut mempool = self.mempool.clone();
1628 let verbose = verbose.unwrap_or(0) != 0;
1629
1630 let txid = transaction::Hash::from_hex(txid)
1633 .map_error(server::error::LegacyCode::InvalidAddressOrKey)?;
1634
1635 if block_hash.is_none() {
1637 match mempool
1638 .ready()
1639 .and_then(|service| {
1640 service.call(mempool::Request::TransactionsByMinedId([txid].into()))
1641 })
1642 .await
1643 .map_misc_error()?
1644 {
1645 mempool::Response::Transactions(txns) => {
1646 if let Some(tx) = txns.first() {
1647 return Ok(if verbose {
1648 GetRawTransactionResponse::Object(Box::new(
1649 TransactionObject::from_transaction(
1650 tx.transaction.clone(),
1651 None,
1652 None,
1653 &self.network,
1654 None,
1655 None,
1656 Some(false),
1657 txid,
1658 ),
1659 ))
1660 } else {
1661 let hex = tx.transaction.clone().into();
1662 GetRawTransactionResponse::Raw(hex)
1663 });
1664 }
1665 }
1666
1667 _ => unreachable!("unmatched response to a `TransactionsByMinedId` request"),
1668 };
1669 }
1670
1671 let txid = if let Some(block_hash) = block_hash {
1672 let block_hash = block::Hash::from_hex(block_hash)
1673 .map_error(server::error::LegacyCode::InvalidAddressOrKey)?;
1674 match self
1675 .read_state
1676 .clone()
1677 .oneshot(zebra_state::ReadRequest::AnyChainTransactionIdsForBlock(
1678 block_hash.into(),
1679 ))
1680 .await
1681 .map_misc_error()?
1682 {
1683 zebra_state::ReadResponse::AnyChainTransactionIdsForBlock(tx_ids) => *tx_ids
1684 .ok_or_error(
1685 server::error::LegacyCode::InvalidAddressOrKey,
1686 "block not found",
1687 )?
1688 .0
1689 .iter()
1690 .find(|id| **id == txid)
1691 .ok_or_error(
1692 server::error::LegacyCode::InvalidAddressOrKey,
1693 "txid not found",
1694 )?,
1695 _ => {
1696 unreachable!("unmatched response to a `AnyChainTransactionIdsForBlock` request")
1697 }
1698 }
1699 } else {
1700 txid
1701 };
1702
1703 match self
1705 .read_state
1706 .clone()
1707 .oneshot(zebra_state::ReadRequest::AnyChainTransaction(txid))
1708 .await
1709 .map_misc_error()?
1710 {
1711 zebra_state::ReadResponse::AnyChainTransaction(Some(tx)) => Ok(if verbose {
1712 match tx {
1713 AnyTx::Mined(tx) => {
1714 let block_hash = match self
1715 .read_state
1716 .clone()
1717 .oneshot(zebra_state::ReadRequest::BestChainBlockHash(tx.height))
1718 .await
1719 .map_misc_error()?
1720 {
1721 zebra_state::ReadResponse::BlockHash(block_hash) => block_hash,
1722 _ => {
1723 unreachable!("unmatched response to a `BestChainBlockHash` request")
1724 }
1725 };
1726
1727 GetRawTransactionResponse::Object(Box::new(
1728 TransactionObject::from_transaction(
1729 tx.tx.clone(),
1730 Some(tx.height),
1731 Some(tx.confirmations),
1732 &self.network,
1733 Some(tx.block_time),
1736 block_hash,
1737 Some(true),
1738 txid,
1739 ),
1740 ))
1741 }
1742 AnyTx::Side((tx, block_hash)) => GetRawTransactionResponse::Object(Box::new(
1743 TransactionObject::from_transaction(
1744 tx.clone(),
1745 None,
1746 None,
1747 &self.network,
1748 None,
1749 Some(block_hash),
1750 Some(false),
1751 txid,
1752 ),
1753 )),
1754 }
1755 } else {
1756 let tx: Arc<Transaction> = tx.into();
1757 let hex = tx.into();
1758 GetRawTransactionResponse::Raw(hex)
1759 }),
1760
1761 zebra_state::ReadResponse::AnyChainTransaction(None) => {
1762 Err("No such mempool or main chain transaction")
1763 .map_error(server::error::LegacyCode::InvalidAddressOrKey)
1764 }
1765
1766 _ => unreachable!("unmatched response to a `Transaction` read request"),
1767 }
1768 }
1769
1770 async fn z_get_treestate(&self, hash_or_height: String) -> Result<GetTreestateResponse> {
1771 let mut read_state = self.read_state.clone();
1772 let network = self.network.clone();
1773
1774 let hash_or_height =
1775 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1776 .map_error(server::error::LegacyCode::InvalidParameter)?;
1779
1780 let block = match read_state
1789 .ready()
1790 .and_then(|service| service.call(zebra_state::ReadRequest::Block(hash_or_height)))
1791 .await
1792 .map_misc_error()?
1793 {
1794 zebra_state::ReadResponse::Block(Some(block)) => block,
1795 zebra_state::ReadResponse::Block(None) => {
1796 return Err("the requested block is not in the main chain")
1799 .map_error(server::error::LegacyCode::InvalidParameter);
1800 }
1801 _ => unreachable!("unmatched response to a block request"),
1802 };
1803
1804 let hash = hash_or_height
1805 .hash_or_else(|_| Some(block.hash()))
1806 .expect("block hash");
1807
1808 let height = hash_or_height
1809 .height_or_else(|_| block.coinbase_height())
1810 .expect("verified blocks have a coinbase height");
1811
1812 let time = u32::try_from(block.header.time.timestamp())
1813 .expect("Timestamps of valid blocks always fit into u32.");
1814
1815 let sapling_nu = zcash_primitives::consensus::NetworkUpgrade::Sapling;
1816 let sapling = if network.is_nu_active(sapling_nu, height.into()) {
1817 match read_state
1818 .ready()
1819 .and_then(|service| {
1820 service.call(zebra_state::ReadRequest::SaplingTree(hash.into()))
1821 })
1822 .await
1823 .map_misc_error()?
1824 {
1825 zebra_state::ReadResponse::SaplingTree(tree) => {
1826 tree.map(|t| (t.to_rpc_bytes(), t.root().bytes_in_display_order().to_vec()))
1827 }
1828 _ => unreachable!("unmatched response to a Sapling tree request"),
1829 }
1830 } else {
1831 None
1832 };
1833 let (sapling_tree, sapling_root) =
1834 sapling.map_or((None, None), |(tree, root)| (Some(tree), Some(root)));
1835
1836 let orchard_nu = zcash_primitives::consensus::NetworkUpgrade::Nu5;
1837 let orchard = if network.is_nu_active(orchard_nu, height.into()) {
1838 match read_state
1839 .ready()
1840 .and_then(|service| {
1841 service.call(zebra_state::ReadRequest::OrchardTree(hash.into()))
1842 })
1843 .await
1844 .map_misc_error()?
1845 {
1846 zebra_state::ReadResponse::OrchardTree(tree) => {
1847 tree.map(|t| (t.to_rpc_bytes(), t.root().bytes_in_display_order().to_vec()))
1848 }
1849 _ => unreachable!("unmatched response to an Orchard tree request"),
1850 }
1851 } else {
1852 None
1853 };
1854 let (orchard_tree, orchard_root) =
1855 orchard.map_or((None, None), |(tree, root)| (Some(tree), Some(root)));
1856
1857 Ok(GetTreestateResponse::new(
1858 hash,
1859 height,
1860 time,
1861 None,
1864 Treestate::new(trees::Commitments::new(sapling_root, sapling_tree)),
1865 Treestate::new(trees::Commitments::new(orchard_root, orchard_tree)),
1866 ))
1867 }
1868
1869 async fn z_get_subtrees_by_index(
1870 &self,
1871 pool: String,
1872 start_index: NoteCommitmentSubtreeIndex,
1873 limit: Option<NoteCommitmentSubtreeIndex>,
1874 ) -> Result<GetSubtreesByIndexResponse> {
1875 let mut read_state = self.read_state.clone();
1876
1877 const POOL_LIST: &[&str] = &["sapling", "orchard"];
1878
1879 if pool == "sapling" {
1880 let request = zebra_state::ReadRequest::SaplingSubtrees { start_index, limit };
1881 let response = read_state
1882 .ready()
1883 .and_then(|service| service.call(request))
1884 .await
1885 .map_misc_error()?;
1886
1887 let subtrees = match response {
1888 zebra_state::ReadResponse::SaplingSubtrees(subtrees) => subtrees,
1889 _ => unreachable!("unmatched response to a subtrees request"),
1890 };
1891
1892 let subtrees = subtrees
1893 .values()
1894 .map(|subtree| SubtreeRpcData {
1895 root: subtree.root.to_bytes().encode_hex(),
1896 end_height: subtree.end_height,
1897 })
1898 .collect();
1899
1900 Ok(GetSubtreesByIndexResponse {
1901 pool,
1902 start_index,
1903 subtrees,
1904 })
1905 } else if pool == "orchard" {
1906 let request = zebra_state::ReadRequest::OrchardSubtrees { start_index, limit };
1907 let response = read_state
1908 .ready()
1909 .and_then(|service| service.call(request))
1910 .await
1911 .map_misc_error()?;
1912
1913 let subtrees = match response {
1914 zebra_state::ReadResponse::OrchardSubtrees(subtrees) => subtrees,
1915 _ => unreachable!("unmatched response to a subtrees request"),
1916 };
1917
1918 let subtrees = subtrees
1919 .values()
1920 .map(|subtree| SubtreeRpcData {
1921 root: subtree.root.encode_hex(),
1922 end_height: subtree.end_height,
1923 })
1924 .collect();
1925
1926 Ok(GetSubtreesByIndexResponse {
1927 pool,
1928 start_index,
1929 subtrees,
1930 })
1931 } else {
1932 Err(ErrorObject::owned(
1933 server::error::LegacyCode::Misc.into(),
1934 format!("invalid pool name, must be one of: {POOL_LIST:?}").as_str(),
1935 None::<()>,
1936 ))
1937 }
1938 }
1939
1940 async fn get_address_tx_ids(&self, request: GetAddressTxIdsRequest) -> Result<Vec<String>> {
1941 let mut read_state = self.read_state.clone();
1942 let latest_chain_tip = self.latest_chain_tip.clone();
1943
1944 let height_range = build_height_range(
1945 request.start,
1946 request.end,
1947 best_chain_tip_height(&latest_chain_tip)?,
1948 )?;
1949
1950 let valid_addresses = request.valid_addresses()?;
1951
1952 let request = zebra_state::ReadRequest::TransactionIdsByAddresses {
1953 addresses: valid_addresses,
1954 height_range,
1955 };
1956 let response = read_state
1957 .ready()
1958 .and_then(|service| service.call(request))
1959 .await
1960 .map_misc_error()?;
1961
1962 let hashes = match response {
1963 zebra_state::ReadResponse::AddressesTransactionIds(hashes) => {
1964 let mut last_tx_location = TransactionLocation::from_usize(Height(0), 0);
1965
1966 hashes
1967 .iter()
1968 .map(|(tx_loc, tx_id)| {
1969 assert!(
1971 *tx_loc > last_tx_location,
1972 "Transactions were not in chain order:\n\
1973 {tx_loc:?} {tx_id:?} was after:\n\
1974 {last_tx_location:?}",
1975 );
1976
1977 last_tx_location = *tx_loc;
1978
1979 tx_id.to_string()
1980 })
1981 .collect()
1982 }
1983 _ => unreachable!("unmatched response to a TransactionsByAddresses request"),
1984 };
1985
1986 Ok(hashes)
1987 }
1988
1989 async fn get_address_utxos(
1990 &self,
1991 utxos_request: GetAddressUtxosRequest,
1992 ) -> Result<GetAddressUtxosResponse> {
1993 let mut read_state = self.read_state.clone();
1994 let mut response_utxos = vec![];
1995
1996 let valid_addresses = utxos_request.valid_addresses()?;
1997
1998 let request = zebra_state::ReadRequest::UtxosByAddresses(valid_addresses);
2000 let response = read_state
2001 .ready()
2002 .and_then(|service| service.call(request))
2003 .await
2004 .map_misc_error()?;
2005 let utxos = match response {
2006 zebra_state::ReadResponse::AddressUtxos(utxos) => utxos,
2007 _ => unreachable!("unmatched response to a UtxosByAddresses request"),
2008 };
2009
2010 let mut last_output_location = OutputLocation::from_usize(Height(0), 0, 0);
2011
2012 for utxo_data in utxos.utxos() {
2013 let address = utxo_data.0;
2014 let txid = *utxo_data.1;
2015 let height = utxo_data.2.height();
2016 let output_index = utxo_data.2.output_index();
2017 let script = utxo_data.3.lock_script.clone();
2018 let satoshis = u64::from(utxo_data.3.value);
2019
2020 let output_location = *utxo_data.2;
2021 assert!(
2023 output_location > last_output_location,
2024 "UTXOs were not in chain order:\n\
2025 {output_location:?} {address:?} {txid:?} was after:\n\
2026 {last_output_location:?}",
2027 );
2028
2029 let entry = Utxo {
2030 address,
2031 txid,
2032 output_index,
2033 script,
2034 satoshis,
2035 height,
2036 };
2037 response_utxos.push(entry);
2038
2039 last_output_location = output_location;
2040 }
2041
2042 if !utxos_request.chain_info {
2043 Ok(GetAddressUtxosResponse::Utxos(response_utxos))
2044 } else {
2045 let (height, hash) = utxos
2046 .last_height_and_hash()
2047 .ok_or_misc_error("No blocks in state")?;
2048
2049 Ok(GetAddressUtxosResponse::UtxosAndChainInfo(
2050 GetAddressUtxosResponseObject {
2051 utxos: response_utxos,
2052 hash,
2053 height,
2054 },
2055 ))
2056 }
2057 }
2058
2059 fn stop(&self) -> Result<String> {
2060 #[cfg(not(target_os = "windows"))]
2061 if self.network.is_regtest() {
2062 match nix::sys::signal::raise(nix::sys::signal::SIGINT) {
2063 Ok(_) => Ok("Zebra server stopping".to_string()),
2064 Err(error) => Err(ErrorObject::owned(
2065 ErrorCode::InternalError.code(),
2066 format!("Failed to shut down: {error}").as_str(),
2067 None::<()>,
2068 )),
2069 }
2070 } else {
2071 Err(ErrorObject::borrowed(
2072 ErrorCode::MethodNotFound.code(),
2073 "stop is only available on regtest networks",
2074 None,
2075 ))
2076 }
2077 #[cfg(target_os = "windows")]
2078 Err(ErrorObject::borrowed(
2079 ErrorCode::MethodNotFound.code(),
2080 "stop is not available in windows targets",
2081 None,
2082 ))
2083 }
2084
2085 fn get_block_count(&self) -> Result<u32> {
2086 best_chain_tip_height(&self.latest_chain_tip).map(|height| height.0)
2087 }
2088
2089 async fn get_block_hash(&self, index: i32) -> Result<GetBlockHashResponse> {
2090 let mut read_state = self.read_state.clone();
2091 let latest_chain_tip = self.latest_chain_tip.clone();
2092
2093 let tip_height = best_chain_tip_height(&latest_chain_tip)?;
2095
2096 let height = height_from_signed_int(index, tip_height)?;
2097
2098 let request = zebra_state::ReadRequest::BestChainBlockHash(height);
2099 let response = read_state
2100 .ready()
2101 .and_then(|service| service.call(request))
2102 .await
2103 .map_error(server::error::LegacyCode::default())?;
2104
2105 match response {
2106 zebra_state::ReadResponse::BlockHash(Some(hash)) => Ok(GetBlockHashResponse(hash)),
2107 zebra_state::ReadResponse::BlockHash(None) => Err(ErrorObject::borrowed(
2108 server::error::LegacyCode::InvalidParameter.into(),
2109 "Block not found",
2110 None,
2111 )),
2112 _ => unreachable!("unmatched response to a block request"),
2113 }
2114 }
2115
2116 async fn get_block_template(
2117 &self,
2118 parameters: Option<GetBlockTemplateParameters>,
2119 ) -> Result<GetBlockTemplateResponse> {
2120 use types::get_block_template::{
2121 check_parameters, check_synced_to_tip, fetch_mempool_transactions,
2122 fetch_state_tip_and_local_time, validate_block_proposal,
2123 zip317::select_mempool_transactions,
2124 };
2125
2126 let network = self.network.clone();
2128 let extra_coinbase_data = self.gbt.extra_coinbase_data();
2129
2130 let mempool = self.mempool.clone();
2132 let mut latest_chain_tip = self.latest_chain_tip.clone();
2133 let sync_status = self.gbt.sync_status();
2134 let read_state = self.read_state.clone();
2135
2136 if let Some(HexData(block_proposal_bytes)) = parameters
2137 .as_ref()
2138 .and_then(GetBlockTemplateParameters::block_proposal_data)
2139 {
2140 return validate_block_proposal(
2141 self.gbt.block_verifier_router(),
2142 block_proposal_bytes,
2143 network,
2144 latest_chain_tip,
2145 sync_status,
2146 )
2147 .await;
2148 }
2149
2150 check_parameters(¶meters)?;
2152
2153 let client_long_poll_id = parameters.as_ref().and_then(|params| params.long_poll_id);
2154
2155 let miner_address = self
2156 .gbt
2157 .miner_address()
2158 .ok_or_misc_error("miner_address not configured")?;
2159
2160 let mut max_time_reached = false;
2164
2165 let (
2168 server_long_poll_id,
2169 chain_tip_and_local_time,
2170 mempool_txs,
2171 mempool_tx_deps,
2172 submit_old,
2173 ) = loop {
2174 check_synced_to_tip(&network, latest_chain_tip.clone(), sync_status.clone())?;
2180 latest_chain_tip.mark_best_tip_seen();
2188
2189 let chain_tip_and_local_time @ zebra_state::GetBlockTemplateChainInfo {
2196 tip_hash,
2197 tip_height,
2198 max_time,
2199 cur_time,
2200 ..
2201 } = fetch_state_tip_and_local_time(read_state.clone()).await?;
2202
2203 let Some((mempool_txs, mempool_tx_deps)) =
2214 fetch_mempool_transactions(mempool.clone(), tip_hash)
2215 .await?
2216 .or_else(|| client_long_poll_id.is_none().then(Default::default))
2220 else {
2221 continue;
2222 };
2223
2224 let server_long_poll_id = LongPollInput::new(
2226 tip_height,
2227 tip_hash,
2228 max_time,
2229 mempool_txs.iter().map(|tx| tx.transaction.id),
2230 )
2231 .generate_id();
2232
2233 if Some(&server_long_poll_id) != client_long_poll_id.as_ref() || max_time_reached {
2238 let mut submit_old = client_long_poll_id
2239 .as_ref()
2240 .map(|old_long_poll_id| server_long_poll_id.submit_old(old_long_poll_id));
2241
2242 if max_time_reached {
2247 submit_old = Some(false);
2248 }
2249
2250 break (
2251 server_long_poll_id,
2252 chain_tip_and_local_time,
2253 mempool_txs,
2254 mempool_tx_deps,
2255 submit_old,
2256 );
2257 }
2258
2259 let wait_for_mempool_request =
2269 tokio::time::sleep(Duration::from_secs(MEMPOOL_LONG_POLL_INTERVAL));
2270
2271 let mut wait_for_best_tip_change = latest_chain_tip.clone();
2274 let wait_for_best_tip_change = wait_for_best_tip_change.best_tip_changed();
2275
2276 let duration_until_max_time = max_time.saturating_duration_since(cur_time);
2288 let wait_for_max_time: OptionFuture<_> = if duration_until_max_time.seconds() > 0 {
2289 Some(tokio::time::sleep(duration_until_max_time.to_std()))
2290 } else {
2291 None
2292 }
2293 .into();
2294
2295 tokio::select! {
2302 biased;
2305
2306 _elapsed = wait_for_mempool_request => {
2308 tracing::debug!(
2309 ?max_time,
2310 ?cur_time,
2311 ?server_long_poll_id,
2312 ?client_long_poll_id,
2313 MEMPOOL_LONG_POLL_INTERVAL,
2314 "checking for a new mempool change after waiting a few seconds"
2315 );
2316 }
2317
2318 tip_changed_result = wait_for_best_tip_change => {
2320 match tip_changed_result {
2321 Ok(()) => {
2322 latest_chain_tip.mark_best_tip_seen();
2326
2327 let new_tip_hash = latest_chain_tip.best_tip_hash();
2328 if new_tip_hash == Some(tip_hash) {
2329 tracing::debug!(
2330 ?max_time,
2331 ?cur_time,
2332 ?server_long_poll_id,
2333 ?client_long_poll_id,
2334 ?tip_hash,
2335 ?tip_height,
2336 "ignoring spurious state change notification"
2337 );
2338
2339 tokio::time::sleep(Duration::from_secs(
2341 MEMPOOL_LONG_POLL_INTERVAL,
2342 )).await;
2343
2344 continue;
2345 }
2346
2347 tracing::debug!(
2348 ?max_time,
2349 ?cur_time,
2350 ?server_long_poll_id,
2351 ?client_long_poll_id,
2352 "returning from long poll because state has changed"
2353 );
2354 }
2355
2356 Err(recv_error) => {
2357 tracing::info!(
2359 ?recv_error,
2360 ?max_time,
2361 ?cur_time,
2362 ?server_long_poll_id,
2363 ?client_long_poll_id,
2364 "returning from long poll due to a state error.\
2365 Is Zebra shutting down?"
2366 );
2367
2368 return Err(recv_error).map_error(server::error::LegacyCode::default());
2369 }
2370 }
2371 }
2372
2373 Some(_elapsed) = wait_for_max_time => {
2376 tracing::info!(
2378 ?max_time,
2379 ?cur_time,
2380 ?server_long_poll_id,
2381 ?client_long_poll_id,
2382 "returning from long poll because max time was reached"
2383 );
2384
2385 max_time_reached = true;
2386 }
2387 }
2388 };
2389
2390 let next_block_height =
2398 (chain_tip_and_local_time.tip_height + 1).expect("tip is far below Height::MAX");
2399
2400 tracing::debug!(
2401 mempool_tx_hashes = ?mempool_txs
2402 .iter()
2403 .map(|tx| tx.transaction.id.mined_id())
2404 .collect::<Vec<_>>(),
2405 "selecting transactions for the template from the mempool"
2406 );
2407
2408 let mempool_txs = select_mempool_transactions(
2410 &network,
2411 next_block_height,
2412 &miner_address,
2413 mempool_txs,
2414 mempool_tx_deps,
2415 extra_coinbase_data.clone(),
2416 #[cfg(all(zcash_unstable = "nu7", feature = "tx_v6"))]
2417 None,
2418 );
2419
2420 tracing::debug!(
2421 selected_mempool_tx_hashes = ?mempool_txs
2422 .iter()
2423 .map(|#[cfg(not(test))] tx, #[cfg(test)] (_, tx)| tx.transaction.id.mined_id())
2424 .collect::<Vec<_>>(),
2425 "selected transactions for the template from the mempool"
2426 );
2427
2428 let response = BlockTemplateResponse::new_internal(
2431 &network,
2432 &miner_address,
2433 &chain_tip_and_local_time,
2434 server_long_poll_id,
2435 mempool_txs,
2436 submit_old,
2437 extra_coinbase_data,
2438 #[cfg(all(zcash_unstable = "nu7", feature = "tx_v6"))]
2439 None,
2440 );
2441
2442 Ok(response.into())
2443 }
2444
2445 async fn submit_block(
2446 &self,
2447 HexData(block_bytes): HexData,
2448 _parameters: Option<SubmitBlockParameters>,
2449 ) -> Result<SubmitBlockResponse> {
2450 let mut block_verifier_router = self.gbt.block_verifier_router();
2451
2452 let block: Block = match block_bytes.zcash_deserialize_into() {
2453 Ok(block_bytes) => block_bytes,
2454 Err(error) => {
2455 tracing::info!(
2456 ?error,
2457 "submit block failed: block bytes could not be deserialized into a structurally valid block"
2458 );
2459
2460 return Ok(SubmitBlockErrorResponse::Rejected.into());
2461 }
2462 };
2463
2464 let height = block
2465 .coinbase_height()
2466 .ok_or_error(0, "coinbase height not found")?;
2467 let block_hash = block.hash();
2468
2469 let block_verifier_router_response = block_verifier_router
2470 .ready()
2471 .await
2472 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?
2473 .call(zebra_consensus::Request::Commit(Arc::new(block)))
2474 .await;
2475
2476 let chain_error = match block_verifier_router_response {
2477 Ok(hash) => {
2484 tracing::info!(?hash, ?height, "submit block accepted");
2485
2486 self.gbt
2487 .advertise_mined_block(hash, height)
2488 .map_error_with_prefix(0, "failed to send mined block to gossip task")?;
2489
2490 return Ok(SubmitBlockResponse::Accepted);
2491 }
2492
2493 Err(box_error) => {
2496 let error = box_error
2497 .downcast::<RouterError>()
2498 .map(|boxed_chain_error| *boxed_chain_error);
2499
2500 tracing::info!(
2501 ?error,
2502 ?block_hash,
2503 ?height,
2504 "submit block failed verification"
2505 );
2506
2507 error
2508 }
2509 };
2510
2511 let response = match chain_error {
2512 Ok(source) if source.is_duplicate_request() => SubmitBlockErrorResponse::Duplicate,
2513
2514 Ok(_verify_chain_error) => SubmitBlockErrorResponse::Rejected,
2530
2531 Err(_unknown_error_type) => SubmitBlockErrorResponse::Rejected,
2534 };
2535
2536 Ok(response.into())
2537 }
2538
2539 async fn get_mining_info(&self) -> Result<GetMiningInfoResponse> {
2540 let network = self.network.clone();
2541 let mut read_state = self.read_state.clone();
2542
2543 let chain_tip = self.latest_chain_tip.clone();
2544 let tip_height = chain_tip.best_tip_height().unwrap_or(Height(0)).0;
2545
2546 let mut current_block_tx = None;
2547 if tip_height > 0 {
2548 let mined_tx_ids = chain_tip.best_tip_mined_transaction_ids();
2549 current_block_tx =
2550 (!mined_tx_ids.is_empty()).then(|| mined_tx_ids.len().saturating_sub(1));
2551 }
2552
2553 let solution_rate_fut = self.get_network_sol_ps(None, None);
2554 let mut current_block_size = None;
2556 if tip_height > 0 {
2557 let request = zebra_state::ReadRequest::TipBlockSize;
2558 let response: zebra_state::ReadResponse = read_state
2559 .ready()
2560 .and_then(|service| service.call(request))
2561 .await
2562 .map_error(server::error::LegacyCode::default())?;
2563 current_block_size = match response {
2564 zebra_state::ReadResponse::TipBlockSize(Some(block_size)) => Some(block_size),
2565 _ => None,
2566 };
2567 }
2568
2569 Ok(GetMiningInfoResponse::new_internal(
2570 tip_height,
2571 current_block_size,
2572 current_block_tx,
2573 network,
2574 solution_rate_fut.await?,
2575 ))
2576 }
2577
2578 async fn get_network_sol_ps(
2579 &self,
2580 num_blocks: Option<i32>,
2581 height: Option<i32>,
2582 ) -> Result<u64> {
2583 let mut num_blocks = num_blocks.unwrap_or(DEFAULT_SOLUTION_RATE_WINDOW_SIZE);
2585 if num_blocks < 1 {
2587 num_blocks = i32::try_from(POW_AVERAGING_WINDOW).expect("fits in i32");
2588 }
2589 let num_blocks =
2590 usize::try_from(num_blocks).expect("just checked for negatives, i32 fits in usize");
2591
2592 let height = height.and_then(|height| height.try_into_height().ok());
2595
2596 let mut read_state = self.read_state.clone();
2597
2598 let request = ReadRequest::SolutionRate { num_blocks, height };
2599
2600 let response = read_state
2601 .ready()
2602 .and_then(|service| service.call(request))
2603 .await
2604 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
2605
2606 let solution_rate = match response {
2607 ReadResponse::SolutionRate(solution_rate) => solution_rate.unwrap_or(0),
2609
2610 _ => unreachable!("unmatched response to a solution rate request"),
2611 };
2612
2613 Ok(solution_rate
2614 .try_into()
2615 .expect("per-second solution rate always fits in u64"))
2616 }
2617
2618 async fn get_network_info(&self) -> Result<GetNetworkInfoResponse> {
2619 let version = GetInfoResponse::version_from_string(&self.build_version)
2620 .expect("invalid version string");
2621
2622 let subversion = self.user_agent.clone();
2623
2624 let protocol_version = zebra_network::constants::CURRENT_NETWORK_PROTOCOL_VERSION.0;
2625
2626 let local_services = format!("{:016x}", PeerServices::NODE_NETWORK);
2628
2629 let timeoffset = 0;
2631
2632 let connections = self.address_book.recently_live_peers(Utc::now()).len();
2633
2634 let networks = vec![
2636 NetworkInfo::new("ipv4".to_string(), false, true, "".to_string(), false),
2637 NetworkInfo::new("ipv6".to_string(), false, true, "".to_string(), false),
2638 NetworkInfo::new("onion".to_string(), false, false, "".to_string(), false),
2639 ];
2640
2641 let relay_fee = zebra_chain::transaction::zip317::MIN_MEMPOOL_TX_FEE_RATE as f64
2642 / (zebra_chain::amount::COIN as f64);
2643
2644 let local_addresses = vec![];
2646
2647 let warnings = "".to_string();
2649
2650 let response = GetNetworkInfoResponse {
2651 version,
2652 subversion,
2653 protocol_version,
2654 local_services,
2655 timeoffset,
2656 connections,
2657 networks,
2658 relay_fee,
2659 local_addresses,
2660 warnings,
2661 };
2662
2663 Ok(response)
2664 }
2665
2666 async fn get_peer_info(&self) -> Result<Vec<PeerInfo>> {
2667 let address_book = self.address_book.clone();
2668 Ok(address_book
2669 .recently_live_peers(chrono::Utc::now())
2670 .into_iter()
2671 .map(PeerInfo::from)
2672 .collect())
2673 }
2674
2675 async fn validate_address(&self, raw_address: String) -> Result<ValidateAddressResponse> {
2676 let network = self.network.clone();
2677
2678 validate_address(network, raw_address)
2679 }
2680
2681 async fn z_validate_address(&self, raw_address: String) -> Result<ZValidateAddressResponse> {
2682 let network = self.network.clone();
2683
2684 z_validate_address(network, raw_address)
2685 }
2686
2687 async fn get_block_subsidy(&self, height: Option<u32>) -> Result<GetBlockSubsidyResponse> {
2688 let latest_chain_tip = self.latest_chain_tip.clone();
2689 let network = self.network.clone();
2690
2691 let height = if let Some(height) = height {
2692 Height(height)
2693 } else {
2694 best_chain_tip_height(&latest_chain_tip)?
2695 };
2696
2697 if height < network.height_for_first_halving() {
2698 return Err(ErrorObject::borrowed(
2699 0,
2700 "Zebra does not support founders' reward subsidies, \
2701 use a block height that is after the first halving",
2702 None,
2703 ));
2704 }
2705
2706 let founders = Amount::zero();
2708
2709 let total_block_subsidy =
2710 block_subsidy(height, &network).map_error(server::error::LegacyCode::default())?;
2711 let miner_subsidy = miner_subsidy(height, &network, total_block_subsidy)
2712 .map_error(server::error::LegacyCode::default())?;
2713
2714 let (lockbox_streams, mut funding_streams): (Vec<_>, Vec<_>) =
2715 funding_stream_values(height, &network, total_block_subsidy)
2716 .map_error(server::error::LegacyCode::default())?
2717 .into_iter()
2718 .partition(|(receiver, _)| matches!(receiver, FundingStreamReceiver::Deferred));
2720
2721 let is_nu6 = NetworkUpgrade::current(&network, height) == NetworkUpgrade::Nu6;
2722
2723 let [lockbox_total, funding_streams_total]: [std::result::Result<
2724 Amount<NonNegative>,
2725 amount::Error,
2726 >; 2] = [&lockbox_streams, &funding_streams]
2727 .map(|streams| streams.iter().map(|&(_, amount)| amount).sum());
2728
2729 funding_streams.sort_by_key(|(receiver, _funding_stream)| {
2731 ZCASHD_FUNDING_STREAM_ORDER
2732 .iter()
2733 .position(|zcashd_receiver| zcashd_receiver == receiver)
2734 });
2735
2736 let [funding_streams, lockbox_streams]: [Vec<_>; 2] = [funding_streams, lockbox_streams]
2738 .map(|streams| {
2739 streams
2740 .into_iter()
2741 .map(|(receiver, value)| {
2742 let address = funding_stream_address(height, &network, receiver);
2743 types::subsidy::FundingStream::new_internal(
2744 is_nu6, receiver, value, address,
2745 )
2746 })
2747 .collect()
2748 });
2749
2750 Ok(GetBlockSubsidyResponse {
2751 miner: miner_subsidy.into(),
2752 founders: founders.into(),
2753 funding_streams,
2754 lockbox_streams,
2755 funding_streams_total: funding_streams_total
2756 .map_error(server::error::LegacyCode::default())?
2757 .into(),
2758 lockbox_total: lockbox_total
2759 .map_error(server::error::LegacyCode::default())?
2760 .into(),
2761 total_block_subsidy: total_block_subsidy.into(),
2762 })
2763 }
2764
2765 async fn get_difficulty(&self) -> Result<f64> {
2766 chain_tip_difficulty(self.network.clone(), self.read_state.clone(), false).await
2767 }
2768
2769 async fn z_list_unified_receivers(
2770 &self,
2771 address: String,
2772 ) -> Result<ZListUnifiedReceiversResponse> {
2773 use zcash_address::unified::Container;
2774
2775 let (network, unified_address): (
2776 zcash_protocol::consensus::NetworkType,
2777 zcash_address::unified::Address,
2778 ) = zcash_address::unified::Encoding::decode(address.clone().as_str())
2779 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
2780
2781 let mut p2pkh = None;
2782 let mut p2sh = None;
2783 let mut orchard = None;
2784 let mut sapling = None;
2785
2786 for item in unified_address.items() {
2787 match item {
2788 zcash_address::unified::Receiver::Orchard(_data) => {
2789 let addr = zcash_address::unified::Address::try_from_items(vec![item])
2790 .expect("using data already decoded as valid");
2791 orchard = Some(addr.encode(&network));
2792 }
2793 zcash_address::unified::Receiver::Sapling(data) => {
2794 let addr = zebra_chain::primitives::Address::try_from_sapling(network, data)
2795 .expect("using data already decoded as valid");
2796 sapling = Some(addr.payment_address().unwrap_or_default());
2797 }
2798 zcash_address::unified::Receiver::P2pkh(data) => {
2799 let addr =
2800 zebra_chain::primitives::Address::try_from_transparent_p2pkh(network, data)
2801 .expect("using data already decoded as valid");
2802 p2pkh = Some(addr.payment_address().unwrap_or_default());
2803 }
2804 zcash_address::unified::Receiver::P2sh(data) => {
2805 let addr =
2806 zebra_chain::primitives::Address::try_from_transparent_p2sh(network, data)
2807 .expect("using data already decoded as valid");
2808 p2sh = Some(addr.payment_address().unwrap_or_default());
2809 }
2810 _ => (),
2811 }
2812 }
2813
2814 Ok(ZListUnifiedReceiversResponse::new(
2815 orchard, sapling, p2pkh, p2sh,
2816 ))
2817 }
2818
2819 async fn invalidate_block(&self, block_hash: String) -> Result<()> {
2820 let block_hash = block_hash
2821 .parse()
2822 .map_error(server::error::LegacyCode::InvalidParameter)?;
2823
2824 self.state
2825 .clone()
2826 .oneshot(zebra_state::Request::InvalidateBlock(block_hash))
2827 .await
2828 .map(|rsp| assert_eq!(rsp, zebra_state::Response::Invalidated(block_hash)))
2829 .map_misc_error()
2830 }
2831
2832 async fn reconsider_block(&self, block_hash: String) -> Result<Vec<block::Hash>> {
2833 let block_hash = block_hash
2834 .parse()
2835 .map_error(server::error::LegacyCode::InvalidParameter)?;
2836
2837 self.state
2838 .clone()
2839 .oneshot(zebra_state::Request::ReconsiderBlock(block_hash))
2840 .await
2841 .map(|rsp| match rsp {
2842 zebra_state::Response::Reconsidered(block_hashes) => block_hashes,
2843 _ => unreachable!("unmatched response to a reconsider block request"),
2844 })
2845 .map_misc_error()
2846 }
2847
2848 async fn generate(&self, num_blocks: u32) -> Result<Vec<Hash>> {
2849 let mut rpc = self.clone();
2850 let network = self.network.clone();
2851
2852 if !network.disable_pow() {
2853 return Err(ErrorObject::borrowed(
2854 0,
2855 "generate is only supported on networks where PoW is disabled",
2856 None,
2857 ));
2858 }
2859
2860 let mut block_hashes = Vec::new();
2861 for _ in 0..num_blocks {
2862 let mut extra_coinbase_data = [0u8; 32];
2867 OsRng.fill_bytes(&mut extra_coinbase_data);
2868 rpc.gbt
2869 .set_extra_coinbase_data(extra_coinbase_data.to_vec());
2870
2871 let block_template = rpc
2872 .get_block_template(None)
2873 .await
2874 .map_error(server::error::LegacyCode::default())?;
2875
2876 let GetBlockTemplateResponse::TemplateMode(block_template) = block_template else {
2877 return Err(ErrorObject::borrowed(
2878 0,
2879 "error generating block template",
2880 None,
2881 ));
2882 };
2883
2884 let proposal_block = proposal_block_from_template(
2885 &block_template,
2886 BlockTemplateTimeSource::CurTime,
2887 &network,
2888 )
2889 .map_error(server::error::LegacyCode::default())?;
2890
2891 let hex_proposal_block = HexData(
2892 proposal_block
2893 .zcash_serialize_to_vec()
2894 .map_error(server::error::LegacyCode::default())?,
2895 );
2896
2897 let r = rpc
2898 .submit_block(hex_proposal_block, None)
2899 .await
2900 .map_error(server::error::LegacyCode::default())?;
2901 match r {
2902 SubmitBlockResponse::Accepted => { }
2903 SubmitBlockResponse::ErrorResponse(response) => {
2904 return Err(ErrorObject::owned(
2905 server::error::LegacyCode::Misc.into(),
2906 format!("block was rejected: {:?}", response),
2907 None::<()>,
2908 ));
2909 }
2910 }
2911
2912 block_hashes.push(GetBlockHashResponse(proposal_block.hash()));
2913 }
2914
2915 Ok(block_hashes)
2916 }
2917
2918 async fn add_node(
2919 &self,
2920 addr: zebra_network::PeerSocketAddr,
2921 command: AddNodeCommand,
2922 ) -> Result<()> {
2923 if self.network.is_regtest() {
2924 match command {
2925 AddNodeCommand::Add => {
2926 tracing::info!(?addr, "adding peer address to the address book");
2927 if self.address_book.clone().add_peer(addr) {
2928 Ok(())
2929 } else {
2930 return Err(ErrorObject::owned(
2931 server::error::LegacyCode::ClientNodeAlreadyAdded.into(),
2932 format!("peer address was already present in the address book: {addr}"),
2933 None::<()>,
2934 ));
2935 }
2936 }
2937 }
2938 } else {
2939 return Err(ErrorObject::owned(
2940 ErrorCode::InvalidParams.code(),
2941 "addnode command is only supported on regtest",
2942 None::<()>,
2943 ));
2944 }
2945 }
2946}
2947
2948pub fn best_chain_tip_height<Tip>(latest_chain_tip: &Tip) -> Result<Height>
2953where
2954 Tip: ChainTip + Clone + Send + Sync + 'static,
2955{
2956 latest_chain_tip
2957 .best_tip_height()
2958 .ok_or_misc_error("No blocks in state")
2959}
2960
2961#[allow(clippy::too_many_arguments)]
2965#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
2966pub struct GetInfoResponse {
2967 #[getter(rename = "raw_version")]
2969 version: u64,
2970
2971 build: String,
2973
2974 subversion: String,
2976
2977 #[serde(rename = "protocolversion")]
2979 protocol_version: u32,
2980
2981 blocks: u32,
2983
2984 connections: usize,
2986
2987 #[serde(skip_serializing_if = "Option::is_none")]
2989 proxy: Option<String>,
2990
2991 difficulty: f64,
2993
2994 testnet: bool,
2996
2997 #[serde(rename = "paytxfee")]
2999 pay_tx_fee: f64,
3000
3001 #[serde(rename = "relayfee")]
3003 relay_fee: f64,
3004
3005 errors: String,
3007
3008 #[serde(rename = "errorstimestamp")]
3010 errors_timestamp: String,
3011}
3012
3013#[deprecated(note = "Use `GetInfoResponse` instead")]
3014pub use self::GetInfoResponse as GetInfo;
3015
3016impl Default for GetInfoResponse {
3017 fn default() -> Self {
3018 GetInfoResponse {
3019 version: 0,
3020 build: "some build version".to_string(),
3021 subversion: "some subversion".to_string(),
3022 protocol_version: 0,
3023 blocks: 0,
3024 connections: 0,
3025 proxy: None,
3026 difficulty: 0.0,
3027 testnet: false,
3028 pay_tx_fee: 0.0,
3029 relay_fee: 0.0,
3030 errors: "no errors".to_string(),
3031 errors_timestamp: "no errors timestamp".to_string(),
3032 }
3033 }
3034}
3035
3036impl GetInfoResponse {
3037 #[allow(clippy::too_many_arguments)]
3039 #[deprecated(note = "Use `GetInfoResponse::new` instead")]
3040 pub fn from_parts(
3041 version: u64,
3042 build: String,
3043 subversion: String,
3044 protocol_version: u32,
3045 blocks: u32,
3046 connections: usize,
3047 proxy: Option<String>,
3048 difficulty: f64,
3049 testnet: bool,
3050 pay_tx_fee: f64,
3051 relay_fee: f64,
3052 errors: String,
3053 errors_timestamp: String,
3054 ) -> Self {
3055 Self {
3056 version,
3057 build,
3058 subversion,
3059 protocol_version,
3060 blocks,
3061 connections,
3062 proxy,
3063 difficulty,
3064 testnet,
3065 pay_tx_fee,
3066 relay_fee,
3067 errors,
3068 errors_timestamp,
3069 }
3070 }
3071
3072 pub fn into_parts(
3074 self,
3075 ) -> (
3076 u64,
3077 String,
3078 String,
3079 u32,
3080 u32,
3081 usize,
3082 Option<String>,
3083 f64,
3084 bool,
3085 f64,
3086 f64,
3087 String,
3088 String,
3089 ) {
3090 (
3091 self.version,
3092 self.build,
3093 self.subversion,
3094 self.protocol_version,
3095 self.blocks,
3096 self.connections,
3097 self.proxy,
3098 self.difficulty,
3099 self.testnet,
3100 self.pay_tx_fee,
3101 self.relay_fee,
3102 self.errors,
3103 self.errors_timestamp,
3104 )
3105 }
3106
3107 fn version_from_string(build_string: &str) -> Option<u64> {
3109 let semver_version = semver::Version::parse(build_string.strip_prefix('v')?).ok()?;
3110 let build_number = semver_version
3111 .build
3112 .as_str()
3113 .split('.')
3114 .next()
3115 .and_then(|num_str| num_str.parse::<u64>().ok())
3116 .unwrap_or_default();
3117
3118 let version_number = 1_000_000 * semver_version.major
3120 + 10_000 * semver_version.minor
3121 + 100 * semver_version.patch
3122 + build_number;
3123
3124 Some(version_number)
3125 }
3126}
3127
3128pub type BlockchainValuePoolBalances = [GetBlockchainInfoBalance; 5];
3130
3131#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters)]
3135pub struct GetBlockchainInfoResponse {
3136 chain: String,
3138
3139 #[getter(copy)]
3141 blocks: Height,
3142
3143 #[getter(copy)]
3146 headers: Height,
3147
3148 difficulty: f64,
3150
3151 #[serde(rename = "verificationprogress")]
3153 verification_progress: f64,
3154
3155 #[serde(rename = "chainwork")]
3157 chain_work: u64,
3158
3159 pruned: bool,
3161
3162 size_on_disk: u64,
3164
3165 commitments: u64,
3167
3168 #[serde(rename = "bestblockhash", with = "hex")]
3170 #[getter(copy)]
3171 best_block_hash: block::Hash,
3172
3173 #[serde(rename = "estimatedheight")]
3177 #[getter(copy)]
3178 estimated_height: Height,
3179
3180 #[serde(rename = "chainSupply")]
3182 chain_supply: GetBlockchainInfoBalance,
3183
3184 #[serde(rename = "valuePools")]
3186 value_pools: BlockchainValuePoolBalances,
3187
3188 upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
3190
3191 #[getter(copy)]
3193 consensus: TipConsensusBranch,
3194}
3195
3196impl Default for GetBlockchainInfoResponse {
3197 fn default() -> Self {
3198 Self {
3199 chain: "main".to_string(),
3200 blocks: Height(1),
3201 best_block_hash: block::Hash([0; 32]),
3202 estimated_height: Height(1),
3203 chain_supply: GetBlockchainInfoBalance::chain_supply(Default::default()),
3204 value_pools: GetBlockchainInfoBalance::zero_pools(),
3205 upgrades: IndexMap::new(),
3206 consensus: TipConsensusBranch {
3207 chain_tip: ConsensusBranchIdHex(ConsensusBranchId::default()),
3208 next_block: ConsensusBranchIdHex(ConsensusBranchId::default()),
3209 },
3210 headers: Height(1),
3211 difficulty: 0.0,
3212 verification_progress: 0.0,
3213 chain_work: 0,
3214 pruned: false,
3215 size_on_disk: 0,
3216 commitments: 0,
3217 }
3218 }
3219}
3220
3221impl GetBlockchainInfoResponse {
3222 #[allow(clippy::too_many_arguments)]
3226 pub fn new(
3227 chain: String,
3228 blocks: Height,
3229 best_block_hash: block::Hash,
3230 estimated_height: Height,
3231 chain_supply: GetBlockchainInfoBalance,
3232 value_pools: BlockchainValuePoolBalances,
3233 upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
3234 consensus: TipConsensusBranch,
3235 headers: Height,
3236 difficulty: f64,
3237 verification_progress: f64,
3238 chain_work: u64,
3239 pruned: bool,
3240 size_on_disk: u64,
3241 commitments: u64,
3242 ) -> Self {
3243 Self {
3244 chain,
3245 blocks,
3246 best_block_hash,
3247 estimated_height,
3248 chain_supply,
3249 value_pools,
3250 upgrades,
3251 consensus,
3252 headers,
3253 difficulty,
3254 verification_progress,
3255 chain_work,
3256 pruned,
3257 size_on_disk,
3258 commitments,
3259 }
3260 }
3261}
3262
3263#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
3265#[serde(from = "DGetAddressBalanceRequest")]
3266pub struct GetAddressBalanceRequest {
3267 addresses: Vec<String>,
3269}
3270
3271impl From<DGetAddressBalanceRequest> for GetAddressBalanceRequest {
3272 fn from(address_strings: DGetAddressBalanceRequest) -> Self {
3273 match address_strings {
3274 DGetAddressBalanceRequest::Addresses { addresses } => {
3275 GetAddressBalanceRequest { addresses }
3276 }
3277 DGetAddressBalanceRequest::Address(address) => GetAddressBalanceRequest {
3278 addresses: vec![address],
3279 },
3280 }
3281 }
3282}
3283
3284#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize)]
3286#[serde(untagged)]
3287enum DGetAddressBalanceRequest {
3288 Addresses { addresses: Vec<String> },
3290 Address(String),
3292}
3293
3294#[deprecated(note = "Use `GetAddressBalanceRequest` instead.")]
3296pub type AddressStrings = GetAddressBalanceRequest;
3297
3298pub trait ValidateAddresses {
3300 fn valid_addresses(&self) -> Result<HashSet<Address>> {
3304 let valid_addresses: HashSet<Address> = self
3307 .addresses()
3308 .iter()
3309 .map(|address| {
3310 address
3311 .parse()
3312 .map_error(server::error::LegacyCode::InvalidAddressOrKey)
3313 })
3314 .collect::<Result<_>>()?;
3315
3316 Ok(valid_addresses)
3317 }
3318
3319 fn addresses(&self) -> &[String];
3321}
3322
3323impl ValidateAddresses for GetAddressBalanceRequest {
3324 fn addresses(&self) -> &[String] {
3325 &self.addresses
3326 }
3327}
3328
3329impl GetAddressBalanceRequest {
3330 pub fn new(addresses: Vec<String>) -> GetAddressBalanceRequest {
3332 GetAddressBalanceRequest { addresses }
3333 }
3334
3335 #[deprecated(
3337 note = "Use `AddressStrings::new` instead. Validity will be checked by the server."
3338 )]
3339 pub fn new_valid(addresses: Vec<String>) -> Result<GetAddressBalanceRequest> {
3340 let req = Self { addresses };
3341 req.valid_addresses()?;
3342 Ok(req)
3343 }
3344}
3345
3346#[derive(
3348 Clone,
3349 Copy,
3350 Debug,
3351 Default,
3352 Eq,
3353 PartialEq,
3354 Hash,
3355 serde::Serialize,
3356 serde::Deserialize,
3357 Getters,
3358 new,
3359)]
3360pub struct GetAddressBalanceResponse {
3361 balance: u64,
3363 pub received: u64,
3365}
3366
3367#[deprecated(note = "Use `GetAddressBalanceResponse` instead.")]
3368pub use self::GetAddressBalanceResponse as AddressBalance;
3369
3370#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, Getters, new)]
3372#[serde(from = "DGetAddressUtxosRequest")]
3373pub struct GetAddressUtxosRequest {
3374 addresses: Vec<String>,
3376 #[serde(default)]
3378 #[serde(rename = "chainInfo")]
3379 chain_info: bool,
3380}
3381
3382impl From<DGetAddressUtxosRequest> for GetAddressUtxosRequest {
3383 fn from(request: DGetAddressUtxosRequest) -> Self {
3384 match request {
3385 DGetAddressUtxosRequest::Single(addr) => GetAddressUtxosRequest {
3386 addresses: vec![addr],
3387 chain_info: false,
3388 },
3389 DGetAddressUtxosRequest::Object {
3390 addresses,
3391 chain_info,
3392 } => GetAddressUtxosRequest {
3393 addresses,
3394 chain_info,
3395 },
3396 }
3397 }
3398}
3399
3400#[derive(Debug, serde::Deserialize)]
3402#[serde(untagged)]
3403enum DGetAddressUtxosRequest {
3404 Single(String),
3406 Object {
3408 addresses: Vec<String>,
3410 #[serde(default)]
3412 #[serde(rename = "chainInfo")]
3413 chain_info: bool,
3414 },
3415}
3416
3417impl ValidateAddresses for GetAddressUtxosRequest {
3418 fn addresses(&self) -> &[String] {
3419 &self.addresses
3420 }
3421}
3422
3423#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
3425pub struct ConsensusBranchIdHex(#[serde(with = "hex")] ConsensusBranchId);
3426
3427impl ConsensusBranchIdHex {
3428 pub fn new(consensus_branch_id: u32) -> Self {
3430 ConsensusBranchIdHex(consensus_branch_id.into())
3431 }
3432
3433 pub fn inner(&self) -> u32 {
3435 self.0.into()
3436 }
3437}
3438
3439#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3441pub struct NetworkUpgradeInfo {
3442 name: NetworkUpgrade,
3446
3447 #[serde(rename = "activationheight")]
3449 activation_height: Height,
3450
3451 status: NetworkUpgradeStatus,
3453}
3454
3455impl NetworkUpgradeInfo {
3456 pub fn from_parts(
3458 name: NetworkUpgrade,
3459 activation_height: Height,
3460 status: NetworkUpgradeStatus,
3461 ) -> Self {
3462 Self {
3463 name,
3464 activation_height,
3465 status,
3466 }
3467 }
3468
3469 pub fn into_parts(self) -> (NetworkUpgrade, Height, NetworkUpgradeStatus) {
3471 (self.name, self.activation_height, self.status)
3472 }
3473}
3474
3475#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3477pub enum NetworkUpgradeStatus {
3478 #[serde(rename = "active")]
3483 Active,
3484
3485 #[serde(rename = "disabled")]
3487 Disabled,
3488
3489 #[serde(rename = "pending")]
3491 Pending,
3492}
3493
3494#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3498pub struct TipConsensusBranch {
3499 #[serde(rename = "chaintip")]
3501 chain_tip: ConsensusBranchIdHex,
3502
3503 #[serde(rename = "nextblock")]
3505 next_block: ConsensusBranchIdHex,
3506}
3507
3508impl TipConsensusBranch {
3509 pub fn from_parts(chain_tip: u32, next_block: u32) -> Self {
3511 Self {
3512 chain_tip: ConsensusBranchIdHex::new(chain_tip),
3513 next_block: ConsensusBranchIdHex::new(next_block),
3514 }
3515 }
3516
3517 pub fn into_parts(self) -> (u32, u32) {
3519 (self.chain_tip.inner(), self.next_block.inner())
3520 }
3521}
3522
3523#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3529pub struct SendRawTransactionResponse(#[serde(with = "hex")] transaction::Hash);
3530
3531#[deprecated(note = "Use `SendRawTransactionResponse` instead")]
3532pub use self::SendRawTransactionResponse as SentTransactionHash;
3533
3534impl Default for SendRawTransactionResponse {
3535 fn default() -> Self {
3536 Self(transaction::Hash::from([0; 32]))
3537 }
3538}
3539
3540impl SendRawTransactionResponse {
3541 pub fn new(hash: transaction::Hash) -> Self {
3543 SendRawTransactionResponse(hash)
3544 }
3545
3546 #[deprecated(note = "Use `SentTransactionHash::hash` instead")]
3548 pub fn inner(&self) -> transaction::Hash {
3549 self.hash()
3550 }
3551
3552 pub fn hash(&self) -> transaction::Hash {
3554 self.0
3555 }
3556}
3557
3558#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3562#[serde(untagged)]
3563pub enum GetBlockResponse {
3564 Raw(#[serde(with = "hex")] SerializedBlock),
3566 Object(Box<BlockObject>),
3568}
3569
3570#[deprecated(note = "Use `GetBlockResponse` instead")]
3571pub use self::GetBlockResponse as GetBlock;
3572
3573impl Default for GetBlockResponse {
3574 fn default() -> Self {
3575 GetBlockResponse::Object(Box::new(BlockObject {
3576 hash: block::Hash([0; 32]),
3577 confirmations: 0,
3578 height: None,
3579 time: None,
3580 tx: Vec::new(),
3581 trees: GetBlockTrees::default(),
3582 size: None,
3583 version: None,
3584 merkle_root: None,
3585 block_commitments: None,
3586 final_sapling_root: None,
3587 final_orchard_root: None,
3588 nonce: None,
3589 bits: None,
3590 difficulty: None,
3591 chain_supply: None,
3592 value_pools: None,
3593 previous_block_hash: None,
3594 next_block_hash: None,
3595 solution: None,
3596 }))
3597 }
3598}
3599
3600#[allow(clippy::too_many_arguments)]
3602#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3603pub struct BlockObject {
3604 #[getter(copy)]
3606 #[serde(with = "hex")]
3607 hash: block::Hash,
3608
3609 confirmations: i64,
3612
3613 #[serde(skip_serializing_if = "Option::is_none")]
3615 #[getter(copy)]
3616 size: Option<i64>,
3617
3618 #[serde(skip_serializing_if = "Option::is_none")]
3620 #[getter(copy)]
3621 height: Option<Height>,
3622
3623 #[serde(skip_serializing_if = "Option::is_none")]
3625 #[getter(copy)]
3626 version: Option<u32>,
3627
3628 #[serde(with = "opthex", rename = "merkleroot")]
3630 #[serde(skip_serializing_if = "Option::is_none")]
3631 #[getter(copy)]
3632 merkle_root: Option<block::merkle::Root>,
3633
3634 #[serde(with = "opthex", rename = "blockcommitments")]
3637 #[serde(skip_serializing_if = "Option::is_none")]
3638 #[getter(copy)]
3639 block_commitments: Option<[u8; 32]>,
3640
3641 #[serde(with = "opthex", rename = "finalsaplingroot")]
3645 #[serde(skip_serializing_if = "Option::is_none")]
3646 #[getter(copy)]
3647 final_sapling_root: Option<[u8; 32]>,
3648
3649 #[serde(with = "opthex", rename = "finalorchardroot")]
3651 #[serde(skip_serializing_if = "Option::is_none")]
3652 #[getter(copy)]
3653 final_orchard_root: Option<[u8; 32]>,
3654
3655 tx: Vec<GetBlockTransaction>,
3660
3661 #[serde(skip_serializing_if = "Option::is_none")]
3663 #[getter(copy)]
3664 time: Option<i64>,
3665
3666 #[serde(with = "opthex")]
3668 #[serde(skip_serializing_if = "Option::is_none")]
3669 #[getter(copy)]
3670 nonce: Option<[u8; 32]>,
3671
3672 #[serde(with = "opthex")]
3675 #[serde(skip_serializing_if = "Option::is_none")]
3676 #[getter(copy)]
3677 solution: Option<Solution>,
3678
3679 #[serde(with = "opthex")]
3681 #[serde(skip_serializing_if = "Option::is_none")]
3682 #[getter(copy)]
3683 bits: Option<CompactDifficulty>,
3684
3685 #[serde(skip_serializing_if = "Option::is_none")]
3688 #[getter(copy)]
3689 difficulty: Option<f64>,
3690
3691 #[serde(rename = "chainSupply")]
3696 #[serde(skip_serializing_if = "Option::is_none")]
3697 chain_supply: Option<GetBlockchainInfoBalance>,
3698
3699 #[serde(rename = "valuePools")]
3701 #[serde(skip_serializing_if = "Option::is_none")]
3702 value_pools: Option<BlockchainValuePoolBalances>,
3703
3704 #[getter(copy)]
3706 trees: GetBlockTrees,
3707
3708 #[serde(rename = "previousblockhash", skip_serializing_if = "Option::is_none")]
3710 #[serde(with = "opthex")]
3711 #[getter(copy)]
3712 previous_block_hash: Option<block::Hash>,
3713
3714 #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
3716 #[serde(with = "opthex")]
3717 #[getter(copy)]
3718 next_block_hash: Option<block::Hash>,
3719}
3720
3721#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3722#[serde(untagged)]
3723pub enum GetBlockTransaction {
3726 Hash(#[serde(with = "hex")] transaction::Hash),
3728 Object(Box<TransactionObject>),
3730}
3731
3732#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3736#[serde(untagged)]
3737pub enum GetBlockHeaderResponse {
3738 Raw(hex_data::HexData),
3740
3741 Object(Box<BlockHeaderObject>),
3743}
3744
3745#[deprecated(note = "Use `GetBlockHeaderResponse` instead")]
3746pub use self::GetBlockHeaderResponse as GetBlockHeader;
3747
3748#[allow(clippy::too_many_arguments)]
3749#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3750pub struct BlockHeaderObject {
3754 #[serde(with = "hex")]
3756 #[getter(copy)]
3757 hash: block::Hash,
3758
3759 confirmations: i64,
3762
3763 #[getter(copy)]
3765 height: Height,
3766
3767 version: u32,
3769
3770 #[serde(with = "hex", rename = "merkleroot")]
3772 #[getter(copy)]
3773 merkle_root: block::merkle::Root,
3774
3775 #[serde(with = "hex", rename = "blockcommitments")]
3778 #[getter(copy)]
3779 block_commitments: [u8; 32],
3780
3781 #[serde(with = "hex", rename = "finalsaplingroot")]
3783 #[getter(copy)]
3784 final_sapling_root: [u8; 32],
3785
3786 #[serde(skip)]
3789 sapling_tree_size: u64,
3790
3791 time: i64,
3793
3794 #[serde(with = "hex")]
3796 #[getter(copy)]
3797 nonce: [u8; 32],
3798
3799 #[serde(with = "hex")]
3801 #[getter(copy)]
3802 solution: Solution,
3803
3804 #[serde(with = "hex")]
3806 #[getter(copy)]
3807 bits: CompactDifficulty,
3808
3809 difficulty: f64,
3812
3813 #[serde(rename = "previousblockhash")]
3815 #[serde(with = "hex")]
3816 #[getter(copy)]
3817 previous_block_hash: block::Hash,
3818
3819 #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
3821 #[getter(copy)]
3822 #[serde(with = "opthex")]
3823 next_block_hash: Option<block::Hash>,
3824}
3825
3826#[deprecated(note = "Use `BlockHeaderObject` instead")]
3827pub use BlockHeaderObject as GetBlockHeaderObject;
3828
3829impl Default for GetBlockHeaderResponse {
3830 fn default() -> Self {
3831 GetBlockHeaderResponse::Object(Box::default())
3832 }
3833}
3834
3835impl Default for BlockHeaderObject {
3836 fn default() -> Self {
3837 let difficulty: ExpandedDifficulty = zebra_chain::work::difficulty::U256::one().into();
3838
3839 BlockHeaderObject {
3840 hash: block::Hash([0; 32]),
3841 confirmations: 0,
3842 height: Height::MIN,
3843 version: 4,
3844 merkle_root: block::merkle::Root([0; 32]),
3845 block_commitments: Default::default(),
3846 final_sapling_root: Default::default(),
3847 sapling_tree_size: Default::default(),
3848 time: 0,
3849 nonce: [0; 32],
3850 solution: Solution::for_proposal(),
3851 bits: difficulty.to_compact(),
3852 difficulty: 1.0,
3853 previous_block_hash: block::Hash([0; 32]),
3854 next_block_hash: Some(block::Hash([0; 32])),
3855 }
3856 }
3857}
3858
3859#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3865#[serde(transparent)]
3866pub struct GetBlockHashResponse(#[serde(with = "hex")] pub(crate) block::Hash);
3867
3868impl GetBlockHashResponse {
3869 pub fn new(hash: block::Hash) -> Self {
3871 GetBlockHashResponse(hash)
3872 }
3873
3874 pub fn hash(&self) -> block::Hash {
3876 self.0
3877 }
3878}
3879
3880#[deprecated(note = "Use `GetBlockHashResponse` instead")]
3881pub use self::GetBlockHashResponse as GetBlockHash;
3882
3883pub type Hash = GetBlockHashResponse;
3885
3886#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, Getters, new)]
3888pub struct GetBlockHeightAndHashResponse {
3889 #[getter(copy)]
3891 height: block::Height,
3892 #[getter(copy)]
3894 hash: block::Hash,
3895}
3896
3897#[deprecated(note = "Use `GetBlockHeightAndHashResponse` instead.")]
3898pub use GetBlockHeightAndHashResponse as GetBestBlockHeightAndHash;
3899
3900impl Default for GetBlockHeightAndHashResponse {
3901 fn default() -> Self {
3902 Self {
3903 height: block::Height::MIN,
3904 hash: block::Hash([0; 32]),
3905 }
3906 }
3907}
3908
3909impl Default for GetBlockHashResponse {
3910 fn default() -> Self {
3911 GetBlockHashResponse(block::Hash([0; 32]))
3912 }
3913}
3914
3915#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3919#[serde(untagged)]
3920pub enum GetRawTransactionResponse {
3921 Raw(#[serde(with = "hex")] SerializedTransaction),
3923 Object(Box<TransactionObject>),
3925}
3926
3927#[deprecated(note = "Use `GetRawTransactionResponse` instead")]
3928pub use self::GetRawTransactionResponse as GetRawTransaction;
3929
3930impl Default for GetRawTransactionResponse {
3931 fn default() -> Self {
3932 Self::Object(Box::default())
3933 }
3934}
3935
3936#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3938#[serde(untagged)]
3939pub enum GetAddressUtxosResponse {
3940 Utxos(Vec<Utxo>),
3942 UtxosAndChainInfo(GetAddressUtxosResponseObject),
3944}
3945
3946#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3948pub struct GetAddressUtxosResponseObject {
3949 utxos: Vec<Utxo>,
3950 #[serde(with = "hex")]
3951 #[getter(copy)]
3952 hash: block::Hash,
3953 #[getter(copy)]
3954 height: block::Height,
3955}
3956
3957#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3961pub struct Utxo {
3962 address: transparent::Address,
3964
3965 #[serde(with = "hex")]
3967 #[getter(copy)]
3968 txid: transaction::Hash,
3969
3970 #[serde(rename = "outputIndex")]
3972 #[getter(copy)]
3973 output_index: OutputIndex,
3974
3975 #[serde(with = "hex")]
3977 script: transparent::Script,
3978
3979 satoshis: u64,
3981
3982 #[getter(copy)]
3986 height: Height,
3987}
3988
3989#[deprecated(note = "Use `Utxo` instead")]
3990pub use self::Utxo as GetAddressUtxos;
3991
3992impl Default for Utxo {
3993 fn default() -> Self {
3994 Self {
3995 address: transparent::Address::from_pub_key_hash(
3996 zebra_chain::parameters::NetworkKind::default(),
3997 [0u8; 20],
3998 ),
3999 txid: transaction::Hash::from([0; 32]),
4000 output_index: OutputIndex::from_u64(0),
4001 script: transparent::Script::new(&[0u8; 10]),
4002 satoshis: u64::default(),
4003 height: Height(0),
4004 }
4005 }
4006}
4007
4008impl Utxo {
4009 #[deprecated(note = "Use `Utxo::new` instead")]
4011 pub fn from_parts(
4012 address: transparent::Address,
4013 txid: transaction::Hash,
4014 output_index: OutputIndex,
4015 script: transparent::Script,
4016 satoshis: u64,
4017 height: Height,
4018 ) -> Self {
4019 Utxo {
4020 address,
4021 txid,
4022 output_index,
4023 script,
4024 satoshis,
4025 height,
4026 }
4027 }
4028
4029 pub fn into_parts(
4031 &self,
4032 ) -> (
4033 transparent::Address,
4034 transaction::Hash,
4035 OutputIndex,
4036 transparent::Script,
4037 u64,
4038 Height,
4039 ) {
4040 (
4041 self.address.clone(),
4042 self.txid,
4043 self.output_index,
4044 self.script.clone(),
4045 self.satoshis,
4046 self.height,
4047 )
4048 }
4049}
4050
4051#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, Getters, new)]
4055#[serde(from = "DGetAddressTxIdsRequest")]
4056pub struct GetAddressTxIdsRequest {
4057 addresses: Vec<String>,
4060 start: Option<u32>,
4062 end: Option<u32>,
4064}
4065
4066impl GetAddressTxIdsRequest {
4067 #[deprecated(note = "Use `GetAddressTxIdsRequest::new` instead.")]
4069 pub fn from_parts(addresses: Vec<String>, start: u32, end: u32) -> Self {
4070 GetAddressTxIdsRequest {
4071 addresses,
4072 start: Some(start),
4073 end: Some(end),
4074 }
4075 }
4076
4077 pub fn into_parts(&self) -> (Vec<String>, u32, u32) {
4079 (
4080 self.addresses.clone(),
4081 self.start.unwrap_or(0),
4082 self.end.unwrap_or(0),
4083 )
4084 }
4085}
4086
4087impl From<DGetAddressTxIdsRequest> for GetAddressTxIdsRequest {
4088 fn from(request: DGetAddressTxIdsRequest) -> Self {
4089 match request {
4090 DGetAddressTxIdsRequest::Single(addr) => GetAddressTxIdsRequest {
4091 addresses: vec![addr],
4092 start: None,
4093 end: None,
4094 },
4095 DGetAddressTxIdsRequest::Object {
4096 addresses,
4097 start,
4098 end,
4099 } => GetAddressTxIdsRequest {
4100 addresses,
4101 start,
4102 end,
4103 },
4104 }
4105 }
4106}
4107
4108#[derive(Debug, serde::Deserialize)]
4110#[serde(untagged)]
4111enum DGetAddressTxIdsRequest {
4112 Single(String),
4114 Object {
4116 addresses: Vec<String>,
4118 start: Option<u32>,
4120 end: Option<u32>,
4122 },
4123}
4124
4125impl ValidateAddresses for GetAddressTxIdsRequest {
4126 fn addresses(&self) -> &[String] {
4127 &self.addresses
4128 }
4129}
4130
4131#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
4133pub struct GetBlockTrees {
4134 #[serde(skip_serializing_if = "SaplingTrees::is_empty")]
4135 sapling: SaplingTrees,
4136 #[serde(skip_serializing_if = "OrchardTrees::is_empty")]
4137 orchard: OrchardTrees,
4138}
4139
4140impl Default for GetBlockTrees {
4141 fn default() -> Self {
4142 GetBlockTrees {
4143 sapling: SaplingTrees { size: 0 },
4144 orchard: OrchardTrees { size: 0 },
4145 }
4146 }
4147}
4148
4149impl GetBlockTrees {
4150 pub fn new(sapling: u64, orchard: u64) -> Self {
4152 GetBlockTrees {
4153 sapling: SaplingTrees { size: sapling },
4154 orchard: OrchardTrees { size: orchard },
4155 }
4156 }
4157
4158 pub fn sapling(self) -> u64 {
4160 self.sapling.size
4161 }
4162
4163 pub fn orchard(self) -> u64 {
4165 self.orchard.size
4166 }
4167}
4168
4169#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
4171pub struct SaplingTrees {
4172 size: u64,
4173}
4174
4175impl SaplingTrees {
4176 fn is_empty(&self) -> bool {
4177 self.size == 0
4178 }
4179}
4180
4181#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
4183pub struct OrchardTrees {
4184 size: u64,
4185}
4186
4187impl OrchardTrees {
4188 fn is_empty(&self) -> bool {
4189 self.size == 0
4190 }
4191}
4192
4193fn build_height_range(
4209 start: Option<u32>,
4210 end: Option<u32>,
4211 chain_height: Height,
4212) -> Result<RangeInclusive<Height>> {
4213 let start = Height(start.unwrap_or(0)).min(chain_height);
4216
4217 let end = match end {
4219 Some(0) | None => chain_height,
4220 Some(val) => Height(val).min(chain_height),
4221 };
4222
4223 if start > end {
4224 return Err(ErrorObject::owned(
4225 ErrorCode::InvalidParams.code(),
4226 format!("start {start:?} must be less than or equal to end {end:?}"),
4227 None::<()>,
4228 ));
4229 }
4230
4231 Ok(start..=end)
4232}
4233
4234pub fn height_from_signed_int(index: i32, tip_height: Height) -> Result<Height> {
4242 if index >= 0 {
4243 let height = index.try_into().map_err(|_| {
4244 ErrorObject::borrowed(
4245 ErrorCode::InvalidParams.code(),
4246 "Index conversion failed",
4247 None,
4248 )
4249 })?;
4250 if height > tip_height.0 {
4251 return Err(ErrorObject::borrowed(
4252 ErrorCode::InvalidParams.code(),
4253 "Provided index is greater than the current tip",
4254 None,
4255 ));
4256 }
4257 Ok(Height(height))
4258 } else {
4259 let height = i32::try_from(tip_height.0)
4261 .map_err(|_| {
4262 ErrorObject::borrowed(
4263 ErrorCode::InvalidParams.code(),
4264 "Tip height conversion failed",
4265 None,
4266 )
4267 })?
4268 .checked_add(index + 1);
4269
4270 let sanitized_height = match height {
4271 None => {
4272 return Err(ErrorObject::borrowed(
4273 ErrorCode::InvalidParams.code(),
4274 "Provided index is not valid",
4275 None,
4276 ));
4277 }
4278 Some(h) => {
4279 if h < 0 {
4280 return Err(ErrorObject::borrowed(
4281 ErrorCode::InvalidParams.code(),
4282 "Provided negative index ends up with a negative height",
4283 None,
4284 ));
4285 }
4286 let h: u32 = h.try_into().map_err(|_| {
4287 ErrorObject::borrowed(
4288 ErrorCode::InvalidParams.code(),
4289 "Height conversion failed",
4290 None,
4291 )
4292 })?;
4293 if h > tip_height.0 {
4294 return Err(ErrorObject::borrowed(
4295 ErrorCode::InvalidParams.code(),
4296 "Provided index is greater than the current tip",
4297 None,
4298 ));
4299 }
4300
4301 h
4302 }
4303 };
4304
4305 Ok(Height(sanitized_height))
4306 }
4307}
4308
4309pub mod opthex {
4311 use hex::{FromHex, ToHex};
4312 use serde::{de, Deserialize, Deserializer, Serializer};
4313
4314 #[allow(missing_docs)]
4315 pub fn serialize<S, T>(data: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
4316 where
4317 S: Serializer,
4318 T: ToHex,
4319 {
4320 match data {
4321 Some(data) => {
4322 let s = data.encode_hex::<String>();
4323 serializer.serialize_str(&s)
4324 }
4325 None => serializer.serialize_none(),
4326 }
4327 }
4328
4329 #[allow(missing_docs)]
4330 pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
4331 where
4332 D: Deserializer<'de>,
4333 T: FromHex,
4334 {
4335 let opt = Option::<String>::deserialize(deserializer)?;
4336 match opt {
4337 Some(s) => T::from_hex(&s)
4338 .map(Some)
4339 .map_err(|_e| de::Error::custom("failed to convert hex string")),
4340 None => Ok(None),
4341 }
4342 }
4343}
4344
4345pub mod arrayhex {
4347 use serde::{Deserializer, Serializer};
4348 use std::fmt;
4349
4350 #[allow(missing_docs)]
4351 pub fn serialize<S, const N: usize>(data: &[u8; N], serializer: S) -> Result<S::Ok, S::Error>
4352 where
4353 S: Serializer,
4354 {
4355 let hex_string = hex::encode(data);
4356 serializer.serialize_str(&hex_string)
4357 }
4358
4359 #[allow(missing_docs)]
4360 pub fn deserialize<'de, D, const N: usize>(deserializer: D) -> Result<[u8; N], D::Error>
4361 where
4362 D: Deserializer<'de>,
4363 {
4364 struct HexArrayVisitor<const N: usize>;
4365
4366 impl<const N: usize> serde::de::Visitor<'_> for HexArrayVisitor<N> {
4367 type Value = [u8; N];
4368
4369 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
4370 write!(formatter, "a hex string representing exactly {N} bytes")
4371 }
4372
4373 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
4374 where
4375 E: serde::de::Error,
4376 {
4377 let vec = hex::decode(v).map_err(E::custom)?;
4378 vec.clone().try_into().map_err(|_| {
4379 E::invalid_length(vec.len(), &format!("expected {N} bytes").as_str())
4380 })
4381 }
4382 }
4383
4384 deserializer.deserialize_str(HexArrayVisitor::<N>)
4385 }
4386}
4387
4388pub async fn chain_tip_difficulty<State>(
4390 network: Network,
4391 mut state: State,
4392 should_use_default: bool,
4393) -> Result<f64>
4394where
4395 State: ReadStateService,
4396{
4397 let request = ReadRequest::ChainInfo;
4398
4399 let response = state
4405 .ready()
4406 .and_then(|service| service.call(request))
4407 .await;
4408
4409 let response = match (should_use_default, response) {
4410 (_, Ok(res)) => res,
4411 (true, Err(_)) => {
4412 return Ok((U256::from(network.target_difficulty_limit()) >> 128).as_u128() as f64);
4413 }
4414 (false, Err(error)) => return Err(ErrorObject::owned(0, error.to_string(), None::<()>)),
4415 };
4416
4417 let chain_info = match response {
4418 ReadResponse::ChainInfo(info) => info,
4419 _ => unreachable!("unmatched response to a chain info request"),
4420 };
4421
4422 let pow_limit: U256 = network.target_difficulty_limit().into();
4445 let Some(difficulty) = chain_info.expected_difficulty.to_expanded() else {
4446 return Ok(0.0);
4447 };
4448
4449 let pow_limit = pow_limit >> 128;
4451 let difficulty = U256::from(difficulty) >> 128;
4452
4453 let pow_limit = pow_limit.as_u128() as f64;
4456 let difficulty = difficulty.as_u128() as f64;
4457
4458 Ok(pow_limit / difficulty)
4460}
4461
4462#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
4464pub enum AddNodeCommand {
4465 #[serde(rename = "add")]
4467 Add,
4468}