Merge pull request #1917

6e679478 mnemonics: sanity checks for word lists (moneromooo-monero)
e98f1114 mnemonics: misc cleanup (moneromooo-monero)
This commit is contained in:
Riccardo Spagni 2017-03-25 17:35:52 +02:00
commit f64a0f2243
No known key found for this signature in database
GPG Key ID: 55432DF31CCD4FCD
12 changed files with 102 additions and 104 deletions

View File

@ -49,9 +49,7 @@ namespace Language
class Dutch: public Base class Dutch: public Base
{ {
public: public:
Dutch() Dutch(): Base("Dutch", std::vector<std::string>({
{
word_list = new std::vector<std::string>({
"aalglad", "aalglad",
"aalscholver", "aalscholver",
"aambeeld", "aambeeld",
@ -1678,11 +1676,8 @@ namespace Language
"zwiep", "zwiep",
"zwijmel", "zwijmel",
"zworen" "zworen"
}); }), 4)
unique_prefix_length = 4; {
word_map = new std::unordered_map<std::string, uint32_t>;
trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
language_name = "Dutch";
populate_maps(); populate_maps();
} }
}; };

View File

@ -49,9 +49,7 @@ namespace Language
class English: public Base class English: public Base
{ {
public: public:
English() English(): Base("English", std::vector<std::string>({
{
word_list = new std::vector<std::string>({
"abbey", "abbey",
"abducts", "abducts",
"ability", "ability",
@ -1678,11 +1676,8 @@ namespace Language
"zombie", "zombie",
"zones", "zones",
"zoom" "zoom"
}); }), 3)
unique_prefix_length = 3; {
word_map = new std::unordered_map<std::string, uint32_t>;
trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
language_name = "English";
populate_maps(); populate_maps();
} }
}; };

View File

@ -49,9 +49,7 @@ namespace Language
class French: public Base class French: public Base
{ {
public: public:
French() French(): Base("French", std::vector<std::string>({
{
word_list = new std::vector<std::string>({
"abandon", "abandon",
"abattre", "abattre",
"aboi", "aboi",
@ -1678,11 +1676,8 @@ namespace Language
"zinc", "zinc",
"zone", "zone",
"zoom" "zoom"
}); }), 4)
unique_prefix_length = 4; {
word_map = new std::unordered_map<std::string, uint32_t>;
trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
language_name = "French";
populate_maps(); populate_maps();
} }
}; };

View File

@ -51,9 +51,7 @@ namespace Language
class German: public Base class German: public Base
{ {
public: public:
German() German(): Base("German", std::vector<std::string>({
{
word_list = new std::vector<std::string>({
"Abakus", "Abakus",
"Abart", "Abart",
"abbilden", "abbilden",
@ -1680,11 +1678,8 @@ namespace Language
"Zündung", "Zündung",
"Zweck", "Zweck",
"Zyklop" "Zyklop"
}); }), 4)
unique_prefix_length = 4; {
word_map = new std::unordered_map<std::string, uint32_t>;
trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
language_name = "German";
populate_maps(); populate_maps();
} }
}; };

View File

@ -51,9 +51,7 @@ namespace Language
class Italian: public Base class Italian: public Base
{ {
public: public:
Italian() Italian(): Base("Italian", std::vector<std::string>({
{
word_list = new std::vector<std::string>({
"abbinare", "abbinare",
"abbonato", "abbonato",
"abisso", "abisso",
@ -1680,11 +1678,8 @@ namespace Language
"zolfo", "zolfo",
"zombie", "zombie",
"zucchero" "zucchero"
}); }), 4)
unique_prefix_length = 4; {
word_map = new std::unordered_map<std::string, uint32_t>;
trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
language_name = "Italian";
populate_maps(); populate_maps();
} }
}; };

View File

@ -51,9 +51,7 @@ namespace Language
class Japanese: public Base class Japanese: public Base
{ {
public: public:
Japanese() Japanese(): Base("Japanese", std::vector<std::string>({
{
word_list = new std::vector<std::string>({
"あいこくしん", "あいこくしん",
"あいさつ", "あいさつ",
"あいだ", "あいだ",
@ -1680,11 +1678,8 @@ namespace Language
"ひさん", "ひさん",
"びじゅつかん", "びじゅつかん",
"ひしょ" "ひしょ"
}); }), 3)
unique_prefix_length = 3; {
word_map = new std::unordered_map<std::string, uint32_t>;
trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
language_name = "Japanese";
populate_maps(); populate_maps();
} }
}; };

