1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")]
//! # Usage
//! The main interface for the crate is provided by the [`Address`] and [`WIF`] traits . These
//! traits provide methods for generating addresses and WIFs (serialized private keys)
//! respectively. Both traits are implemented for all types that implement `AsRef<[u8]>`.
use std::fmt;
type Result<T> = std::result::Result<T, Error>;
mod address;
pub use address::AddressFormat;
mod wif;
pub use wif::WIFFormat;
/// Generate an Address string from existing Public Key
///
/// This trait provides the main functionality of the crate. The provided `addr` method implements
/// encoding a public key as a P2PKH address. This trait is implemented for all types which expose
/// a sequence of bytes via `AsRef<[u8]>`.
///
/// ### Usage
/// Addresses are generated from public keys using [`Address::addr`]
///
/// ```
/// use crypto_addr::{Address, AddressFormat, Error};
/// use hex_literal::hex;
///
/// // This is a compressed public key in SEC 1 standard format
/// const PUBLIC_KEY_BYTES: [u8; 33] =
/// hex!("029d08f65e6aac041328edaeddba9c682356f81b37b052728fd8637c14eddb7ff1");
///
/// // Generate a Bitcoin P2PKH address
/// assert_eq!(
/// PUBLIC_KEY_BYTES.addr(&AddressFormat::Bitcoin).as_deref(),
/// Ok("14cWWeAYZmA9oGgLpjekay9xLwHew3Ni34")
/// );
///
/// // Generate a Litcoin P2PKH address
/// assert_eq!(
/// PUBLIC_KEY_BYTES.addr(&AddressFormat::Litecoin).as_deref(),
/// Ok("LNqTmrUNeRQD45NVzse3rzDiZ9ew7HMYDY")
/// );
///
/// // Generate a Dogecoin P2PKH address
/// assert_eq!(
/// PUBLIC_KEY_BYTES.addr(&AddressFormat::Dogecoin).as_deref(),
/// Ok("D8kc3u7BsB4SLGrwZKeK8jKZE51xKNc3FF")
/// );
///
/// // Generate a Bitcoin Cash P2PKH address with elided "bitcoincash" prefix
/// assert_eq!(
/// PUBLIC_KEY_BYTES.addr(&AddressFormat::BitcoinCash(None)).as_deref(),
/// Ok("qqnelysjjjyrn4eywr04vf4v9nydrqdk6vep2tp2g9")
/// );
///
/// // Generate a Bitcoin Cash P2PKH address with a custom prefix
/// assert_eq!(
/// PUBLIC_KEY_BYTES.addr(&AddressFormat::BitcoinCash(Some("foo".to_owned()))).as_deref(),
/// Ok("foo:qqnelysjjjyrn4eywr04vf4v9nydrqdk6v730zmhs6")
/// );
///
/// // Errors are detected
/// let partial_pubkey = &PUBLIC_KEY_BYTES[..30];
/// assert_eq!(
/// partial_pubkey.addr(&AddressFormat::Bitcoin).unwrap_err(),
/// Error::InvalidLength{expected: 33, received: 30}
/// );
/// ```
pub trait Address {
/// Derive a P2PKH address string from a public key (`self`). Parameters for the address format
/// are given by `opts`. Returns `Err` if public key is not able to derive an address, i.e. if
/// it is not the correct length.
fn addr(&self, opts: &AddressFormat) -> Result<String>;
}
/// `Address` is implemented for all types that expose a sequence of bytes via `AsRef<[u8]>`. In
/// this case, the input bytes are interpreted as a compressed public key encoded with encoding
/// described in [SEC 1: Elliptic Curve Cryptography (Version
/// 2.0)](http://www.secg.org/sec1-v2.pdf) section 2.3.3 (page 10).
impl<T: AsRef<[u8]>> Address for T {
fn addr(&self, opts: &AddressFormat) -> Result<String> {
opts.derive(self)
}
}
/// Encode a private key using WIF format
///
/// ### Usage
/// WIFs are generated using [`WIF::wif`]
/// ```
/// use crypto_addr::{WIF, WIFFormat};
///
/// let sigkey = b"\xeb\x904)e2\x8a\xe1\xe0\x8e\xfd\xeb+x\xbc2\xd0&/\x04-\xe8\xab\xcd]0\xd6\
/// \x07t\x9c\xb3\xaa";
/// let wif_string = sigkey.wif(WIFFormat::Dogecoin).expect("Could not serialize sigkey data");
/// assert_eq!(wif_string, "QWWXnSvkQ948FxJfGj4hBtkWjTsrHZYjVpMRMBwqogP9NWvJUzFm");
/// ```
pub trait WIF {
/// Encode private key bytes as a WIF using blockchain parameters given by `opts`
fn wif(&self, opts: WIFFormat) -> Result<String>;
}
/// WIF is implemented for all types which expose a sequence of bytes via `AsRef<[u8]>`. In this
/// case, the bytes are interpreted as the raw private key in big-endian format.
impl<T: AsRef<[u8]>> WIF for T {
fn wif(&self, opts: WIFFormat) -> Result<String> {
opts.encode(self)
}
}
#[derive(Debug, PartialEq, Eq)]
/// Error enum describing something that went wrong.
pub enum Error {
InvalidLength { received: usize, expected: usize },
ParseFailure(String),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidLength { received, expected } => write!(
f,
"Invalid length detected, expected {}, got {}",
received, expected
),
Self::ParseFailure(msg) => write!(f, "{}", msg),
}
}
}
impl std::error::Error for Error {}
#[cfg(test)]
mod test_vectors;
#[cfg(test)]
mod tests {
use crate::{Address, AddressFormat, WIFFormat, WIF};
#[test]
fn serialize_addr() {
use crate::test_vectors::addr::*;
for tc in BTC_TEST_VECTORS.lines().map(TestCase::from) {
assert_eq!(
tc.pubkey.addr(&AddressFormat::Bitcoin).as_deref(),
Ok(tc.addr)
)
}
}
#[test]
fn serialize_wif() {
use crate::test_vectors::wif::*;
for tc in BTC_TEST_VECTORS.lines().map(TestCase::from) {
assert_eq!(tc.prv.wif(WIFFormat::Bitcoin).as_deref(), Ok(tc.wif));
}
}
}