zebra_rpc/
methods.rs

1//! Zebra supported RPC methods.
2//!
3//! Based on the [`zcashd` RPC methods](https://zcash.github.io/rpc/)
4//! as used by `lightwalletd.`
5//!
6//! Some parts of the `zcashd` RPC documentation are outdated.
7//! So this implementation follows the `zcashd` server and `lightwalletd` client implementations.
8
9use std::{
10    cmp,
11    collections::{HashMap, HashSet},
12    fmt,
13    ops::RangeInclusive,
14    sync::Arc,
15    time::Duration,
16};
17
18use chrono::Utc;
19use futures::{future::OptionFuture, stream::FuturesOrdered, StreamExt, TryFutureExt};
20use hex::{FromHex, ToHex};
21use hex_data::HexData;
22use indexmap::IndexMap;
23use jsonrpsee::core::{async_trait, RpcResult as Result};
24use jsonrpsee_proc_macros::rpc;
25use jsonrpsee_types::{ErrorCode, ErrorObject};
26use tokio::{
27    sync::{broadcast, watch},
28    task::JoinHandle,
29};
30use tower::{Service, ServiceExt};
31use tracing::Instrument;
32
33use zcash_address::{unified::Encoding, TryFromAddress};
34use zcash_primitives::consensus::Parameters;
35
36use zebra_chain::{
37    amount::{self, Amount, NonNegative},
38    block::{self, Block, Commitment, Height, SerializedBlock, TryIntoHeight},
39    chain_sync_status::ChainSyncStatus,
40    chain_tip::{ChainTip, NetworkChainTipHeightEstimator},
41    parameters::{
42        subsidy::{FundingStreamReceiver, ParameterSubsidy},
43        ConsensusBranchId, Network, NetworkUpgrade, POW_AVERAGING_WINDOW,
44    },
45    primitives,
46    serialization::{ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize},
47    subtree::NoteCommitmentSubtreeIndex,
48    transaction::{self, SerializedTransaction, Transaction, UnminedTx},
49    transparent::{self, Address},
50    work::{
51        difficulty::{CompactDifficulty, ExpandedDifficulty, ParameterDifficulty, U256},
52        equihash::Solution,
53    },
54};
55use zebra_consensus::{
56    block_subsidy, funding_stream_address, funding_stream_values, miner_subsidy,
57    ParameterCheckpoint, RouterError,
58};
59use zebra_network::address_book_peers::AddressBookPeers;
60use zebra_node_services::mempool;
61use zebra_state::{
62    HashOrHeight, OutputIndex, OutputLocation, ReadRequest, ReadResponse, TransactionLocation,
63};
64
65use crate::{
66    config,
67    methods::trees::{GetSubtrees, GetTreestate, SubtreeRpcData},
68    queue::Queue,
69    server::{
70        self,
71        error::{MapError, OkOrError},
72    },
73};
74
75use types::{
76    get_block_template::{
77        self, constants::MEMPOOL_LONG_POLL_INTERVAL, proposal::proposal_block_from_template,
78        GetBlockTemplate, GetBlockTemplateHandler, ZCASHD_FUNDING_STREAM_ORDER,
79    },
80    get_blockchain_info, get_mining_info,
81    get_raw_mempool::{self, GetRawMempool},
82    long_poll::LongPollInput,
83    peer_info::PeerInfo,
84    submit_block,
85    subsidy::BlockSubsidy,
86    transaction::TransactionObject,
87    unified_address, validate_address, z_validate_address,
88};
89
90pub mod hex_data;
91pub mod trees;
92pub mod types;
93
94#[cfg(test)]
95mod tests;
96
97#[rpc(server)]
98/// RPC method signatures.
99pub trait Rpc {
100    /// Returns software information from the RPC server, as a [`GetInfo`] JSON struct.
101    ///
102    /// zcashd reference: [`getinfo`](https://zcash.github.io/rpc/getinfo.html)
103    /// method: post
104    /// tags: control
105    ///
106    /// # Notes
107    ///
108    /// [The zcashd reference](https://zcash.github.io/rpc/getinfo.html) might not show some fields
109    /// in Zebra's [`GetInfo`]. Zebra uses the field names and formats from the
110    /// [zcashd code](https://github.com/zcash/zcash/blob/v4.6.0-1/src/rpc/misc.cpp#L86-L87).
111    ///
112    /// Some fields from the zcashd reference are missing from Zebra's [`GetInfo`]. It only contains the fields
113    /// [required for lightwalletd support.](https://github.com/zcash/lightwalletd/blob/v0.4.9/common/common.go#L91-L95)
114    #[method(name = "getinfo")]
115    async fn get_info(&self) -> Result<GetInfo>;
116
117    /// Returns blockchain state information, as a [`GetBlockChainInfo`] JSON struct.
118    ///
119    /// zcashd reference: [`getblockchaininfo`](https://zcash.github.io/rpc/getblockchaininfo.html)
120    /// method: post
121    /// tags: blockchain
122    ///
123    /// # Notes
124    ///
125    /// Some fields from the zcashd reference are missing from Zebra's [`GetBlockChainInfo`]. It only contains the fields
126    /// [required for lightwalletd support.](https://github.com/zcash/lightwalletd/blob/v0.4.9/common/common.go#L72-L89)
127    #[method(name = "getblockchaininfo")]
128    async fn get_blockchain_info(&self) -> Result<GetBlockChainInfo>;
129
130    /// Returns the total balance of a provided `addresses` in an [`AddressBalance`] instance.
131    ///
132    /// zcashd reference: [`getaddressbalance`](https://zcash.github.io/rpc/getaddressbalance.html)
133    /// method: post
134    /// tags: address
135    ///
136    /// # Parameters
137    ///
138    /// - `address_strings`: (object, example={"addresses": ["tmYXBYJj1K7vhejSec5osXK2QsGa5MTisUQ"]}) A JSON map with a single entry
139    ///     - `addresses`: (array of strings) A list of base-58 encoded addresses.
140    ///
141    /// # Notes
142    ///
143    /// zcashd also accepts a single string parameter instead of an array of strings, but Zebra
144    /// doesn't because lightwalletd always calls this RPC with an array of addresses.
145    ///
146    /// zcashd also returns the total amount of Zatoshis received by the addresses, but Zebra
147    /// doesn't because lightwalletd doesn't use that information.
148    ///
149    /// The RPC documentation says that the returned object has a string `balance` field, but
150    /// zcashd actually [returns an
151    /// integer](https://github.com/zcash/lightwalletd/blob/bdaac63f3ee0dbef62bde04f6817a9f90d483b00/common/common.go#L128-L130).
152    #[method(name = "getaddressbalance")]
153    async fn get_address_balance(&self, address_strings: AddressStrings) -> Result<AddressBalance>;
154
155    /// Sends the raw bytes of a signed transaction to the local node's mempool, if the transaction is valid.
156    /// Returns the [`SentTransactionHash`] for the transaction, as a JSON string.
157    ///
158    /// zcashd reference: [`sendrawtransaction`](https://zcash.github.io/rpc/sendrawtransaction.html)
159    /// method: post
160    /// tags: transaction
161    ///
162    /// # Parameters
163    ///
164    /// - `raw_transaction_hex`: (string, required, example="signedhex") The hex-encoded raw transaction bytes.
165    /// - `allow_high_fees`: (bool, optional) A legacy parameter accepted by zcashd but ignored by Zebra.
166    ///
167    /// # Notes
168    ///
169    /// zcashd accepts an optional `allowhighfees` parameter. Zebra doesn't support this parameter,
170    /// because lightwalletd doesn't use it.
171    #[method(name = "sendrawtransaction")]
172    async fn send_raw_transaction(
173        &self,
174        raw_transaction_hex: String,
175        _allow_high_fees: Option<bool>,
176    ) -> Result<SentTransactionHash>;
177
178    /// Returns the requested block by hash or height, as a [`GetBlock`] JSON string.
179    /// If the block is not in Zebra's state, returns
180    /// [error code `-8`.](https://github.com/zcash/zcash/issues/5758) if a height was
181    /// passed or -5 if a hash was passed.
182    ///
183    /// zcashd reference: [`getblock`](https://zcash.github.io/rpc/getblock.html)
184    /// method: post
185    /// tags: blockchain
186    ///
187    /// # Parameters
188    ///
189    /// - `hash_or_height`: (string, required, example="1") The hash or height for the block to be returned.
190    /// - `verbosity`: (number, optional, default=1, example=1) 0 for hex encoded data, 1 for a json object, and 2 for json object with transaction data.
191    ///
192    /// # Notes
193    ///
194    /// The `size` field is only returned with verbosity=2.
195    ///
196    /// The undocumented `chainwork` field is not returned.
197    #[method(name = "getblock")]
198    async fn get_block(&self, hash_or_height: String, verbosity: Option<u8>) -> Result<GetBlock>;
199
200    /// Returns the requested block header by hash or height, as a [`GetBlockHeader`] JSON string.
201    /// If the block is not in Zebra's state,
202    /// returns [error code `-8`.](https://github.com/zcash/zcash/issues/5758)
203    /// if a height was passed or -5 if a hash was passed.
204    ///
205    /// zcashd reference: [`getblockheader`](https://zcash.github.io/rpc/getblockheader.html)
206    /// method: post
207    /// tags: blockchain
208    ///
209    /// # Parameters
210    ///
211    /// - `hash_or_height`: (string, required, example="1") The hash or height for the block to be returned.
212    /// - `verbose`: (bool, optional, default=false, example=true) false for hex encoded data, true for a json object
213    ///
214    /// # Notes
215    ///
216    /// The undocumented `chainwork` field is not returned.
217    #[method(name = "getblockheader")]
218    async fn get_block_header(
219        &self,
220        hash_or_height: String,
221        verbose: Option<bool>,
222    ) -> Result<GetBlockHeader>;
223
224    /// Returns the hash of the current best blockchain tip block, as a [`GetBlockHash`] JSON string.
225    ///
226    /// zcashd reference: [`getbestblockhash`](https://zcash.github.io/rpc/getbestblockhash.html)
227    /// method: post
228    /// tags: blockchain
229    #[method(name = "getbestblockhash")]
230    fn get_best_block_hash(&self) -> Result<GetBlockHash>;
231
232    /// Returns the height and hash of the current best blockchain tip block, as a [`GetBlockHeightAndHash`] JSON struct.
233    ///
234    /// zcashd reference: none
235    /// method: post
236    /// tags: blockchain
237    #[method(name = "getbestblockheightandhash")]
238    fn get_best_block_height_and_hash(&self) -> Result<GetBlockHeightAndHash>;
239
240    /// Returns all transaction ids in the memory pool, as a JSON array.
241    ///
242    /// # Parameters
243    ///
244    /// - `verbose`: (boolean, optional, default=false) true for a json object, false for array of transaction ids.
245    ///
246    /// zcashd reference: [`getrawmempool`](https://zcash.github.io/rpc/getrawmempool.html)
247    /// method: post
248    /// tags: blockchain
249    #[method(name = "getrawmempool")]
250    async fn get_raw_mempool(&self, verbose: Option<bool>) -> Result<GetRawMempool>;
251
252    /// Returns information about the given block's Sapling & Orchard tree state.
253    ///
254    /// zcashd reference: [`z_gettreestate`](https://zcash.github.io/rpc/z_gettreestate.html)
255    /// method: post
256    /// tags: blockchain
257    ///
258    /// # Parameters
259    ///
260    /// - `hash | height`: (string, required, example="00000000febc373a1da2bd9f887b105ad79ddc26ac26c2b28652d64e5207c5b5") The block hash or height.
261    ///
262    /// # Notes
263    ///
264    /// The zcashd doc reference above says that the parameter "`height` can be
265    /// negative where -1 is the last known valid block". On the other hand,
266    /// `lightwalletd` only uses positive heights, so Zebra does not support
267    /// negative heights.
268    #[method(name = "z_gettreestate")]
269    async fn z_get_treestate(&self, hash_or_height: String) -> Result<GetTreestate>;
270
271    /// Returns information about a range of Sapling or Orchard subtrees.
272    ///
273    /// zcashd reference: [`z_getsubtreesbyindex`](https://zcash.github.io/rpc/z_getsubtreesbyindex.html) - TODO: fix link
274    /// method: post
275    /// tags: blockchain
276    ///
277    /// # Parameters
278    ///
279    /// - `pool`: (string, required) The pool from which subtrees should be returned. Either "sapling" or "orchard".
280    /// - `start_index`: (number, required) The index of the first 2^16-leaf subtree to return.
281    /// - `limit`: (number, optional) The maximum number of subtree values to return.
282    ///
283    /// # Notes
284    ///
285    /// While Zebra is doing its initial subtree index rebuild, subtrees will become available
286    /// starting at the chain tip. This RPC will return an empty list if the `start_index` subtree
287    /// exists, but has not been rebuilt yet. This matches `zcashd`'s behaviour when subtrees aren't
288    /// available yet. (But `zcashd` does its rebuild before syncing any blocks.)
289    #[method(name = "z_getsubtreesbyindex")]
290    async fn z_get_subtrees_by_index(
291        &self,
292        pool: String,
293        start_index: NoteCommitmentSubtreeIndex,
294        limit: Option<NoteCommitmentSubtreeIndex>,
295    ) -> Result<GetSubtrees>;
296
297    /// Returns the raw transaction data, as a [`GetRawTransaction`] JSON string or structure.
298    ///
299    /// zcashd reference: [`getrawtransaction`](https://zcash.github.io/rpc/getrawtransaction.html)
300    /// method: post
301    /// tags: transaction
302    ///
303    /// # Parameters
304    ///
305    /// - `txid`: (string, required, example="mytxid") The transaction ID of the transaction to be returned.
306    /// - `verbose`: (number, optional, default=0, example=1) If 0, return a string of hex-encoded data, otherwise return a JSON object.
307    ///
308    /// # Notes
309    ///
310    /// We don't currently support the `blockhash` parameter since lightwalletd does not
311    /// use it.
312    ///
313    /// In verbose mode, we only expose the `hex` and `height` fields since
314    /// lightwalletd uses only those:
315    /// <https://github.com/zcash/lightwalletd/blob/631bb16404e3d8b045e74a7c5489db626790b2f6/common/common.go#L119>
316    #[method(name = "getrawtransaction")]
317    async fn get_raw_transaction(
318        &self,
319        txid: String,
320        verbose: Option<u8>,
321    ) -> Result<GetRawTransaction>;
322
323    /// Returns the transaction ids made by the provided transparent addresses.
324    ///
325    /// zcashd reference: [`getaddresstxids`](https://zcash.github.io/rpc/getaddresstxids.html)
326    /// method: post
327    /// tags: address
328    ///
329    /// # Parameters
330    ///
331    /// - `request`: (object, required, example={\"addresses\": [\"tmYXBYJj1K7vhejSec5osXK2QsGa5MTisUQ\"], \"start\": 1000, \"end\": 2000}) A struct with the following named fields:
332    ///     - `addresses`: (json array of string, required) The addresses to get transactions from.
333    ///     - `start`: (numeric, optional) The lower height to start looking for transactions (inclusive).
334    ///     - `end`: (numeric, optional) The top height to stop looking for transactions (inclusive).
335    ///
336    /// # Notes
337    ///
338    /// Only the multi-argument format is used by lightwalletd and this is what we currently support:
339    /// <https://github.com/zcash/lightwalletd/blob/631bb16404e3d8b045e74a7c5489db626790b2f6/common/common.go#L97-L102>
340    #[method(name = "getaddresstxids")]
341    async fn get_address_tx_ids(&self, request: GetAddressTxIdsRequest) -> Result<Vec<String>>;
342
343    /// Returns all unspent outputs for a list of addresses.
344    ///
345    /// zcashd reference: [`getaddressutxos`](https://zcash.github.io/rpc/getaddressutxos.html)
346    /// method: post
347    /// tags: address
348    ///
349    /// # Parameters
350    ///
351    /// - `addresses`: (array, required, example={\"addresses\": [\"tmYXBYJj1K7vhejSec5osXK2QsGa5MTisUQ\"]}) The addresses to get outputs from.
352    ///
353    /// # Notes
354    ///
355    /// lightwalletd always uses the multi-address request, without chaininfo:
356    /// <https://github.com/zcash/lightwalletd/blob/master/frontend/service.go#L402>
357    #[method(name = "getaddressutxos")]
358    async fn get_address_utxos(
359        &self,
360        address_strings: AddressStrings,
361    ) -> Result<Vec<GetAddressUtxos>>;
362
363    /// Stop the running zebrad process.
364    ///
365    /// # Notes
366    ///
367    /// - Works for non windows targets only.
368    /// - Works only if the network of the running zebrad process is `Regtest`.
369    ///
370    /// zcashd reference: [`stop`](https://zcash.github.io/rpc/stop.html)
371    /// method: post
372    /// tags: control
373    #[method(name = "stop")]
374    fn stop(&self) -> Result<String>;
375
376    /// Returns the height of the most recent block in the best valid block chain (equivalently,
377    /// the number of blocks in this chain excluding the genesis block).
378    ///
379    /// zcashd reference: [`getblockcount`](https://zcash.github.io/rpc/getblockcount.html)
380    /// method: post
381    /// tags: blockchain
382    #[method(name = "getblockcount")]
383    fn get_block_count(&self) -> Result<u32>;
384
385    /// Returns the hash of the block of a given height iff the index argument correspond
386    /// to a block in the best chain.
387    ///
388    /// zcashd reference: [`getblockhash`](https://zcash-rpc.github.io/getblockhash.html)
389    /// method: post
390    /// tags: blockchain
391    ///
392    /// # Parameters
393    ///
394    /// - `index`: (numeric, required, example=1) The block index.
395    ///
396    /// # Notes
397    ///
398    /// - If `index` is positive then index = block height.
399    /// - If `index` is negative then -1 is the last known valid block.
400    #[method(name = "getblockhash")]
401    async fn get_block_hash(&self, index: i32) -> Result<GetBlockHash>;
402
403    /// Returns a block template for mining new Zcash blocks.
404    ///
405    /// # Parameters
406    ///
407    /// - `jsonrequestobject`: (string, optional) A JSON object containing arguments.
408    ///
409    /// zcashd reference: [`getblocktemplate`](https://zcash-rpc.github.io/getblocktemplate.html)
410    /// method: post
411    /// tags: mining
412    ///
413    /// # Notes
414    ///
415    /// Arguments to this RPC are currently ignored.
416    /// Long polling, block proposals, server lists, and work IDs are not supported.
417    ///
418    /// Miners can make arbitrary changes to blocks, as long as:
419    /// - the data sent to `submitblock` is a valid Zcash block, and
420    /// - the parent block is a valid block that Zebra already has, or will receive soon.
421    ///
422    /// Zebra verifies blocks in parallel, and keeps recent chains in parallel,
423    /// so moving between chains and forking chains is very cheap.
424    #[method(name = "getblocktemplate")]
425    async fn get_block_template(
426        &self,
427        parameters: Option<get_block_template::parameters::JsonParameters>,
428    ) -> Result<get_block_template::Response>;
429
430    /// Submits block to the node to be validated and committed.
431    /// Returns the [`submit_block::Response`] for the operation, as a JSON string.
432    ///
433    /// zcashd reference: [`submitblock`](https://zcash.github.io/rpc/submitblock.html)
434    /// method: post
435    /// tags: mining
436    ///
437    /// # Parameters
438    ///
439    /// - `hexdata`: (string, required)
440    /// - `jsonparametersobject`: (string, optional) - currently ignored
441    ///
442    /// # Notes
443    ///
444    ///  - `jsonparametersobject` holds a single field, workid, that must be included in submissions if provided by the server.
445    #[method(name = "submitblock")]
446    async fn submit_block(
447        &self,
448        hex_data: HexData,
449        _parameters: Option<submit_block::JsonParameters>,
450    ) -> Result<submit_block::Response>;
451
452    /// Returns mining-related information.
453    ///
454    /// zcashd reference: [`getmininginfo`](https://zcash.github.io/rpc/getmininginfo.html)
455    /// method: post
456    /// tags: mining
457    #[method(name = "getmininginfo")]
458    async fn get_mining_info(&self) -> Result<get_mining_info::Response>;
459
460    /// Returns the estimated network solutions per second based on the last `num_blocks` before
461    /// `height`.
462    ///
463    /// If `num_blocks` is not supplied, uses 120 blocks. If it is 0 or -1, uses the difficulty
464    /// averaging window.
465    /// If `height` is not supplied or is -1, uses the tip height.
466    ///
467    /// zcashd reference: [`getnetworksolps`](https://zcash.github.io/rpc/getnetworksolps.html)
468    /// method: post
469    /// tags: mining
470    #[method(name = "getnetworksolps")]
471    async fn get_network_sol_ps(&self, num_blocks: Option<i32>, height: Option<i32>)
472        -> Result<u64>;
473
474    /// Returns the estimated network solutions per second based on the last `num_blocks` before
475    /// `height`.
476    ///
477    /// This method name is deprecated, use [`getnetworksolps`](Self::get_network_sol_ps) instead.
478    /// See that method for details.
479    ///
480    /// zcashd reference: [`getnetworkhashps`](https://zcash.github.io/rpc/getnetworkhashps.html)
481    /// method: post
482    /// tags: mining
483    #[method(name = "getnetworkhashps")]
484    async fn get_network_hash_ps(
485        &self,
486        num_blocks: Option<i32>,
487        height: Option<i32>,
488    ) -> Result<u64> {
489        self.get_network_sol_ps(num_blocks, height).await
490    }
491
492    /// Returns data about each connected network node.
493    ///
494    /// zcashd reference: [`getpeerinfo`](https://zcash.github.io/rpc/getpeerinfo.html)
495    /// method: post
496    /// tags: network
497    #[method(name = "getpeerinfo")]
498    async fn get_peer_info(&self) -> Result<Vec<PeerInfo>>;
499
500    /// Checks if a zcash transparent address of type P2PKH, P2SH or TEX is valid.
501    /// Returns information about the given address if valid.
502    ///
503    /// zcashd reference: [`validateaddress`](https://zcash.github.io/rpc/validateaddress.html)
504    /// method: post
505    /// tags: util
506    ///
507    /// # Parameters
508    ///
509    /// - `address`: (string, required) The zcash address to validate.
510    #[method(name = "validateaddress")]
511    async fn validate_address(&self, address: String) -> Result<validate_address::Response>;
512
513    /// Checks if a zcash address of type P2PKH, P2SH, TEX, SAPLING or UNIFIED is valid.
514    /// Returns information about the given address if valid.
515    ///
516    /// zcashd reference: [`z_validateaddress`](https://zcash.github.io/rpc/z_validateaddress.html)
517    /// method: post
518    /// tags: util
519    ///
520    /// # Parameters
521    ///
522    /// - `address`: (string, required) The zcash address to validate.
523    ///
524    /// # Notes
525    ///
526    /// - No notes
527    #[method(name = "z_validateaddress")]
528    async fn z_validate_address(&self, address: String) -> Result<z_validate_address::Response>;
529
530    /// Returns the block subsidy reward of the block at `height`, taking into account the mining slow start.
531    /// Returns an error if `height` is less than the height of the first halving for the current network.
532    ///
533    /// zcashd reference: [`getblocksubsidy`](https://zcash.github.io/rpc/getblocksubsidy.html)
534    /// method: post
535    /// tags: mining
536    ///
537    /// # Parameters
538    ///
539    /// - `height`: (numeric, optional, example=1) Can be any valid current or future height.
540    ///
541    /// # Notes
542    ///
543    /// If `height` is not supplied, uses the tip height.
544    #[method(name = "getblocksubsidy")]
545    async fn get_block_subsidy(&self, height: Option<u32>) -> Result<BlockSubsidy>;
546
547    /// Returns the proof-of-work difficulty as a multiple of the minimum difficulty.
548    ///
549    /// zcashd reference: [`getdifficulty`](https://zcash.github.io/rpc/getdifficulty.html)
550    /// method: post
551    /// tags: blockchain
552    #[method(name = "getdifficulty")]
553    async fn get_difficulty(&self) -> Result<f64>;
554
555    /// Returns the list of individual payment addresses given a unified address.
556    ///
557    /// zcashd reference: [`z_listunifiedreceivers`](https://zcash.github.io/rpc/z_listunifiedreceivers.html)
558    /// method: post
559    /// tags: wallet
560    ///
561    /// # Parameters
562    ///
563    /// - `address`: (string, required) The zcash unified address to get the list from.
564    ///
565    /// # Notes
566    ///
567    /// - No notes
568    #[method(name = "z_listunifiedreceivers")]
569    async fn z_list_unified_receivers(&self, address: String) -> Result<unified_address::Response>;
570
571    #[method(name = "generate")]
572    /// Mine blocks immediately. Returns the block hashes of the generated blocks.
573    ///
574    /// # Parameters
575    ///
576    /// - `num_blocks`: (numeric, required, example=1) Number of blocks to be generated.
577    ///
578    /// # Notes
579    ///
580    /// Only works if the network of the running zebrad process is `Regtest`.
581    ///
582    /// zcashd reference: [`generate`](https://zcash.github.io/rpc/generate.html)
583    /// method: post
584    /// tags: generating
585    async fn generate(&self, num_blocks: u32) -> Result<Vec<GetBlockHash>>;
586}
587
588/// RPC method implementations.
589#[derive(Clone)]
590pub struct RpcImpl<Mempool, State, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
591where
592    Mempool: Service<
593            mempool::Request,
594            Response = mempool::Response,
595            Error = zebra_node_services::BoxError,
596        > + Clone
597        + Send
598        + Sync
599        + 'static,
600    Mempool::Future: Send,
601    State: Service<
602            zebra_state::ReadRequest,
603            Response = zebra_state::ReadResponse,
604            Error = zebra_state::BoxError,
605        > + Clone
606        + Send
607        + Sync
608        + 'static,
609    State::Future: Send,
610    Tip: ChainTip + Clone + Send + Sync + 'static,
611    AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
612    BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
613        + Clone
614        + Send
615        + Sync
616        + 'static,
617    <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
618    SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
619{
620    // Configuration
621    //
622    /// Zebra's application version, with build metadata.
623    build_version: String,
624
625    /// Zebra's RPC user agent.
626    user_agent: String,
627
628    /// The configured network for this RPC service.
629    network: Network,
630
631    /// Test-only option that makes Zebra say it is at the chain tip,
632    /// no matter what the estimated height or local clock is.
633    debug_force_finished_sync: bool,
634
635    /// Test-only option that makes RPC responses more like `zcashd`.
636    #[allow(dead_code)]
637    debug_like_zcashd: bool,
638
639    // Services
640    //
641    /// A handle to the mempool service.
642    mempool: Mempool,
643
644    /// A handle to the state service.
645    state: State,
646
647    /// Allows efficient access to the best tip of the blockchain.
648    latest_chain_tip: Tip,
649
650    // Tasks
651    //
652    /// A sender component of a channel used to send transactions to the mempool queue.
653    queue_sender: broadcast::Sender<UnminedTx>,
654
655    /// Peer address book.
656    address_book: AddressBook,
657
658    /// The last warning or error event logged by the server.
659    last_warn_error_log_rx: LoggedLastEvent,
660
661    /// Handler for the `getblocktemplate` RPC.
662    gbt: GetBlockTemplateHandler<BlockVerifierRouter, SyncStatus>,
663}
664
665/// A type alias for the last event logged by the server.
666pub type LoggedLastEvent = watch::Receiver<Option<(String, tracing::Level, chrono::DateTime<Utc>)>>;
667
668impl<Mempool, State, Tip, AddressBook, BlockVerifierRouter, SyncStatus> fmt::Debug
669    for RpcImpl<Mempool, State, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
670where
671    Mempool: Service<
672            mempool::Request,
673            Response = mempool::Response,
674            Error = zebra_node_services::BoxError,
675        > + Clone
676        + Send
677        + Sync
678        + 'static,
679    Mempool::Future: Send,
680    State: Service<
681            zebra_state::ReadRequest,
682            Response = zebra_state::ReadResponse,
683            Error = zebra_state::BoxError,
684        > + Clone
685        + Send
686        + Sync
687        + 'static,
688    State::Future: Send,
689    Tip: ChainTip + Clone + Send + Sync + 'static,
690    AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
691    BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
692        + Clone
693        + Send
694        + Sync
695        + 'static,
696    <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
697    SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
698{
699    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
700        // Skip fields without Debug impls, and skip channels
701        f.debug_struct("RpcImpl")
702            .field("build_version", &self.build_version)
703            .field("user_agent", &self.user_agent)
704            .field("network", &self.network)
705            .field("debug_force_finished_sync", &self.debug_force_finished_sync)
706            .field("debug_like_zcashd", &self.debug_like_zcashd)
707            .field("getblocktemplate", &self.gbt)
708            .finish()
709    }
710}
711
712impl<Mempool, State, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
713    RpcImpl<Mempool, State, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
714where
715    Mempool: Service<
716            mempool::Request,
717            Response = mempool::Response,
718            Error = zebra_node_services::BoxError,
719        > + Clone
720        + Send
721        + Sync
722        + 'static,
723    Mempool::Future: Send,
724    State: Service<
725            zebra_state::ReadRequest,
726            Response = zebra_state::ReadResponse,
727            Error = zebra_state::BoxError,
728        > + Clone
729        + Send
730        + Sync
731        + 'static,
732    State::Future: Send,
733    Tip: ChainTip + Clone + Send + Sync + 'static,
734    AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
735    BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
736        + Clone
737        + Send
738        + Sync
739        + 'static,
740    <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
741    SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
742{
743    /// Create a new instance of the RPC handler.
744    //
745    // TODO:
746    // - put some of the configs or services in their own struct?
747    #[allow(clippy::too_many_arguments)]
748    pub fn new<VersionString, UserAgentString>(
749        network: Network,
750        mining_config: config::mining::Config,
751        debug_force_finished_sync: bool,
752        build_version: VersionString,
753        user_agent: UserAgentString,
754        mempool: Mempool,
755        state: State,
756        block_verifier_router: BlockVerifierRouter,
757        sync_status: SyncStatus,
758        latest_chain_tip: Tip,
759        address_book: AddressBook,
760        last_warn_error_log_rx: LoggedLastEvent,
761        mined_block_sender: Option<watch::Sender<(block::Hash, block::Height)>>,
762    ) -> (Self, JoinHandle<()>)
763    where
764        VersionString: ToString + Clone + Send + 'static,
765        UserAgentString: ToString + Clone + Send + 'static,
766    {
767        let (runner, queue_sender) = Queue::start();
768
769        let mut build_version = build_version.to_string();
770        let user_agent = user_agent.to_string();
771
772        // Match zcashd's version format, if the version string has anything in it
773        if !build_version.is_empty() && !build_version.starts_with('v') {
774            build_version.insert(0, 'v');
775        }
776
777        let gbt = GetBlockTemplateHandler::new(
778            &network,
779            mining_config.clone(),
780            block_verifier_router,
781            sync_status,
782            mined_block_sender,
783        );
784
785        let rpc_impl = RpcImpl {
786            build_version,
787            user_agent,
788            network: network.clone(),
789            debug_force_finished_sync,
790            debug_like_zcashd: mining_config.debug_like_zcashd,
791            mempool: mempool.clone(),
792            state: state.clone(),
793            latest_chain_tip: latest_chain_tip.clone(),
794            queue_sender,
795            address_book,
796            last_warn_error_log_rx,
797            gbt,
798        };
799
800        // run the process queue
801        let rpc_tx_queue_task_handle = tokio::spawn(
802            runner
803                .run(mempool, state, latest_chain_tip, network)
804                .in_current_span(),
805        );
806
807        (rpc_impl, rpc_tx_queue_task_handle)
808    }
809}
810
811#[async_trait]
812impl<Mempool, State, Tip, AddressBook, BlockVerifierRouter, SyncStatus> RpcServer
813    for RpcImpl<Mempool, State, Tip, AddressBook, BlockVerifierRouter, SyncStatus>
814where
815    Mempool: Service<
816            mempool::Request,
817            Response = mempool::Response,
818            Error = zebra_node_services::BoxError,
819        > + Clone
820        + Send
821        + Sync
822        + 'static,
823    Mempool::Future: Send,
824    State: Service<
825            zebra_state::ReadRequest,
826            Response = zebra_state::ReadResponse,
827            Error = zebra_state::BoxError,
828        > + Clone
829        + Send
830        + Sync
831        + 'static,
832    State::Future: Send,
833    Tip: ChainTip + Clone + Send + Sync + 'static,
834    AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
835    BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
836        + Clone
837        + Send
838        + Sync
839        + 'static,
840    <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
841    SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
842{
843    async fn get_info(&self) -> Result<GetInfo> {
844        let version = GetInfo::version(&self.build_version).expect("invalid version string");
845
846        let connections = self.address_book.recently_live_peers(Utc::now()).len();
847
848        let last_error_recorded = self.last_warn_error_log_rx.borrow().clone();
849        let (last_error_log, _level, last_error_log_time) = last_error_recorded.unwrap_or((
850            GetInfo::default().errors,
851            tracing::Level::INFO,
852            Utc::now(),
853        ));
854
855        let tip_height = self
856            .latest_chain_tip
857            .best_tip_height()
858            .unwrap_or(Height::MIN);
859        let testnet = self.network.is_a_test_network();
860
861        // This field is behind the `ENABLE_WALLET` feature flag in zcashd:
862        // https://github.com/zcash/zcash/blob/v6.1.0/src/rpc/misc.cpp#L113
863        // However it is not documented as optional:
864        // https://github.com/zcash/zcash/blob/v6.1.0/src/rpc/misc.cpp#L70
865        // For compatibility, we keep the field in the response, but always return 0.
866        let pay_tx_fee = 0.0;
867
868        let relay_fee = zebra_chain::transaction::zip317::MIN_MEMPOOL_TX_FEE_RATE as f64
869            / (zebra_chain::amount::COIN as f64);
870        let difficulty = chain_tip_difficulty(self.network.clone(), self.state.clone(), true)
871            .await
872            .expect("should always be Ok when `should_use_default` is true");
873
874        let response = GetInfo {
875            version,
876            build: self.build_version.clone(),
877            subversion: self.user_agent.clone(),
878            protocol_version: zebra_network::constants::CURRENT_NETWORK_PROTOCOL_VERSION.0,
879            blocks: tip_height.0,
880            connections,
881            proxy: None,
882            difficulty,
883            testnet,
884            pay_tx_fee,
885            relay_fee,
886            errors: last_error_log,
887            errors_timestamp: last_error_log_time.to_string(),
888        };
889
890        Ok(response)
891    }
892
893    #[allow(clippy::unwrap_in_result)]
894    async fn get_blockchain_info(&self) -> Result<GetBlockChainInfo> {
895        let debug_force_finished_sync = self.debug_force_finished_sync;
896        let network = &self.network;
897
898        let (usage_info_rsp, tip_pool_values_rsp, chain_tip_difficulty) = {
899            use zebra_state::ReadRequest::*;
900            let state_call = |request| self.state.clone().oneshot(request);
901            tokio::join!(
902                state_call(UsageInfo),
903                state_call(TipPoolValues),
904                chain_tip_difficulty(network.clone(), self.state.clone(), true)
905            )
906        };
907
908        let (size_on_disk, (tip_height, tip_hash), value_balance, difficulty) = {
909            use zebra_state::ReadResponse::*;
910
911            let UsageInfo(size_on_disk) = usage_info_rsp.map_misc_error()? else {
912                unreachable!("unmatched response to a TipPoolValues request")
913            };
914
915            let (tip, value_balance) = match tip_pool_values_rsp {
916                Ok(TipPoolValues {
917                    tip_height,
918                    tip_hash,
919                    value_balance,
920                }) => ((tip_height, tip_hash), value_balance),
921                Ok(_) => unreachable!("unmatched response to a TipPoolValues request"),
922                Err(_) => ((Height::MIN, network.genesis_hash()), Default::default()),
923            };
924
925            let difficulty = chain_tip_difficulty
926                .expect("should always be Ok when `should_use_default` is true");
927
928            (size_on_disk, tip, value_balance, difficulty)
929        };
930
931        let now = Utc::now();
932        let (estimated_height, verification_progress) = self
933            .latest_chain_tip
934            .best_tip_height_and_block_time()
935            .map(|(tip_height, tip_block_time)| {
936                let height =
937                    NetworkChainTipHeightEstimator::new(tip_block_time, tip_height, network)
938                        .estimate_height_at(now);
939
940                // If we're testing the mempool, force the estimated height to be the actual tip height, otherwise,
941                // check if the estimated height is below Zebra's latest tip height, or if the latest tip's block time is
942                // later than the current time on the local clock.
943                let height =
944                    if tip_block_time > now || height < tip_height || debug_force_finished_sync {
945                        tip_height
946                    } else {
947                        height
948                    };
949
950                (height, f64::from(tip_height.0) / f64::from(height.0))
951            })
952            // TODO: Add a `genesis_block_time()` method on `Network` to use here.
953            .unwrap_or((Height::MIN, 0.0));
954
955        // `upgrades` object
956        //
957        // Get the network upgrades in height order, like `zcashd`.
958        let mut upgrades = IndexMap::new();
959        for (activation_height, network_upgrade) in network.full_activation_list() {
960            // Zebra defines network upgrades based on incompatible consensus rule changes,
961            // but zcashd defines them based on ZIPs.
962            //
963            // All the network upgrades with a consensus branch ID are the same in Zebra and zcashd.
964            if let Some(branch_id) = network_upgrade.branch_id() {
965                // zcashd's RPC seems to ignore Disabled network upgrades, so Zebra does too.
966                let status = if tip_height >= activation_height {
967                    NetworkUpgradeStatus::Active
968                } else {
969                    NetworkUpgradeStatus::Pending
970                };
971
972                let upgrade = NetworkUpgradeInfo {
973                    name: network_upgrade,
974                    activation_height,
975                    status,
976                };
977                upgrades.insert(ConsensusBranchIdHex(branch_id), upgrade);
978            }
979        }
980
981        // `consensus` object
982        let next_block_height =
983            (tip_height + 1).expect("valid chain tips are a lot less than Height::MAX");
984        let consensus = TipConsensusBranch {
985            chain_tip: ConsensusBranchIdHex(
986                NetworkUpgrade::current(network, tip_height)
987                    .branch_id()
988                    .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
989            ),
990            next_block: ConsensusBranchIdHex(
991                NetworkUpgrade::current(network, next_block_height)
992                    .branch_id()
993                    .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
994            ),
995        };
996
997        let response = GetBlockChainInfo {
998            chain: network.bip70_network_name(),
999            blocks: tip_height,
1000            best_block_hash: tip_hash,
1001            estimated_height,
1002            chain_supply: get_blockchain_info::Balance::chain_supply(value_balance),
1003            value_pools: get_blockchain_info::Balance::value_pools(value_balance),
1004            upgrades,
1005            consensus,
1006            headers: tip_height,
1007            difficulty,
1008            verification_progress,
1009            // TODO: store work in the finalized state for each height (#7109)
1010            chain_work: 0,
1011            pruned: false,
1012            size_on_disk,
1013            // TODO: Investigate whether this needs to be implemented (it's sprout-only in zcashd)
1014            commitments: 0,
1015        };
1016
1017        Ok(response)
1018    }
1019
1020    async fn get_address_balance(&self, address_strings: AddressStrings) -> Result<AddressBalance> {
1021        let state = self.state.clone();
1022
1023        let valid_addresses = address_strings.valid_addresses()?;
1024
1025        let request = zebra_state::ReadRequest::AddressBalance(valid_addresses);
1026        let response = state.oneshot(request).await.map_misc_error()?;
1027
1028        match response {
1029            zebra_state::ReadResponse::AddressBalance(balance) => Ok(AddressBalance {
1030                balance: u64::from(balance),
1031            }),
1032            _ => unreachable!("Unexpected response from state service: {response:?}"),
1033        }
1034    }
1035
1036    // TODO: use HexData or GetRawTransaction::Bytes to handle the transaction data argument
1037    async fn send_raw_transaction(
1038        &self,
1039        raw_transaction_hex: String,
1040        _allow_high_fees: Option<bool>,
1041    ) -> Result<SentTransactionHash> {
1042        let mempool = self.mempool.clone();
1043        let queue_sender = self.queue_sender.clone();
1044
1045        // Reference for the legacy error code:
1046        // <https://github.com/zcash/zcash/blob/99ad6fdc3a549ab510422820eea5e5ce9f60a5fd/src/rpc/rawtransaction.cpp#L1259-L1260>
1047        let raw_transaction_bytes = Vec::from_hex(raw_transaction_hex)
1048            .map_error(server::error::LegacyCode::Deserialization)?;
1049        let raw_transaction = Transaction::zcash_deserialize(&*raw_transaction_bytes)
1050            .map_error(server::error::LegacyCode::Deserialization)?;
1051
1052        let transaction_hash = raw_transaction.hash();
1053
1054        // send transaction to the rpc queue, ignore any error.
1055        let unmined_transaction = UnminedTx::from(raw_transaction.clone());
1056        let _ = queue_sender.send(unmined_transaction);
1057
1058        let transaction_parameter = mempool::Gossip::Tx(raw_transaction.into());
1059        let request = mempool::Request::Queue(vec![transaction_parameter]);
1060
1061        let response = mempool.oneshot(request).await.map_misc_error()?;
1062
1063        let mut queue_results = match response {
1064            mempool::Response::Queued(results) => results,
1065            _ => unreachable!("incorrect response variant from mempool service"),
1066        };
1067
1068        assert_eq!(
1069            queue_results.len(),
1070            1,
1071            "mempool service returned more results than expected"
1072        );
1073
1074        let queue_result = queue_results
1075            .pop()
1076            .expect("there should be exactly one item in Vec")
1077            .inspect_err(|err| tracing::debug!("sent transaction to mempool: {:?}", &err))
1078            .map_misc_error()?
1079            .await
1080            .map_misc_error()?;
1081
1082        tracing::debug!("sent transaction to mempool: {:?}", &queue_result);
1083
1084        queue_result
1085            .map(|_| SentTransactionHash(transaction_hash))
1086            // Reference for the legacy error code:
1087            // <https://github.com/zcash/zcash/blob/99ad6fdc3a549ab510422820eea5e5ce9f60a5fd/src/rpc/rawtransaction.cpp#L1290-L1301>
1088            // Note that this error code might not exactly match the one returned by zcashd
1089            // since zcashd's error code selection logic is more granular. We'd need to
1090            // propagate the error coming from the verifier to be able to return more specific
1091            // error codes.
1092            .map_error(server::error::LegacyCode::Verify)
1093    }
1094
1095    // # Performance
1096    //
1097    // `lightwalletd` calls this RPC with verosity 1 for its initial sync of 2 million blocks, the
1098    // performance of this RPC with verbosity 1 significantly affects `lightwalletd`s sync time.
1099    //
1100    // TODO:
1101    // - use `height_from_signed_int()` to handle negative heights
1102    //   (this might be better in the state request, because it needs the state height)
1103    async fn get_block(&self, hash_or_height: String, verbosity: Option<u8>) -> Result<GetBlock> {
1104        let mut state = self.state.clone();
1105        let verbosity = verbosity.unwrap_or(1);
1106        let network = self.network.clone();
1107        let original_hash_or_height = hash_or_height.clone();
1108
1109        // If verbosity requires a call to `get_block_header`, resolve it here
1110        let get_block_header_future = if matches!(verbosity, 1 | 2) {
1111            Some(self.get_block_header(original_hash_or_height.clone(), Some(true)))
1112        } else {
1113            None
1114        };
1115
1116        let hash_or_height =
1117            HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1118                // Reference for the legacy error code:
1119                // <https://github.com/zcash/zcash/blob/99ad6fdc3a549ab510422820eea5e5ce9f60a5fd/src/rpc/blockchain.cpp#L629>
1120                .map_error(server::error::LegacyCode::InvalidParameter)?;
1121
1122        if verbosity == 0 {
1123            let request = zebra_state::ReadRequest::Block(hash_or_height);
1124            let response = state
1125                .ready()
1126                .and_then(|service| service.call(request))
1127                .await
1128                .map_misc_error()?;
1129
1130            match response {
1131                zebra_state::ReadResponse::Block(Some(block)) => Ok(GetBlock::Raw(block.into())),
1132                zebra_state::ReadResponse::Block(None) => {
1133                    Err("Block not found").map_error(server::error::LegacyCode::InvalidParameter)
1134                }
1135                _ => unreachable!("unmatched response to a block request"),
1136            }
1137        } else if let Some(get_block_header_future) = get_block_header_future {
1138            let get_block_header_result: Result<GetBlockHeader> = get_block_header_future.await;
1139
1140            let GetBlockHeader::Object(block_header) = get_block_header_result? else {
1141                panic!("must return Object")
1142            };
1143
1144            let GetBlockHeaderObject {
1145                hash,
1146                confirmations,
1147                height,
1148                version,
1149                merkle_root,
1150                block_commitments,
1151                final_sapling_root,
1152                sapling_tree_size,
1153                time,
1154                nonce,
1155                solution,
1156                bits,
1157                difficulty,
1158                previous_block_hash,
1159                next_block_hash,
1160            } = *block_header;
1161
1162            let transactions_request = match verbosity {
1163                1 => zebra_state::ReadRequest::TransactionIdsForBlock(hash_or_height),
1164                2 => zebra_state::ReadRequest::BlockAndSize(hash_or_height),
1165                _other => panic!("get_block_header_fut should be none"),
1166            };
1167
1168            // # Concurrency
1169            //
1170            // We look up by block hash so the hash, transaction IDs, and confirmations
1171            // are consistent.
1172            let hash_or_height = hash.0.into();
1173            let requests = vec![
1174                // Get transaction IDs from the transaction index by block hash
1175                //
1176                // # Concurrency
1177                //
1178                // A block's transaction IDs are never modified, so all possible responses are
1179                // valid. Clients that query block heights must be able to handle chain forks,
1180                // including getting transaction IDs from any chain fork.
1181                transactions_request,
1182                // Orchard trees
1183                zebra_state::ReadRequest::OrchardTree(hash_or_height),
1184            ];
1185
1186            let mut futs = FuturesOrdered::new();
1187
1188            for request in requests {
1189                futs.push_back(state.clone().oneshot(request));
1190            }
1191
1192            let tx_ids_response = futs.next().await.expect("`futs` should not be empty");
1193            let (tx, size): (Vec<_>, Option<usize>) = match tx_ids_response.map_misc_error()? {
1194                zebra_state::ReadResponse::TransactionIdsForBlock(tx_ids) => (
1195                    tx_ids
1196                        .ok_or_misc_error("block not found")?
1197                        .iter()
1198                        .map(|tx_id| GetBlockTransaction::Hash(*tx_id))
1199                        .collect(),
1200                    None,
1201                ),
1202                zebra_state::ReadResponse::BlockAndSize(block_and_size) => {
1203                    let (block, size) = block_and_size.ok_or_misc_error("Block not found")?;
1204                    let block_time = block.header.time;
1205                    let transactions =
1206                        block
1207                            .transactions
1208                            .iter()
1209                            .map(|tx| {
1210                                GetBlockTransaction::Object(Box::new(
1211                                    TransactionObject::from_transaction(
1212                                        tx.clone(),
1213                                        Some(height),
1214                                        Some(confirmations.try_into().expect(
1215                                            "should be less than max block height, i32::MAX",
1216                                        )),
1217                                        &network,
1218                                        Some(block_time),
1219                                    ),
1220                                ))
1221                            })
1222                            .collect();
1223                    (transactions, Some(size))
1224                }
1225                _ => unreachable!("unmatched response to a transaction_ids_for_block request"),
1226            };
1227
1228            let orchard_tree_response = futs.next().await.expect("`futs` should not be empty");
1229            let zebra_state::ReadResponse::OrchardTree(orchard_tree) =
1230                orchard_tree_response.map_misc_error()?
1231            else {
1232                unreachable!("unmatched response to a OrchardTree request");
1233            };
1234
1235            let nu5_activation = NetworkUpgrade::Nu5.activation_height(&network);
1236
1237            // This could be `None` if there's a chain reorg between state queries.
1238            let orchard_tree = orchard_tree.ok_or_misc_error("missing Orchard tree")?;
1239
1240            let final_orchard_root = match nu5_activation {
1241                Some(activation_height) if height >= activation_height => {
1242                    Some(orchard_tree.root().into())
1243                }
1244                _other => None,
1245            };
1246
1247            let sapling = SaplingTrees {
1248                size: sapling_tree_size,
1249            };
1250
1251            let orchard_tree_size = orchard_tree.count();
1252            let orchard = OrchardTrees {
1253                size: orchard_tree_size,
1254            };
1255
1256            let trees = GetBlockTrees { sapling, orchard };
1257
1258            Ok(GetBlock::Object {
1259                hash,
1260                confirmations,
1261                height: Some(height),
1262                version: Some(version),
1263                merkle_root: Some(merkle_root),
1264                time: Some(time),
1265                nonce: Some(nonce),
1266                solution: Some(solution),
1267                bits: Some(bits),
1268                difficulty: Some(difficulty),
1269                tx,
1270                trees,
1271                size: size.map(|size| size as i64),
1272                block_commitments: Some(block_commitments),
1273                final_sapling_root: Some(final_sapling_root),
1274                final_orchard_root,
1275                previous_block_hash: Some(previous_block_hash),
1276                next_block_hash,
1277            })
1278        } else {
1279            Err("invalid verbosity value").map_error(server::error::LegacyCode::InvalidParameter)
1280        }
1281    }
1282
1283    async fn get_block_header(
1284        &self,
1285        hash_or_height: String,
1286        verbose: Option<bool>,
1287    ) -> Result<GetBlockHeader> {
1288        let state = self.state.clone();
1289        let verbose = verbose.unwrap_or(true);
1290        let network = self.network.clone();
1291
1292        let hash_or_height =
1293            HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1294                // Reference for the legacy error code:
1295                // <https://github.com/zcash/zcash/blob/99ad6fdc3a549ab510422820eea5e5ce9f60a5fd/src/rpc/blockchain.cpp#L629>
1296                .map_error(server::error::LegacyCode::InvalidParameter)?;
1297        let zebra_state::ReadResponse::BlockHeader {
1298            header,
1299            hash,
1300            height,
1301            next_block_hash,
1302        } = state
1303            .clone()
1304            .oneshot(zebra_state::ReadRequest::BlockHeader(hash_or_height))
1305            .await
1306            .map_err(|_| "block height not in best chain")
1307            .map_error(
1308                // ## Compatibility with `zcashd`.
1309                //
1310                // Since this function is reused by getblock(), we return the errors
1311                // expected by it (they differ whether a hash or a height was passed).
1312                if hash_or_height.hash().is_some() {
1313                    server::error::LegacyCode::InvalidAddressOrKey
1314                } else {
1315                    server::error::LegacyCode::InvalidParameter
1316                },
1317            )?
1318        else {
1319            panic!("unexpected response to BlockHeader request")
1320        };
1321
1322        let response = if !verbose {
1323            GetBlockHeader::Raw(HexData(header.zcash_serialize_to_vec().map_misc_error()?))
1324        } else {
1325            let zebra_state::ReadResponse::SaplingTree(sapling_tree) = state
1326                .clone()
1327                .oneshot(zebra_state::ReadRequest::SaplingTree(hash_or_height))
1328                .await
1329                .map_misc_error()?
1330            else {
1331                panic!("unexpected response to SaplingTree request")
1332            };
1333
1334            // This could be `None` if there's a chain reorg between state queries.
1335            let sapling_tree = sapling_tree.ok_or_misc_error("missing Sapling tree")?;
1336
1337            let zebra_state::ReadResponse::Depth(depth) = state
1338                .clone()
1339                .oneshot(zebra_state::ReadRequest::Depth(hash))
1340                .await
1341                .map_misc_error()?
1342            else {
1343                panic!("unexpected response to SaplingTree request")
1344            };
1345
1346            // From <https://zcash.github.io/rpc/getblock.html>
1347            // TODO: Deduplicate const definition, consider refactoring this to avoid duplicate logic
1348            const NOT_IN_BEST_CHAIN_CONFIRMATIONS: i64 = -1;
1349
1350            // Confirmations are one more than the depth.
1351            // Depth is limited by height, so it will never overflow an i64.
1352            let confirmations = depth
1353                .map(|depth| i64::from(depth) + 1)
1354                .unwrap_or(NOT_IN_BEST_CHAIN_CONFIRMATIONS);
1355
1356            let mut nonce = *header.nonce;
1357            nonce.reverse();
1358
1359            let sapling_activation = NetworkUpgrade::Sapling.activation_height(&network);
1360            let sapling_tree_size = sapling_tree.count();
1361            let final_sapling_root: [u8; 32] =
1362                if sapling_activation.is_some() && height >= sapling_activation.unwrap() {
1363                    let mut root: [u8; 32] = sapling_tree.root().into();
1364                    root.reverse();
1365                    root
1366                } else {
1367                    [0; 32]
1368                };
1369
1370            let difficulty = header.difficulty_threshold.relative_to_network(&network);
1371
1372            let block_commitments = match header.commitment(&network, height).expect(
1373                "Unexpected failure while parsing the blockcommitments field in get_block_header",
1374            ) {
1375                Commitment::PreSaplingReserved(bytes) => bytes,
1376                Commitment::FinalSaplingRoot(_) => final_sapling_root,
1377                Commitment::ChainHistoryActivationReserved => [0; 32],
1378                Commitment::ChainHistoryRoot(root) => root.bytes_in_display_order(),
1379                Commitment::ChainHistoryBlockTxAuthCommitment(hash) => {
1380                    hash.bytes_in_display_order()
1381                }
1382            };
1383
1384            let block_header = GetBlockHeaderObject {
1385                hash: GetBlockHash(hash),
1386                confirmations,
1387                height,
1388                version: header.version,
1389                merkle_root: header.merkle_root,
1390                block_commitments,
1391                final_sapling_root,
1392                sapling_tree_size,
1393                time: header.time.timestamp(),
1394                nonce,
1395                solution: header.solution,
1396                bits: header.difficulty_threshold,
1397                difficulty,
1398                previous_block_hash: GetBlockHash(header.previous_block_hash),
1399                next_block_hash: next_block_hash.map(GetBlockHash),
1400            };
1401
1402            GetBlockHeader::Object(Box::new(block_header))
1403        };
1404
1405        Ok(response)
1406    }
1407
1408    fn get_best_block_hash(&self) -> Result<GetBlockHash> {
1409        self.latest_chain_tip
1410            .best_tip_hash()
1411            .map(GetBlockHash)
1412            .ok_or_misc_error("No blocks in state")
1413    }
1414
1415    fn get_best_block_height_and_hash(&self) -> Result<GetBlockHeightAndHash> {
1416        self.latest_chain_tip
1417            .best_tip_height_and_hash()
1418            .map(|(height, hash)| GetBlockHeightAndHash { height, hash })
1419            .ok_or_misc_error("No blocks in state")
1420    }
1421
1422    async fn get_raw_mempool(&self, verbose: Option<bool>) -> Result<GetRawMempool> {
1423        #[allow(unused)]
1424        let verbose = verbose.unwrap_or(false);
1425
1426        use zebra_chain::block::MAX_BLOCK_BYTES;
1427
1428        // Determines whether the output of this RPC is sorted like zcashd
1429        let should_use_zcashd_order = self.debug_like_zcashd;
1430
1431        let mut mempool = self.mempool.clone();
1432
1433        let request = if should_use_zcashd_order || verbose {
1434            mempool::Request::FullTransactions
1435        } else {
1436            mempool::Request::TransactionIds
1437        };
1438
1439        // `zcashd` doesn't check if it is synced to the tip here, so we don't either.
1440        let response = mempool
1441            .ready()
1442            .and_then(|service| service.call(request))
1443            .await
1444            .map_misc_error()?;
1445
1446        match response {
1447            mempool::Response::FullTransactions {
1448                mut transactions,
1449                transaction_dependencies,
1450                last_seen_tip_hash: _,
1451            } => {
1452                if verbose {
1453                    let map = transactions
1454                        .iter()
1455                        .map(|unmined_tx| {
1456                            (
1457                                unmined_tx.transaction.id.mined_id().encode_hex(),
1458                                get_raw_mempool::MempoolObject::from_verified_unmined_tx(
1459                                    unmined_tx,
1460                                    &transactions,
1461                                    &transaction_dependencies,
1462                                ),
1463                            )
1464                        })
1465                        .collect::<HashMap<_, _>>();
1466                    Ok(GetRawMempool::Verbose(map))
1467                } else {
1468                    // Sort transactions in descending order by fee/size, using
1469                    // hash in serialized byte order as a tie-breaker. Note that
1470                    // this is only done in not verbose because in verbose mode
1471                    // a dictionary is returned, where order does not matter.
1472                    transactions.sort_by_cached_key(|tx| {
1473                        // zcashd uses modified fee here but Zebra doesn't currently
1474                        // support prioritizing transactions
1475                        cmp::Reverse((
1476                            i64::from(tx.miner_fee) as u128 * MAX_BLOCK_BYTES as u128
1477                                / tx.transaction.size as u128,
1478                            // transaction hashes are compared in their serialized byte-order.
1479                            tx.transaction.id.mined_id(),
1480                        ))
1481                    });
1482                    let tx_ids: Vec<String> = transactions
1483                        .iter()
1484                        .map(|unmined_tx| unmined_tx.transaction.id.mined_id().encode_hex())
1485                        .collect();
1486
1487                    Ok(GetRawMempool::TxIds(tx_ids))
1488                }
1489            }
1490
1491            mempool::Response::TransactionIds(unmined_transaction_ids) => {
1492                let mut tx_ids: Vec<String> = unmined_transaction_ids
1493                    .iter()
1494                    .map(|id| id.mined_id().encode_hex())
1495                    .collect();
1496
1497                // Sort returned transaction IDs in numeric/string order.
1498                tx_ids.sort();
1499
1500                Ok(GetRawMempool::TxIds(tx_ids))
1501            }
1502
1503            _ => unreachable!("unmatched response to a transactionids request"),
1504        }
1505    }
1506
1507    async fn get_raw_transaction(
1508        &self,
1509        txid: String,
1510        verbose: Option<u8>,
1511    ) -> Result<GetRawTransaction> {
1512        let mut state = self.state.clone();
1513        let mut mempool = self.mempool.clone();
1514        let verbose = verbose.unwrap_or(0) != 0;
1515
1516        // Reference for the legacy error code:
1517        // <https://github.com/zcash/zcash/blob/99ad6fdc3a549ab510422820eea5e5ce9f60a5fd/src/rpc/rawtransaction.cpp#L544>
1518        let txid = transaction::Hash::from_hex(txid)
1519            .map_error(server::error::LegacyCode::InvalidAddressOrKey)?;
1520
1521        // Check the mempool first.
1522        match mempool
1523            .ready()
1524            .and_then(|service| {
1525                service.call(mempool::Request::TransactionsByMinedId([txid].into()))
1526            })
1527            .await
1528            .map_misc_error()?
1529        {
1530            mempool::Response::Transactions(txns) => {
1531                if let Some(tx) = txns.first() {
1532                    return Ok(if verbose {
1533                        GetRawTransaction::Object(Box::new(TransactionObject::from_transaction(
1534                            tx.transaction.clone(),
1535                            None,
1536                            None,
1537                            &self.network,
1538                            None,
1539                        )))
1540                    } else {
1541                        let hex = tx.transaction.clone().into();
1542                        GetRawTransaction::Raw(hex)
1543                    });
1544                }
1545            }
1546
1547            _ => unreachable!("unmatched response to a `TransactionsByMinedId` request"),
1548        };
1549
1550        // If the tx wasn't in the mempool, check the state.
1551        match state
1552            .ready()
1553            .and_then(|service| service.call(zebra_state::ReadRequest::Transaction(txid)))
1554            .await
1555            .map_misc_error()?
1556        {
1557            zebra_state::ReadResponse::Transaction(Some(tx)) => Ok(if verbose {
1558                GetRawTransaction::Object(Box::new(TransactionObject::from_transaction(
1559                    tx.tx.clone(),
1560                    Some(tx.height),
1561                    Some(tx.confirmations),
1562                    &self.network,
1563                    // TODO: Performance gain:
1564                    // https://github.com/ZcashFoundation/zebra/pull/9458#discussion_r2059352752
1565                    Some(tx.block_time),
1566                )))
1567            } else {
1568                let hex = tx.tx.into();
1569                GetRawTransaction::Raw(hex)
1570            }),
1571
1572            zebra_state::ReadResponse::Transaction(None) => {
1573                Err("No such mempool or main chain transaction")
1574                    .map_error(server::error::LegacyCode::InvalidAddressOrKey)
1575            }
1576
1577            _ => unreachable!("unmatched response to a `Transaction` read request"),
1578        }
1579    }
1580
1581    // TODO:
1582    // - use `height_from_signed_int()` to handle negative heights
1583    //   (this might be better in the state request, because it needs the state height)
1584    async fn z_get_treestate(&self, hash_or_height: String) -> Result<GetTreestate> {
1585        let mut state = self.state.clone();
1586        let network = self.network.clone();
1587
1588        let hash_or_height =
1589            HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1590                // Reference for the legacy error code:
1591                // <https://github.com/zcash/zcash/blob/99ad6fdc3a549ab510422820eea5e5ce9f60a5fd/src/rpc/blockchain.cpp#L629>
1592                .map_error(server::error::LegacyCode::InvalidParameter)?;
1593
1594        // Fetch the block referenced by [`hash_or_height`] from the state.
1595        //
1596        // # Concurrency
1597        //
1598        // For consistency, this lookup must be performed first, then all the other lookups must
1599        // be based on the hash.
1600        //
1601        // TODO: If this RPC is called a lot, just get the block header, rather than the whole block.
1602        let block = match state
1603            .ready()
1604            .and_then(|service| service.call(zebra_state::ReadRequest::Block(hash_or_height)))
1605            .await
1606            .map_misc_error()?
1607        {
1608            zebra_state::ReadResponse::Block(Some(block)) => block,
1609            zebra_state::ReadResponse::Block(None) => {
1610                // Reference for the legacy error code:
1611                // <https://github.com/zcash/zcash/blob/99ad6fdc3a549ab510422820eea5e5ce9f60a5fd/src/rpc/blockchain.cpp#L629>
1612                return Err("the requested block is not in the main chain")
1613                    .map_error(server::error::LegacyCode::InvalidParameter);
1614            }
1615            _ => unreachable!("unmatched response to a block request"),
1616        };
1617
1618        let hash = hash_or_height
1619            .hash_or_else(|_| Some(block.hash()))
1620            .expect("block hash");
1621
1622        let height = hash_or_height
1623            .height_or_else(|_| block.coinbase_height())
1624            .expect("verified blocks have a coinbase height");
1625
1626        let time = u32::try_from(block.header.time.timestamp())
1627            .expect("Timestamps of valid blocks always fit into u32.");
1628
1629        let sapling_nu = zcash_primitives::consensus::NetworkUpgrade::Sapling;
1630        let sapling = if network.is_nu_active(sapling_nu, height.into()) {
1631            match state
1632                .ready()
1633                .and_then(|service| {
1634                    service.call(zebra_state::ReadRequest::SaplingTree(hash.into()))
1635                })
1636                .await
1637                .map_misc_error()?
1638            {
1639                zebra_state::ReadResponse::SaplingTree(tree) => tree.map(|t| t.to_rpc_bytes()),
1640                _ => unreachable!("unmatched response to a Sapling tree request"),
1641            }
1642        } else {
1643            None
1644        };
1645
1646        let orchard_nu = zcash_primitives::consensus::NetworkUpgrade::Nu5;
1647        let orchard = if network.is_nu_active(orchard_nu, height.into()) {
1648            match state
1649                .ready()
1650                .and_then(|service| {
1651                    service.call(zebra_state::ReadRequest::OrchardTree(hash.into()))
1652                })
1653                .await
1654                .map_misc_error()?
1655            {
1656                zebra_state::ReadResponse::OrchardTree(tree) => tree.map(|t| t.to_rpc_bytes()),
1657                _ => unreachable!("unmatched response to an Orchard tree request"),
1658            }
1659        } else {
1660            None
1661        };
1662
1663        Ok(GetTreestate::from_parts(
1664            hash, height, time, sapling, orchard,
1665        ))
1666    }
1667
1668    async fn z_get_subtrees_by_index(
1669        &self,
1670        pool: String,
1671        start_index: NoteCommitmentSubtreeIndex,
1672        limit: Option<NoteCommitmentSubtreeIndex>,
1673    ) -> Result<GetSubtrees> {
1674        let mut state = self.state.clone();
1675
1676        const POOL_LIST: &[&str] = &["sapling", "orchard"];
1677
1678        if pool == "sapling" {
1679            let request = zebra_state::ReadRequest::SaplingSubtrees { start_index, limit };
1680            let response = state
1681                .ready()
1682                .and_then(|service| service.call(request))
1683                .await
1684                .map_misc_error()?;
1685
1686            let subtrees = match response {
1687                zebra_state::ReadResponse::SaplingSubtrees(subtrees) => subtrees,
1688                _ => unreachable!("unmatched response to a subtrees request"),
1689            };
1690
1691            let subtrees = subtrees
1692                .values()
1693                .map(|subtree| SubtreeRpcData {
1694                    root: subtree.root.encode_hex(),
1695                    end_height: subtree.end_height,
1696                })
1697                .collect();
1698
1699            Ok(GetSubtrees {
1700                pool,
1701                start_index,
1702                subtrees,
1703            })
1704        } else if pool == "orchard" {
1705            let request = zebra_state::ReadRequest::OrchardSubtrees { start_index, limit };
1706            let response = state
1707                .ready()
1708                .and_then(|service| service.call(request))
1709                .await
1710                .map_misc_error()?;
1711
1712            let subtrees = match response {
1713                zebra_state::ReadResponse::OrchardSubtrees(subtrees) => subtrees,
1714                _ => unreachable!("unmatched response to a subtrees request"),
1715            };
1716
1717            let subtrees = subtrees
1718                .values()
1719                .map(|subtree| SubtreeRpcData {
1720                    root: subtree.root.encode_hex(),
1721                    end_height: subtree.end_height,
1722                })
1723                .collect();
1724
1725            Ok(GetSubtrees {
1726                pool,
1727                start_index,
1728                subtrees,
1729            })
1730        } else {
1731            Err(ErrorObject::owned(
1732                server::error::LegacyCode::Misc.into(),
1733                format!("invalid pool name, must be one of: {:?}", POOL_LIST).as_str(),
1734                None::<()>,
1735            ))
1736        }
1737    }
1738
1739    async fn get_address_tx_ids(&self, request: GetAddressTxIdsRequest) -> Result<Vec<String>> {
1740        let mut state = self.state.clone();
1741        let latest_chain_tip = self.latest_chain_tip.clone();
1742
1743        let height_range = build_height_range(
1744            request.start,
1745            request.end,
1746            best_chain_tip_height(&latest_chain_tip)?,
1747        )?;
1748
1749        let valid_addresses = AddressStrings {
1750            addresses: request.addresses,
1751        }
1752        .valid_addresses()?;
1753
1754        let request = zebra_state::ReadRequest::TransactionIdsByAddresses {
1755            addresses: valid_addresses,
1756            height_range,
1757        };
1758        let response = state
1759            .ready()
1760            .and_then(|service| service.call(request))
1761            .await
1762            .map_misc_error()?;
1763
1764        let hashes = match response {
1765            zebra_state::ReadResponse::AddressesTransactionIds(hashes) => {
1766                let mut last_tx_location = TransactionLocation::from_usize(Height(0), 0);
1767
1768                hashes
1769                    .iter()
1770                    .map(|(tx_loc, tx_id)| {
1771                        // Check that the returned transactions are in chain order.
1772                        assert!(
1773                            *tx_loc > last_tx_location,
1774                            "Transactions were not in chain order:\n\
1775                                 {tx_loc:?} {tx_id:?} was after:\n\
1776                                 {last_tx_location:?}",
1777                        );
1778
1779                        last_tx_location = *tx_loc;
1780
1781                        tx_id.to_string()
1782                    })
1783                    .collect()
1784            }
1785            _ => unreachable!("unmatched response to a TransactionsByAddresses request"),
1786        };
1787
1788        Ok(hashes)
1789    }
1790
1791    async fn get_address_utxos(
1792        &self,
1793        address_strings: AddressStrings,
1794    ) -> Result<Vec<GetAddressUtxos>> {
1795        let mut state = self.state.clone();
1796        let mut response_utxos = vec![];
1797
1798        let valid_addresses = address_strings.valid_addresses()?;
1799
1800        // get utxos data for addresses
1801        let request = zebra_state::ReadRequest::UtxosByAddresses(valid_addresses);
1802        let response = state
1803            .ready()
1804            .and_then(|service| service.call(request))
1805            .await
1806            .map_misc_error()?;
1807        let utxos = match response {
1808            zebra_state::ReadResponse::AddressUtxos(utxos) => utxos,
1809            _ => unreachable!("unmatched response to a UtxosByAddresses request"),
1810        };
1811
1812        let mut last_output_location = OutputLocation::from_usize(Height(0), 0, 0);
1813
1814        for utxo_data in utxos.utxos() {
1815            let address = utxo_data.0;
1816            let txid = *utxo_data.1;
1817            let height = utxo_data.2.height();
1818            let output_index = utxo_data.2.output_index();
1819            let script = utxo_data.3.lock_script.clone();
1820            let satoshis = u64::from(utxo_data.3.value);
1821
1822            let output_location = *utxo_data.2;
1823            // Check that the returned UTXOs are in chain order.
1824            assert!(
1825                output_location > last_output_location,
1826                "UTXOs were not in chain order:\n\
1827                     {output_location:?} {address:?} {txid:?} was after:\n\
1828                     {last_output_location:?}",
1829            );
1830
1831            let entry = GetAddressUtxos {
1832                address,
1833                txid,
1834                output_index,
1835                script,
1836                satoshis,
1837                height,
1838            };
1839            response_utxos.push(entry);
1840
1841            last_output_location = output_location;
1842        }
1843
1844        Ok(response_utxos)
1845    }
1846
1847    fn stop(&self) -> Result<String> {
1848        #[cfg(not(target_os = "windows"))]
1849        if self.network.is_regtest() {
1850            match nix::sys::signal::raise(nix::sys::signal::SIGINT) {
1851                Ok(_) => Ok("Zebra server stopping".to_string()),
1852                Err(error) => Err(ErrorObject::owned(
1853                    ErrorCode::InternalError.code(),
1854                    format!("Failed to shut down: {}", error).as_str(),
1855                    None::<()>,
1856                )),
1857            }
1858        } else {
1859            Err(ErrorObject::borrowed(
1860                ErrorCode::MethodNotFound.code(),
1861                "stop is only available on regtest networks",
1862                None,
1863            ))
1864        }
1865        #[cfg(target_os = "windows")]
1866        Err(ErrorObject::borrowed(
1867            ErrorCode::MethodNotFound.code(),
1868            "stop is not available in windows targets",
1869            None,
1870        ))
1871    }
1872
1873    fn get_block_count(&self) -> Result<u32> {
1874        best_chain_tip_height(&self.latest_chain_tip).map(|height| height.0)
1875    }
1876
1877    async fn get_block_hash(&self, index: i32) -> Result<GetBlockHash> {
1878        let mut state = self.state.clone();
1879        let latest_chain_tip = self.latest_chain_tip.clone();
1880
1881        // TODO: look up this height as part of the state request?
1882        let tip_height = best_chain_tip_height(&latest_chain_tip)?;
1883
1884        let height = height_from_signed_int(index, tip_height)?;
1885
1886        let request = zebra_state::ReadRequest::BestChainBlockHash(height);
1887        let response = state
1888            .ready()
1889            .and_then(|service| service.call(request))
1890            .await
1891            .map_error(server::error::LegacyCode::default())?;
1892
1893        match response {
1894            zebra_state::ReadResponse::BlockHash(Some(hash)) => Ok(GetBlockHash(hash)),
1895            zebra_state::ReadResponse::BlockHash(None) => Err(ErrorObject::borrowed(
1896                server::error::LegacyCode::InvalidParameter.into(),
1897                "Block not found",
1898                None,
1899            )),
1900            _ => unreachable!("unmatched response to a block request"),
1901        }
1902    }
1903
1904    async fn get_block_template(
1905        &self,
1906        parameters: Option<get_block_template::JsonParameters>,
1907    ) -> Result<get_block_template::Response> {
1908        // Clone Configs
1909        let network = self.network.clone();
1910        let miner_address = self.gbt.miner_address();
1911        let debug_like_zcashd = self.debug_like_zcashd;
1912        let extra_coinbase_data = self.gbt.extra_coinbase_data();
1913
1914        // Clone Services
1915        let mempool = self.mempool.clone();
1916        let mut latest_chain_tip = self.latest_chain_tip.clone();
1917        let sync_status = self.gbt.sync_status();
1918        let state = self.state.clone();
1919
1920        if let Some(HexData(block_proposal_bytes)) = parameters
1921            .as_ref()
1922            .and_then(get_block_template::JsonParameters::block_proposal_data)
1923        {
1924            return get_block_template::validate_block_proposal(
1925                self.gbt.block_verifier_router(),
1926                block_proposal_bytes,
1927                network,
1928                latest_chain_tip,
1929                sync_status,
1930            )
1931            .await;
1932        }
1933
1934        // To implement long polling correctly, we split this RPC into multiple phases.
1935        get_block_template::check_parameters(&parameters)?;
1936
1937        let client_long_poll_id = parameters.as_ref().and_then(|params| params.long_poll_id);
1938
1939        // - One-off checks
1940
1941        // Check config and parameters.
1942        // These checks always have the same result during long polling.
1943        let miner_address = get_block_template::check_miner_address(miner_address)?;
1944
1945        // - Checks and fetches that can change during long polling
1946        //
1947        // Set up the loop.
1948        let mut max_time_reached = false;
1949
1950        // The loop returns the server long poll ID,
1951        // which should be different to the client long poll ID.
1952        let (
1953            server_long_poll_id,
1954            chain_tip_and_local_time,
1955            mempool_txs,
1956            mempool_tx_deps,
1957            submit_old,
1958        ) = loop {
1959            // Check if we are synced to the tip.
1960            // The result of this check can change during long polling.
1961            //
1962            // Optional TODO:
1963            // - add `async changed()` method to ChainSyncStatus (like `ChainTip`)
1964            get_block_template::check_synced_to_tip(
1965                &network,
1966                latest_chain_tip.clone(),
1967                sync_status.clone(),
1968            )?;
1969            // TODO: return an error if we have no peers, like `zcashd` does,
1970            //       and add a developer config that mines regardless of how many peers we have.
1971            // https://github.com/zcash/zcash/blob/6fdd9f1b81d3b228326c9826fa10696fc516444b/src/miner.cpp#L865-L880
1972
1973            // We're just about to fetch state data, then maybe wait for any changes.
1974            // Mark all the changes before the fetch as seen.
1975            // Changes are also ignored in any clones made after the mark.
1976            latest_chain_tip.mark_best_tip_seen();
1977
1978            // Fetch the state data and local time for the block template:
1979            // - if the tip block hash changes, we must return from long polling,
1980            // - if the local clock changes on testnet, we might return from long polling
1981            //
1982            // We always return after 90 minutes on mainnet, even if we have the same response,
1983            // because the max time has been reached.
1984            let chain_tip_and_local_time @ zebra_state::GetBlockTemplateChainInfo {
1985                tip_hash,
1986                tip_height,
1987                max_time,
1988                cur_time,
1989                ..
1990            } = get_block_template::fetch_state_tip_and_local_time(state.clone()).await?;
1991
1992            // Fetch the mempool data for the block template:
1993            // - if the mempool transactions change, we might return from long polling.
1994            //
1995            // If the chain fork has just changed, miners want to get the new block as fast
1996            // as possible, rather than wait for transactions to re-verify. This increases
1997            // miner profits (and any delays can cause chain forks). So we don't wait between
1998            // the chain tip changing and getting mempool transactions.
1999            //
2000            // Optional TODO:
2001            // - add a `MempoolChange` type with an `async changed()` method (like `ChainTip`)
2002            let Some((mempool_txs, mempool_tx_deps)) =
2003                get_block_template::fetch_mempool_transactions(mempool.clone(), tip_hash)
2004                    .await?
2005                    // If the mempool and state responses are out of sync:
2006                    // - if we are not long polling, omit mempool transactions from the template,
2007                    // - if we are long polling, continue to the next iteration of the loop to make fresh state and mempool requests.
2008                    .or_else(|| client_long_poll_id.is_none().then(Default::default))
2009            else {
2010                continue;
2011            };
2012
2013            // - Long poll ID calculation
2014            let server_long_poll_id = LongPollInput::new(
2015                tip_height,
2016                tip_hash,
2017                max_time,
2018                mempool_txs.iter().map(|tx| tx.transaction.id),
2019            )
2020            .generate_id();
2021
2022            // The loop finishes if:
2023            // - the client didn't pass a long poll ID,
2024            // - the server long poll ID is different to the client long poll ID, or
2025            // - the previous loop iteration waited until the max time.
2026            if Some(&server_long_poll_id) != client_long_poll_id.as_ref() || max_time_reached {
2027                let mut submit_old = client_long_poll_id
2028                    .as_ref()
2029                    .map(|old_long_poll_id| server_long_poll_id.submit_old(old_long_poll_id));
2030
2031                // On testnet, the max time changes the block difficulty, so old shares are
2032                // invalid. On mainnet, this means there has been 90 minutes without a new
2033                // block or mempool transaction, which is very unlikely. So the miner should
2034                // probably reset anyway.
2035                if max_time_reached {
2036                    submit_old = Some(false);
2037                }
2038
2039                break (
2040                    server_long_poll_id,
2041                    chain_tip_and_local_time,
2042                    mempool_txs,
2043                    mempool_tx_deps,
2044                    submit_old,
2045                );
2046            }
2047
2048            // - Polling wait conditions
2049            //
2050            // TODO: when we're happy with this code, split it into a function.
2051            //
2052            // Periodically check the mempool for changes.
2053            //
2054            // Optional TODO:
2055            // Remove this polling wait if we switch to using futures to detect sync status
2056            // and mempool changes.
2057            let wait_for_mempool_request =
2058                tokio::time::sleep(Duration::from_secs(MEMPOOL_LONG_POLL_INTERVAL));
2059
2060            // Return immediately if the chain tip has changed.
2061            // The clone preserves the seen status of the chain tip.
2062            let mut wait_for_best_tip_change = latest_chain_tip.clone();
2063            let wait_for_best_tip_change = wait_for_best_tip_change.best_tip_changed();
2064
2065            // Wait for the maximum block time to elapse. This can change the block header
2066            // on testnet. (On mainnet it can happen due to a network disconnection, or a
2067            // rapid drop in hash rate.)
2068            //
2069            // This duration might be slightly lower than the actual maximum,
2070            // if cur_time was clamped to min_time. In that case the wait is very long,
2071            // and it's ok to return early.
2072            //
2073            // It can also be zero if cur_time was clamped to max_time. In that case,
2074            // we want to wait for another change, and ignore this timeout. So we use an
2075            // `OptionFuture::None`.
2076            let duration_until_max_time = max_time.saturating_duration_since(cur_time);
2077            let wait_for_max_time: OptionFuture<_> = if duration_until_max_time.seconds() > 0 {
2078                Some(tokio::time::sleep(duration_until_max_time.to_std()))
2079            } else {
2080                None
2081            }
2082            .into();
2083
2084            // Optional TODO:
2085            // `zcashd` generates the next coinbase transaction while waiting for changes.
2086            // When Zebra supports shielded coinbase, we might want to do this in parallel.
2087            // But the coinbase value depends on the selected transactions, so this needs
2088            // further analysis to check if it actually saves us any time.
2089
2090            tokio::select! {
2091                // Poll the futures in the listed order, for efficiency.
2092                // We put the most frequent conditions first.
2093                biased;
2094
2095                // This timer elapses every few seconds
2096                _elapsed = wait_for_mempool_request => {
2097                    tracing::debug!(
2098                        ?max_time,
2099                        ?cur_time,
2100                        ?server_long_poll_id,
2101                        ?client_long_poll_id,
2102                        MEMPOOL_LONG_POLL_INTERVAL,
2103                        "checking for a new mempool change after waiting a few seconds"
2104                    );
2105                }
2106
2107                // The state changes after around a target block interval (75s)
2108                tip_changed_result = wait_for_best_tip_change => {
2109                    match tip_changed_result {
2110                        Ok(()) => {
2111                            // Spurious updates shouldn't happen in the state, because the
2112                            // difficulty and hash ordering is a stable total order. But
2113                            // since they could cause a busy-loop, guard against them here.
2114                            latest_chain_tip.mark_best_tip_seen();
2115
2116                            let new_tip_hash = latest_chain_tip.best_tip_hash();
2117                            if new_tip_hash == Some(tip_hash) {
2118                                tracing::debug!(
2119                                    ?max_time,
2120                                    ?cur_time,
2121                                    ?server_long_poll_id,
2122                                    ?client_long_poll_id,
2123                                    ?tip_hash,
2124                                    ?tip_height,
2125                                    "ignoring spurious state change notification"
2126                                );
2127
2128                                // Wait for the mempool interval, then check for any changes.
2129                                tokio::time::sleep(Duration::from_secs(
2130                                    MEMPOOL_LONG_POLL_INTERVAL,
2131                                )).await;
2132
2133                                continue;
2134                            }
2135
2136                            tracing::debug!(
2137                                ?max_time,
2138                                ?cur_time,
2139                                ?server_long_poll_id,
2140                                ?client_long_poll_id,
2141                                "returning from long poll because state has changed"
2142                            );
2143                        }
2144
2145                        Err(recv_error) => {
2146                            // This log is rare and helps with debugging, so it's ok to be info.
2147                            tracing::info!(
2148                                ?recv_error,
2149                                ?max_time,
2150                                ?cur_time,
2151                                ?server_long_poll_id,
2152                                ?client_long_poll_id,
2153                                "returning from long poll due to a state error.\
2154                                Is Zebra shutting down?"
2155                            );
2156
2157                            return Err(recv_error).map_error(server::error::LegacyCode::default());
2158                        }
2159                    }
2160                }
2161
2162                // The max time does not elapse during normal operation on mainnet,
2163                // and it rarely elapses on testnet.
2164                Some(_elapsed) = wait_for_max_time => {
2165                    // This log is very rare so it's ok to be info.
2166                    tracing::info!(
2167                        ?max_time,
2168                        ?cur_time,
2169                        ?server_long_poll_id,
2170                        ?client_long_poll_id,
2171                        "returning from long poll because max time was reached"
2172                    );
2173
2174                    max_time_reached = true;
2175                }
2176            }
2177        };
2178
2179        // - Processing fetched data to create a transaction template
2180        //
2181        // Apart from random weighted transaction selection,
2182        // the template only depends on the previously fetched data.
2183        // This processing never fails.
2184
2185        // Calculate the next block height.
2186        let next_block_height =
2187            (chain_tip_and_local_time.tip_height + 1).expect("tip is far below Height::MAX");
2188
2189        tracing::debug!(
2190            mempool_tx_hashes = ?mempool_txs
2191                .iter()
2192                .map(|tx| tx.transaction.id.mined_id())
2193                .collect::<Vec<_>>(),
2194            "selecting transactions for the template from the mempool"
2195        );
2196
2197        // Randomly select some mempool transactions.
2198        let mempool_txs = get_block_template::zip317::select_mempool_transactions(
2199            &network,
2200            next_block_height,
2201            &miner_address,
2202            mempool_txs,
2203            mempool_tx_deps,
2204            debug_like_zcashd,
2205            extra_coinbase_data.clone(),
2206        );
2207
2208        tracing::debug!(
2209            selected_mempool_tx_hashes = ?mempool_txs
2210                .iter()
2211                .map(|#[cfg(not(test))] tx, #[cfg(test)] (_, tx)| tx.transaction.id.mined_id())
2212                .collect::<Vec<_>>(),
2213            "selected transactions for the template from the mempool"
2214        );
2215
2216        // - After this point, the template only depends on the previously fetched data.
2217
2218        let response = GetBlockTemplate::new(
2219            &network,
2220            &miner_address,
2221            &chain_tip_and_local_time,
2222            server_long_poll_id,
2223            mempool_txs,
2224            submit_old,
2225            debug_like_zcashd,
2226            extra_coinbase_data,
2227        );
2228
2229        Ok(response.into())
2230    }
2231
2232    async fn submit_block(
2233        &self,
2234        HexData(block_bytes): HexData,
2235        _parameters: Option<submit_block::JsonParameters>,
2236    ) -> Result<submit_block::Response> {
2237        let mut block_verifier_router = self.gbt.block_verifier_router();
2238
2239        let block: Block = match block_bytes.zcash_deserialize_into() {
2240            Ok(block_bytes) => block_bytes,
2241            Err(error) => {
2242                tracing::info!(
2243                    ?error,
2244                    "submit block failed: block bytes could not be deserialized into a structurally valid block"
2245                );
2246
2247                return Ok(submit_block::ErrorResponse::Rejected.into());
2248            }
2249        };
2250
2251        let height = block
2252            .coinbase_height()
2253            .ok_or_error(0, "coinbase height not found")?;
2254        let block_hash = block.hash();
2255
2256        let block_verifier_router_response = block_verifier_router
2257            .ready()
2258            .await
2259            .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?
2260            .call(zebra_consensus::Request::Commit(Arc::new(block)))
2261            .await;
2262
2263        let chain_error = match block_verifier_router_response {
2264            // Currently, this match arm returns `null` (Accepted) for blocks committed
2265            // to any chain, but Accepted is only for blocks in the best chain.
2266            //
2267            // TODO (#5487):
2268            // - Inconclusive: check if the block is on a side-chain
2269            // The difference is important to miners, because they want to mine on the best chain.
2270            Ok(hash) => {
2271                tracing::info!(?hash, ?height, "submit block accepted");
2272
2273                self.gbt
2274                    .advertise_mined_block(hash, height)
2275                    .map_error_with_prefix(0, "failed to send mined block")?;
2276
2277                return Ok(submit_block::Response::Accepted);
2278            }
2279
2280            // Turns BoxError into Result<VerifyChainError, BoxError>,
2281            // by downcasting from Any to VerifyChainError.
2282            Err(box_error) => {
2283                let error = box_error
2284                    .downcast::<RouterError>()
2285                    .map(|boxed_chain_error| *boxed_chain_error);
2286
2287                tracing::info!(
2288                    ?error,
2289                    ?block_hash,
2290                    ?height,
2291                    "submit block failed verification"
2292                );
2293
2294                error
2295            }
2296        };
2297
2298        let response = match chain_error {
2299            Ok(source) if source.is_duplicate_request() => submit_block::ErrorResponse::Duplicate,
2300
2301            // Currently, these match arms return Reject for the older duplicate in a queue,
2302            // but queued duplicates should be DuplicateInconclusive.
2303            //
2304            // Optional TODO (#5487):
2305            // - DuplicateInconclusive: turn these non-finalized state duplicate block errors
2306            //   into BlockError enum variants, and handle them as DuplicateInconclusive:
2307            //   - "block already sent to be committed to the state"
2308            //   - "replaced by newer request"
2309            // - keep the older request in the queue,
2310            //   and return a duplicate error for the newer request immediately.
2311            //   This improves the speed of the RPC response.
2312            //
2313            // Checking the download queues and BlockVerifierRouter buffer for duplicates
2314            // might require architectural changes to Zebra, so we should only do it
2315            // if mining pools really need it.
2316            Ok(_verify_chain_error) => submit_block::ErrorResponse::Rejected,
2317
2318            // This match arm is currently unreachable, but if future changes add extra error types,
2319            // we want to turn them into `Rejected`.
2320            Err(_unknown_error_type) => submit_block::ErrorResponse::Rejected,
2321        };
2322
2323        Ok(response.into())
2324    }
2325
2326    async fn get_mining_info(&self) -> Result<get_mining_info::Response> {
2327        let network = self.network.clone();
2328        let mut state = self.state.clone();
2329
2330        let chain_tip = self.latest_chain_tip.clone();
2331        let tip_height = chain_tip.best_tip_height().unwrap_or(Height(0)).0;
2332
2333        let mut current_block_tx = None;
2334        if tip_height > 0 {
2335            let mined_tx_ids = chain_tip.best_tip_mined_transaction_ids();
2336            current_block_tx =
2337                (!mined_tx_ids.is_empty()).then(|| mined_tx_ids.len().saturating_sub(1));
2338        }
2339
2340        let solution_rate_fut = self.get_network_sol_ps(None, None);
2341        // Get the current block size.
2342        let mut current_block_size = None;
2343        if tip_height > 0 {
2344            let request = zebra_state::ReadRequest::TipBlockSize;
2345            let response: zebra_state::ReadResponse = state
2346                .ready()
2347                .and_then(|service| service.call(request))
2348                .await
2349                .map_error(server::error::LegacyCode::default())?;
2350            current_block_size = match response {
2351                zebra_state::ReadResponse::TipBlockSize(Some(block_size)) => Some(block_size),
2352                _ => None,
2353            };
2354        }
2355
2356        Ok(get_mining_info::Response::new(
2357            tip_height,
2358            current_block_size,
2359            current_block_tx,
2360            network,
2361            solution_rate_fut.await?,
2362        ))
2363    }
2364
2365    async fn get_network_sol_ps(
2366        &self,
2367        num_blocks: Option<i32>,
2368        height: Option<i32>,
2369    ) -> Result<u64> {
2370        // Default number of blocks is 120 if not supplied.
2371        let mut num_blocks =
2372            num_blocks.unwrap_or(get_block_template::DEFAULT_SOLUTION_RATE_WINDOW_SIZE);
2373        // But if it is 0 or negative, it uses the proof of work averaging window.
2374        if num_blocks < 1 {
2375            num_blocks = i32::try_from(POW_AVERAGING_WINDOW).expect("fits in i32");
2376        }
2377        let num_blocks =
2378            usize::try_from(num_blocks).expect("just checked for negatives, i32 fits in usize");
2379
2380        // Default height is the tip height if not supplied. Negative values also mean the tip
2381        // height. Since negative values aren't valid heights, we can just use the conversion.
2382        let height = height.and_then(|height| height.try_into_height().ok());
2383
2384        let mut state = self.state.clone();
2385
2386        let request = ReadRequest::SolutionRate { num_blocks, height };
2387
2388        let response = state
2389            .ready()
2390            .and_then(|service| service.call(request))
2391            .await
2392            .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
2393
2394        let solution_rate = match response {
2395            // zcashd returns a 0 rate when the calculation is invalid
2396            ReadResponse::SolutionRate(solution_rate) => solution_rate.unwrap_or(0),
2397
2398            _ => unreachable!("unmatched response to a solution rate request"),
2399        };
2400
2401        Ok(solution_rate
2402            .try_into()
2403            .expect("per-second solution rate always fits in u64"))
2404    }
2405
2406    async fn get_peer_info(&self) -> Result<Vec<PeerInfo>> {
2407        let address_book = self.address_book.clone();
2408        Ok(address_book
2409            .recently_live_peers(chrono::Utc::now())
2410            .into_iter()
2411            .map(PeerInfo::from)
2412            .collect())
2413    }
2414
2415    async fn validate_address(&self, raw_address: String) -> Result<validate_address::Response> {
2416        let network = self.network.clone();
2417
2418        let Ok(address) = raw_address.parse::<zcash_address::ZcashAddress>() else {
2419            return Ok(validate_address::Response::invalid());
2420        };
2421
2422        let address = match address.convert::<primitives::Address>() {
2423            Ok(address) => address,
2424            Err(err) => {
2425                tracing::debug!(?err, "conversion error");
2426                return Ok(validate_address::Response::invalid());
2427            }
2428        };
2429
2430        // we want to match zcashd's behaviour
2431        if !address.is_transparent() {
2432            return Ok(validate_address::Response::invalid());
2433        }
2434
2435        if address.network() == network.kind() {
2436            Ok(validate_address::Response {
2437                address: Some(raw_address),
2438                is_valid: true,
2439                is_script: Some(address.is_script_hash()),
2440            })
2441        } else {
2442            tracing::info!(
2443                ?network,
2444                address_network = ?address.network(),
2445                "invalid address in validateaddress RPC: Zebra's configured network must match address network"
2446            );
2447
2448            Ok(validate_address::Response::invalid())
2449        }
2450    }
2451
2452    async fn z_validate_address(
2453        &self,
2454        raw_address: String,
2455    ) -> Result<z_validate_address::Response> {
2456        let network = self.network.clone();
2457
2458        let Ok(address) = raw_address.parse::<zcash_address::ZcashAddress>() else {
2459            return Ok(z_validate_address::Response::invalid());
2460        };
2461
2462        let address = match address.convert::<primitives::Address>() {
2463            Ok(address) => address,
2464            Err(err) => {
2465                tracing::debug!(?err, "conversion error");
2466                return Ok(z_validate_address::Response::invalid());
2467            }
2468        };
2469
2470        if address.network() == network.kind() {
2471            Ok(z_validate_address::Response {
2472                is_valid: true,
2473                address: Some(raw_address),
2474                address_type: Some(z_validate_address::AddressType::from(&address)),
2475                is_mine: Some(false),
2476            })
2477        } else {
2478            tracing::info!(
2479                ?network,
2480                address_network = ?address.network(),
2481                "invalid address network in z_validateaddress RPC: address is for {:?} but Zebra is on {:?}",
2482                address.network(),
2483                network
2484            );
2485
2486            Ok(z_validate_address::Response::invalid())
2487        }
2488    }
2489
2490    async fn get_block_subsidy(&self, height: Option<u32>) -> Result<BlockSubsidy> {
2491        let latest_chain_tip = self.latest_chain_tip.clone();
2492        let network = self.network.clone();
2493
2494        let height = if let Some(height) = height {
2495            Height(height)
2496        } else {
2497            best_chain_tip_height(&latest_chain_tip)?
2498        };
2499
2500        if height < network.height_for_first_halving() {
2501            return Err(ErrorObject::borrowed(
2502                0,
2503                "Zebra does not support founders' reward subsidies, \
2504                        use a block height that is after the first halving",
2505                None,
2506            ));
2507        }
2508
2509        // Always zero for post-halving blocks
2510        let founders = Amount::zero();
2511
2512        let total_block_subsidy =
2513            block_subsidy(height, &network).map_error(server::error::LegacyCode::default())?;
2514        let miner_subsidy = miner_subsidy(height, &network, total_block_subsidy)
2515            .map_error(server::error::LegacyCode::default())?;
2516
2517        let (lockbox_streams, mut funding_streams): (Vec<_>, Vec<_>) =
2518            funding_stream_values(height, &network, total_block_subsidy)
2519                .map_error(server::error::LegacyCode::default())?
2520                .into_iter()
2521                // Separate the funding streams into deferred and non-deferred streams
2522                .partition(|(receiver, _)| matches!(receiver, FundingStreamReceiver::Deferred));
2523
2524        let is_nu6 = NetworkUpgrade::current(&network, height) == NetworkUpgrade::Nu6;
2525
2526        let [lockbox_total, funding_streams_total]: [std::result::Result<
2527            Amount<NonNegative>,
2528            amount::Error,
2529        >; 2] = [&lockbox_streams, &funding_streams]
2530            .map(|streams| streams.iter().map(|&(_, amount)| amount).sum());
2531
2532        // Use the same funding stream order as zcashd
2533        funding_streams.sort_by_key(|(receiver, _funding_stream)| {
2534            ZCASHD_FUNDING_STREAM_ORDER
2535                .iter()
2536                .position(|zcashd_receiver| zcashd_receiver == receiver)
2537        });
2538
2539        // Format the funding streams and lockbox streams
2540        let [funding_streams, lockbox_streams]: [Vec<_>; 2] = [funding_streams, lockbox_streams]
2541            .map(|streams| {
2542                streams
2543                    .into_iter()
2544                    .map(|(receiver, value)| {
2545                        let address = funding_stream_address(height, &network, receiver);
2546                        types::subsidy::FundingStream::new(is_nu6, receiver, value, address)
2547                    })
2548                    .collect()
2549            });
2550
2551        Ok(BlockSubsidy {
2552            miner: miner_subsidy.into(),
2553            founders: founders.into(),
2554            funding_streams,
2555            lockbox_streams,
2556            funding_streams_total: funding_streams_total
2557                .map_error(server::error::LegacyCode::default())?
2558                .into(),
2559            lockbox_total: lockbox_total
2560                .map_error(server::error::LegacyCode::default())?
2561                .into(),
2562            total_block_subsidy: total_block_subsidy.into(),
2563        })
2564    }
2565
2566    async fn get_difficulty(&self) -> Result<f64> {
2567        chain_tip_difficulty(self.network.clone(), self.state.clone(), false).await
2568    }
2569
2570    async fn z_list_unified_receivers(&self, address: String) -> Result<unified_address::Response> {
2571        use zcash_address::unified::Container;
2572
2573        let (network, unified_address): (
2574            zcash_protocol::consensus::NetworkType,
2575            zcash_address::unified::Address,
2576        ) = zcash_address::unified::Encoding::decode(address.clone().as_str())
2577            .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
2578
2579        let mut p2pkh = String::new();
2580        let mut p2sh = String::new();
2581        let mut orchard = String::new();
2582        let mut sapling = String::new();
2583
2584        for item in unified_address.items() {
2585            match item {
2586                zcash_address::unified::Receiver::Orchard(_data) => {
2587                    let addr = zcash_address::unified::Address::try_from_items(vec![item])
2588                        .expect("using data already decoded as valid");
2589                    orchard = addr.encode(&network);
2590                }
2591                zcash_address::unified::Receiver::Sapling(data) => {
2592                    let addr = zebra_chain::primitives::Address::try_from_sapling(network, data)
2593                        .expect("using data already decoded as valid");
2594                    sapling = addr.payment_address().unwrap_or_default();
2595                }
2596                zcash_address::unified::Receiver::P2pkh(data) => {
2597                    let addr =
2598                        zebra_chain::primitives::Address::try_from_transparent_p2pkh(network, data)
2599                            .expect("using data already decoded as valid");
2600                    p2pkh = addr.payment_address().unwrap_or_default();
2601                }
2602                zcash_address::unified::Receiver::P2sh(data) => {
2603                    let addr =
2604                        zebra_chain::primitives::Address::try_from_transparent_p2sh(network, data)
2605                            .expect("using data already decoded as valid");
2606                    p2sh = addr.payment_address().unwrap_or_default();
2607                }
2608                _ => (),
2609            }
2610        }
2611
2612        Ok(unified_address::Response::new(
2613            orchard, sapling, p2pkh, p2sh,
2614        ))
2615    }
2616
2617    async fn generate(&self, num_blocks: u32) -> Result<Vec<GetBlockHash>> {
2618        let rpc = self.clone();
2619        let network = self.network.clone();
2620
2621        if !network.disable_pow() {
2622            return Err(ErrorObject::borrowed(
2623                0,
2624                "generate is only supported on networks where PoW is disabled",
2625                None,
2626            ));
2627        }
2628
2629        let mut block_hashes = Vec::new();
2630        for _ in 0..num_blocks {
2631            let block_template = rpc
2632                .get_block_template(None)
2633                .await
2634                .map_error(server::error::LegacyCode::default())?;
2635
2636            let get_block_template::Response::TemplateMode(block_template) = block_template else {
2637                return Err(ErrorObject::borrowed(
2638                    0,
2639                    "error generating block template",
2640                    None,
2641                ));
2642            };
2643
2644            let proposal_block = proposal_block_from_template(
2645                &block_template,
2646                get_block_template::TimeSource::CurTime,
2647                NetworkUpgrade::current(&network, Height(block_template.height)),
2648            )
2649            .map_error(server::error::LegacyCode::default())?;
2650            let hex_proposal_block = HexData(
2651                proposal_block
2652                    .zcash_serialize_to_vec()
2653                    .map_error(server::error::LegacyCode::default())?,
2654            );
2655
2656            let _submit = rpc
2657                .submit_block(hex_proposal_block, None)
2658                .await
2659                .map_error(server::error::LegacyCode::default())?;
2660
2661            block_hashes.push(GetBlockHash(proposal_block.hash()));
2662        }
2663
2664        Ok(block_hashes)
2665    }
2666}
2667
2668// TODO: Move the code below to separate modules.
2669
2670/// Returns the best chain tip height of `latest_chain_tip`,
2671/// or an RPC error if there are no blocks in the state.
2672pub fn best_chain_tip_height<Tip>(latest_chain_tip: &Tip) -> Result<Height>
2673where
2674    Tip: ChainTip + Clone + Send + Sync + 'static,
2675{
2676    latest_chain_tip
2677        .best_tip_height()
2678        .ok_or_misc_error("No blocks in state")
2679}
2680
2681/// Response to a `getinfo` RPC request.
2682///
2683/// See the notes for the [`Rpc::get_info` method].
2684#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
2685pub struct GetInfo {
2686    /// The node version
2687    version: u64,
2688
2689    /// The node version build number
2690    build: String,
2691
2692    /// The server sub-version identifier, used as the network protocol user-agent
2693    subversion: String,
2694
2695    /// The protocol version
2696    #[serde(rename = "protocolversion")]
2697    protocol_version: u32,
2698
2699    /// The current number of blocks processed in the server
2700    blocks: u32,
2701
2702    /// The total (inbound and outbound) number of connections the node has
2703    connections: usize,
2704
2705    /// The proxy (if any) used by the server. Currently always `None` in Zebra.
2706    #[serde(skip_serializing_if = "Option::is_none")]
2707    proxy: Option<String>,
2708
2709    /// The current network difficulty
2710    difficulty: f64,
2711
2712    /// True if the server is running in testnet mode, false otherwise
2713    testnet: bool,
2714
2715    /// The minimum transaction fee in ZEC/kB
2716    #[serde(rename = "paytxfee")]
2717    pay_tx_fee: f64,
2718
2719    /// The minimum relay fee for non-free transactions in ZEC/kB
2720    #[serde(rename = "relayfee")]
2721    relay_fee: f64,
2722
2723    /// The last error or warning message, or "no errors" if there are no errors
2724    errors: String,
2725
2726    /// The time of the last error or warning message, or "no errors timestamp" if there are no errors
2727    #[serde(rename = "errorstimestamp")]
2728    errors_timestamp: String,
2729}
2730
2731impl Default for GetInfo {
2732    fn default() -> Self {
2733        GetInfo {
2734            version: 0,
2735            build: "some build version".to_string(),
2736            subversion: "some subversion".to_string(),
2737            protocol_version: 0,
2738            blocks: 0,
2739            connections: 0,
2740            proxy: None,
2741            difficulty: 0.0,
2742            testnet: false,
2743            pay_tx_fee: 0.0,
2744            relay_fee: 0.0,
2745            errors: "no errors".to_string(),
2746            errors_timestamp: "no errors timestamp".to_string(),
2747        }
2748    }
2749}
2750
2751impl GetInfo {
2752    /// Constructs [`GetInfo`] from its constituent parts.
2753    #[allow(clippy::too_many_arguments)]
2754    pub fn from_parts(
2755        version: u64,
2756        build: String,
2757        subversion: String,
2758        protocol_version: u32,
2759        blocks: u32,
2760        connections: usize,
2761        proxy: Option<String>,
2762        difficulty: f64,
2763        testnet: bool,
2764        pay_tx_fee: f64,
2765        relay_fee: f64,
2766        errors: String,
2767        errors_timestamp: String,
2768    ) -> Self {
2769        Self {
2770            version,
2771            build,
2772            subversion,
2773            protocol_version,
2774            blocks,
2775            connections,
2776            proxy,
2777            difficulty,
2778            testnet,
2779            pay_tx_fee,
2780            relay_fee,
2781            errors,
2782            errors_timestamp,
2783        }
2784    }
2785
2786    /// Returns the contents of ['GetInfo'].
2787    pub fn into_parts(
2788        self,
2789    ) -> (
2790        u64,
2791        String,
2792        String,
2793        u32,
2794        u32,
2795        usize,
2796        Option<String>,
2797        f64,
2798        bool,
2799        f64,
2800        f64,
2801        String,
2802        String,
2803    ) {
2804        (
2805            self.version,
2806            self.build,
2807            self.subversion,
2808            self.protocol_version,
2809            self.blocks,
2810            self.connections,
2811            self.proxy,
2812            self.difficulty,
2813            self.testnet,
2814            self.pay_tx_fee,
2815            self.relay_fee,
2816            self.errors,
2817            self.errors_timestamp,
2818        )
2819    }
2820
2821    /// Create the node version number.
2822    pub fn version(build_string: &str) -> Option<u64> {
2823        let semver_version = semver::Version::parse(build_string.strip_prefix('v')?).ok()?;
2824        let build_number = semver_version
2825            .build
2826            .as_str()
2827            .split('.')
2828            .next()
2829            .and_then(|num_str| num_str.parse::<u64>().ok())
2830            .unwrap_or_default();
2831
2832        // https://github.com/zcash/zcash/blob/v6.1.0/src/clientversion.h#L55-L59
2833        let version_number = 1_000_000 * semver_version.major
2834            + 10_000 * semver_version.minor
2835            + 100 * semver_version.patch
2836            + build_number;
2837
2838        Some(version_number)
2839    }
2840}
2841
2842/// Response to a `getblockchaininfo` RPC request.
2843///
2844/// See the notes for the [`Rpc::get_blockchain_info` method].
2845#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
2846pub struct GetBlockChainInfo {
2847    /// Current network name as defined in BIP70 (main, test, regtest)
2848    chain: String,
2849
2850    /// The current number of blocks processed in the server, numeric
2851    blocks: Height,
2852
2853    /// The current number of headers we have validated in the best chain, that is,
2854    /// the height of the best chain.
2855    headers: Height,
2856
2857    /// The estimated network solution rate in Sol/s.
2858    difficulty: f64,
2859
2860    /// The verification progress relative to the estimated network chain tip.
2861    #[serde(rename = "verificationprogress")]
2862    verification_progress: f64,
2863
2864    /// The total amount of work in the best chain, hex-encoded.
2865    #[serde(rename = "chainwork")]
2866    chain_work: u64,
2867
2868    /// Whether this node is pruned, currently always false in Zebra.
2869    pruned: bool,
2870
2871    /// The estimated size of the block and undo files on disk
2872    size_on_disk: u64,
2873
2874    /// The current number of note commitments in the commitment tree
2875    commitments: u64,
2876
2877    /// The hash of the currently best block, in big-endian order, hex-encoded
2878    #[serde(rename = "bestblockhash", with = "hex")]
2879    best_block_hash: block::Hash,
2880
2881    /// If syncing, the estimated height of the chain, else the current best height, numeric.
2882    ///
2883    /// In Zebra, this is always the height estimate, so it might be a little inaccurate.
2884    #[serde(rename = "estimatedheight")]
2885    estimated_height: Height,
2886
2887    /// Chain supply balance
2888    #[serde(rename = "chainSupply")]
2889    chain_supply: get_blockchain_info::Balance,
2890
2891    /// Value pool balances
2892    #[serde(rename = "valuePools")]
2893    value_pools: [get_blockchain_info::Balance; 5],
2894
2895    /// Status of network upgrades
2896    upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
2897
2898    /// Branch IDs of the current and upcoming consensus rules
2899    consensus: TipConsensusBranch,
2900}
2901
2902impl Default for GetBlockChainInfo {
2903    fn default() -> Self {
2904        GetBlockChainInfo {
2905            chain: "main".to_string(),
2906            blocks: Height(1),
2907            best_block_hash: block::Hash([0; 32]),
2908            estimated_height: Height(1),
2909            chain_supply: get_blockchain_info::Balance::chain_supply(Default::default()),
2910            value_pools: get_blockchain_info::Balance::zero_pools(),
2911            upgrades: IndexMap::new(),
2912            consensus: TipConsensusBranch {
2913                chain_tip: ConsensusBranchIdHex(ConsensusBranchId::default()),
2914                next_block: ConsensusBranchIdHex(ConsensusBranchId::default()),
2915            },
2916            headers: Height(1),
2917            difficulty: 0.0,
2918            verification_progress: 0.0,
2919            chain_work: 0,
2920            pruned: false,
2921            size_on_disk: 0,
2922            commitments: 0,
2923        }
2924    }
2925}
2926
2927impl GetBlockChainInfo {
2928    /// Creates a new [`GetBlockChainInfo`] instance.
2929    #[allow(clippy::too_many_arguments)]
2930    pub fn new(
2931        chain: String,
2932        blocks: Height,
2933        best_block_hash: block::Hash,
2934        estimated_height: Height,
2935        chain_supply: get_blockchain_info::Balance,
2936        value_pools: [get_blockchain_info::Balance; 5],
2937        upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
2938        consensus: TipConsensusBranch,
2939        headers: Height,
2940        difficulty: f64,
2941        verification_progress: f64,
2942        chain_work: u64,
2943        pruned: bool,
2944        size_on_disk: u64,
2945        commitments: u64,
2946    ) -> Self {
2947        Self {
2948            chain,
2949            blocks,
2950            best_block_hash,
2951            estimated_height,
2952            chain_supply,
2953            value_pools,
2954            upgrades,
2955            consensus,
2956            headers,
2957            difficulty,
2958            verification_progress,
2959            chain_work,
2960            pruned,
2961            size_on_disk,
2962            commitments,
2963        }
2964    }
2965
2966    /// Returns the current network name as defined in BIP70 (main, test, regtest).
2967    pub fn chain(&self) -> String {
2968        self.chain.clone()
2969    }
2970
2971    /// Returns the current number of blocks processed in the server.
2972    pub fn blocks(&self) -> Height {
2973        self.blocks
2974    }
2975
2976    /// Returns the hash of the current best chain tip block, in big-endian order, hex-encoded.
2977    pub fn best_block_hash(&self) -> &block::Hash {
2978        &self.best_block_hash
2979    }
2980
2981    /// Returns the estimated height of the chain.
2982    ///
2983    /// If syncing, the estimated height of the chain, else the current best height, numeric.
2984    ///
2985    /// In Zebra, this is always the height estimate, so it might be a little inaccurate.
2986    pub fn estimated_height(&self) -> Height {
2987        self.estimated_height
2988    }
2989
2990    /// Returns the value pool balances.
2991    pub fn value_pools(&self) -> &[get_blockchain_info::Balance; 5] {
2992        &self.value_pools
2993    }
2994
2995    /// Returns the network upgrades.
2996    pub fn upgrades(&self) -> &IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo> {
2997        &self.upgrades
2998    }
2999
3000    /// Returns the Branch IDs of the current and upcoming consensus rules.
3001    pub fn consensus(&self) -> &TipConsensusBranch {
3002        &self.consensus
3003    }
3004}
3005
3006/// A wrapper type with a list of transparent address strings.
3007///
3008/// This is used for the input parameter of [`RpcServer::get_address_balance`],
3009/// [`RpcServer::get_address_tx_ids`] and [`RpcServer::get_address_utxos`].
3010#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
3011pub struct AddressStrings {
3012    /// A list of transparent address strings.
3013    addresses: Vec<String>,
3014}
3015
3016impl AddressStrings {
3017    /// Creates a new `AddressStrings` given a vector.
3018    #[cfg(test)]
3019    pub fn new(addresses: Vec<String>) -> AddressStrings {
3020        AddressStrings { addresses }
3021    }
3022
3023    /// Creates a new [`AddressStrings`] from a given vector, returns an error if any addresses are incorrect.
3024    pub fn new_valid(addresses: Vec<String>) -> Result<AddressStrings> {
3025        let address_strings = Self { addresses };
3026        address_strings.clone().valid_addresses()?;
3027        Ok(address_strings)
3028    }
3029
3030    /// Given a list of addresses as strings:
3031    /// - check if provided list have all valid transparent addresses.
3032    /// - return valid addresses as a set of `Address`.
3033    pub fn valid_addresses(self) -> Result<HashSet<Address>> {
3034        // Reference for the legacy error code:
3035        // <https://github.com/zcash/zcash/blob/99ad6fdc3a549ab510422820eea5e5ce9f60a5fd/src/rpc/misc.cpp#L783-L784>
3036        let valid_addresses: HashSet<Address> = self
3037            .addresses
3038            .into_iter()
3039            .map(|address| {
3040                address
3041                    .parse()
3042                    .map_error(server::error::LegacyCode::InvalidAddressOrKey)
3043            })
3044            .collect::<Result<_>>()?;
3045
3046        Ok(valid_addresses)
3047    }
3048
3049    /// Given a list of addresses as strings:
3050    /// - check if provided list have all valid transparent addresses.
3051    /// - return valid addresses as a vec of strings.
3052    pub fn valid_address_strings(self) -> Result<Vec<String>> {
3053        self.clone().valid_addresses()?;
3054        Ok(self.addresses)
3055    }
3056}
3057
3058/// The transparent balance of a set of addresses.
3059#[derive(
3060    Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize,
3061)]
3062pub struct AddressBalance {
3063    /// The total transparent balance.
3064    pub balance: u64,
3065}
3066
3067/// A hex-encoded [`ConsensusBranchId`] string.
3068#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
3069pub struct ConsensusBranchIdHex(#[serde(with = "hex")] ConsensusBranchId);
3070
3071impl ConsensusBranchIdHex {
3072    /// Returns a new instance of ['ConsensusBranchIdHex'].
3073    pub fn new(consensus_branch_id: u32) -> Self {
3074        ConsensusBranchIdHex(consensus_branch_id.into())
3075    }
3076
3077    /// Returns the value of the ['ConsensusBranchId'].
3078    pub fn inner(&self) -> u32 {
3079        self.0.into()
3080    }
3081}
3082
3083/// Information about [`NetworkUpgrade`] activation.
3084#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3085pub struct NetworkUpgradeInfo {
3086    /// Name of upgrade, string.
3087    ///
3088    /// Ignored by lightwalletd, but useful for debugging.
3089    name: NetworkUpgrade,
3090
3091    /// Block height of activation, numeric.
3092    #[serde(rename = "activationheight")]
3093    activation_height: Height,
3094
3095    /// Status of upgrade, string.
3096    status: NetworkUpgradeStatus,
3097}
3098
3099impl NetworkUpgradeInfo {
3100    /// Constructs [`NetworkUpgradeInfo`] from its constituent parts.
3101    pub fn from_parts(
3102        name: NetworkUpgrade,
3103        activation_height: Height,
3104        status: NetworkUpgradeStatus,
3105    ) -> Self {
3106        Self {
3107            name,
3108            activation_height,
3109            status,
3110        }
3111    }
3112
3113    /// Returns the contents of ['NetworkUpgradeInfo'].
3114    pub fn into_parts(self) -> (NetworkUpgrade, Height, NetworkUpgradeStatus) {
3115        (self.name, self.activation_height, self.status)
3116    }
3117}
3118
3119/// The activation status of a [`NetworkUpgrade`].
3120#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3121pub enum NetworkUpgradeStatus {
3122    /// The network upgrade is currently active.
3123    ///
3124    /// Includes all network upgrades that have previously activated,
3125    /// even if they are not the most recent network upgrade.
3126    #[serde(rename = "active")]
3127    Active,
3128
3129    /// The network upgrade does not have an activation height.
3130    #[serde(rename = "disabled")]
3131    Disabled,
3132
3133    /// The network upgrade has an activation height, but we haven't reached it yet.
3134    #[serde(rename = "pending")]
3135    Pending,
3136}
3137
3138/// The [`ConsensusBranchId`]s for the tip and the next block.
3139///
3140/// These branch IDs are different when the next block is a network upgrade activation block.
3141#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3142pub struct TipConsensusBranch {
3143    /// Branch ID used to validate the current chain tip, big-endian, hex-encoded.
3144    #[serde(rename = "chaintip")]
3145    chain_tip: ConsensusBranchIdHex,
3146
3147    /// Branch ID used to validate the next block, big-endian, hex-encoded.
3148    #[serde(rename = "nextblock")]
3149    next_block: ConsensusBranchIdHex,
3150}
3151
3152impl TipConsensusBranch {
3153    /// Constructs [`TipConsensusBranch`] from its constituent parts.
3154    pub fn from_parts(chain_tip: u32, next_block: u32) -> Self {
3155        Self {
3156            chain_tip: ConsensusBranchIdHex::new(chain_tip),
3157            next_block: ConsensusBranchIdHex::new(next_block),
3158        }
3159    }
3160
3161    /// Returns the contents of ['TipConsensusBranch'].
3162    pub fn into_parts(self) -> (u32, u32) {
3163        (self.chain_tip.inner(), self.next_block.inner())
3164    }
3165}
3166
3167/// Response to a `sendrawtransaction` RPC request.
3168///
3169/// Contains the hex-encoded hash of the sent transaction.
3170///
3171/// See the notes for the [`Rpc::send_raw_transaction` method].
3172#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3173pub struct SentTransactionHash(#[serde(with = "hex")] transaction::Hash);
3174
3175impl Default for SentTransactionHash {
3176    fn default() -> Self {
3177        Self(transaction::Hash::from([0; 32]))
3178    }
3179}
3180
3181impl SentTransactionHash {
3182    /// Constructs a new [`SentTransactionHash`].
3183    pub fn new(hash: transaction::Hash) -> Self {
3184        SentTransactionHash(hash)
3185    }
3186
3187    /// Returns the contents of ['SentTransactionHash'].
3188    pub fn inner(&self) -> transaction::Hash {
3189        self.0
3190    }
3191}
3192
3193/// Response to a `getblock` RPC request.
3194///
3195/// See the notes for the [`RpcServer::get_block`] method.
3196#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3197#[serde(untagged)]
3198#[allow(clippy::large_enum_variant)] //TODO: create a struct for the Object and Box it
3199pub enum GetBlock {
3200    /// The request block, hex-encoded.
3201    Raw(#[serde(with = "hex")] SerializedBlock),
3202    /// The block object.
3203    Object {
3204        /// The hash of the requested block.
3205        hash: GetBlockHash,
3206
3207        /// The number of confirmations of this block in the best chain,
3208        /// or -1 if it is not in the best chain.
3209        confirmations: i64,
3210
3211        /// The block size. TODO: fill it
3212        #[serde(skip_serializing_if = "Option::is_none")]
3213        size: Option<i64>,
3214
3215        /// The height of the requested block.
3216        #[serde(skip_serializing_if = "Option::is_none")]
3217        height: Option<Height>,
3218
3219        /// The version field of the requested block.
3220        #[serde(skip_serializing_if = "Option::is_none")]
3221        version: Option<u32>,
3222
3223        /// The merkle root of the requested block.
3224        #[serde(with = "opthex", rename = "merkleroot")]
3225        #[serde(skip_serializing_if = "Option::is_none")]
3226        merkle_root: Option<block::merkle::Root>,
3227
3228        /// The blockcommitments field of the requested block. Its interpretation changes
3229        /// depending on the network and height.
3230        #[serde(with = "opthex", rename = "blockcommitments")]
3231        #[serde(skip_serializing_if = "Option::is_none")]
3232        block_commitments: Option<[u8; 32]>,
3233
3234        // `authdataroot` would be here. Undocumented. TODO: decide if we want to support it
3235        //
3236        /// The root of the Sapling commitment tree after applying this block.
3237        #[serde(with = "opthex", rename = "finalsaplingroot")]
3238        #[serde(skip_serializing_if = "Option::is_none")]
3239        final_sapling_root: Option<[u8; 32]>,
3240
3241        /// The root of the Orchard commitment tree after applying this block.
3242        #[serde(with = "opthex", rename = "finalorchardroot")]
3243        #[serde(skip_serializing_if = "Option::is_none")]
3244        final_orchard_root: Option<[u8; 32]>,
3245
3246        // `chainhistoryroot` would be here. Undocumented. TODO: decide if we want to support it
3247        //
3248        /// List of transactions in block order, hex-encoded if verbosity=1 or
3249        /// as objects if verbosity=2.
3250        tx: Vec<GetBlockTransaction>,
3251
3252        /// The height of the requested block.
3253        #[serde(skip_serializing_if = "Option::is_none")]
3254        time: Option<i64>,
3255
3256        /// The nonce of the requested block header.
3257        #[serde(with = "opthex")]
3258        #[serde(skip_serializing_if = "Option::is_none")]
3259        nonce: Option<[u8; 32]>,
3260
3261        /// The Equihash solution in the requested block header.
3262        /// Note: presence of this field in getblock is not documented in zcashd.
3263        #[serde(with = "opthex")]
3264        #[serde(skip_serializing_if = "Option::is_none")]
3265        solution: Option<Solution>,
3266
3267        /// The difficulty threshold of the requested block header displayed in compact form.
3268        #[serde(with = "opthex")]
3269        #[serde(skip_serializing_if = "Option::is_none")]
3270        bits: Option<CompactDifficulty>,
3271
3272        /// Floating point number that represents the difficulty limit for this block as a multiple
3273        /// of the minimum difficulty for the network.
3274        #[serde(skip_serializing_if = "Option::is_none")]
3275        difficulty: Option<f64>,
3276
3277        // `chainwork` would be here, but we don't plan on supporting it
3278        // `anchor` would be here. Not planned to be supported.
3279        // `chainSupply` would be here, TODO: implement
3280        // `valuePools` would be here, TODO: implement
3281        //
3282        /// Information about the note commitment trees.
3283        trees: GetBlockTrees,
3284
3285        /// The previous block hash of the requested block header.
3286        #[serde(rename = "previousblockhash", skip_serializing_if = "Option::is_none")]
3287        previous_block_hash: Option<GetBlockHash>,
3288
3289        /// The next block hash after the requested block header.
3290        #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
3291        next_block_hash: Option<GetBlockHash>,
3292    },
3293}
3294
3295impl Default for GetBlock {
3296    fn default() -> Self {
3297        GetBlock::Object {
3298            hash: GetBlockHash::default(),
3299            confirmations: 0,
3300            height: None,
3301            time: None,
3302            tx: Vec::new(),
3303            trees: GetBlockTrees::default(),
3304            size: None,
3305            version: None,
3306            merkle_root: None,
3307            block_commitments: None,
3308            final_sapling_root: None,
3309            final_orchard_root: None,
3310            nonce: None,
3311            bits: None,
3312            difficulty: None,
3313            previous_block_hash: None,
3314            next_block_hash: None,
3315            solution: None,
3316        }
3317    }
3318}
3319
3320#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3321#[serde(untagged)]
3322/// The transaction list in a `getblock` call. Can be a list of transaction
3323/// IDs or the full transaction details depending on verbosity.
3324pub enum GetBlockTransaction {
3325    /// The transaction hash, hex-encoded.
3326    Hash(#[serde(with = "hex")] transaction::Hash),
3327    /// The block object.
3328    Object(Box<TransactionObject>),
3329}
3330
3331/// Response to a `getblockheader` RPC request.
3332///
3333/// See the notes for the [`RpcServer::get_block_header`] method.
3334#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3335#[serde(untagged)]
3336pub enum GetBlockHeader {
3337    /// The request block header, hex-encoded.
3338    Raw(hex_data::HexData),
3339
3340    /// The block header object.
3341    Object(Box<GetBlockHeaderObject>),
3342}
3343
3344#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3345/// Verbose response to a `getblockheader` RPC request.
3346///
3347/// See the notes for the [`RpcServer::get_block_header`] method.
3348pub struct GetBlockHeaderObject {
3349    /// The hash of the requested block.
3350    pub hash: GetBlockHash,
3351
3352    /// The number of confirmations of this block in the best chain,
3353    /// or -1 if it is not in the best chain.
3354    pub confirmations: i64,
3355
3356    /// The height of the requested block.
3357    pub height: Height,
3358
3359    /// The version field of the requested block.
3360    pub version: u32,
3361
3362    /// The merkle root of the requesteed block.
3363    #[serde(with = "hex", rename = "merkleroot")]
3364    pub merkle_root: block::merkle::Root,
3365
3366    /// The blockcommitments field of the requested block. Its interpretation changes
3367    /// depending on the network and height.
3368    #[serde(with = "hex", rename = "blockcommitments")]
3369    pub block_commitments: [u8; 32],
3370
3371    /// The root of the Sapling commitment tree after applying this block.
3372    #[serde(with = "hex", rename = "finalsaplingroot")]
3373    pub final_sapling_root: [u8; 32],
3374
3375    /// The number of Sapling notes in the Sapling note commitment tree
3376    /// after applying this block. Used by the `getblock` RPC method.
3377    #[serde(skip)]
3378    pub sapling_tree_size: u64,
3379
3380    /// The block time of the requested block header in non-leap seconds since Jan 1 1970 GMT.
3381    pub time: i64,
3382
3383    /// The nonce of the requested block header.
3384    #[serde(with = "hex")]
3385    pub nonce: [u8; 32],
3386
3387    /// The Equihash solution in the requested block header.
3388    #[serde(with = "hex")]
3389    pub solution: Solution,
3390
3391    /// The difficulty threshold of the requested block header displayed in compact form.
3392    #[serde(with = "hex")]
3393    pub bits: CompactDifficulty,
3394
3395    /// Floating point number that represents the difficulty limit for this block as a multiple
3396    /// of the minimum difficulty for the network.
3397    pub difficulty: f64,
3398
3399    /// The previous block hash of the requested block header.
3400    #[serde(rename = "previousblockhash")]
3401    pub previous_block_hash: GetBlockHash,
3402
3403    /// The next block hash after the requested block header.
3404    #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
3405    pub next_block_hash: Option<GetBlockHash>,
3406}
3407
3408impl Default for GetBlockHeader {
3409    fn default() -> Self {
3410        GetBlockHeader::Object(Box::default())
3411    }
3412}
3413
3414impl Default for GetBlockHeaderObject {
3415    fn default() -> Self {
3416        let difficulty: ExpandedDifficulty = zebra_chain::work::difficulty::U256::one().into();
3417
3418        GetBlockHeaderObject {
3419            hash: GetBlockHash::default(),
3420            confirmations: 0,
3421            height: Height::MIN,
3422            version: 4,
3423            merkle_root: block::merkle::Root([0; 32]),
3424            block_commitments: Default::default(),
3425            final_sapling_root: Default::default(),
3426            sapling_tree_size: Default::default(),
3427            time: 0,
3428            nonce: [0; 32],
3429            solution: Solution::for_proposal(),
3430            bits: difficulty.to_compact(),
3431            difficulty: 1.0,
3432            previous_block_hash: Default::default(),
3433            next_block_hash: Default::default(),
3434        }
3435    }
3436}
3437
3438/// Response to a `getbestblockhash` and `getblockhash` RPC request.
3439///
3440/// Contains the hex-encoded hash of the requested block.
3441///
3442/// Also see the notes for the [`RpcServer::get_best_block_hash`] and `get_block_hash` methods.
3443#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3444#[serde(transparent)]
3445pub struct GetBlockHash(#[serde(with = "hex")] pub block::Hash);
3446
3447/// Response to a `getbestblockheightandhash` RPC request.
3448#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3449pub struct GetBlockHeightAndHash {
3450    /// The best chain tip block height
3451    pub height: block::Height,
3452    /// The best chain tip block hash
3453    pub hash: block::Hash,
3454}
3455
3456impl Default for GetBlockHeightAndHash {
3457    fn default() -> Self {
3458        Self {
3459            height: block::Height::MIN,
3460            hash: block::Hash([0; 32]),
3461        }
3462    }
3463}
3464
3465impl Default for GetBlockHash {
3466    fn default() -> Self {
3467        GetBlockHash(block::Hash([0; 32]))
3468    }
3469}
3470
3471/// Response to a `getrawtransaction` RPC request.
3472///
3473/// See the notes for the [`Rpc::get_raw_transaction` method].
3474#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
3475#[serde(untagged)]
3476pub enum GetRawTransaction {
3477    /// The raw transaction, encoded as hex bytes.
3478    Raw(#[serde(with = "hex")] SerializedTransaction),
3479    /// The transaction object.
3480    Object(Box<TransactionObject>),
3481}
3482
3483impl Default for GetRawTransaction {
3484    fn default() -> Self {
3485        Self::Object(Box::default())
3486    }
3487}
3488
3489/// Response to a `getaddressutxos` RPC request.
3490///
3491/// See the notes for the [`Rpc::get_address_utxos` method].
3492#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
3493pub struct GetAddressUtxos {
3494    /// The transparent address, base58check encoded
3495    address: transparent::Address,
3496
3497    /// The output txid, in big-endian order, hex-encoded
3498    #[serde(with = "hex")]
3499    txid: transaction::Hash,
3500
3501    /// The transparent output index, numeric
3502    #[serde(rename = "outputIndex")]
3503    output_index: OutputIndex,
3504
3505    /// The transparent output script, hex encoded
3506    #[serde(with = "hex")]
3507    script: transparent::Script,
3508
3509    /// The amount of zatoshis in the transparent output
3510    satoshis: u64,
3511
3512    /// The block height, numeric.
3513    ///
3514    /// We put this field last, to match the zcashd order.
3515    height: Height,
3516}
3517
3518impl Default for GetAddressUtxos {
3519    fn default() -> Self {
3520        Self {
3521            address: transparent::Address::from_pub_key_hash(
3522                zebra_chain::parameters::NetworkKind::default(),
3523                [0u8; 20],
3524            ),
3525            txid: transaction::Hash::from([0; 32]),
3526            output_index: OutputIndex::from_u64(0),
3527            script: transparent::Script::new(&[0u8; 10]),
3528            satoshis: u64::default(),
3529            height: Height(0),
3530        }
3531    }
3532}
3533
3534impl GetAddressUtxos {
3535    /// Constructs a new instance of [`GetAddressUtxos`].
3536    pub fn from_parts(
3537        address: transparent::Address,
3538        txid: transaction::Hash,
3539        output_index: OutputIndex,
3540        script: transparent::Script,
3541        satoshis: u64,
3542        height: Height,
3543    ) -> Self {
3544        GetAddressUtxos {
3545            address,
3546            txid,
3547            output_index,
3548            script,
3549            satoshis,
3550            height,
3551        }
3552    }
3553
3554    /// Returns the contents of [`GetAddressUtxos`].
3555    pub fn into_parts(
3556        &self,
3557    ) -> (
3558        transparent::Address,
3559        transaction::Hash,
3560        OutputIndex,
3561        transparent::Script,
3562        u64,
3563        Height,
3564    ) {
3565        (
3566            self.address.clone(),
3567            self.txid,
3568            self.output_index,
3569            self.script.clone(),
3570            self.satoshis,
3571            self.height,
3572        )
3573    }
3574}
3575
3576/// A struct to use as parameter of the `getaddresstxids`.
3577///
3578/// See the notes for the [`Rpc::get_address_tx_ids` method].
3579#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3580pub struct GetAddressTxIdsRequest {
3581    // A list of addresses to get transactions from.
3582    addresses: Vec<String>,
3583    // The height to start looking for transactions.
3584    start: Option<u32>,
3585    // The height to end looking for transactions.
3586    end: Option<u32>,
3587}
3588
3589impl GetAddressTxIdsRequest {
3590    /// Constructs [`GetAddressTxIdsRequest`] from its constituent parts.
3591    pub fn from_parts(addresses: Vec<String>, start: u32, end: u32) -> Self {
3592        GetAddressTxIdsRequest {
3593            addresses,
3594            start: Some(start),
3595            end: Some(end),
3596        }
3597    }
3598    /// Returns the contents of [`GetAddressTxIdsRequest`].
3599    pub fn into_parts(&self) -> (Vec<String>, u32, u32) {
3600        (
3601            self.addresses.clone(),
3602            self.start.unwrap_or(0),
3603            self.end.unwrap_or(0),
3604        )
3605    }
3606}
3607
3608/// Information about the sapling and orchard note commitment trees if any.
3609#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3610pub struct GetBlockTrees {
3611    #[serde(skip_serializing_if = "SaplingTrees::is_empty")]
3612    sapling: SaplingTrees,
3613    #[serde(skip_serializing_if = "OrchardTrees::is_empty")]
3614    orchard: OrchardTrees,
3615}
3616
3617impl Default for GetBlockTrees {
3618    fn default() -> Self {
3619        GetBlockTrees {
3620            sapling: SaplingTrees { size: 0 },
3621            orchard: OrchardTrees { size: 0 },
3622        }
3623    }
3624}
3625
3626impl GetBlockTrees {
3627    /// Constructs a new instance of ['GetBlockTrees'].
3628    pub fn new(sapling: u64, orchard: u64) -> Self {
3629        GetBlockTrees {
3630            sapling: SaplingTrees { size: sapling },
3631            orchard: OrchardTrees { size: orchard },
3632        }
3633    }
3634
3635    /// Returns sapling data held by ['GetBlockTrees'].
3636    pub fn sapling(self) -> u64 {
3637        self.sapling.size
3638    }
3639
3640    /// Returns orchard data held by ['GetBlockTrees'].
3641    pub fn orchard(self) -> u64 {
3642        self.orchard.size
3643    }
3644}
3645
3646/// Sapling note commitment tree information.
3647#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3648pub struct SaplingTrees {
3649    size: u64,
3650}
3651
3652impl SaplingTrees {
3653    fn is_empty(&self) -> bool {
3654        self.size == 0
3655    }
3656}
3657
3658/// Orchard note commitment tree information.
3659#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
3660pub struct OrchardTrees {
3661    size: u64,
3662}
3663
3664impl OrchardTrees {
3665    fn is_empty(&self) -> bool {
3666        self.size == 0
3667    }
3668}
3669
3670/// Build a valid height range from the given optional start and end numbers.
3671///
3672/// # Parameters
3673///
3674/// - `start`: Optional starting height. If not provided, defaults to 0.
3675/// - `end`: Optional ending height. A value of 0 or absence of a value indicates to use `chain_height`.
3676/// - `chain_height`: The maximum permissible height.
3677///
3678/// # Returns
3679///
3680/// A `RangeInclusive<Height>` from the clamped start to the clamped end.
3681///
3682/// # Errors
3683///
3684/// Returns an error if the computed start is greater than the computed end.
3685fn build_height_range(
3686    start: Option<u32>,
3687    end: Option<u32>,
3688    chain_height: Height,
3689) -> Result<RangeInclusive<Height>> {
3690    // Convert optional values to Height, using 0 (as Height(0)) when missing.
3691    // If start is above chain_height, clamp it to chain_height.
3692    let start = Height(start.unwrap_or(0)).min(chain_height);
3693
3694    // For `end`, treat a zero value or missing value as `chain_height`:
3695    let end = match end {
3696        Some(0) | None => chain_height,
3697        Some(val) => Height(val).min(chain_height),
3698    };
3699
3700    if start > end {
3701        return Err(ErrorObject::owned(
3702            ErrorCode::InvalidParams.code(),
3703            format!("start {start:?} must be less than or equal to end {end:?}"),
3704            None::<()>,
3705        ));
3706    }
3707
3708    Ok(start..=end)
3709}
3710
3711/// Given a potentially negative index, find the corresponding `Height`.
3712///
3713/// This function is used to parse the integer index argument of `get_block_hash`.
3714/// This is based on zcashd's implementation:
3715/// <https://github.com/zcash/zcash/blob/c267c3ee26510a974554f227d40a89e3ceb5bb4d/src/rpc/blockchain.cpp#L589-L618>
3716//
3717// TODO: also use this function in `get_block` and `z_get_treestate`
3718#[allow(dead_code)]
3719pub fn height_from_signed_int(index: i32, tip_height: Height) -> Result<Height> {
3720    if index >= 0 {
3721        let height = index.try_into().expect("Positive i32 always fits in u32");
3722        if height > tip_height.0 {
3723            return Err(ErrorObject::borrowed(
3724                ErrorCode::InvalidParams.code(),
3725                "Provided index is greater than the current tip",
3726                None,
3727            ));
3728        }
3729        Ok(Height(height))
3730    } else {
3731        // `index + 1` can't overflow, because `index` is always negative here.
3732        let height = i32::try_from(tip_height.0)
3733            .expect("tip height fits in i32, because Height::MAX fits in i32")
3734            .checked_add(index + 1);
3735
3736        let sanitized_height = match height {
3737            None => {
3738                return Err(ErrorObject::borrowed(
3739                    ErrorCode::InvalidParams.code(),
3740                    "Provided index is not valid",
3741                    None,
3742                ));
3743            }
3744            Some(h) => {
3745                if h < 0 {
3746                    return Err(ErrorObject::borrowed(
3747                        ErrorCode::InvalidParams.code(),
3748                        "Provided negative index ends up with a negative height",
3749                        None,
3750                    ));
3751                }
3752                let h: u32 = h.try_into().expect("Positive i32 always fits in u32");
3753                if h > tip_height.0 {
3754                    return Err(ErrorObject::borrowed(
3755                        ErrorCode::InvalidParams.code(),
3756                        "Provided index is greater than the current tip",
3757                        None,
3758                    ));
3759                }
3760
3761                h
3762            }
3763        };
3764
3765        Ok(Height(sanitized_height))
3766    }
3767}
3768
3769/// A helper module to serialize and deserialize `Option<T: ToHex>` as a hex string.
3770pub mod opthex {
3771    use hex::{FromHex, ToHex};
3772    use serde::{de, Deserialize, Deserializer, Serializer};
3773
3774    #[allow(missing_docs)]
3775    pub fn serialize<S, T>(data: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
3776    where
3777        S: Serializer,
3778        T: ToHex,
3779    {
3780        match data {
3781            Some(data) => {
3782                let s = data.encode_hex::<String>();
3783                serializer.serialize_str(&s)
3784            }
3785            None => serializer.serialize_none(),
3786        }
3787    }
3788
3789    #[allow(missing_docs)]
3790    pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
3791    where
3792        D: Deserializer<'de>,
3793        T: FromHex,
3794    {
3795        let opt = Option::<String>::deserialize(deserializer)?;
3796        match opt {
3797            Some(s) => T::from_hex(&s)
3798                .map(Some)
3799                .map_err(|_e| de::Error::custom("failed to convert hex string")),
3800            None => Ok(None),
3801        }
3802    }
3803}
3804
3805/// A helper module to serialize and deserialize `[u8; N]` as a hex string.
3806pub mod arrayhex {
3807    use serde::{Deserializer, Serializer};
3808    use std::fmt;
3809
3810    #[allow(missing_docs)]
3811    pub fn serialize<S, const N: usize>(data: &[u8; N], serializer: S) -> Result<S::Ok, S::Error>
3812    where
3813        S: Serializer,
3814    {
3815        let hex_string = hex::encode(data);
3816        serializer.serialize_str(&hex_string)
3817    }
3818
3819    #[allow(missing_docs)]
3820    pub fn deserialize<'de, D, const N: usize>(deserializer: D) -> Result<[u8; N], D::Error>
3821    where
3822        D: Deserializer<'de>,
3823    {
3824        struct HexArrayVisitor<const N: usize>;
3825
3826        impl<const N: usize> serde::de::Visitor<'_> for HexArrayVisitor<N> {
3827            type Value = [u8; N];
3828
3829            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
3830                write!(formatter, "a hex string representing exactly {} bytes", N)
3831            }
3832
3833            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
3834            where
3835                E: serde::de::Error,
3836            {
3837                let vec = hex::decode(v).map_err(E::custom)?;
3838                vec.clone().try_into().map_err(|_| {
3839                    E::invalid_length(vec.len(), &format!("expected {} bytes", N).as_str())
3840                })
3841            }
3842        }
3843
3844        deserializer.deserialize_str(HexArrayVisitor::<N>)
3845    }
3846}
3847
3848/// Returns the proof-of-work difficulty as a multiple of the minimum difficulty.
3849pub async fn chain_tip_difficulty<State>(
3850    network: Network,
3851    mut state: State,
3852    should_use_default: bool,
3853) -> Result<f64>
3854where
3855    State: Service<
3856            zebra_state::ReadRequest,
3857            Response = zebra_state::ReadResponse,
3858            Error = zebra_state::BoxError,
3859        > + Clone
3860        + Send
3861        + Sync
3862        + 'static,
3863    State::Future: Send,
3864{
3865    let request = ReadRequest::ChainInfo;
3866
3867    // # TODO
3868    // - add a separate request like BestChainNextMedianTimePast, but skipping the
3869    //   consistency check, because any block's difficulty is ok for display
3870    // - return 1.0 for a "not enough blocks in the state" error, like `zcashd`:
3871    // <https://github.com/zcash/zcash/blob/7b28054e8b46eb46a9589d0bdc8e29f9fa1dc82d/src/rpc/blockchain.cpp#L40-L41>
3872    let response = state
3873        .ready()
3874        .and_then(|service| service.call(request))
3875        .await;
3876
3877    let response = match (should_use_default, response) {
3878        (_, Ok(res)) => res,
3879        (true, Err(_)) => {
3880            return Ok((U256::from(network.target_difficulty_limit()) >> 128).as_u128() as f64)
3881        }
3882        (false, Err(error)) => return Err(ErrorObject::owned(0, error.to_string(), None::<()>)),
3883    };
3884
3885    let chain_info = match response {
3886        ReadResponse::ChainInfo(info) => info,
3887        _ => unreachable!("unmatched response to a chain info request"),
3888    };
3889
3890    // This RPC is typically used for display purposes, so it is not consensus-critical.
3891    // But it uses the difficulty consensus rules for its calculations.
3892    //
3893    // Consensus:
3894    // https://zips.z.cash/protocol/protocol.pdf#nbits
3895    //
3896    // The zcashd implementation performs to_expanded() on f64,
3897    // and then does an inverse division:
3898    // https://github.com/zcash/zcash/blob/d6e2fada844373a8554ee085418e68de4b593a6c/src/rpc/blockchain.cpp#L46-L73
3899    //
3900    // But in Zebra we divide the high 128 bits of each expanded difficulty. This gives
3901    // a similar result, because the lower 128 bits are insignificant after conversion
3902    // to `f64` with a 53-bit mantissa.
3903    //
3904    // `pow_limit >> 128 / difficulty >> 128` is the same as the work calculation
3905    // `(2^256 / pow_limit) / (2^256 / difficulty)`, but it's a bit more accurate.
3906    //
3907    // To simplify the calculation, we don't scale for leading zeroes. (Bitcoin's
3908    // difficulty currently uses 68 bits, so even it would still have full precision
3909    // using this calculation.)
3910
3911    // Get expanded difficulties (256 bits), these are the inverse of the work
3912    let pow_limit: U256 = network.target_difficulty_limit().into();
3913    let Some(difficulty) = chain_info.expected_difficulty.to_expanded() else {
3914        return Ok(0.0);
3915    };
3916
3917    // Shift out the lower 128 bits (256 bits, but the top 128 are all zeroes)
3918    let pow_limit = pow_limit >> 128;
3919    let difficulty = U256::from(difficulty) >> 128;
3920
3921    // Convert to u128 then f64.
3922    // We could also convert U256 to String, then parse as f64, but that's slower.
3923    let pow_limit = pow_limit.as_u128() as f64;
3924    let difficulty = difficulty.as_u128() as f64;
3925
3926    // Invert the division to give approximately: `work(difficulty) / work(pow_limit)`
3927    Ok(pow_limit / difficulty)
3928}