wallet_gen/
bitcoin.rs

1/*
2 * bitcoin.rs
3 *
4 * Copyright 2018 Standard Mining
5 *
6 * Available to be used and modified under the terms of the MIT License.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
10 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
11 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
12 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
13 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14 */
15
16//! Various functions related to Bitcoin wallet generation and validation.
17
18use super::prelude::*;
19use base58::ToBase58;
20use hex_slice::HexSlice;
21use openssl::bn::BigNumContext;
22use openssl::ec::{EcGroup, EcKey, PointConversionForm};
23use openssl::hash::{hash, MessageDigest};
24use openssl::nid::Nid;
25use openssl::sha::sha256;
26use safemem::prepend;
27
28/// Generate a new wallet based on the Bitcoin style of producing [`Wallet`]s.
29/// For Bitcoin and Bitcoin variants.
30///
31/// [`Wallet`]: ../wallet/struct.Wallet.html
32pub fn new_wallet(coin: Coin) -> Result<Wallet> {
33    let bitcoin_data = match wif_data(coin) {
34        Some(data) => data,
35        None => return Err(Error::CoinNotSupported(coin)),
36    };
37
38    let group = EcGroup::from_curve_name(Nid::SECP256K1)?;
39    let mut bn_ctx = BigNumContext::new()?;
40    let key = EcKey::generate(&group)?;
41    let pub_key =
42        key.public_key()
43            .to_bytes(&group, PointConversionForm::UNCOMPRESSED, &mut bn_ctx)?;
44    let mut priv_key = key.private_key().to_vec();
45
46    let pub_hash = sha256(&pub_key);
47    let ripe_hash = hash(MessageDigest::ripemd160(), &pub_hash[..])?;
48    let mut address = ripe_hash.to_vec();
49
50    Ok(Wallet {
51        coin:        coin,
52        address:     base58_check(&mut address, bitcoin_data.network_version()),
53        public_key:  HexSlice::new(&pub_key).format(),
54        private_key: base58_check(&mut priv_key, &[bitcoin_data.private_key_prefix()]),
55        other:       None,
56    })
57}
58
59/// Performs a "base58 check", a modified base 58 byte conversion
60/// that appends a double SHA256 checksum.
61///
62/// This function modifies the buffer, prepending the
63/// "application/version" byte.
64pub fn base58_check(bytes: &mut Vec<u8>, prefix: &[u8]) -> String {
65    prepend(prefix, bytes);
66    let hash = sha256(bytes);
67    let checksum = &sha256(&hash[..])[..4];
68    bytes.extend(checksum.iter().cloned());
69    bytes.as_slice().to_base58()
70}
71
72/// Constants for generating addresses and private keys in Bitcoin-based
73/// coins, when converting to Wallet Import Format (WIF).
74#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
75pub struct BitcoinWifData(&'static [u8], u8, &'static str, &'static str);
76
77impl BitcoinWifData {
78    /// Gets the network version used in WIF conversion.
79    #[inline]
80    pub fn network_version(self) -> &'static [u8] { self.0 }
81
82    /// Gets the prefix to prepend when creating the WIF private key.
83    #[inline]
84    pub fn private_key_prefix(self) -> u8 { self.1 }
85
86    /// Returns the WIF character set for the beginning of the string.
87    #[inline]
88    pub fn wif_start(self) -> &'static str { self.2 }
89
90    /// Returns the CWIF character set for the beginning of the string.
91    #[inline]
92    pub fn cwif_start(self) -> &'static str { self.3 }
93
94    /// See if the given string matches the character set returned by `wif_start()`.
95    pub fn check_wif(self, s: &str) -> bool {
96        for c in self.wif_start().chars() {
97            if s.starts_with(c) {
98                return true;
99            }
100        }
101
102        false
103    }
104
105    /// See if the given string matches the character set returned by `cwif_start()`.
106    pub fn check_cwif(self, s: &str) -> bool {
107        for c in self.cwif_start().chars() {
108            if s.starts_with(c) {
109                return true;
110            }
111        }
112
113        false
114    }
115}
116
117/// Gets the constant data used to generate WIF addresses.
118#[allow(match_same_arms)]
119pub fn wif_data(coin: Coin) -> Option<BitcoinWifData> {
120    Some(match coin {
121        Coin::Bitcoin => BitcoinWifData(&[0x00], 0x80, "5", "LK"),
122        Coin::Testnet => BitcoinWifData(&[0x6f], 0xef, "9", "c"),
123        Coin::Litecoin => BitcoinWifData(&[0x30], 0xb0, "6", "T"),
124        Coin::Dogecoin => BitcoinWifData(&[0x1e], 0x9e, "6", "Q"),
125        Coin::Reddcoin => BitcoinWifData(&[0x3d], 0xbd, "7", "UV"),
126        Coin::Dash => BitcoinWifData(&[0x4c], 0xcc, "7", "X"),
127        Coin::Peercoin => BitcoinWifData(&[0x37], 0xb7, "7", "U"),
128        Coin::Namecoin => BitcoinWifData(&[0x34], 0x80, "5", "LK"),
129        Coin::Feathercoin => BitcoinWifData(&[0x0e], 0x8e, "5", "N"),
130        Coin::Blackcoin => BitcoinWifData(&[0x19], 0x99, "6", "P"),
131        Coin::NuBits => BitcoinWifData(&[0x19], 0xbf, "7", "V"),
132        Coin::Mazacoin => BitcoinWifData(&[0x32], 0xe0, "8", "a"),
133        Coin::Viacoin => BitcoinWifData(&[0x47], 0xc7, "7", "W"),
134        Coin::Rubycoin => BitcoinWifData(&[0x3c], 0xbc, "7", "U"),
135        Coin::Digitalcoin => BitcoinWifData(&[0x1e], 0x9e, "6", "Q"),
136        Coin::Cannacoin => BitcoinWifData(&[0x1c], 0x9c, "6", "Q"),
137        Coin::DigiByte => BitcoinWifData(&[0x1e], 0x9e, "6", "Q"),
138        Coin::Primecoin => BitcoinWifData(&[0x17], 0x97, "6", "P"),
139        Coin::Neoscoin => BitcoinWifData(&[0x35], 0xb1, "6", "T"),
140        Coin::Jumbucks => BitcoinWifData(&[0x2b], 0xab, "6", "S"),
141        Coin::Vertcoin => BitcoinWifData(&[0x47], 0x80, "5", "LK"),
142        Coin::MonetaryUnit => BitcoinWifData(&[0x10], 0x7e, "5", "K"),
143        Coin::CanadaeCoin => BitcoinWifData(&[0x1c], 0x9c, "6", "Q"),
144        Coin::ParkByte => BitcoinWifData(&[0x37], 0xb7, "7", "U"),
145        Coin::Pandacoin => BitcoinWifData(&[0x37], 0xb7, "7", "U"),
146        Coin::Particl => BitcoinWifData(&[0x38], 0x6c, "4", "HG"),
147        Coin::Novacoin => BitcoinWifData(&[0x08], 0x88, "5", "M"),
148        Coin::Bitcoindark => BitcoinWifData(&[0x3c], 0xbc, "7", "U"),
149        Coin::Syscoin => BitcoinWifData(&[0x00], 0x80, "5", "LK"),
150        Coin::Smileycoin => BitcoinWifData(&[0x19], 0x99, "6", "P"),
151        Coin::FujiCoin => BitcoinWifData(&[0x24], 0xa4, "6", "R"),
152        Coin::ElectronicGulden => BitcoinWifData(&[0x30], 0xb0, "6", "T"),
153        Coin::Potcoin => BitcoinWifData(&[0x37], 0xb7, "7", "U"),
154        Coin::Quarkcoin => BitcoinWifData(&[0x3a], 0xba, "7", "U"),
155        Coin::Terracoin => BitcoinWifData(&[0x00], 0x80, "5", "LK"),
156        Coin::Gridcoin => BitcoinWifData(&[0x3e], 0xbe, "7", "V"),
157        Coin::Auroracoin => BitcoinWifData(&[0x17], 0x97, "6", "T"),
158        Coin::Gulden => BitcoinWifData(&[0x26], 0xa6, "6", "R"),
159        Coin::Myriadcoin => BitcoinWifData(&[0x32], 0xb2, "6", "T"),
160        Coin::Unobtanium => BitcoinWifData(&[0x82], 0xe0, "8", "a"),
161        Coin::Stratis => BitcoinWifData(&[0x3f], 0xbf, "7", "V"),
162        Coin::MarsCoin => BitcoinWifData(&[0x32], 0xb2, "6", "T"),
163        Coin::Pesetacoin => BitcoinWifData(&[0x2f], 0xaf, "6", "ST"),
164        Coin::Pinkcoin => BitcoinWifData(&[0x03], 0x83, "RQP", "L"),
165        Coin::PiggyCoin => BitcoinWifData(&[0x76], 0xf6, "9", "d"),
166        Coin::Pivx => BitcoinWifData(&[0x1e], 0xd4, "8", "Y"),
167        Coin::BitZeny => BitcoinWifData(&[0x51], 0x80, "5", "LK"),
168        Coin::StealthCoin => BitcoinWifData(&[0x3e], 0xbe, "7", "V"),
169        Coin::Vcash => BitcoinWifData(&[0x47], 0xc7, "7", "W"),
170        Coin::NavCoin => BitcoinWifData(&[0x35], 0x96, "6", "P"),
171        Coin::Zcash => BitcoinWifData(&[0x1c, 0xb8], 0x80, "5", "LK"),
172        Coin::LBRYCredits => BitcoinWifData(&[0x55], 0x80, "5", "LK"),
173        Coin::Riecoin => BitcoinWifData(&[0x3c], 0x80, "5", "LK"),
174        Coin::BitcoinCash => BitcoinWifData(&[0x00], 0x80, "5", "LK"),
175        Coin::BitcoinGold => BitcoinWifData(&[0x26], 0x80, "5", "LK"),
176        Coin::Bitcore => BitcoinWifData(&[0x00], 0x80, "5", "LK"),
177        Coin::Ember => BitcoinWifData(&[0x5c], 0x32, "2", "8"),
178        Coin::HTMLCOIN => BitcoinWifData(&[0x29], 0xa9, "6", "S"),
179        Coin::MarteXcoin => BitcoinWifData(&[0x32], 0xb2, "6", "T"),
180        Coin::Omni => BitcoinWifData(&[0x73], 0xf3, "9", "cd"),
181        Coin::BoxyCoin => BitcoinWifData(&[0x4b], 0xcb, "7", "X"),
182        Coin::Blocknet => BitcoinWifData(&[0x1a], 0x9a, "6", "P"),
183        Coin::HOdlcoin => BitcoinWifData(&[0x28], 0xa8, "5", "LK"),
184        Coin::Axe => BitcoinWifData(&[0x4b], 0xcb, "7", "X"),
185        Coin::__Nonexhaustive => unreachable!(),
186        _ => return None,
187    })
188}
189
190#[test]
191fn gen_wallets() {
192    use coin::COINS;
193
194    println!("Generating wallets for Bitcoin variants...");
195    for &coin in COINS.iter() {
196        if wif_data(coin).is_some() {
197            let wallet = new_wallet(coin).unwrap();
198            println!("Coin: {:?} ({})", coin, coin.symbol());
199            println!("Address: {}", &wallet.address);
200            println!("Public key: {}", &wallet.public_key);
201            println!("Private key: {}", &wallet.private_key);
202            println!();
203        }
204    }
205}