crypto_wallet_gen/wallets/
monero.rs

1use anyhow::{anyhow, Result};
2use failure::Fail;
3use wagyu_model::private_key::PrivateKey;
4use wagyu_monero::format::MoneroFormat;
5use wagyu_monero::network::mainnet::Mainnet;
6use wagyu_monero::private_key::MoneroPrivateKey;
7
8use super::Wallet;
9use crate::bip32::HDPrivKey;
10use crate::seed::Seed;
11
12pub struct MoneroWallet {
13    private_key: MoneroPrivateKey<Mainnet>,
14}
15
16impl MoneroWallet {
17    pub fn from_seed(seed: &Seed) -> Result<Self> {
18        Ok(Self {
19            private_key: MoneroPrivateKey::from_seed(
20                &hex::encode(seed.to_bytes()),
21                &MoneroFormat::Standard,
22            )
23            .map_err(|err| err.compat())?,
24        })
25    }
26
27    pub fn address(&self) -> Result<String> {
28        Ok(format!(
29            "{}",
30            self.private_key
31                .to_address(&MoneroFormat::Standard)
32                .map_err(|err| err.compat())?
33        ))
34    }
35
36    pub fn private_spend_key(&self) -> String {
37        hex::encode(self.private_key.to_private_spend_key())
38    }
39
40    pub fn public_spend_key(&self) -> Result<String> {
41        Ok(hex::encode(
42            self.private_key
43                .to_public_key()
44                .to_public_spend_key()
45                .ok_or_else(|| anyhow!("Couldn't calculate public spend key"))?,
46        ))
47    }
48
49    pub fn private_view_key(&self) -> String {
50        hex::encode(self.private_key.to_private_view_key())
51    }
52}
53
54impl Wallet for MoneroWallet {
55    fn from_hd_key(private_key: HDPrivKey) -> Result<Self> {
56        Self::from_seed(&private_key.key_part())
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn example1() {
66        // Randomly generated on https://xmr.llcoins.net/addresstests.html
67        let seed =
68            Seed::from_hex("177c328073abe1486ceb190ee4ef544896f2ff0fe6b1c83d28de2cc68d22b106")
69                .unwrap();
70        let wallet = MoneroWallet::from_seed(&seed).unwrap();
71        assert_eq!(
72            "177c328073abe1486ceb190ee4ef544896f2ff0fe6b1c83d28de2cc68d22b106",
73            wallet.private_spend_key(),
74        );
75        assert_eq!(
76            "946f666fd47ba8c0c0f564ec3aea442f4e5d121fe35e00c63056daa6ee93fb7a",
77            wallet.public_spend_key().unwrap(),
78        );
79        assert_eq!(
80            "08b6eeff17cc5a66054b83d6ad710d8894100a6c672925ecc49cf2521af4c206",
81            wallet.private_view_key(),
82        );
83        assert_eq!("47FMqqLkqTVZExG8eJg5hV8uvrUvffjQsa9gS59tLiVxMWtAZH4SULSMhDnPiZDe4bUtGRv3wq7wcER8HymBEeDyDoXyvPa", wallet.address().unwrap());
84    }
85
86    #[test]
87    fn example2() {
88        // Randomly generated on https://xmr.llcoins.net/addresstests.html
89        let seed =
90            Seed::from_hex("786dbcf5c283165f77445327ddaf44a05104d54eb4e5920da776d1a844b20703")
91                .unwrap();
92        let wallet = MoneroWallet::from_seed(&seed).unwrap();
93        assert_eq!(
94            "786dbcf5c283165f77445327ddaf44a05104d54eb4e5920da776d1a844b20703",
95            wallet.private_spend_key(),
96        );
97        assert_eq!(
98            "c98e3bcbb80566d7b1fa9d4d02b4d1e6644cc322f820868dc5e528e175262183",
99            wallet.public_spend_key().unwrap(),
100        );
101        assert_eq!(
102            "17b4eda6613ded666609fcc3a88d2a27336734fe50f6766f917cccf5715ff704",
103            wallet.private_view_key(),
104        );
105        assert_eq!("49G7fW8KGG5d5WoqvjGBUtfY6AUmRSfJmQiNojwGYgCYP36TtVKf4ZgNPf3V15Mf1oB3QT745Hmop2acHnWrC86tJJGhaEi", wallet.address().unwrap());
106    }
107
108    #[test]
109    fn regression1() {
110        // This is a regression test. This special case of a key with trailing zeroes caused the key derivation of
111        // the wallet-gen crate to fail. See https://gitlab.com/standard-mining/wallet-gen/-/issues/1
112        let seed =
113            Seed::from_hex("6734c05d337c2f4883eb710bc02be1c30f1b2d46b2657c46cc833eecb7d7cb10")
114                .unwrap();
115        let wallet = MoneroWallet::from_seed(&seed).unwrap();
116        assert_eq!(
117            "7a60ca0019191df0ac4e7a68e13102af0f1b2d46b2657c46cc833eecb7d7cb00",
118            wallet.private_spend_key(),
119        );
120        assert_eq!(
121            "cb778d7f9fbe165be14a255640745eda8625276469e51659759caf6b3c048b1c",
122            wallet.public_spend_key().unwrap(),
123        );
124        assert_eq!(
125            "f5467d54c558a8a34b5f7bdd51a032fbe95a92e242133780adcd29df5d87da00",
126            wallet.private_view_key(),
127        );
128        assert_eq!("49LKLAixdiuGNMPJne3E7odYxUvgzhGA1FxsNV6zeAUr5nCUXyjUXLugNiMRMiCnZUAck57e5xHE58wiwmtfAfxrTwzkrkX", wallet.address().unwrap());
129    }
130}