View File

@ -38,6 +38,7 @@
#include <vector> #include <vector>
#include <unordered_map> #include <unordered_map>
#include <string> #include <string>
#include "misc_log_ex.h"
/*! /*!
* \namespace Language * \namespace Language
@ -73,44 +74,62 @@ namespace Language
class Base class Base
{ {
protected: protected:
std::vector<std::string> *word_list; /*!< A pointer to the array of words */ enum {
std::unordered_map<std::string, uint32_t> *word_map; /*!< hash table to find word's index */ ALLOW_SHORT_WORDS = 1<<0,
std::unordered_map<std::string, uint32_t> *trimmed_word_map; /*!< hash table to find word's trimmed index */ ALLOW_DUPLICATE_PREFIXES = 1<<1,
};
const std::vector<std::string> word_list; /*!< A pointer to the array of words */
std::unordered_map<std::string, uint32_t> word_map; /*!< hash table to find word's index */
std::unordered_map<std::string, uint32_t> trimmed_word_map; /*!< hash table to find word's trimmed index */
std::string language_name; /*!< Name of language */ std::string language_name; /*!< Name of language */
uint32_t unique_prefix_length; /*!< Number of unique starting characters to trim the wordlist to when matching */ 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. * \brief Populates the word maps after the list is ready.
*/ */
void populate_maps() void populate_maps(uint32_t flags = 0)
{ {
int ii; int ii;
std::vector<std::string>::iterator it; std::vector<std::string>::const_iterator it;
for (it = word_list->begin(), ii = 0; it != word_list->end(); it++, ii++) 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) if (it->length() > unique_prefix_length)
{ {
(*trimmed_word_map)[utf8prefix(*it, unique_prefix_length)] = ii; trimmed = utf8prefix(*it, unique_prefix_length);
} }
else 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: public:
Base() Base(const char *language_name, const std::vector<std::string> &words, uint32_t prefix_length):
word_list(words),
unique_prefix_length(prefix_length),
language_name(language_name)
{ {
word_list = new std::vector<std::string>;
word_map = new std::unordered_map<std::string, uint32_t>;
trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
unique_prefix_length = 4;
} }
virtual ~Base() virtual ~Base()
{ {
delete word_list;
delete word_map;
delete trimmed_word_map;
} }
/*! /*!
* \brief Returns a pointer to the word list. * \brief Returns a pointer to the word list.
@ -118,7 +137,7 @@ namespace Language
*/ */
const std::vector<std::string>& get_word_list() const const std::vector<std::string>& get_word_list() const
{ {
return *word_list; return word_list;
} }
/*! /*!
* \brief Returns a pointer to the word map. * \brief Returns a pointer to the word map.
@ -126,7 +145,7 @@ namespace Language
*/ */
const std::unordered_map<std::string, uint32_t>& get_word_map() const const std::unordered_map<std::string, uint32_t>& get_word_map() const
{ {
return *word_map; return word_map;
} }
/*! /*!
* \brief Returns a pointer to the trimmed word map. * \brief Returns a pointer to the trimmed word map.
@ -134,13 +153,13 @@ namespace Language
*/ */
const std::unordered_map<std::string, uint32_t>& get_trimmed_word_map() const const std::unordered_map<std::string, uint32_t>& get_trimmed_word_map() const
{ {
return *trimmed_word_map; return trimmed_word_map;
} }
/*! /*!
* \brief Returns the name of the language. * \brief Returns the name of the language.
* \return 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; return language_name;
} }

View File

@ -51,9 +51,7 @@ namespace Language
class OldEnglish: public Base class OldEnglish: public Base
{ {
public: public:
OldEnglish() OldEnglish(): Base("OldEnglish", std::vector<std::string>({
{
word_list = new std::vector<std::string>({
"like", "like",
"just", "just",
"love", "love",
@ -1680,12 +1678,9 @@ namespace Language
"unseen", "unseen",
"weapon", "weapon",
"weary" "weary"
}); }), 4)
unique_prefix_length = 4; {
word_map = new std::unordered_map<std::string, uint32_t>; populate_maps(ALLOW_DUPLICATE_PREFIXES | ALLOW_SHORT_WORDS);
trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
language_name = "OldEnglish";
populate_maps();
} }
}; };
} }

View File

@ -49,9 +49,7 @@ namespace Language
class Portuguese: public Base class Portuguese: public Base
{ {
public: public:
Portuguese() Portuguese(): Base("Portuguese", std::vector<std::string>({
{
word_list = new std::vector<std::string>({
"abaular", "abaular",
"abdominal", "abdominal",
"abeto", "abeto",
@ -1678,11 +1676,8 @@ namespace Language
"zeloso", "zeloso",
"zenite", "zenite",
"zumbi" "zumbi"
}); }), 4)
unique_prefix_length = 4; {
word_map = new std::unordered_map<std::string, uint32_t>;
trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
language_name = "Portuguese";
populate_maps(); populate_maps();
} }
}; };

View File

@ -51,9 +51,7 @@ namespace Language
class Russian: public Base class Russian: public Base
{ {
public: public:
Russian() Russian(): Base("Russian", std::vector<std::string>({
{
word_list = new std::vector<std::string>({
"абажур", "абажур",
"абзац", "абзац",
"абонент", "абонент",
@ -1680,11 +1678,8 @@ namespace Language
"яхта", "яхта",
"ячейка", "ячейка",
"ящик" "ящик"
}); }), 4)
unique_prefix_length = 4; {
word_map = new std::unordered_map<std::string, uint32_t>;
trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
language_name = "Russian";
populate_maps(); populate_maps();
} }
}; };

View File

@ -51,9 +51,7 @@ namespace Language
class Spanish: public Base class Spanish: public Base
{ {
public: public:
Spanish() Spanish(): Base("Spanish", std::vector<std::string>({
{
word_list = new std::vector<std::string>({
"ábaco", "ábaco",
"abdomen", "abdomen",
"abeja", "abeja",
@ -1680,12 +1678,9 @@ namespace Language
"risa", "risa",
"ritmo", "ritmo",
"rito" "rito"
}); }), 4)
unique_prefix_length = 4; {
word_map = new std::unordered_map<std::string, uint32_t>; populate_maps(ALLOW_SHORT_WORDS);
trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
language_name = "Spanish";
populate_maps();
} }
}; };
} }

View File

@ -38,6 +38,11 @@
#include "mnemonics/spanish.h" #include "mnemonics/spanish.h"
#include "mnemonics/portuguese.h" #include "mnemonics/portuguese.h"
#include "mnemonics/japanese.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/old_english.h"
#include "mnemonics/language_base.h" #include "mnemonics/language_base.h"
#include "mnemonics/singleton.h" #include "mnemonics/singleton.h"
@ -133,6 +138,19 @@ namespace
} }
} }
TEST(mnemonics, consistency)
{
try {
std::vector<std::string> 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) TEST(mnemonics, all_languages)
{ {
srand(time(NULL)); srand(time(NULL));
@ -141,12 +159,23 @@ TEST(mnemonics, all_languages)
Language::Singleton<Language::Spanish>::instance(), Language::Singleton<Language::Spanish>::instance(),
Language::Singleton<Language::Portuguese>::instance(), Language::Singleton<Language::Portuguese>::instance(),
Language::Singleton<Language::Japanese>::instance(), Language::Singleton<Language::Japanese>::instance(),
Language::Singleton<Language::German>::instance(),
Language::Singleton<Language::Italian>::instance(),
Language::Singleton<Language::Russian>::instance(),
Language::Singleton<Language::French>::instance(),
Language::Singleton<Language::Dutch>::instance(),
}); });
for (std::vector<Language::Base*>::iterator it = languages.begin(); it != languages.end(); it++) for (std::vector<Language::Base*>::iterator it = languages.begin(); it != languages.end(); it++)
{ {
try {
test_language(*(*it)); test_language(*(*it));
} }
catch (const std::exception &e) {
std::cout << "Error testing " << (*it)->get_language_name() << " language: " << e.what() << std::endl;
ASSERT_TRUE(false);
}
}
} }
TEST(mnemonics, language_detection_with_bad_checksum) TEST(mnemonics, language_detection_with_bad_checksum)