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, Clone)]
pub enum ValidatorConnectionType {
Rpc,
Direct(DirectConnectionConfig),
}
#[derive(Debug, Clone)]
pub struct DirectConnectionConfig {
pub validator_state_config: zebra_state::Config,
pub validator_grpc_address: std::net::SocketAddr,
pub validator_cookie_auth: bool,
}
#[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 ephemeral_finalised_state: bool,
pub network: Network,
pub donation_address: Option<DonationAddress>,
pub indexer_version: String,
}
impl CommonBackendConfig {
#[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,
ephemeral_finalised_state: bool,
network: Network,
donation_address: Option<DonationAddress>,
) -> Self {
CommonBackendConfig {
validator_rpc_address,
validator_cookie_path,
validator_rpc_user: validator_rpc_user.unwrap_or_else(|| "xxxxxx".to_string()),
validator_rpc_password: validator_rpc_password.unwrap_or_else(|| "xxxxxx".to_string()),
service,
storage,
ephemeral_finalised_state,
network,
donation_address,
indexer_version: env!("CARGO_PKG_VERSION").to_string(),
}
}
}
#[derive(Debug, Clone)]
pub struct NodeBackedIndexerServiceConfig {
pub common: CommonBackendConfig,
pub connection: ValidatorConnectionType,
}
impl NodeBackedIndexerServiceConfig {
#[allow(clippy::too_many_arguments)]
pub fn new_rpc(
validator_rpc_address: String,
validator_cookie_path: Option<PathBuf>,
validator_rpc_user: Option<String>,
validator_rpc_password: Option<String>,
service: ServiceConfig,
storage: StorageConfig,
ephemeral_finalised_state: bool,
network: Network,
donation_address: Option<DonationAddress>,
) -> Self {
NodeBackedIndexerServiceConfig {
common: CommonBackendConfig::new(
validator_rpc_address,
validator_cookie_path,
validator_rpc_user,
validator_rpc_password,
service,
storage,
ephemeral_finalised_state,
network,
donation_address,
),
connection: ValidatorConnectionType::Rpc,
}
}
#[allow(clippy::too_many_arguments)]
pub fn new_direct(
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,
ephemeral_finalised_state: bool,
network: Network,
donation_address: Option<DonationAddress>,
) -> Self {
NodeBackedIndexerServiceConfig {
common: CommonBackendConfig::new(
validator_rpc_address,
validator_cookie_path,
validator_rpc_user,
validator_rpc_password,
service,
storage,
ephemeral_finalised_state,
network,
donation_address,
),
connection: ValidatorConnectionType::Direct(DirectConnectionConfig {
validator_state_config,
validator_grpc_address,
validator_cookie_auth,
}),
}
}
}
#[derive(Debug, Clone)]
pub struct ChainIndexConfig {
pub storage: StorageConfig,
pub db_version: u32,
pub network: zebra_chain::parameters::Network,
pub ephemeral: bool,
}
impl ChainIndexConfig {
pub fn from_backend_config(
common: &CommonBackendConfig,
network: zebra_chain::parameters::Network,
) -> Self {
Self {
storage: common.storage.clone(),
db_version: 1,
network,
ephemeral: common.ephemeral_finalised_state,
}
}
}
#[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
);
}
}
}