zebra_rpc/methods/types/
get_block_template.rs

1//! Types and functions for the `getblocktemplate` RPC.
2
3pub mod constants;
4pub mod parameters;
5pub mod proposal;
6pub mod zip317;
7
8use std::{collections::HashMap, fmt, iter, sync::Arc};
9
10use jsonrpsee::core::RpcResult;
11use jsonrpsee_types::{ErrorCode, ErrorObject};
12use tokio::sync::watch::{self, error::SendError};
13use tower::{Service, ServiceExt};
14
15use zebra_chain::{
16    amount::{self, Amount, NegativeOrZero, NonNegative},
17    block::{
18        self,
19        merkle::{self, AuthDataRoot},
20        Block, ChainHistoryBlockTxAuthCommitmentHash, ChainHistoryMmrRootHash, Height,
21        MAX_BLOCK_BYTES, ZCASH_BLOCK_VERSION,
22    },
23    chain_sync_status::ChainSyncStatus,
24    chain_tip::ChainTip,
25    parameters::{
26        subsidy::{block_subsidy, funding_stream_values, miner_subsidy, FundingStreamReceiver},
27        Network, NetworkKind, NetworkUpgrade,
28    },
29    serialization::{DateTime32, ZcashDeserializeInto},
30    transaction::{Transaction, UnminedTx, VerifiedUnminedTx},
31    transparent::{
32        self, EXTRA_ZEBRA_COINBASE_DATA, MAX_COINBASE_DATA_LEN, MAX_COINBASE_HEIGHT_DATA_LEN,
33    },
34    work::difficulty::{CompactDifficulty, ExpandedDifficulty},
35};
36use zebra_consensus::{funding_stream_address, MAX_BLOCK_SIGOPS};
37use zebra_node_services::mempool::{self, TransactionDependencies};
38use zebra_state::GetBlockTemplateChainInfo;
39
40use crate::{
41    config,
42    methods::{
43        types::{
44            default_roots::DefaultRoots, long_poll::LongPollId, submit_block,
45            transaction::TransactionTemplate,
46        },
47        GetBlockHash,
48    },
49    server::error::OkOrError,
50};
51
52pub use constants::{
53    CAPABILITIES_FIELD, DEFAULT_SOLUTION_RATE_WINDOW_SIZE,
54    MAX_ESTIMATED_DISTANCE_TO_NETWORK_CHAIN_TIP, MEMPOOL_LONG_POLL_INTERVAL, MUTABLE_FIELD,
55    NONCE_RANGE_FIELD, NOT_SYNCED_ERROR_CODE, ZCASHD_FUNDING_STREAM_ORDER,
56};
57pub use parameters::{GetBlockTemplateRequestMode, JsonParameters};
58pub use proposal::{ProposalResponse, TimeSource};
59
60/// An alias to indicate that a usize value represents the depth of in-block dependencies of a
61/// transaction.
62///
63/// See the `dependencies_depth()` function in [`zip317`] for more details.
64pub type InBlockTxDependenciesDepth = usize;
65
66/// A serialized `getblocktemplate` RPC response in template mode.
67///
68/// This is the output of the `getblocktemplate` RPC in the default 'template' mode. See
69/// [`ProposalResponse`] for the output in 'proposal' mode.
70#[derive(Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
71pub struct GetBlockTemplate {
72    /// The getblocktemplate RPC capabilities supported by Zebra.
73    ///
74    /// At the moment, Zebra does not support any of the extra capabilities from the specification:
75    /// - `proposal`: <https://en.bitcoin.it/wiki/BIP_0023#Block_Proposal>
76    /// - `longpoll`: <https://en.bitcoin.it/wiki/BIP_0022#Optional:_Long_Polling>
77    /// - `serverlist`: <https://en.bitcoin.it/wiki/BIP_0023#Logical_Services>
78    ///
79    /// By the above, Zebra will always return an empty vector here.
80    pub capabilities: Vec<String>,
81
82    /// The version of the block format.
83    /// Always 4 for new Zcash blocks.
84    pub version: u32,
85
86    /// The hash of the previous block.
87    #[serde(rename = "previousblockhash")]
88    pub previous_block_hash: GetBlockHash,
89
90    /// The block commitment for the new block's header.
91    ///
92    /// Same as [`DefaultRoots.block_commitments_hash`], see that field for details.
93    #[serde(rename = "blockcommitmentshash")]
94    #[serde(with = "hex")]
95    pub block_commitments_hash: ChainHistoryBlockTxAuthCommitmentHash,
96
97    /// Legacy backwards-compatibility header root field.
98    ///
99    /// Same as [`DefaultRoots.block_commitments_hash`], see that field for details.
100    #[serde(rename = "lightclientroothash")]
101    #[serde(with = "hex")]
102    pub light_client_root_hash: ChainHistoryBlockTxAuthCommitmentHash,
103
104    /// Legacy backwards-compatibility header root field.
105    ///
106    /// Same as [`DefaultRoots.block_commitments_hash`], see that field for details.
107    #[serde(rename = "finalsaplingroothash")]
108    #[serde(with = "hex")]
109    pub final_sapling_root_hash: ChainHistoryBlockTxAuthCommitmentHash,
110
111    /// The block header roots for [`GetBlockTemplate.transactions`].
112    ///
113    /// If the transactions in the block template are modified, these roots must be recalculated
114    /// [according to the specification](https://zcash.github.io/rpc/getblocktemplate.html).
115    #[serde(rename = "defaultroots")]
116    pub default_roots: DefaultRoots,
117
118    /// The non-coinbase transactions selected for this block template.
119    pub transactions: Vec<TransactionTemplate<amount::NonNegative>>,
120
121    /// The coinbase transaction generated from `transactions` and `height`.
122    #[serde(rename = "coinbasetxn")]
123    pub coinbase_txn: TransactionTemplate<amount::NegativeOrZero>,
124
125    /// An ID that represents the chain tip and mempool contents for this template.
126    #[serde(rename = "longpollid")]
127    pub long_poll_id: LongPollId,
128
129    /// The expected difficulty for the new block displayed in expanded form.
130    #[serde(with = "hex")]
131    pub target: ExpandedDifficulty,
132
133    /// > For each block other than the genesis block, nTime MUST be strictly greater than
134    /// > the median-time-past of that block.
135    ///
136    /// <https://zips.z.cash/protocol/protocol.pdf#blockheader>
137    #[serde(rename = "mintime")]
138    pub min_time: DateTime32,
139
140    /// Hardcoded list of block fields the miner is allowed to change.
141    pub mutable: Vec<String>,
142
143    /// A range of valid nonces that goes from `u32::MIN` to `u32::MAX`.
144    #[serde(rename = "noncerange")]
145    pub nonce_range: String,
146
147    /// Max legacy signature operations in the block.
148    #[serde(rename = "sigoplimit")]
149    pub sigop_limit: u64,
150
151    /// Max block size in bytes
152    #[serde(rename = "sizelimit")]
153    pub size_limit: u64,
154
155    /// > the current time as seen by the server (recommended for block time).
156    /// > note this is not necessarily the system clock, and must fall within the mintime/maxtime rules
157    ///
158    /// <https://en.bitcoin.it/wiki/BIP_0022#Block_Template_Request>
159    #[serde(rename = "curtime")]
160    pub cur_time: DateTime32,
161
162    /// The expected difficulty for the new block displayed in compact form.
163    #[serde(with = "hex")]
164    pub bits: CompactDifficulty,
165
166    /// The height of the next block in the best chain.
167    // Optional TODO: use Height type, but check that deserialized heights are within Height::MAX
168    pub height: u32,
169
170    /// > the maximum time allowed
171    ///
172    /// <https://en.bitcoin.it/wiki/BIP_0023#Mutations>
173    ///
174    /// Zebra adjusts the minimum and current times for testnet minimum difficulty blocks,
175    /// so we need to tell miners what the maximum valid time is.
176    ///
177    /// This field is not in `zcashd` or the Zcash RPC reference yet.
178    ///
179    /// Currently, some miners just use `min_time` or `cur_time`. Others calculate `max_time` from the
180    /// fixed 90 minute consensus rule, or a smaller fixed interval (like 1000s).
181    /// Some miners don't check the maximum time. This can cause invalid blocks after network downtime,
182    /// a significant drop in the hash rate, or after the testnet minimum difficulty interval.
183    #[serde(rename = "maxtime")]
184    pub max_time: DateTime32,
185
186    /// > only relevant for long poll responses:
187    /// > indicates if work received prior to this response remains potentially valid (default)
188    /// > and should have its shares submitted;
189    /// > if false, the miner may wish to discard its share queue
190    ///
191    /// <https://en.bitcoin.it/wiki/BIP_0022#Optional:_Long_Polling>
192    ///
193    /// This field is not in `zcashd` or the Zcash RPC reference yet.
194    ///
195    /// In Zebra, `submit_old` is `false` when the tip block changed or max time is reached,
196    /// and `true` if only the mempool transactions have changed.
197    #[serde(skip_serializing_if = "Option::is_none")]
198    #[serde(default)]
199    #[serde(rename = "submitold")]
200    pub submit_old: Option<bool>,
201}
202
203impl fmt::Debug for GetBlockTemplate {
204    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205        // A block with a lot of transactions can be extremely long in logs.
206        let mut transactions_truncated = self.transactions.clone();
207        if self.transactions.len() > 4 {
208            // Remove transaction 3 onwards, but leave the last transaction
209            let end = self.transactions.len() - 2;
210            transactions_truncated.splice(3..=end, Vec::new());
211        }
212
213        f.debug_struct("GetBlockTemplate")
214            .field("capabilities", &self.capabilities)
215            .field("version", &self.version)
216            .field("previous_block_hash", &self.previous_block_hash)
217            .field("block_commitments_hash", &self.block_commitments_hash)
218            .field("light_client_root_hash", &self.light_client_root_hash)
219            .field("final_sapling_root_hash", &self.final_sapling_root_hash)
220            .field("default_roots", &self.default_roots)
221            .field("transaction_count", &self.transactions.len())
222            .field("transactions", &transactions_truncated)
223            .field("coinbase_txn", &self.coinbase_txn)
224            .field("long_poll_id", &self.long_poll_id)
225            .field("target", &self.target)
226            .field("min_time", &self.min_time)
227            .field("mutable", &self.mutable)
228            .field("nonce_range", &self.nonce_range)
229            .field("sigop_limit", &self.sigop_limit)
230            .field("size_limit", &self.size_limit)
231            .field("cur_time", &self.cur_time)
232            .field("bits", &self.bits)
233            .field("height", &self.height)
234            .field("max_time", &self.max_time)
235            .field("submit_old", &self.submit_old)
236            .finish()
237    }
238}
239
240impl GetBlockTemplate {
241    /// Returns a `Vec` of capabilities supported by the `getblocktemplate` RPC
242    pub fn capabilities() -> Vec<String> {
243        CAPABILITIES_FIELD.iter().map(ToString::to_string).collect()
244    }
245
246    /// Returns a new [`GetBlockTemplate`] struct, based on the supplied arguments and defaults.
247    ///
248    /// The result of this method only depends on the supplied arguments and constants.
249    ///
250    /// If `like_zcashd` is true, try to match the coinbase transactions generated by `zcashd`
251    /// in the `getblocktemplate` RPC.
252    #[allow(clippy::too_many_arguments)]
253    pub fn new(
254        network: &Network,
255        miner_address: &transparent::Address,
256        chain_tip_and_local_time: &GetBlockTemplateChainInfo,
257        long_poll_id: LongPollId,
258        #[cfg(not(test))] mempool_txs: Vec<VerifiedUnminedTx>,
259        #[cfg(test)] mempool_txs: Vec<(InBlockTxDependenciesDepth, VerifiedUnminedTx)>,
260        submit_old: Option<bool>,
261        like_zcashd: bool,
262        extra_coinbase_data: Vec<u8>,
263    ) -> Self {
264        // Calculate the next block height.
265        let next_block_height =
266            (chain_tip_and_local_time.tip_height + 1).expect("tip is far below Height::MAX");
267
268        // Convert transactions into TransactionTemplates
269        #[cfg(not(test))]
270        let (mempool_tx_templates, mempool_txs): (Vec<_>, Vec<_>) =
271            mempool_txs.into_iter().map(|tx| ((&tx).into(), tx)).unzip();
272
273        // Transaction selection returns transactions in an arbitrary order,
274        // but Zebra's snapshot tests expect the same order every time.
275        //
276        // # Correctness
277        //
278        // Transactions that spend outputs created in the same block must appear
279        // after the transactions that create those outputs.
280        #[cfg(test)]
281        let (mempool_tx_templates, mempool_txs): (Vec<_>, Vec<_>) = {
282            let mut mempool_txs_with_templates: Vec<(
283                InBlockTxDependenciesDepth,
284                TransactionTemplate<amount::NonNegative>,
285                VerifiedUnminedTx,
286            )> = mempool_txs
287                .into_iter()
288                .map(|(min_tx_index, tx)| (min_tx_index, (&tx).into(), tx))
289                .collect();
290
291            if like_zcashd {
292                // Sort in serialized data order, excluding the length byte.
293                // `zcashd` sometimes seems to do this, but other times the order is arbitrary.
294                mempool_txs_with_templates.sort_by_key(|(min_tx_index, tx_template, _tx)| {
295                    (*min_tx_index, tx_template.data.clone())
296                });
297            } else {
298                // Sort by hash, this is faster.
299                mempool_txs_with_templates.sort_by_key(|(min_tx_index, tx_template, _tx)| {
300                    (*min_tx_index, tx_template.hash.bytes_in_display_order())
301                });
302            }
303            mempool_txs_with_templates
304                .into_iter()
305                .map(|(_, template, tx)| (template, tx))
306                .unzip()
307        };
308
309        // Generate the coinbase transaction and default roots
310        //
311        // TODO: move expensive root, hash, and tree cryptography to a rayon thread?
312        let (coinbase_txn, default_roots) = generate_coinbase_and_roots(
313            network,
314            next_block_height,
315            miner_address,
316            &mempool_txs,
317            chain_tip_and_local_time.chain_history_root,
318            like_zcashd,
319            extra_coinbase_data,
320        );
321
322        // Convert difficulty
323        let target = chain_tip_and_local_time
324            .expected_difficulty
325            .to_expanded()
326            .expect("state always returns a valid difficulty value");
327
328        // Convert default values
329        let capabilities: Vec<String> = Self::capabilities();
330        let mutable: Vec<String> = MUTABLE_FIELD.iter().map(ToString::to_string).collect();
331
332        tracing::debug!(
333            selected_txs = ?mempool_txs
334                .iter()
335                .map(|tx| (tx.transaction.id.mined_id(), tx.unpaid_actions))
336                .collect::<Vec<_>>(),
337            "creating template ... "
338        );
339
340        GetBlockTemplate {
341            capabilities,
342
343            version: ZCASH_BLOCK_VERSION,
344
345            previous_block_hash: GetBlockHash(chain_tip_and_local_time.tip_hash),
346            block_commitments_hash: default_roots.block_commitments_hash,
347            light_client_root_hash: default_roots.block_commitments_hash,
348            final_sapling_root_hash: default_roots.block_commitments_hash,
349            default_roots,
350
351            transactions: mempool_tx_templates,
352
353            coinbase_txn,
354
355            long_poll_id,
356
357            target,
358
359            min_time: chain_tip_and_local_time.min_time,
360
361            mutable,
362
363            nonce_range: NONCE_RANGE_FIELD.to_string(),
364
365            sigop_limit: MAX_BLOCK_SIGOPS,
366
367            size_limit: MAX_BLOCK_BYTES,
368
369            cur_time: chain_tip_and_local_time.cur_time,
370
371            bits: chain_tip_and_local_time.expected_difficulty,
372
373            height: next_block_height.0,
374
375            max_time: chain_tip_and_local_time.max_time,
376
377            submit_old,
378        }
379    }
380}
381
382#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
383#[serde(untagged)]
384/// A `getblocktemplate` RPC response.
385pub enum Response {
386    /// `getblocktemplate` RPC request in template mode.
387    TemplateMode(Box<GetBlockTemplate>),
388
389    /// `getblocktemplate` RPC request in proposal mode.
390    ProposalMode(ProposalResponse),
391}
392
393impl Response {
394    /// Returns the inner template, if the response is in template mode.
395    pub fn try_into_template(self) -> Option<GetBlockTemplate> {
396        match self {
397            Response::TemplateMode(template) => Some(*template),
398            Response::ProposalMode(_) => None,
399        }
400    }
401
402    /// Returns the inner proposal, if the response is in proposal mode.
403    pub fn try_into_proposal(self) -> Option<ProposalResponse> {
404        match self {
405            Response::TemplateMode(_) => None,
406            Response::ProposalMode(proposal) => Some(proposal),
407        }
408    }
409}
410
411///  Handler for the `getblocktemplate` RPC.
412#[derive(Clone)]
413pub struct GetBlockTemplateHandler<BlockVerifierRouter, SyncStatus>
414where
415    BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
416        + Clone
417        + Send
418        + Sync
419        + 'static,
420    <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
421    SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
422{
423    /// The configured miner address for this RPC service.
424    ///
425    /// Zebra currently only supports transparent addresses.
426    miner_address: Option<transparent::Address>,
427
428    /// Extra data to include in coinbase transaction inputs.
429    /// Limited to around 95 bytes by the consensus rules.
430    extra_coinbase_data: Vec<u8>,
431
432    /// The chain verifier, used for submitting blocks.
433    block_verifier_router: BlockVerifierRouter,
434
435    /// The chain sync status, used for checking if Zebra is likely close to the network chain tip.
436    sync_status: SyncStatus,
437
438    /// A channel to send successful block submissions to the block gossip task,
439    /// so they can be advertised to peers.
440    mined_block_sender: watch::Sender<(block::Hash, block::Height)>,
441}
442
443impl<BlockVerifierRouter, SyncStatus> GetBlockTemplateHandler<BlockVerifierRouter, SyncStatus>
444where
445    BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
446        + Clone
447        + Send
448        + Sync
449        + 'static,
450    <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
451    SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
452{
453    /// Creates a new [`GetBlockTemplateHandler`].
454    ///
455    /// # Panics
456    ///
457    /// - If the provided `mining_config` is not valid.
458    pub fn new(
459        net: &Network,
460        conf: config::mining::Config,
461        block_verifier_router: BlockVerifierRouter,
462        sync_status: SyncStatus,
463        mined_block_sender: Option<watch::Sender<(block::Hash, block::Height)>>,
464    ) -> Self {
465        // Prevent loss of miner funds due to an unsupported or incorrect address type.
466        if let Some(miner_address) = conf.miner_address.clone() {
467            match net.kind() {
468                NetworkKind::Mainnet => assert_eq!(
469                    miner_address.network_kind(),
470                    NetworkKind::Mainnet,
471                    "Incorrect config: Zebra is configured to run on a Mainnet network, \
472                    which implies the configured mining address needs to be for Mainnet, \
473                    but the provided address is for {}.",
474                    miner_address.network_kind(),
475                ),
476                // `Regtest` uses `Testnet` transparent addresses.
477                network_kind @ (NetworkKind::Testnet | NetworkKind::Regtest) => assert_eq!(
478                    miner_address.network_kind(),
479                    NetworkKind::Testnet,
480                    "Incorrect config: Zebra is configured to run on a {network_kind} network, \
481                    which implies the configured mining address needs to be for Testnet, \
482                    but the provided address is for {}.",
483                    miner_address.network_kind(),
484                ),
485            }
486        }
487
488        // A limit on the configured extra coinbase data, regardless of the current block height.
489        // This is different from the consensus rule, which limits the total height + data.
490        const EXTRA_COINBASE_DATA_LIMIT: usize =
491            MAX_COINBASE_DATA_LEN - MAX_COINBASE_HEIGHT_DATA_LEN;
492
493        let debug_like_zcashd = conf.debug_like_zcashd;
494
495        // Hex-decode to bytes if possible, otherwise UTF-8 encode to bytes.
496        let extra_coinbase_data = conf.extra_coinbase_data.unwrap_or_else(|| {
497            if debug_like_zcashd {
498                ""
499            } else {
500                EXTRA_ZEBRA_COINBASE_DATA
501            }
502            .to_string()
503        });
504        let extra_coinbase_data = hex::decode(&extra_coinbase_data)
505            .unwrap_or_else(|_error| extra_coinbase_data.as_bytes().to_vec());
506
507        assert!(
508            extra_coinbase_data.len() <= EXTRA_COINBASE_DATA_LIMIT,
509            "extra coinbase data is {} bytes, but Zebra's limit is {}.\n\
510             Configure mining.extra_coinbase_data with a shorter string",
511            extra_coinbase_data.len(),
512            EXTRA_COINBASE_DATA_LIMIT,
513        );
514
515        Self {
516            miner_address: conf.miner_address,
517            extra_coinbase_data,
518            block_verifier_router,
519            sync_status,
520            mined_block_sender: mined_block_sender
521                .unwrap_or(submit_block::SubmitBlockChannel::default().sender()),
522        }
523    }
524
525    /// Returns the miner's address.
526    pub fn miner_address(&self) -> Option<transparent::Address> {
527        self.miner_address.clone()
528    }
529
530    /// Returns the extra coinbase data.
531    pub fn extra_coinbase_data(&self) -> Vec<u8> {
532        self.extra_coinbase_data.clone()
533    }
534
535    /// Returns the sync status.
536    pub fn sync_status(&self) -> SyncStatus {
537        self.sync_status.clone()
538    }
539
540    /// Returns the block verifier router.
541    pub fn block_verifier_router(&self) -> BlockVerifierRouter {
542        self.block_verifier_router.clone()
543    }
544
545    /// Advertises the mined block.
546    pub fn advertise_mined_block(
547        &self,
548        block: block::Hash,
549        height: block::Height,
550    ) -> Result<(), SendError<(block::Hash, block::Height)>> {
551        self.mined_block_sender.send((block, height))
552    }
553}
554
555impl<BlockVerifierRouter, SyncStatus> fmt::Debug
556    for GetBlockTemplateHandler<BlockVerifierRouter, SyncStatus>
557where
558    BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
559        + Clone
560        + Send
561        + Sync
562        + 'static,
563    <BlockVerifierRouter as Service<zebra_consensus::Request>>::Future: Send,
564    SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
565{
566    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
567        // Skip fields without debug impls
568        f.debug_struct("GetBlockTemplateRpcImpl")
569            .field("miner_address", &self.miner_address)
570            .field("extra_coinbase_data", &self.extra_coinbase_data)
571            .finish()
572    }
573}
574
575// - Parameter checks
576
577/// Checks that `data` is omitted in `Template` mode or provided in `Proposal` mode,
578///
579/// Returns an error if there's a mismatch between the mode and whether `data` is provided.
580pub fn check_parameters(parameters: &Option<JsonParameters>) -> RpcResult<()> {
581    let Some(parameters) = parameters else {
582        return Ok(());
583    };
584
585    match parameters {
586        JsonParameters {
587            mode: GetBlockTemplateRequestMode::Template,
588            data: None,
589            ..
590        }
591        | JsonParameters {
592            mode: GetBlockTemplateRequestMode::Proposal,
593            data: Some(_),
594            ..
595        } => Ok(()),
596
597        JsonParameters {
598            mode: GetBlockTemplateRequestMode::Proposal,
599            data: None,
600            ..
601        } => Err(ErrorObject::borrowed(
602            ErrorCode::InvalidParams.code(),
603            "\"data\" parameter must be \
604                provided in \"proposal\" mode",
605            None,
606        )),
607
608        JsonParameters {
609            mode: GetBlockTemplateRequestMode::Template,
610            data: Some(_),
611            ..
612        } => Err(ErrorObject::borrowed(
613            ErrorCode::InvalidParams.code(),
614            "\"data\" parameter must be \
615                omitted in \"template\" mode",
616            None,
617        )),
618    }
619}
620
621/// Returns the miner address, or an error if it is invalid.
622pub fn check_miner_address(
623    miner_address: Option<transparent::Address>,
624) -> RpcResult<transparent::Address> {
625    miner_address.ok_or_misc_error(
626        "set `mining.miner_address` in `zebrad.toml` to a transparent address".to_string(),
627    )
628}
629
630/// Attempts to validate block proposal against all of the server's
631/// usual acceptance rules (except proof-of-work).
632///
633/// Returns a `getblocktemplate` [`Response`].
634pub async fn validate_block_proposal<BlockVerifierRouter, Tip, SyncStatus>(
635    mut block_verifier_router: BlockVerifierRouter,
636    block_proposal_bytes: Vec<u8>,
637    network: Network,
638    latest_chain_tip: Tip,
639    sync_status: SyncStatus,
640) -> RpcResult<Response>
641where
642    BlockVerifierRouter: Service<zebra_consensus::Request, Response = block::Hash, Error = zebra_consensus::BoxError>
643        + Clone
644        + Send
645        + Sync
646        + 'static,
647    Tip: ChainTip + Clone + Send + Sync + 'static,
648    SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
649{
650    check_synced_to_tip(&network, latest_chain_tip, sync_status)?;
651
652    let block: Block = match block_proposal_bytes.zcash_deserialize_into() {
653        Ok(block) => block,
654        Err(parse_error) => {
655            tracing::info!(
656                ?parse_error,
657                "error response from block parser in CheckProposal request"
658            );
659
660            return Ok(
661                ProposalResponse::rejected("invalid proposal format", parse_error.into()).into(),
662            );
663        }
664    };
665
666    let block_verifier_router_response = block_verifier_router
667        .ready()
668        .await
669        .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?
670        .call(zebra_consensus::Request::CheckProposal(Arc::new(block)))
671        .await;
672
673    Ok(block_verifier_router_response
674        .map(|_hash| ProposalResponse::Valid)
675        .unwrap_or_else(|verify_chain_error| {
676            tracing::info!(
677                ?verify_chain_error,
678                "error response from block_verifier_router in CheckProposal request"
679            );
680
681            ProposalResponse::rejected("invalid proposal", verify_chain_error)
682        })
683        .into())
684}
685
686// - State and syncer checks
687
688/// Returns an error if Zebra is not synced to the consensus chain tip.
689/// Returns early with `Ok(())` if Proof-of-Work is disabled on the provided `network`.
690/// This error might be incorrect if the local clock is skewed.
691pub fn check_synced_to_tip<Tip, SyncStatus>(
692    network: &Network,
693    latest_chain_tip: Tip,
694    sync_status: SyncStatus,
695) -> RpcResult<()>
696where
697    Tip: ChainTip + Clone + Send + Sync + 'static,
698    SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
699{
700    if network.is_a_test_network() {
701        return Ok(());
702    }
703
704    // The tip estimate may not be the same as the one coming from the state
705    // but this is ok for an estimate
706    let (estimated_distance_to_chain_tip, local_tip_height) = latest_chain_tip
707        .estimate_distance_to_network_chain_tip(network)
708        .ok_or_misc_error("no chain tip available yet")?;
709
710    if !sync_status.is_close_to_tip()
711        || estimated_distance_to_chain_tip > MAX_ESTIMATED_DISTANCE_TO_NETWORK_CHAIN_TIP
712    {
713        tracing::info!(
714            ?estimated_distance_to_chain_tip,
715            ?local_tip_height,
716            "Zebra has not synced to the chain tip. \
717             Hint: check your network connection, clock, and time zone settings."
718        );
719
720        return Err(ErrorObject::owned(
721            NOT_SYNCED_ERROR_CODE.code(),
722            format!(
723                "Zebra has not synced to the chain tip, \
724                 estimated distance: {estimated_distance_to_chain_tip:?}, \
725                 local tip: {local_tip_height:?}. \
726                 Hint: check your network connection, clock, and time zone settings."
727            ),
728            None::<()>,
729        ));
730    }
731
732    Ok(())
733}
734
735// - State and mempool data fetches
736
737/// Returns the state data for the block template.
738///
739/// You should call `check_synced_to_tip()` before calling this function.
740/// If the state does not have enough blocks, returns an error.
741pub async fn fetch_state_tip_and_local_time<State>(
742    state: State,
743) -> RpcResult<GetBlockTemplateChainInfo>
744where
745    State: Service<
746            zebra_state::ReadRequest,
747            Response = zebra_state::ReadResponse,
748            Error = zebra_state::BoxError,
749        > + Clone
750        + Send
751        + Sync
752        + 'static,
753{
754    let request = zebra_state::ReadRequest::ChainInfo;
755    let response = state
756        .oneshot(request.clone())
757        .await
758        .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
759
760    let chain_info = match response {
761        zebra_state::ReadResponse::ChainInfo(chain_info) => chain_info,
762        _ => unreachable!("incorrect response to {request:?}"),
763    };
764
765    Ok(chain_info)
766}
767
768/// Returns the transactions that are currently in `mempool`, or None if the
769/// `last_seen_tip_hash` from the mempool response doesn't match the tip hash from the state.
770///
771/// You should call `check_synced_to_tip()` before calling this function.
772/// If the mempool is inactive because Zebra is not synced to the tip, returns no transactions.
773pub async fn fetch_mempool_transactions<Mempool>(
774    mempool: Mempool,
775    chain_tip_hash: block::Hash,
776) -> RpcResult<Option<(Vec<VerifiedUnminedTx>, TransactionDependencies)>>
777where
778    Mempool: Service<
779            mempool::Request,
780            Response = mempool::Response,
781            Error = zebra_node_services::BoxError,
782        > + 'static,
783    Mempool::Future: Send,
784{
785    let response = mempool
786        .oneshot(mempool::Request::FullTransactions)
787        .await
788        .map_err(|error| ErrorObject::owned(0, error.to_string(), None::<()>))?;
789
790    // TODO: Order transactions in block templates based on their dependencies
791
792    let mempool::Response::FullTransactions {
793        transactions,
794        transaction_dependencies,
795        last_seen_tip_hash,
796    } = response
797    else {
798        unreachable!("unmatched response to a mempool::FullTransactions request")
799    };
800
801    // Check that the mempool and state were in sync when we made the requests
802    Ok((last_seen_tip_hash == chain_tip_hash).then_some((transactions, transaction_dependencies)))
803}
804
805// - Response processing
806
807/// Generates and returns the coinbase transaction and default roots.
808///
809/// If `like_zcashd` is true, try to match the coinbase transactions generated by `zcashd`
810/// in the `getblocktemplate` RPC.
811pub fn generate_coinbase_and_roots(
812    network: &Network,
813    block_template_height: Height,
814    miner_address: &transparent::Address,
815    mempool_txs: &[VerifiedUnminedTx],
816    chain_history_root: Option<ChainHistoryMmrRootHash>,
817    like_zcashd: bool,
818    extra_coinbase_data: Vec<u8>,
819) -> (TransactionTemplate<NegativeOrZero>, DefaultRoots) {
820    // Generate the coinbase transaction
821    let miner_fee = calculate_miner_fee(mempool_txs);
822    let coinbase_txn = generate_coinbase_transaction(
823        network,
824        block_template_height,
825        miner_address,
826        miner_fee,
827        like_zcashd,
828        extra_coinbase_data,
829    );
830
831    // Calculate block default roots
832    //
833    // TODO: move expensive root, hash, and tree cryptography to a rayon thread?
834    let chain_history_root = chain_history_root
835        .or_else(|| {
836            (NetworkUpgrade::Heartwood.activation_height(network) == Some(block_template_height))
837                .then_some([0; 32].into())
838        })
839        .expect("history tree can't be empty");
840    let default_roots =
841        calculate_default_root_hashes(&coinbase_txn, mempool_txs, chain_history_root);
842
843    let coinbase_txn = TransactionTemplate::from_coinbase(&coinbase_txn, miner_fee);
844
845    (coinbase_txn, default_roots)
846}
847
848// - Coinbase transaction processing
849
850/// Returns a coinbase transaction for the supplied parameters.
851///
852/// If `like_zcashd` is true, try to match the coinbase transactions generated by `zcashd`
853/// in the `getblocktemplate` RPC.
854pub fn generate_coinbase_transaction(
855    network: &Network,
856    height: Height,
857    miner_address: &transparent::Address,
858    miner_fee: Amount<NonNegative>,
859    like_zcashd: bool,
860    extra_coinbase_data: Vec<u8>,
861) -> UnminedTx {
862    let outputs = standard_coinbase_outputs(network, height, miner_address, miner_fee, like_zcashd);
863
864    if like_zcashd {
865        Transaction::new_v4_coinbase(network, height, outputs, like_zcashd, extra_coinbase_data)
866            .into()
867    } else {
868        Transaction::new_v5_coinbase(network, height, outputs, extra_coinbase_data).into()
869    }
870}
871
872/// Returns the total miner fee for `mempool_txs`.
873pub fn calculate_miner_fee(mempool_txs: &[VerifiedUnminedTx]) -> Amount<NonNegative> {
874    let miner_fee: amount::Result<Amount<NonNegative>> =
875        mempool_txs.iter().map(|tx| tx.miner_fee).sum();
876
877    miner_fee.expect(
878        "invalid selected transactions: \
879         fees in a valid block can not be more than MAX_MONEY",
880    )
881}
882
883/// Returns the standard funding stream and miner reward transparent output scripts
884/// for `network`, `height` and `miner_fee`.
885///
886/// Only works for post-Canopy heights.
887///
888/// If `like_zcashd` is true, try to match the coinbase transactions generated by `zcashd`
889/// in the `getblocktemplate` RPC.
890pub fn standard_coinbase_outputs(
891    network: &Network,
892    height: Height,
893    miner_address: &transparent::Address,
894    miner_fee: Amount<NonNegative>,
895    like_zcashd: bool,
896) -> Vec<(Amount<NonNegative>, transparent::Script)> {
897    let expected_block_subsidy = block_subsidy(height, network).expect("valid block subsidy");
898    let funding_streams = funding_stream_values(height, network, expected_block_subsidy)
899        .expect("funding stream value calculations are valid for reasonable chain heights");
900
901    // Optional TODO: move this into a zebra_consensus function?
902    let funding_streams: HashMap<
903        FundingStreamReceiver,
904        (Amount<NonNegative>, &transparent::Address),
905    > = funding_streams
906        .into_iter()
907        .filter_map(|(receiver, amount)| {
908            Some((
909                receiver,
910                (amount, funding_stream_address(height, network, receiver)?),
911            ))
912        })
913        .collect();
914
915    let miner_reward = miner_subsidy(height, network, expected_block_subsidy)
916        .expect("reward calculations are valid for reasonable chain heights")
917        + miner_fee;
918    let miner_reward =
919        miner_reward.expect("reward calculations are valid for reasonable chain heights");
920
921    combine_coinbase_outputs(funding_streams, miner_address, miner_reward, like_zcashd)
922}
923
924/// Combine the miner reward and funding streams into a list of coinbase amounts and addresses.
925///
926/// If `like_zcashd` is true, try to match the coinbase transactions generated by `zcashd`
927/// in the `getblocktemplate` RPC.
928fn combine_coinbase_outputs(
929    funding_streams: HashMap<FundingStreamReceiver, (Amount<NonNegative>, &transparent::Address)>,
930    miner_address: &transparent::Address,
931    miner_reward: Amount<NonNegative>,
932    like_zcashd: bool,
933) -> Vec<(Amount<NonNegative>, transparent::Script)> {
934    // Collect all the funding streams and convert them to outputs.
935    let funding_streams_outputs: Vec<(Amount<NonNegative>, &transparent::Address)> =
936        funding_streams
937            .into_iter()
938            .map(|(_receiver, (amount, address))| (amount, address))
939            .collect();
940
941    let mut coinbase_outputs: Vec<(Amount<NonNegative>, transparent::Script)> =
942        funding_streams_outputs
943            .iter()
944            .map(|(amount, address)| (*amount, address.create_script_from_address()))
945            .collect();
946
947    // The HashMap returns funding streams in an arbitrary order,
948    // but Zebra's snapshot tests expect the same order every time.
949    if like_zcashd {
950        // zcashd sorts outputs in serialized data order, excluding the length field
951        coinbase_outputs.sort_by_key(|(_amount, script)| script.clone());
952
953        // The miner reward is always the first output independent of the sort order
954        coinbase_outputs.insert(
955            0,
956            (miner_reward, miner_address.create_script_from_address()),
957        );
958    } else {
959        // Unlike zcashd, in Zebra the miner reward is part of the sorting
960        coinbase_outputs.push((miner_reward, miner_address.create_script_from_address()));
961
962        // Zebra sorts by amount then script.
963        //
964        // Since the sort is stable, equal amounts will remain sorted by script.
965        coinbase_outputs.sort_by_key(|(_amount, script)| script.clone());
966        coinbase_outputs.sort_by_key(|(amount, _script)| *amount);
967    }
968
969    coinbase_outputs
970}
971
972// - Transaction roots processing
973
974/// Returns the default block roots for the supplied coinbase and mempool transactions,
975/// and the supplied history tree.
976///
977/// This function runs expensive cryptographic operations.
978pub fn calculate_default_root_hashes(
979    coinbase_txn: &UnminedTx,
980    mempool_txs: &[VerifiedUnminedTx],
981    chain_history_root: ChainHistoryMmrRootHash,
982) -> DefaultRoots {
983    let (merkle_root, auth_data_root) = calculate_transaction_roots(coinbase_txn, mempool_txs);
984
985    let block_commitments_hash = if chain_history_root == [0; 32].into() {
986        [0; 32].into()
987    } else {
988        ChainHistoryBlockTxAuthCommitmentHash::from_commitments(
989            &chain_history_root,
990            &auth_data_root,
991        )
992    };
993
994    DefaultRoots {
995        merkle_root,
996        chain_history_root,
997        auth_data_root,
998        block_commitments_hash,
999    }
1000}
1001
1002/// Returns the transaction effecting and authorizing roots
1003/// for `coinbase_txn` and `mempool_txs`, which are used in the block header.
1004//
1005// TODO: should this be spawned into a cryptographic operations pool?
1006//       (it would only matter if there were a lot of small transactions in a block)
1007pub fn calculate_transaction_roots(
1008    coinbase_txn: &UnminedTx,
1009    mempool_txs: &[VerifiedUnminedTx],
1010) -> (merkle::Root, AuthDataRoot) {
1011    let block_transactions =
1012        || iter::once(coinbase_txn).chain(mempool_txs.iter().map(|tx| &tx.transaction));
1013
1014    let merkle_root = block_transactions().cloned().collect();
1015    let auth_data_root = block_transactions().cloned().collect();
1016
1017    (merkle_root, auth_data_root)
1018}