use std::{collections::HashMap, ops::Deref};
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use serde_with::{serde_as, DisplayFromStr};
use strum_macros::EnumIter;
use zakura_chain::parameters::NetworkKind;
use zcash_address::ZcashAddress;
use zcash_transparent::coinbase::{MAX_COINBASE_HEIGHT_LEN, MAX_COINBASE_SCRIPT_LEN};
pub(crate) const MAX_MINER_DATA_LEN: usize = MAX_COINBASE_SCRIPT_LEN - MAX_COINBASE_HEIGHT_LEN;
pub(crate) const ZAKURA_COINBASE_MARKER: &str = "🌸";
pub(crate) const ZAKURA_COINBASE_SEPARATOR: &str = ": ";
pub(crate) const MAX_USER_COINBASE_DATA_LEN: usize =
MAX_MINER_DATA_LEN - ZAKURA_COINBASE_MARKER.len() - ZAKURA_COINBASE_SEPARATOR.len() - 2;
#[serde_as]
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
#[serde(deny_unknown_fields, default)]
pub struct Config {
#[serde_as(as = "Option<DisplayFromStr>")]
pub miner_address: Option<ZcashAddress>,
pub extra_coinbase_data: Option<ExtraCoinbaseData>,
pub miner_memo: Option<String>,
#[serde(default)]
pub internal_miner: bool,
}
impl Config {
#[cfg(feature = "internal-miner")]
pub fn is_internal_miner_enabled(&self) -> bool {
self.internal_miner
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ExtraCoinbaseData(String);
impl Deref for ExtraCoinbaseData {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Debug, thiserror::Error)]
#[error("extra_coinbase_data is {0} bytes, but the maximum is {MAX_USER_COINBASE_DATA_LEN}")]
pub struct ExtraCoinbaseDataTooLong(usize);
impl TryFrom<String> for ExtraCoinbaseData {
type Error = ExtraCoinbaseDataTooLong;
fn try_from(data: String) -> Result<Self, Self::Error> {
if data.len() > MAX_USER_COINBASE_DATA_LEN {
Err(ExtraCoinbaseDataTooLong(data.len()))
} else {
Ok(Self(data))
}
}
}
impl<'de> Deserialize<'de> for ExtraCoinbaseData {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Self::try_from(String::deserialize(deserializer)?).map_err(de::Error::custom)
}
}
impl Serialize for ExtraCoinbaseData {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.serialize(serializer)
}
}
#[derive(EnumIter, Eq, PartialEq, Default, Hash)]
pub enum MinerAddressType {
Unified,
Sapling,
#[default]
Transparent,
}
pub fn default_miner_address(kind: NetworkKind, addr_type: &MinerAddressType) -> &'static str {
MINER_ADDRESS[&kind][addr_type]
}
lazy_static::lazy_static! {
static ref MINER_ADDRESS: HashMap<NetworkKind, HashMap<MinerAddressType, &'static str>> = [
(NetworkKind::Mainnet, [
(MinerAddressType::Unified, "u1cymdny2u2vllkx7t5jnelp0kde0dgnwu0jzmggzguxvxj6fe7gpuqehywejndlrjwgk9snr6g69azs8jfet78s9zy60uepx6tltk7ee57jlax49dezkhkgvjy2puuue6dvaevt53nah7t2cc2k4p0h0jxmlu9sx58m2xdm5f9sy2n89jdf8llflvtml2ll43e334avu2fwytuna404a"),
(MinerAddressType::Sapling, "zs1xl84ekz6stprmvrp39s77mf9t953nqjndwlcjtzfrr3cgjjez87639xm4u9pfuvylrhec3uryy5"),
(MinerAddressType::Transparent, "t1T92bmyoPM7PTSWUnaWnLGcxkF6Jp1AwMY"),
].into()),
(NetworkKind::Testnet, [
(MinerAddressType::Unified, "utest10a8k6aw5w33kvyt7x6fryzu7vvsjru5vgcfnvr288qx2zm6p63ygcajtaze0px08t583dyrgr42vasazjhhnntus2tqrpkzu0dm2l4cgf3ld6wdqdrf3jv8mvfx9c80e73syer9l2wlgawjtf7yvj0eqwdf354trtelxnr0fhpw9792eaf49ghstkyftc9lwqqwy4ye0cleagp4nzyt"),
(MinerAddressType::Sapling, "ztestsapling1xl84ekz6stprmvrp39s77mf9t953nqjndwlcjtzfrr3cgjjez87639xm4u9pfuvylrhecet38rq"),
(MinerAddressType::Transparent, "tmJymvcUCn1ctbghvTJpXBwHiMEB8P6wxNV"),
].into()),
(NetworkKind::Regtest, [
(MinerAddressType::Unified, "uregtest1efxggx6lduhm2fx5lnrhxv7h7kpztlpa3ahf3n4w0q0zj5epj4av9xjq6ljsja3xk8z7rzd067kc7mgpy9448rdfzpfjz5gq389zdmpgnk6rp4ykk0xk6cmqw6zqcrnmsuaxv3yzsvcwsd4gagtalh0uzrdvy03nhmltjz2eu0232qlcs0zvxuqyut73yucd9gy5jaudnyt7yqhgpqv"),
(MinerAddressType::Sapling, "zregtestsapling1xl84ekz6stprmvrp39s77mf9t953nqjndwlcjtzfrr3cgjjez87639xm4u9pfuvylrhecx0c2j8"),
(MinerAddressType::Transparent, "tmJymvcUCn1ctbghvTJpXBwHiMEB8P6wxNV"),
].into()),
].into();
}