From 6e6794786ae6f9a81281dd4467c834f70040b77c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 21 Oct 2015 23:28:39 +0100 Subject: [PATCH] mnemonics: sanity checks for word lists and a test to go with it Remember to run the test when changing word lists, or simplewallet will throw uncaught if that word list is used. --- src/mnemonics/language_base.h | 29 ++++++++++++++++++++++++++--- src/mnemonics/old_english.h | 2 +- src/mnemonics/spanish.h | 2 +- tests/unit_tests/mnemonics.cpp | 31 ++++++++++++++++++++++++++++++- 4 files changed, 58 insertions(+), 6 deletions(-) diff --git a/src/mnemonics/language_base.h b/src/mnemonics/language_base.h index c085a8b1..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,6 +74,10 @@ namespace Language class Base { protected: + 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 */ @@ -81,21 +86,39 @@ namespace Language /*! * \brief Populates the word maps after the list is ready. */ - void populate_maps() + void populate_maps(uint32_t flags = 0) { int 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; + 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: diff --git a/src/mnemonics/old_english.h b/src/mnemonics/old_english.h index 1a96f4b1..21ac95de 100644 --- a/src/mnemonics/old_english.h +++ b/src/mnemonics/old_english.h @@ -1680,7 +1680,7 @@ namespace Language "weary" }), 4) { - populate_maps(); + populate_maps(ALLOW_DUPLICATE_PREFIXES | ALLOW_SHORT_WORDS); } }; } diff --git a/src/mnemonics/spanish.h b/src/mnemonics/spanish.h index a9a93243..4b386a96 100644 --- a/src/mnemonics/spanish.h +++ b/src/mnemonics/spanish.h @@ -1680,7 +1680,7 @@ namespace Language "rito" }), 4) { - populate_maps(); + 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); + } } }