zcash_address: Improve documentation
This commit is contained in:
parent
6570116384
commit
22271145a7
|
@ -2,7 +2,7 @@ use std::{error::Error, fmt};
|
|||
|
||||
use crate::{kind::*, AddressKind, Network, ZcashAddress};
|
||||
|
||||
/// An address type is not supported for conversion.
|
||||
/// An error indicating that an address type is not supported for conversion.
|
||||
#[derive(Debug)]
|
||||
pub struct UnsupportedAddress(&'static str);
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Implementation of [ZIP 316](https://zips.z.cash/zip-0316) Unified Addresses and Viewing Keys.
|
||||
|
||||
use bech32::{self, FromBase32, ToBase32, Variant};
|
||||
use std::cmp;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
@ -17,12 +19,23 @@ pub use ivk::{Ivk, Uivk};
|
|||
|
||||
const PADDING_LEN: usize = 16;
|
||||
|
||||
/// The known Receiver and Viewing Key types.
|
||||
///
|
||||
/// The typecodes `0xFFFA..=0xFFFF` reserved for experiments are currently not
|
||||
/// distinguished from unknown values, and will be parsed as [`Typecode::Unknown`].
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum Typecode {
|
||||
/// A transparent P2PKH address, FVK, or IVK encoding as specified in [ZIP 316](https://zips.z.cash/zip-0316).
|
||||
P2pkh,
|
||||
/// A transparent P2SH address.
|
||||
///
|
||||
/// This typecode cannot occur in a [`Ufvk`] or [`Uivk`].
|
||||
P2sh,
|
||||
/// A Sapling raw address, FVK, or IVK encoding as specified in [ZIP 316](https://zips.z.cash/zip-0316).
|
||||
Sapling,
|
||||
/// An Orchard raw address, FVK, or IVK encoding as specified in [ZIP 316](https://zips.z.cash/zip-0316).
|
||||
Orchard,
|
||||
/// An unknown or experimental typecode.
|
||||
Unknown(u32),
|
||||
}
|
||||
|
||||
|
|
|
@ -55,6 +55,49 @@ impl SealedItem for Receiver {
|
|||
}
|
||||
|
||||
/// A Unified Address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use std::convert::Infallible;
|
||||
/// # use std::error::Error;
|
||||
/// use zcash_address::{
|
||||
/// unified::{self, Container, Encoding},
|
||||
/// ConversionError, TryFromRawAddress, ZcashAddress,
|
||||
/// };
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// # let address_from_user = || "u1pg2aaph7jp8rpf6yhsza25722sg5fcn3vaca6ze27hqjw7jvvhhuxkpcg0ge9xh6drsgdkda8qjq5chpehkcpxf87rnjryjqwymdheptpvnljqqrjqzjwkc2ma6hcq666kgwfytxwac8eyex6ndgr6ezte66706e3vaqrd25dzvzkc69kw0jgywtd0cmq52q5lkw6uh7hyvzjse8ksx";
|
||||
/// let example_ua: &str = address_from_user();
|
||||
///
|
||||
/// // We can parse this directly as a `unified::Address`:
|
||||
/// let (network, ua) = unified::Address::decode(example_ua)?;
|
||||
///
|
||||
/// // Or we can parse via `ZcashAddress` (which you should do):
|
||||
/// struct MyUnifiedAddress(unified::Address);
|
||||
/// impl TryFromRawAddress for MyUnifiedAddress {
|
||||
/// // In this example we aren't checking the validity of the
|
||||
/// // inner Unified Address, but your code should do so!
|
||||
/// type Error = Infallible;
|
||||
///
|
||||
/// fn try_from_raw_unified(ua: unified::Address) -> Result<Self, ConversionError<Self::Error>> {
|
||||
/// Ok(MyUnifiedAddress(ua))
|
||||
/// }
|
||||
/// }
|
||||
/// let addr: ZcashAddress = example_ua.parse()?;
|
||||
/// let parsed = addr.convert_if_network::<MyUnifiedAddress>(network)?;
|
||||
/// assert_eq!(parsed.0, ua);
|
||||
///
|
||||
/// // We can obtain the receivers for the UA in preference order
|
||||
/// // (the order in which wallets should prefer to use them):
|
||||
/// let receivers: Vec<unified::Receiver> = ua.items();
|
||||
///
|
||||
/// // And we can create the UA from a list of receivers:
|
||||
/// let new_ua = unified::Address::try_from_items(receivers)?;
|
||||
/// assert_eq!(new_ua, ua);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Address(pub(crate) Vec<Receiver>);
|
||||
|
||||
|
|
|
@ -78,6 +78,30 @@ impl SealedItem for Fvk {
|
|||
}
|
||||
|
||||
/// A Unified Full Viewing Key.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use std::error::Error;
|
||||
/// use zcash_address::unified::{self, Container, Encoding};
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// # let ufvk_from_user = || "uview1cgrqnry478ckvpr0f580t6fsahp0a5mj2e9xl7hv2d2jd4ldzy449mwwk2l9yeuts85wjls6hjtghdsy5vhhvmjdw3jxl3cxhrg3vs296a3czazrycrr5cywjhwc5c3ztfyjdhmz0exvzzeyejamyp0cr9z8f9wj0953fzht0m4lenk94t70ruwgjxag2tvp63wn9ftzhtkh20gyre3w5s24f6wlgqxnjh40gd2lxe75sf3z8h5y2x0atpxcyf9t3em4h0evvsftluruqne6w4sm066sw0qe5y8qg423grple5fftxrqyy7xmqmatv7nzd7tcjadu8f7mqz4l83jsyxy4t8pkayytyk7nrp467ds85knekdkvnd7hqkfer8mnqd7pv";
|
||||
/// let example_ufvk: &str = ufvk_from_user();
|
||||
///
|
||||
/// let (network, ufvk) = unified::Ufvk::decode(example_ufvk)?;
|
||||
///
|
||||
/// // We can obtain the pool-specific Full Viewing Keys for the UFVK in preference
|
||||
/// // order (the order in which wallets should prefer to use their corresponding
|
||||
/// // address receivers):
|
||||
/// let fvks: Vec<unified::Fvk> = ufvk.items();
|
||||
///
|
||||
/// // And we can create the UFVK from a list of FVKs:
|
||||
/// let new_ufvk = unified::Ufvk::try_from_items(fvks)?;
|
||||
/// assert_eq!(new_ufvk, ufvk);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Ufvk(pub(crate) Vec<Fvk>);
|
||||
|
||||
|
|
|
@ -83,6 +83,30 @@ impl SealedItem for Ivk {
|
|||
}
|
||||
|
||||
/// A Unified Incoming Viewing Key.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use std::error::Error;
|
||||
/// use zcash_address::unified::{self, Container, Encoding};
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// # let uivk_from_user = || "uivk1djetqg3fws7y7qu5tekynvcdhz69gsyq07ewvppmzxdqhpfzdgmx8urnkqzv7ylz78ez43ux266pqjhecd59fzhn7wpe6zarnzh804hjtkyad25ryqla5pnc8p5wdl3phj9fczhz64zprun3ux7y9jc08567xryumuz59rjmg4uuflpjqwnq0j0tzce0x74t4tv3gfjq7nczkawxy6y7hse733ae3vw7qfjd0ss0pytvezxp42p6rrpzeh6t2zrz7zpjk0xhngcm6gwdppxs58jkx56gsfflugehf5vjlmu7vj3393gj6u37wenavtqyhdvcdeaj86s6jczl4zq";
|
||||
/// let example_uivk: &str = uivk_from_user();
|
||||
///
|
||||
/// let (network, uivk) = unified::Uivk::decode(example_uivk)?;
|
||||
///
|
||||
/// // We can obtain the pool-specific Incoming Viewing Keys for the UIVK in
|
||||
/// // preference order (the order in which wallets should prefer to use their
|
||||
/// // corresponding address receivers):
|
||||
/// let ivks: Vec<unified::Ivk> = uivk.items();
|
||||
///
|
||||
/// // And we can create the UIVK from a list of IVKs:
|
||||
/// let new_uivk = unified::Uivk::try_from_items(ivks)?;
|
||||
/// assert_eq!(new_uivk, uivk);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Uivk(pub(crate) Vec<Ivk>);
|
||||
|
||||
|
|
|
@ -1,3 +1,129 @@
|
|||
//! *Parser for all defined Zcash address types.*
|
||||
//!
|
||||
//! This crate implements address parsing as a two-phase process, built around the opaque
|
||||
//! [`ZcashAddress`] type.
|
||||
//!
|
||||
//! - [`ZcashAddress`] can be parsed from, and encoded to, strings.
|
||||
//! - [`ZcashAddress::convert`] or [`ZcashAddress::convert_if_network`] can be used to
|
||||
//! convert a parsed address into custom types that implement the [`TryFromAddress`] or
|
||||
//! [`TryFromRawAddress`] traits.
|
||||
//! - Custom types can be converted into a [`ZcashAddress`] via its implementation of the
|
||||
//! [`ToAddress`] trait.
|
||||
//!
|
||||
//! ```text
|
||||
//! 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
|
||||
//!
|
||||
//! ```
|
||||
//! # use zcash_address::ZcashAddress;
|
||||
//! 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()
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
// Catch documentation errors caused by code changes.
|
||||
|
|
Loading…
Reference in New Issue