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));
        }
    }
}