diff --git a/src/mnemonics/dutch.h b/src/mnemonics/dutch.h index d0a972e5..0d7a637e 100644 --- a/src/mnemonics/dutch.h +++ b/src/mnemonics/dutch.h @@ -49,9 +49,7 @@ namespace Language class Dutch: public Base { public: - Dutch() - { - word_list = new std::vector({ + Dutch(): Base("Dutch", std::vector({ "aalglad", "aalscholver", "aambeeld", @@ -1678,11 +1676,8 @@ namespace Language "zwiep", "zwijmel", "zworen" - }); - unique_prefix_length = 4; - word_map = new std::unordered_map; - trimmed_word_map = new std::unordered_map; - language_name = "Dutch"; + }), 4) + { populate_maps(); } }; diff --git a/src/mnemonics/english.h b/src/mnemonics/english.h index 703c7780..e6cfa895 100644 --- a/src/mnemonics/english.h +++ b/src/mnemonics/english.h @@ -49,9 +49,7 @@ namespace Language class English: public Base { public: - English() - { - word_list = new std::vector({ + English(): Base("English", std::vector({ "abbey", "abducts", "ability", @@ -1678,11 +1676,8 @@ namespace Language "zombie", "zones", "zoom" - }); - unique_prefix_length = 3; - word_map = new std::unordered_map; - trimmed_word_map = new std::unordered_map; - language_name = "English"; + }), 3) + { populate_maps(); } }; diff --git a/src/mnemonics/french.h b/src/mnemonics/french.h index 2f6eb7e8..6cf44a19 100644 --- a/src/mnemonics/french.h +++ b/src/mnemonics/french.h @@ -49,9 +49,7 @@ namespace Language class French: public Base { public: - French() - { - word_list = new std::vector({ + French(): Base("French", std::vector({ "abandon", "abattre", "aboi", @@ -1678,11 +1676,8 @@ namespace Language "zinc", "zone", "zoom" - }); - unique_prefix_length = 4; - word_map = new std::unordered_map; - trimmed_word_map = new std::unordered_map; - language_name = "French"; + }), 4) + { populate_maps(); } }; diff --git a/src/mnemonics/german.h b/src/mnemonics/german.h index a9614e4a..1a1e6b46 100644 --- a/src/mnemonics/german.h +++ b/src/mnemonics/german.h @@ -51,9 +51,7 @@ namespace Language class German: public Base { public: - German() - { - word_list = new std::vector({ + German(): Base("German", std::vector({ "Abakus", "Abart", "abbilden", @@ -1680,11 +1678,8 @@ namespace Language "Zündung", "Zweck", "Zyklop" - }); - unique_prefix_length = 4; - word_map = new std::unordered_map; - trimmed_word_map = new std::unordered_map; - language_name = "German"; + }), 4) + { populate_maps(); } }; diff --git a/src/mnemonics/italian.h b/src/mnemonics/italian.h index 24ed6744..28cee9d9 100644 --- a/src/mnemonics/italian.h +++ b/src/mnemonics/italian.h @@ -51,9 +51,7 @@ namespace Language class Italian: public Base { public: - Italian() - { - word_list = new std::vector({ + Italian(): Base("Italian", std::vector({ "abbinare", "abbonato", "abisso", @@ -1680,11 +1678,8 @@ namespace Language "zolfo", "zombie", "zucchero" - }); - unique_prefix_length = 4; - word_map = new std::unordered_map; - trimmed_word_map = new std::unordered_map; - language_name = "Italian"; + }), 4) + { populate_maps(); } }; diff --git a/src/mnemonics/japanese.h b/src/mnemonics/japanese.h index b437ae2a..09d05d13 100644 --- a/src/mnemonics/japanese.h +++ b/src/mnemonics/japanese.h @@ -51,9 +51,7 @@ namespace Language class Japanese: public Base { public: - Japanese() - { - word_list = new std::vector({ + Japanese(): Base("Japanese", std::vector({ "あいこくしん", "あいさつ", "あいだ", @@ -1680,11 +1678,8 @@ namespace Language "ひさん", "びじゅつかん", "ひしょ" - }); - unique_prefix_length = 3; - word_map = new std::unordered_map; - trimmed_word_map = new std::unordered_map; - language_name = "Japanese"; + }), 3) + { populate_maps(); } }; diff --git a/src/mnemonics/language_base.h b/src/mnemonics/language_base.h index 5ad4211d..8f0a7a9d 100644 --- a/src/mnemonics/language_base.h +++ b/src/mnemonics/language_base.h @@ -38,6 +38,7 @@ #include #include #include +#include "misc_log_ex.h" /*! * \namespace Language @@ -73,44 +74,62 @@ namespace Language class Base { protected: - std::vector *word_list; /*!< A pointer to the array of words */ - std::unordered_map *word_map; /*!< hash table to find word's index */ - std::unordered_map *trimmed_word_map; /*!< hash table to find word's trimmed index */ + enum { + ALLOW_SHORT_WORDS = 1<<0, + ALLOW_DUPLICATE_PREFIXES = 1<<1, + }; + const std::vector word_list; /*!< A pointer to the array of words */ + std::unordered_map word_map; /*!< hash table to find word's index */ + std::unordered_map trimmed_word_map; /*!< hash table to find word's trimmed index */ std::string language_name; /*!< Name of language */ uint32_t unique_prefix_length; /*!< Number of unique starting characters to trim the wordlist to when matching */ /*! * \brief Populates the word maps after the list is ready. */ - void populate_maps() + void populate_maps(uint32_t flags = 0) { int ii; - std::vector::iterator it; - for (it = word_list->begin(), ii = 0; it != word_list->end(); it++, ii++) + std::vector::const_iterator it; + if (word_list.size () != 1626) + throw std::runtime_error("Wrong word list length for " + language_name); + for (it = word_list.begin(), ii = 0; it != word_list.end(); it++, ii++) { - (*word_map)[*it] = ii; + word_map[*it] = ii; + if ((*it).size() < unique_prefix_length) + { + if (flags & ALLOW_SHORT_WORDS) + MWARNING(language_name << " word '" << *it << "' is shorter than its prefix length, " << unique_prefix_length); + else + throw std::runtime_error("Too short word in " + language_name + " word list: " + *it); + } + std::string trimmed; if (it->length() > unique_prefix_length) { - (*trimmed_word_map)[utf8prefix(*it, unique_prefix_length)] = ii; + trimmed = utf8prefix(*it, unique_prefix_length); } else { - (*trimmed_word_map)[*it] = ii; + trimmed = *it; } + if (trimmed_word_map.find(trimmed) != trimmed_word_map.end()) + { + if (flags & ALLOW_DUPLICATE_PREFIXES) + MWARNING("Duplicate prefix in " << language_name << " word list: " << trimmed); + else + throw std::runtime_error("Duplicate prefix in " + language_name + " word list: " + trimmed); + } + trimmed_word_map[trimmed] = ii; } } public: - Base() + Base(const char *language_name, const std::vector &words, uint32_t prefix_length): + word_list(words), + unique_prefix_length(prefix_length), + language_name(language_name) { - word_list = new std::vector; - word_map = new std::unordered_map; - trimmed_word_map = new std::unordered_map; - unique_prefix_length = 4; } virtual ~Base() { - delete word_list; - delete word_map; - delete trimmed_word_map; } /*! * \brief Returns a pointer to the word list. @@ -118,7 +137,7 @@ namespace Language */ const std::vector& get_word_list() const { - return *word_list; + return word_list; } /*! * \brief Returns a pointer to the word map. @@ -126,7 +145,7 @@ namespace Language */ const std::unordered_map& get_word_map() const { - return *word_map; + return word_map; } /*! * \brief Returns a pointer to the trimmed word map. @@ -134,13 +153,13 @@ namespace Language */ const std::unordered_map& get_trimmed_word_map() const { - return *trimmed_word_map; + return trimmed_word_map; } /*! * \brief Returns the name of the language. * \return Name of the language. */ - std::string get_language_name() const + const std::string &get_language_name() const { return language_name; } diff --git a/src/mnemonics/old_english.h b/src/mnemonics/old_english.h index 690e4661..21ac95de 100644 --- a/src/mnemonics/old_english.h +++ b/src/mnemonics/old_english.h @@ -51,9 +51,7 @@ namespace Language class OldEnglish: public Base { public: - OldEnglish() - { - word_list = new std::vector({ + OldEnglish(): Base("OldEnglish", std::vector({ "like", "just", "love", @@ -1680,12 +1678,9 @@ namespace Language "unseen", "weapon", "weary" - }); - unique_prefix_length = 4; - word_map = new std::unordered_map; - trimmed_word_map = new std::unordered_map; - language_name = "OldEnglish"; - populate_maps(); + }), 4) + { + populate_maps(ALLOW_DUPLICATE_PREFIXES | ALLOW_SHORT_WORDS); } }; } diff --git a/src/mnemonics/portuguese.h b/src/mnemonics/portuguese.h index 91e2145b..09d7132f 100644 --- a/src/mnemonics/portuguese.h +++ b/src/mnemonics/portuguese.h @@ -49,9 +49,7 @@ namespace Language class Portuguese: public Base { public: - Portuguese() - { - word_list = new std::vector({ + Portuguese(): Base("Portuguese", std::vector({ "abaular", "abdominal", "abeto", @@ -1678,11 +1676,8 @@ namespace Language "zeloso", "zenite", "zumbi" - }); - unique_prefix_length = 4; - word_map = new std::unordered_map; - trimmed_word_map = new std::unordered_map; - language_name = "Portuguese"; + }), 4) + { populate_maps(); } }; diff --git a/src/mnemonics/russian.h b/src/mnemonics/russian.h index 981f40d9..b3db4aa4 100644 --- a/src/mnemonics/russian.h +++ b/src/mnemonics/russian.h @@ -51,9 +51,7 @@ namespace Language class Russian: public Base { public: - Russian() - { - word_list = new std::vector({ + Russian(): Base("Russian", std::vector({ "абажур", "абзац", "абонент", @@ -1680,11 +1678,8 @@ namespace Language "яхта", "ячейка", "ящик" - }); - unique_prefix_length = 4; - word_map = new std::unordered_map; - trimmed_word_map = new std::unordered_map; - language_name = "Russian"; + }), 4) + { populate_maps(); } }; diff --git a/src/mnemonics/spanish.h b/src/mnemonics/spanish.h index ec3a8991..4b386a96 100644 --- a/src/mnemonics/spanish.h +++ b/src/mnemonics/spanish.h @@ -51,9 +51,7 @@ namespace Language class Spanish: public Base { public: - Spanish() - { - word_list = new std::vector({ + Spanish(): Base("Spanish", std::vector({ "ábaco", "abdomen", "abeja", @@ -1680,12 +1678,9 @@ namespace Language "risa", "ritmo", "rito" - }); - unique_prefix_length = 4; - word_map = new std::unordered_map; - trimmed_word_map = new std::unordered_map; - language_name = "Spanish"; - populate_maps(); + }), 4) + { + populate_maps(ALLOW_SHORT_WORDS); } }; } diff --git a/tests/unit_tests/mnemonics.cpp b/tests/unit_tests/mnemonics.cpp index c0026a00..94e22624 100644 --- a/tests/unit_tests/mnemonics.cpp +++ b/tests/unit_tests/mnemonics.cpp @@ -38,6 +38,11 @@ #include "mnemonics/spanish.h" #include "mnemonics/portuguese.h" #include "mnemonics/japanese.h" +#include "mnemonics/german.h" +#include "mnemonics/italian.h" +#include "mnemonics/russian.h" +#include "mnemonics/french.h" +#include "mnemonics/dutch.h" #include "mnemonics/old_english.h" #include "mnemonics/language_base.h" #include "mnemonics/singleton.h" @@ -133,6 +138,19 @@ namespace } } +TEST(mnemonics, consistency) +{ + try { + std::vector language_list; + crypto::ElectrumWords::get_language_list(language_list); + } + catch(const std::exception &e) + { + std::cout << "Error initializing mnemonics: " << e.what() << std::endl; + ASSERT_TRUE(false); + } +} + TEST(mnemonics, all_languages) { srand(time(NULL)); @@ -141,11 +159,22 @@ TEST(mnemonics, all_languages) Language::Singleton::instance(), Language::Singleton::instance(), Language::Singleton::instance(), + Language::Singleton::instance(), + Language::Singleton::instance(), + Language::Singleton::instance(), + Language::Singleton::instance(), + Language::Singleton::instance(), }); for (std::vector::iterator it = languages.begin(); it != languages.end(); it++) { - test_language(*(*it)); + try { + test_language(*(*it)); + } + catch (const std::exception &e) { + std::cout << "Error testing " << (*it)->get_language_name() << " language: " << e.what() << std::endl; + ASSERT_TRUE(false); + } } }