Skip to main content

zebra_rpc/config/
mining.rs

1//! Mining config
2
3use std::{collections::HashMap, ops::Deref};
4
5use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
6use serde_with::{serde_as, DisplayFromStr};
7
8use strum_macros::EnumIter;
9use zcash_address::ZcashAddress;
10use zcash_transparent::coinbase::{MAX_COINBASE_HEIGHT_LEN, MAX_COINBASE_SCRIPT_LEN};
11use zebra_chain::parameters::NetworkKind;
12
13/// The maximum length of the optional, arbitrary data in the script sig field of a coinbase tx.
14pub(crate) const MAX_MINER_DATA_LEN: usize = MAX_COINBASE_SCRIPT_LEN - MAX_COINBASE_HEIGHT_LEN;
15
16/// The marker Zebra prepends to the coinbase input of every block it builds.
17///
18/// The zebra emoji (`U+1F993`), 4 UTF-8 bytes.
19pub(crate) const ZEBRA_COINBASE_MARKER: &str = "🦓";
20
21/// Separates [`ZEBRA_COINBASE_MARKER`] from `extra_coinbase_data`. Present only when that is set.
22pub(crate) const ZEBRA_COINBASE_SEPARATOR: &str = ": ";
23
24/// The maximum length of the user-configurable `extra_coinbase_data`.
25///
26/// The coinbase data is the marker, separator, and user data in a single push, so the user
27/// portion is [`MAX_MINER_DATA_LEN`] minus the marker, separator, and the 2-byte `OP_PUSHDATA1`
28/// opcode (for pushes over 75 bytes).
29pub(crate) const MAX_USER_COINBASE_DATA_LEN: usize =
30    MAX_MINER_DATA_LEN - ZEBRA_COINBASE_MARKER.len() - ZEBRA_COINBASE_SEPARATOR.len() - 2;
31
32/// Mining configuration section.
33#[serde_as]
34#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
35#[serde(deny_unknown_fields, default)]
36pub struct Config {
37    /// Address for receiving miner subsidy and tx fees.
38    ///
39    /// Used in coinbase tx constructed in `getblocktemplate` RPC.
40    #[serde_as(as = "Option<DisplayFromStr>")]
41    pub miner_address: Option<ZcashAddress>,
42
43    /// Optional tag that Zebra appends to the coinbase input of every block it builds, after the
44    /// Zebra `🦓` marker and a `: ` separator.
45    ///
46    /// Limited to `MAX_USER_COINBASE_DATA_LEN` bytes.
47    pub extra_coinbase_data: Option<ExtraCoinbaseData>,
48
49    /// Optional shielded memo that Zebra will include in the output of a shielded coinbase
50    /// transaction. Limited to 512 bytes.
51    ///
52    /// Applies only if [`Self::miner_address`] contains a shielded component.
53    pub miner_memo: Option<String>,
54
55    /// Mine blocks using Zebra's internal miner, without an external mining pool or equihash solver.
56    ///
57    /// This experimental feature is only supported on regtest as it uses null solutions and skips checking
58    /// for a valid Proof of Work.
59    ///
60    /// The internal miner is off by default.
61    #[serde(default)]
62    pub internal_miner: bool,
63}
64
65impl Config {
66    /// Is the internal miner enabled using at least one thread?
67    #[cfg(feature = "internal-miner")]
68    pub fn is_internal_miner_enabled(&self) -> bool {
69        // TODO: Changed to return always false so internal miner is never started. Part of https://github.com/ZcashFoundation/zebra/issues/8180
70        // Find the removed code at https://github.com/ZcashFoundation/zebra/blob/v1.5.1/zebra-rpc/src/config/mining.rs#L83
71        // Restore the code when conditions are met. https://github.com/ZcashFoundation/zebra/issues/8183
72        self.internal_miner
73    }
74}
75
76/// Operator-configured data appended to the coinbase input of every block Zebra builds, after
77/// Zebra's `🦓` marker and `: ` separator.
78///
79/// Validated on construction to fit within the coinbase data budget, so an oversized value can't
80/// be represented — and an oversized `mining.extra_coinbase_data` in the config makes Zebra fail
81/// to start.
82#[derive(Clone, Debug, Eq, PartialEq)]
83pub struct ExtraCoinbaseData(String);
84
85impl Deref for ExtraCoinbaseData {
86    type Target = str;
87
88    fn deref(&self) -> &Self::Target {
89        &self.0
90    }
91}
92
93/// The error returned when [`ExtraCoinbaseData`] is constructed from too many bytes.
94#[derive(Clone, Debug, thiserror::Error)]
95#[error("extra_coinbase_data is {0} bytes, but the maximum is {MAX_USER_COINBASE_DATA_LEN}")]
96pub struct ExtraCoinbaseDataTooLong(usize);
97
98impl TryFrom<String> for ExtraCoinbaseData {
99    type Error = ExtraCoinbaseDataTooLong;
100
101    fn try_from(data: String) -> Result<Self, Self::Error> {
102        if data.len() > MAX_USER_COINBASE_DATA_LEN {
103            Err(ExtraCoinbaseDataTooLong(data.len()))
104        } else {
105            Ok(Self(data))
106        }
107    }
108}
109
110impl<'de> Deserialize<'de> for ExtraCoinbaseData {
111    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
112    where
113        D: Deserializer<'de>,
114    {
115        Self::try_from(String::deserialize(deserializer)?).map_err(de::Error::custom)
116    }
117}
118
119impl Serialize for ExtraCoinbaseData {
120    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
121    where
122        S: Serializer,
123    {
124        self.0.serialize(serializer)
125    }
126}
127
128/// The desired address type for the `mining.miner_address` field in the config.
129#[derive(EnumIter, Eq, PartialEq, Default, Hash)]
130pub enum MinerAddressType {
131    /// A unified address, containing the components of all the other address types.
132    Unified,
133    /// A Sapling address.
134    Sapling,
135    /// A transparent address.
136    #[default]
137    Transparent,
138}
139
140/// Returns the hard-coded default miner address string for a given network and address type.
141///
142/// All addresses come from a single address:
143///
144/// - addresses for different networks are only different encodings of the same address;
145/// - addresses of different types are components of the same unified address.
146pub fn default_miner_address(kind: NetworkKind, addr_type: &MinerAddressType) -> &'static str {
147    MINER_ADDRESS[&kind][addr_type]
148}
149
150lazy_static::lazy_static! {
151    static ref MINER_ADDRESS: HashMap<NetworkKind, HashMap<MinerAddressType, &'static str>> = [
152        (NetworkKind::Mainnet, [
153            (MinerAddressType::Unified, "u1cymdny2u2vllkx7t5jnelp0kde0dgnwu0jzmggzguxvxj6fe7gpuqehywejndlrjwgk9snr6g69azs8jfet78s9zy60uepx6tltk7ee57jlax49dezkhkgvjy2puuue6dvaevt53nah7t2cc2k4p0h0jxmlu9sx58m2xdm5f9sy2n89jdf8llflvtml2ll43e334avu2fwytuna404a"),
154            // (MinerAddressType::Orchard, "u1hmfjpqdxaec3mvqypl7fkqcy53u438csydljpuepsfs7jx6sjwyznuzlna8qsslj3tg6sn9ua4q653280aqv4m2fjd4csptwxq3fjpwy"),
155            (MinerAddressType::Sapling, "zs1xl84ekz6stprmvrp39s77mf9t953nqjndwlcjtzfrr3cgjjez87639xm4u9pfuvylrhec3uryy5"),
156            (MinerAddressType::Transparent, "t1T92bmyoPM7PTSWUnaWnLGcxkF6Jp1AwMY"),
157        ].into()),
158        (NetworkKind::Testnet, [
159            (MinerAddressType::Unified, "utest10a8k6aw5w33kvyt7x6fryzu7vvsjru5vgcfnvr288qx2zm6p63ygcajtaze0px08t583dyrgr42vasazjhhnntus2tqrpkzu0dm2l4cgf3ld6wdqdrf3jv8mvfx9c80e73syer9l2wlgawjtf7yvj0eqwdf354trtelxnr0fhpw9792eaf49ghstkyftc9lwqqwy4ye0cleagp4nzyt"),
160            // (MinerAddressType::Orchard, "utest10zg6frxk32ma8980kdv9473e4aclw7clq9hydzcj6l349pkqzxk2mmj3cn7j5x38w6l4wyryv50whnlrw0k9agzpdf5fxyj7kq96ukcp"),
161            (MinerAddressType::Sapling, "ztestsapling1xl84ekz6stprmvrp39s77mf9t953nqjndwlcjtzfrr3cgjjez87639xm4u9pfuvylrhecet38rq"),
162            (MinerAddressType::Transparent, "tmJymvcUCn1ctbghvTJpXBwHiMEB8P6wxNV"),
163        ].into()),
164        (NetworkKind::Regtest, [
165            (MinerAddressType::Unified, "uregtest1efxggx6lduhm2fx5lnrhxv7h7kpztlpa3ahf3n4w0q0zj5epj4av9xjq6ljsja3xk8z7rzd067kc7mgpy9448rdfzpfjz5gq389zdmpgnk6rp4ykk0xk6cmqw6zqcrnmsuaxv3yzsvcwsd4gagtalh0uzrdvy03nhmltjz2eu0232qlcs0zvxuqyut73yucd9gy5jaudnyt7yqhgpqv"),
166            // (MinerAddressType::Orchard, "uregtest1pszqlgxaf5w8mu2yd9uygg8cswp0ec4f7eejqnqc35tztw4tk0sxnt3pym2f3s2872cy2ruuc5n8y9cen5q6ngzlmzu8ztrjesv8zm9j"),
167            (MinerAddressType::Sapling, "zregtestsapling1xl84ekz6stprmvrp39s77mf9t953nqjndwlcjtzfrr3cgjjez87639xm4u9pfuvylrhecx0c2j8"),
168            (MinerAddressType::Transparent, "tmJymvcUCn1ctbghvTJpXBwHiMEB8P6wxNV"),
169        ].into()),
170    ].into();
171}