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
#![doc = include_str!("../README.md")]
//! # Usage
//! The main interface for the crate is [`CryptoEncode`]. This trait provides methods for
//! generating address and WIFs (serialized private keys). It is implemented for all types that
//! implement `AsRef<[u8]>`.
//!
//! ### Generating Addresses
//! Addresses are generated using [`CryptoEncode::addr`]
//!
//! ```
//! use crypto_addr::{CryptoEncode, AddressOptions, Error};
//! use hex_literal::hex;
//!
//! const PUBLIC_KEY_BYTES: [u8; 33] =
//! hex!("029d08f65e6aac041328edaeddba9c682356f81b37b052728fd8637c14eddb7ff1");
//!
//! // Generate a Bitcoin P2PKH address
//! assert_eq!(
//! PUBLIC_KEY_BYTES.addr(&AddressOptions::Bitcoin).as_deref(),
//! Ok("14cWWeAYZmA9oGgLpjekay9xLwHew3Ni34")
//! );
//!
//! // Generate a Litcoin P2PKH address
//! assert_eq!(
//! PUBLIC_KEY_BYTES.addr(&AddressOptions::Litecoin).as_deref(),
//! Ok("LNqTmrUNeRQD45NVzse3rzDiZ9ew7HMYDY")
//! );
//!
//! // Generate a Dogecoin P2PKH address
//! assert_eq!(
//! PUBLIC_KEY_BYTES.addr(&AddressOptions::Dogecoin).as_deref(),
//! Ok("D8kc3u7BsB4SLGrwZKeK8jKZE51xKNc3FF")
//! );
//!
//! // Generate a Bitcoin Cash P2PKH address with elided "bitcoincash" prefix
//! assert_eq!(
//! PUBLIC_KEY_BYTES.addr(&AddressOptions::BitcoinCash(None)).as_deref(),
//! Ok("qqnelysjjjyrn4eywr04vf4v9nydrqdk6vep2tp2g9")
//! );
//!
//! // Generate a Bitcoin Cash P2PKH address with a custom prefix
//! assert_eq!(
//! PUBLIC_KEY_BYTES.addr(&AddressOptions::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(&AddressOptions::Bitcoin).unwrap_err(),
//! Error::InvalidLength{expected: 33, received: 30}
//! );
//! ```
//! ### Generating WIFs
//! WIFs are generated using [`CryptoEncode::wif`]
//! ```
//! use crypto_addr::{CryptoEncode, WIFOptions};
//!
//! 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(WIFOptions::Dogecoin).expect("Could not serialize sigkey data");
//! assert_eq!(wif_string, "QWWXnSvkQ948FxJfGj4hBtkWjTsrHZYjVpMRMBwqogP9NWvJUzFm");
//! ```
//! )
use std::fmt;
type Result<T> = std::result::Result<T, Error>;
mod address;
pub use address::AddressOptions;
mod wif;
pub use wif::WIFOptions;
/// Encode a sequence of bytes as a WIF or an Address
///
/// Provides the main serialization functionality for the crate. Provided methods implement
/// serialization an arbitrary sequence of bytes into either an Address or a WIF. Trait is
/// implemented for all types which expose a sequence of bytes via `AsRef<[u8]>`.
pub trait CryptoEncode: AsRef<[u8]> {
/// Encode input bytes as an Address.
///
/// How the input bytes are interpreted and formatted is determined by `opts`. See
/// [`AddressOptions`] for details
fn addr(&self, opts: &AddressOptions) -> Result<String> {
opts.serialize(self)
}
/// Encode private key as a WIF
///
/// Input bytes are interpreted as a private key. Output contains a String representing the
/// private key information according to the given `opts`.
fn wif(&self, opts: WIFOptions) -> Result<String> {
opts.serialize(self)
}
}
/// Encode is implemented for all types which expose a sequence of bytes via `AsRef<[u8]>`
impl<T: AsRef<[u8]>> CryptoEncode for T {}
#[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::{AddressOptions, CryptoEncode, WIFOptions};
#[test]
fn serialize_addr() {
use crate::test_vectors::addr::*;
for tc in BTC_TEST_VECTORS.lines().map(TestCase::from) {
assert_eq!(
tc.pubkey.addr(&AddressOptions::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(WIFOptions::Bitcoin).as_deref(), Ok(tc.wif));
}
}
}