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 schemars::JsonSchema;
54use tokio::{
55 sync::{broadcast, mpsc, watch},
56 task::JoinHandle,
57};
58use tower::ServiceExt;
59use tracing::Instrument;
60
61use zcash_address::{unified::Encoding, TryFromAddress};
62use zcash_primitives::consensus::Parameters;
63
64use zebra_chain::{
65 amount::{Amount, NegativeAllowed},
66 block::{self, Block, Commitment, Height, SerializedBlock, TryIntoHeight},
67 chain_sync_status::ChainSyncStatus,
68 chain_tip::{ChainTip, NetworkChainTipHeightEstimator},
69 parameters::{
70 subsidy::{
71 block_subsidy, founders_reward, funding_stream_values, miner_subsidy,
72 FundingStreamReceiver,
73 },
74 ConsensusBranchId, Network, NetworkUpgrade, POW_AVERAGING_WINDOW,
75 },
76 serialization::{BytesInDisplayOrder, ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize},
77 subtree::NoteCommitmentSubtreeIndex,
78 transaction::{self, SerializedTransaction, Transaction, UnminedTx},
79 transparent::{self, Address, OutputIndex},
80 value_balance::ValueBalance,
81 work::{
82 difficulty::{CompactDifficulty, ExpandedDifficulty, ParameterDifficulty, U256},
83 equihash::Solution,
84 },
85};
86use zebra_consensus::{
87 funding_stream_address, router::service_trait::BlockVerifierService, RouterError,
88};
89use zebra_network::{address_book_peers::AddressBookPeers, types::PeerServices, PeerSocketAddr};
90use zebra_node_services::mempool::{self, CreatedOrSpent, MempoolService};
91use zebra_state::{
92 AnyTx, HashOrHeight, OutputLocation, ReadRequest, ReadResponse, ReadState as ReadStateService,
93 State as StateService, TransactionLocation,
94};
95
96use crate::{
97 client::Treestate,
98 config,
99 methods::types::{
100 validate_address::validate_address, z_validate_address::z_validate_address, zec::Zec,
101 },
102 queue::Queue,
103 server::{
104 self,
105 error::{MapError, OkOrError},
106 },
107};
108
109pub(crate) mod hex_data;
110pub(crate) mod trees;
111pub(crate) mod types;
112
113use hex_data::HexData;
114use trees::{GetSubtreesByIndexResponse, GetTreestateResponse, SubtreeRpcData};
115use types::{
116 get_block_template::{
117 constants::{
118 DEFAULT_SOLUTION_RATE_WINDOW_SIZE, MEMPOOL_LONG_POLL_INTERVAL,
119 ZCASHD_FUNDING_STREAM_ORDER,
120 },
121 proposal::proposal_block_from_template,
122 BlockTemplateResponse, BlockTemplateTimeSource, GetBlockTemplateHandler,
123 GetBlockTemplateParameters, GetBlockTemplateResponse,
124 },
125 get_blockchain_info::GetBlockchainInfoBalance,
126 get_mempool_info::GetMempoolInfoResponse,
127 get_mining_info::GetMiningInfoResponse,
128 get_raw_mempool::{self, GetRawMempoolResponse},
129 long_poll::LongPollInput,
130 network_info::{GetNetworkInfoResponse, NetworkInfo},
131 peer_info::PeerInfo,
132 submit_block::{SubmitBlockErrorResponse, SubmitBlockParameters, SubmitBlockResponse},
133 subsidy::GetBlockSubsidyResponse,
134 transaction::TransactionObject,
135 unified_address::ZListUnifiedReceiversResponse,
136 validate_address::ValidateAddressResponse,
137 z_validate_address::ZValidateAddressResponse,
138};
139
140include!(concat!(env!("OUT_DIR"), "/rpc_openrpc.rs"));
141
142pub(super) const PARAM_VERBOSE_DESC: &str =
145 "Boolean flag to indicate verbosity, true for a json object, false for hex encoded data.";
146pub(super) const PARAM_POOL_DESC: &str =
147 "The pool from which subtrees should be returned. Either \"sapling\" or \"orchard\".";
148pub(super) const PARAM_START_INDEX_DESC: &str =
149 "The index of the first 2^16-leaf subtree to return.";
150pub(super) const PARAM_LIMIT_DESC: &str = "The maximum number of subtrees to return.";
151pub(super) const PARAM_REQUEST_DESC: &str = "The request object containing the parameters.";
152pub(super) const PARAM_INDEX_DESC: &str = "The index of the subtree to return.";
153pub(super) const PARAM_RAW_TRANSACTION_HEX_DESC: &str = "The hex-encoded raw transaction bytes.";
154#[allow(non_upper_case_globals)]
155pub(super) const PARAM__ALLOW_HIGH_FEES_DESC: &str = "Whether to allow high fees.";
156pub(super) const PARAM_NUM_BLOCKS_DESC: &str = "The number of blocks to return.";
157pub(super) const PARAM_HEIGHT_DESC: &str = "The height of the block to return.";
158pub(super) const PARAM_COMMAND_DESC: &str = "The command to execute.";
159#[allow(non_upper_case_globals)]
160pub(super) const PARAM__PARAMETERS_DESC: &str = "The parameters for the command.";
161pub(super) const PARAM_BLOCK_HASH_DESC: &str = "The hash of the block to return.";
162pub(super) const PARAM_ADDRESS_DESC: &str = "The address to return.";
163pub(super) const PARAM_ADDRESS_STRINGS_DESC: &str = "The addresses to return.";
164pub(super) const PARAM_ADDR_DESC: &str = "The address to return.";
165pub(super) const PARAM_HEX_DATA_DESC: &str = "The hex-encoded data to return.";
166pub(super) const PARAM_TXID_DESC: &str = "The transaction ID to return.";
167pub(super) const PARAM_HASH_OR_HEIGHT_DESC: &str = "The block hash or height to return.";
168pub(super) const PARAM_PARAMETERS_DESC: &str = "The parameters for the command.";
169pub(super) const PARAM_VERBOSITY_DESC: &str = "Whether to include verbose output.";
170pub(super) const PARAM_N_DESC: &str = "The output index in the transaction.";
171pub(super) const PARAM_INCLUDE_MEMPOOL_DESC: &str =
172 "Whether to include mempool transactions in the response.";
173
174#[cfg(test)]
175mod tests;
176
177#[rpc(server)]
178pub trait Rpc {
180 #[method(name = "getinfo")]
195 async fn get_info(&self) -> Result<GetInfoResponse>;
196
197 #[method(name = "getblockchaininfo")]
208 async fn get_blockchain_info(&self) -> Result<GetBlockchainInfoResponse>;
209
210 #[method(name = "getaddressbalance")]
233 async fn get_address_balance(
234 &self,
235 address_strings: GetAddressBalanceRequest,
236 ) -> Result<GetAddressBalanceResponse>;
237
238 #[method(name = "sendrawtransaction")]
255 async fn send_raw_transaction(
256 &self,
257 raw_transaction_hex: String,
258 _allow_high_fees: Option<bool>,
259 ) -> Result<SendRawTransactionResponse>;
260
261 #[method(name = "getblock")]
281 async fn get_block(
282 &self,
283 hash_or_height: String,
284 verbosity: Option<u8>,
285 ) -> Result<GetBlockResponse>;
286
287 #[method(name = "getblockheader")]
305 async fn get_block_header(
306 &self,
307 hash_or_height: String,
308 verbose: Option<bool>,
309 ) -> Result<GetBlockHeaderResponse>;
310
311 #[method(name = "getbestblockhash")]
317 fn get_best_block_hash(&self) -> Result<GetBlockHashResponse>;
318
319 #[method(name = "getbestblockheightandhash")]
325 fn get_best_block_height_and_hash(&self) -> Result<GetBlockHeightAndHashResponse>;
326
327 #[method(name = "getmempoolinfo")]
331 async fn get_mempool_info(&self) -> Result<GetMempoolInfoResponse>;
332
333 #[method(name = "getrawmempool")]
343 async fn get_raw_mempool(&self, verbose: Option<bool>) -> Result<GetRawMempoolResponse>;
344
345 #[method(name = "z_gettreestate")]
362 async fn z_get_treestate(&self, hash_or_height: String) -> Result<GetTreestateResponse>;
363
364 #[method(name = "z_getsubtreesbyindex")]
383 async fn z_get_subtrees_by_index(
384 &self,
385 pool: String,
386 start_index: NoteCommitmentSubtreeIndex,
387 limit: Option<NoteCommitmentSubtreeIndex>,
388 ) -> Result<GetSubtreesByIndexResponse>;
389
390 #[method(name = "getrawtransaction")]
402 async fn get_raw_transaction(
403 &self,
404 txid: String,
405 verbose: Option<u8>,
406 block_hash: Option<String>,
407 ) -> Result<GetRawTransactionResponse>;
408
409 #[method(name = "getaddresstxids")]
439 async fn get_address_tx_ids(&self, request: GetAddressTxIdsRequest) -> Result<Vec<String>>;
440
441 #[method(name = "getaddressutxos")]
460 async fn get_address_utxos(
461 &self,
462 request: GetAddressUtxosRequest,
463 ) -> Result<GetAddressUtxosResponse>;
464
465 #[method(name = "stop")]
476 fn stop(&self) -> Result<String>;
477
478 #[method(name = "getblockcount")]
485 fn get_block_count(&self) -> Result<u32>;
486
487 #[method(name = "getblockhash")]
503 async fn get_block_hash(&self, index: i32) -> Result<GetBlockHashResponse>;
504
505 #[method(name = "getblocktemplate")]
527 async fn get_block_template(
528 &self,
529 parameters: Option<GetBlockTemplateParameters>,
530 ) -> Result<GetBlockTemplateResponse>;
531
532 #[method(name = "submitblock")]
548 async fn submit_block(
549 &self,
550 hex_data: HexData,
551 _parameters: Option<SubmitBlockParameters>,
552 ) -> Result<SubmitBlockResponse>;
553
554 #[method(name = "getmininginfo")]
560 async fn get_mining_info(&self) -> Result<GetMiningInfoResponse>;
561
562 #[method(name = "getnetworksolps")]
573 async fn get_network_sol_ps(&self, num_blocks: Option<i32>, height: Option<i32>)
574 -> Result<u64>;
575
576 #[method(name = "getnetworkhashps")]
586 async fn get_network_hash_ps(
587 &self,
588 num_blocks: Option<i32>,
589 height: Option<i32>,
590 ) -> Result<u64> {
591 self.get_network_sol_ps(num_blocks, height).await
592 }
593
594 #[method(name = "getnetworkinfo")]
600 async fn get_network_info(&self) -> Result<GetNetworkInfoResponse>;
601
602 #[method(name = "getpeerinfo")]
608 async fn get_peer_info(&self) -> Result<Vec<PeerInfo>>;
609
610 #[method(name = "ping")]
619 async fn ping(&self) -> Result<()>;
620
621 #[method(name = "validateaddress")]
632 async fn validate_address(&self, address: String) -> Result<ValidateAddressResponse>;
633
634 #[method(name = "z_validateaddress")]
649 async fn z_validate_address(&self, address: String) -> Result<ZValidateAddressResponse>;
650
651 #[method(name = "getblocksubsidy")]
666 async fn get_block_subsidy(&self, height: Option<u32>) -> Result<GetBlockSubsidyResponse>;
667
668 #[method(name = "getdifficulty")]
674 async fn get_difficulty(&self) -> Result<f64>;
675
676 #[method(name = "z_listunifiedreceivers")]
690 async fn z_list_unified_receivers(
691 &self,
692 address: String,
693 ) -> Result<ZListUnifiedReceiversResponse>;
694
695 #[method(name = "invalidateblock")]
703 async fn invalidate_block(&self, block_hash: String) -> Result<()>;
704
705 #[method(name = "reconsiderblock")]
711 async fn reconsider_block(&self, block_hash: String) -> Result<Vec<block::Hash>>;
712
713 #[method(name = "generate")]
714 async fn generate(&self, num_blocks: u32) -> Result<Vec<GetBlockHashResponse>>;
728
729 #[method(name = "addnode")]
730 async fn add_node(&self, addr: PeerSocketAddr, command: AddNodeCommand) -> Result<()>;
745
746 #[method(name = "rpc.discover")]
748 fn openrpc(&self) -> openrpsee::openrpc::Response;
749 #[method(name = "gettxout")]
761 async fn get_tx_out(
762 &self,
763 txid: String,
764 n: u32,
765 include_mempool: Option<bool>,
766 ) -> Result<GetTxOutResponse>;
767}
768
769#[derive(Clone)]
771pub struct RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
772where
773 Mempool: MempoolService,
774 State: StateService,
775 ReadState: ReadStateService,
776 Tip: ChainTip + Clone + Send + Sync + 'static,
777 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
778 BlockVerifierRouter: BlockVerifierService,
779 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
780{
781 build_version: String,
785
786 user_agent: String,
788
789 network: Network,
791
792 debug_force_finished_sync: bool,
795
796 mempool: Mempool,
800
801 state: State,
803
804 read_state: ReadState,
806
807 latest_chain_tip: Tip,
809
810 queue_sender: broadcast::Sender<UnminedTx>,
814
815 address_book: AddressBook,
817
818 last_warn_error_log_rx: LoggedLastEvent,
820
821 gbt: GetBlockTemplateHandler<BlockVerifierRouter, SyncStatus>,
823}
824
825pub type LoggedLastEvent = watch::Receiver<Option<(String, tracing::Level, chrono::DateTime<Utc>)>>;
827
828impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus> fmt::Debug
829 for RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
830where
831 Mempool: MempoolService,
832 State: StateService,
833 ReadState: ReadStateService,
834 Tip: ChainTip + Clone + Send + Sync + 'static,
835 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
836 BlockVerifierRouter: BlockVerifierService,
837 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
838{
839 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
840 f.debug_struct("RpcImpl")
842 .field("build_version", &self.build_version)
843 .field("user_agent", &self.user_agent)
844 .field("network", &self.network)
845 .field("debug_force_finished_sync", &self.debug_force_finished_sync)
846 .field("getblocktemplate", &self.gbt)
847 .finish()
848 }
849}
850
851impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
852 RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
853where
854 Mempool: MempoolService,
855 State: StateService,
856 ReadState: ReadStateService,
857 Tip: ChainTip + Clone + Send + Sync + 'static,
858 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
859 BlockVerifierRouter: BlockVerifierService,
860 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
861{
862 #[allow(clippy::too_many_arguments)]
867 pub fn new<VersionString, UserAgentString>(
868 network: Network,
869 mining_config: config::mining::Config,
870 debug_force_finished_sync: bool,
871 build_version: VersionString,
872 user_agent: UserAgentString,
873 mempool: Mempool,
874 state: State,
875 read_state: ReadState,
876 block_verifier_router: BlockVerifierRouter,
877 sync_status: SyncStatus,
878 latest_chain_tip: Tip,
879 address_book: AddressBook,
880 last_warn_error_log_rx: LoggedLastEvent,
881 mined_block_sender: Option<mpsc::Sender<(block::Hash, block::Height)>>,
882 ) -> (Self, JoinHandle<()>)
883 where
884 VersionString: ToString + Clone + Send + 'static,
885 UserAgentString: ToString + Clone + Send + 'static,
886 {
887 let (runner, queue_sender) = Queue::start();
888
889 let mut build_version = build_version.to_string();
890 let user_agent = user_agent.to_string();
891
892 if !build_version.is_empty() && !build_version.starts_with('v') {
894 build_version.insert(0, 'v');
895 }
896
897 let gbt = GetBlockTemplateHandler::new(
898 &network,
899 mining_config.clone(),
900 block_verifier_router,
901 sync_status,
902 mined_block_sender,
903 );
904
905 let rpc_impl = RpcImpl {
906 build_version,
907 user_agent,
908 network: network.clone(),
909 debug_force_finished_sync,
910 mempool: mempool.clone(),
911 state: state.clone(),
912 read_state: read_state.clone(),
913 latest_chain_tip: latest_chain_tip.clone(),
914 queue_sender,
915 address_book,
916 last_warn_error_log_rx,
917 gbt,
918 };
919
920 let rpc_tx_queue_task_handle = tokio::spawn(
922 runner
923 .run(mempool, read_state, latest_chain_tip, network)
924 .in_current_span(),
925 );
926
927 (rpc_impl, rpc_tx_queue_task_handle)
928 }
929
930 pub fn network(&self) -> &Network {
932 &self.network
933 }
934}
935
936#[async_trait]
937impl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus> RpcServer
938 for RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
939where
940 Mempool: MempoolService,
941 State: StateService,
942 ReadState: ReadStateService,
943 Tip: ChainTip + Clone + Send + Sync + 'static,
944 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
945 BlockVerifierRouter: BlockVerifierService,
946 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
947{
948 async fn get_info(&self) -> Result<GetInfoResponse> {
949 let version = GetInfoResponse::version_from_string(&self.build_version)
950 .expect("invalid version string");
951
952 let connections = self.address_book.recently_live_peers(Utc::now()).len();
953
954 let last_error_recorded = self.last_warn_error_log_rx.borrow().clone();
955 let (last_error_log, _level, last_error_log_time) = last_error_recorded.unwrap_or((
956 GetInfoResponse::default().errors,
957 tracing::Level::INFO,
958 Utc::now(),
959 ));
960
961 let tip_height = self
962 .latest_chain_tip
963 .best_tip_height()
964 .unwrap_or(Height::MIN);
965 let testnet = self.network.is_a_test_network();
966
967 let pay_tx_fee = 0.0;
973
974 let relay_fee = zebra_chain::transaction::zip317::MIN_MEMPOOL_TX_FEE_RATE as f64
975 / (zebra_chain::amount::COIN as f64);
976 let difficulty = chain_tip_difficulty(self.network.clone(), self.read_state.clone(), true)
977 .await
978 .expect("should always be Ok when `should_use_default` is true");
979
980 let response = GetInfoResponse {
981 version,
982 build: self.build_version.clone(),
983 subversion: self.user_agent.clone(),
984 protocol_version: zebra_network::constants::CURRENT_NETWORK_PROTOCOL_VERSION.0,
985 blocks: tip_height.0,
986 connections,
987 proxy: None,
988 difficulty,
989 testnet,
990 pay_tx_fee,
991 relay_fee,
992 errors: last_error_log,
993 errors_timestamp: last_error_log_time.timestamp(),
994 };
995
996 Ok(response)
997 }
998
999 #[allow(clippy::unwrap_in_result)]
1000 async fn get_blockchain_info(&self) -> Result<GetBlockchainInfoResponse> {
1001 let debug_force_finished_sync = self.debug_force_finished_sync;
1002 let network = &self.network;
1003
1004 let (usage_info_rsp, tip_pool_values_rsp, chain_tip_difficulty) = {
1005 use zebra_state::ReadRequest::*;
1006 let state_call = |request| self.read_state.clone().oneshot(request);
1007 tokio::join!(
1008 state_call(UsageInfo),
1009 state_call(TipPoolValues),
1010 chain_tip_difficulty(network.clone(), self.read_state.clone(), true)
1011 )
1012 };
1013
1014 let (size_on_disk, (tip_height, tip_hash), value_balance, difficulty) = {
1015 use zebra_state::ReadResponse::*;
1016
1017 let UsageInfo(size_on_disk) = usage_info_rsp.map_misc_error()? else {
1018 unreachable!("unmatched response to a TipPoolValues request")
1019 };
1020
1021 let (tip, value_balance) = match tip_pool_values_rsp {
1022 Ok(TipPoolValues {
1023 tip_height,
1024 tip_hash,
1025 value_balance,
1026 }) => ((tip_height, tip_hash), value_balance),
1027 Ok(_) => unreachable!("unmatched response to a TipPoolValues request"),
1028 Err(_) => ((Height::MIN, network.genesis_hash()), Default::default()),
1029 };
1030
1031 let difficulty = chain_tip_difficulty
1032 .expect("should always be Ok when `should_use_default` is true");
1033
1034 (size_on_disk, tip, value_balance, difficulty)
1035 };
1036
1037 let now = Utc::now();
1038 let (estimated_height, verification_progress) = self
1039 .latest_chain_tip
1040 .best_tip_height_and_block_time()
1041 .map(|(tip_height, tip_block_time)| {
1042 let height =
1043 NetworkChainTipHeightEstimator::new(tip_block_time, tip_height, network)
1044 .estimate_height_at(now);
1045
1046 let height =
1050 if tip_block_time > now || height < tip_height || debug_force_finished_sync {
1051 tip_height
1052 } else {
1053 height
1054 };
1055
1056 (height, f64::from(tip_height.0) / f64::from(height.0))
1057 })
1058 .unwrap_or((Height::MIN, 0.0));
1060
1061 let verification_progress = if network.is_regtest() {
1062 1.0
1063 } else {
1064 verification_progress
1065 };
1066
1067 let mut upgrades = IndexMap::new();
1071 for (activation_height, network_upgrade) in network.full_activation_list() {
1072 if let Some(branch_id) = network_upgrade.branch_id() {
1077 let status = if tip_height >= activation_height {
1079 NetworkUpgradeStatus::Active
1080 } else {
1081 NetworkUpgradeStatus::Pending
1082 };
1083
1084 let upgrade = NetworkUpgradeInfo {
1085 name: network_upgrade,
1086 activation_height,
1087 status,
1088 };
1089 upgrades.insert(ConsensusBranchIdHex(branch_id), upgrade);
1090 }
1091 }
1092
1093 let next_block_height =
1095 (tip_height + 1).expect("valid chain tips are a lot less than Height::MAX");
1096 let consensus = TipConsensusBranch {
1097 chain_tip: ConsensusBranchIdHex(
1098 NetworkUpgrade::current(network, tip_height)
1099 .branch_id()
1100 .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
1101 ),
1102 next_block: ConsensusBranchIdHex(
1103 NetworkUpgrade::current(network, next_block_height)
1104 .branch_id()
1105 .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
1106 ),
1107 };
1108
1109 let response = GetBlockchainInfoResponse {
1110 chain: network.bip70_network_name(),
1111 blocks: tip_height,
1112 best_block_hash: tip_hash,
1113 estimated_height,
1114 chain_supply: GetBlockchainInfoBalance::chain_supply(value_balance),
1115 value_pools: GetBlockchainInfoBalance::value_pools(value_balance, None),
1116 upgrades,
1117 consensus,
1118 headers: tip_height,
1119 difficulty,
1120 verification_progress,
1121 chain_work: 0,
1123 pruned: false,
1124 size_on_disk,
1125 commitments: 0,
1127 };
1128
1129 Ok(response)
1130 }
1131
1132 async fn get_address_balance(
1133 &self,
1134 address_strings: GetAddressBalanceRequest,
1135 ) -> Result<GetAddressBalanceResponse> {
1136 let valid_addresses = address_strings.valid_addresses()?;
1137
1138 let request = zebra_state::ReadRequest::AddressBalance(valid_addresses);
1139 let response = self
1140 .read_state
1141 .clone()
1142 .oneshot(request)
1143 .await
1144 .map_misc_error()?;
1145
1146 match response {
1147 zebra_state::ReadResponse::AddressBalance { balance, received } => {
1148 Ok(GetAddressBalanceResponse {
1149 balance: u64::from(balance),
1150 received,
1151 })
1152 }
1153 _ => unreachable!("Unexpected response from state service: {response:?}"),
1154 }
1155 }
1156
1157 async fn send_raw_transaction(
1159 &self,
1160 raw_transaction_hex: String,
1161 _allow_high_fees: Option<bool>,
1162 ) -> Result<SendRawTransactionResponse> {
1163 let mempool = self.mempool.clone();
1164 let queue_sender = self.queue_sender.clone();
1165
1166 let raw_transaction_bytes = Vec::from_hex(raw_transaction_hex)
1169 .map_error(server::error::LegacyCode::Deserialization)?;
1170 let raw_transaction = Transaction::zcash_deserialize(&*raw_transaction_bytes)
1171 .map_error(server::error::LegacyCode::Deserialization)?;
1172
1173 let transaction_hash = raw_transaction.hash();
1174
1175 let unmined_transaction = UnminedTx::from(raw_transaction.clone());
1177 let _ = queue_sender.send(unmined_transaction);
1178
1179 let transaction_parameter = mempool::Gossip::Tx(raw_transaction.into());
1180 let request = mempool::Request::Queue(vec![transaction_parameter]);
1181
1182 let response = mempool.oneshot(request).await.map_misc_error()?;
1183
1184 let mut queue_results = match response {
1185 mempool::Response::Queued(results) => results,
1186 _ => unreachable!("incorrect response variant from mempool service"),
1187 };
1188
1189 assert_eq!(
1190 queue_results.len(),
1191 1,
1192 "mempool service returned more results than expected"
1193 );
1194
1195 let queue_result = queue_results
1196 .pop()
1197 .expect("there should be exactly one item in Vec")
1198 .inspect_err(|err| tracing::debug!("sent transaction to mempool: {:?}", &err))
1199 .map_misc_error()?
1200 .await
1201 .map_misc_error()?;
1202
1203 tracing::debug!("sent transaction to mempool: {:?}", &queue_result);
1204
1205 queue_result
1206 .map(|_| SendRawTransactionResponse(transaction_hash))
1207 .map_error(server::error::LegacyCode::Verify)
1214 }
1215
1216 async fn get_block(
1221 &self,
1222 hash_or_height: String,
1223 verbosity: Option<u8>,
1224 ) -> Result<GetBlockResponse> {
1225 let verbosity = verbosity.unwrap_or(1);
1226 let network = self.network.clone();
1227 let original_hash_or_height = hash_or_height.clone();
1228
1229 let get_block_header_future = if matches!(verbosity, 1 | 2) {
1231 Some(self.get_block_header(original_hash_or_height.clone(), Some(true)))
1232 } else {
1233 None
1234 };
1235
1236 let hash_or_height =
1237 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1238 .map_error(server::error::LegacyCode::InvalidParameter)?;
1241
1242 if verbosity == 0 {
1243 let request = zebra_state::ReadRequest::Block(hash_or_height);
1244 let response = self
1245 .read_state
1246 .clone()
1247 .oneshot(request)
1248 .await
1249 .map_misc_error()?;
1250
1251 match response {
1252 zebra_state::ReadResponse::Block(Some(block)) => {
1253 Ok(GetBlockResponse::Raw(block.into()))
1254 }
1255 zebra_state::ReadResponse::Block(None) => {
1256 Err("Block not found").map_error(server::error::LegacyCode::InvalidParameter)
1257 }
1258 _ => unreachable!("unmatched response to a block request"),
1259 }
1260 } else if let Some(get_block_header_future) = get_block_header_future {
1261 let get_block_header_result: Result<GetBlockHeaderResponse> =
1262 get_block_header_future.await;
1263
1264 let GetBlockHeaderResponse::Object(block_header) = get_block_header_result? else {
1265 panic!("must return Object")
1266 };
1267
1268 let BlockHeaderObject {
1269 hash,
1270 confirmations,
1271 height,
1272 version,
1273 merkle_root,
1274 block_commitments,
1275 final_sapling_root,
1276 sapling_tree_size,
1277 time,
1278 nonce,
1279 solution,
1280 bits,
1281 difficulty,
1282 previous_block_hash,
1283 next_block_hash,
1284 } = *block_header;
1285
1286 let transactions_request = match verbosity {
1287 1 => zebra_state::ReadRequest::TransactionIdsForBlock(hash_or_height),
1288 2 => zebra_state::ReadRequest::BlockAndSize(hash_or_height),
1289 _other => panic!("get_block_header_fut should be none"),
1290 };
1291
1292 let hash_or_height = hash.into();
1297 let requests = vec![
1298 transactions_request,
1306 zebra_state::ReadRequest::OrchardTree(hash_or_height),
1308 zebra_state::ReadRequest::BlockInfo(previous_block_hash.into()),
1310 zebra_state::ReadRequest::BlockInfo(hash_or_height),
1311 ];
1312
1313 let mut futs = FuturesOrdered::new();
1314
1315 for request in requests {
1316 futs.push_back(self.read_state.clone().oneshot(request));
1317 }
1318
1319 let tx_ids_response = futs.next().await.expect("`futs` should not be empty");
1320 let (tx, size): (Vec<_>, Option<usize>) = match tx_ids_response.map_misc_error()? {
1321 zebra_state::ReadResponse::TransactionIdsForBlock(tx_ids) => (
1322 tx_ids
1323 .ok_or_misc_error("block not found")?
1324 .iter()
1325 .map(|tx_id| GetBlockTransaction::Hash(*tx_id))
1326 .collect(),
1327 None,
1328 ),
1329 zebra_state::ReadResponse::BlockAndSize(block_and_size) => {
1330 let (block, size) = block_and_size.ok_or_misc_error("Block not found")?;
1331 let block_time = block.header.time;
1332 let transactions =
1333 block
1334 .transactions
1335 .iter()
1336 .map(|tx| {
1337 GetBlockTransaction::Object(Box::new(
1338 TransactionObject::from_transaction(
1339 tx.clone(),
1340 Some(height),
1341 Some(confirmations.try_into().expect(
1342 "should be less than max block height, i32::MAX",
1343 )),
1344 &network,
1345 Some(block_time),
1346 Some(hash),
1347 Some(true),
1348 tx.hash(),
1349 ),
1350 ))
1351 })
1352 .collect();
1353 (transactions, Some(size))
1354 }
1355 _ => unreachable!("unmatched response to a transaction_ids_for_block request"),
1356 };
1357
1358 let orchard_tree_response = futs.next().await.expect("`futs` should not be empty");
1359 let zebra_state::ReadResponse::OrchardTree(orchard_tree) =
1360 orchard_tree_response.map_misc_error()?
1361 else {
1362 unreachable!("unmatched response to a OrchardTree request");
1363 };
1364
1365 let nu5_activation = NetworkUpgrade::Nu5.activation_height(&network);
1366
1367 let orchard_tree = orchard_tree.ok_or_misc_error("missing Orchard tree")?;
1369
1370 let final_orchard_root = match nu5_activation {
1371 Some(activation_height) if height >= activation_height => {
1372 Some(orchard_tree.root().into())
1373 }
1374 _other => None,
1375 };
1376
1377 let sapling = SaplingTrees {
1378 size: sapling_tree_size,
1379 };
1380
1381 let orchard_tree_size = orchard_tree.count();
1382 let orchard = OrchardTrees {
1383 size: orchard_tree_size,
1384 };
1385
1386 let trees = GetBlockTrees { sapling, orchard };
1387
1388 let block_info_response = futs.next().await.expect("`futs` should not be empty");
1389 let zebra_state::ReadResponse::BlockInfo(prev_block_info) =
1390 block_info_response.map_misc_error()?
1391 else {
1392 unreachable!("unmatched response to a BlockInfo request");
1393 };
1394 let block_info_response = futs.next().await.expect("`futs` should not be empty");
1395 let zebra_state::ReadResponse::BlockInfo(block_info) =
1396 block_info_response.map_misc_error()?
1397 else {
1398 unreachable!("unmatched response to a BlockInfo request");
1399 };
1400
1401 let delta = block_info.as_ref().and_then(|d| {
1402 let value_pools = d.value_pools().constrain::<NegativeAllowed>().ok()?;
1403 let prev_value_pools = prev_block_info
1404 .map(|d| d.value_pools().constrain::<NegativeAllowed>())
1405 .unwrap_or(Ok(ValueBalance::<NegativeAllowed>::zero()))
1406 .ok()?;
1407 (value_pools - prev_value_pools).ok()
1408 });
1409 let size = size.or(block_info.as_ref().map(|d| d.size() as usize));
1410
1411 Ok(GetBlockResponse::Object(Box::new(BlockObject {
1412 hash,
1413 confirmations,
1414 height: Some(height),
1415 version: Some(version),
1416 merkle_root: Some(merkle_root),
1417 time: Some(time),
1418 nonce: Some(nonce),
1419 solution: Some(solution),
1420 bits: Some(bits),
1421 difficulty: Some(difficulty),
1422 tx,
1423 trees,
1424 chain_supply: block_info
1425 .as_ref()
1426 .map(|d| GetBlockchainInfoBalance::chain_supply(*d.value_pools())),
1427 value_pools: block_info
1428 .map(|d| GetBlockchainInfoBalance::value_pools(*d.value_pools(), delta)),
1429 size: size.map(|size| size as i64),
1430 block_commitments: Some(block_commitments),
1431 final_sapling_root: Some(final_sapling_root),
1432 final_orchard_root,
1433 previous_block_hash: Some(previous_block_hash),
1434 next_block_hash,
1435 })))
1436 } else {
1437 Err("invalid verbosity value").map_error(server::error::LegacyCode::InvalidParameter)
1438 }
1439 }
1440
1441 async fn get_block_header(
1442 &self,
1443 hash_or_height: String,
1444 verbose: Option<bool>,
1445 ) -> Result<GetBlockHeaderResponse> {
1446 let verbose = verbose.unwrap_or(true);
1447 let network = self.network.clone();
1448
1449 let hash_or_height =
1450 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1451 .map_error(server::error::LegacyCode::InvalidParameter)?;
1454 let zebra_state::ReadResponse::BlockHeader {
1455 header,
1456 hash,
1457 height,
1458 next_block_hash,
1459 } = self
1460 .read_state
1461 .clone()
1462 .oneshot(zebra_state::ReadRequest::BlockHeader(hash_or_height))
1463 .await
1464 .map_err(|_| "block height not in best chain")
1465 .map_error(
1466 if hash_or_height.hash().is_some() {
1471 server::error::LegacyCode::InvalidAddressOrKey
1472 } else {
1473 server::error::LegacyCode::InvalidParameter
1474 },
1475 )?
1476 else {
1477 panic!("unexpected response to BlockHeader request")
1478 };
1479
1480 let response = if !verbose {
1481 GetBlockHeaderResponse::Raw(HexData(header.zcash_serialize_to_vec().map_misc_error()?))
1482 } else {
1483 let zebra_state::ReadResponse::SaplingTree(sapling_tree) = self
1484 .read_state
1485 .clone()
1486 .oneshot(zebra_state::ReadRequest::SaplingTree(hash_or_height))
1487 .await
1488 .map_misc_error()?
1489 else {
1490 panic!("unexpected response to SaplingTree request")
1491 };
1492
1493 let sapling_tree = sapling_tree.ok_or_misc_error("missing Sapling tree")?;
1495
1496 let zebra_state::ReadResponse::Depth(depth) = self
1497 .read_state
1498 .clone()
1499 .oneshot(zebra_state::ReadRequest::Depth(hash))
1500 .await
1501 .map_misc_error()?
1502 else {
1503 panic!("unexpected response to SaplingTree request")
1504 };
1505
1506 const NOT_IN_BEST_CHAIN_CONFIRMATIONS: i64 = -1;
1509
1510 let confirmations = depth
1513 .map(|depth| i64::from(depth) + 1)
1514 .unwrap_or(NOT_IN_BEST_CHAIN_CONFIRMATIONS);
1515
1516 let mut nonce = *header.nonce;
1517 nonce.reverse();
1518
1519 let sapling_activation = NetworkUpgrade::Sapling.activation_height(&network);
1520 let sapling_tree_size = sapling_tree.count();
1521 let final_sapling_root: [u8; 32] =
1522 if sapling_activation.is_some() && height >= sapling_activation.unwrap() {
1523 let mut root: [u8; 32] = sapling_tree.root().into();
1524 root.reverse();
1525 root
1526 } else {
1527 [0; 32]
1528 };
1529
1530 let difficulty = header.difficulty_threshold.relative_to_network(&network);
1531
1532 let block_commitments = match header.commitment(&network, height).expect(
1533 "Unexpected failure while parsing the blockcommitments field in get_block_header",
1534 ) {
1535 Commitment::PreSaplingReserved(bytes) => bytes,
1536 Commitment::FinalSaplingRoot(_) => final_sapling_root,
1537 Commitment::ChainHistoryActivationReserved => [0; 32],
1538 Commitment::ChainHistoryRoot(root) => root.bytes_in_display_order(),
1539 Commitment::ChainHistoryBlockTxAuthCommitment(hash) => {
1540 hash.bytes_in_display_order()
1541 }
1542 };
1543
1544 let block_header = BlockHeaderObject {
1545 hash,
1546 confirmations,
1547 height,
1548 version: header.version,
1549 merkle_root: header.merkle_root,
1550 block_commitments,
1551 final_sapling_root,
1552 sapling_tree_size,
1553 time: header.time.timestamp(),
1554 nonce,
1555 solution: header.solution,
1556 bits: header.difficulty_threshold,
1557 difficulty,
1558 previous_block_hash: header.previous_block_hash,
1559 next_block_hash,
1560 };
1561
1562 GetBlockHeaderResponse::Object(Box::new(block_header))
1563 };
1564
1565 Ok(response)
1566 }
1567
1568 fn get_best_block_hash(&self) -> Result<GetBlockHashResponse> {
1569 self.latest_chain_tip
1570 .best_tip_hash()
1571 .map(GetBlockHashResponse)
1572 .ok_or_misc_error("No blocks in state")
1573 }
1574
1575 fn get_best_block_height_and_hash(&self) -> Result<GetBlockHeightAndHashResponse> {
1576 self.latest_chain_tip
1577 .best_tip_height_and_hash()
1578 .map(|(height, hash)| GetBlockHeightAndHashResponse { height, hash })
1579 .ok_or_misc_error("No blocks in state")
1580 }
1581
1582 async fn get_mempool_info(&self) -> Result<GetMempoolInfoResponse> {
1583 let mut mempool = self.mempool.clone();
1584
1585 let response = mempool
1586 .ready()
1587 .and_then(|service| service.call(mempool::Request::QueueStats))
1588 .await
1589 .map_misc_error()?;
1590
1591 if let mempool::Response::QueueStats {
1592 size,
1593 bytes,
1594 usage,
1595 fully_notified,
1596 } = response
1597 {
1598 Ok(GetMempoolInfoResponse {
1599 size,
1600 bytes,
1601 usage,
1602 fully_notified,
1603 })
1604 } else {
1605 unreachable!("unexpected response to QueueStats request")
1606 }
1607 }
1608
1609 async fn get_raw_mempool(&self, verbose: Option<bool>) -> Result<GetRawMempoolResponse> {
1610 #[allow(unused)]
1611 let verbose = verbose.unwrap_or(false);
1612
1613 use zebra_chain::block::MAX_BLOCK_BYTES;
1614
1615 let mut mempool = self.mempool.clone();
1616
1617 let request = if verbose {
1618 mempool::Request::FullTransactions
1619 } else {
1620 mempool::Request::TransactionIds
1621 };
1622
1623 let response = mempool
1625 .ready()
1626 .and_then(|service| service.call(request))
1627 .await
1628 .map_misc_error()?;
1629
1630 match response {
1631 mempool::Response::FullTransactions {
1632 mut transactions,
1633 transaction_dependencies,
1634 last_seen_tip_hash: _,
1635 } => {
1636 if verbose {
1637 let map = transactions
1638 .iter()
1639 .map(|unmined_tx| {
1640 (
1641 unmined_tx.transaction.id.mined_id().encode_hex(),
1642 get_raw_mempool::MempoolObject::from_verified_unmined_tx(
1643 unmined_tx,
1644 &transactions,
1645 &transaction_dependencies,
1646 ),
1647 )
1648 })
1649 .collect::<HashMap<_, _>>();
1650 Ok(GetRawMempoolResponse::Verbose(map))
1651 } else {
1652 transactions.sort_by_cached_key(|tx| {
1657 cmp::Reverse((
1660 i64::from(tx.miner_fee) as u128 * MAX_BLOCK_BYTES as u128
1661 / tx.transaction.size as u128,
1662 tx.transaction.id.mined_id(),
1664 ))
1665 });
1666 let tx_ids: Vec<String> = transactions
1667 .iter()
1668 .map(|unmined_tx| unmined_tx.transaction.id.mined_id().encode_hex())
1669 .collect();
1670
1671 Ok(GetRawMempoolResponse::TxIds(tx_ids))
1672 }
1673 }
1674
1675 mempool::Response::TransactionIds(unmined_transaction_ids) => {
1676 let mut tx_ids: Vec<String> = unmined_transaction_ids
1677 .iter()
1678 .map(|id| id.mined_id().encode_hex())
1679 .collect();
1680
1681 tx_ids.sort();
1683
1684 Ok(GetRawMempoolResponse::TxIds(tx_ids))
1685 }
1686
1687 _ => unreachable!("unmatched response to a transactionids request"),
1688 }
1689 }
1690
1691 async fn get_raw_transaction(
1692 &self,
1693 txid: String,
1694 verbose: Option<u8>,
1695 block_hash: Option<String>,
1696 ) -> Result<GetRawTransactionResponse> {
1697 let mut mempool = self.mempool.clone();
1698 let verbose = verbose.unwrap_or(0) != 0;
1699
1700 let txid = transaction::Hash::from_hex(txid)
1703 .map_error(server::error::LegacyCode::InvalidAddressOrKey)?;
1704
1705 if block_hash.is_none() {
1707 match mempool
1708 .ready()
1709 .and_then(|service| {
1710 service.call(mempool::Request::TransactionsByMinedId([txid].into()))
1711 })
1712 .await
1713 .map_misc_error()?
1714 {
1715 mempool::Response::Transactions(txns) => {
1716 if let Some(tx) = txns.first() {
1717 return Ok(if verbose {
1718 GetRawTransactionResponse::Object(Box::new(
1719 TransactionObject::from_transaction(
1720 tx.transaction.clone(),
1721 None,
1722 None,
1723 &self.network,
1724 None,
1725 None,
1726 Some(false),
1727 txid,
1728 ),
1729 ))
1730 } else {
1731 let hex = tx.transaction.clone().into();
1732 GetRawTransactionResponse::Raw(hex)
1733 });
1734 }
1735 }
1736
1737 _ => unreachable!("unmatched response to a `TransactionsByMinedId` request"),
1738 };
1739 }
1740
1741 let txid = if let Some(block_hash) = block_hash {
1742 let block_hash = block::Hash::from_hex(block_hash)
1743 .map_error(server::error::LegacyCode::InvalidAddressOrKey)?;
1744 match self
1745 .read_state
1746 .clone()
1747 .oneshot(zebra_state::ReadRequest::AnyChainTransactionIdsForBlock(
1748 block_hash.into(),
1749 ))
1750 .await
1751 .map_misc_error()?
1752 {
1753 zebra_state::ReadResponse::AnyChainTransactionIdsForBlock(tx_ids) => *tx_ids
1754 .ok_or_error(
1755 server::error::LegacyCode::InvalidAddressOrKey,
1756 "block not found",
1757 )?
1758 .0
1759 .iter()
1760 .find(|id| **id == txid)
1761 .ok_or_error(
1762 server::error::LegacyCode::InvalidAddressOrKey,
1763 "txid not found",
1764 )?,
1765 _ => {
1766 unreachable!("unmatched response to a `AnyChainTransactionIdsForBlock` request")
1767 }
1768 }
1769 } else {
1770 txid
1771 };
1772
1773 match self
1775 .read_state
1776 .clone()
1777 .oneshot(zebra_state::ReadRequest::AnyChainTransaction(txid))
1778 .await
1779 .map_misc_error()?
1780 {
1781 zebra_state::ReadResponse::AnyChainTransaction(Some(tx)) => Ok(if verbose {
1782 match tx {
1783 AnyTx::Mined(tx) => {
1784 let block_hash = match self
1785 .read_state
1786 .clone()
1787 .oneshot(zebra_state::ReadRequest::BestChainBlockHash(tx.height))
1788 .await
1789 .map_misc_error()?
1790 {
1791 zebra_state::ReadResponse::BlockHash(block_hash) => block_hash,
1792 _ => {
1793 unreachable!("unmatched response to a `BestChainBlockHash` request")
1794 }
1795 };
1796
1797 GetRawTransactionResponse::Object(Box::new(
1798 TransactionObject::from_transaction(
1799 tx.tx.clone(),
1800 Some(tx.height),
1801 Some(tx.confirmations),
1802 &self.network,
1803 Some(tx.block_time),
1806 block_hash,
1807 Some(true),
1808 txid,
1809 ),
1810 ))
1811 }
1812 AnyTx::Side((tx, block_hash)) => GetRawTransactionResponse::Object(Box::new(
1813 TransactionObject::from_transaction(
1814 tx.clone(),
1815 None,
1816 None,
1817 &self.network,
1818 None,
1819 Some(block_hash),
1820 Some(false),
1821 txid,
1822 ),
1823 )),
1824 }
1825 } else {
1826 let tx: Arc<Transaction> = tx.into();
1827 let hex = tx.into();
1828 GetRawTransactionResponse::Raw(hex)
1829 }),
1830
1831 zebra_state::ReadResponse::AnyChainTransaction(None) => {
1832 Err("No such mempool or main chain transaction")
1833 .map_error(server::error::LegacyCode::InvalidAddressOrKey)
1834 }
1835
1836 _ => unreachable!("unmatched response to a `Transaction` read request"),
1837 }
1838 }
1839
1840 async fn z_get_treestate(&self, hash_or_height: String) -> Result<GetTreestateResponse> {
1841 let mut read_state = self.read_state.clone();
1842 let network = self.network.clone();
1843
1844 let hash_or_height =
1845 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1846 .map_error(server::error::LegacyCode::InvalidParameter)?;
1849
1850 let block = match read_state
1859 .ready()
1860 .and_then(|service| service.call(zebra_state::ReadRequest::Block(hash_or_height)))
1861 .await
1862 .map_misc_error()?
1863 {
1864 zebra_state::ReadResponse::Block(Some(block)) => block,
1865 zebra_state::ReadResponse::Block(None) => {
1866 return Err("the requested block is not in the main chain")
1869 .map_error(server::error::LegacyCode::InvalidParameter);
1870 }
1871 _ => unreachable!("unmatched response to a block request"),
1872 };
1873
1874 let hash = hash_or_height
1875 .hash_or_else(|_| Some(block.hash()))
1876 .expect("block hash");
1877
1878 let height = hash_or_height
1879 .height_or_else(|_| block.coinbase_height())
1880 .expect("verified blocks have a coinbase height");
1881
1882 let time = u32::try_from(block.header.time.timestamp())
1883 .expect("Timestamps of valid blocks always fit into u32.");
1884
1885 let sapling_nu = zcash_primitives::consensus::NetworkUpgrade::Sapling;
1886 let sapling = if network.is_nu_active(sapling_nu, height.into()) {
1887 match read_state
1888 .ready()
1889 .and_then(|service| {
1890 service.call(zebra_state::ReadRequest::SaplingTree(hash.into()))
1891 })
1892 .await
1893 .map_misc_error()?
1894 {
1895 zebra_state::ReadResponse::SaplingTree(tree) => {
1896 tree.map(|t| (t.to_rpc_bytes(), t.root().bytes_in_display_order().to_vec()))
1897 }
1898 _ => unreachable!("unmatched response to a Sapling tree request"),
1899 }
1900 } else {
1901 None
1902 };
1903 let (sapling_tree, sapling_root) =
1904 sapling.map_or((None, None), |(tree, root)| (Some(tree), Some(root)));
1905
1906 let orchard_nu = zcash_primitives::consensus::NetworkUpgrade::Nu5;
1907 let orchard = if network.is_nu_active(orchard_nu, height.into()) {
1908 match read_state
1909 .ready()
1910 .and_then(|service| {
1911 service.call(zebra_state::ReadRequest::OrchardTree(hash.into()))
1912 })
1913 .await
1914 .map_misc_error()?
1915 {
1916 zebra_state::ReadResponse::OrchardTree(tree) => {
1917 tree.map(|t| (t.to_rpc_bytes(), t.root().bytes_in_display_order().to_vec()))
1918 }
1919 _ => unreachable!("unmatched response to an Orchard tree request"),
1920 }
1921 } else {
1922 None
1923 };
1924 let (orchard_tree, orchard_root) =
1925 orchard.map_or((None, None), |(tree, root)| (Some(tree), Some(root)));
1926
1927 Ok(GetTreestateResponse::new(
1928 hash,
1929 height,
1930 time,
1931 None,
1934 Treestate::new(trees::Commitments::new(sapling_root, sapling_tree)),
1935 Treestate::new(trees::Commitments::new(orchard_root, orchard_tree)),
1936 ))
1937 }
1938
1939 async fn z_get_subtrees_by_index(
1940 &self,
1941 pool: String,
1942 start_index: NoteCommitmentSubtreeIndex,
1943 limit: Option<NoteCommitmentSubtreeIndex>,
1944 ) -> Result<GetSubtreesByIndexResponse> {
1945 let mut read_state = self.read_state.clone();
1946
1947 const POOL_LIST: &[&str] = &["sapling", "orchard"];
1948
1949 if pool == "sapling" {
1950 let request = zebra_state::ReadRequest::SaplingSubtrees { start_index, limit };
1951 let response = read_state
1952 .ready()
1953 .and_then(|service| service.call(request))
1954 .await
1955 .map_misc_error()?;
1956
1957 let subtrees = match response {
1958 zebra_state::ReadResponse::SaplingSubtrees(subtrees) => subtrees,
1959 _ => unreachable!("unmatched response to a subtrees request"),
1960 };
1961
1962 let subtrees = subtrees
1963 .values()
1964 .map(|subtree| SubtreeRpcData {
1965 root: subtree.root.to_bytes().encode_hex(),
1966 end_height: subtree.end_height,
1967 })
1968 .collect();
1969
1970 Ok(GetSubtreesByIndexResponse {
1971 pool,
1972 start_index,
1973 subtrees,
1974 })
1975 } else if pool == "orchard" {
1976 let request = zebra_state::ReadRequest::OrchardSubtrees { start_index, limit };
1977 let response = read_state
1978 .ready()
1979 .and_then(|service| service.call(request))
1980 .await
1981 .map_misc_error()?;
1982
1983 let subtrees = match response {
1984 zebra_state::ReadResponse::OrchardSubtrees(subtrees) => subtrees,
1985 _ => unreachable!("unmatched response to a subtrees request"),
1986 };
1987
1988 let subtrees = subtrees
1989 .values()
1990 .map(|subtree| SubtreeRpcData {
1991 root: subtree.root.encode_hex(),
1992 end_height: subtree.end_height,
1993 })
1994 .collect();
1995
1996 Ok(GetSubtreesByIndexResponse {
1997 pool,
1998 start_index,
1999 subtrees,
2000 })
2001 } else {
2002 Err(ErrorObject::owned(
2003 server::error::LegacyCode::Misc.into(),
2004 format!("invalid pool name, must be one of: {POOL_LIST:?}").as_str(),
2005 None::<()>,
2006 ))
2007 }
2008 }
2009
2010 async fn get_address_tx_ids(&self, request: GetAddressTxIdsRequest) -> Result<Vec<String>> {
2011 let mut read_state = self.read_state.clone();
2012 let latest_chain_tip = self.latest_chain_tip.clone();
2013
2014 let height_range = build_height_range(
2015 request.start,
2016 request.end,
2017 best_chain_tip_height(&latest_chain_tip)?,
2018 )?;
2019
2020 let valid_addresses = request.valid_addresses()?;
2021
2022 let request = zebra_state::ReadRequest::TransactionIdsByAddresses {
2023 addresses: valid_addresses,
2024 height_range,
2025 };
2026 let response = read_state
2027 .ready()
2028 .and_then(|service| service.call(request))
2029 .await
2030 .map_misc_error()?;
2031
2032 let hashes = match response {
2033 zebra_state::ReadResponse::AddressesTransactionIds(hashes) => {
2034 let mut last_tx_location = TransactionLocation::from_usize(Height(0), 0);
2035
2036 hashes
2037 .iter()
2038 .map(|(tx_loc, tx_id)| {
2039 assert!(
2041 *tx_loc > last_tx_location,
2042 "Transactions were not in chain order:\n\
2043 {tx_loc:?} {tx_id:?} was after:\n\
2044 {last_tx_location:?}",
2045 );
2046
2047 last_tx_location = *tx_loc;
2048
2049 tx_id.to_string()
2050 })
2051 .collect()
2052 }
2053 _ => unreachable!("unmatched response to a TransactionsByAddresses request"),
2054 };
2055
2056 Ok(hashes)
2057 }
2058
2059 async fn get_address_utxos(
2060 &self,
2061 utxos_request: GetAddressUtxosRequest,
2062 ) -> Result<GetAddressUtxosResponse> {
2063 let mut read_state = self.read_state.clone();
2064 let mut response_utxos = vec![];
2065
2066 let valid_addresses = utxos_request.valid_addresses()?;
2067
2068 let request = zebra_state::ReadRequest::UtxosByAddresses(valid_addresses);
2070 let response = read_state
2071 .ready()
2072 .and_then(|service| service.call(request))
2073 .await
2074 .map_misc_error()?;
2075 let utxos = match response {
2076 zebra_state::ReadResponse::AddressUtxos(utxos) => utxos,
2077 _ => unreachable!("unmatched response to a UtxosByAddresses request"),
2078 };
2079
2080 let mut last_output_location = OutputLocation::from_usize(Height(0), 0, 0);
2081
2082 for utxo_data in utxos.utxos() {
2083 let address = utxo_data.0;
2084 let txid = *utxo_data.1;
2085 let height = utxo_data.2.height();
2086 let output_index = utxo_data.2.output_index();
2087 let script = utxo_data.3.lock_script.clone();
2088 let satoshis = u64::from(utxo_data.3.value);
2089
2090 let output_location = *utxo_data.2;
2091 assert!(
2093 output_location > last_output_location,
2094 "UTXOs were not in chain order:\n\
2095 {output_location:?} {address:?} {txid:?} was after:\n\
2096 {last_output_location:?}",
2097 );
2098
2099 let entry = Utxo {
2100 address,
2101 txid,
2102 output_index,
2103 script,
2104 satoshis,
2105 height,
2106 };
2107 response_utxos.push(entry);
2108
2109 last_output_location = output_location;
2110 }
2111
2112 if !utxos_request.chain_info {
2113 Ok(GetAddressUtxosResponse::Utxos(response_utxos))
2114 } else {
2115 let (height, hash) = utxos
2116 .last_height_and_hash()
2117 .ok_or_misc_error("No blocks in state")?;
2118
2119 Ok(GetAddressUtxosResponse::UtxosAndChainInfo(
2120 GetAddressUtxosResponseObject {
2121 utxos: response_utxos,
2122 hash,
2123 height,
2124 },
2125 ))
2126 }
2127 }
2128
2129 fn stop(&self) -> Result<String> {
2130 #[cfg(not(target_os = "windows"))]
2131 if self.network.is_regtest() {
2132 match nix::sys::signal::raise(nix::sys::signal::SIGINT) {
2133 Ok(_) => Ok("Zebra server stopping".to_string()),
2134 Err(error) => Err(ErrorObject::owned(
2135 ErrorCode::InternalError.code(),
2136 format!("Failed to shut down: {error}").as_str(),
2137 None::<()>,
2138 )),
2139 }
2140 } else {
2141 Err(ErrorObject::borrowed(
2142 ErrorCode::MethodNotFound.code(),
2143 "stop is only available on regtest networks",
2144 None,
2145 ))
2146 }
2147 #[cfg(target_os = "windows")]
2148 Err(ErrorObject::borrowed(
2149 ErrorCode::MethodNotFound.code(),
2150 "stop is not available in windows targets",
2151 None,
2152 ))
2153 }
2154
2155 fn get_block_count(&self) -> Result<u32> {
2156 best_chain_tip_height(&self.latest_chain_tip).map(|height| height.0)
2157 }
2158
2159 async fn get_block_hash(&self, index: i32) -> Result<GetBlockHashResponse> {
2160 let mut read_state = self.read_state.clone();
2161 let latest_chain_tip = self.latest_chain_tip.clone();
2162
2163 let tip_height = best_chain_tip_height(&latest_chain_tip)?;
2165
2166 let height = height_from_signed_int(index, tip_height)?;
2167
2168 let request = zebra_state::ReadRequest::BestChainBlockHash(height);
2169 let response = read_state
2170 .ready()
2171 .and_then(|service| service.call(request))
2172 .await
2173 .map_error(server::error::LegacyCode::default())?;
2174
2175 match response {
2176 zebra_state::ReadResponse::BlockHash(Some(hash)) => Ok(GetBlockHashResponse(hash)),
2177 zebra_state::ReadResponse::BlockHash(None) => Err(ErrorObject::borrowed(
2178 server::error::LegacyCode::InvalidParameter.into(),
2179 "Block not found",
2180 None,
2181 )),
2182 _ => unreachable!("unmatched response to a block request"),
2183 }
2184 }
2185
2186 async fn get_block_template(
2187 &self,
2188 parameters: Option<GetBlockTemplateParameters>,
2189 ) -> Result<GetBlockTemplateResponse> {
2190 use types::get_block_template::{
2191 check_parameters, check_synced_to_tip, fetch_mempool_transactions,
2192 fetch_state_tip_and_local_time, validate_block_proposal,
2193 zip317::select_mempool_transactions,
2194 };
2195
2196 let network = self.network.clone();
2198 let extra_coinbase_data = self.gbt.extra_coinbase_data();
2199
2200 let mempool = self.mempool.clone();
2202 let mut latest_chain_tip = self.latest_chain_tip.clone();
2203 let sync_status = self.gbt.sync_status();
2204 let read_state = self.read_state.clone();
2205
2206 if let Some(HexData(block_proposal_bytes)) = parameters
2207 .as_ref()
2208 .and_then(GetBlockTemplateParameters::block_proposal_data)
2209 {
2210 return validate_block_proposal(
2211 self.gbt.block_verifier_router(),
2212 block_proposal_bytes,
2213 network,
2214 latest_chain_tip,
2215 sync_status,
2216 )
2217 .await;
2218 }
2219
2220 check_parameters(¶meters)?;
2222
2223 let client_long_poll_id = parameters.as_ref().and_then(|params| params.long_poll_id);
2224
2225 let miner_address = self
2226 .gbt
2227 .miner_address()
2228 .ok_or_misc_error("miner_address not configured")?;
2229
2230 let mut max_time_reached = false;
2234
2235 let (
2238 server_long_poll_id,
2239 chain_tip_and_local_time,
2240 mempool_txs,
2241 mempool_tx_deps,
2242 submit_old,
2243 ) = loop {
2244 check_synced_to_tip(&network, latest_chain_tip.clone(), sync_status.clone())?;
2250 latest_chain_tip.mark_best_tip_seen();
2258
2259 let chain_tip_and_local_time @ zebra_state::GetBlockTemplateChainInfo {
2266 tip_hash,
2267 tip_height,
2268 max_time,
2269 cur_time,
2270 ..
2271 } = fetch_state_tip_and_local_time(read_state.clone()).await?;
2272
2273 let Some((mempool_txs, mempool_tx_deps)) =
2284 fetch_mempool_transactions(mempool.clone(), tip_hash)
2285 .await?
2286 .or_else(|| client_long_poll_id.is_none().then(Default::default))
2290 else {
2291 continue;
2292 };
2293
2294 let server_long_poll_id = LongPollInput::new(
2296 tip_height,
2297 tip_hash,
2298 max_time,
2299 mempool_txs.iter().map(|tx| tx.transaction.id),
2300 )
2301 .generate_id();
2302
2303 if Some(&server_long_poll_id) != client_long_poll_id.as_ref() || max_time_reached {
2308 let mut submit_old = client_long_poll_id
2309 .as_ref()
2310 .map(|old_long_poll_id| server_long_poll_id.submit_old(old_long_poll_id));
2311
2312 if max_time_reached {
2317 submit_old = Some(false);
2318 }
2319
2320 break (
2321 server_long_poll_id,
2322 chain_tip_and_local_time,
2323 mempool_txs,
2324 mempool_tx_deps,
2325 submit_old,
2326 );
2327 }
2328
2329 let wait_for_mempool_request =
2339 tokio::time::sleep(Duration::from_secs(MEMPOOL_LONG_POLL_INTERVAL));
2340
2341 let mut wait_for_best_tip_change = latest_chain_tip.clone();
2344 let wait_for_best_tip_change = wait_for_best_tip_change.best_tip_changed();
2345
2346 let duration_until_max_time = max_time.saturating_duration_since(cur_time);
2358 let wait_for_max_time: OptionFuture<_> = if duration_until_max_time.seconds() > 0 {
2359 Some(tokio::time::sleep(duration_until_max_time.to_std()))
2360 } else {
2361 None
2362 }
2363 .into();
2364
2365 tokio::select! {
2372 biased;
2375
2376 _elapsed = wait_for_mempool_request => {
2378 tracing::debug!(
2379 ?max_time,
2380 ?cur_time,
2381 ?server_long_poll_id,
2382 ?client_long_poll_id,
2383 MEMPOOL_LONG_POLL_INTERVAL,
2384 "checking for a new mempool change after waiting a few seconds"
2385 );
2386 }
2387
2388 tip_changed_result = wait_for_best_tip_change => {
2390 match tip_changed_result {
2391 Ok(()) => {
2392 latest_chain_tip.mark_best_tip_seen();
2396
2397 let new_tip_hash = latest_chain_tip.best_tip_hash();
2398 if new_tip_hash == Some(tip_hash) {
2399 tracing::debug!(
2400 ?max_time,
2401 ?cur_time,
2402 ?server_long_poll_id,
2403 ?client_long_poll_id,
2404 ?tip_hash,
2405 ?tip_height,
2406 "ignoring spurious state change notification"
2407 );
2408
2409 tokio::time::sleep(Duration::from_secs(
2411 MEMPOOL_LONG_POLL_INTERVAL,
2412 )).await;
2413
2414 continue;
2415 }
2416
2417 tracing::debug!(
2418 ?max_time,
2419 ?cur_time,
2420 ?server_long_poll_id,
2421 ?client_long_poll_id,
2422 "returning from long poll because state has changed"
2423 );
2424 }
2425
2426 Err(recv_error) => {
2427 tracing::info!(
2429 ?recv_error,
2430 ?max_time,
2431 ?cur_time,
2432 ?server_long_poll_id,
2433 ?client_long_poll_id,
2434 "returning from long poll due to a state error.\
2435 Is Zebra shutting down?"
2436 );
2437
2438 return Err(recv_error).map_error(server::error::LegacyCode::default());
2439 }
2440 }
2441 }
2442
2443 Some(_elapsed) = wait_for_max_time => {
2446 tracing::info!(
2448 ?max_time,
2449 ?cur_time,
2450 ?server_long_poll_id,
2451 ?client_long_poll_id,
2452 "returning from long poll because max time was reached"
2453 );
2454
2455 max_time_reached = true;
2456 }
2457 }
2458 };
2459
2460 let next_block_height =
2468 (chain_tip_and_local_time.tip_height + 1).expect("tip is far below Height::MAX");
2469
2470 tracing::debug!(
2471 mempool_tx_hashes = ?mempool_txs
2472 .iter()
2473 .map(|tx| tx.transaction.id.mined_id())
2474 .collect::<Vec<_>>(),
2475 "selecting transactions for the template from the mempool"
2476 );
2477
2478 let mempool_txs = select_mempool_transactions(
2480 &network,
2481 next_block_height,
2482 &miner_address,
2483 mempool_txs,
2484 mempool_tx_deps,
2485 extra_coinbase_data.clone(),
2486 #[cfg(all(zcash_unstable = "nu7", feature = "tx_v6"))]
2487 None,
2488 );
2489
2490 tracing::debug!(
2491 selected_mempool_tx_hashes = ?mempool_txs
2492 .iter()
2493 .map(|#[cfg(not(test))] tx, #[cfg(test)] (_, tx)| tx.transaction.id.mined_id())
2494 .collect::<Vec<_>>(),
2495 "selected transactions for the template from the mempool"
2496 );
2497
2498 let response = BlockTemplateResponse::new_internal(
2501 &network,
2502 &miner_address,
2503 &chain_tip_and_local_time,
2504 server_long_poll_id,
2505 mempool_txs,
2506 submit_old,
2507 extra_coinbase_data,
2508 #[cfg(all(zcash_unstable = "nu7", feature = "tx_v6"))]
2509 None,
2510 );
2511
2512 Ok(response.into())
2513 }
2514
2515 async fn submit_block(
2516 &self,
2517 HexData(block_bytes): HexData,
2518 _parameters: Option<SubmitBlockParameters>,
2519 ) -> Result<SubmitBlockResponse> {
2520 let mut block_verifier_router = self.gbt.block_verifier_router();
2521
2522 let block: Block = match block_bytes.zcash_deserialize_into() {
2523 Ok(block_bytes) => block_bytes,
2524 Err(error) => {
2525 tracing::info!(
2526 ?error,
2527 "submit block failed: block bytes could not be deserialized into a structurally valid block"
2528 );
2529
2530 return Ok(SubmitBlockErrorResponse::Rejected.into());
2531 }
2532 };
2533
2534 let height = block
2535 .coinbase_height()
2536 .ok_or_error(0, "coinbase height not found")?;
2537 let block_hash = block.hash();
2538
2539 let block_verifier_router_response = block_verifier_router
2540 .ready()
2541 .await
2542 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?
2543 .call(zebra_consensus::Request::Commit(Arc::new(block)))
2544 .await;
2545
2546 let chain_error = match block_verifier_router_response {
2547 Ok(hash) => {
2554 tracing::info!(?hash, ?height, "submit block accepted");
2555
2556 self.gbt
2557 .advertise_mined_block(hash, height)
2558 .map_error_with_prefix(0, "failed to send mined block to gossip task")?;
2559
2560 return Ok(SubmitBlockResponse::Accepted);
2561 }
2562
2563 Err(box_error) => {
2566 let error = box_error
2567 .downcast::<RouterError>()
2568 .map(|boxed_chain_error| *boxed_chain_error);
2569
2570 tracing::info!(
2571 ?error,
2572 ?block_hash,
2573 ?height,
2574 "submit block failed verification"
2575 );
2576
2577 error
2578 }
2579 };
2580
2581 let response = match chain_error {
2582 Ok(source) if source.is_duplicate_request() => SubmitBlockErrorResponse::Duplicate,
2583
2584 Ok(_verify_chain_error) => SubmitBlockErrorResponse::Rejected,
2600
2601 Err(_unknown_error_type) => SubmitBlockErrorResponse::Rejected,
2604 };
2605
2606 Ok(response.into())
2607 }
2608
2609 async fn get_mining_info(&self) -> Result<GetMiningInfoResponse> {
2610 let network = self.network.clone();
2611 let mut read_state = self.read_state.clone();
2612
2613 let chain_tip = self.latest_chain_tip.clone();
2614 let tip_height = chain_tip.best_tip_height().unwrap_or(Height(0)).0;
2615
2616 let mut current_block_tx = None;
2617 if tip_height > 0 {
2618 let mined_tx_ids = chain_tip.best_tip_mined_transaction_ids();
2619 current_block_tx =
2620 (!mined_tx_ids.is_empty()).then(|| mined_tx_ids.len().saturating_sub(1));
2621 }
2622
2623 let solution_rate_fut = self.get_network_sol_ps(None, None);
2624 let mut current_block_size = None;
2626 if tip_height > 0 {
2627 let request = zebra_state::ReadRequest::TipBlockSize;
2628 let response: zebra_state::ReadResponse = read_state
2629 .ready()
2630 .and_then(|service| service.call(request))
2631 .await
2632 .map_error(server::error::LegacyCode::default())?;
2633 current_block_size = match response {
2634 zebra_state::ReadResponse::TipBlockSize(Some(block_size)) => Some(block_size),
2635 _ => None,
2636 };
2637 }
2638
2639 Ok(GetMiningInfoResponse::new_internal(
2640 tip_height,
2641 current_block_size,
2642 current_block_tx,
2643 network,
2644 solution_rate_fut.await?,
2645 ))
2646 }
2647
2648 async fn get_network_sol_ps(
2649 &self,
2650 num_blocks: Option<i32>,
2651 height: Option<i32>,
2652 ) -> Result<u64> {
2653 let mut num_blocks = num_blocks.unwrap_or(DEFAULT_SOLUTION_RATE_WINDOW_SIZE);
2655 if num_blocks < 1 {
2657 num_blocks = i32::try_from(POW_AVERAGING_WINDOW).expect("fits in i32");
2658 }
2659 let num_blocks =
2660 usize::try_from(num_blocks).expect("just checked for negatives, i32 fits in usize");
2661
2662 let height = height.and_then(|height| height.try_into_height().ok());
2665
2666 let mut read_state = self.read_state.clone();
2667
2668 let request = ReadRequest::SolutionRate { num_blocks, height };
2669
2670 let response = read_state
2671 .ready()
2672 .and_then(|service| service.call(request))
2673 .await
2674 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
2675
2676 let solution_rate = match response {
2677 ReadResponse::SolutionRate(solution_rate) => solution_rate.unwrap_or(0),
2679
2680 _ => unreachable!("unmatched response to a solution rate request"),
2681 };
2682
2683 Ok(solution_rate
2684 .try_into()
2685 .expect("per-second solution rate always fits in u64"))
2686 }
2687
2688 async fn get_network_info(&self) -> Result<GetNetworkInfoResponse> {
2689 let version = GetInfoResponse::version_from_string(&self.build_version)
2690 .expect("invalid version string");
2691
2692 let subversion = self.user_agent.clone();
2693
2694 let protocol_version = zebra_network::constants::CURRENT_NETWORK_PROTOCOL_VERSION.0;
2695
2696 let local_services = format!("{:016x}", PeerServices::NODE_NETWORK);
2698
2699 let timeoffset = 0;
2701
2702 let connections = self.address_book.recently_live_peers(Utc::now()).len();
2703
2704 let networks = vec![
2706 NetworkInfo::new("ipv4".to_string(), false, true, "".to_string(), false),
2707 NetworkInfo::new("ipv6".to_string(), false, true, "".to_string(), false),
2708 NetworkInfo::new("onion".to_string(), false, false, "".to_string(), false),
2709 ];
2710
2711 let relay_fee = zebra_chain::transaction::zip317::MIN_MEMPOOL_TX_FEE_RATE as f64
2712 / (zebra_chain::amount::COIN as f64);
2713
2714 let local_addresses = vec![];
2716
2717 let warnings = "".to_string();
2719
2720 let response = GetNetworkInfoResponse {
2721 version,
2722 subversion,
2723 protocol_version,
2724 local_services,
2725 timeoffset,
2726 connections,
2727 networks,
2728 relay_fee,
2729 local_addresses,
2730 warnings,
2731 };
2732
2733 Ok(response)
2734 }
2735
2736 async fn get_peer_info(&self) -> Result<Vec<PeerInfo>> {
2737 let address_book = self.address_book.clone();
2738 Ok(address_book
2739 .recently_live_peers(chrono::Utc::now())
2740 .into_iter()
2741 .map(PeerInfo::from)
2742 .collect())
2743 }
2744
2745 async fn ping(&self) -> Result<()> {
2746 tracing::debug!("Receiving ping request via RPC");
2747
2748 Ok(())
2752 }
2753
2754 async fn validate_address(&self, raw_address: String) -> Result<ValidateAddressResponse> {
2755 let network = self.network.clone();
2756
2757 validate_address(network, raw_address)
2758 }
2759
2760 async fn z_validate_address(&self, raw_address: String) -> Result<ZValidateAddressResponse> {
2761 let network = self.network.clone();
2762
2763 z_validate_address(network, raw_address)
2764 }
2765
2766 async fn get_block_subsidy(&self, height: Option<u32>) -> Result<GetBlockSubsidyResponse> {
2767 let net = self.network.clone();
2768
2769 let height = match height {
2770 Some(h) => Height(h),
2771 None => best_chain_tip_height(&self.latest_chain_tip)?,
2772 };
2773
2774 let subsidy = block_subsidy(height, &net).map_misc_error()?;
2775
2776 let (lockbox_streams, mut funding_streams): (Vec<_>, Vec<_>) =
2777 funding_stream_values(height, &net, subsidy)
2778 .map_misc_error()?
2779 .into_iter()
2780 .partition(|(receiver, _)| matches!(receiver, FundingStreamReceiver::Deferred));
2782
2783 let [lockbox_total, funding_streams_total] =
2784 [&lockbox_streams, &funding_streams].map(|streams| {
2785 streams
2786 .iter()
2787 .map(|&(_, amount)| amount)
2788 .sum::<std::result::Result<Amount<_>, _>>()
2789 .map(Zec::from)
2790 .map_misc_error()
2791 });
2792
2793 funding_streams.sort_by_key(|(receiver, _funding_stream)| {
2795 ZCASHD_FUNDING_STREAM_ORDER
2796 .iter()
2797 .position(|zcashd_receiver| zcashd_receiver == receiver)
2798 });
2799
2800 let is_nu6 = NetworkUpgrade::current(&net, height) == NetworkUpgrade::Nu6;
2801
2802 let [funding_streams, lockbox_streams] =
2804 [funding_streams, lockbox_streams].map(|streams| {
2805 streams
2806 .into_iter()
2807 .map(|(receiver, value)| {
2808 let address = funding_stream_address(height, &net, receiver);
2809 types::subsidy::FundingStream::new_internal(
2810 is_nu6, receiver, value, address,
2811 )
2812 })
2813 .collect()
2814 });
2815
2816 Ok(GetBlockSubsidyResponse {
2817 miner: miner_subsidy(height, &net, subsidy)
2818 .map_misc_error()?
2819 .into(),
2820 founders: founders_reward(&net, height).into(),
2821 funding_streams,
2822 lockbox_streams,
2823 funding_streams_total: funding_streams_total?,
2824 lockbox_total: lockbox_total?,
2825 total_block_subsidy: subsidy.into(),
2826 })
2827 }
2828
2829 async fn get_difficulty(&self) -> Result<f64> {
2830 chain_tip_difficulty(self.network.clone(), self.read_state.clone(), false).await
2831 }
2832
2833 async fn z_list_unified_receivers(
2834 &self,
2835 address: String,
2836 ) -> Result<ZListUnifiedReceiversResponse> {
2837 use zcash_address::unified::Container;
2838
2839 let (network, unified_address): (
2840 zcash_protocol::consensus::NetworkType,
2841 zcash_address::unified::Address,
2842 ) = zcash_address::unified::Encoding::decode(address.clone().as_str())
2843 .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
2844
2845 let mut p2pkh = None;
2846 let mut p2sh = None;
2847 let mut orchard = None;
2848 let mut sapling = None;
2849
2850 for item in unified_address.items() {
2851 match item {
2852 zcash_address::unified::Receiver::Orchard(_data) => {
2853 let addr = zcash_address::unified::Address::try_from_items(vec![item])
2854 .expect("using data already decoded as valid");
2855 orchard = Some(addr.encode(&network));
2856 }
2857 zcash_address::unified::Receiver::Sapling(data) => {
2858 let addr = zebra_chain::primitives::Address::try_from_sapling(network, data)
2859 .expect("using data already decoded as valid");
2860 sapling = Some(addr.payment_address().unwrap_or_default());
2861 }
2862 zcash_address::unified::Receiver::P2pkh(data) => {
2863 let addr =
2864 zebra_chain::primitives::Address::try_from_transparent_p2pkh(network, data)
2865 .expect("using data already decoded as valid");
2866 p2pkh = Some(addr.payment_address().unwrap_or_default());
2867 }
2868 zcash_address::unified::Receiver::P2sh(data) => {
2869 let addr =
2870 zebra_chain::primitives::Address::try_from_transparent_p2sh(network, data)
2871 .expect("using data already decoded as valid");
2872 p2sh = Some(addr.payment_address().unwrap_or_default());
2873 }
2874 _ => (),
2875 }
2876 }
2877
2878 Ok(ZListUnifiedReceiversResponse::new(
2879 orchard, sapling, p2pkh, p2sh,
2880 ))
2881 }
2882
2883 async fn invalidate_block(&self, block_hash: String) -> Result<()> {
2884 let block_hash = block_hash
2885 .parse()
2886 .map_error(server::error::LegacyCode::InvalidParameter)?;
2887
2888 self.state
2889 .clone()
2890 .oneshot(zebra_state::Request::InvalidateBlock(block_hash))
2891 .await
2892 .map(|rsp| assert_eq!(rsp, zebra_state::Response::Invalidated(block_hash)))
2893 .map_misc_error()
2894 }
2895
2896 async fn reconsider_block(&self, block_hash: String) -> Result<Vec<block::Hash>> {
2897 let block_hash = block_hash
2898 .parse()
2899 .map_error(server::error::LegacyCode::InvalidParameter)?;
2900
2901 self.state
2902 .clone()
2903 .oneshot(zebra_state::Request::ReconsiderBlock(block_hash))
2904 .await
2905 .map(|rsp| match rsp {
2906 zebra_state::Response::Reconsidered(block_hashes) => block_hashes,
2907 _ => unreachable!("unmatched response to a reconsider block request"),
2908 })
2909 .map_misc_error()
2910 }
2911
2912 async fn generate(&self, num_blocks: u32) -> Result<Vec<Hash>> {
2913 let mut rpc = self.clone();
2914 let network = self.network.clone();
2915
2916 if !network.disable_pow() {
2917 return Err(ErrorObject::borrowed(
2918 0,
2919 "generate is only supported on networks where PoW is disabled",
2920 None,
2921 ));
2922 }
2923
2924 let mut block_hashes = Vec::new();
2925 for _ in 0..num_blocks {
2926 let mut extra_coinbase_data = [0u8; 32];
2931 OsRng.fill_bytes(&mut extra_coinbase_data);
2932 rpc.gbt
2933 .set_extra_coinbase_data(extra_coinbase_data.to_vec());
2934
2935 let block_template = rpc
2936 .get_block_template(None)
2937 .await
2938 .map_error(server::error::LegacyCode::default())?;
2939
2940 let GetBlockTemplateResponse::TemplateMode(block_template) = block_template else {
2941 return Err(ErrorObject::borrowed(
2942 0,
2943 "error generating block template",
2944 None,
2945 ));
2946 };
2947
2948 let proposal_block = proposal_block_from_template(
2949 &block_template,
2950 BlockTemplateTimeSource::CurTime,
2951 &network,
2952 )
2953 .map_error(server::error::LegacyCode::default())?;
2954
2955 let hex_proposal_block = HexData(
2956 proposal_block
2957 .zcash_serialize_to_vec()
2958 .map_error(server::error::LegacyCode::default())?,
2959 );
2960
2961 let r = rpc
2962 .submit_block(hex_proposal_block, None)
2963 .await
2964 .map_error(server::error::LegacyCode::default())?;
2965 match r {
2966 SubmitBlockResponse::Accepted => { }
2967 SubmitBlockResponse::ErrorResponse(response) => {
2968 return Err(ErrorObject::owned(
2969 server::error::LegacyCode::Misc.into(),
2970 format!("block was rejected: {:?}", response),
2971 None::<()>,
2972 ));
2973 }
2974 }
2975
2976 block_hashes.push(GetBlockHashResponse(proposal_block.hash()));
2977 }
2978
2979 Ok(block_hashes)
2980 }
2981
2982 async fn add_node(
2983 &self,
2984 addr: zebra_network::PeerSocketAddr,
2985 command: AddNodeCommand,
2986 ) -> Result<()> {
2987 if self.network.is_regtest() {
2988 match command {
2989 AddNodeCommand::Add => {
2990 tracing::info!(?addr, "adding peer address to the address book");
2991 if self.address_book.clone().add_peer(addr) {
2992 Ok(())
2993 } else {
2994 return Err(ErrorObject::owned(
2995 server::error::LegacyCode::ClientNodeAlreadyAdded.into(),
2996 format!("peer address was already present in the address book: {addr}"),
2997 None::<()>,
2998 ));
2999 }
3000 }
3001 }
3002 } else {
3003 return Err(ErrorObject::owned(
3004 ErrorCode::InvalidParams.code(),
3005 "addnode command is only supported on regtest",
3006 None::<()>,
3007 ));
3008 }
3009 }
3010
3011 fn openrpc(&self) -> openrpsee::openrpc::Response {
3012 let mut generator = openrpsee::openrpc::Generator::new();
3013
3014 let methods = METHODS
3015 .into_iter()
3016 .map(|(name, method)| method.generate(&mut generator, name))
3017 .collect();
3018
3019 Ok(openrpsee::openrpc::OpenRpc {
3020 openrpc: "1.3.2",
3021 info: openrpsee::openrpc::Info {
3022 title: env!("CARGO_PKG_NAME"),
3023 description: env!("CARGO_PKG_DESCRIPTION"),
3024 version: env!("CARGO_PKG_VERSION"),
3025 },
3026 methods,
3027 components: generator.into_components(),
3028 })
3029 }
3030 async fn get_tx_out(
3031 &self,
3032 txid: String,
3033 n: u32,
3034 include_mempool: Option<bool>,
3035 ) -> Result<GetTxOutResponse> {
3036 let txid = transaction::Hash::from_hex(txid)
3037 .map_error(server::error::LegacyCode::InvalidParameter)?;
3038
3039 let outpoint = transparent::OutPoint {
3040 hash: txid,
3041 index: n,
3042 };
3043
3044 if include_mempool.unwrap_or(true) {
3046 let rsp = self
3047 .mempool
3048 .clone()
3049 .oneshot(mempool::Request::UnspentOutput(outpoint))
3050 .await
3051 .map_misc_error()?;
3052
3053 match rsp {
3054 mempool::Response::TransparentOutput(Some(CreatedOrSpent::Created {
3056 output,
3057 tx_version,
3058 last_seen_hash,
3059 })) => {
3060 return Ok(GetTxOutResponse(Some(
3061 types::transaction::OutputObject::from_output(
3062 &output,
3063 last_seen_hash.to_string(),
3064 0,
3065 tx_version,
3066 false,
3067 self.network(),
3068 ),
3069 )))
3070 }
3071 mempool::Response::TransparentOutput(Some(CreatedOrSpent::Spent)) => {
3072 return Ok(GetTxOutResponse(None))
3073 }
3074 mempool::Response::TransparentOutput(None) => {}
3075 _ => unreachable!("unmatched response to an `UnspentOutput` request"),
3076 };
3077 }
3078
3079 let tip_rsp = self
3084 .read_state
3085 .clone()
3086 .oneshot(zebra_state::ReadRequest::Tip)
3087 .await
3088 .map_misc_error()?;
3089
3090 let best_block_hash = match tip_rsp {
3091 zebra_state::ReadResponse::Tip(tip) => tip.ok_or_misc_error("No blocks in state")?.1,
3092 _ => unreachable!("unmatched response to a `Tip` request"),
3093 };
3094
3095 let rsp = self
3097 .read_state
3098 .clone()
3099 .oneshot(zebra_state::ReadRequest::Transaction(txid))
3100 .await
3101 .map_misc_error()?;
3102
3103 match rsp {
3104 zebra_state::ReadResponse::Transaction(Some(tx)) => {
3105 let outputs = tx.tx.outputs();
3106 let index: usize = n.try_into().expect("u32 always fits in usize");
3107 let output = match outputs.get(index) {
3108 Some(output) => output,
3109 None => return Ok(GetTxOutResponse(None)),
3111 };
3112
3113 let is_spent = {
3115 let rsp = self
3116 .read_state
3117 .clone()
3118 .oneshot(zebra_state::ReadRequest::IsTransparentOutputSpent(outpoint))
3119 .await
3120 .map_misc_error()?;
3121
3122 match rsp {
3123 zebra_state::ReadResponse::IsTransparentOutputSpent(spent) => spent,
3124 _ => unreachable!(
3125 "unmatched response to an `IsTransparentOutputSpent` request"
3126 ),
3127 }
3128 };
3129
3130 if is_spent {
3131 return Ok(GetTxOutResponse(None));
3132 }
3133
3134 Ok(GetTxOutResponse(Some(
3135 types::transaction::OutputObject::from_output(
3136 output,
3137 best_block_hash.to_string(),
3138 tx.confirmations,
3139 tx.tx.version(),
3140 tx.tx.is_coinbase(),
3141 self.network(),
3142 ),
3143 )))
3144 }
3145 zebra_state::ReadResponse::Transaction(None) => Ok(GetTxOutResponse(None)),
3146 _ => unreachable!("unmatched response to a `Transaction` request"),
3147 }
3148 }
3149}
3150
3151pub fn best_chain_tip_height<Tip>(latest_chain_tip: &Tip) -> Result<Height>
3156where
3157 Tip: ChainTip + Clone + Send + Sync + 'static,
3158{
3159 latest_chain_tip
3160 .best_tip_height()
3161 .ok_or_misc_error("No blocks in state")
3162}
3163
3164#[allow(clippy::too_many_arguments)]
3168#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3169pub struct GetInfoResponse {
3170 #[getter(rename = "raw_version")]
3172 version: u64,
3173
3174 build: String,
3176
3177 subversion: String,
3179
3180 #[serde(rename = "protocolversion")]
3182 protocol_version: u32,
3183
3184 blocks: u32,
3186
3187 connections: usize,
3189
3190 #[serde(skip_serializing_if = "Option::is_none")]
3192 proxy: Option<String>,
3193
3194 difficulty: f64,
3196
3197 testnet: bool,
3199
3200 #[serde(rename = "paytxfee")]
3202 pay_tx_fee: f64,
3203
3204 #[serde(rename = "relayfee")]
3206 relay_fee: f64,
3207
3208 errors: String,
3210
3211 #[serde(rename = "errorstimestamp")]
3213 errors_timestamp: i64,
3214}
3215
3216#[deprecated(note = "Use `GetInfoResponse` instead")]
3217pub use self::GetInfoResponse as GetInfo;
3218
3219impl Default for GetInfoResponse {
3220 fn default() -> Self {
3221 GetInfoResponse {
3222 version: 0,
3223 build: "some build version".to_string(),
3224 subversion: "some subversion".to_string(),
3225 protocol_version: 0,
3226 blocks: 0,
3227 connections: 0,
3228 proxy: None,
3229 difficulty: 0.0,
3230 testnet: false,
3231 pay_tx_fee: 0.0,
3232 relay_fee: 0.0,
3233 errors: "no errors".to_string(),
3234 errors_timestamp: Utc::now().timestamp(),
3235 }
3236 }
3237}
3238
3239impl GetInfoResponse {
3240 #[allow(clippy::too_many_arguments)]
3242 #[deprecated(note = "Use `GetInfoResponse::new` instead")]
3243 pub fn from_parts(
3244 version: u64,
3245 build: String,
3246 subversion: String,
3247 protocol_version: u32,
3248 blocks: u32,
3249 connections: usize,
3250 proxy: Option<String>,
3251 difficulty: f64,
3252 testnet: bool,
3253 pay_tx_fee: f64,
3254 relay_fee: f64,
3255 errors: String,
3256 errors_timestamp: i64,
3257 ) -> Self {
3258 Self {
3259 version,
3260 build,
3261 subversion,
3262 protocol_version,
3263 blocks,
3264 connections,
3265 proxy,
3266 difficulty,
3267 testnet,
3268 pay_tx_fee,
3269 relay_fee,
3270 errors,
3271 errors_timestamp,
3272 }
3273 }
3274
3275 pub fn into_parts(
3277 self,
3278 ) -> (
3279 u64,
3280 String,
3281 String,
3282 u32,
3283 u32,
3284 usize,
3285 Option<String>,
3286 f64,
3287 bool,
3288 f64,
3289 f64,
3290 String,
3291 i64,
3292 ) {
3293 (
3294 self.version,
3295 self.build,
3296 self.subversion,
3297 self.protocol_version,
3298 self.blocks,
3299 self.connections,
3300 self.proxy,
3301 self.difficulty,
3302 self.testnet,
3303 self.pay_tx_fee,
3304 self.relay_fee,
3305 self.errors,
3306 self.errors_timestamp,
3307 )
3308 }
3309
3310 fn version_from_string(build_string: &str) -> Option<u64> {
3312 let semver_version = semver::Version::parse(build_string.strip_prefix('v')?).ok()?;
3313 let build_number = semver_version
3314 .build
3315 .as_str()
3316 .split('.')
3317 .next()
3318 .and_then(|num_str| num_str.parse::<u64>().ok())
3319 .unwrap_or_default();
3320
3321 let version_number = 1_000_000 * semver_version.major
3323 + 10_000 * semver_version.minor
3324 + 100 * semver_version.patch
3325 + build_number;
3326
3327 Some(version_number)
3328 }
3329}
3330
3331pub type BlockchainValuePoolBalances = [GetBlockchainInfoBalance; 5];
3333
3334#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters)]
3338pub struct GetBlockchainInfoResponse {
3339 chain: String,
3341
3342 #[getter(copy)]
3344 blocks: Height,
3345
3346 #[getter(copy)]
3349 headers: Height,
3350
3351 difficulty: f64,
3353
3354 #[serde(rename = "verificationprogress")]
3356 verification_progress: f64,
3357
3358 #[serde(rename = "chainwork")]
3360 chain_work: u64,
3361
3362 pruned: bool,
3364
3365 size_on_disk: u64,
3367
3368 commitments: u64,
3370
3371 #[serde(rename = "bestblockhash", with = "hex")]
3373 #[getter(copy)]
3374 best_block_hash: block::Hash,
3375
3376 #[serde(rename = "estimatedheight")]
3380 #[getter(copy)]
3381 estimated_height: Height,
3382
3383 #[serde(rename = "chainSupply")]
3385 chain_supply: GetBlockchainInfoBalance,
3386
3387 #[serde(rename = "valuePools")]
3389 value_pools: BlockchainValuePoolBalances,
3390
3391 upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
3393
3394 #[getter(copy)]
3396 consensus: TipConsensusBranch,
3397}
3398
3399impl Default for GetBlockchainInfoResponse {
3400 fn default() -> Self {
3401 Self {
3402 chain: "main".to_string(),
3403 blocks: Height(1),
3404 best_block_hash: block::Hash([0; 32]),
3405 estimated_height: Height(1),
3406 chain_supply: GetBlockchainInfoBalance::chain_supply(Default::default()),
3407 value_pools: GetBlockchainInfoBalance::zero_pools(),
3408 upgrades: IndexMap::new(),
3409 consensus: TipConsensusBranch {
3410 chain_tip: ConsensusBranchIdHex(ConsensusBranchId::default()),
3411 next_block: ConsensusBranchIdHex(ConsensusBranchId::default()),
3412 },
3413 headers: Height(1),
3414 difficulty: 0.0,
3415 verification_progress: 0.0,
3416 chain_work: 0,
3417 pruned: false,
3418 size_on_disk: 0,
3419 commitments: 0,
3420 }
3421 }
3422}
3423
3424impl GetBlockchainInfoResponse {
3425 #[allow(clippy::too_many_arguments)]
3429 pub fn new(
3430 chain: String,
3431 blocks: Height,
3432 best_block_hash: block::Hash,
3433 estimated_height: Height,
3434 chain_supply: GetBlockchainInfoBalance,
3435 value_pools: BlockchainValuePoolBalances,
3436 upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
3437 consensus: TipConsensusBranch,
3438 headers: Height,
3439 difficulty: f64,
3440 verification_progress: f64,
3441 chain_work: u64,
3442 pruned: bool,
3443 size_on_disk: u64,
3444 commitments: u64,
3445 ) -> Self {
3446 Self {
3447 chain,
3448 blocks,
3449 best_block_hash,
3450 estimated_height,
3451 chain_supply,
3452 value_pools,
3453 upgrades,
3454 consensus,
3455 headers,
3456 difficulty,
3457 verification_progress,
3458 chain_work,
3459 pruned,
3460 size_on_disk,
3461 commitments,
3462 }
3463 }
3464}
3465
3466#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, JsonSchema)]
3468#[serde(from = "DGetAddressBalanceRequest")]
3469pub struct GetAddressBalanceRequest {
3470 addresses: Vec<String>,
3472}
3473
3474impl From<DGetAddressBalanceRequest> for GetAddressBalanceRequest {
3475 fn from(address_strings: DGetAddressBalanceRequest) -> Self {
3476 match address_strings {
3477 DGetAddressBalanceRequest::Addresses { addresses } => {
3478 GetAddressBalanceRequest { addresses }
3479 }
3480 DGetAddressBalanceRequest::Address(address) => GetAddressBalanceRequest {
3481 addresses: vec![address],
3482 },
3483 }
3484 }
3485}
3486
3487#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, JsonSchema)]
3489#[serde(untagged)]
3490enum DGetAddressBalanceRequest {
3491 Addresses { addresses: Vec<String> },
3493 Address(String),
3495}
3496
3497#[deprecated(note = "Use `GetAddressBalanceRequest` instead.")]
3499pub type AddressStrings = GetAddressBalanceRequest;
3500
3501pub trait ValidateAddresses {
3503 fn valid_addresses(&self) -> Result<HashSet<Address>> {
3507 let valid_addresses: HashSet<Address> = self
3510 .addresses()
3511 .iter()
3512 .map(|address| {
3513 address
3514 .parse()
3515 .map_error(server::error::LegacyCode::InvalidAddressOrKey)
3516 })
3517 .collect::<Result<_>>()?;
3518
3519 Ok(valid_addresses)
3520 }
3521
3522 fn addresses(&self) -> &[String];
3524}
3525
3526impl ValidateAddresses for GetAddressBalanceRequest {
3527 fn addresses(&self) -> &[String] {
3528 &self.addresses
3529 }
3530}
3531
3532impl GetAddressBalanceRequest {
3533 pub fn new(addresses: Vec<String>) -> GetAddressBalanceRequest {
3535 GetAddressBalanceRequest { addresses }
3536 }
3537
3538 #[deprecated(
3540 note = "Use `AddressStrings::new` instead. Validity will be checked by the server."
3541 )]
3542 pub fn new_valid(addresses: Vec<String>) -> Result<GetAddressBalanceRequest> {
3543 let req = Self { addresses };
3544 req.valid_addresses()?;
3545 Ok(req)
3546 }
3547}
3548
3549#[derive(
3551 Clone,
3552 Copy,
3553 Debug,
3554 Default,
3555 Eq,
3556 PartialEq,
3557 Hash,
3558 serde::Serialize,
3559 serde::Deserialize,
3560 Getters,
3561 new,
3562)]
3563pub struct GetAddressBalanceResponse {
3564 balance: u64,
3566 pub received: u64,
3568}
3569
3570#[deprecated(note = "Use `GetAddressBalanceResponse` instead.")]
3571pub use self::GetAddressBalanceResponse as AddressBalance;
3572
3573#[derive(
3575 Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, Getters, new, JsonSchema,
3576)]
3577#[serde(from = "DGetAddressUtxosRequest")]
3578pub struct GetAddressUtxosRequest {
3579 addresses: Vec<String>,
3581 #[serde(default)]
3583 #[serde(rename = "chainInfo")]
3584 chain_info: bool,
3585}
3586
3587impl From<DGetAddressUtxosRequest> for GetAddressUtxosRequest {
3588 fn from(request: DGetAddressUtxosRequest) -> Self {
3589 match request {
3590 DGetAddressUtxosRequest::Single(addr) => GetAddressUtxosRequest {
3591 addresses: vec![addr],
3592 chain_info: false,
3593 },
3594 DGetAddressUtxosRequest::Object {
3595 addresses,
3596 chain_info,
3597 } => GetAddressUtxosRequest {
3598 addresses,
3599 chain_info,
3600 },
3601 }
3602 }
3603}
3604
3605#[derive(Debug, serde::Deserialize, JsonSchema)]
3607#[serde(untagged)]
3608enum DGetAddressUtxosRequest {
3609 Single(String),
3611 Object {
3613 addresses: Vec<String>,
3615 #[serde(default)]
3617 #[serde(rename = "chainInfo")]
3618 chain_info: bool,
3619 },
3620}
3621
3622impl ValidateAddresses for GetAddressUtxosRequest {
3623 fn addresses(&self) -> &[String] {
3624 &self.addresses
3625 }
3626}
3627
3628#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
3630pub struct ConsensusBranchIdHex(#[serde(with = "hex")] ConsensusBranchId);
3631
3632impl ConsensusBranchIdHex {
3633 pub fn new(consensus_branch_id: u32) -> Self {
3635 ConsensusBranchIdHex(consensus_branch_id.into())
3636 }
3637
3638 pub fn inner(&self) -> u32 {
3640 self.0.into()
3641 }
3642}
3643
3644#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3646pub struct NetworkUpgradeInfo {
3647 name: NetworkUpgrade,
3651
3652 #[serde(rename = "activationheight")]
3654 activation_height: Height,
3655
3656 status: NetworkUpgradeStatus,
3658}
3659
3660impl NetworkUpgradeInfo {
3661 pub fn from_parts(
3663 name: NetworkUpgrade,
3664 activation_height: Height,
3665 status: NetworkUpgradeStatus,
3666 ) -> Self {
3667 Self {
3668 name,
3669 activation_height,
3670 status,
3671 }
3672 }
3673
3674 pub fn into_parts(self) -> (NetworkUpgrade, Height, NetworkUpgradeStatus) {
3676 (self.name, self.activation_height, self.status)
3677 }
3678}
3679
3680#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3682pub enum NetworkUpgradeStatus {
3683 #[serde(rename = "active")]
3688 Active,
3689
3690 #[serde(rename = "disabled")]
3692 Disabled,
3693
3694 #[serde(rename = "pending")]
3696 Pending,
3697}
3698
3699#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3703pub struct TipConsensusBranch {
3704 #[serde(rename = "chaintip")]
3706 chain_tip: ConsensusBranchIdHex,
3707
3708 #[serde(rename = "nextblock")]
3710 next_block: ConsensusBranchIdHex,
3711}
3712
3713impl TipConsensusBranch {
3714 pub fn from_parts(chain_tip: u32, next_block: u32) -> Self {
3716 Self {
3717 chain_tip: ConsensusBranchIdHex::new(chain_tip),
3718 next_block: ConsensusBranchIdHex::new(next_block),
3719 }
3720 }
3721
3722 pub fn into_parts(self) -> (u32, u32) {
3724 (self.chain_tip.inner(), self.next_block.inner())
3725 }
3726}
3727
3728#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3734pub struct SendRawTransactionResponse(#[serde(with = "hex")] transaction::Hash);
3735
3736#[deprecated(note = "Use `SendRawTransactionResponse` instead")]
3737pub use self::SendRawTransactionResponse as SentTransactionHash;
3738
3739impl Default for SendRawTransactionResponse {
3740 fn default() -> Self {
3741 Self(transaction::Hash::from([0; 32]))
3742 }
3743}
3744
3745impl SendRawTransactionResponse {
3746 pub fn new(hash: transaction::Hash) -> Self {
3748 SendRawTransactionResponse(hash)
3749 }
3750
3751 #[deprecated(note = "Use `SentTransactionHash::hash` instead")]
3753 pub fn inner(&self) -> transaction::Hash {
3754 self.hash()
3755 }
3756
3757 pub fn hash(&self) -> transaction::Hash {
3759 self.0
3760 }
3761}
3762
3763#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3767#[serde(untagged)]
3768pub enum GetBlockResponse {
3769 Raw(#[serde(with = "hex")] SerializedBlock),
3771 Object(Box<BlockObject>),
3773}
3774
3775#[deprecated(note = "Use `GetBlockResponse` instead")]
3776pub use self::GetBlockResponse as GetBlock;
3777
3778impl Default for GetBlockResponse {
3779 fn default() -> Self {
3780 GetBlockResponse::Object(Box::new(BlockObject {
3781 hash: block::Hash([0; 32]),
3782 confirmations: 0,
3783 height: None,
3784 time: None,
3785 tx: Vec::new(),
3786 trees: GetBlockTrees::default(),
3787 size: None,
3788 version: None,
3789 merkle_root: None,
3790 block_commitments: None,
3791 final_sapling_root: None,
3792 final_orchard_root: None,
3793 nonce: None,
3794 bits: None,
3795 difficulty: None,
3796 chain_supply: None,
3797 value_pools: None,
3798 previous_block_hash: None,
3799 next_block_hash: None,
3800 solution: None,
3801 }))
3802 }
3803}
3804
3805#[allow(clippy::too_many_arguments)]
3807#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3808pub struct BlockObject {
3809 #[getter(copy)]
3811 #[serde(with = "hex")]
3812 hash: block::Hash,
3813
3814 confirmations: i64,
3817
3818 #[serde(skip_serializing_if = "Option::is_none")]
3820 #[getter(copy)]
3821 size: Option<i64>,
3822
3823 #[serde(skip_serializing_if = "Option::is_none")]
3825 #[getter(copy)]
3826 height: Option<Height>,
3827
3828 #[serde(skip_serializing_if = "Option::is_none")]
3830 #[getter(copy)]
3831 version: Option<u32>,
3832
3833 #[serde(with = "opthex", rename = "merkleroot")]
3835 #[serde(skip_serializing_if = "Option::is_none")]
3836 #[getter(copy)]
3837 merkle_root: Option<block::merkle::Root>,
3838
3839 #[serde(with = "opthex", rename = "blockcommitments")]
3842 #[serde(skip_serializing_if = "Option::is_none")]
3843 #[getter(copy)]
3844 block_commitments: Option<[u8; 32]>,
3845
3846 #[serde(with = "opthex", rename = "finalsaplingroot")]
3850 #[serde(skip_serializing_if = "Option::is_none")]
3851 #[getter(copy)]
3852 final_sapling_root: Option<[u8; 32]>,
3853
3854 #[serde(with = "opthex", rename = "finalorchardroot")]
3856 #[serde(skip_serializing_if = "Option::is_none")]
3857 #[getter(copy)]
3858 final_orchard_root: Option<[u8; 32]>,
3859
3860 tx: Vec<GetBlockTransaction>,
3865
3866 #[serde(skip_serializing_if = "Option::is_none")]
3868 #[getter(copy)]
3869 time: Option<i64>,
3870
3871 #[serde(with = "opthex")]
3873 #[serde(skip_serializing_if = "Option::is_none")]
3874 #[getter(copy)]
3875 nonce: Option<[u8; 32]>,
3876
3877 #[serde(with = "opthex")]
3880 #[serde(skip_serializing_if = "Option::is_none")]
3881 #[getter(copy)]
3882 solution: Option<Solution>,
3883
3884 #[serde(with = "opthex")]
3886 #[serde(skip_serializing_if = "Option::is_none")]
3887 #[getter(copy)]
3888 bits: Option<CompactDifficulty>,
3889
3890 #[serde(skip_serializing_if = "Option::is_none")]
3893 #[getter(copy)]
3894 difficulty: Option<f64>,
3895
3896 #[serde(rename = "chainSupply")]
3901 #[serde(skip_serializing_if = "Option::is_none")]
3902 chain_supply: Option<GetBlockchainInfoBalance>,
3903
3904 #[serde(rename = "valuePools")]
3906 #[serde(skip_serializing_if = "Option::is_none")]
3907 value_pools: Option<BlockchainValuePoolBalances>,
3908
3909 #[getter(copy)]
3911 trees: GetBlockTrees,
3912
3913 #[serde(rename = "previousblockhash", skip_serializing_if = "Option::is_none")]
3915 #[serde(with = "opthex")]
3916 #[getter(copy)]
3917 previous_block_hash: Option<block::Hash>,
3918
3919 #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
3921 #[serde(with = "opthex")]
3922 #[getter(copy)]
3923 next_block_hash: Option<block::Hash>,
3924}
3925
3926#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3927#[serde(untagged)]
3928pub enum GetBlockTransaction {
3931 Hash(#[serde(with = "hex")] transaction::Hash),
3933 Object(Box<TransactionObject>),
3935}
3936
3937#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3941#[serde(untagged)]
3942pub enum GetBlockHeaderResponse {
3943 Raw(hex_data::HexData),
3945
3946 Object(Box<BlockHeaderObject>),
3948}
3949
3950#[deprecated(note = "Use `GetBlockHeaderResponse` instead")]
3951pub use self::GetBlockHeaderResponse as GetBlockHeader;
3952
3953#[allow(clippy::too_many_arguments)]
3954#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
3955pub struct BlockHeaderObject {
3959 #[serde(with = "hex")]
3961 #[getter(copy)]
3962 hash: block::Hash,
3963
3964 confirmations: i64,
3967
3968 #[getter(copy)]
3970 height: Height,
3971
3972 version: u32,
3974
3975 #[serde(with = "hex", rename = "merkleroot")]
3977 #[getter(copy)]
3978 merkle_root: block::merkle::Root,
3979
3980 #[serde(with = "hex", rename = "blockcommitments")]
3983 #[getter(copy)]
3984 block_commitments: [u8; 32],
3985
3986 #[serde(with = "hex", rename = "finalsaplingroot")]
3988 #[getter(copy)]
3989 final_sapling_root: [u8; 32],
3990
3991 #[serde(skip)]
3994 sapling_tree_size: u64,
3995
3996 time: i64,
3998
3999 #[serde(with = "hex")]
4001 #[getter(copy)]
4002 nonce: [u8; 32],
4003
4004 #[serde(with = "hex")]
4006 #[getter(copy)]
4007 solution: Solution,
4008
4009 #[serde(with = "hex")]
4011 #[getter(copy)]
4012 bits: CompactDifficulty,
4013
4014 difficulty: f64,
4017
4018 #[serde(rename = "previousblockhash")]
4020 #[serde(with = "hex")]
4021 #[getter(copy)]
4022 previous_block_hash: block::Hash,
4023
4024 #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
4026 #[getter(copy)]
4027 #[serde(with = "opthex")]
4028 next_block_hash: Option<block::Hash>,
4029}
4030
4031#[deprecated(note = "Use `BlockHeaderObject` instead")]
4032pub use BlockHeaderObject as GetBlockHeaderObject;
4033
4034impl Default for GetBlockHeaderResponse {
4035 fn default() -> Self {
4036 GetBlockHeaderResponse::Object(Box::default())
4037 }
4038}
4039
4040impl Default for BlockHeaderObject {
4041 fn default() -> Self {
4042 let difficulty: ExpandedDifficulty = zebra_chain::work::difficulty::U256::one().into();
4043
4044 BlockHeaderObject {
4045 hash: block::Hash([0; 32]),
4046 confirmations: 0,
4047 height: Height::MIN,
4048 version: 4,
4049 merkle_root: block::merkle::Root([0; 32]),
4050 block_commitments: Default::default(),
4051 final_sapling_root: Default::default(),
4052 sapling_tree_size: Default::default(),
4053 time: 0,
4054 nonce: [0; 32],
4055 solution: Solution::for_proposal(),
4056 bits: difficulty.to_compact(),
4057 difficulty: 1.0,
4058 previous_block_hash: block::Hash([0; 32]),
4059 next_block_hash: Some(block::Hash([0; 32])),
4060 }
4061 }
4062}
4063
4064#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
4070#[serde(transparent)]
4071pub struct GetBlockHashResponse(#[serde(with = "hex")] pub(crate) block::Hash);
4072
4073impl GetBlockHashResponse {
4074 pub fn new(hash: block::Hash) -> Self {
4076 GetBlockHashResponse(hash)
4077 }
4078
4079 pub fn hash(&self) -> block::Hash {
4081 self.0
4082 }
4083}
4084
4085#[deprecated(note = "Use `GetBlockHashResponse` instead")]
4086pub use self::GetBlockHashResponse as GetBlockHash;
4087
4088pub type Hash = GetBlockHashResponse;
4090
4091#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, Getters, new)]
4093pub struct GetBlockHeightAndHashResponse {
4094 #[getter(copy)]
4096 height: block::Height,
4097 #[getter(copy)]
4099 hash: block::Hash,
4100}
4101
4102#[deprecated(note = "Use `GetBlockHeightAndHashResponse` instead.")]
4103pub use GetBlockHeightAndHashResponse as GetBestBlockHeightAndHash;
4104
4105impl Default for GetBlockHeightAndHashResponse {
4106 fn default() -> Self {
4107 Self {
4108 height: block::Height::MIN,
4109 hash: block::Hash([0; 32]),
4110 }
4111 }
4112}
4113
4114impl Default for GetBlockHashResponse {
4115 fn default() -> Self {
4116 GetBlockHashResponse(block::Hash([0; 32]))
4117 }
4118}
4119
4120#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
4124#[serde(untagged)]
4125pub enum GetRawTransactionResponse {
4126 Raw(#[serde(with = "hex")] SerializedTransaction),
4128 Object(Box<TransactionObject>),
4130}
4131
4132#[deprecated(note = "Use `GetRawTransactionResponse` instead")]
4133pub use self::GetRawTransactionResponse as GetRawTransaction;
4134
4135impl Default for GetRawTransactionResponse {
4136 fn default() -> Self {
4137 Self::Object(Box::default())
4138 }
4139}
4140
4141#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
4143#[serde(untagged)]
4144pub enum GetAddressUtxosResponse {
4145 Utxos(Vec<Utxo>),
4147 UtxosAndChainInfo(GetAddressUtxosResponseObject),
4149}
4150
4151#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
4153pub struct GetAddressUtxosResponseObject {
4154 utxos: Vec<Utxo>,
4155 #[serde(with = "hex")]
4156 #[getter(copy)]
4157 hash: block::Hash,
4158 #[getter(copy)]
4159 height: block::Height,
4160}
4161
4162#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
4166pub struct Utxo {
4167 address: transparent::Address,
4169
4170 #[serde(with = "hex")]
4172 #[getter(copy)]
4173 txid: transaction::Hash,
4174
4175 #[serde(rename = "outputIndex")]
4177 #[getter(copy)]
4178 output_index: OutputIndex,
4179
4180 #[serde(with = "hex")]
4182 script: transparent::Script,
4183
4184 satoshis: u64,
4186
4187 #[getter(copy)]
4191 height: Height,
4192}
4193
4194#[deprecated(note = "Use `Utxo` instead")]
4195pub use self::Utxo as GetAddressUtxos;
4196
4197impl Default for Utxo {
4198 fn default() -> Self {
4199 Self {
4200 address: transparent::Address::from_pub_key_hash(
4201 zebra_chain::parameters::NetworkKind::default(),
4202 [0u8; 20],
4203 ),
4204 txid: transaction::Hash::from([0; 32]),
4205 output_index: OutputIndex::from_u64(0),
4206 script: transparent::Script::new(&[0u8; 10]),
4207 satoshis: u64::default(),
4208 height: Height(0),
4209 }
4210 }
4211}
4212
4213impl Utxo {
4214 #[deprecated(note = "Use `Utxo::new` instead")]
4216 pub fn from_parts(
4217 address: transparent::Address,
4218 txid: transaction::Hash,
4219 output_index: OutputIndex,
4220 script: transparent::Script,
4221 satoshis: u64,
4222 height: Height,
4223 ) -> Self {
4224 Utxo {
4225 address,
4226 txid,
4227 output_index,
4228 script,
4229 satoshis,
4230 height,
4231 }
4232 }
4233
4234 pub fn into_parts(
4236 &self,
4237 ) -> (
4238 transparent::Address,
4239 transaction::Hash,
4240 OutputIndex,
4241 transparent::Script,
4242 u64,
4243 Height,
4244 ) {
4245 (
4246 self.address.clone(),
4247 self.txid,
4248 self.output_index,
4249 self.script.clone(),
4250 self.satoshis,
4251 self.height,
4252 )
4253 }
4254}
4255
4256#[derive(
4260 Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, Getters, new, JsonSchema,
4261)]
4262#[serde(from = "DGetAddressTxIdsRequest")]
4263pub struct GetAddressTxIdsRequest {
4264 addresses: Vec<String>,
4267 start: Option<u32>,
4269 end: Option<u32>,
4271}
4272
4273impl GetAddressTxIdsRequest {
4274 #[deprecated(note = "Use `GetAddressTxIdsRequest::new` instead.")]
4276 pub fn from_parts(addresses: Vec<String>, start: u32, end: u32) -> Self {
4277 GetAddressTxIdsRequest {
4278 addresses,
4279 start: Some(start),
4280 end: Some(end),
4281 }
4282 }
4283
4284 pub fn into_parts(&self) -> (Vec<String>, u32, u32) {
4286 (
4287 self.addresses.clone(),
4288 self.start.unwrap_or(0),
4289 self.end.unwrap_or(0),
4290 )
4291 }
4292}
4293
4294impl From<DGetAddressTxIdsRequest> for GetAddressTxIdsRequest {
4295 fn from(request: DGetAddressTxIdsRequest) -> Self {
4296 match request {
4297 DGetAddressTxIdsRequest::Single(addr) => GetAddressTxIdsRequest {
4298 addresses: vec![addr],
4299 start: None,
4300 end: None,
4301 },
4302 DGetAddressTxIdsRequest::Object {
4303 addresses,
4304 start,
4305 end,
4306 } => GetAddressTxIdsRequest {
4307 addresses,
4308 start,
4309 end,
4310 },
4311 }
4312 }
4313}
4314
4315#[derive(Debug, serde::Deserialize, JsonSchema)]
4317#[serde(untagged)]
4318enum DGetAddressTxIdsRequest {
4319 Single(String),
4321 Object {
4323 addresses: Vec<String>,
4325 start: Option<u32>,
4327 end: Option<u32>,
4329 },
4330}
4331
4332impl ValidateAddresses for GetAddressTxIdsRequest {
4333 fn addresses(&self) -> &[String] {
4334 &self.addresses
4335 }
4336}
4337
4338#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
4340pub struct GetBlockTrees {
4341 #[serde(skip_serializing_if = "SaplingTrees::is_empty")]
4342 sapling: SaplingTrees,
4343 #[serde(skip_serializing_if = "OrchardTrees::is_empty")]
4344 orchard: OrchardTrees,
4345}
4346
4347impl Default for GetBlockTrees {
4348 fn default() -> Self {
4349 GetBlockTrees {
4350 sapling: SaplingTrees { size: 0 },
4351 orchard: OrchardTrees { size: 0 },
4352 }
4353 }
4354}
4355
4356impl GetBlockTrees {
4357 pub fn new(sapling: u64, orchard: u64) -> Self {
4359 GetBlockTrees {
4360 sapling: SaplingTrees { size: sapling },
4361 orchard: OrchardTrees { size: orchard },
4362 }
4363 }
4364
4365 pub fn sapling(self) -> u64 {
4367 self.sapling.size
4368 }
4369
4370 pub fn orchard(self) -> u64 {
4372 self.orchard.size
4373 }
4374}
4375
4376#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
4378pub struct SaplingTrees {
4379 size: u64,
4380}
4381
4382impl SaplingTrees {
4383 fn is_empty(&self) -> bool {
4384 self.size == 0
4385 }
4386}
4387
4388#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
4390pub struct OrchardTrees {
4391 size: u64,
4392}
4393
4394impl OrchardTrees {
4395 fn is_empty(&self) -> bool {
4396 self.size == 0
4397 }
4398}
4399
4400fn build_height_range(
4416 start: Option<u32>,
4417 end: Option<u32>,
4418 chain_height: Height,
4419) -> Result<RangeInclusive<Height>> {
4420 let start = Height(start.unwrap_or(0)).min(chain_height);
4423
4424 let end = match end {
4426 Some(0) | None => chain_height,
4427 Some(val) => Height(val).min(chain_height),
4428 };
4429
4430 if start > end {
4431 return Err(ErrorObject::owned(
4432 ErrorCode::InvalidParams.code(),
4433 format!("start {start:?} must be less than or equal to end {end:?}"),
4434 None::<()>,
4435 ));
4436 }
4437
4438 Ok(start..=end)
4439}
4440
4441pub fn height_from_signed_int(index: i32, tip_height: Height) -> Result<Height> {
4449 if index >= 0 {
4450 let height = index.try_into().map_err(|_| {
4451 ErrorObject::borrowed(
4452 ErrorCode::InvalidParams.code(),
4453 "Index conversion failed",
4454 None,
4455 )
4456 })?;
4457 if height > tip_height.0 {
4458 return Err(ErrorObject::borrowed(
4459 ErrorCode::InvalidParams.code(),
4460 "Provided index is greater than the current tip",
4461 None,
4462 ));
4463 }
4464 Ok(Height(height))
4465 } else {
4466 let height = i32::try_from(tip_height.0)
4468 .map_err(|_| {
4469 ErrorObject::borrowed(
4470 ErrorCode::InvalidParams.code(),
4471 "Tip height conversion failed",
4472 None,
4473 )
4474 })?
4475 .checked_add(index + 1);
4476
4477 let sanitized_height = match height {
4478 None => {
4479 return Err(ErrorObject::borrowed(
4480 ErrorCode::InvalidParams.code(),
4481 "Provided index is not valid",
4482 None,
4483 ));
4484 }
4485 Some(h) => {
4486 if h < 0 {
4487 return Err(ErrorObject::borrowed(
4488 ErrorCode::InvalidParams.code(),
4489 "Provided negative index ends up with a negative height",
4490 None,
4491 ));
4492 }
4493 let h: u32 = h.try_into().map_err(|_| {
4494 ErrorObject::borrowed(
4495 ErrorCode::InvalidParams.code(),
4496 "Height conversion failed",
4497 None,
4498 )
4499 })?;
4500 if h > tip_height.0 {
4501 return Err(ErrorObject::borrowed(
4502 ErrorCode::InvalidParams.code(),
4503 "Provided index is greater than the current tip",
4504 None,
4505 ));
4506 }
4507
4508 h
4509 }
4510 };
4511
4512 Ok(Height(sanitized_height))
4513 }
4514}
4515
4516pub mod opthex {
4518 use hex::{FromHex, ToHex};
4519 use serde::{de, Deserialize, Deserializer, Serializer};
4520
4521 #[allow(missing_docs)]
4522 pub fn serialize<S, T>(data: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
4523 where
4524 S: Serializer,
4525 T: ToHex,
4526 {
4527 match data {
4528 Some(data) => {
4529 let s = data.encode_hex::<String>();
4530 serializer.serialize_str(&s)
4531 }
4532 None => serializer.serialize_none(),
4533 }
4534 }
4535
4536 #[allow(missing_docs)]
4537 pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
4538 where
4539 D: Deserializer<'de>,
4540 T: FromHex,
4541 {
4542 let opt = Option::<String>::deserialize(deserializer)?;
4543 match opt {
4544 Some(s) => T::from_hex(&s)
4545 .map(Some)
4546 .map_err(|_e| de::Error::custom("failed to convert hex string")),
4547 None => Ok(None),
4548 }
4549 }
4550}
4551
4552pub mod arrayhex {
4554 use serde::{Deserializer, Serializer};
4555 use std::fmt;
4556
4557 #[allow(missing_docs)]
4558 pub fn serialize<S, const N: usize>(data: &[u8; N], serializer: S) -> Result<S::Ok, S::Error>
4559 where
4560 S: Serializer,
4561 {
4562 let hex_string = hex::encode(data);
4563 serializer.serialize_str(&hex_string)
4564 }
4565
4566 #[allow(missing_docs)]
4567 pub fn deserialize<'de, D, const N: usize>(deserializer: D) -> Result<[u8; N], D::Error>
4568 where
4569 D: Deserializer<'de>,
4570 {
4571 struct HexArrayVisitor<const N: usize>;
4572
4573 impl<const N: usize> serde::de::Visitor<'_> for HexArrayVisitor<N> {
4574 type Value = [u8; N];
4575
4576 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
4577 write!(formatter, "a hex string representing exactly {N} bytes")
4578 }
4579
4580 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
4581 where
4582 E: serde::de::Error,
4583 {
4584 let vec = hex::decode(v).map_err(E::custom)?;
4585 vec.clone().try_into().map_err(|_| {
4586 E::invalid_length(vec.len(), &format!("expected {N} bytes").as_str())
4587 })
4588 }
4589 }
4590
4591 deserializer.deserialize_str(HexArrayVisitor::<N>)
4592 }
4593}
4594
4595pub async fn chain_tip_difficulty<State>(
4597 network: Network,
4598 mut state: State,
4599 should_use_default: bool,
4600) -> Result<f64>
4601where
4602 State: ReadStateService,
4603{
4604 let request = ReadRequest::ChainInfo;
4605
4606 let response = state
4612 .ready()
4613 .and_then(|service| service.call(request))
4614 .await;
4615
4616 let response = match (should_use_default, response) {
4617 (_, Ok(res)) => res,
4618 (true, Err(_)) => {
4619 return Ok((U256::from(network.target_difficulty_limit()) >> 128).as_u128() as f64);
4620 }
4621 (false, Err(error)) => return Err(ErrorObject::owned(0, error.to_string(), None::<()>)),
4622 };
4623
4624 let chain_info = match response {
4625 ReadResponse::ChainInfo(info) => info,
4626 _ => unreachable!("unmatched response to a chain info request"),
4627 };
4628
4629 let pow_limit: U256 = network.target_difficulty_limit().into();
4652 let Some(difficulty) = chain_info.expected_difficulty.to_expanded() else {
4653 return Ok(0.0);
4654 };
4655
4656 let pow_limit = pow_limit >> 128;
4658 let difficulty = U256::from(difficulty) >> 128;
4659
4660 let pow_limit = pow_limit.as_u128() as f64;
4663 let difficulty = difficulty.as_u128() as f64;
4664
4665 Ok(pow_limit / difficulty)
4667}
4668
4669#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, JsonSchema)]
4671pub enum AddNodeCommand {
4672 #[serde(rename = "add")]
4674 Add,
4675}
4676
4677#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
4681#[serde(transparent)]
4682pub struct GetTxOutResponse(Option<types::transaction::OutputObject>);