zebra_rpc/methods/types/
z_validate_address.rs

1//! Response type for the `z_validateaddress` RPC.
2
3use derive_getters::Getters;
4use derive_new::new;
5use jsonrpsee::core::RpcResult;
6
7use zebra_chain::{parameters::Network, primitives::Address};
8
9/// `z_validateaddress` response
10#[derive(
11    Clone, Default, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, Getters, new,
12)]
13pub struct ZValidateAddressResponse {
14    /// Whether the address is valid.
15    ///
16    /// If not, this is the only property returned.
17    #[serde(rename = "isvalid")]
18    pub(crate) is_valid: bool,
19
20    /// The zcash address that has been validated.
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub(crate) address: Option<String>,
23
24    /// The type of the address.
25    #[serde(skip_serializing_if = "Option::is_none")]
26    #[getter(copy)]
27    pub(crate) address_type: Option<ZValidateAddressType>,
28
29    /// Whether the address is yours or not.
30    ///
31    /// Always false for now since Zebra doesn't have a wallet yet.
32    #[serde(rename = "ismine")]
33    #[serde(skip_serializing_if = "Option::is_none")]
34    #[getter(copy)]
35    pub(crate) is_mine: Option<bool>,
36}
37
38impl ZValidateAddressResponse {
39    /// Creates an empty response with `isvalid` of false.
40    pub fn invalid() -> Self {
41        Self::default()
42    }
43}
44
45/// Address types supported by the `z_validateaddress` RPC according to
46/// <https://zcash.github.io/rpc/z_validateaddress.html>.
47#[derive(Copy, Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq, Eq)]
48#[serde(rename_all = "lowercase")]
49pub enum ZValidateAddressType {
50    /// The `p2pkh` address type.
51    P2pkh,
52    /// The `p2sh` address type.
53    P2sh,
54    /// The `sapling` address type.
55    Sapling,
56    /// The `unified` address type.
57    Unified,
58}
59
60impl From<&Address> for ZValidateAddressType {
61    fn from(address: &Address) -> Self {
62        match address {
63            Address::Transparent(_) => {
64                if address.is_script_hash() {
65                    Self::P2sh
66                } else {
67                    Self::P2pkh
68                }
69            }
70            Address::Sapling { .. } => Self::Sapling,
71            Address::Unified { .. } => Self::Unified,
72        }
73    }
74}
75
76/// Checks if a zcash address of type P2PKH, P2SH, TEX, SAPLING or UNIFIED is valid.
77/// Returns information about the given address if valid.
78pub fn z_validate_address(
79    network: Network,
80    raw_address: String,
81) -> RpcResult<ZValidateAddressResponse> {
82    let Ok(address) = raw_address.parse::<zcash_address::ZcashAddress>() else {
83        return Ok(ZValidateAddressResponse::invalid());
84    };
85
86    let address = match address.convert::<Address>() {
87        Ok(address) => address,
88        Err(err) => {
89            tracing::debug!(?err, "conversion error");
90            return Ok(ZValidateAddressResponse::invalid());
91        }
92    };
93
94    if address.network() == network.kind() {
95        Ok(ZValidateAddressResponse {
96            is_valid: true,
97            address: Some(raw_address),
98            address_type: Some(ZValidateAddressType::from(&address)),
99            is_mine: Some(false),
100        })
101    } else {
102        tracing::info!(
103            ?network,
104            address_network = ?address.network(),
105            "invalid address network in z_validateaddress RPC: address is for {:?} but Zebra is on {:?}",
106            address.network(),
107            network
108        );
109
110        Ok(ZValidateAddressResponse::invalid())
111    }
112}