From 6adb4eeae1e054c9abf2b9777c6d5f86ed2d8822 Mon Sep 17 00:00:00 2001 From: lance allen Date: Mon, 19 Feb 2018 09:35:30 -0800 Subject: [PATCH] Squashed commit of the following: commit 091aa668e194489e3a45e418941ea922c09f7024 Author: lance allen Date: Mon Feb 19 09:32:50 2018 -0800 removing extraneous import commit 47d2dbcf4e20a4cbba1bb77caa39e8005db00042 Author: lance allen Date: Mon Feb 19 00:18:19 2018 -0800 matching the version to emesik master and fixing the method name commit 63b5f5fae3a3125b36a6ed18f3f7e47e54cd1d1d Author: lance allen Date: Sun Feb 18 22:38:37 2018 -0800 adding unit file for seed class commit 515ccfe18663d6f52810d59ccdd9ab51e2be7d58 Author: lance allen Date: Sun Feb 18 22:38:17 2018 -0800 updating init files commit 6e0803661f08694a5a81a83a7fbeede6e18f741c Author: lance allen Date: Sun Feb 18 22:37:46 2018 -0800 updating init from assert to raise, set gte operator on checks commit e066039bf8ccf5128987e6d291864f3a9d61df28 Author: lance allen Date: Sun Feb 18 18:42:30 2018 -0800 move get_checksum outside of class and always return seed with checksum commit 1b0cb8adc6945d777abba18acfcaf8d3b6f4be42 Author: lance allen Date: Sun Feb 18 17:14:46 2018 -0800 removing extraneous seed_ prefixes from class vars and change class param to commit 9b60a34da554696f3cfbfd7cb3f0780dfc32bc55 Author: lance allen Date: Sun Feb 18 13:52:12 2018 -0800 move generate_hex function outside of class commit 162036cb38ff5a90cf515fd951d70299117866aa Author: lance allen Date: Sun Feb 18 13:49:46 2018 -0800 adding string input to class constructor and determine validity commit 30f04ade1819b152fd600eff0b7364d9bd7aaab4 Author: lalanza808 Date: Sat Feb 17 23:59:19 2018 -0800 adding commit hashes and revamping comments commit c46029fbc9bf74bf64231a1d4c4c6613be08bf00 Author: lalanza808 Date: Sat Feb 17 23:18:07 2018 -0800 adding licensing, verb noun functions, checksum val, hex generation commit fa38d8aa1ea3aeb5ba7d03f89f9610a1b9b76e49 Author: lance allen Date: Sat Feb 17 16:57:19 2018 -0800 adding seed class for encoding/decoding mnemonic phrases or hex strings commit d523e51a92c626a435aad64740d2fe04eb2f8b99 Author: lance allen Date: Sat Feb 17 16:57:03 2018 -0800 adding english wordlist for mnemonic seeds commit f6d78a497a0d48b565f1a584b51150a03f9df7d6 Author: lance allen Date: Sat Feb 17 16:56:48 2018 -0800 updating monero init file to include wordlists --- monero/__init__.py | 4 +- monero/seed.py | 168 ++++ monero/wordlists/__init__.py | 1 + monero/wordlists/english.py | 1660 ++++++++++++++++++++++++++++++++++ tests/__init__.py | 1 + tests/test_seed.py | 65 ++ 6 files changed, 1897 insertions(+), 2 deletions(-) create mode 100644 monero/seed.py create mode 100644 monero/wordlists/__init__.py create mode 100644 monero/wordlists/english.py create mode 100644 tests/test_seed.py diff --git a/monero/__init__.py b/monero/__init__.py index fc182c4..46e3e26 100644 --- a/monero/__init__.py +++ b/monero/__init__.py @@ -1,3 +1,3 @@ -from . import address, account, daemon, wallet, numbers, prio +from . import address, account, daemon, wallet, numbers, prio, wordlists, seed -__version__ = '0.1.1' +__version__ = '0.1.2' diff --git a/monero/seed.py b/monero/seed.py new file mode 100644 index 0000000..b25aa3c --- /dev/null +++ b/monero/seed.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python + +# Electrum - lightweight Bitcoin client +# Copyright (C) 2011 thomasv@gitorious +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# Note about US patent no 5892470: Here each word does not represent a given digit. +# Instead, the digit represented by a word is variable, it depends on the previous word. +# +# Copied 17 February 2018 from MoneroPy, originally from Electrum: +# https://github.com/bigreddmachine/MoneroPy/blob/master/moneropy/mnemonic.py ch: 80cc16c39b16c55a8d052fbf7fae68644f7a5f02 +# https://github.com/spesmilo/electrum/blob/master/lib/old_mnemonic.py ch:9a0aa9b4783ea03ea13c6d668e080e0cdf261c5b + +from monero import wordlists +from binascii import crc32, hexlify +from os import urandom + +class Seed(object): + """Creates a seed object either from local system randomness or an imported phrase. + + :rtype: :class:`Seed ` + """ + + n = 1626 + wordlist = wordlists.english.wordlist # default english for now + + phrase = "" #13 or 25 word mnemonic word string + hex = "" # hexadecimal + + def __init__(self, phrase=""): + """If user supplied a seed string to the class, break it down and determine + if it's hexadecimal or mnemonic word string. Gather the values and store them. + If no seed is passed, automatically generate a new one from local system randomness. + + :rtype: :class:`Seed ` + """ + if phrase: + seed_split = phrase.split(" ") + if len(seed_split) >= 24: + # standard mnemonic + self.phrase = phrase + if len(seed_split) == 25: + # with checksum + self.validate_checksum() + self.hex = self.decode_seed() + elif len(seed_split) >= 12: + # mymonero mnemonic + self.phrase = phrase + if len(seed_split) == 13: + # with checksum + self.validate_checksum() + self.hex = self.decode_seed() + elif len(seed_split) == 1: + # single string, probably hex, but confirm + if not len(phrase) % 8 == 0: + raise ValueError("Not valid hexadecimal: {hex}".format(hex=phrase)) + self.hex = phrase + self.phrase = self.encode_seed() + else: + raise ValueError("Not valid mnemonic phrase: {phrase}".format(phrase=phrase)) + else: + self.hex = generate_hex() + self.encode_seed() + + + def endian_swap(self, word): + """Given any string, swap bits and return the result. + + :rtype: str + """ + return "".join([word[i:i+2] for i in [6, 4, 2, 0]]) + + def encode_seed(self): + """Given a hexadecimal string, return it's mnemonic word representation with checksum. + + :rtype: str + """ + assert self.hex, "Seed hex not set" + assert len(self.hex) % 8 == 0, "Not valid hexadecimal" + out = [] + for i in range(len(self.hex) // 8): + word = self.endian_swap(self.hex[8*i:8*i+8]) + x = int(word, 16) + w1 = x % self.n + w2 = (x // self.n + w1) % self.n + w3 = (x // self.n // self.n + w2) % self.n + out += [self.wordlist[w1], self.wordlist[w2], self.wordlist[w3]] + checksum = get_checksum(" ".join(out)) + out.append(checksum) + self.phrase = " ".join(out) + return self.phrase + + def decode_seed(self): + """Given a mnemonic word string, return it's hexadecimal representation. + + :rtype: str + """ + assert self.phrase, "Seed phrase not set" + phrase = self.phrase.split(" ") + assert len(phrase) >= 12, "Not valid mnemonic phrase" + out = "" + for i in range(len(phrase) // 3): + word1, word2, word3 = phrase[3*i:3*i+3] + w1 = self.wordlist.index(word1) + w2 = self.wordlist.index(word2) % self.n + w3 = self.wordlist.index(word3) % self.n + x = w1 + self.n *((w2 - w1) % self.n) + self.n * self.n * ((w3 - w2) % self.n) + out += self.endian_swap("%08x" % x) + self.hex = out + return self.hex + + def validate_checksum(self): + """Given a mnemonic word string, confirm seed checksum (last word) matches the computed checksum. + + :rtype: bool + """ + assert self.phrase, "Seed phrase not set" + phrase = self.phrase.split(" ") + assert len(phrase) > 12, "Not valid mnemonic phrase" + is_match = get_checksum(self.phrase) == phrase[-1] + assert is_match, "Not valid checksum" + return is_match + + +def get_checksum(phrase): + """Given a mnemonic word string, return a string of the computed checksum. + + :rtype: str + """ + phrase_split = phrase.split(" ") + assert len(phrase_split) >= 12, "Not valid mnemonic phrase" + if len(phrase_split) > 13: + # Standard format + phrase = phrase_split[:24] + else: + # MyMonero format + phrase = phrase_split[:12] + wstr = "".join(word[:3] for word in phrase) + z = ((crc32(wstr.encode()) & 0xffffffff) ^ 0xffffffff ) >> 0 + z2 = ((z ^ 0xffffffff) >> 0) % len(phrase) + return phrase_split[z2] + +def generate_hex(n_bytes=32): + """Generate a secure and random hexadecimal string. 32 bytes by default, but arguments can override. + + :rtype: str + """ + h = hexlify(urandom(n_bytes)) + return "".join(h.decode("utf-8")) diff --git a/monero/wordlists/__init__.py b/monero/wordlists/__init__.py new file mode 100644 index 0000000..134b07e --- /dev/null +++ b/monero/wordlists/__init__.py @@ -0,0 +1 @@ +from . import english diff --git a/monero/wordlists/english.py b/monero/wordlists/english.py new file mode 100644 index 0000000..4ba4cd6 --- /dev/null +++ b/monero/wordlists/english.py @@ -0,0 +1,1660 @@ +# Copyright (c) 2014-2018, The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# list of words from: +# https://github.com/monero-project/monero/blob/master/src/mnemonics/english.h +# Most recent commit: 18216f19dd479466cb7fd38f52d23bddfcfd4880 + +wordlist = [ + "abbey", + "abducts", + "ability", + "ablaze", + "abnormal", + "abort", + "abrasive", + "absorb", + "abyss", + "academy", + "aces", + "aching", + "acidic", + "acoustic", + "acquire", + "across", + "actress", + "acumen", + "adapt", + "addicted", + "adept", + "adhesive", + "adjust", + "adopt", + "adrenalin", + "adult", + "adventure", + "aerial", + "afar", + "affair", + "afield", + "afloat", + "afoot", + "afraid", + "after", + "against", + "agenda", + "aggravate", + "agile", + "aglow", + "agnostic", + "agony", + "agreed", + "ahead", + "aided", + "ailments", + "aimless", + "airport", + "aisle", + "ajar", + "akin", + "alarms", + "album", + "alchemy", + "alerts", + "algebra", + "alkaline", + "alley", + "almost", + "aloof", + "alpine", + "already", + "also", + "altitude", + "alumni", + "always", + "amaze", + "ambush", + "amended", + "amidst", + "ammo", + "amnesty", + "among", + "amply", + "amused", + "anchor", + "android", + "anecdote", + "angled", + "ankle", + "annoyed", + "answers", + "antics", + "anvil", + "anxiety", + "anybody", + "apart", + "apex", + "aphid", + "aplomb", + "apology", + "apply", + "apricot", + "aptitude", + "aquarium", + "arbitrary", + "archer", + "ardent", + "arena", + "argue", + "arises", + "army", + "around", + "arrow", + "arsenic", + "artistic", + "ascend", + "ashtray", + "aside", + "asked", + "asleep", + "aspire", + "assorted", + "asylum", + "athlete", + "atlas", + "atom", + "atrium", + "attire", + "auburn", + "auctions", + "audio", + "august", + "aunt", + "austere", + "autumn", + "avatar", + "avidly", + "avoid", + "awakened", + "awesome", + "awful", + "awkward", + "awning", + "awoken", + "axes", + "axis", + "axle", + "aztec", + "azure", + "baby", + "bacon", + "badge", + "baffles", + "bagpipe", + "bailed", + "bakery", + "balding", + "bamboo", + "banjo", + "baptism", + "basin", + "batch", + "bawled", + "bays", + "because", + "beer", + "befit", + "begun", + "behind", + "being", + "below", + "bemused", + "benches", + "berries", + "bested", + "betting", + "bevel", + "beware", + "beyond", + "bias", + "bicycle", + "bids", + "bifocals", + "biggest", + "bikini", + "bimonthly", + "binocular", + "biology", + "biplane", + "birth", + "biscuit", + "bite", + "biweekly", + "blender", + "blip", + "bluntly", + "boat", + "bobsled", + "bodies", + "bogeys", + "boil", + "boldly", + "bomb", + "border", + "boss", + "both", + "bounced", + "bovine", + "bowling", + "boxes", + "boyfriend", + "broken", + "brunt", + "bubble", + "buckets", + "budget", + "buffet", + "bugs", + "building", + "bulb", + "bumper", + "bunch", + "business", + "butter", + "buying", + "buzzer", + "bygones", + "byline", + "bypass", + "cabin", + "cactus", + "cadets", + "cafe", + "cage", + "cajun", + "cake", + "calamity", + "camp", + "candy", + "casket", + "catch", + "cause", + "cavernous", + "cease", + "cedar", + "ceiling", + "cell", + "cement", + "cent", + "certain", + "chlorine", + "chrome", + "cider", + "cigar", + "cinema", + "circle", + "cistern", + "citadel", + "civilian", + "claim", + "click", + "clue", + "coal", + "cobra", + "cocoa", + "code", + "coexist", + "coffee", + "cogs", + "cohesive", + "coils", + "colony", + "comb", + "cool", + "copy", + "corrode", + "costume", + "cottage", + "cousin", + "cowl", + "criminal", + "cube", + "cucumber", + "cuddled", + "cuffs", + "cuisine", + "cunning", + "cupcake", + "custom", + "cycling", + "cylinder", + "cynical", + "dabbing", + "dads", + "daft", + "dagger", + "daily", + "damp", + "dangerous", + "dapper", + "darted", + "dash", + "dating", + "dauntless", + "dawn", + "daytime", + "dazed", + "debut", + "decay", + "dedicated", + "deepest", + "deftly", + "degrees", + "dehydrate", + "deity", + "dejected", + "delayed", + "demonstrate", + "dented", + "deodorant", + "depth", + "desk", + "devoid", + "dewdrop", + "dexterity", + "dialect", + "dice", + "diet", + "different", + "digit", + "dilute", + "dime", + "dinner", + "diode", + "diplomat", + "directed", + "distance", + "ditch", + "divers", + "dizzy", + "doctor", + "dodge", + "does", + "dogs", + "doing", + "dolphin", + "domestic", + "donuts", + "doorway", + "dormant", + "dosage", + "dotted", + "double", + "dove", + "down", + "dozen", + "dreams", + "drinks", + "drowning", + "drunk", + "drying", + "dual", + "dubbed", + "duckling", + "dude", + "duets", + "duke", + "dullness", + "dummy", + "dunes", + "duplex", + "duration", + "dusted", + "duties", + "dwarf", + "dwelt", + "dwindling", + "dying", + "dynamite", + "dyslexic", + "each", + "eagle", + "earth", + "easy", + "eating", + "eavesdrop", + "eccentric", + "echo", + "eclipse", + "economics", + "ecstatic", + "eden", + "edgy", + "edited", + "educated", + "eels", + "efficient", + "eggs", + "egotistic", + "eight", + "either", + "eject", + "elapse", + "elbow", + "eldest", + "eleven", + "elite", + "elope", + "else", + "eluded", + "emails", + "ember", + "emerge", + "emit", + "emotion", + "empty", + "emulate", + "energy", + "enforce", + "enhanced", + "enigma", + "enjoy", + "enlist", + "enmity", + "enough", + "enraged", + "ensign", + "entrance", + "envy", + "epoxy", + "equip", + "erase", + "erected", + "erosion", + "error", + "eskimos", + "espionage", + "essential", + "estate", + "etched", + "eternal", + "ethics", + "etiquette", + "evaluate", + "evenings", + "evicted", + "evolved", + "examine", + "excess", + "exhale", + "exit", + "exotic", + "exquisite", + "extra", + "exult", + "fabrics", + "factual", + "fading", + "fainted", + "faked", + "fall", + "family", + "fancy", + "farming", + "fatal", + "faulty", + "fawns", + "faxed", + "fazed", + "feast", + "february", + "federal", + "feel", + "feline", + "females", + "fences", + "ferry", + "festival", + "fetches", + "fever", + "fewest", + "fiat", + "fibula", + "fictional", + "fidget", + "fierce", + "fifteen", + "fight", + "films", + "firm", + "fishing", + "fitting", + "five", + "fixate", + "fizzle", + "fleet", + "flippant", + "flying", + "foamy", + "focus", + "foes", + "foggy", + "foiled", + "folding", + "fonts", + "foolish", + "fossil", + "fountain", + "fowls", + "foxes", + "foyer", + "framed", + "friendly", + "frown", + "fruit", + "frying", + "fudge", + "fuel", + "fugitive", + "fully", + "fuming", + "fungal", + "furnished", + "fuselage", + "future", + "fuzzy", + "gables", + "gadget", + "gags", + "gained", + "galaxy", + "gambit", + "gang", + "gasp", + "gather", + "gauze", + "gave", + "gawk", + "gaze", + "gearbox", + "gecko", + "geek", + "gels", + "gemstone", + "general", + "geometry", + "germs", + "gesture", + "getting", + "geyser", + "ghetto", + "ghost", + "giant", + "giddy", + "gifts", + "gigantic", + "gills", + "gimmick", + "ginger", + "girth", + "giving", + "glass", + "gleeful", + "glide", + "gnaw", + "gnome", + "goat", + "goblet", + "godfather", + "goes", + "goggles", + "going", + "goldfish", + "gone", + "goodbye", + "gopher", + "gorilla", + "gossip", + "gotten", + "gourmet", + "governing", + "gown", + "greater", + "grunt", + "guarded", + "guest", + "guide", + "gulp", + "gumball", + "guru", + "gusts", + "gutter", + "guys", + "gymnast", + "gypsy", + "gyrate", + "habitat", + "hacksaw", + "haggled", + "hairy", + "hamburger", + "happens", + "hashing", + "hatchet", + "haunted", + "having", + "hawk", + "haystack", + "hazard", + "hectare", + "hedgehog", + "heels", + "hefty", + "height", + "hemlock", + "hence", + "heron", + "hesitate", + "hexagon", + "hickory", + "hiding", + "highway", + "hijack", + "hiker", + "hills", + "himself", + "hinder", + "hippo", + "hire", + "history", + "hitched", + "hive", + "hoax", + "hobby", + "hockey", + "hoisting", + "hold", + "honked", + "hookup", + "hope", + "hornet", + "hospital", + "hotel", + "hounded", + "hover", + "howls", + "hubcaps", + "huddle", + "huge", + "hull", + "humid", + "hunter", + "hurried", + "husband", + "huts", + "hybrid", + "hydrogen", + "hyper", + "iceberg", + "icing", + "icon", + "identity", + "idiom", + "idled", + "idols", + "igloo", + "ignore", + "iguana", + "illness", + "imagine", + "imbalance", + "imitate", + "impel", + "inactive", + "inbound", + "incur", + "industrial", + "inexact", + "inflamed", + "ingested", + "initiate", + "injury", + "inkling", + "inline", + "inmate", + "innocent", + "inorganic", + "input", + "inquest", + "inroads", + "insult", + "intended", + "inundate", + "invoke", + "inwardly", + "ionic", + "irate", + "iris", + "irony", + "irritate", + "island", + "isolated", + "issued", + "italics", + "itches", + "items", + "itinerary", + "itself", + "ivory", + "jabbed", + "jackets", + "jaded", + "jagged", + "jailed", + "jamming", + "january", + "jargon", + "jaunt", + "javelin", + "jaws", + "jazz", + "jeans", + "jeers", + "jellyfish", + "jeopardy", + "jerseys", + "jester", + "jetting", + "jewels", + "jigsaw", + "jingle", + "jittery", + "jive", + "jobs", + "jockey", + "jogger", + "joining", + "joking", + "jolted", + "jostle", + "journal", + "joyous", + "jubilee", + "judge", + "juggled", + "juicy", + "jukebox", + "july", + "jump", + "junk", + "jury", + "justice", + "juvenile", + "kangaroo", + "karate", + "keep", + "kennel", + "kept", + "kernels", + "kettle", + "keyboard", + "kickoff", + "kidneys", + "king", + "kiosk", + "kisses", + "kitchens", + "kiwi", + "knapsack", + "knee", + "knife", + "knowledge", + "knuckle", + "koala", + "laboratory", + "ladder", + "lagoon", + "lair", + "lakes", + "lamb", + "language", + "laptop", + "large", + "last", + "later", + "launching", + "lava", + "lawsuit", + "layout", + "lazy", + "lectures", + "ledge", + "leech", + "left", + "legion", + "leisure", + "lemon", + "lending", + "leopard", + "lesson", + "lettuce", + "lexicon", + "liar", + "library", + "licks", + "lids", + "lied", + "lifestyle", + "light", + "likewise", + "lilac", + "limits", + "linen", + "lion", + "lipstick", + "liquid", + "listen", + "lively", + "loaded", + "lobster", + "locker", + "lodge", + "lofty", + "logic", + "loincloth", + "long", + "looking", + "lopped", + "lordship", + "losing", + "lottery", + "loudly", + "love", + "lower", + "loyal", + "lucky", + "luggage", + "lukewarm", + "lullaby", + "lumber", + "lunar", + "lurk", + "lush", + "luxury", + "lymph", + "lynx", + "lyrics", + "macro", + "madness", + "magically", + "mailed", + "major", + "makeup", + "malady", + "mammal", + "maps", + "masterful", + "match", + "maul", + "maverick", + "maximum", + "mayor", + "maze", + "meant", + "mechanic", + "medicate", + "meeting", + "megabyte", + "melting", + "memoir", + "menu", + "merger", + "mesh", + "metro", + "mews", + "mice", + "midst", + "mighty", + "mime", + "mirror", + "misery", + "mittens", + "mixture", + "moat", + "mobile", + "mocked", + "mohawk", + "moisture", + "molten", + "moment", + "money", + "moon", + "mops", + "morsel", + "mostly", + "motherly", + "mouth", + "movement", + "mowing", + "much", + "muddy", + "muffin", + "mugged", + "mullet", + "mumble", + "mundane", + "muppet", + "mural", + "musical", + "muzzle", + "myriad", + "mystery", + "myth", + "nabbing", + "nagged", + "nail", + "names", + "nanny", + "napkin", + "narrate", + "nasty", + "natural", + "nautical", + "navy", + "nearby", + "necklace", + "needed", + "negative", + "neither", + "neon", + "nephew", + "nerves", + "nestle", + "network", + "neutral", + "never", + "newt", + "nexus", + "nibs", + "niche", + "niece", + "nifty", + "nightly", + "nimbly", + "nineteen", + "nirvana", + "nitrogen", + "nobody", + "nocturnal", + "nodes", + "noises", + "nomad", + "noodles", + "northern", + "nostril", + "noted", + "nouns", + "novelty", + "nowhere", + "nozzle", + "nuance", + "nucleus", + "nudged", + "nugget", + "nuisance", + "null", + "number", + "nuns", + "nurse", + "nutshell", + "nylon", + "oaks", + "oars", + "oasis", + "oatmeal", + "obedient", + "object", + "obliged", + "obnoxious", + "observant", + "obtains", + "obvious", + "occur", + "ocean", + "october", + "odds", + "odometer", + "offend", + "often", + "oilfield", + "ointment", + "okay", + "older", + "olive", + "olympics", + "omega", + "omission", + "omnibus", + "onboard", + "oncoming", + "oneself", + "ongoing", + "onion", + "online", + "onslaught", + "onto", + "onward", + "oozed", + "opacity", + "opened", + "opposite", + "optical", + "opus", + "orange", + "orbit", + "orchid", + "orders", + "organs", + "origin", + "ornament", + "orphans", + "oscar", + "ostrich", + "otherwise", + "otter", + "ouch", + "ought", + "ounce", + "ourselves", + "oust", + "outbreak", + "oval", + "oven", + "owed", + "owls", + "owner", + "oxidant", + "oxygen", + "oyster", + "ozone", + "pact", + "paddles", + "pager", + "pairing", + "palace", + "pamphlet", + "pancakes", + "paper", + "paradise", + "pastry", + "patio", + "pause", + "pavements", + "pawnshop", + "payment", + "peaches", + "pebbles", + "peculiar", + "pedantic", + "peeled", + "pegs", + "pelican", + "pencil", + "people", + "pepper", + "perfect", + "pests", + "petals", + "phase", + "pheasants", + "phone", + "phrases", + "physics", + "piano", + "picked", + "pierce", + "pigment", + "piloted", + "pimple", + "pinched", + "pioneer", + "pipeline", + "pirate", + "pistons", + "pitched", + "pivot", + "pixels", + "pizza", + "playful", + "pledge", + "pliers", + "plotting", + "plus", + "plywood", + "poaching", + "pockets", + "podcast", + "poetry", + "point", + "poker", + "polar", + "ponies", + "pool", + "popular", + "portents", + "possible", + "potato", + "pouch", + "poverty", + "powder", + "pram", + "present", + "pride", + "problems", + "pruned", + "prying", + "psychic", + "public", + "puck", + "puddle", + "puffin", + "pulp", + "pumpkins", + "punch", + "puppy", + "purged", + "push", + "putty", + "puzzled", + "pylons", + "pyramid", + "python", + "queen", + "quick", + "quote", + "rabbits", + "racetrack", + "radar", + "rafts", + "rage", + "railway", + "raking", + "rally", + "ramped", + "randomly", + "rapid", + "rarest", + "rash", + "rated", + "ravine", + "rays", + "razor", + "react", + "rebel", + "recipe", + "reduce", + "reef", + "refer", + "regular", + "reheat", + "reinvest", + "rejoices", + "rekindle", + "relic", + "remedy", + "renting", + "reorder", + "repent", + "request", + "reruns", + "rest", + "return", + "reunion", + "revamp", + "rewind", + "rhino", + "rhythm", + "ribbon", + "richly", + "ridges", + "rift", + "rigid", + "rims", + "ringing", + "riots", + "ripped", + "rising", + "ritual", + "river", + "roared", + "robot", + "rockets", + "rodent", + "rogue", + "roles", + "romance", + "roomy", + "roped", + "roster", + "rotate", + "rounded", + "rover", + "rowboat", + "royal", + "ruby", + "rudely", + "ruffled", + "rugged", + "ruined", + "ruling", + "rumble", + "runway", + "rural", + "rustled", + "ruthless", + "sabotage", + "sack", + "sadness", + "safety", + "saga", + "sailor", + "sake", + "salads", + "sample", + "sanity", + "sapling", + "sarcasm", + "sash", + "satin", + "saucepan", + "saved", + "sawmill", + "saxophone", + "sayings", + "scamper", + "scenic", + "school", + "science", + "scoop", + "scrub", + "scuba", + "seasons", + "second", + "sedan", + "seeded", + "segments", + "seismic", + "selfish", + "semifinal", + "sensible", + "september", + "sequence", + "serving", + "session", + "setup", + "seventh", + "sewage", + "shackles", + "shelter", + "shipped", + "shocking", + "shrugged", + "shuffled", + "shyness", + "siblings", + "sickness", + "sidekick", + "sieve", + "sifting", + "sighting", + "silk", + "simplest", + "sincerely", + "sipped", + "siren", + "situated", + "sixteen", + "sizes", + "skater", + "skew", + "skirting", + "skulls", + "skydive", + "slackens", + "sleepless", + "slid", + "slower", + "slug", + "smash", + "smelting", + "smidgen", + "smog", + "smuggled", + "snake", + "sneeze", + "sniff", + "snout", + "snug", + "soapy", + "sober", + "soccer", + "soda", + "software", + "soggy", + "soil", + "solved", + "somewhere", + "sonic", + "soothe", + "soprano", + "sorry", + "southern", + "sovereign", + "sowed", + "soya", + "space", + "speedy", + "sphere", + "spiders", + "splendid", + "spout", + "sprig", + "spud", + "spying", + "square", + "stacking", + "stellar", + "stick", + "stockpile", + "strained", + "stunning", + "stylishly", + "subtly", + "succeed", + "suddenly", + "suede", + "suffice", + "sugar", + "suitcase", + "sulking", + "summon", + "sunken", + "superior", + "surfer", + "sushi", + "suture", + "swagger", + "swept", + "swiftly", + "sword", + "swung", + "syllabus", + "symptoms", + "syndrome", + "syringe", + "system", + "taboo", + "tacit", + "tadpoles", + "tagged", + "tail", + "taken", + "talent", + "tamper", + "tanks", + "tapestry", + "tarnished", + "tasked", + "tattoo", + "taunts", + "tavern", + "tawny", + "taxi", + "teardrop", + "technical", + "tedious", + "teeming", + "tell", + "template", + "tender", + "tepid", + "tequila", + "terminal", + "testing", + "tether", + "textbook", + "thaw", + "theatrics", + "thirsty", + "thorn", + "threaten", + "thumbs", + "thwart", + "ticket", + "tidy", + "tiers", + "tiger", + "tilt", + "timber", + "tinted", + "tipsy", + "tirade", + "tissue", + "titans", + "toaster", + "tobacco", + "today", + "toenail", + "toffee", + "together", + "toilet", + "token", + "tolerant", + "tomorrow", + "tonic", + "toolbox", + "topic", + "torch", + "tossed", + "total", + "touchy", + "towel", + "toxic", + "toyed", + "trash", + "trendy", + "tribal", + "trolling", + "truth", + "trying", + "tsunami", + "tubes", + "tucks", + "tudor", + "tuesday", + "tufts", + "tugs", + "tuition", + "tulips", + "tumbling", + "tunnel", + "turnip", + "tusks", + "tutor", + "tuxedo", + "twang", + "tweezers", + "twice", + "twofold", + "tycoon", + "typist", + "tyrant", + "ugly", + "ulcers", + "ultimate", + "umbrella", + "umpire", + "unafraid", + "unbending", + "uncle", + "under", + "uneven", + "unfit", + "ungainly", + "unhappy", + "union", + "unjustly", + "unknown", + "unlikely", + "unmask", + "unnoticed", + "unopened", + "unplugs", + "unquoted", + "unrest", + "unsafe", + "until", + "unusual", + "unveil", + "unwind", + "unzip", + "upbeat", + "upcoming", + "update", + "upgrade", + "uphill", + "upkeep", + "upload", + "upon", + "upper", + "upright", + "upstairs", + "uptight", + "upwards", + "urban", + "urchins", + "urgent", + "usage", + "useful", + "usher", + "using", + "usual", + "utensils", + "utility", + "utmost", + "utopia", + "uttered", + "vacation", + "vague", + "vain", + "value", + "vampire", + "vane", + "vapidly", + "vary", + "vastness", + "vats", + "vaults", + "vector", + "veered", + "vegan", + "vehicle", + "vein", + "velvet", + "venomous", + "verification", + "vessel", + "veteran", + "vexed", + "vials", + "vibrate", + "victim", + "video", + "viewpoint", + "vigilant", + "viking", + "village", + "vinegar", + "violin", + "vipers", + "virtual", + "visited", + "vitals", + "vivid", + "vixen", + "vocal", + "vogue", + "voice", + "volcano", + "vortex", + "voted", + "voucher", + "vowels", + "voyage", + "vulture", + "wade", + "waffle", + "wagtail", + "waist", + "waking", + "wallets", + "wanted", + "warped", + "washing", + "water", + "waveform", + "waxing", + "wayside", + "weavers", + "website", + "wedge", + "weekday", + "weird", + "welders", + "went", + "wept", + "were", + "western", + "wetsuit", + "whale", + "when", + "whipped", + "whole", + "wickets", + "width", + "wield", + "wife", + "wiggle", + "wildly", + "winter", + "wipeout", + "wiring", + "wise", + "withdrawn", + "wives", + "wizard", + "wobbly", + "woes", + "woken", + "wolf", + "womanly", + "wonders", + "woozy", + "worry", + "wounded", + "woven", + "wrap", + "wrist", + "wrong", + "yacht", + "yahoo", + "yanks", + "yard", + "yawning", + "yearbook", + "yellow", + "yesterday", + "yeti", + "yields", + "yodel", + "yoga", + "younger", + "yoyo", + "zapped", + "zeal", + "zebra", + "zero", + "zesty", + "zigzags", + "zinger", + "zippers", + "zodiac", + "zombie", + "zones", + "zoom" +] diff --git a/tests/__init__.py b/tests/__init__.py index ed42103..fff48c0 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -3,3 +3,4 @@ from . import test_numbers from . import test_transaction from . import test_wallet from . import test_jsonrpcwallet +from . import test_seed diff --git a/tests/test_seed.py b/tests/test_seed.py new file mode 100644 index 0000000..cde0c84 --- /dev/null +++ b/tests/test_seed.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python + +import unittest +from monero.seed import Seed, get_checksum + +class SeedTestCase(unittest.TestCase): + + def test_mnemonic_seed(self): + # Known good 25 word seed phrases should construct a class, validate checksum, and register valid hex + seed = Seed("wedge going quick racetrack auburn physics lectures light waist axes whipped habitat square awkward together injury niece nugget guarded hive obnoxious waxing faked folding square") + self.assertTrue(seed.validate_checksum()) + self.assertEqual(seed.hex, "8ffa9f586b86d294d93731765d192765311bddc76a4fa60311f8af36bbf6fb06") + + # Known good 24 word seed phrases should construct a class, store phrase with valid checksum, and register valid hex + seed = Seed("wedge going quick racetrack auburn physics lectures light waist axes whipped habitat square awkward together injury niece nugget guarded hive obnoxious waxing faked folding") + seed.encode_seed() + self.assertTrue(seed.validate_checksum()) + self.assertEqual(seed.hex, "8ffa9f586b86d294d93731765d192765311bddc76a4fa60311f8af36bbf6fb06") + + # Known good 25 word hexadecimal strings should construct a class, store phrase with valid checksum, and register valid hex + seed = Seed("8ffa9f586b86d294d93731765d192765311bddc76a4fa60311f8af36bbf6fb06") + self.assertTrue(seed.validate_checksum()) + self.assertEqual(seed.phrase, "wedge going quick racetrack auburn physics lectures light waist axes whipped habitat square awkward together injury niece nugget guarded hive obnoxious waxing faked folding square") + + # Known good 13 word seed phrases should construct a class, validate checksum, and register valid hex + seed = Seed("ought knowledge upright innocent eldest nerves gopher fowls below exquisite aces basin fowls") + self.assertTrue(seed.validate_checksum()) + self.assertEqual(seed.hex, "932d70711acc2d536ca11fcb79e05516") + + # Known good 12 word seed phrases should construct a class, store phrase with valid checksum, and register valid hex + seed = Seed("ought knowledge upright innocent eldest nerves gopher fowls below exquisite aces basin") + seed.encode_seed() + self.assertTrue(seed.validate_checksum()) + self.assertEqual(seed.hex, "932d70711acc2d536ca11fcb79e05516") + + # Known good 13 word hexadecimal strings should construct a class, store phrase with valid checksum, and register valid hex + seed = Seed("932d70711acc2d536ca11fcb79e05516") + self.assertTrue(seed.validate_checksum()) + self.assertEqual(seed.phrase, "ought knowledge upright innocent eldest nerves gopher fowls below exquisite aces basin fowls") + + # Generated seed phrases should be 25 words, validate checksum, register valid hex, and return matching outputs for decode/encode + seed = Seed() + seed_split = seed.phrase.split(" ") + self.assertTrue(len(seed_split) == 25) + self.assertTrue(seed.validate_checksum()) + self.assertTrue(len(seed.hex) % 8 == 0) + self.assertEqual(seed.hex, seed.decode_seed()) + self.assertEqual(seed.phrase, seed.encode_seed()) + + # Invalid phrases should not be allowed + with self.assertRaises(ValueError) as ts: + Seed("oh damn you thought fool") + self.assertEqual(ts.expected, ValueError) + with self.assertRaises(ValueError) as ts: + Seed("Thi5isMyS3cr3tPa55w0rd") + self.assertEqual(ts.expected, ValueError) + with self.assertRaises(ValueError) as ts: + Seed(" ") + self.assertEqual(ts.expected, ValueError) + with self.assertRaises(ValueError) as ts: + Seed("\\x008") + self.assertEqual(ts.expected, ValueError) + +if __name__ == "__main__": + unittest.main()