From 11dba9173fe65cbb37791b03c8fa1e76a7643f0f Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Sun, 23 Jun 2024 06:31:22 -0400 Subject: [PATCH] Smash out seed --- Cargo.lock | 11 ++ Cargo.toml | 1 + coins/monero/wallet/polyseed/Cargo.toml | 3 +- coins/monero/wallet/seed/Cargo.toml | 37 ++++++ coins/monero/wallet/seed/README.md | 2 +- .../{src/seed/classic.rs => seed/src/lib.rs} | 112 +++++++++++------- .../seed/classic => seed/src/words}/ang.rs | 2 +- .../seed/classic => seed/src/words}/de.rs | 2 +- .../seed/classic => seed/src/words}/en.rs | 2 +- .../seed/classic => seed/src/words}/eo.rs | 2 +- .../seed/classic => seed/src/words}/es.rs | 2 +- .../seed/classic => seed/src/words}/fr.rs | 2 +- .../seed/classic => seed/src/words}/it.rs | 2 +- .../seed/classic => seed/src/words}/ja.rs | 2 +- .../seed/classic => seed/src/words}/jbo.rs | 2 +- .../seed/classic => seed/src/words}/nl.rs | 2 +- .../seed/classic => seed/src/words}/pt.rs | 2 +- .../seed/classic => seed/src/words}/ru.rs | 2 +- .../seed/classic => seed/src/words}/zh.rs | 2 +- coins/monero/wallet/src/seed/mod.rs | 2 +- coins/monero/wallet/src/tests/seed.rs | 14 ++- tests/no-std/Cargo.toml | 2 + 22 files changed, 146 insertions(+), 64 deletions(-) create mode 100644 coins/monero/wallet/seed/Cargo.toml rename coins/monero/wallet/{src/seed/classic.rs => seed/src/lib.rs} (69%) rename coins/monero/wallet/{src/seed/classic => seed/src/words}/ang.rs (99%) rename coins/monero/wallet/{src/seed/classic => seed/src/words}/de.rs (99%) rename coins/monero/wallet/{src/seed/classic => seed/src/words}/en.rs (99%) rename coins/monero/wallet/{src/seed/classic => seed/src/words}/eo.rs (99%) rename coins/monero/wallet/{src/seed/classic => seed/src/words}/es.rs (99%) rename coins/monero/wallet/{src/seed/classic => seed/src/words}/fr.rs (99%) rename coins/monero/wallet/{src/seed/classic => seed/src/words}/it.rs (99%) rename coins/monero/wallet/{src/seed/classic => seed/src/words}/ja.rs (99%) rename coins/monero/wallet/{src/seed/classic => seed/src/words}/jbo.rs (99%) rename coins/monero/wallet/{src/seed/classic => seed/src/words}/nl.rs (99%) rename coins/monero/wallet/{src/seed/classic => seed/src/words}/pt.rs (99%) rename coins/monero/wallet/{src/seed/classic => seed/src/words}/ru.rs (99%) rename coins/monero/wallet/{src/seed/classic => seed/src/words}/zh.rs (99%) diff --git a/Cargo.lock b/Cargo.lock index 60934fef..14ced837 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4862,6 +4862,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "monero-seed" +version = "0.1.0" +dependencies = [ + "curve25519-dalek", + "rand_core", + "std-shims", + "thiserror", + "zeroize", +] + [[package]] name = "monero-serai" version = "0.1.4-alpha" diff --git a/Cargo.toml b/Cargo.toml index 74ac13ad..c17baff3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ members = [ "coins/monero/rpc", "coins/monero/rpc/simple-request", "coins/monero/wallet", + "coins/monero/wallet/seed", "coins/monero/wallet/polyseed", "message-queue", diff --git a/coins/monero/wallet/polyseed/Cargo.toml b/coins/monero/wallet/polyseed/Cargo.toml index 7acf7665..68c2eab8 100644 --- a/coins/monero/wallet/polyseed/Cargo.toml +++ b/coins/monero/wallet/polyseed/Cargo.toml @@ -3,7 +3,7 @@ name = "polyseed" version = "0.1.0" description = "Rust implementation of Polyseed" license = "MIT" -repository = "https://github.com/serai-dex/serai/tree/develop/coins/monero/wallet.polyseed" +repository = "https://github.com/serai-dex/serai/tree/develop/coins/monero/wallet/polyseed" authors = ["Luke Parker "] edition = "2021" rust-version = "1.79" @@ -40,5 +40,4 @@ std = [ "sha3/std", "pbkdf2/std", ] - default = ["std"] diff --git a/coins/monero/wallet/seed/Cargo.toml b/coins/monero/wallet/seed/Cargo.toml new file mode 100644 index 00000000..d1e56c97 --- /dev/null +++ b/coins/monero/wallet/seed/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "monero-seed" +version = "0.1.0" +description = "Rust implementation of Monero's seed algorithm" +license = "MIT" +repository = "https://github.com/serai-dex/serai/tree/develop/coins/monero/wallet/seed" +authors = ["Luke Parker "] +edition = "2021" +rust-version = "1.79" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[lints] +workspace = true + +[dependencies] +std-shims = { path = "../../../../common/std-shims", version = "^0.1.1", default-features = false } + +thiserror = { version = "1", default-features = false, optional = true } + +zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] } +rand_core = { version = "0.6", default-features = false } + +curve25519-dalek = { version = "4", default-features = false, features = ["alloc", "zeroize"] } + +[features] +std = [ + "std-shims/std", + + "thiserror", + + "zeroize/std", + "rand_core/std", +] +default = ["std"] diff --git a/coins/monero/wallet/seed/README.md b/coins/monero/wallet/seed/README.md index dd5eee2a..23e254ce 100644 --- a/coins/monero/wallet/seed/README.md +++ b/coins/monero/wallet/seed/README.md @@ -1,6 +1,6 @@ # Monero Seeds -A Rust implementation of Monero's seed algorithm. +Rust implementation of Monero's seed algorithm. This library is usable under no-std when the `std` feature (on by default) is disabled. diff --git a/coins/monero/wallet/src/seed/classic.rs b/coins/monero/wallet/seed/src/lib.rs similarity index 69% rename from coins/monero/wallet/src/seed/classic.rs rename to coins/monero/wallet/seed/src/lib.rs index df9198c8..41e56ed6 100644 --- a/coins/monero/wallet/src/seed/classic.rs +++ b/coins/monero/wallet/seed/src/lib.rs @@ -11,26 +11,55 @@ use rand_core::{RngCore, CryptoRng}; use curve25519_dalek::scalar::Scalar; -use crate::seed::SeedError; +const CLASSIC_SEED_LENGTH: usize = 24; +const CLASSIC_SEED_LENGTH_WITH_CHECKSUM: usize = 25; -pub(crate) const CLASSIC_SEED_LENGTH: usize = 24; -pub(crate) const CLASSIC_SEED_LENGTH_WITH_CHECKSUM: usize = 25; +/// An error when working with a seed. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "std", derive(thiserror::Error))] +pub enum SeedError { + /// The deprecated English language option was used with a checksum. + /// + /// The deprecated English language option did not include a checksum. + #[cfg_attr(feature = "std", error("deprecated English language option included a checksum"))] + DeprecatedEnglishWithChecksum, + #[cfg_attr(feature = "std", error("invalid seed"))] + /// The seed was invalid. + InvalidSeed, + /// The checksum did not match the data. + #[cfg_attr(feature = "std", error("invalid checksum"))] + InvalidChecksum, +} +/// Language options. #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Zeroize)] pub enum Language { + /// Chinese language option. Chinese, + /// English language option. English, + /// Dutch language option. Dutch, + /// French language option. French, + /// Spanish language option. Spanish, + /// German language option. German, + /// Italian language option. Italian, + /// Portuguese language option. Portuguese, + /// Japanese language option. Japanese, + /// Russian language option. Russian, + /// Esperanto language option. Esperanto, + /// Lojban language option. Lojban, - EnglishOld, + /// The original, and deprecated, English language. + DeprecatedEnglish, } fn trim(word: &str, len: usize) -> Zeroizing { @@ -38,14 +67,14 @@ fn trim(word: &str, len: usize) -> Zeroizing { } struct WordList { - word_list: Vec<&'static str>, + word_list: &'static [&'static str], word_map: HashMap<&'static str, usize>, trimmed_word_map: HashMap, unique_prefix_length: usize, } impl WordList { - fn new(word_list: Vec<&'static str>, prefix_length: usize) -> WordList { + fn new(word_list: &'static [&'static str], prefix_length: usize) -> WordList { let mut lang = WordList { word_list, word_map: HashMap::new(), @@ -67,32 +96,23 @@ static LANGUAGES_CELL: OnceLock> = OnceLock::new(); fn LANGUAGES() -> &'static HashMap { LANGUAGES_CELL.get_or_init(|| { HashMap::from([ - (Language::Chinese, WordList::new(include!("./classic/zh.rs"), 1)), - (Language::English, WordList::new(include!("./classic/en.rs"), 3)), - (Language::Dutch, WordList::new(include!("./classic/nl.rs"), 4)), - (Language::French, WordList::new(include!("./classic/fr.rs"), 4)), - (Language::Spanish, WordList::new(include!("./classic/es.rs"), 4)), - (Language::German, WordList::new(include!("./classic/de.rs"), 4)), - (Language::Italian, WordList::new(include!("./classic/it.rs"), 4)), - (Language::Portuguese, WordList::new(include!("./classic/pt.rs"), 4)), - (Language::Japanese, WordList::new(include!("./classic/ja.rs"), 3)), - (Language::Russian, WordList::new(include!("./classic/ru.rs"), 4)), - (Language::Esperanto, WordList::new(include!("./classic/eo.rs"), 4)), - (Language::Lojban, WordList::new(include!("./classic/jbo.rs"), 4)), - (Language::EnglishOld, WordList::new(include!("./classic/ang.rs"), 4)), + (Language::Chinese, WordList::new(include!("./words/zh.rs"), 1)), + (Language::English, WordList::new(include!("./words/en.rs"), 3)), + (Language::Dutch, WordList::new(include!("./words/nl.rs"), 4)), + (Language::French, WordList::new(include!("./words/fr.rs"), 4)), + (Language::Spanish, WordList::new(include!("./words/es.rs"), 4)), + (Language::German, WordList::new(include!("./words/de.rs"), 4)), + (Language::Italian, WordList::new(include!("./words/it.rs"), 4)), + (Language::Portuguese, WordList::new(include!("./words/pt.rs"), 4)), + (Language::Japanese, WordList::new(include!("./words/ja.rs"), 3)), + (Language::Russian, WordList::new(include!("./words/ru.rs"), 4)), + (Language::Esperanto, WordList::new(include!("./words/eo.rs"), 4)), + (Language::Lojban, WordList::new(include!("./words/jbo.rs"), 4)), + (Language::DeprecatedEnglish, WordList::new(include!("./words/ang.rs"), 4)), ]) }) } -#[cfg(test)] -pub(crate) fn trim_by_lang(word: &str, lang: Language) -> String { - if lang != Language::EnglishOld { - word.chars().take(LANGUAGES()[&lang].unique_prefix_length).collect() - } else { - word.to_string() - } -} - fn checksum_index(words: &[Zeroizing], lang: &WordList) -> usize { let mut trimmed_words = Zeroizing::new(String::new()); for w in words { @@ -135,7 +155,7 @@ fn checksum_index(words: &[Zeroizing], lang: &WordList) -> usize { // Convert a private key to a seed #[allow(clippy::needless_pass_by_value)] -fn key_to_seed(lang: Language, key: Zeroizing) -> ClassicSeed { +fn key_to_seed(lang: Language, key: Zeroizing) -> Seed { let bytes = Zeroizing::new(key.to_bytes()); // get the language words @@ -172,7 +192,7 @@ fn key_to_seed(lang: Language, key: Zeroizing) -> ClassicSeed { indices.zeroize(); // create a checksum word for all languages except old english - if lang != Language::EnglishOld { + if lang != Language::DeprecatedEnglish { let checksum = seed[checksum_index(&seed, &LANGUAGES()[&lang])].clone(); seed.push(checksum); } @@ -184,11 +204,11 @@ fn key_to_seed(lang: Language, key: Zeroizing) -> ClassicSeed { } *res += word; } - ClassicSeed(lang, res) + Seed(lang, res) } // Convert a seed to bytes -pub(crate) fn seed_to_bytes(lang: Language, words: &str) -> Result, SeedError> { +fn seed_to_bytes(lang: Language, words: &str) -> Result, SeedError> { // get seed words let words = words.split_whitespace().map(|w| Zeroizing::new(w.to_string())).collect::>(); if (words.len() != CLASSIC_SEED_LENGTH) && (words.len() != CLASSIC_SEED_LENGTH_WITH_CHECKSUM) { @@ -196,8 +216,8 @@ pub(crate) fn seed_to_bytes(lang: Language, words: &str) -> Result Result); -impl ClassicSeed { - pub(crate) fn new(rng: &mut R, lang: Language) -> ClassicSeed { - key_to_seed(lang, Zeroizing::new(Scalar::random(rng))) +pub struct Seed(Language, Zeroizing); +impl Seed { + /// Create a new seed. + pub fn new(rng: &mut R, lang: Language) -> Seed { + let mut scalar_bytes = Zeroizing::new([0; 64]); + rng.fill_bytes(scalar_bytes.as_mut()); + key_to_seed(lang, Zeroizing::new(Scalar::from_bytes_mod_order_wide(scalar_bytes.deref()))) } + /// Parse a seed from a string. #[allow(clippy::needless_pass_by_value)] - pub fn from_string(lang: Language, words: Zeroizing) -> Result { + pub fn from_string(lang: Language, words: Zeroizing) -> Result { let entropy = seed_to_bytes(lang, &words)?; // Make sure this is a valid scalar @@ -295,17 +320,20 @@ impl ClassicSeed { Ok(Self::from_entropy(lang, entropy).unwrap()) } + /// Create a seed from entropy. #[allow(clippy::needless_pass_by_value)] - pub fn from_entropy(lang: Language, entropy: Zeroizing<[u8; 32]>) -> Option { + pub fn from_entropy(lang: Language, entropy: Zeroizing<[u8; 32]>) -> Option { Option::from(Scalar::from_canonical_bytes(*entropy)) .map(|scalar| key_to_seed(lang, Zeroizing::new(scalar))) } - pub(crate) fn to_string(&self) -> Zeroizing { + /// Convert a seed to a string. + pub fn to_string(&self) -> Zeroizing { self.1.clone() } - pub(crate) fn entropy(&self) -> Zeroizing<[u8; 32]> { + /// Return the entropy underlying this seed. + pub fn entropy(&self) -> Zeroizing<[u8; 32]> { seed_to_bytes(self.0, &self.1).unwrap() } } diff --git a/coins/monero/wallet/src/seed/classic/ang.rs b/coins/monero/wallet/seed/src/words/ang.rs similarity index 99% rename from coins/monero/wallet/src/seed/classic/ang.rs rename to coins/monero/wallet/seed/src/words/ang.rs index d2e47840..2800b1a9 100644 --- a/coins/monero/wallet/src/seed/classic/ang.rs +++ b/coins/monero/wallet/seed/src/words/ang.rs @@ -1,4 +1,4 @@ -vec![ +&[ "like", "just", "love", diff --git a/coins/monero/wallet/src/seed/classic/de.rs b/coins/monero/wallet/seed/src/words/de.rs similarity index 99% rename from coins/monero/wallet/src/seed/classic/de.rs rename to coins/monero/wallet/seed/src/words/de.rs index c6618356..85dee081 100644 --- a/coins/monero/wallet/src/seed/classic/de.rs +++ b/coins/monero/wallet/seed/src/words/de.rs @@ -1,4 +1,4 @@ -vec![ +&[ "Abakus", "Abart", "abbilden", diff --git a/coins/monero/wallet/src/seed/classic/en.rs b/coins/monero/wallet/seed/src/words/en.rs similarity index 99% rename from coins/monero/wallet/src/seed/classic/en.rs rename to coins/monero/wallet/seed/src/words/en.rs index ae788691..c6f9a454 100644 --- a/coins/monero/wallet/src/seed/classic/en.rs +++ b/coins/monero/wallet/seed/src/words/en.rs @@ -1,4 +1,4 @@ -vec![ +&[ "abbey", "abducts", "ability", diff --git a/coins/monero/wallet/src/seed/classic/eo.rs b/coins/monero/wallet/seed/src/words/eo.rs similarity index 99% rename from coins/monero/wallet/src/seed/classic/eo.rs rename to coins/monero/wallet/seed/src/words/eo.rs index eb518af0..d9d6ff40 100644 --- a/coins/monero/wallet/src/seed/classic/eo.rs +++ b/coins/monero/wallet/seed/src/words/eo.rs @@ -1,4 +1,4 @@ -vec![ +&[ "abako", "abdiki", "abelo", diff --git a/coins/monero/wallet/src/seed/classic/es.rs b/coins/monero/wallet/seed/src/words/es.rs similarity index 99% rename from coins/monero/wallet/src/seed/classic/es.rs rename to coins/monero/wallet/seed/src/words/es.rs index d6f26855..09fb346d 100644 --- a/coins/monero/wallet/src/seed/classic/es.rs +++ b/coins/monero/wallet/seed/src/words/es.rs @@ -1,4 +1,4 @@ -vec![ +&[ "ábaco", "abdomen", "abeja", diff --git a/coins/monero/wallet/src/seed/classic/fr.rs b/coins/monero/wallet/seed/src/words/fr.rs similarity index 99% rename from coins/monero/wallet/src/seed/classic/fr.rs rename to coins/monero/wallet/seed/src/words/fr.rs index e2ca8ad4..338eeb38 100644 --- a/coins/monero/wallet/src/seed/classic/fr.rs +++ b/coins/monero/wallet/seed/src/words/fr.rs @@ -1,4 +1,4 @@ -vec![ +&[ "abandon", "abattre", "aboi", diff --git a/coins/monero/wallet/src/seed/classic/it.rs b/coins/monero/wallet/seed/src/words/it.rs similarity index 99% rename from coins/monero/wallet/src/seed/classic/it.rs rename to coins/monero/wallet/seed/src/words/it.rs index d303452c..343984e6 100644 --- a/coins/monero/wallet/src/seed/classic/it.rs +++ b/coins/monero/wallet/seed/src/words/it.rs @@ -1,4 +1,4 @@ -vec![ +&[ "abbinare", "abbonato", "abisso", diff --git a/coins/monero/wallet/src/seed/classic/ja.rs b/coins/monero/wallet/seed/src/words/ja.rs similarity index 99% rename from coins/monero/wallet/src/seed/classic/ja.rs rename to coins/monero/wallet/seed/src/words/ja.rs index bc2aafde..da2d9fb6 100644 --- a/coins/monero/wallet/src/seed/classic/ja.rs +++ b/coins/monero/wallet/seed/src/words/ja.rs @@ -1,4 +1,4 @@ -vec![ +&[ "あいこくしん", "あいさつ", "あいだ", diff --git a/coins/monero/wallet/src/seed/classic/jbo.rs b/coins/monero/wallet/seed/src/words/jbo.rs similarity index 99% rename from coins/monero/wallet/src/seed/classic/jbo.rs rename to coins/monero/wallet/seed/src/words/jbo.rs index bcfcc6bc..a58f8d11 100644 --- a/coins/monero/wallet/src/seed/classic/jbo.rs +++ b/coins/monero/wallet/seed/src/words/jbo.rs @@ -1,4 +1,4 @@ -vec![ +&[ "backi", "bacru", "badna", diff --git a/coins/monero/wallet/src/seed/classic/nl.rs b/coins/monero/wallet/seed/src/words/nl.rs similarity index 99% rename from coins/monero/wallet/src/seed/classic/nl.rs rename to coins/monero/wallet/seed/src/words/nl.rs index e2f1912f..0c191e7f 100644 --- a/coins/monero/wallet/src/seed/classic/nl.rs +++ b/coins/monero/wallet/seed/src/words/nl.rs @@ -1,4 +1,4 @@ -vec![ +&[ "aalglad", "aalscholver", "aambeeld", diff --git a/coins/monero/wallet/src/seed/classic/pt.rs b/coins/monero/wallet/seed/src/words/pt.rs similarity index 99% rename from coins/monero/wallet/src/seed/classic/pt.rs rename to coins/monero/wallet/seed/src/words/pt.rs index 6f37336b..cede0ac5 100644 --- a/coins/monero/wallet/src/seed/classic/pt.rs +++ b/coins/monero/wallet/seed/src/words/pt.rs @@ -1,4 +1,4 @@ -vec![ +&[ "abaular", "abdominal", "abeto", diff --git a/coins/monero/wallet/src/seed/classic/ru.rs b/coins/monero/wallet/seed/src/words/ru.rs similarity index 99% rename from coins/monero/wallet/src/seed/classic/ru.rs rename to coins/monero/wallet/seed/src/words/ru.rs index 3b36ef61..609fa4cb 100644 --- a/coins/monero/wallet/src/seed/classic/ru.rs +++ b/coins/monero/wallet/seed/src/words/ru.rs @@ -1,4 +1,4 @@ -vec![ +&[ "абажур", "абзац", "абонент", diff --git a/coins/monero/wallet/src/seed/classic/zh.rs b/coins/monero/wallet/seed/src/words/zh.rs similarity index 99% rename from coins/monero/wallet/src/seed/classic/zh.rs rename to coins/monero/wallet/seed/src/words/zh.rs index 2ea7916e..42f05b4a 100644 --- a/coins/monero/wallet/src/seed/classic/zh.rs +++ b/coins/monero/wallet/seed/src/words/zh.rs @@ -1,4 +1,4 @@ -vec![ +&[ "的", "一", "是", diff --git a/coins/monero/wallet/src/seed/mod.rs b/coins/monero/wallet/src/seed/mod.rs index f75400da..1b9c062b 100644 --- a/coins/monero/wallet/src/seed/mod.rs +++ b/coins/monero/wallet/src/seed/mod.rs @@ -4,7 +4,7 @@ use std_shims::string::String; use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing}; use rand_core::{RngCore, CryptoRng}; -pub(crate) mod classic; +pub(crate) use monero_seed as classic; pub(crate) use polyseed; use classic::{CLASSIC_SEED_LENGTH, CLASSIC_SEED_LENGTH_WITH_CHECKSUM, ClassicSeed}; use polyseed::{POLYSEED_LENGTH, Polyseed}; diff --git a/coins/monero/wallet/src/tests/seed.rs b/coins/monero/wallet/src/tests/seed.rs index 9b744ffe..a9acbe0a 100644 --- a/coins/monero/wallet/src/tests/seed.rs +++ b/coins/monero/wallet/src/tests/seed.rs @@ -6,11 +6,7 @@ use curve25519_dalek::scalar::Scalar; use monero_serai::primitives::keccak256; -use crate::seed::{ - Seed, SeedType, SeedError, - classic::{self, trim_by_lang}, - polyseed, -}; +use crate::seed::{Seed, SeedType, SeedError, classic, polyseed}; #[test] fn test_classic_seed() { @@ -186,6 +182,14 @@ fn test_classic_seed() { ]; for vector in vectors { + fn trim_by_lang(word: &str, lang: Language) -> String { + if lang != Language::DeprecatedEnglish { + word.chars().take(LANGUAGES()[&lang].unique_prefix_length).collect() + } else { + word.to_string() + } + } + let trim_seed = |seed: &str| { seed .split_whitespace() diff --git a/tests/no-std/Cargo.toml b/tests/no-std/Cargo.toml index 65132244..64f641ed 100644 --- a/tests/no-std/Cargo.toml +++ b/tests/no-std/Cargo.toml @@ -43,4 +43,6 @@ monero-clsag = { path = "../../coins/monero/ringct/clsag", default-features = fa monero-bulletproofs = { path = "../../coins/monero/ringct/bulletproofs", default-features = false } monero-serai = { path = "../../coins/monero", default-features = false } monero-rpc = { path = "../../coins/monero/rpc", default-features = false } +monero-seed = { path = "../../coins/monero/wallet/seed", default-features = false } +monero-polyseed = { path = "../../coins/monero/wallet/polyseed", default-features = false } monero-wallet = { path = "../../coins/monero/wallet", default-features = false }