use std::path::PathBuf;
use zaino_common::{Network, ServiceConfig, StorageConfig};
use zcash_address::ZcashAddress;
#[derive(Clone, Debug)]
pub struct DonationAddress(ZcashAddress);
impl DonationAddress {
pub(crate) fn try_from_encoded(s: &str) -> Result<Self, zcash_address::ParseError> {
ZcashAddress::try_from_encoded(s).map(DonationAddress)
}
pub(crate) fn encode(&self) -> String {
self.0.encode()
}
}
impl<'de> serde::Deserialize<'de> for DonationAddress {
fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
let s = String::deserialize(d)?;
Self::try_from_encoded(&s).map_err(serde::de::Error::custom)
}
}
impl serde::Serialize for DonationAddress {
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
s.serialize_str(&self.0.encode())
}
}
impl std::fmt::Display for DonationAddress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0.encode())
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "lowercase")]
pub enum BackendType {
State,
#[default]
Fetch,
}
#[derive(Debug, Clone)]
#[allow(deprecated)]
pub enum BackendConfig {
State(StateServiceConfig),
Fetch(FetchServiceConfig),
}
#[derive(Debug, Clone)]
pub struct CommonBackendConfig {
pub validator_rpc_address: String,
pub validator_cookie_path: Option<PathBuf>,
pub validator_rpc_user: String,
pub validator_rpc_password: String,
pub service: ServiceConfig,
pub storage: StorageConfig,
pub network: Network,
pub donation_address: Option<DonationAddress>,
pub indexer_version: String,
}
#[derive(Debug, Clone)]
pub struct StateServiceConfig {
pub common: CommonBackendConfig,
pub validator_state_config: zebra_state::Config,
pub validator_grpc_address: std::net::SocketAddr,
pub validator_cookie_auth: bool,
}
#[allow(deprecated)]
impl StateServiceConfig {
#[allow(clippy::too_many_arguments)]
pub fn new(
validator_state_config: zebra_state::Config,
validator_rpc_address: String,
validator_grpc_address: std::net::SocketAddr,
validator_cookie_auth: bool,
validator_cookie_path: Option<PathBuf>,
validator_rpc_user: Option<String>,
validator_rpc_password: Option<String>,
service: ServiceConfig,
storage: StorageConfig,
network: Network,
donation_address: Option<DonationAddress>,
) -> Self {
tracing::trace!(
"State service expecting NU activations:\n{:?}",
network.to_zebra_network().full_activation_list()
);
StateServiceConfig {
common: CommonBackendConfig {
validator_rpc_address,
validator_cookie_path,
validator_rpc_user: validator_rpc_user.unwrap_or("xxxxxx".to_string()),
validator_rpc_password: validator_rpc_password.unwrap_or("xxxxxx".to_string()),
service,
storage,
network,
donation_address,
indexer_version: env!("CARGO_PKG_VERSION").to_string(),
},
validator_state_config,
validator_grpc_address,
validator_cookie_auth,
}
}
}
#[derive(Debug, Clone)]
#[deprecated]
pub struct FetchServiceConfig {
pub common: CommonBackendConfig,
}
#[allow(deprecated)]
impl FetchServiceConfig {
#[allow(clippy::too_many_arguments)]
pub fn new(
validator_rpc_address: String,
validator_cookie_path: Option<PathBuf>,
validator_rpc_user: Option<String>,
validator_rpc_password: Option<String>,
service: ServiceConfig,
storage: StorageConfig,
network: Network,
donation_address: Option<DonationAddress>,
) -> Self {
FetchServiceConfig {
common: CommonBackendConfig {
validator_rpc_address,
validator_cookie_path,
validator_rpc_user: validator_rpc_user.unwrap_or("xxxxxx".to_string()),
validator_rpc_password: validator_rpc_password.unwrap_or("xxxxxx".to_string()),
service,
storage,
network,
donation_address,
indexer_version: env!("CARGO_PKG_VERSION").to_string(),
},
}
}
}
#[derive(Debug, Clone)]
pub struct BlockCacheConfig {
pub storage: StorageConfig,
pub db_version: u32,
pub network: Network,
}
impl BlockCacheConfig {
#[allow(dead_code)]
pub fn new(storage: StorageConfig, db_version: u32, network: Network, _no_sync: bool) -> Self {
BlockCacheConfig {
storage,
db_version,
network,
}
}
}
impl From<CommonBackendConfig> for BlockCacheConfig {
fn from(value: CommonBackendConfig) -> Self {
Self {
storage: value.storage,
db_version: 1,
network: value.network,
}
}
}
#[allow(deprecated)]
impl From<StateServiceConfig> for BlockCacheConfig {
fn from(value: StateServiceConfig) -> Self {
value.common.into()
}
}
#[allow(deprecated)]
impl From<FetchServiceConfig> for BlockCacheConfig {
fn from(value: FetchServiceConfig) -> Self {
value.common.into()
}
}
#[cfg(test)]
mod tests {
use super::*;
mod donation_address {
use super::*;
use zcash_address::{unified::Encoding as _, ToAddress as _, ZcashAddress};
use zcash_protocol::consensus::NetworkType;
#[test]
fn valid_transparent_p2pkh() {
let encoded =
ZcashAddress::from_transparent_p2pkh(NetworkType::Main, [1u8; 20]).encode();
assert!(DonationAddress::try_from_encoded(&encoded).is_ok());
}
#[test]
fn valid_transparent_p2sh() {
let encoded =
ZcashAddress::from_transparent_p2sh(NetworkType::Main, [2u8; 20]).encode();
assert!(DonationAddress::try_from_encoded(&encoded).is_ok());
}
#[test]
fn valid_sapling() {
let encoded = ZcashAddress::from_sapling(NetworkType::Main, [3u8; 43]).encode();
assert!(DonationAddress::try_from_encoded(&encoded).is_ok());
}
#[test]
fn valid_unified_orchard() {
let (_network, ua) = zcash_address::unified::Address::decode(
"u1pg2aaph7jp8rpf6yhsza25722sg5fcn3vaca6ze27hqjw7jvvhhuxkpcg0ge9xh6drsgdkda8qjq5chpehkcpxf87rnjryjqwymdheptpvnljqqrjqzjwkc2ma6hcq666kgwfytxwac8eyex6ndgr6ezte66706e3vaqrd25dzvzkc69kw0jgywtd0cmq52q5lkw6uh7hyvzjse8ksx"
).unwrap();
let encoded = ZcashAddress::from_unified(NetworkType::Main, ua).encode();
assert!(DonationAddress::try_from_encoded(&encoded).is_ok());
}
#[test]
fn invalid_empty_string() {
assert!(DonationAddress::try_from_encoded("").is_err());
}
#[test]
fn invalid_arbitrary_text() {
assert!(DonationAddress::try_from_encoded("not_a_zcash_address").is_err());
}
#[test]
fn invalid_truncated_prefix() {
assert!(DonationAddress::try_from_encoded("t1abc").is_err());
}
#[test]
fn round_trip_transparent() {
let encoded =
ZcashAddress::from_transparent_p2pkh(NetworkType::Main, [5u8; 20]).encode();
assert_eq!(
DonationAddress::try_from_encoded(&encoded)
.unwrap()
.encode(),
encoded
);
}
#[test]
fn round_trip_sapling() {
let encoded = ZcashAddress::from_sapling(NetworkType::Main, [6u8; 43]).encode();
assert_eq!(
DonationAddress::try_from_encoded(&encoded)
.unwrap()
.encode(),
encoded
);
}
#[test]
fn round_trip_unified() {
let (_network, ua) = zcash_address::unified::Address::decode(
"u1pg2aaph7jp8rpf6yhsza25722sg5fcn3vaca6ze27hqjw7jvvhhuxkpcg0ge9xh6drsgdkda8qjq5chpehkcpxf87rnjryjqwymdheptpvnljqqrjqzjwkc2ma6hcq666kgwfytxwac8eyex6ndgr6ezte66706e3vaqrd25dzvzkc69kw0jgywtd0cmq52q5lkw6uh7hyvzjse8ksx"
).unwrap();
let encoded = ZcashAddress::from_unified(NetworkType::Main, ua).encode();
assert_eq!(
DonationAddress::try_from_encoded(&encoded)
.unwrap()
.encode(),
encoded
);
}
}
}