zebra_rpc/methods/types/
validate_address.rs

1//! Response type for the `validateaddress` RPC.
2
3use derive_getters::Getters;
4use derive_new::new;
5use jsonrpsee::core::RpcResult;
6use zebra_chain::{
7    parameters::{Network, NetworkKind},
8    primitives,
9};
10
11use crate::methods::types::validate_address;
12
13/// `validateaddress` response
14#[derive(
15    Clone, Default, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, Getters, new,
16)]
17pub struct ValidateAddressResponse {
18    /// Whether the address is valid.
19    ///
20    /// If not, this is the only property returned.
21    #[serde(rename = "isvalid")]
22    pub(crate) is_valid: bool,
23
24    /// The zcash address that has been validated.
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub(crate) address: Option<String>,
27
28    /// If the key is a script.
29    #[serde(rename = "isscript")]
30    #[serde(skip_serializing_if = "Option::is_none")]
31    #[getter(copy)]
32    pub(crate) is_script: Option<bool>,
33}
34
35impl ValidateAddressResponse {
36    /// Creates an empty response with `isvalid` of false.
37    pub fn invalid() -> Self {
38        Self::default()
39    }
40}
41
42/// Checks if a zcash transparent address of type P2PKH, P2SH or TEX is valid.
43/// Returns information about the given address if valid.
44pub fn validate_address(
45    network: Network,
46    raw_address: String,
47) -> RpcResult<ValidateAddressResponse> {
48    let Ok(address) = raw_address.parse::<zcash_address::ZcashAddress>() else {
49        return Ok(ValidateAddressResponse::invalid());
50    };
51
52    let address = match address.convert::<primitives::Address>() {
53        Ok(address) => address,
54        Err(err) => {
55            tracing::debug!(?err, "conversion error");
56            return Ok(ValidateAddressResponse::invalid());
57        }
58    };
59
60    // We want to match zcashd's behaviour
61    if !address.is_transparent() {
62        return Ok(ValidateAddressResponse::invalid());
63    }
64
65    // Testnet & regtest share format; only mainnet differs.
66    let addr_is_mainnet = matches!(address.network(), NetworkKind::Mainnet);
67    let net_is_mainnet = network.kind() == NetworkKind::Mainnet;
68
69    if addr_is_mainnet != net_is_mainnet {
70        tracing::info!(
71            ?network,
72            address_network = ?address.network(),
73            "invalid address in validateaddress RPC: Zebra's configured network must match address network"
74        );
75        return Ok(validate_address::ValidateAddressResponse::invalid());
76    }
77
78    Ok(validate_address::ValidateAddressResponse {
79        address: Some(raw_address),
80        is_valid: true,
81        is_script: Some(address.is_script_hash()),
82    })
83}