1use std::{
10 cmp,
11 collections::{HashMap, HashSet},
12 fmt,
13 ops::RangeInclusive,
14 sync::Arc,
15 time::Duration,
16};
17
18use chrono::Utc;
19use futures::{future::OptionFuture, stream::FuturesOrdered, StreamExt, TryFutureExt};
20use hex::{FromHex, ToHex};
21use hex_data::HexData;
22use indexmap::IndexMap;
23use jsonrpsee::core::{async_trait, RpcResult as Result};
24use jsonrpsee_proc_macros::rpc;
25use jsonrpsee_types::{ErrorCode, ErrorObject};
26use tokio::{
27 sync::{broadcast, watch},
28 task::JoinHandle,
29};
30use tower::{Service, ServiceExt};
31use tracing::Instrument;
32
33use zcash_address::{unified::Encoding, TryFromAddress};
34use zcash_primitives::consensus::Parameters;
35
36use zebra_chain::{
37 amount::{self, Amount, NonNegative},
38 block::{self, Block, Commitment, Height, SerializedBlock, TryIntoHeight},
39 chain_sync_status::ChainSyncStatus,
40 chain_tip::{ChainTip, NetworkChainTipHeightEstimator},
41 parameters::{
42 subsidy::{
43 block_subsidy, funding_stream_values, miner_subsidy, FundingStreamReceiver,
44 ParameterSubsidy,
45 },
46 ConsensusBranchId, Network, NetworkUpgrade, POW_AVERAGING_WINDOW,
47 },
48 primitives,
49 serialization::{ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize},
50 subtree::NoteCommitmentSubtreeIndex,
51 transaction::{self, SerializedTransaction, Transaction, UnminedTx},
52 transparent::{self, Address},
53 work::{
54 difficulty::{CompactDifficulty, ExpandedDifficulty, ParameterDifficulty, U256},
55 equihash::Solution,
56 },
57};
58use zebra_consensus::{funding_stream_address, ParameterCheckpoint, RouterError};
59use zebra_network::{address_book_peers::AddressBookPeers, PeerSocketAddr};
60use zebra_node_services::mempool;
61use zebra_state::{
62 HashOrHeight, OutputIndex, OutputLocation, ReadRequest, ReadResponse, TransactionLocation,
63};
64
65use crate::{
66 config,
67 methods::trees::{GetSubtrees, GetTreestate, SubtreeRpcData},
68 queue::Queue,
69 server::{
70 self,
71 error::{MapError, OkOrError},
72 },
73};
74
75use types::{
76 get_block_template::{
77 self, constants::MEMPOOL_LONG_POLL_INTERVAL, proposal::proposal_block_from_template,
78 GetBlockTemplate, GetBlockTemplateHandler, ZCASHD_FUNDING_STREAM_ORDER,
79 },
80 get_blockchain_info, get_mining_info,
81 get_raw_mempool::{self, GetRawMempool},
82 long_poll::LongPollInput,
83 peer_info::PeerInfo,
84 submit_block,
85 subsidy::BlockSubsidy,
86 transaction::TransactionObject,
87 unified_address, validate_address, z_validate_address,
88};
89
90pub mod hex_data;
91pub mod trees;
92pub mod types;
93
94#[cfg(test)]
95mod tests;
96
97#[rpc(server)]
98pub trait Rpc {
100 #[method(name = "getinfo")]
115 async fn get_info(&self) -> Result<GetInfo>;
116
117 #[method(name = "getblockchaininfo")]
128 async fn get_blockchain_info(&self) -> Result<GetBlockChainInfo>;
129
130 #[method(name = "getaddressbalance")]
153 async fn get_address_balance(&self, address_strings: AddressStrings) -> Result<AddressBalance>;
154
155 #[method(name = "sendrawtransaction")]
172 async fn send_raw_transaction(
173 &self,
174 raw_transaction_hex: String,
175 _allow_high_fees: Option<bool>,
176 ) -> Result<SentTransactionHash>;
177
178 #[method(name = "getblock")]
198 async fn get_block(&self, hash_or_height: String, verbosity: Option<u8>) -> Result<GetBlock>;
199
200 #[method(name = "getblockheader")]
218 async fn get_block_header(
219 &self,
220 hash_or_height: String,
221 verbose: Option<bool>,
222 ) -> Result<GetBlockHeader>;
223
224 #[method(name = "getbestblockhash")]
230 fn get_best_block_hash(&self) -> Result<GetBlockHash>;
231
232 #[method(name = "getbestblockheightandhash")]
238 fn get_best_block_height_and_hash(&self) -> Result<GetBlockHeightAndHash>;
239
240 #[method(name = "getrawmempool")]
250 async fn get_raw_mempool(&self, verbose: Option<bool>) -> Result<GetRawMempool>;
251
252 #[method(name = "z_gettreestate")]
269 async fn z_get_treestate(&self, hash_or_height: String) -> Result<GetTreestate>;
270
271 #[method(name = "z_getsubtreesbyindex")]
290 async fn z_get_subtrees_by_index(
291 &self,
292 pool: String,
293 start_index: NoteCommitmentSubtreeIndex,
294 limit: Option<NoteCommitmentSubtreeIndex>,
295 ) -> Result<GetSubtrees>;
296
297 #[method(name = "getrawtransaction")]
309 async fn get_raw_transaction(
310 &self,
311 txid: String,
312 verbose: Option<u8>,
313 block_hash: Option<String>,
314 ) -> Result<GetRawTransaction>;
315
316 #[method(name = "getaddresstxids")]
334 async fn get_address_tx_ids(&self, request: GetAddressTxIdsRequest) -> Result<Vec<String>>;
335
336 #[method(name = "getaddressutxos")]
351 async fn get_address_utxos(
352 &self,
353 address_strings: AddressStrings,
354 ) -> Result<Vec<GetAddressUtxos>>;
355
356 #[method(name = "stop")]
367 fn stop(&self) -> Result<String>;
368
369 #[method(name = "getblockcount")]
376 fn get_block_count(&self) -> Result<u32>;
377
378 #[method(name = "getblockhash")]
394 async fn get_block_hash(&self, index: i32) -> Result<GetBlockHash>;
395
396 #[method(name = "getblocktemplate")]
418 async fn get_block_template(
419 &self,
420 parameters: Option<get_block_template::parameters::JsonParameters>,
421 ) -> Result<get_block_template::Response>;
422
423 #[method(name = "submitblock")]
439 async fn submit_block(
440 &self,
441 hex_data: HexData,
442 _parameters: Option<submit_block::JsonParameters>,
443 ) -> Result<submit_block::Response>;
444
445 #[method(name = "getmininginfo")]
451 async fn get_mining_info(&self) -> Result<get_mining_info::Response>;
452
453 #[method(name = "getnetworksolps")]
464 async fn get_network_sol_ps(&self, num_blocks: Option<i32>, height: Option<i32>)
465 -> Result<u64>;
466
467 #[method(name = "getnetworkhashps")]
477 async fn get_network_hash_ps(
478 &self,
479 num_blocks: Option<i32>,
480 height: Option<i32>,
481 ) -> Result<u64> {
482 self.get_network_sol_ps(num_blocks, height).await
483 }
484
485 #[method(name = "getpeerinfo")]
491 async fn get_peer_info(&self) -> Result<Vec<PeerInfo>>;
492
493 #[method(name = "validateaddress")]
504 async fn validate_address(&self, address: String) -> Result<validate_address::Response>;
505
506 #[method(name = "z_validateaddress")]
521 async fn z_validate_address(&self, address: String) -> Result<z_validate_address::Response>;
522
523 #[method(name = "getblocksubsidy")]
538 async fn get_block_subsidy(&self, height: Option<u32>) -> Result<BlockSubsidy>;
539
540 #[method(name = "getdifficulty")]
546 async fn get_difficulty(&self) -> Result<f64>;
547
548 #[method(name = "z_listunifiedreceivers")]
562 async fn z_list_unified_receivers(&self, address: String) -> Result<unified_address::Response>;
563
564 #[method(name = "invalidateblock")]
572 async fn invalidate_block(&self, block_hash: block::Hash) -> Result<()>;
573
574 #[method(name = "reconsiderblock")]
580 async fn reconsider_block(&self, block_hash: block::Hash) -> Result<Vec<block::Hash>>;
581
582 #[method(name = "generate")]
583 async fn generate(&self, num_blocks: u32) -> Result<Vec<GetBlockHash>>;
597
598 #[method(name = "addnode")]
599 async fn add_node(&self, addr: PeerSocketAddr, command: AddNodeCommand) -> Result<()>;
614}
615
616#[derive(Clone)]
618pub struct RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
619where
620 Mempool: Service<
621 mempool::Request,
622 Response = mempool::Response,
623 Error = zebra_node_services::BoxError,
624 > + Clone
625 + Send
626 + Sync
627 + 'static,
628 Mempool::Future: Send,
629 State: Service<
630 zebra_state::Request,
631 Response = zebra_state::Response,
632 Error = zebra_state::BoxError,
633 > + Clone
634 + Send
635 + Sync
636 + 'static,
637 State::Future: Send,
638 ReadState: Service<
639 zebra_state::ReadRequest,
640 Response = zebra_state::ReadResponse,
641 Error = zebra_state::BoxError,
642 > + Clone
643 + Send
644 + Sync
645 + 'static,
646 ReadState::Future: Send,
647 Tip: ChainTip + Clone + Send + Sync + 'static,
648 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
649 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
650 + Clone
651 + Send
652 + Sync
653 + 'static,
654 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
655 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
656{
657 build_version: String,
661
662 user_agent: String,
664
665 network: Network,
667
668 debug_force_finished_sync: bool,
671
672 mempool: Mempool,
676
677 state: State,
679
680 read_state: ReadState,
682
683 latest_chain_tip: Tip,
685
686 queue_sender: broadcast::Sender<UnminedTx>,
690
691 address_book: AddressBook,
693
694 last_warn_error_log_rx: LoggedLastEvent,
696
697 gbt: GetBlockTemplateHandler<BlockVerifierRouter, SyncStatus>,
699}
700
701pub type LoggedLastEvent = watch::Receiver<Option<(String, tracing::Level, chrono::DateTime<Utc>)>>;
703
704impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus> fmt::Debug
705 for RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
706where
707 Mempool: Service<
708 mempool::Request,
709 Response = mempool::Response,
710 Error = zebra_node_services::BoxError,
711 > + Clone
712 + Send
713 + Sync
714 + 'static,
715 Mempool::Future: Send,
716 State: Service<
717 zebra_state::Request,
718 Response = zebra_state::Response,
719 Error = zebra_state::BoxError,
720 > + Clone
721 + Send
722 + Sync
723 + 'static,
724 State::Future: Send,
725 ReadState: Service<
726 zebra_state::ReadRequest,
727 Response = zebra_state::ReadResponse,
728 Error = zebra_state::BoxError,
729 > + Clone
730 + Send
731 + Sync
732 + 'static,
733 ReadState::Future: Send,
734 Tip: ChainTip + Clone + Send + Sync + 'static,
735 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
736 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
737 + Clone
738 + Send
739 + Sync
740 + 'static,
741 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
742 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
743{
744 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
745 f.debug_struct("RpcImpl")
747 .field("build_version", &self.build_version)
748 .field("user_agent", &self.user_agent)
749 .field("network", &self.network)
750 .field("debug_force_finished_sync", &self.debug_force_finished_sync)
751 .field("getblocktemplate", &self.gbt)
752 .finish()
753 }
754}
755
756impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
757 RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
758where
759 Mempool: Service<
760 mempool::Request,
761 Response = mempool::Response,
762 Error = zebra_node_services::BoxError,
763 > + Clone
764 + Send
765 + Sync
766 + 'static,
767 Mempool::Future: Send,
768 State: Service<
769 zebra_state::Request,
770 Response = zebra_state::Response,
771 Error = zebra_state::BoxError,
772 > + Clone
773 + Send
774 + Sync
775 + 'static,
776 State::Future: Send,
777 ReadState: Service<
778 zebra_state::ReadRequest,
779 Response = zebra_state::ReadResponse,
780 Error = zebra_state::BoxError,
781 > + Clone
782 + Send
783 + Sync
784 + 'static,
785 ReadState::Future: Send,
786 Tip: ChainTip + Clone + Send + Sync + 'static,
787 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
788 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
789 + Clone
790 + Send
791 + Sync
792 + 'static,
793 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
794 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
795{
796 #[allow(clippy::too_many_arguments)]
801 pub fn new<VersionString, UserAgentString>(
802 network: Network,
803 mining_config: config::mining::Config,
804 debug_force_finished_sync: bool,
805 build_version: VersionString,
806 user_agent: UserAgentString,
807 mempool: Mempool,
808 state: State,
809 read_state: ReadState,
810 block_verifier_router: BlockVerifierRouter,
811 sync_status: SyncStatus,
812 latest_chain_tip: Tip,
813 address_book: AddressBook,
814 last_warn_error_log_rx: LoggedLastEvent,
815 mined_block_sender: Option<watch::Sender<(block::Hash, block::Height)>>,
816 ) -> (Self, JoinHandle<()>)
817 where
818 VersionString: ToString + Clone + Send + 'static,
819 UserAgentString: ToString + Clone + Send + 'static,
820 {
821 let (runner, queue_sender) = Queue::start();
822
823 let mut build_version = build_version.to_string();
824 let user_agent = user_agent.to_string();
825
826 if !build_version.is_empty() && !build_version.starts_with('v') {
828 build_version.insert(0, 'v');
829 }
830
831 let gbt = GetBlockTemplateHandler::new(
832 &network,
833 mining_config.clone(),
834 block_verifier_router,
835 sync_status,
836 mined_block_sender,
837 );
838
839 let rpc_impl = RpcImpl {
840 build_version,
841 user_agent,
842 network: network.clone(),
843 debug_force_finished_sync,
844 mempool: mempool.clone(),
845 state: state.clone(),
846 read_state: read_state.clone(),
847 latest_chain_tip: latest_chain_tip.clone(),
848 queue_sender,
849 address_book,
850 last_warn_error_log_rx,
851 gbt,
852 };
853
854 let rpc_tx_queue_task_handle = tokio::spawn(
856 runner
857 .run(mempool, read_state, latest_chain_tip, network)
858 .in_current_span(),
859 );
860
861 (rpc_impl, rpc_tx_queue_task_handle)
862 }
863}
864
865#[async_trait]
866impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus> RpcServer
867 for RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
868where
869 Mempool: Service<
870 mempool::Request,
871 Response = mempool::Response,
872 Error = zebra_node_services::BoxError,
873 > + Clone
874 + Send
875 + Sync
876 + 'static,
877 Mempool::Future: Send,
878 State: Service<
879 zebra_state::Request,
880 Response = zebra_state::Response,
881 Error = zebra_state::BoxError,
882 > + Clone
883 + Send
884 + Sync
885 + 'static,
886 State::Future: Send,
887 ReadState: Service<
888 zebra_state::ReadRequest,
889 Response = zebra_state::ReadResponse,
890 Error = zebra_state::BoxError,
891 > + Clone
892 + Send
893 + Sync
894 + 'static,
895 ReadState::Future: Send,
896 Tip: ChainTip + Clone + Send + Sync + 'static,
897 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
898 BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
899 + Clone
900 + Send
901 + Sync
902 + 'static,
903 <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
904 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
905{
906 async fn get_info(&self) -> Result<GetInfo> {
907 let version = GetInfo::version(&self.build_version).expect("invalid version string");
908
909 let connections = self.address_book.recently_live_peers(Utc::now()).len();
910
911 let last_error_recorded = self.last_warn_error_log_rx.borrow().clone();
912 let (last_error_log, _level, last_error_log_time) = last_error_recorded.unwrap_or((
913 GetInfo::default().errors,
914 tracing::Level::INFO,
915 Utc::now(),
916 ));
917
918 let tip_height = self
919 .latest_chain_tip
920 .best_tip_height()
921 .unwrap_or(Height::MIN);
922 let testnet = self.network.is_a_test_network();
923
924 let pay_tx_fee = 0.0;
930
931 let relay_fee = zebra_chain::transaction::zip317::MIN_MEMPOOL_TX_FEE_RATE as f64
932 / (zebra_chain::amount::COIN as f64);
933 let difficulty = chain_tip_difficulty(self.network.clone(), self.read_state.clone(), true)
934 .await
935 .expect("should always be Ok when `should_use_default` is true");
936
937 let response = GetInfo {
938 version,
939 build: self.build_version.clone(),
940 subversion: self.user_agent.clone(),
941 protocol_version: zebra_network::constants::CURRENT_NETWORK_PROTOCOL_VERSION.0,
942 blocks: tip_height.0,
943 connections,
944 proxy: None,
945 difficulty,
946 testnet,
947 pay_tx_fee,
948 relay_fee,
949 errors: last_error_log,
950 errors_timestamp: last_error_log_time.to_string(),
951 };
952
953 Ok(response)
954 }
955
956 #[allow(clippy::unwrap_in_result)]
957 async fn get_blockchain_info(&self) -> Result<GetBlockChainInfo> {
958 let debug_force_finished_sync = self.debug_force_finished_sync;
959 let network = &self.network;
960
961 let (usage_info_rsp, tip_pool_values_rsp, chain_tip_difficulty) = {
962 use zebra_state::ReadRequest::*;
963 let state_call = |request| self.read_state.clone().oneshot(request);
964 tokio::join!(
965 state_call(UsageInfo),
966 state_call(TipPoolValues),
967 chain_tip_difficulty(network.clone(), self.read_state.clone(), true)
968 )
969 };
970
971 let (size_on_disk, (tip_height, tip_hash), value_balance, difficulty) = {
972 use zebra_state::ReadResponse::*;
973
974 let UsageInfo(size_on_disk) = usage_info_rsp.map_misc_error()? else {
975 unreachable!("unmatched response to a TipPoolValues request")
976 };
977
978 let (tip, value_balance) = match tip_pool_values_rsp {
979 Ok(TipPoolValues {
980 tip_height,
981 tip_hash,
982 value_balance,
983 }) => ((tip_height, tip_hash), value_balance),
984 Ok(_) => unreachable!("unmatched response to a TipPoolValues request"),
985 Err(_) => ((Height::MIN, network.genesis_hash()), Default::default()),
986 };
987
988 let difficulty = chain_tip_difficulty
989 .expect("should always be Ok when `should_use_default` is true");
990
991 (size_on_disk, tip, value_balance, difficulty)
992 };
993
994 let now = Utc::now();
995 let (estimated_height, verification_progress) = self
996 .latest_chain_tip
997 .best_tip_height_and_block_time()
998 .map(|(tip_height, tip_block_time)| {
999 let height =
1000 NetworkChainTipHeightEstimator::new(tip_block_time, tip_height, network)
1001 .estimate_height_at(now);
1002
1003 let height =
1007 if tip_block_time > now || height < tip_height || debug_force_finished_sync {
1008 tip_height
1009 } else {
1010 height
1011 };
1012
1013 (height, f64::from(tip_height.0) / f64::from(height.0))
1014 })
1015 .unwrap_or((Height::MIN, 0.0));
1017
1018 let mut upgrades = IndexMap::new();
1022 for (activation_height, network_upgrade) in network.full_activation_list() {
1023 if let Some(branch_id) = network_upgrade.branch_id() {
1028 let status = if tip_height >= activation_height {
1030 NetworkUpgradeStatus::Active
1031 } else {
1032 NetworkUpgradeStatus::Pending
1033 };
1034
1035 let upgrade = NetworkUpgradeInfo {
1036 name: network_upgrade,
1037 activation_height,
1038 status,
1039 };
1040 upgrades.insert(ConsensusBranchIdHex(branch_id), upgrade);
1041 }
1042 }
1043
1044 let next_block_height =
1046 (tip_height + 1).expect("valid chain tips are a lot less than Height::MAX");
1047 let consensus = TipConsensusBranch {
1048 chain_tip: ConsensusBranchIdHex(
1049 NetworkUpgrade::current(network, tip_height)
1050 .branch_id()
1051 .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
1052 ),
1053 next_block: ConsensusBranchIdHex(
1054 NetworkUpgrade::current(network, next_block_height)
1055 .branch_id()
1056 .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
1057 ),
1058 };
1059
1060 let response = GetBlockChainInfo {
1061 chain: network.bip70_network_name(),
1062 blocks: tip_height,
1063 best_block_hash: tip_hash,
1064 estimated_height,
1065 chain_supply: get_blockchain_info::Balance::chain_supply(value_balance),
1066 value_pools: get_blockchain_info::Balance::value_pools(value_balance),
1067 upgrades,
1068 consensus,
1069 headers: tip_height,
1070 difficulty,
1071 verification_progress,
1072 chain_work: 0,
1074 pruned: false,
1075 size_on_disk,
1076 commitments: 0,
1078 };
1079
1080 Ok(response)
1081 }
1082
1083 async fn get_address_balance(&self, address_strings: AddressStrings) -> Result<AddressBalance> {
1084 let valid_addresses = address_strings.valid_addresses()?;
1085
1086 let request = zebra_state::ReadRequest::AddressBalance(valid_addresses);
1087 let response = self
1088 .read_state
1089 .clone()
1090 .oneshot(request)
1091 .await
1092 .map_misc_error()?;
1093
1094 match response {
1095 zebra_state::ReadResponse::AddressBalance { balance, received } => Ok(AddressBalance {
1096 balance: u64::from(balance),
1097 received,
1098 }),
1099 _ => unreachable!("Unexpected response from state service: {response:?}"),
1100 }
1101 }
1102
1103 async fn send_raw_transaction(
1105 &self,
1106 raw_transaction_hex: String,
1107 _allow_high_fees: Option<bool>,
1108 ) -> Result<SentTransactionHash> {
1109 let mempool = self.mempool.clone();
1110 let queue_sender = self.queue_sender.clone();
1111
1112 let raw_transaction_bytes = Vec::from_hex(raw_transaction_hex)
1115 .map_error(server::error::LegacyCode::Deserialization)?;
1116 let raw_transaction = Transaction::zcash_deserialize(&*raw_transaction_bytes)
1117 .map_error(server::error::LegacyCode::Deserialization)?;
1118
1119 let transaction_hash = raw_transaction.hash();
1120
1121 let unmined_transaction = UnminedTx::from(raw_transaction.clone());
1123 let _ = queue_sender.send(unmined_transaction);
1124
1125 let transaction_parameter = mempool::Gossip::Tx(raw_transaction.into());
1126 let request = mempool::Request::Queue(vec![transaction_parameter]);
1127
1128 let response = mempool.oneshot(request).await.map_misc_error()?;
1129
1130 let mut queue_results = match response {
1131 mempool::Response::Queued(results) => results,
1132 _ => unreachable!("incorrect response variant from mempool service"),
1133 };
1134
1135 assert_eq!(
1136 queue_results.len(),
1137 1,
1138 "mempool service returned more results than expected"
1139 );
1140
1141 let queue_result = queue_results
1142 .pop()
1143 .expect("there should be exactly one item in Vec")
1144 .inspect_err(|err| tracing::debug!("sent transaction to mempool: {:?}", &err))
1145 .map_misc_error()?
1146 .await
1147 .map_misc_error()?;
1148
1149 tracing::debug!("sent transaction to mempool: {:?}", &queue_result);
1150
1151 queue_result
1152 .map(|_| SentTransactionHash(transaction_hash))
1153 .map_error(server::error::LegacyCode::Verify)
1160 }
1161
1162 async fn get_block(&self, hash_or_height: String, verbosity: Option<u8>) -> Result<GetBlock> {
1171 let verbosity = verbosity.unwrap_or(1);
1172 let network = self.network.clone();
1173 let original_hash_or_height = hash_or_height.clone();
1174
1175 let get_block_header_future = if matches!(verbosity, 1 | 2) {
1177 Some(self.get_block_header(original_hash_or_height.clone(), Some(true)))
1178 } else {
1179 None
1180 };
1181
1182 let hash_or_height =
1183 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1184 .map_error(server::error::LegacyCode::InvalidParameter)?;
1187
1188 if verbosity == 0 {
1189 let request = zebra_state::ReadRequest::Block(hash_or_height);
1190 let response = self
1191 .read_state
1192 .clone()
1193 .oneshot(request)
1194 .await
1195 .map_misc_error()?;
1196
1197 match response {
1198 zebra_state::ReadResponse::Block(Some(block)) => Ok(GetBlock::Raw(block.into())),
1199 zebra_state::ReadResponse::Block(None) => {
1200 Err("Block not found").map_error(server::error::LegacyCode::InvalidParameter)
1201 }
1202 _ => unreachable!("unmatched response to a block request"),
1203 }
1204 } else if let Some(get_block_header_future) = get_block_header_future {
1205 let get_block_header_result: Result<GetBlockHeader> = get_block_header_future.await;
1206
1207 let GetBlockHeader::Object(block_header) = get_block_header_result? else {
1208 panic!("must return Object")
1209 };
1210
1211 let GetBlockHeaderObject {
1212 hash,
1213 confirmations,
1214 height,
1215 version,
1216 merkle_root,
1217 block_commitments,
1218 final_sapling_root,
1219 sapling_tree_size,
1220 time,
1221 nonce,
1222 solution,
1223 bits,
1224 difficulty,
1225 previous_block_hash,
1226 next_block_hash,
1227 } = *block_header;
1228
1229 let transactions_request = match verbosity {
1230 1 => zebra_state::ReadRequest::TransactionIdsForBlock(hash_or_height),
1231 2 => zebra_state::ReadRequest::BlockAndSize(hash_or_height),
1232 _other => panic!("get_block_header_fut should be none"),
1233 };
1234
1235 let hash_or_height = hash.0.into();
1240 let requests = vec![
1241 transactions_request,
1249 zebra_state::ReadRequest::OrchardTree(hash_or_height),
1251 ];
1252
1253 let mut futs = FuturesOrdered::new();
1254
1255 for request in requests {
1256 futs.push_back(self.read_state.clone().oneshot(request));
1257 }
1258
1259 let tx_ids_response = futs.next().await.expect("`futs` should not be empty");
1260 let (tx, size): (Vec<_>, Option<usize>) = match tx_ids_response.map_misc_error()? {
1261 zebra_state::ReadResponse::TransactionIdsForBlock(tx_ids) => (
1262 tx_ids
1263 .ok_or_misc_error("block not found")?
1264 .iter()
1265 .map(|tx_id| GetBlockTransaction::Hash(*tx_id))
1266 .collect(),
1267 None,
1268 ),
1269 zebra_state::ReadResponse::BlockAndSize(block_and_size) => {
1270 let (block, size) = block_and_size.ok_or_misc_error("Block not found")?;
1271 let block_time = block.header.time;
1272 let transactions =
1273 block
1274 .transactions
1275 .iter()
1276 .map(|tx| {
1277 GetBlockTransaction::Object(Box::new(
1278 TransactionObject::from_transaction(
1279 tx.clone(),
1280 Some(height),
1281 Some(confirmations.try_into().expect(
1282 "should be less than max block height, i32::MAX",
1283 )),
1284 &network,
1285 Some(block_time),
1286 Some(hash.0),
1287 Some(true),
1288 tx.hash(),
1289 ),
1290 ))
1291 })
1292 .collect();
1293 (transactions, Some(size))
1294 }
1295 _ => unreachable!("unmatched response to a transaction_ids_for_block request"),
1296 };
1297
1298 let orchard_tree_response = futs.next().await.expect("`futs` should not be empty");
1299 let zebra_state::ReadResponse::OrchardTree(orchard_tree) =
1300 orchard_tree_response.map_misc_error()?
1301 else {
1302 unreachable!("unmatched response to a OrchardTree request");
1303 };
1304
1305 let nu5_activation = NetworkUpgrade::Nu5.activation_height(&network);
1306
1307 let orchard_tree = orchard_tree.ok_or_misc_error("missing Orchard tree")?;
1309
1310 let final_orchard_root = match nu5_activation {
1311 Some(activation_height) if height >= activation_height => {
1312 Some(orchard_tree.root().into())
1313 }
1314 _other => None,
1315 };
1316
1317 let sapling = SaplingTrees {
1318 size: sapling_tree_size,
1319 };
1320
1321 let orchard_tree_size = orchard_tree.count();
1322 let orchard = OrchardTrees {
1323 size: orchard_tree_size,
1324 };
1325
1326 let trees = GetBlockTrees { sapling, orchard };
1327
1328 Ok(GetBlock::Object {
1329 hash,
1330 confirmations,
1331 height: Some(height),
1332 version: Some(version),
1333 merkle_root: Some(merkle_root),
1334 time: Some(time),
1335 nonce: Some(nonce),
1336 solution: Some(solution),
1337 bits: Some(bits),
1338 difficulty: Some(difficulty),
1339 tx,
1340 trees,
1341 size: size.map(|size| size as i64),
1342 block_commitments: Some(block_commitments),
1343 final_sapling_root: Some(final_sapling_root),
1344 final_orchard_root,
1345 previous_block_hash: Some(previous_block_hash),
1346 next_block_hash,
1347 })
1348 } else {
1349 Err("invalid verbosity value").map_error(server::error::LegacyCode::InvalidParameter)
1350 }
1351 }
1352
1353 async fn get_block_header(
1354 &self,
1355 hash_or_height: String,
1356 verbose: Option<bool>,
1357 ) -> Result<GetBlockHeader> {
1358 let verbose = verbose.unwrap_or(true);
1359 let network = self.network.clone();
1360
1361 let hash_or_height =
1362 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1363 .map_error(server::error::LegacyCode::InvalidParameter)?;
1366 let zebra_state::ReadResponse::BlockHeader {
1367 header,
1368 hash,
1369 height,
1370 next_block_hash,
1371 } = self
1372 .read_state
1373 .clone()
1374 .oneshot(zebra_state::ReadRequest::BlockHeader(hash_or_height))
1375 .await
1376 .map_err(|_| "block height not in best chain")
1377 .map_error(
1378 if hash_or_height.hash().is_some() {
1383 server::error::LegacyCode::InvalidAddressOrKey
1384 } else {
1385 server::error::LegacyCode::InvalidParameter
1386 },
1387 )?
1388 else {
1389 panic!("unexpected response to BlockHeader request")
1390 };
1391
1392 let response = if !verbose {
1393 GetBlockHeader::Raw(HexData(header.zcash_serialize_to_vec().map_misc_error()?))
1394 } else {
1395 let zebra_state::ReadResponse::SaplingTree(sapling_tree) = self
1396 .read_state
1397 .clone()
1398 .oneshot(zebra_state::ReadRequest::SaplingTree(hash_or_height))
1399 .await
1400 .map_misc_error()?
1401 else {
1402 panic!("unexpected response to SaplingTree request")
1403 };
1404
1405 let sapling_tree = sapling_tree.ok_or_misc_error("missing Sapling tree")?;
1407
1408 let zebra_state::ReadResponse::Depth(depth) = self
1409 .read_state
1410 .clone()
1411 .oneshot(zebra_state::ReadRequest::Depth(hash))
1412 .await
1413 .map_misc_error()?
1414 else {
1415 panic!("unexpected response to SaplingTree request")
1416 };
1417
1418 const NOT_IN_BEST_CHAIN_CONFIRMATIONS: i64 = -1;
1421
1422 let confirmations = depth
1425 .map(|depth| i64::from(depth) + 1)
1426 .unwrap_or(NOT_IN_BEST_CHAIN_CONFIRMATIONS);
1427
1428 let mut nonce = *header.nonce;
1429 nonce.reverse();
1430
1431 let sapling_activation = NetworkUpgrade::Sapling.activation_height(&network);
1432 let sapling_tree_size = sapling_tree.count();
1433 let final_sapling_root: [u8; 32] =
1434 if sapling_activation.is_some() && height >= sapling_activation.unwrap() {
1435 let mut root: [u8; 32] = sapling_tree.root().into();
1436 root.reverse();
1437 root
1438 } else {
1439 [0; 32]
1440 };
1441
1442 let difficulty = header.difficulty_threshold.relative_to_network(&network);
1443
1444 let block_commitments = match header.commitment(&network, height).expect(
1445 "Unexpected failure while parsing the blockcommitments field in get_block_header",
1446 ) {
1447 Commitment::PreSaplingReserved(bytes) => bytes,
1448 Commitment::FinalSaplingRoot(_) => final_sapling_root,
1449 Commitment::ChainHistoryActivationReserved => [0; 32],
1450 Commitment::ChainHistoryRoot(root) => root.bytes_in_display_order(),
1451 Commitment::ChainHistoryBlockTxAuthCommitment(hash) => {
1452 hash.bytes_in_display_order()
1453 }
1454 };
1455
1456 let block_header = GetBlockHeaderObject {
1457 hash: GetBlockHash(hash),
1458 confirmations,
1459 height,
1460 version: header.version,
1461 merkle_root: header.merkle_root,
1462 block_commitments,
1463 final_sapling_root,
1464 sapling_tree_size,
1465 time: header.time.timestamp(),
1466 nonce,
1467 solution: header.solution,
1468 bits: header.difficulty_threshold,
1469 difficulty,
1470 previous_block_hash: GetBlockHash(header.previous_block_hash),
1471 next_block_hash: next_block_hash.map(GetBlockHash),
1472 };
1473
1474 GetBlockHeader::Object(Box::new(block_header))
1475 };
1476
1477 Ok(response)
1478 }
1479
1480 fn get_best_block_hash(&self) -> Result<GetBlockHash> {
1481 self.latest_chain_tip
1482 .best_tip_hash()
1483 .map(GetBlockHash)
1484 .ok_or_misc_error("No blocks in state")
1485 }
1486
1487 fn get_best_block_height_and_hash(&self) -> Result<GetBlockHeightAndHash> {
1488 self.latest_chain_tip
1489 .best_tip_height_and_hash()
1490 .map(|(height, hash)| GetBlockHeightAndHash { height, hash })
1491 .ok_or_misc_error("No blocks in state")
1492 }
1493
1494 async fn get_raw_mempool(&self, verbose: Option<bool>) -> Result<GetRawMempool> {
1495 #[allow(unused)]
1496 let verbose = verbose.unwrap_or(false);
1497
1498 use zebra_chain::block::MAX_BLOCK_BYTES;
1499
1500 let mut mempool = self.mempool.clone();
1501
1502 let request = if verbose {
1503 mempool::Request::FullTransactions
1504 } else {
1505 mempool::Request::TransactionIds
1506 };
1507
1508 let response = mempool
1510 .ready()
1511 .and_then(|service| service.call(request))
1512 .await
1513 .map_misc_error()?;
1514
1515 match response {
1516 mempool::Response::FullTransactions {
1517 mut transactions,
1518 transaction_dependencies,
1519 last_seen_tip_hash: _,
1520 } => {
1521 if verbose {
1522 let map = transactions
1523 .iter()
1524 .map(|unmined_tx| {
1525 (
1526 unmined_tx.transaction.id.mined_id().encode_hex(),
1527 get_raw_mempool::MempoolObject::from_verified_unmined_tx(
1528 unmined_tx,
1529 &transactions,
1530 &transaction_dependencies,
1531 ),
1532 )
1533 })
1534 .collect::<HashMap<_, _>>();
1535 Ok(GetRawMempool::Verbose(map))
1536 } else {
1537 transactions.sort_by_cached_key(|tx| {
1542 cmp::Reverse((
1545 i64::from(tx.miner_fee) as u128 * MAX_BLOCK_BYTES as u128
1546 / tx.transaction.size as u128,
1547 tx.transaction.id.mined_id(),
1549 ))
1550 });
1551 let tx_ids: Vec<String> = transactions
1552 .iter()
1553 .map(|unmined_tx| unmined_tx.transaction.id.mined_id().encode_hex())
1554 .collect();
1555
1556 Ok(GetRawMempool::TxIds(tx_ids))
1557 }
1558 }
1559
1560 mempool::Response::TransactionIds(unmined_transaction_ids) => {
1561 let mut tx_ids: Vec<String> = unmined_transaction_ids
1562 .iter()
1563 .map(|id| id.mined_id().encode_hex())
1564 .collect();
1565
1566 tx_ids.sort();
1568
1569 Ok(GetRawMempool::TxIds(tx_ids))
1570 }
1571
1572 _ => unreachable!("unmatched response to a transactionids request"),
1573 }
1574 }
1575
1576 async fn get_raw_transaction(
1577 &self,
1578 txid: String,
1579 verbose: Option<u8>,
1580 block_hash: Option<String>,
1581 ) -> Result<GetRawTransaction> {
1582 let mut mempool = self.mempool.clone();
1583 let verbose = verbose.unwrap_or(0) != 0;
1584
1585 let txid = transaction::Hash::from_hex(txid)
1588 .map_error(server::error::LegacyCode::InvalidAddressOrKey)?;
1589
1590 if block_hash.is_none() {
1592 match mempool
1593 .ready()
1594 .and_then(|service| {
1595 service.call(mempool::Request::TransactionsByMinedId([txid].into()))
1596 })
1597 .await
1598 .map_misc_error()?
1599 {
1600 mempool::Response::Transactions(txns) => {
1601 if let Some(tx) = txns.first() {
1602 return Ok(if verbose {
1603 GetRawTransaction::Object(Box::new(
1604 TransactionObject::from_transaction(
1605 tx.transaction.clone(),
1606 None,
1607 None,
1608 &self.network,
1609 None,
1610 None,
1611 Some(false),
1612 txid,
1613 ),
1614 ))
1615 } else {
1616 let hex = tx.transaction.clone().into();
1617 GetRawTransaction::Raw(hex)
1618 });
1619 }
1620 }
1621
1622 _ => unreachable!("unmatched response to a `TransactionsByMinedId` request"),
1623 };
1624 }
1625
1626 let txid = if let Some(block_hash) = block_hash {
1628 let block_hash = block::Hash::from_hex(block_hash)
1629 .map_error(server::error::LegacyCode::InvalidAddressOrKey)?;
1630 match self
1631 .read_state
1632 .clone()
1633 .oneshot(zebra_state::ReadRequest::TransactionIdsForBlock(
1634 block_hash.into(),
1635 ))
1636 .await
1637 .map_misc_error()?
1638 {
1639 zebra_state::ReadResponse::TransactionIdsForBlock(tx_ids) => *tx_ids
1640 .ok_or_error(
1641 server::error::LegacyCode::InvalidAddressOrKey,
1642 "block not found",
1643 )?
1644 .iter()
1645 .find(|id| **id == txid)
1646 .ok_or_error(
1647 server::error::LegacyCode::InvalidAddressOrKey,
1648 "txid not found",
1649 )?,
1650 _ => unreachable!("unmatched response to a `TransactionsByMinedId` request"),
1651 }
1652 } else {
1653 txid
1654 };
1655
1656 match self
1658 .read_state
1659 .clone()
1660 .oneshot(zebra_state::ReadRequest::Transaction(txid))
1661 .await
1662 .map_misc_error()?
1663 {
1664 zebra_state::ReadResponse::Transaction(Some(tx)) => Ok(if verbose {
1665 let block_hash = match self
1666 .read_state
1667 .clone()
1668 .oneshot(zebra_state::ReadRequest::BestChainBlockHash(tx.height))
1669 .await
1670 .map_misc_error()?
1671 {
1672 zebra_state::ReadResponse::BlockHash(block_hash) => block_hash,
1673 _ => unreachable!("unmatched response to a `TransactionsByMinedId` request"),
1674 };
1675
1676 GetRawTransaction::Object(Box::new(TransactionObject::from_transaction(
1677 tx.tx.clone(),
1678 Some(tx.height),
1679 Some(tx.confirmations),
1680 &self.network,
1681 Some(tx.block_time),
1684 block_hash,
1685 Some(true),
1686 txid,
1687 )))
1688 } else {
1689 let hex = tx.tx.into();
1690 GetRawTransaction::Raw(hex)
1691 }),
1692
1693 zebra_state::ReadResponse::Transaction(None) => {
1694 Err("No such mempool or main chain transaction")
1695 .map_error(server::error::LegacyCode::InvalidAddressOrKey)
1696 }
1697
1698 _ => unreachable!("unmatched response to a `Transaction` read request"),
1699 }
1700 }
1701
1702 async fn z_get_treestate(&self, hash_or_height: String) -> Result<GetTreestate> {
1706 let mut read_state = self.read_state.clone();
1707 let network = self.network.clone();
1708
1709 let hash_or_height =
1710 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1711 .map_error(server::error::LegacyCode::InvalidParameter)?;
1714
1715 let block = match read_state
1724 .ready()
1725 .and_then(|service| service.call(zebra_state::ReadRequest::Block(hash_or_height)))
1726 .await
1727 .map_misc_error()?
1728 {
1729 zebra_state::ReadResponse::Block(Some(block)) => block,
1730 zebra_state::ReadResponse::Block(None) => {
1731 return Err("the requested block is not in the main chain")
1734 .map_error(server::error::LegacyCode::InvalidParameter);
1735 }
1736 _ => unreachable!("unmatched response to a block request"),
1737 };
1738
1739 let hash = hash_or_height
1740 .hash_or_else(|_| Some(block.hash()))
1741 .expect("block hash");
1742
1743 let height = hash_or_height
1744 .height_or_else(|_| block.coinbase_height())
1745 .expect("verified blocks have a coinbase height");
1746
1747 let time = u32::try_from(block.header.time.timestamp())
1748 .expect("Timestamps of valid blocks always fit into u32.");
1749
1750 let sapling_nu = zcash_primitives::consensus::NetworkUpgrade::Sapling;
1751 let sapling = if network.is_nu_active(sapling_nu, height.into()) {
1752 match read_state
1753 .ready()
1754 .and_then(|service| {
1755 service.call(zebra_state::ReadRequest::SaplingTree(hash.into()))
1756 })
1757 .await
1758 .map_misc_error()?
1759 {
1760 zebra_state::ReadResponse::SaplingTree(tree) => tree.map(|t| t.to_rpc_bytes()),
1761 _ => unreachable!("unmatched response to a Sapling tree request"),
1762 }
1763 } else {
1764 None
1765 };
1766
1767 let orchard_nu = zcash_primitives::consensus::NetworkUpgrade::Nu5;
1768 let orchard = if network.is_nu_active(orchard_nu, height.into()) {
1769 match read_state
1770 .ready()
1771 .and_then(|service| {
1772 service.call(zebra_state::ReadRequest::OrchardTree(hash.into()))
1773 })
1774 .await
1775 .map_misc_error()?
1776 {
1777 zebra_state::ReadResponse::OrchardTree(tree) => tree.map(|t| t.to_rpc_bytes()),
1778 _ => unreachable!("unmatched response to an Orchard tree request"),
1779 }
1780 } else {
1781 None
1782 };
1783
1784 Ok(GetTreestate::from_parts(
1785 hash, height, time, sapling, orchard,
1786 ))
1787 }
1788
1789 async fn z_get_subtrees_by_index(
1790 &self,
1791 pool: String,
1792 start_index: NoteCommitmentSubtreeIndex,
1793 limit: Option<NoteCommitmentSubtreeIndex>,
1794 ) -> Result<GetSubtrees> {
1795 let mut read_state = self.read_state.clone();
1796
1797 const POOL_LIST: &[&str] = &["sapling", "orchard"];
1798
1799 if pool == "sapling" {
1800 let request = zebra_state::ReadRequest::SaplingSubtrees { start_index, limit };
1801 let response = read_state
1802 .ready()
1803 .and_then(|service| service.call(request))
1804 .await
1805 .map_misc_error()?;
1806
1807 let subtrees = match response {
1808 zebra_state::ReadResponse::SaplingSubtrees(subtrees) => subtrees,
1809 _ => unreachable!("unmatched response to a subtrees request"),
1810 };
1811
1812 let subtrees = subtrees
1813 .values()
1814 .map(|subtree| SubtreeRpcData {
1815 root: subtree.root.encode_hex(),
1816 end_height: subtree.end_height,
1817 })
1818 .collect();
1819
1820 Ok(GetSubtrees {
1821 pool,
1822 start_index,
1823 subtrees,
1824 })
1825 } else if pool == "orchard" {
1826 let request = zebra_state::ReadRequest::OrchardSubtrees { start_index, limit };
1827 let response = read_state
1828 .ready()
1829 .and_then(|service| service.call(request))
1830 .await
1831 .map_misc_error()?;
1832
1833 let subtrees = match response {
1834 zebra_state::ReadResponse::OrchardSubtrees(subtrees) => subtrees,
1835 _ => unreachable!("unmatched response to a subtrees request"),
1836 };
1837
1838 let subtrees = subtrees
1839 .values()
1840 .map(|subtree| SubtreeRpcData {
1841 root: subtree.root.encode_hex(),
1842 end_height: subtree.end_height,
1843 })
1844 .collect();
1845
1846 Ok(GetSubtrees {
1847 pool,
1848 start_index,
1849 subtrees,
1850 })
1851 } else {
1852 Err(ErrorObject::owned(
1853 server::error::LegacyCode::Misc.into(),
1854 format!("invalid pool name, must be one of: {:?}", POOL_LIST).as_str(),
1855 None::<()>,
1856 ))
1857 }
1858 }
1859
1860 async fn get_address_tx_ids(&self, request: GetAddressTxIdsRequest) -> Result<Vec<String>> {
1861 let mut read_state = self.read_state.clone();
1862 let latest_chain_tip = self.latest_chain_tip.clone();
1863
1864 let height_range = build_height_range(
1865 request.start,
1866 request.end,
1867 best_chain_tip_height(&latest_chain_tip)?,
1868 )?;
1869
1870 let valid_addresses = AddressStrings {
1871 addresses: request.addresses,
1872 }
1873 .valid_addresses()?;
1874
1875 let request = zebra_state::ReadRequest::TransactionIdsByAddresses {
1876 addresses: valid_addresses,
1877 height_range,
1878 };
1879 let response = read_state
1880 .ready()
1881 .and_then(|service| service.call(request))
1882 .await
1883 .map_misc_error()?;
1884
1885 let hashes = match response {
1886 zebra_state::ReadResponse::AddressesTransactionIds(hashes) => {
1887 let mut last_tx_location = TransactionLocation::from_usize(Height(0), 0);
1888
1889 hashes
1890 .iter()
1891 .map(|(tx_loc, tx_id)| {
1892 assert!(
1894 *tx_loc > last_tx_location,
1895 "Transactions were not in chain order:\n\
1896 {tx_loc:?} {tx_id:?} was after:\n\
1897 {last_tx_location:?}",
1898 );
1899
1900 last_tx_location = *tx_loc;
1901
1902 tx_id.to_string()
1903 })
1904 .collect()
1905 }
1906 _ => unreachable!("unmatched response to a TransactionsByAddresses request"),
1907 };
1908
1909 Ok(hashes)
1910 }
1911
1912 async fn get_address_utxos(
1913 &self,
1914 address_strings: AddressStrings,
1915 ) -> Result<Vec<GetAddressUtxos>> {
1916 let mut read_state = self.read_state.clone();
1917 let mut response_utxos = vec![];
1918
1919 let valid_addresses = address_strings.valid_addresses()?;
1920
1921 let request = zebra_state::ReadRequest::UtxosByAddresses(valid_addresses);
1923 let response = read_state
1924 .ready()
1925 .and_then(|service| service.call(request))
1926 .await
1927 .map_misc_error()?;
1928 let utxos = match response {
1929 zebra_state::ReadResponse::AddressUtxos(utxos) => utxos,
1930 _ => unreachable!("unmatched response to a UtxosByAddresses request"),
1931 };
1932
1933 let mut last_output_location = OutputLocation::from_usize(Height(0), 0, 0);
1934
1935 for utxo_data in utxos.utxos() {
1936 let address = utxo_data.0;
1937 let txid = *utxo_data.1;
1938 let height = utxo_data.2.height();
1939 let output_index = utxo_data.2.output_index();
1940 let script = utxo_data.3.lock_script.clone();
1941 let satoshis = u64::from(utxo_data.3.value);
1942
1943 let output_location = *utxo_data.2;
1944 assert!(
1946 output_location > last_output_location,
1947 "UTXOs were not in chain order:\n\
1948 {output_location:?} {address:?} {txid:?} was after:\n\
1949 {last_output_location:?}",
1950 );
1951
1952 let entry = GetAddressUtxos {
1953 address,
1954 txid,
1955 output_index,
1956 script,
1957 satoshis,
1958 height,
1959 };
1960 response_utxos.push(entry);
1961
1962 last_output_location = output_location;
1963 }
1964
1965 Ok(response_utxos)
1966 }
1967
1968 fn stop(&self) -> Result<String> {
1969 #[cfg(not(target_os = "windows"))]
1970 if self.network.is_regtest() {
1971 match nix::sys::signal::raise(nix::sys::signal::SIGINT) {
1972 Ok(_) => Ok("Zebra server stopping".to_string()),
1973 Err(error) => Err(ErrorObject::owned(
1974 ErrorCode::InternalError.code(),
1975 format!("Failed to shut down: {}", error).as_str(),
1976 None::<()>,
1977 )),
1978 }
1979 } else {
1980 Err(ErrorObject::borrowed(
1981 ErrorCode::MethodNotFound.code(),
1982 "stop is only available on regtest networks",
1983 None,
1984 ))
1985 }
1986 #[cfg(target_os = "windows")]
1987 Err(ErrorObject::borrowed(
1988 ErrorCode::MethodNotFound.code(),
1989 "stop is not available in windows targets",
1990 None,
1991 ))
1992 }
1993
1994 fn get_block_count(&self) -> Result<u32> {
1995 best_chain_tip_height(&self.latest_chain_tip).map(|height| height.0)
1996 }
1997
1998 async fn get_block_hash(&self, index: i32) -> Result<GetBlockHash> {
1999 let mut read_state = self.read_state.clone();
2000 let latest_chain_tip = self.latest_chain_tip.clone();
2001
2002 let tip_height = best_chain_tip_height(&latest_chain_tip)?;
2004
2005 let height = height_from_signed_int(index, tip_height)?;
2006
2007 let request = zebra_state::ReadRequest::BestChainBlockHash(height);
2008 let response = read_state
2009 .ready()
2010 .and_then(|service| service.call(request))
2011 .await
2012 .map_error(server::error::LegacyCode::default())?;
2013
2014 match response {
2015 zebra_state::ReadResponse::BlockHash(Some(hash)) => Ok(GetBlockHash(hash)),
2016 zebra_state::ReadResponse::BlockHash(None) => Err(ErrorObject::borrowed(
2017 server::error::LegacyCode::InvalidParameter.into(),
2018 "Block not found",
2019 None,
2020 )),
2021 _ => unreachable!("unmatched response to a block request"),
2022 }
2023 }
2024
2025 async fn get_block_template(
2026 &self,
2027 parameters: Option<get_block_template::JsonParameters>,
2028 ) -> Result<get_block_template::Response> {
2029 let network = self.network.clone();
2031 let extra_coinbase_data = self.gbt.extra_coinbase_data();
2032
2033 let mempool = self.mempool.clone();
2035 let mut latest_chain_tip = self.latest_chain_tip.clone();
2036 let sync_status = self.gbt.sync_status();
2037 let read_state = self.read_state.clone();
2038
2039 if let Some(HexData(block_proposal_bytes)) = parameters
2040 .as_ref()
2041 .and_then(get_block_template::JsonParameters::block_proposal_data)
2042 {
2043 return get_block_template::validate_block_proposal(
2044 self.gbt.block_verifier_router(),
2045 block_proposal_bytes,
2046 network,
2047 latest_chain_tip,
2048 sync_status,
2049 )
2050 .await;
2051 }
2052
2053 get_block_template::check_parameters(¶meters)?;
2055
2056 let client_long_poll_id = parameters.as_ref().and_then(|params| params.long_poll_id);
2057
2058 let miner_address = self
2059 .gbt
2060 .miner_address()
2061 .ok_or_misc_error("miner_address not configured")?;
2062
2063 let mut max_time_reached = false;
2067
2068 let (
2071 server_long_poll_id,
2072 chain_tip_and_local_time,
2073 mempool_txs,
2074 mempool_tx_deps,
2075 submit_old,
2076 ) = loop {
2077 get_block_template::check_synced_to_tip(
2083 &network,
2084 latest_chain_tip.clone(),
2085 sync_status.clone(),
2086 )?;
2087 latest_chain_tip.mark_best_tip_seen();
2095
2096 let chain_tip_and_local_time @ zebra_state::GetBlockTemplateChainInfo {
2103 tip_hash,
2104 tip_height,
2105 max_time,
2106 cur_time,
2107 ..
2108 } = get_block_template::fetch_state_tip_and_local_time(read_state.clone()).await?;
2109
2110 let Some((mempool_txs, mempool_tx_deps)) =
2121 get_block_template::fetch_mempool_transactions(mempool.clone(), tip_hash)
2122 .await?
2123 .or_else(|| client_long_poll_id.is_none().then(Default::default))
2127 else {
2128 continue;
2129 };
2130
2131 let server_long_poll_id = LongPollInput::new(
2133 tip_height,
2134 tip_hash,
2135 max_time,
2136 mempool_txs.iter().map(|tx| tx.transaction.id),
2137 )
2138 .generate_id();
2139
2140 if Some(&server_long_poll_id) != client_long_poll_id.as_ref() || max_time_reached {
2145 let mut submit_old = client_long_poll_id
2146 .as_ref()
2147 .map(|old_long_poll_id| server_long_poll_id.submit_old(old_long_poll_id));
2148
2149 if max_time_reached {
2154 submit_old = Some(false);
2155 }
2156
2157 break (
2158 server_long_poll_id,
2159 chain_tip_and_local_time,
2160 mempool_txs,
2161 mempool_tx_deps,
2162 submit_old,
2163 );
2164 }
2165
2166 let wait_for_mempool_request =
2176 tokio::time::sleep(Duration::from_secs(MEMPOOL_LONG_POLL_INTERVAL));
2177
2178 let mut wait_for_best_tip_change = latest_chain_tip.clone();
2181 let wait_for_best_tip_change = wait_for_best_tip_change.best_tip_changed();
2182
2183 let duration_until_max_time = max_time.saturating_duration_since(cur_time);
2195 let wait_for_max_time: OptionFuture<_> = if duration_until_max_time.seconds() > 0 {
2196 Some(tokio::time::sleep(duration_until_max_time.to_std()))
2197 } else {
2198 None
2199 }
2200 .into();
2201
2202 tokio::select! {
2209 biased;
2212
2213 _elapsed = wait_for_mempool_request => {
2215 tracing::debug!(
2216 ?max_time,
2217 ?cur_time,
2218 ?server_long_poll_id,
2219 ?client_long_poll_id,
2220 MEMPOOL_LONG_POLL_INTERVAL,
2221 "checking for a new mempool change after waiting a few seconds"
2222 );
2223 }
2224
2225 tip_changed_result = wait_for_best_tip_change => {
2227 match tip_changed_result {
2228 Ok(()) => {
2229 latest_chain_tip.mark_best_tip_seen();
2233
2234 let new_tip_hash = latest_chain_tip.best_tip_hash();
2235 if new_tip_hash == Some(tip_hash) {
2236 tracing::debug!(
2237 ?max_time,
2238 ?cur_time,
2239 ?server_long_poll_id,
2240 ?client_long_poll_id,
2241 ?tip_hash,
2242 ?tip_height,
2243 "ignoring spurious state change notification"
2244 );
2245
2246 tokio::time::sleep(Duration::from_secs(
2248 MEMPOOL_LONG_POLL_INTERVAL,
2249 )).await;
2250
2251 continue;
2252 }
2253
2254 tracing::debug!(
2255 ?max_time,
2256 ?cur_time,
2257 ?server_long_poll_id,
2258 ?client_long_poll_id,
2259 "returning from long poll because state has changed"
2260 );
2261 }
2262
2263 Err(recv_error) => {
2264 tracing::info!(
2266 ?recv_error,
2267 ?max_time,
2268 ?cur_time,
2269 ?server_long_poll_id,
2270 ?client_long_poll_id,
2271 "returning from long poll due to a state error.\
2272 Is Zebra shutting down?"
2273 );
2274
2275 return Err(recv_error).map_error(server::error::LegacyCode::default());
2276 }
2277 }
2278 }
2279
2280 Some(_elapsed) = wait_for_max_time => {
2283 tracing::info!(
2285 ?max_time,
2286 ?cur_time,
2287 ?server_long_poll_id,
2288 ?client_long_poll_id,
2289 "returning from long poll because max time was reached"
2290 );
2291
2292 max_time_reached = true;
2293 }
2294 }
2295 };
2296
2297 let next_block_height =
2305 (chain_tip_and_local_time.tip_height + 1).expect("tip is far below Height::MAX");
2306
2307 tracing::debug!(
2308 mempool_tx_hashes = ?mempool_txs
2309 .iter()
2310 .map(|tx| tx.transaction.id.mined_id())
2311 .collect::<Vec<_>>(),
2312 "selecting transactions for the template from the mempool"
2313 );
2314
2315 let mempool_txs = get_block_template::zip317::select_mempool_transactions(
2317 &network,
2318 next_block_height,
2319 &miner_address,
2320 mempool_txs,
2321 mempool_tx_deps,
2322 extra_coinbase_data.clone(),
2323 );
2324
2325 tracing::debug!(
2326 selected_mempool_tx_hashes = ?mempool_txs
2327 .iter()
2328 .map(|#[cfg(not(test))] tx, #[cfg(test)] (_, tx)| tx.transaction.id.mined_id())
2329 .collect::<Vec<_>>(),
2330 "selected transactions for the template from the mempool"
2331 );
2332
2333 let response = GetBlockTemplate::new(
2336 &network,
2337 &miner_address,
2338 &chain_tip_and_local_time,
2339 server_long_poll_id,
2340 mempool_txs,
2341 submit_old,
2342 extra_coinbase_data,
2343 );
2344
2345 Ok(response.into())
2346 }
2347
2348 async fn submit_block(
2349 &self,
2350 HexData(block_bytes): HexData,
2351 _parameters: Option<submit_block::JsonParameters>,
2352 ) -> Result<submit_block::Response> {
2353 let mut block_verifier_router = self.gbt.block_verifier_router();
2354
2355 let block: Block = match block_bytes.zcash_deserialize_into() {
2356 Ok(block_bytes) => block_bytes,
2357 Err(error) => {
2358 tracing::info!(
2359 ?error,
2360 "submit block failed: block bytes could not be deserialized into a structurally valid block"
2361 );
2362
2363 return Ok(submit_block::ErrorResponse::Rejected.into());
2364 }
2365 };
2366
2367 let height = block
2368 .coinbase_height()
2369 .ok_or_error(0, "coinbase height not found")?;
2370 let block_hash = block.hash();
2371
2372 let block_verifier_router_response = block_verifier_router
2373 .ready()
2374 .await
2375 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?
2376 .call(zebra_consensus::Request::Commit(Arc::new(block)))
2377 .await;
2378
2379 let chain_error = match block_verifier_router_response {
2380 Ok(hash) => {
2387 tracing::info!(?hash, ?height, "submit block accepted");
2388
2389 self.gbt
2390 .advertise_mined_block(hash, height)
2391 .map_error_with_prefix(0, "failed to send mined block")?;
2392
2393 return Ok(submit_block::Response::Accepted);
2394 }
2395
2396 Err(box_error) => {
2399 let error = box_error
2400 .downcast::<RouterError>()
2401 .map(|boxed_chain_error| *boxed_chain_error);
2402
2403 tracing::info!(
2404 ?error,
2405 ?block_hash,
2406 ?height,
2407 "submit block failed verification"
2408 );
2409
2410 error
2411 }
2412 };
2413
2414 let response = match chain_error {
2415 Ok(source) if source.is_duplicate_request() => submit_block::ErrorResponse::Duplicate,
2416
2417 Ok(_verify_chain_error) => submit_block::ErrorResponse::Rejected,
2433
2434 Err(_unknown_error_type) => submit_block::ErrorResponse::Rejected,
2437 };
2438
2439 Ok(response.into())
2440 }
2441
2442 async fn get_mining_info(&self) -> Result<get_mining_info::Response> {
2443 let network = self.network.clone();
2444 let mut read_state = self.read_state.clone();
2445
2446 let chain_tip = self.latest_chain_tip.clone();
2447 let tip_height = chain_tip.best_tip_height().unwrap_or(Height(0)).0;
2448
2449 let mut current_block_tx = None;
2450 if tip_height > 0 {
2451 let mined_tx_ids = chain_tip.best_tip_mined_transaction_ids();
2452 current_block_tx =
2453 (!mined_tx_ids.is_empty()).then(|| mined_tx_ids.len().saturating_sub(1));
2454 }
2455
2456 let solution_rate_fut = self.get_network_sol_ps(None, None);
2457 let mut current_block_size = None;
2459 if tip_height > 0 {
2460 let request = zebra_state::ReadRequest::TipBlockSize;
2461 let response: zebra_state::ReadResponse = read_state
2462 .ready()
2463 .and_then(|service| service.call(request))
2464 .await
2465 .map_error(server::error::LegacyCode::default())?;
2466 current_block_size = match response {
2467 zebra_state::ReadResponse::TipBlockSize(Some(block_size)) => Some(block_size),
2468 _ => None,
2469 };
2470 }
2471
2472 Ok(get_mining_info::Response::new(
2473 tip_height,
2474 current_block_size,
2475 current_block_tx,
2476 network,
2477 solution_rate_fut.await?,
2478 ))
2479 }
2480
2481 async fn get_network_sol_ps(
2482 &self,
2483 num_blocks: Option<i32>,
2484 height: Option<i32>,
2485 ) -> Result<u64> {
2486 let mut num_blocks =
2488 num_blocks.unwrap_or(get_block_template::DEFAULT_SOLUTION_RATE_WINDOW_SIZE);
2489 if num_blocks < 1 {
2491 num_blocks = i32::try_from(POW_AVERAGING_WINDOW).expect("fits in i32");
2492 }
2493 let num_blocks =
2494 usize::try_from(num_blocks).expect("just checked for negatives, i32 fits in usize");
2495
2496 let height = height.and_then(|height| height.try_into_height().ok());
2499
2500 let mut read_state = self.read_state.clone();
2501
2502 let request = ReadRequest::SolutionRate { num_blocks, height };
2503
2504 let response = read_state
2505 .ready()
2506 .and_then(|service| service.call(request))
2507 .await
2508 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
2509
2510 let solution_rate = match response {
2511 ReadResponse::SolutionRate(solution_rate) => solution_rate.unwrap_or(0),
2513
2514 _ => unreachable!("unmatched response to a solution rate request"),
2515 };
2516
2517 Ok(solution_rate
2518 .try_into()
2519 .expect("per-second solution rate always fits in u64"))
2520 }
2521
2522 async fn get_peer_info(&self) -> Result<Vec<PeerInfo>> {
2523 let address_book = self.address_book.clone();
2524 Ok(address_book
2525 .recently_live_peers(chrono::Utc::now())
2526 .into_iter()
2527 .map(PeerInfo::from)
2528 .collect())
2529 }
2530
2531 async fn validate_address(&self, raw_address: String) -> Result<validate_address::Response> {
2532 let network = self.network.clone();
2533
2534 let Ok(address) = raw_address.parse::<zcash_address::ZcashAddress>() else {
2535 return Ok(validate_address::Response::invalid());
2536 };
2537
2538 let address = match address.convert::<primitives::Address>() {
2539 Ok(address) => address,
2540 Err(err) => {
2541 tracing::debug!(?err, "conversion error");
2542 return Ok(validate_address::Response::invalid());
2543 }
2544 };
2545
2546 if !address.is_transparent() {
2548 return Ok(validate_address::Response::invalid());
2549 }
2550
2551 if address.network() == network.kind() {
2552 Ok(validate_address::Response {
2553 address: Some(raw_address),
2554 is_valid: true,
2555 is_script: Some(address.is_script_hash()),
2556 })
2557 } else {
2558 tracing::info!(
2559 ?network,
2560 address_network = ?address.network(),
2561 "invalid address in validateaddress RPC: Zebra's configured network must match address network"
2562 );
2563
2564 Ok(validate_address::Response::invalid())
2565 }
2566 }
2567
2568 async fn z_validate_address(
2569 &self,
2570 raw_address: String,
2571 ) -> Result<z_validate_address::Response> {
2572 let network = self.network.clone();
2573
2574 let Ok(address) = raw_address.parse::<zcash_address::ZcashAddress>() else {
2575 return Ok(z_validate_address::Response::invalid());
2576 };
2577
2578 let address = match address.convert::<primitives::Address>() {
2579 Ok(address) => address,
2580 Err(err) => {
2581 tracing::debug!(?err, "conversion error");
2582 return Ok(z_validate_address::Response::invalid());
2583 }
2584 };
2585
2586 if address.network() == network.kind() {
2587 Ok(z_validate_address::Response {
2588 is_valid: true,
2589 address: Some(raw_address),
2590 address_type: Some(z_validate_address::AddressType::from(&address)),
2591 is_mine: Some(false),
2592 })
2593 } else {
2594 tracing::info!(
2595 ?network,
2596 address_network = ?address.network(),
2597 "invalid address network in z_validateaddress RPC: address is for {:?} but Zebra is on {:?}",
2598 address.network(),
2599 network
2600 );
2601
2602 Ok(z_validate_address::Response::invalid())
2603 }
2604 }
2605
2606 async fn get_block_subsidy(&self, height: Option<u32>) -> Result<BlockSubsidy> {
2607 let latest_chain_tip = self.latest_chain_tip.clone();
2608 let network = self.network.clone();
2609
2610 let height = if let Some(height) = height {
2611 Height(height)
2612 } else {
2613 best_chain_tip_height(&latest_chain_tip)?
2614 };
2615
2616 if height < network.height_for_first_halving() {
2617 return Err(ErrorObject::borrowed(
2618 0,
2619 "Zebra does not support founders' reward subsidies, \
2620 use a block height that is after the first halving",
2621 None,
2622 ));
2623 }
2624
2625 let founders = Amount::zero();
2627
2628 let total_block_subsidy =
2629 block_subsidy(height, &network).map_error(server::error::LegacyCode::default())?;
2630 let miner_subsidy = miner_subsidy(height, &network, total_block_subsidy)
2631 .map_error(server::error::LegacyCode::default())?;
2632
2633 let (lockbox_streams, mut funding_streams): (Vec<_>, Vec<_>) =
2634 funding_stream_values(height, &network, total_block_subsidy)
2635 .map_error(server::error::LegacyCode::default())?
2636 .into_iter()
2637 .partition(|(receiver, _)| matches!(receiver, FundingStreamReceiver::Deferred));
2639
2640 let is_nu6 = NetworkUpgrade::current(&network, height) == NetworkUpgrade::Nu6;
2641
2642 let [lockbox_total, funding_streams_total]: [std::result::Result<
2643 Amount<NonNegative>,
2644 amount::Error,
2645 >; 2] = [&lockbox_streams, &funding_streams]
2646 .map(|streams| streams.iter().map(|&(_, amount)| amount).sum());
2647
2648 funding_streams.sort_by_key(|(receiver, _funding_stream)| {
2650 ZCASHD_FUNDING_STREAM_ORDER
2651 .iter()
2652 .position(|zcashd_receiver| zcashd_receiver == receiver)
2653 });
2654
2655 let [funding_streams, lockbox_streams]: [Vec<_>; 2] = [funding_streams, lockbox_streams]
2657 .map(|streams| {
2658 streams
2659 .into_iter()
2660 .map(|(receiver, value)| {
2661 let address = funding_stream_address(height, &network, receiver);
2662 types::subsidy::FundingStream::new(is_nu6, receiver, value, address)
2663 })
2664 .collect()
2665 });
2666
2667 Ok(BlockSubsidy {
2668 miner: miner_subsidy.into(),
2669 founders: founders.into(),
2670 funding_streams,
2671 lockbox_streams,
2672 funding_streams_total: funding_streams_total
2673 .map_error(server::error::LegacyCode::default())?
2674 .into(),
2675 lockbox_total: lockbox_total
2676 .map_error(server::error::LegacyCode::default())?
2677 .into(),
2678 total_block_subsidy: total_block_subsidy.into(),
2679 })
2680 }
2681
2682 async fn get_difficulty(&self) -> Result<f64> {
2683 chain_tip_difficulty(self.network.clone(), self.read_state.clone(), false).await
2684 }
2685
2686 async fn z_list_unified_receivers(&self, address: String) -> Result<unified_address::Response> {
2687 use zcash_address::unified::Container;
2688
2689 let (network, unified_address): (
2690 zcash_protocol::consensus::NetworkType,
2691 zcash_address::unified::Address,
2692 ) = zcash_address::unified::Encoding::decode(address.clone().as_str())
2693 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
2694
2695 let mut p2pkh = String::new();
2696 let mut p2sh = String::new();
2697 let mut orchard = String::new();
2698 let mut sapling = String::new();
2699
2700 for item in unified_address.items() {
2701 match item {
2702 zcash_address::unified::Receiver::Orchard(_data) => {
2703 let addr = zcash_address::unified::Address::try_from_items(vec![item])
2704 .expect("using data already decoded as valid");
2705 orchard = addr.encode(&network);
2706 }
2707 zcash_address::unified::Receiver::Sapling(data) => {
2708 let addr = zebra_chain::primitives::Address::try_from_sapling(network, data)
2709 .expect("using data already decoded as valid");
2710 sapling = addr.payment_address().unwrap_or_default();
2711 }
2712 zcash_address::unified::Receiver::P2pkh(data) => {
2713 let addr =
2714 zebra_chain::primitives::Address::try_from_transparent_p2pkh(network, data)
2715 .expect("using data already decoded as valid");
2716 p2pkh = addr.payment_address().unwrap_or_default();
2717 }
2718 zcash_address::unified::Receiver::P2sh(data) => {
2719 let addr =
2720 zebra_chain::primitives::Address::try_from_transparent_p2sh(network, data)
2721 .expect("using data already decoded as valid");
2722 p2sh = addr.payment_address().unwrap_or_default();
2723 }
2724 _ => (),
2725 }
2726 }
2727
2728 Ok(unified_address::Response::new(
2729 orchard, sapling, p2pkh, p2sh,
2730 ))
2731 }
2732
2733 async fn invalidate_block(&self, block_hash: block::Hash) -> Result<()> {
2734 self.state
2735 .clone()
2736 .oneshot(zebra_state::Request::InvalidateBlock(block_hash))
2737 .await
2738 .map(|rsp| assert_eq!(rsp, zebra_state::Response::Invalidated(block_hash)))
2739 .map_misc_error()
2740 }
2741
2742 async fn reconsider_block(&self, block_hash: block::Hash) -> Result<Vec<block::Hash>> {
2743 self.state
2744 .clone()
2745 .oneshot(zebra_state::Request::ReconsiderBlock(block_hash))
2746 .await
2747 .map(|rsp| match rsp {
2748 zebra_state::Response::Reconsidered(block_hashes) => block_hashes,
2749 _ => unreachable!("unmatched response to a reconsider block request"),
2750 })
2751 .map_misc_error()
2752 }
2753
2754 async fn generate(&self, num_blocks: u32) -> Result<Vec<GetBlockHash>> {
2755 let rpc = self.clone();
2756 let network = self.network.clone();
2757
2758 if !network.disable_pow() {
2759 return Err(ErrorObject::borrowed(
2760 0,
2761 "generate is only supported on networks where PoW is disabled",
2762 None,
2763 ));
2764 }
2765
2766 let mut block_hashes = Vec::new();
2767 for _ in 0..num_blocks {
2768 let block_template = rpc
2769 .get_block_template(None)
2770 .await
2771 .map_error(server::error::LegacyCode::default())?;
2772
2773 let get_block_template::Response::TemplateMode(block_template) = block_template else {
2774 return Err(ErrorObject::borrowed(
2775 0,
2776 "error generating block template",
2777 None,
2778 ));
2779 };
2780
2781 let proposal_block = proposal_block_from_template(
2782 &block_template,
2783 get_block_template::TimeSource::CurTime,
2784 )
2785 .map_error(server::error::LegacyCode::default())?;
2786 let hex_proposal_block = HexData(
2787 proposal_block
2788 .zcash_serialize_to_vec()
2789 .map_error(server::error::LegacyCode::default())?,
2790 );
2791
2792 let _submit = rpc
2793 .submit_block(hex_proposal_block, None)
2794 .await
2795 .map_error(server::error::LegacyCode::default())?;
2796
2797 block_hashes.push(GetBlockHash(proposal_block.hash()));
2798 }
2799
2800 Ok(block_hashes)
2801 }
2802
2803 async fn add_node(
2804 &self,
2805 addr: zebra_network::PeerSocketAddr,
2806 command: AddNodeCommand,
2807 ) -> Result<()> {
2808 if self.network.is_regtest() {
2809 match command {
2810 AddNodeCommand::Add => {
2811 tracing::info!(?addr, "adding peer address to the address book");
2812 if self.address_book.clone().add_peer(addr) {
2813 Ok(())
2814 } else {
2815 return Err(ErrorObject::owned(
2816 ErrorCode::InvalidParams.code(),
2817 format!("peer address was already present in the address book: {addr}"),
2818 None::<()>,
2819 ));
2820 }
2821 }
2822 }
2823 } else {
2824 return Err(ErrorObject::owned(
2825 ErrorCode::InvalidParams.code(),
2826 "addnode command is only supported on regtest",
2827 None::<()>,
2828 ));
2829 }
2830 }
2831}
2832
2833pub fn best_chain_tip_height<Tip>(latest_chain_tip: &Tip) -> Result<Height>
2838where
2839 Tip: ChainTip + Clone + Send + Sync + 'static,
2840{
2841 latest_chain_tip
2842 .best_tip_height()
2843 .ok_or_misc_error("No blocks in state")
2844}
2845
2846#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
2850pub struct GetInfo {
2851 version: u64,
2853
2854 build: String,
2856
2857 subversion: String,
2859
2860 #[serde(rename = "protocolversion")]
2862 protocol_version: u32,
2863
2864 blocks: u32,
2866
2867 connections: usize,
2869
2870 #[serde(skip_serializing_if = "Option::is_none")]
2872 proxy: Option<String>,
2873
2874 difficulty: f64,
2876
2877 testnet: bool,
2879
2880 #[serde(rename = "paytxfee")]
2882 pay_tx_fee: f64,
2883
2884 #[serde(rename = "relayfee")]
2886 relay_fee: f64,
2887
2888 errors: String,
2890
2891 #[serde(rename = "errorstimestamp")]
2893 errors_timestamp: String,
2894}
2895
2896impl Default for GetInfo {
2897 fn default() -> Self {
2898 GetInfo {
2899 version: 0,
2900 build: "some build version".to_string(),
2901 subversion: "some subversion".to_string(),
2902 protocol_version: 0,
2903 blocks: 0,
2904 connections: 0,
2905 proxy: None,
2906 difficulty: 0.0,
2907 testnet: false,
2908 pay_tx_fee: 0.0,
2909 relay_fee: 0.0,
2910 errors: "no errors".to_string(),
2911 errors_timestamp: "no errors timestamp".to_string(),
2912 }
2913 }
2914}
2915
2916impl GetInfo {
2917 #[allow(clippy::too_many_arguments)]
2919 pub fn from_parts(
2920 version: u64,
2921 build: String,
2922 subversion: String,
2923 protocol_version: u32,
2924 blocks: u32,
2925 connections: usize,
2926 proxy: Option<String>,
2927 difficulty: f64,
2928 testnet: bool,
2929 pay_tx_fee: f64,
2930 relay_fee: f64,
2931 errors: String,
2932 errors_timestamp: String,
2933 ) -> Self {
2934 Self {
2935 version,
2936 build,
2937 subversion,
2938 protocol_version,
2939 blocks,
2940 connections,
2941 proxy,
2942 difficulty,
2943 testnet,
2944 pay_tx_fee,
2945 relay_fee,
2946 errors,
2947 errors_timestamp,
2948 }
2949 }
2950
2951 pub fn into_parts(
2953 self,
2954 ) -> (
2955 u64,
2956 String,
2957 String,
2958 u32,
2959 u32,
2960 usize,
2961 Option<String>,
2962 f64,
2963 bool,
2964 f64,
2965 f64,
2966 String,
2967 String,
2968 ) {
2969 (
2970 self.version,
2971 self.build,
2972 self.subversion,
2973 self.protocol_version,
2974 self.blocks,
2975 self.connections,
2976 self.proxy,
2977 self.difficulty,
2978 self.testnet,
2979 self.pay_tx_fee,
2980 self.relay_fee,
2981 self.errors,
2982 self.errors_timestamp,
2983 )
2984 }
2985
2986 pub fn version(build_string: &str) -> Option<u64> {
2988 let semver_version = semver::Version::parse(build_string.strip_prefix('v')?).ok()?;
2989 let build_number = semver_version
2990 .build
2991 .as_str()
2992 .split('.')
2993 .next()
2994 .and_then(|num_str| num_str.parse::<u64>().ok())
2995 .unwrap_or_default();
2996
2997 let version_number = 1_000_000 * semver_version.major
2999 + 10_000 * semver_version.minor
3000 + 100 * semver_version.patch
3001 + build_number;
3002
3003 Some(version_number)
3004 }
3005}
3006
3007#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
3011pub struct GetBlockChainInfo {
3012 chain: String,
3014
3015 blocks: Height,
3017
3018 headers: Height,
3021
3022 difficulty: f64,
3024
3025 #[serde(rename = "verificationprogress")]
3027 verification_progress: f64,
3028
3029 #[serde(rename = "chainwork")]
3031 chain_work: u64,
3032
3033 pruned: bool,
3035
3036 size_on_disk: u64,
3038
3039 commitments: u64,
3041
3042 #[serde(rename = "bestblockhash", with = "hex")]
3044 best_block_hash: block::Hash,
3045
3046 #[serde(rename = "estimatedheight")]
3050 estimated_height: Height,
3051
3052 #[serde(rename = "chainSupply")]
3054 chain_supply: get_blockchain_info::Balance,
3055
3056 #[serde(rename = "valuePools")]
3058 value_pools: [get_blockchain_info::Balance; 5],
3059
3060 upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
3062
3063 consensus: TipConsensusBranch,
3065}
3066
3067impl Default for GetBlockChainInfo {
3068 fn default() -> Self {
3069 GetBlockChainInfo {
3070 chain: "main".to_string(),
3071 blocks: Height(1),
3072 best_block_hash: block::Hash([0; 32]),
3073 estimated_height: Height(1),
3074 chain_supply: get_blockchain_info::Balance::chain_supply(Default::default()),
3075 value_pools: get_blockchain_info::Balance::zero_pools(),
3076 upgrades: IndexMap::new(),
3077 consensus: TipConsensusBranch {
3078 chain_tip: ConsensusBranchIdHex(ConsensusBranchId::default()),
3079 next_block: ConsensusBranchIdHex(ConsensusBranchId::default()),
3080 },
3081 headers: Height(1),
3082 difficulty: 0.0,
3083 verification_progress: 0.0,
3084 chain_work: 0,
3085 pruned: false,
3086 size_on_disk: 0,
3087 commitments: 0,
3088 }
3089 }
3090}
3091
3092impl GetBlockChainInfo {
3093 #[allow(clippy::too_many_arguments)]
3095 pub fn new(
3096 chain: String,
3097 blocks: Height,
3098 best_block_hash: block::Hash,
3099 estimated_height: Height,
3100 chain_supply: get_blockchain_info::Balance,
3101 value_pools: [get_blockchain_info::Balance; 5],
3102 upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
3103 consensus: TipConsensusBranch,
3104 headers: Height,
3105 difficulty: f64,
3106 verification_progress: f64,
3107 chain_work: u64,
3108 pruned: bool,
3109 size_on_disk: u64,
3110 commitments: u64,
3111 ) -> Self {
3112 Self {
3113 chain,
3114 blocks,
3115 best_block_hash,
3116 estimated_height,
3117 chain_supply,
3118 value_pools,
3119 upgrades,
3120 consensus,
3121 headers,
3122 difficulty,
3123 verification_progress,
3124 chain_work,
3125 pruned,
3126 size_on_disk,
3127 commitments,
3128 }
3129 }
3130
3131 pub fn chain(&self) -> String {
3133 self.chain.clone()
3134 }
3135
3136 pub fn blocks(&self) -> Height {
3138 self.blocks
3139 }
3140
3141 pub fn best_block_hash(&self) -> &block::Hash {
3143 &self.best_block_hash
3144 }
3145
3146 pub fn estimated_height(&self) -> Height {
3152 self.estimated_height
3153 }
3154
3155 pub fn value_pools(&self) -> &[get_blockchain_info::Balance; 5] {
3157 &self.value_pools
3158 }
3159
3160 pub fn upgrades(&self) -> &IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo> {
3162 &self.upgrades
3163 }
3164
3165 pub fn consensus(&self) -> &TipConsensusBranch {
3167 &self.consensus
3168 }
3169}
3170
3171#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
3176#[serde(from = "DAddressStrings")]
3177pub struct AddressStrings {
3178 addresses: Vec<String>,
3180}
3181
3182impl From<DAddressStrings> for AddressStrings {
3183 fn from(address_strings: DAddressStrings) -> Self {
3184 match address_strings {
3185 DAddressStrings::Addresses { addresses } => AddressStrings { addresses },
3186 DAddressStrings::Address(address) => AddressStrings {
3187 addresses: vec![address],
3188 },
3189 }
3190 }
3191}
3192
3193#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize)]
3195#[serde(untagged)]
3196enum DAddressStrings {
3197 Addresses { addresses: Vec<String> },
3199 Address(String),
3201}
3202
3203impl AddressStrings {
3204 #[cfg(test)]
3206 pub fn new(addresses: Vec<String>) -> AddressStrings {
3207 AddressStrings { addresses }
3208 }
3209
3210 pub fn new_valid(addresses: Vec<String>) -> Result<AddressStrings> {
3212 let address_strings = Self { addresses };
3213 address_strings.clone().valid_addresses()?;
3214 Ok(address_strings)
3215 }
3216
3217 pub fn valid_addresses(self) -> Result<HashSet<Address>> {
3221 let valid_addresses: HashSet<Address> = self
3224 .addresses
3225 .into_iter()
3226 .map(|address| {
3227 address
3228 .parse()
3229 .map_error(server::error::LegacyCode::InvalidAddressOrKey)
3230 })
3231 .collect::<Result<_>>()?;
3232
3233 Ok(valid_addresses)
3234 }
3235
3236 pub fn valid_address_strings(self) -> Result<Vec<String>> {
3240 self.clone().valid_addresses()?;
3241 Ok(self.addresses)
3242 }
3243}
3244
3245#[derive(
3247 Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize,
3248)]
3249pub struct AddressBalance {
3250 pub balance: u64,
3252 pub received: u64,
3254}
3255
3256#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
3258pub struct ConsensusBranchIdHex(#[serde(with = "hex")] ConsensusBranchId);
3259
3260impl ConsensusBranchIdHex {
3261 pub fn new(consensus_branch_id: u32) -> Self {
3263 ConsensusBranchIdHex(consensus_branch_id.into())
3264 }
3265
3266 pub fn inner(&self) -> u32 {
3268 self.0.into()
3269 }
3270}
3271
3272#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3274pub struct NetworkUpgradeInfo {
3275 name: NetworkUpgrade,
3279
3280 #[serde(rename = "activationheight")]
3282 activation_height: Height,
3283
3284 status: NetworkUpgradeStatus,
3286}
3287
3288impl NetworkUpgradeInfo {
3289 pub fn from_parts(
3291 name: NetworkUpgrade,
3292 activation_height: Height,
3293 status: NetworkUpgradeStatus,
3294 ) -> Self {
3295 Self {
3296 name,
3297 activation_height,
3298 status,
3299 }
3300 }
3301
3302 pub fn into_parts(self) -> (NetworkUpgrade, Height, NetworkUpgradeStatus) {
3304 (self.name, self.activation_height, self.status)
3305 }
3306}
3307
3308#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3310pub enum NetworkUpgradeStatus {
3311 #[serde(rename = "active")]
3316 Active,
3317
3318 #[serde(rename = "disabled")]
3320 Disabled,
3321
3322 #[serde(rename = "pending")]
3324 Pending,
3325}
3326
3327#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3331pub struct TipConsensusBranch {
3332 #[serde(rename = "chaintip")]
3334 chain_tip: ConsensusBranchIdHex,
3335
3336 #[serde(rename = "nextblock")]
3338 next_block: ConsensusBranchIdHex,
3339}
3340
3341impl TipConsensusBranch {
3342 pub fn from_parts(chain_tip: u32, next_block: u32) -> Self {
3344 Self {
3345 chain_tip: ConsensusBranchIdHex::new(chain_tip),
3346 next_block: ConsensusBranchIdHex::new(next_block),
3347 }
3348 }
3349
3350 pub fn into_parts(self) -> (u32, u32) {
3352 (self.chain_tip.inner(), self.next_block.inner())
3353 }
3354}
3355
3356#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3362pub struct SentTransactionHash(#[serde(with = "hex")] transaction::Hash);
3363
3364impl Default for SentTransactionHash {
3365 fn default() -> Self {
3366 Self(transaction::Hash::from([0; 32]))
3367 }
3368}
3369
3370impl SentTransactionHash {
3371 pub fn new(hash: transaction::Hash) -> Self {
3373 SentTransactionHash(hash)
3374 }
3375
3376 pub fn inner(&self) -> transaction::Hash {
3378 self.0
3379 }
3380}
3381
3382#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3386#[serde(untagged)]
3387#[allow(clippy::large_enum_variant)] pub enum GetBlock {
3389 Raw(#[serde(with = "hex")] SerializedBlock),
3391 Object {
3393 hash: GetBlockHash,
3395
3396 confirmations: i64,
3399
3400 #[serde(skip_serializing_if = "Option::is_none")]
3402 size: Option<i64>,
3403
3404 #[serde(skip_serializing_if = "Option::is_none")]
3406 height: Option<Height>,
3407
3408 #[serde(skip_serializing_if = "Option::is_none")]
3410 version: Option<u32>,
3411
3412 #[serde(with = "opthex", rename = "merkleroot")]
3414 #[serde(skip_serializing_if = "Option::is_none", default)]
3415 merkle_root: Option<block::merkle::Root>,
3416
3417 #[serde(with = "opthex", rename = "blockcommitments")]
3420 #[serde(skip_serializing_if = "Option::is_none", default)]
3421 block_commitments: Option<[u8; 32]>,
3422
3423 #[serde(with = "opthex", rename = "finalsaplingroot")]
3427 #[serde(skip_serializing_if = "Option::is_none", default)]
3428 final_sapling_root: Option<[u8; 32]>,
3429
3430 #[serde(with = "opthex", rename = "finalorchardroot")]
3432 #[serde(skip_serializing_if = "Option::is_none", default)]
3433 final_orchard_root: Option<[u8; 32]>,
3434
3435 tx: Vec<GetBlockTransaction>,
3440
3441 #[serde(skip_serializing_if = "Option::is_none")]
3443 time: Option<i64>,
3444
3445 #[serde(with = "opthex")]
3447 #[serde(skip_serializing_if = "Option::is_none", default)]
3448 nonce: Option<[u8; 32]>,
3449
3450 #[serde(with = "opthex")]
3453 #[serde(skip_serializing_if = "Option::is_none", default)]
3454 solution: Option<Solution>,
3455
3456 #[serde(with = "opthex")]
3458 #[serde(skip_serializing_if = "Option::is_none", default)]
3459 bits: Option<CompactDifficulty>,
3460
3461 #[serde(skip_serializing_if = "Option::is_none")]
3464 difficulty: Option<f64>,
3465
3466 trees: GetBlockTrees,
3473
3474 #[serde(rename = "previousblockhash", skip_serializing_if = "Option::is_none")]
3476 previous_block_hash: Option<GetBlockHash>,
3477
3478 #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
3480 next_block_hash: Option<GetBlockHash>,
3481 },
3482}
3483
3484impl Default for GetBlock {
3485 fn default() -> Self {
3486 GetBlock::Object {
3487 hash: GetBlockHash::default(),
3488 confirmations: 0,
3489 height: None,
3490 time: None,
3491 tx: Vec::new(),
3492 trees: GetBlockTrees::default(),
3493 size: None,
3494 version: None,
3495 merkle_root: None,
3496 block_commitments: None,
3497 final_sapling_root: None,
3498 final_orchard_root: None,
3499 nonce: None,
3500 bits: None,
3501 difficulty: None,
3502 previous_block_hash: None,
3503 next_block_hash: None,
3504 solution: None,
3505 }
3506 }
3507}
3508
3509#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3510#[serde(untagged)]
3511pub enum GetBlockTransaction {
3514 Hash(#[serde(with = "hex")] transaction::Hash),
3516 Object(Box<TransactionObject>),
3518}
3519
3520#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3524#[serde(untagged)]
3525pub enum GetBlockHeader {
3526 Raw(hex_data::HexData),
3528
3529 Object(Box<GetBlockHeaderObject>),
3531}
3532
3533#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3534pub struct GetBlockHeaderObject {
3538 pub hash: GetBlockHash,
3540
3541 pub confirmations: i64,
3544
3545 pub height: Height,
3547
3548 pub version: u32,
3550
3551 #[serde(with = "hex", rename = "merkleroot")]
3553 pub merkle_root: block::merkle::Root,
3554
3555 #[serde(with = "hex", rename = "blockcommitments")]
3558 pub block_commitments: [u8; 32],
3559
3560 #[serde(with = "hex", rename = "finalsaplingroot")]
3562 pub final_sapling_root: [u8; 32],
3563
3564 #[serde(skip)]
3567 pub sapling_tree_size: u64,
3568
3569 pub time: i64,
3571
3572 #[serde(with = "hex")]
3574 pub nonce: [u8; 32],
3575
3576 #[serde(with = "hex")]
3578 pub solution: Solution,
3579
3580 #[serde(with = "hex")]
3582 pub bits: CompactDifficulty,
3583
3584 pub difficulty: f64,
3587
3588 #[serde(rename = "previousblockhash")]
3590 pub previous_block_hash: GetBlockHash,
3591
3592 #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
3594 pub next_block_hash: Option<GetBlockHash>,
3595}
3596
3597impl Default for GetBlockHeader {
3598 fn default() -> Self {
3599 GetBlockHeader::Object(Box::default())
3600 }
3601}
3602
3603impl Default for GetBlockHeaderObject {
3604 fn default() -> Self {
3605 let difficulty: ExpandedDifficulty = zebra_chain::work::difficulty::U256::one().into();
3606
3607 GetBlockHeaderObject {
3608 hash: GetBlockHash::default(),
3609 confirmations: 0,
3610 height: Height::MIN,
3611 version: 4,
3612 merkle_root: block::merkle::Root([0; 32]),
3613 block_commitments: Default::default(),
3614 final_sapling_root: Default::default(),
3615 sapling_tree_size: Default::default(),
3616 time: 0,
3617 nonce: [0; 32],
3618 solution: Solution::for_proposal(),
3619 bits: difficulty.to_compact(),
3620 difficulty: 1.0,
3621 previous_block_hash: Default::default(),
3622 next_block_hash: Default::default(),
3623 }
3624 }
3625}
3626
3627#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3633#[serde(transparent)]
3634pub struct GetBlockHash(#[serde(with = "hex")] pub block::Hash);
3635
3636#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3638pub struct GetBlockHeightAndHash {
3639 pub height: block::Height,
3641 pub hash: block::Hash,
3643}
3644
3645impl Default for GetBlockHeightAndHash {
3646 fn default() -> Self {
3647 Self {
3648 height: block::Height::MIN,
3649 hash: block::Hash([0; 32]),
3650 }
3651 }
3652}
3653
3654impl Default for GetBlockHash {
3655 fn default() -> Self {
3656 GetBlockHash(block::Hash([0; 32]))
3657 }
3658}
3659
3660#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3664#[serde(untagged)]
3665pub enum GetRawTransaction {
3666 Raw(#[serde(with = "hex")] SerializedTransaction),
3668 Object(Box<TransactionObject>),
3670}
3671
3672impl Default for GetRawTransaction {
3673 fn default() -> Self {
3674 Self::Object(Box::default())
3675 }
3676}
3677
3678#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3682pub struct GetAddressUtxos {
3683 address: transparent::Address,
3685
3686 #[serde(with = "hex")]
3688 txid: transaction::Hash,
3689
3690 #[serde(rename = "outputIndex")]
3692 output_index: OutputIndex,
3693
3694 #[serde(with = "hex")]
3696 script: transparent::Script,
3697
3698 satoshis: u64,
3700
3701 height: Height,
3705}
3706
3707impl Default for GetAddressUtxos {
3708 fn default() -> Self {
3709 Self {
3710 address: transparent::Address::from_pub_key_hash(
3711 zebra_chain::parameters::NetworkKind::default(),
3712 [0u8; 20],
3713 ),
3714 txid: transaction::Hash::from([0; 32]),
3715 output_index: OutputIndex::from_u64(0),
3716 script: transparent::Script::new(&[0u8; 10]),
3717 satoshis: u64::default(),
3718 height: Height(0),
3719 }
3720 }
3721}
3722
3723impl GetAddressUtxos {
3724 pub fn from_parts(
3726 address: transparent::Address,
3727 txid: transaction::Hash,
3728 output_index: OutputIndex,
3729 script: transparent::Script,
3730 satoshis: u64,
3731 height: Height,
3732 ) -> Self {
3733 GetAddressUtxos {
3734 address,
3735 txid,
3736 output_index,
3737 script,
3738 satoshis,
3739 height,
3740 }
3741 }
3742
3743 pub fn into_parts(
3745 &self,
3746 ) -> (
3747 transparent::Address,
3748 transaction::Hash,
3749 OutputIndex,
3750 transparent::Script,
3751 u64,
3752 Height,
3753 ) {
3754 (
3755 self.address.clone(),
3756 self.txid,
3757 self.output_index,
3758 self.script.clone(),
3759 self.satoshis,
3760 self.height,
3761 )
3762 }
3763}
3764
3765#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3769pub struct GetAddressTxIdsRequest {
3770 addresses: Vec<String>,
3772 start: Option<u32>,
3774 end: Option<u32>,
3776}
3777
3778impl GetAddressTxIdsRequest {
3779 pub fn from_parts(addresses: Vec<String>, start: u32, end: u32) -> Self {
3781 GetAddressTxIdsRequest {
3782 addresses,
3783 start: Some(start),
3784 end: Some(end),
3785 }
3786 }
3787 pub fn into_parts(&self) -> (Vec<String>, u32, u32) {
3789 (
3790 self.addresses.clone(),
3791 self.start.unwrap_or(0),
3792 self.end.unwrap_or(0),
3793 )
3794 }
3795}
3796
3797#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3799pub struct GetBlockTrees {
3800 #[serde(skip_serializing_if = "SaplingTrees::is_empty")]
3801 sapling: SaplingTrees,
3802 #[serde(skip_serializing_if = "OrchardTrees::is_empty")]
3803 orchard: OrchardTrees,
3804}
3805
3806impl Default for GetBlockTrees {
3807 fn default() -> Self {
3808 GetBlockTrees {
3809 sapling: SaplingTrees { size: 0 },
3810 orchard: OrchardTrees { size: 0 },
3811 }
3812 }
3813}
3814
3815impl GetBlockTrees {
3816 pub fn new(sapling: u64, orchard: u64) -> Self {
3818 GetBlockTrees {
3819 sapling: SaplingTrees { size: sapling },
3820 orchard: OrchardTrees { size: orchard },
3821 }
3822 }
3823
3824 pub fn sapling(self) -> u64 {
3826 self.sapling.size
3827 }
3828
3829 pub fn orchard(self) -> u64 {
3831 self.orchard.size
3832 }
3833}
3834
3835#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3837pub struct SaplingTrees {
3838 size: u64,
3839}
3840
3841impl SaplingTrees {
3842 fn is_empty(&self) -> bool {
3843 self.size == 0
3844 }
3845}
3846
3847#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3849pub struct OrchardTrees {
3850 size: u64,
3851}
3852
3853impl OrchardTrees {
3854 fn is_empty(&self) -> bool {
3855 self.size == 0
3856 }
3857}
3858
3859fn build_height_range(
3875 start: Option<u32>,
3876 end: Option<u32>,
3877 chain_height: Height,
3878) -> Result<RangeInclusive<Height>> {
3879 let start = Height(start.unwrap_or(0)).min(chain_height);
3882
3883 let end = match end {
3885 Some(0) | None => chain_height,
3886 Some(val) => Height(val).min(chain_height),
3887 };
3888
3889 if start > end {
3890 return Err(ErrorObject::owned(
3891 ErrorCode::InvalidParams.code(),
3892 format!("start {start:?} must be less than or equal to end {end:?}"),
3893 None::<()>,
3894 ));
3895 }
3896
3897 Ok(start..=end)
3898}
3899
3900#[allow(dead_code)]
3908pub fn height_from_signed_int(index: i32, tip_height: Height) -> Result<Height> {
3909 if index >= 0 {
3910 let height = index.try_into().expect("Positive i32 always fits in u32");
3911 if height > tip_height.0 {
3912 return Err(ErrorObject::borrowed(
3913 ErrorCode::InvalidParams.code(),
3914 "Provided index is greater than the current tip",
3915 None,
3916 ));
3917 }
3918 Ok(Height(height))
3919 } else {
3920 let height = i32::try_from(tip_height.0)
3922 .expect("tip height fits in i32, because Height::MAX fits in i32")
3923 .checked_add(index + 1);
3924
3925 let sanitized_height = match height {
3926 None => {
3927 return Err(ErrorObject::borrowed(
3928 ErrorCode::InvalidParams.code(),
3929 "Provided index is not valid",
3930 None,
3931 ));
3932 }
3933 Some(h) => {
3934 if h < 0 {
3935 return Err(ErrorObject::borrowed(
3936 ErrorCode::InvalidParams.code(),
3937 "Provided negative index ends up with a negative height",
3938 None,
3939 ));
3940 }
3941 let h: u32 = h.try_into().expect("Positive i32 always fits in u32");
3942 if h > tip_height.0 {
3943 return Err(ErrorObject::borrowed(
3944 ErrorCode::InvalidParams.code(),
3945 "Provided index is greater than the current tip",
3946 None,
3947 ));
3948 }
3949
3950 h
3951 }
3952 };
3953
3954 Ok(Height(sanitized_height))
3955 }
3956}
3957
3958pub mod opthex {
3960 use hex::{FromHex, ToHex};
3961 use serde::{de, Deserialize, Deserializer, Serializer};
3962
3963 #[allow(missing_docs)]
3964 pub fn serialize<S, T>(data: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
3965 where
3966 S: Serializer,
3967 T: ToHex,
3968 {
3969 match data {
3970 Some(data) => {
3971 let s = data.encode_hex::<String>();
3972 serializer.serialize_str(&s)
3973 }
3974 None => serializer.serialize_none(),
3975 }
3976 }
3977
3978 #[allow(missing_docs)]
3979 pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
3980 where
3981 D: Deserializer<'de>,
3982 T: FromHex,
3983 {
3984 let opt = Option::<String>::deserialize(deserializer)?;
3985 match opt {
3986 Some(s) => T::from_hex(&s)
3987 .map(Some)
3988 .map_err(|_e| de::Error::custom("failed to convert hex string")),
3989 None => Ok(None),
3990 }
3991 }
3992}
3993
3994pub mod arrayhex {
3996 use serde::{Deserializer, Serializer};
3997 use std::fmt;
3998
3999 #[allow(missing_docs)]
4000 pub fn serialize<S, const N: usize>(data: &[u8; N], serializer: S) -> Result<S::Ok, S::Error>
4001 where
4002 S: Serializer,
4003 {
4004 let hex_string = hex::encode(data);
4005 serializer.serialize_str(&hex_string)
4006 }
4007
4008 #[allow(missing_docs)]
4009 pub fn deserialize<'de, D, const N: usize>(deserializer: D) -> Result<[u8; N], D::Error>
4010 where
4011 D: Deserializer<'de>,
4012 {
4013 struct HexArrayVisitor<const N: usize>;
4014
4015 impl<const N: usize> serde::de::Visitor<'_> for HexArrayVisitor<N> {
4016 type Value = [u8; N];
4017
4018 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
4019 write!(formatter, "a hex string representing exactly {} bytes", N)
4020 }
4021
4022 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
4023 where
4024 E: serde::de::Error,
4025 {
4026 let vec = hex::decode(v).map_err(E::custom)?;
4027 vec.clone().try_into().map_err(|_| {
4028 E::invalid_length(vec.len(), &format!("expected {} bytes", N).as_str())
4029 })
4030 }
4031 }
4032
4033 deserializer.deserialize_str(HexArrayVisitor::<N>)
4034 }
4035}
4036
4037pub async fn chain_tip_difficulty<State>(
4039 network: Network,
4040 mut state: State,
4041 should_use_default: bool,
4042) -> Result<f64>
4043where
4044 State: Service<
4045 zebra_state::ReadRequest,
4046 Response = zebra_state::ReadResponse,
4047 Error = zebra_state::BoxError,
4048 > + Clone
4049 + Send
4050 + Sync
4051 + 'static,
4052 State::Future: Send,
4053{
4054 let request = ReadRequest::ChainInfo;
4055
4056 let response = state
4062 .ready()
4063 .and_then(|service| service.call(request))
4064 .await;
4065
4066 let response = match (should_use_default, response) {
4067 (_, Ok(res)) => res,
4068 (true, Err(_)) => {
4069 return Ok((U256::from(network.target_difficulty_limit()) >> 128).as_u128() as f64)
4070 }
4071 (false, Err(error)) => return Err(ErrorObject::owned(0, error.to_string(), None::<()>)),
4072 };
4073
4074 let chain_info = match response {
4075 ReadResponse::ChainInfo(info) => info,
4076 _ => unreachable!("unmatched response to a chain info request"),
4077 };
4078
4079 let pow_limit: U256 = network.target_difficulty_limit().into();
4102 let Some(difficulty) = chain_info.expected_difficulty.to_expanded() else {
4103 return Ok(0.0);
4104 };
4105
4106 let pow_limit = pow_limit >> 128;
4108 let difficulty = U256::from(difficulty) >> 128;
4109
4110 let pow_limit = pow_limit.as_u128() as f64;
4113 let difficulty = difficulty.as_u128() as f64;
4114
4115 Ok(pow_limit / difficulty)
4117}
4118
4119#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
4121pub enum AddNodeCommand {
4122 #[serde(rename = "add")]
4124 Add,
4125}