Smash out polyseed

This commit is contained in:
Luke Parker
2024-06-23 05:34:41 -04:00
parent df095f027f
commit 1e2e3bd5ce
24 changed files with 210 additions and 115 deletions

19
Cargo.lock generated
View File

@@ -4867,7 +4867,6 @@ name = "monero-serai"
version = "0.1.4-alpha" version = "0.1.4-alpha"
dependencies = [ dependencies = [
"curve25519-dalek", "curve25519-dalek",
"hex",
"hex-literal", "hex-literal",
"monero-borromean", "monero-borromean",
"monero-bulletproofs", "monero-bulletproofs",
@@ -4877,8 +4876,6 @@ dependencies = [
"monero-mlsag", "monero-mlsag",
"monero-primitives", "monero-primitives",
"rand_core", "rand_core",
"serde",
"serde_json",
"std-shims", "std-shims",
"zeroize", "zeroize",
] ]
@@ -4911,16 +4908,13 @@ dependencies = [
"monero-rpc", "monero-rpc",
"monero-serai", "monero-serai",
"monero-simple-request-rpc", "monero-simple-request-rpc",
"pbkdf2 0.12.2",
"rand", "rand",
"rand_chacha", "rand_chacha",
"rand_core", "rand_core",
"rand_distr", "rand_distr",
"serde", "serde",
"serde_json", "serde_json",
"sha3",
"std-shims", "std-shims",
"subtle",
"thiserror", "thiserror",
"tokio", "tokio",
"zeroize", "zeroize",
@@ -5809,6 +5803,19 @@ dependencies = [
"universal-hash", "universal-hash",
] ]
[[package]]
name = "polyseed"
version = "0.1.0"
dependencies = [
"pbkdf2 0.12.2",
"rand_core",
"sha3",
"std-shims",
"subtle",
"thiserror",
"zeroize",
]
[[package]] [[package]]
name = "polyval" name = "polyval"
version = "0.6.2" version = "0.6.2"

View File

@@ -54,6 +54,7 @@ members = [
"coins/monero/rpc", "coins/monero/rpc",
"coins/monero/rpc/simple-request", "coins/monero/rpc/simple-request",
"coins/monero/wallet", "coins/monero/wallet",
"coins/monero/wallet/polyseed",
"message-queue", "message-queue",

View File

@@ -32,9 +32,6 @@ monero-borromean = { path = "ringct/borromean", version = "0.1", default-feature
monero-bulletproofs = { path = "ringct/bulletproofs", version = "0.1", default-features = false } monero-bulletproofs = { path = "ringct/bulletproofs", version = "0.1", default-features = false }
hex-literal = "0.4" hex-literal = "0.4"
hex = { version = "0.4", default-features = false, features = ["alloc"] }
serde = { version = "1", default-features = false, features = ["derive", "alloc"] }
serde_json = { version = "1", default-features = false, features = ["alloc"] }
[features] [features]
std = [ std = [
@@ -50,10 +47,6 @@ std = [
"monero-mlsag/std", "monero-mlsag/std",
"monero-clsag/std", "monero-clsag/std",
"monero-bulletproofs/std", "monero-bulletproofs/std",
"hex/std",
"serde/std",
"serde_json/std",
] ]
compile-time-generators = ["curve25519-dalek/precomputed-tables", "monero-bulletproofs/compile-time-generators"] compile-time-generators = ["curve25519-dalek/precomputed-tables", "monero-bulletproofs/compile-time-generators"]

View File

@@ -3,7 +3,15 @@
A modern Monero transaction library. It provides a modern, Rust-friendly view of A modern Monero transaction library. It provides a modern, Rust-friendly view of
the Monero protocol. the Monero protocol.
### Purpose and support This library is usable under no-std when the `std` feature (on by default) is
disabled.
### Wallet Functionality
monero-serai originally included wallet functionality. That has been moved to
monero-wallet.
### Purpose and Support
monero-serai was written for Serai, a decentralized exchange aiming to support monero-serai was written for Serai, a decentralized exchange aiming to support
Monero. Despite this, monero-serai is intended to be a widely usable library, Monero. Despite this, monero-serai is intended to be a widely usable library,

View File

@@ -22,7 +22,6 @@ async-trait = { version = "0.1", default-features = false }
thiserror = { version = "1", default-features = false, optional = true } thiserror = { version = "1", default-features = false, optional = true }
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] } zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
subtle = { version = "^2.4", default-features = false }
rand_core = { version = "0.6", default-features = false } rand_core = { version = "0.6", default-features = false }
# Used to send transactions # Used to send transactions
@@ -31,9 +30,6 @@ rand_chacha = { version = "0.3", default-features = false }
# Used to select decoys # Used to select decoys
rand_distr = { version = "0.4", default-features = false } rand_distr = { version = "0.4", default-features = false }
sha3 = { version = "0.10", default-features = false }
pbkdf2 = { version = "0.12", features = ["simple"], default-features = false }
group = { version = "0.13", default-features = false } group = { version = "0.13", default-features = false }
curve25519-dalek = { version = "4", default-features = false, features = ["alloc", "zeroize", "group"] } curve25519-dalek = { version = "4", default-features = false, features = ["alloc", "zeroize", "group"] }
@@ -66,16 +62,12 @@ std = [
"thiserror", "thiserror",
"zeroize/std", "zeroize/std",
"subtle/std",
"rand_core/std", "rand_core/std",
"rand/std", "rand/std",
"rand_chacha/std", "rand_chacha/std",
"rand_distr/std", "rand_distr/std",
"sha3/std",
"pbkdf2/std",
"hex/std", "hex/std",
"base58-monero/std", "base58-monero/std",
"serde/std", "serde/std",

View File

@@ -1,6 +1,45 @@
# Monero Wallet # Monero Wallet
Wallet functionality for the Monero protocol, built around monero-serai. Wallet functionality for the Monero protocol, built around monero-serai. This
library prides itself on resolving common pit falls developers may face.
monero-wallet also offers a FROST-inspired multisignature protocol orders of
magnitude more performant than Monero's own.
This library is usable under no-std when the `std` feature (on by default) is This library is usable under no-std when the `std` feature (on by default) is
disabled. disabled.
### Features
- Scanning Monero transactions
- Sending Monero transactions
- Sending Monero transactions with a FROST-inspired threshold multisignature
protocol, orders of magnitude more performant than Monero's own.
### Caveats
This library DOES attempt to do the following:
- Create on-chain transactions identical to how wallet2 would (unless told not
to)
- Not be detectable as monero-serai when scanning outputs
- Not reveal spent outputs to the connected RPC node
This library DOES NOT attempt to do the following:
- Have identical RPC behavior when creating transactions
- Be a wallet
This means that monero-serai shouldn't be fingerprintable on-chain. It also
shouldn't be fingerprintable if a targeted attack occurs to detect if the
receiving wallet is monero-serai or wallet2. It also should be generally safe
for usage with remote nodes.
It won't hide from remote nodes it's monero-serai however, potentially
allowing a remote node to profile you. The implications of this are left to the
user to consider.
It also won't act as a wallet, just as a wallet functionality library. wallet2
has several *non-transaction-level* policies, such as always attempting to use
two inputs to create transactions. These are considered out of scope to
monero-serai.

View File

@@ -1,49 +0,0 @@
# monero-serai
A modern Monero transaction library intended for usage in wallets. It prides
itself on accuracy, correctness, and removing common pit falls developers may
face.
monero-serai also offers the following features:
- Featured Addresses
- A FROST-based multisig orders of magnitude more performant than Monero's
### Purpose and support
monero-serai was written for Serai, a decentralized exchange aiming to support
Monero. Despite this, monero-serai is intended to be a widely usable library,
accurate to Monero. monero-serai guarantees the functionality needed for Serai,
yet will not deprive functionality from other users.
Various legacy transaction formats are not currently implemented, yet we are
willing to add support for them. There aren't active development efforts around
them however.
### Caveats
This library DOES attempt to do the following:
- Create on-chain transactions identical to how wallet2 would (unless told not
to)
- Not be detectable as monero-serai when scanning outputs
- Not reveal spent outputs to the connected RPC node
This library DOES NOT attempt to do the following:
- Have identical RPC behavior when creating transactions
- Be a wallet
This means that monero-serai shouldn't be fingerprintable on-chain. It also
shouldn't be fingerprintable if a targeted attack occurs to detect if the
receiving wallet is monero-serai or wallet2. It also should be generally safe
for usage with remote nodes.
It won't hide from remote nodes it's monero-serai however, potentially
allowing a remote node to profile you. The implications of this are left to the
user to consider.
It also won't act as a wallet, just as a transaction library. wallet2 has
several *non-transaction-level* policies, such as always attempting to use two
inputs to create transactions. These are considered out of scope to
monero-serai.

View File

@@ -0,0 +1,44 @@
[package]
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"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
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 }
subtle = { version = "^2.4", default-features = false }
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
rand_core = { version = "0.6", default-features = false }
sha3 = { version = "0.10", default-features = false }
pbkdf2 = { version = "0.12", features = ["simple"], default-features = false }
[features]
std = [
"std-shims/std",
"thiserror",
"subtle/std",
"zeroize/std",
"rand_core/std",
"sha3/std",
"pbkdf2/std",
]
default = ["std"]

View File

@@ -0,0 +1,6 @@
# Polyseed
Rust implementation of [Polyseed](https://github.com/tevador/polyseed).
This library is usable under no-std when the `std` feature (on by default) is
disabled.

View File

@@ -1,5 +1,10 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")]
#![deny(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
use core::fmt; use core::fmt;
use std_shims::{sync::OnceLock, vec::Vec, string::String, collections::HashMap}; use std_shims::{sync::OnceLock, string::String, collections::HashMap};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
@@ -10,8 +15,6 @@ use rand_core::{RngCore, CryptoRng};
use sha3::Sha3_256; use sha3::Sha3_256;
use pbkdf2::pbkdf2_hmac; use pbkdf2::pbkdf2_hmac;
use super::SeedError;
// Features // Features
const FEATURE_BITS: u8 = 5; const FEATURE_BITS: u8 = 5;
#[allow(dead_code)] #[allow(dead_code)]
@@ -34,7 +37,7 @@ fn polyseed_features_supported(features: u8) -> bool {
const DATE_BITS: u8 = 10; const DATE_BITS: u8 = 10;
const DATE_MASK: u16 = (1u16 << DATE_BITS) - 1; const DATE_MASK: u16 = (1u16 << DATE_BITS) - 1;
const POLYSEED_EPOCH: u64 = 1635768000; // 1st November 2021 12:00 UTC const POLYSEED_EPOCH: u64 = 1635768000; // 1st November 2021 12:00 UTC
pub(crate) const TIME_STEP: u64 = 2629746; // 30.436875 days = 1/12 of the Gregorian year const TIME_STEP: u64 = 2629746; // 30.436875 days = 1/12 of the Gregorian year
// After ~85 years, this will roll over. // After ~85 years, this will roll over.
fn birthday_encode(time: u64) -> u16 { fn birthday_encode(time: u64) -> u16 {
@@ -61,9 +64,9 @@ const LAST_BYTE_SECRET_BITS_MASK: u8 = ((1 << (BITS_PER_BYTE - CLEAR_BITS)) - 1)
const SECRET_BITS_PER_WORD: usize = 10; const SECRET_BITS_PER_WORD: usize = 10;
// Amount of words in a seed // Amount of words in a seed
pub(crate) const POLYSEED_LENGTH: usize = 16; const POLYSEED_LENGTH: usize = 16;
// Amount of characters each word must have if trimmed // Amount of characters each word must have if trimmed
pub(crate) const PREFIX_LEN: usize = 4; const PREFIX_LEN: usize = 4;
const POLY_NUM_CHECK_DIGITS: usize = 1; const POLY_NUM_CHECK_DIGITS: usize = 1;
const DATA_WORDS: usize = POLYSEED_LENGTH - POLY_NUM_CHECK_DIGITS; const DATA_WORDS: usize = POLYSEED_LENGTH - POLY_NUM_CHECK_DIGITS;
@@ -98,30 +101,58 @@ const POLYSEED_KEYGEN_ITERATIONS: u32 = 10000;
// See: https://github.com/tevador/polyseed/blob/master/include/polyseed.h#L58 // See: https://github.com/tevador/polyseed/blob/master/include/polyseed.h#L58
const COIN: u16 = 0; const COIN: u16 = 0;
/// An error when working with a Polyseed.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "std", derive(thiserror::Error))]
pub enum PolyseedError {
/// Unsupported feature bits were set.
#[cfg_attr(feature = "std", error("unsupported features"))]
UnsupportedFeatures,
/// The entropy was invalid.
#[cfg_attr(feature = "std", error("invalid entropy"))]
InvalidEntropy,
#[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 for Polyseed. /// Language options for Polyseed.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Zeroize)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Zeroize)]
pub enum Language { pub enum Language {
/// English language option.
English, English,
/// Spanish language option.
Spanish, Spanish,
/// French language option.
French, French,
/// Italian language option.
Italian, Italian,
/// Japanese language option.
Japanese, Japanese,
/// Korean language option.
Korean, Korean,
/// Czech language option.
Czech, Czech,
/// Portuguese language option.
Portuguese, Portuguese,
/// Simplified Chinese language option.
ChineseSimplified, ChineseSimplified,
/// Traditional Chinese language option.
ChineseTraditional, ChineseTraditional,
} }
struct WordList { struct WordList {
words: Vec<String>, words: &'static [&'static str],
has_prefix: bool, has_prefix: bool,
has_accent: bool, has_accent: bool,
} }
impl WordList { impl WordList {
fn new(words: &str, has_prefix: bool, has_accent: bool) -> WordList { fn new(words: &'static [&'static str], has_prefix: bool, has_accent: bool) -> WordList {
let res = WordList { words: serde_json::from_str(words).unwrap(), has_prefix, has_accent }; let res = WordList { words, has_prefix, has_accent };
// This is needed for a later unwrap to not fails // This is needed for a later unwrap to not fails
assert!(words.len() < usize::from(u16::MAX)); assert!(words.len() < usize::from(u16::MAX));
res res
@@ -133,26 +164,27 @@ static LANGUAGES_CELL: OnceLock<HashMap<Language, WordList>> = OnceLock::new();
fn LANGUAGES() -> &'static HashMap<Language, WordList> { fn LANGUAGES() -> &'static HashMap<Language, WordList> {
LANGUAGES_CELL.get_or_init(|| { LANGUAGES_CELL.get_or_init(|| {
HashMap::from([ HashMap::from([
(Language::Czech, WordList::new(include_str!("./polyseed/cs.json"), true, false)), (Language::Czech, WordList::new(include!("./words/cs.rs"), true, false)),
(Language::French, WordList::new(include_str!("./polyseed/fr.json"), true, true)), (Language::French, WordList::new(include!("./words/fr.rs"), true, true)),
(Language::Korean, WordList::new(include_str!("./polyseed/ko.json"), false, false)), (Language::Korean, WordList::new(include!("./words/ko.rs"), false, false)),
(Language::English, WordList::new(include_str!("./polyseed/en.json"), true, false)), (Language::English, WordList::new(include!("./words/en.rs"), true, false)),
(Language::Italian, WordList::new(include_str!("./polyseed/it.json"), true, false)), (Language::Italian, WordList::new(include!("./words/it.rs"), true, false)),
(Language::Spanish, WordList::new(include_str!("./polyseed/es.json"), true, true)), (Language::Spanish, WordList::new(include!("./words/es.rs"), true, true)),
(Language::Japanese, WordList::new(include_str!("./polyseed/ja.json"), false, false)), (Language::Japanese, WordList::new(include!("./words/ja.rs"), false, false)),
(Language::Portuguese, WordList::new(include_str!("./polyseed/pt.json"), true, false)), (Language::Portuguese, WordList::new(include!("./words/pt.rs"), true, false)),
( (
Language::ChineseSimplified, Language::ChineseSimplified,
WordList::new(include_str!("./polyseed/zh_simplified.json"), false, false), WordList::new(include!("./words/zh_simplified.rs"), false, false),
), ),
( (
Language::ChineseTraditional, Language::ChineseTraditional,
WordList::new(include_str!("./polyseed/zh_traditional.json"), false, false), WordList::new(include!("./words/zh_traditional.rs"), false, false),
), ),
]) ])
}) })
} }
/// A Polyseed.
#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)] #[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
pub struct Polyseed { pub struct Polyseed {
language: Language, language: Language,
@@ -222,13 +254,13 @@ impl Polyseed {
masked_features: u8, masked_features: u8,
encoded_birthday: u16, encoded_birthday: u16,
entropy: Zeroizing<[u8; 32]>, entropy: Zeroizing<[u8; 32]>,
) -> Result<Polyseed, SeedError> { ) -> Result<Polyseed, PolyseedError> {
if !polyseed_features_supported(masked_features) { if !polyseed_features_supported(masked_features) {
Err(SeedError::UnsupportedFeatures)?; Err(PolyseedError::UnsupportedFeatures)?;
} }
if !valid_entropy(&entropy) { if !valid_entropy(&entropy) {
Err(SeedError::InvalidEntropy)?; Err(PolyseedError::InvalidEntropy)?;
} }
let mut res = Polyseed { let mut res = Polyseed {
@@ -244,23 +276,24 @@ impl Polyseed {
/// Create a new `Polyseed` with specific internals. /// Create a new `Polyseed` with specific internals.
/// ///
/// `birthday` is defined in seconds since the Unix epoch. /// `birthday` is defined in seconds since the epoch.
pub fn from( fn from(
language: Language, language: Language,
features: u8, features: u8,
birthday: u64, birthday: u64,
entropy: Zeroizing<[u8; 32]>, entropy: Zeroizing<[u8; 32]>,
) -> Result<Polyseed, SeedError> { ) -> Result<Polyseed, PolyseedError> {
Self::from_internal(language, user_features(features), birthday_encode(birthday), entropy) Self::from_internal(language, user_features(features), birthday_encode(birthday), entropy)
} }
/// Create a new `Polyseed`. /// Create a new `Polyseed`.
/// ///
/// This uses the system's time for the birthday, if available. /// This uses the system's time for the birthday, if available, else 0.
pub fn new<R: RngCore + CryptoRng>(rng: &mut R, language: Language) -> Polyseed { pub fn new<R: RngCore + CryptoRng>(rng: &mut R, language: Language) -> Polyseed {
// Get the birthday // Get the birthday
#[cfg(feature = "std")] #[cfg(feature = "std")]
let birthday = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(); let birthday =
SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or(core::time::Duration::ZERO).as_secs();
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
let birthday = 0; let birthday = 0;
@@ -275,7 +308,7 @@ impl Polyseed {
/// Create a new `Polyseed` from a String. /// Create a new `Polyseed` from a String.
#[allow(clippy::needless_pass_by_value)] #[allow(clippy::needless_pass_by_value)]
pub fn from_string(lang: Language, seed: Zeroizing<String>) -> Result<Polyseed, SeedError> { pub fn from_string(lang: Language, seed: Zeroizing<String>) -> Result<Polyseed, PolyseedError> {
// Decode the seed into its polynomial coefficients // Decode the seed into its polynomial coefficients
let mut poly = [0; POLYSEED_LENGTH]; let mut poly = [0; POLYSEED_LENGTH];
@@ -325,7 +358,7 @@ impl Polyseed {
} else { } else {
check_if_matches(lang_word_list.has_prefix, lang_word_list.words.iter(), word) check_if_matches(lang_word_list.has_prefix, lang_word_list.words.iter(), word)
}) else { }) else {
Err(SeedError::InvalidSeed)? Err(PolyseedError::InvalidSeed)?
}; };
// WordList asserts the word list length is less than u16::MAX // WordList asserts the word list length is less than u16::MAX
@@ -337,7 +370,7 @@ impl Polyseed {
// Validate the checksum // Validate the checksum
if poly_eval(&poly) != 0 { if poly_eval(&poly) != 0 {
Err(SeedError::InvalidChecksum)?; Err(PolyseedError::InvalidChecksum)?;
} }
// Convert the polynomial into entropy // Convert the polynomial into entropy
@@ -416,6 +449,7 @@ impl Polyseed {
key key
} }
/// The String representation of this seed.
pub fn to_string(&self) -> Zeroizing<String> { pub fn to_string(&self) -> Zeroizing<String> {
// Encode the polynomial with the existing checksum // Encode the polynomial with the existing checksum
let mut poly = self.to_poly(); let mut poly = self.to_poly();
@@ -428,7 +462,7 @@ impl Polyseed {
let mut seed = Zeroizing::new(String::new()); let mut seed = Zeroizing::new(String::new());
let words = &LANGUAGES()[&self.language].words; let words = &LANGUAGES()[&self.language].words;
for i in 0 .. poly.len() { for i in 0 .. poly.len() {
seed.push_str(&words[usize::from(poly[i])]); seed.push_str(words[usize::from(poly[i])]);
if i < poly.len() - 1 { if i < poly.len() - 1 {
seed.push(' '); seed.push(' ');
} }

View File

@@ -1,4 +1,4 @@
[ &[
"abdikace", "abdikace",
"abeceda", "abeceda",
"adresa", "adresa",
@@ -2047,4 +2047,4 @@
"zvrat", "zvrat",
"zvukovod", "zvukovod",
"zvyk" "zvyk"
] ]

View File

@@ -1,4 +1,4 @@
[ &[
"abandon", "abandon",
"ability", "ability",
"able", "able",

View File

@@ -1,4 +1,4 @@
[ &[
"ábaco", "ábaco",
"abdomen", "abdomen",
"abeja", "abeja",

View File

@@ -1,4 +1,4 @@
[ &[
"abaisser", "abaisser",
"abandon", "abandon",
"abdiquer", "abdiquer",

View File

@@ -1,4 +1,4 @@
[ &[
"abaco", "abaco",
"abbaglio", "abbaglio",
"abbinato", "abbinato",

View File

@@ -1,4 +1,4 @@
[ &[
"あいこくしん", "あいこくしん",
"あいさつ", "あいさつ",
"あいだ", "あいだ",

View File

@@ -1,4 +1,4 @@
[ &[
"가격", "가격",
"가끔", "가끔",
"가난", "가난",

View File

@@ -1,4 +1,4 @@
[ &[
"abacate", "abacate",
"abaixo", "abaixo",
"abalar", "abalar",

View File

@@ -1,4 +1,4 @@
[ &[
"", "",
"", "",
"", "",

View File

@@ -0,0 +1,6 @@
# Monero Seeds
A Rust implementation of Monero's seed algorithm.
This library is usable under no-std when the `std` feature (on by default) is
disabled.

View File

@@ -5,7 +5,7 @@ use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
use rand_core::{RngCore, CryptoRng}; use rand_core::{RngCore, CryptoRng};
pub(crate) mod classic; pub(crate) mod classic;
pub(crate) mod polyseed; pub(crate) use polyseed;
use classic::{CLASSIC_SEED_LENGTH, CLASSIC_SEED_LENGTH_WITH_CHECKSUM, ClassicSeed}; use classic::{CLASSIC_SEED_LENGTH, CLASSIC_SEED_LENGTH_WITH_CHECKSUM, ClassicSeed};
use polyseed::{POLYSEED_LENGTH, Polyseed}; use polyseed::{POLYSEED_LENGTH, Polyseed};

View File

@@ -0,0 +1,12 @@
# Monero Wallet Utilities
Additional utility functions for monero-wallet.
This library is isolated as it adds a notable amount of dependencies to the
tree, and to be a subject to a distinct versioning policy. This library may
more frequently undergo breaking API changes.
### Features
- Support for Monero's seed algorithm
- Support for Polyseed

View File

@@ -0,0 +1,2 @@
pub use monero_seed as seed;
pub use monero_polyseed as seed;