#address #zcash #unified #sapling #encoded-string

no-std zcash_address

Zcash address parsing and serialization

12 releases (6 breaking)

new 0.6.2 Dec 13, 2024
0.6.0 Oct 2, 2024
0.3.2 Mar 6, 2024
0.3.0 Jun 6, 2023
0.0.0 Mar 7, 2021

#519 in Magic Beans

Download history 2536/week @ 2024-08-28 1983/week @ 2024-09-04 1685/week @ 2024-09-11 1464/week @ 2024-09-18 3090/week @ 2024-09-25 2639/week @ 2024-10-02 1536/week @ 2024-10-09 1477/week @ 2024-10-16 1873/week @ 2024-10-23 1647/week @ 2024-10-30 1953/week @ 2024-11-06 2754/week @ 2024-11-13 2270/week @ 2024-11-20 1874/week @ 2024-11-27 2487/week @ 2024-12-04 2228/week @ 2024-12-11

9,460 downloads per month
Used in 25 crates (11 directly)

MIT/Apache

785KB
10K SLoC

zcash_address

Zcash address parsing and serialization. This library allows its users to easily recognize and give good error messages for new Zcash address types.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.


lib.rs:

Parser for all defined Zcash address types.

This crate implements address parsing as a two-phase process, built around the opaque ZcashAddress type.

        s.parse()              .convert()
        -------->              --------->
Strings           ZcashAddress            Custom types
        <--------              <---------
        .encode()              ToAddress

It is important to note that this crate does not depend on any of the Zcash protocol crates (e.g. sapling-crypto or orchard). This crate has minimal dependencies by design; it focuses solely on parsing, handling those concerns for you, while exposing APIs that enable you to convert the parsed data into the Rust types you want to use.

Using this crate

I just need to validate Zcash addresses

fn is_valid_zcash_address(addr_string: &str) -> bool {
    addr_string.parse::<ZcashAddress>().is_ok()
}

I want to parse Zcash addresses in a Rust wallet app that uses the zcash_primitives transaction builder

Use zcash_client_backend::address::RecipientAddress, which implements the traits in this crate to parse address strings into protocol types that work with the transaction builder in the zcash_primitives crate (as well as the wallet functionality in the zcash_client_backend crate itself).

We intend to refactor the key and address types from the zcash_client_backend and zcash_primitives crates into a separate crate focused on dealing with Zcash key material. That crate will then be what you should use.

I want to parse Unified Addresses

See the unified::Address documentation for examples.

While the unified::Address type does have parsing methods, you should still parse your address strings with ZcashAddress and then convert; this will ensure that for other Zcash address types you get a ConversionError::Unsupported, which is a better error for your users.

I want to parse mainnet Zcash addresses in a language that supports C FFI

As an example, you could use static functions to create the address types in the target language from the parsed data.

use std::ffi::{CStr, c_char, c_void};
use std::ptr;

use zcash_address::{ConversionError, Network, TryFromRawAddress, ZcashAddress};

// Functions that return a pointer to a heap-allocated address of the given kind in
// the target language. These should be augmented to return any relevant errors.
extern {
    fn addr_from_sapling(data: *const u8) -> *mut c_void;
    fn addr_from_transparent_p2pkh(data: *const u8) -> *mut c_void;
}

struct ParsedAddress(*mut c_void);

impl TryFromRawAddress for ParsedAddress {
    type Error = &'static str;

    fn try_from_raw_sapling(
        data: [u8; 43],
    ) -> Result<Self, ConversionError<Self::Error>> {
        let parsed = unsafe { addr_from_sapling(data[..].as_ptr()) };
        if parsed.is_null() {
            Err("Reason for the failure".into())
        } else {
            Ok(Self(parsed))
        }
    }

    fn try_from_raw_transparent_p2pkh(
        data: [u8; 20],
    ) -> Result<Self, ConversionError<Self::Error>> {
        let parsed = unsafe { addr_from_transparent_p2pkh(data[..].as_ptr()) };
        if parsed.is_null() {
            Err("Reason for the failure".into())
        } else {
            Ok(Self(parsed))
        }
    }
}

pub extern "C" fn parse_zcash_address(encoded: *const c_char) -> *mut c_void {
    let encoded = unsafe { CStr::from_ptr(encoded) }.to_str().expect("valid");

    let addr = match ZcashAddress::try_from_encoded(encoded) {
        Ok(addr) => addr,
        Err(e) => {
            // This was either an invalid address encoding, or not a Zcash address.
            // You should pass this error back across the FFI.
            return ptr::null_mut();
        }
    };

    match addr.convert_if_network::<ParsedAddress>(Network::Main) {
        Ok(parsed) => parsed.0,
        Err(e) => {
            // We didn't implement all of the methods of `TryFromRawAddress`, so if an
            // address with one of those kinds is parsed, it will result in an error
            // here that should be passed back across the FFI.
            ptr::null_mut()
        }
    }
}

Dependencies

~1.4–2.2MB
~47K SLoC