Include mnemonic dependency directly.
This commit is contained in:
		
							parent
							
								
									d3e3c3c95b
								
							
						
					
					
						commit
						ae1df0b556
					
				
							
								
								
									
										3
									
								
								basicswap/contrib/mnemonic/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								basicswap/contrib/mnemonic/__init__.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					from .mnemonic import Mnemonic
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__all__ = ["Mnemonic"]
 | 
				
			||||||
							
								
								
									
										298
									
								
								basicswap/contrib/mnemonic/mnemonic.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										298
									
								
								basicswap/contrib/mnemonic/mnemonic.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,298 @@
 | 
				
			|||||||
 | 
					#
 | 
				
			||||||
 | 
					# Copyright (c) 2013 Pavol Rusnak
 | 
				
			||||||
 | 
					# Copyright (c) 2017 mruddy
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# 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.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					from __future__ import annotations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import hashlib
 | 
				
			||||||
 | 
					import hmac
 | 
				
			||||||
 | 
					import itertools
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import secrets
 | 
				
			||||||
 | 
					import typing as t
 | 
				
			||||||
 | 
					import unicodedata
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PBKDF2_ROUNDS = 2048
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ConfigurationError(Exception):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Refactored code segments from <https://github.com/keis/base58>
 | 
				
			||||||
 | 
					def b58encode(v: bytes) -> str:
 | 
				
			||||||
 | 
					    alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    p, acc = 1, 0
 | 
				
			||||||
 | 
					    for c in reversed(v):
 | 
				
			||||||
 | 
					        acc += p * c
 | 
				
			||||||
 | 
					        p = p << 8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    string = ""
 | 
				
			||||||
 | 
					    while acc:
 | 
				
			||||||
 | 
					        acc, idx = divmod(acc, 58)
 | 
				
			||||||
 | 
					        string = alphabet[idx : idx + 1] + string
 | 
				
			||||||
 | 
					    return string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Mnemonic(object):
 | 
				
			||||||
 | 
					    def __init__(self, language: str = "english", wordlist: list[str] | None = None):
 | 
				
			||||||
 | 
					        self.radix = 2048
 | 
				
			||||||
 | 
					        self.language = language
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if wordlist is None:
 | 
				
			||||||
 | 
					            d = os.path.join(os.path.dirname(__file__), f"wordlist/{language}.txt")
 | 
				
			||||||
 | 
					            if os.path.exists(d) and os.path.isfile(d):
 | 
				
			||||||
 | 
					                with open(d, "r", encoding="utf-8") as f:
 | 
				
			||||||
 | 
					                    wordlist = [w.strip() for w in f.readlines()]
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                raise ConfigurationError("Language not detected")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if len(wordlist) != self.radix:
 | 
				
			||||||
 | 
					            raise ConfigurationError(f"Wordlist must contain {self.radix} words.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wordlist = wordlist
 | 
				
			||||||
 | 
					        # Japanese must be joined by ideographic space
 | 
				
			||||||
 | 
					        self.delimiter = "\u3000" if language == "japanese" else " "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def list_languages(cls) -> list[str]:
 | 
				
			||||||
 | 
					        return [
 | 
				
			||||||
 | 
					            f.split(".")[0]
 | 
				
			||||||
 | 
					            for f in os.listdir(os.path.join(os.path.dirname(__file__), "wordlist"))
 | 
				
			||||||
 | 
					            if f.endswith(".txt")
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def normalize_string(txt: t.AnyStr) -> str:
 | 
				
			||||||
 | 
					        if isinstance(txt, bytes):
 | 
				
			||||||
 | 
					            utxt = txt.decode("utf8")
 | 
				
			||||||
 | 
					        elif isinstance(txt, str):
 | 
				
			||||||
 | 
					            utxt = txt
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            raise TypeError("String value expected")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return unicodedata.normalize("NFKD", utxt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def detect_language(cls, code: str) -> str:
 | 
				
			||||||
 | 
					        """Scan the Mnemonic until the language becomes unambiguous, including as abbreviation prefixes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Unfortunately, there are valid words that are ambiguous between languages, which are complete words
 | 
				
			||||||
 | 
					        in one language and are prefixes in another:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            english: abandon ... about
 | 
				
			||||||
 | 
					            french:  abandon ... aboutir
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        If prefixes remain ambiguous, require exactly one language where word(s) match exactly.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        code = cls.normalize_string(code)
 | 
				
			||||||
 | 
					        possible = set(cls(lang) for lang in cls.list_languages())
 | 
				
			||||||
 | 
					        words = set(code.split())
 | 
				
			||||||
 | 
					        for word in words:
 | 
				
			||||||
 | 
					            # possible languages have candidate(s) starting with the word/prefix
 | 
				
			||||||
 | 
					            possible = set(
 | 
				
			||||||
 | 
					                p for p in possible if any(c.startswith(word) for c in p.wordlist)
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            if not possible:
 | 
				
			||||||
 | 
					                raise ConfigurationError(f"Language unrecognized for {word!r}")
 | 
				
			||||||
 | 
					        if len(possible) == 1:
 | 
				
			||||||
 | 
					            return possible.pop().language
 | 
				
			||||||
 | 
					        # Multiple languages match: A prefix in many, but an exact match in one determines language.
 | 
				
			||||||
 | 
					        complete = set()
 | 
				
			||||||
 | 
					        for word in words:
 | 
				
			||||||
 | 
					            exact = set(p for p in possible if word in p.wordlist)
 | 
				
			||||||
 | 
					            if len(exact) == 1:
 | 
				
			||||||
 | 
					                complete.update(exact)
 | 
				
			||||||
 | 
					        if len(complete) == 1:
 | 
				
			||||||
 | 
					            return complete.pop().language
 | 
				
			||||||
 | 
					        raise ConfigurationError(
 | 
				
			||||||
 | 
					            f"Language ambiguous between {', '.join(p.language for p in possible)}"
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def generate(self, strength: int = 128) -> str:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Create a new mnemonic using a random generated number as entropy.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        As defined in BIP39, the entropy must be a multiple of 32 bits, and its size must be between 128 and 256 bits.
 | 
				
			||||||
 | 
					        Therefore the possible values for `strength` are 128, 160, 192, 224 and 256.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        If not provided, the default entropy length will be set to 128 bits.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        The return is a list of words that encodes the generated entropy.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param strength: Number of bytes used as entropy
 | 
				
			||||||
 | 
					        :type strength: int
 | 
				
			||||||
 | 
					        :return: A randomly generated mnemonic
 | 
				
			||||||
 | 
					        :rtype: str
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if strength not in [128, 160, 192, 224, 256]:
 | 
				
			||||||
 | 
					            raise ValueError(
 | 
				
			||||||
 | 
					                "Invalid strength value. Allowed values are [128, 160, 192, 224, 256]."
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        return self.to_mnemonic(secrets.token_bytes(strength // 8))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Adapted from <http://tinyurl.com/oxmn476>
 | 
				
			||||||
 | 
					    def to_entropy(self, words: list[str] | str) -> bytearray:
 | 
				
			||||||
 | 
					        if not isinstance(words, list):
 | 
				
			||||||
 | 
					            words = words.split(" ")
 | 
				
			||||||
 | 
					        if len(words) not in [12, 15, 18, 21, 24]:
 | 
				
			||||||
 | 
					            raise ValueError(
 | 
				
			||||||
 | 
					                "Number of words must be one of the following: [12, 15, 18, 21, 24], but it is not (%d)."
 | 
				
			||||||
 | 
					                % len(words)
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        # Look up all the words in the list and construct the
 | 
				
			||||||
 | 
					        # concatenation of the original entropy and the checksum.
 | 
				
			||||||
 | 
					        concatLenBits = len(words) * 11
 | 
				
			||||||
 | 
					        concatBits = [False] * concatLenBits
 | 
				
			||||||
 | 
					        wordindex = 0
 | 
				
			||||||
 | 
					        for word in words:
 | 
				
			||||||
 | 
					            # Find the words index in the wordlist
 | 
				
			||||||
 | 
					            ndx = self.wordlist.index(self.normalize_string(word))
 | 
				
			||||||
 | 
					            if ndx < 0:
 | 
				
			||||||
 | 
					                raise LookupError('Unable to find "%s" in word list.' % word)
 | 
				
			||||||
 | 
					            # Set the next 11 bits to the value of the index.
 | 
				
			||||||
 | 
					            for ii in range(11):
 | 
				
			||||||
 | 
					                concatBits[(wordindex * 11) + ii] = (ndx & (1 << (10 - ii))) != 0
 | 
				
			||||||
 | 
					            wordindex += 1
 | 
				
			||||||
 | 
					        checksumLengthBits = concatLenBits // 33
 | 
				
			||||||
 | 
					        entropyLengthBits = concatLenBits - checksumLengthBits
 | 
				
			||||||
 | 
					        # Extract original entropy as bytes.
 | 
				
			||||||
 | 
					        entropy = bytearray(entropyLengthBits // 8)
 | 
				
			||||||
 | 
					        for ii in range(len(entropy)):
 | 
				
			||||||
 | 
					            for jj in range(8):
 | 
				
			||||||
 | 
					                if concatBits[(ii * 8) + jj]:
 | 
				
			||||||
 | 
					                    entropy[ii] |= 1 << (7 - jj)
 | 
				
			||||||
 | 
					        # Take the digest of the entropy.
 | 
				
			||||||
 | 
					        hashBytes = hashlib.sha256(entropy).digest()
 | 
				
			||||||
 | 
					        hashBits = list(
 | 
				
			||||||
 | 
					            itertools.chain.from_iterable(
 | 
				
			||||||
 | 
					                [c & (1 << (7 - i)) != 0 for i in range(8)] for c in hashBytes
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        # Check all the checksum bits.
 | 
				
			||||||
 | 
					        for i in range(checksumLengthBits):
 | 
				
			||||||
 | 
					            if concatBits[entropyLengthBits + i] != hashBits[i]:
 | 
				
			||||||
 | 
					                raise ValueError("Failed checksum.")
 | 
				
			||||||
 | 
					        return entropy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_mnemonic(self, data: bytes) -> str:
 | 
				
			||||||
 | 
					        if len(data) not in [16, 20, 24, 28, 32]:
 | 
				
			||||||
 | 
					            raise ValueError(
 | 
				
			||||||
 | 
					                f"Data length should be one of the following: [16, 20, 24, 28, 32], but it is not {len(data)}."
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        h = hashlib.sha256(data).hexdigest()
 | 
				
			||||||
 | 
					        b = (
 | 
				
			||||||
 | 
					            bin(int.from_bytes(data, byteorder="big"))[2:].zfill(len(data) * 8)
 | 
				
			||||||
 | 
					            + bin(int(h, 16))[2:].zfill(256)[: len(data) * 8 // 32]
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        result = []
 | 
				
			||||||
 | 
					        for i in range(len(b) // 11):
 | 
				
			||||||
 | 
					            idx = int(b[i * 11 : (i + 1) * 11], 2)
 | 
				
			||||||
 | 
					            result.append(self.wordlist[idx])
 | 
				
			||||||
 | 
					        return self.delimiter.join(result)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check(self, mnemonic: str) -> bool:
 | 
				
			||||||
 | 
					        mnemonic_list = self.normalize_string(mnemonic).split(" ")
 | 
				
			||||||
 | 
					        # list of valid mnemonic lengths
 | 
				
			||||||
 | 
					        if len(mnemonic_list) not in [12, 15, 18, 21, 24]:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            idx = map(
 | 
				
			||||||
 | 
					                lambda x: bin(self.wordlist.index(x))[2:].zfill(11), mnemonic_list
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            b = "".join(idx)
 | 
				
			||||||
 | 
					        except ValueError:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        l = len(b)  # noqa: E741
 | 
				
			||||||
 | 
					        d = b[: l // 33 * 32]
 | 
				
			||||||
 | 
					        h = b[-l // 33 :]
 | 
				
			||||||
 | 
					        nd = int(d, 2).to_bytes(l // 33 * 4, byteorder="big")
 | 
				
			||||||
 | 
					        nh = bin(int(hashlib.sha256(nd).hexdigest(), 16))[2:].zfill(256)[: l // 33]
 | 
				
			||||||
 | 
					        return h == nh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def expand_word(self, prefix: str) -> str:
 | 
				
			||||||
 | 
					        if prefix in self.wordlist:
 | 
				
			||||||
 | 
					            return prefix
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            matches = [word for word in self.wordlist if word.startswith(prefix)]
 | 
				
			||||||
 | 
					            if len(matches) == 1:  # matched exactly one word in the wordlist
 | 
				
			||||||
 | 
					                return matches[0]
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                # exact match not found.
 | 
				
			||||||
 | 
					                # this is not a validation routine, just return the input
 | 
				
			||||||
 | 
					                return prefix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def expand(self, mnemonic: str) -> str:
 | 
				
			||||||
 | 
					        return " ".join(map(self.expand_word, mnemonic.split(" ")))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def to_seed(cls, mnemonic: str, passphrase: str = "") -> bytes:
 | 
				
			||||||
 | 
					        mnemonic = cls.normalize_string(mnemonic)
 | 
				
			||||||
 | 
					        passphrase = cls.normalize_string(passphrase)
 | 
				
			||||||
 | 
					        passphrase = "mnemonic" + passphrase
 | 
				
			||||||
 | 
					        mnemonic_bytes = mnemonic.encode("utf-8")
 | 
				
			||||||
 | 
					        passphrase_bytes = passphrase.encode("utf-8")
 | 
				
			||||||
 | 
					        stretched = hashlib.pbkdf2_hmac(
 | 
				
			||||||
 | 
					            "sha512", mnemonic_bytes, passphrase_bytes, PBKDF2_ROUNDS
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        return stretched[:64]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def to_hd_master_key(seed: bytes, testnet: bool = False) -> str:
 | 
				
			||||||
 | 
					        if len(seed) != 64:
 | 
				
			||||||
 | 
					            raise ValueError("Provided seed should have length of 64")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Compute HMAC-SHA512 of seed
 | 
				
			||||||
 | 
					        seed = hmac.new(b"Bitcoin seed", seed, digestmod=hashlib.sha512).digest()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Serialization format can be found at: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#serialization-format
 | 
				
			||||||
 | 
					        xprv = b"\x04\x88\xad\xe4"  # Version for private mainnet
 | 
				
			||||||
 | 
					        if testnet:
 | 
				
			||||||
 | 
					            xprv = b"\x04\x35\x83\x94"  # Version for private testnet
 | 
				
			||||||
 | 
					        xprv += b"\x00" * 9  # Depth, parent fingerprint, and child number
 | 
				
			||||||
 | 
					        xprv += seed[32:]  # Chain code
 | 
				
			||||||
 | 
					        xprv += b"\x00" + seed[:32]  # Master key
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Double hash using SHA256
 | 
				
			||||||
 | 
					        hashed_xprv = hashlib.sha256(xprv).digest()
 | 
				
			||||||
 | 
					        hashed_xprv = hashlib.sha256(hashed_xprv).digest()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Append 4 bytes of checksum
 | 
				
			||||||
 | 
					        xprv += hashed_xprv[:4]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Return base58
 | 
				
			||||||
 | 
					        return b58encode(xprv)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def main() -> None:
 | 
				
			||||||
 | 
					    import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if len(sys.argv) > 1:
 | 
				
			||||||
 | 
					        hex_data = sys.argv[1]
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        hex_data = sys.stdin.readline().strip()
 | 
				
			||||||
 | 
					    data = bytes.fromhex(hex_data)
 | 
				
			||||||
 | 
					    m = Mnemonic("english")
 | 
				
			||||||
 | 
					    print(m.to_mnemonic(data))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    main()
 | 
				
			||||||
							
								
								
									
										1
									
								
								basicswap/contrib/mnemonic/py.typed
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								basicswap/contrib/mnemonic/py.typed
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					# Marker file for PEP 561.
 | 
				
			||||||
							
								
								
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/chinese_simplified.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/chinese_simplified.txt
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/chinese_traditional.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/chinese_traditional.txt
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/czech.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/czech.txt
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/english.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/english.txt
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/french.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/french.txt
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/italian.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/italian.txt
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/japanese.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/japanese.txt
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/korean.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/korean.txt
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/portuguese.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/portuguese.txt
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/russian.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/russian.txt
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/spanish.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/spanish.txt
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/turkish.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2048
									
								
								basicswap/contrib/mnemonic/wordlist/turkish.txt
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -8,7 +8,7 @@
 | 
				
			|||||||
from .btc import BTCInterface
 | 
					from .btc import BTCInterface
 | 
				
			||||||
from basicswap.chainparams import Coins
 | 
					from basicswap.chainparams import Coins
 | 
				
			||||||
from basicswap.util.address import decodeAddress
 | 
					from basicswap.util.address import decodeAddress
 | 
				
			||||||
from mnemonic import Mnemonic
 | 
					from basicswap.contrib.mnemonic import Mnemonic
 | 
				
			||||||
from basicswap.contrib.test_framework.script import (
 | 
					from basicswap.contrib.test_framework.script import (
 | 
				
			||||||
    CScript,
 | 
					    CScript,
 | 
				
			||||||
    OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG
 | 
					    OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG
 | 
				
			||||||
@ -25,11 +25,11 @@ class DASHInterface(BTCInterface):
 | 
				
			|||||||
        self._wallet_passphrase = ''
 | 
					        self._wallet_passphrase = ''
 | 
				
			||||||
        self._have_checked_seed = False
 | 
					        self._have_checked_seed = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def seedToMnemonic(self, key: bytes) -> str:
 | 
					    def entropyToMnemonic(self, key: bytes) -> str:
 | 
				
			||||||
        return Mnemonic('english').to_mnemonic(key)
 | 
					        return Mnemonic('english').to_mnemonic(key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def initialiseWallet(self, key: bytes):
 | 
					    def initialiseWallet(self, key: bytes):
 | 
				
			||||||
        words = self.seedToMnemonic(key)
 | 
					        words = self.entropyToMnemonic(key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        mnemonic_passphrase = ''
 | 
					        mnemonic_passphrase = ''
 | 
				
			||||||
        self.rpc_wallet('upgradetohd', [words, mnemonic_passphrase, self._wallet_passphrase])
 | 
					        self.rpc_wallet('upgradetohd', [words, mnemonic_passphrase, self._wallet_passphrase])
 | 
				
			||||||
 | 
				
			|||||||
@ -21,6 +21,7 @@ from basicswap.interface.btc import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
from basicswap.rpc import make_rpc_func
 | 
					from basicswap.rpc import make_rpc_func
 | 
				
			||||||
from basicswap.chainparams import Coins
 | 
					from basicswap.chainparams import Coins
 | 
				
			||||||
 | 
					from basicswap.contrib.mnemonic import Mnemonic
 | 
				
			||||||
from basicswap.interface.contrib.nav_test_framework.mininode import (
 | 
					from basicswap.interface.contrib.nav_test_framework.mininode import (
 | 
				
			||||||
    CTxIn,
 | 
					    CTxIn,
 | 
				
			||||||
    CTxOut,
 | 
					    CTxOut,
 | 
				
			||||||
@ -52,7 +53,6 @@ from basicswap.interface.contrib.nav_test_framework.script import (
 | 
				
			|||||||
    SIGHASH_ALL,
 | 
					    SIGHASH_ALL,
 | 
				
			||||||
    SegwitVersion1SignatureHash,
 | 
					    SegwitVersion1SignatureHash,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from mnemonic import Mnemonic
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NAVInterface(BTCInterface):
 | 
					class NAVInterface(BTCInterface):
 | 
				
			||||||
@ -77,7 +77,7 @@ class NAVInterface(BTCInterface):
 | 
				
			|||||||
        # p2sh-p2wsh
 | 
					        # p2sh-p2wsh
 | 
				
			||||||
        return True
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def seedToMnemonic(self, key: bytes) -> None:
 | 
					    def entropyToMnemonic(self, key: bytes) -> None:
 | 
				
			||||||
        return Mnemonic('english').to_mnemonic(key)
 | 
					        return Mnemonic('english').to_mnemonic(key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def initialiseWallet(self, key):
 | 
					    def initialiseWallet(self, key):
 | 
				
			||||||
 | 
				
			|||||||
@ -688,7 +688,7 @@ def js_getcoinseed(self, url_split, post_string, is_json) -> bytes:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    seed_key = swap_client.getWalletKey(coin, 1)
 | 
					    seed_key = swap_client.getWalletKey(coin, 1)
 | 
				
			||||||
    if coin == Coins.DASH:
 | 
					    if coin == Coins.DASH:
 | 
				
			||||||
        return bytes(json.dumps({'coin': ci.ticker(), 'seed': seed_key.hex(), 'mnemonic': ci.seedToMnemonic(seed_key)}), 'UTF-8')
 | 
					        return bytes(json.dumps({'coin': ci.ticker(), 'seed': seed_key.hex(), 'mnemonic': ci.entropyToMnemonic(seed_key)}), 'UTF-8')
 | 
				
			||||||
    seed_id = ci.getSeedHash(seed_key)
 | 
					    seed_id = ci.getSeedHash(seed_key)
 | 
				
			||||||
    return bytes(json.dumps({'coin': ci.ticker(), 'seed': seed_key.hex(), 'seed_id': seed_id.hex()}), 'UTF-8')
 | 
					    return bytes(json.dumps({'coin': ci.ticker(), 'seed': seed_key.hex(), 'seed_id': seed_id.hex()}), 'UTF-8')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@
 | 
				
			|||||||
==============
 | 
					==============
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Remove protobuf and protoc dependencies.
 | 
					- Remove protobuf and protoc dependencies.
 | 
				
			||||||
 | 
					- Include mnemonic dependency directly.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
0.13.1
 | 
					0.13.1
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1
									
								
								guix.scm
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								guix.scm
									
									
									
									
									
								
							@ -154,7 +154,6 @@
 | 
				
			|||||||
    python-gnupg
 | 
					    python-gnupg
 | 
				
			||||||
    python-jinja2
 | 
					    python-jinja2
 | 
				
			||||||
    python-pysocks
 | 
					    python-pysocks
 | 
				
			||||||
    python-mnemonic
 | 
					 | 
				
			||||||
    ))
 | 
					    ))
 | 
				
			||||||
  (native-inputs
 | 
					  (native-inputs
 | 
				
			||||||
   (list
 | 
					   (list
 | 
				
			||||||
 | 
				
			|||||||
@ -5,4 +5,3 @@ python-gnupg
 | 
				
			|||||||
Jinja2
 | 
					Jinja2
 | 
				
			||||||
pycryptodome
 | 
					pycryptodome
 | 
				
			||||||
PySocks
 | 
					PySocks
 | 
				
			||||||
mnemonic
 | 
					 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								setup.py
									
									
									
									
									
								
							@ -38,7 +38,6 @@ setuptools.setup(
 | 
				
			|||||||
        "Jinja2",
 | 
					        "Jinja2",
 | 
				
			||||||
        "pycryptodome",
 | 
					        "pycryptodome",
 | 
				
			||||||
        "PySocks",
 | 
					        "PySocks",
 | 
				
			||||||
        "mnemonic",
 | 
					 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    entry_points={
 | 
					    entry_points={
 | 
				
			||||||
        "console_scripts": [
 | 
					        "console_scripts": [
 | 
				
			||||||
 | 
				
			|||||||
@ -61,7 +61,7 @@ from basicswap.interface.contrib.nav_test_framework.script import (
 | 
				
			|||||||
from bin.basicswap_run import startDaemon
 | 
					from bin.basicswap_run import startDaemon
 | 
				
			||||||
from basicswap.contrib.rpcauth import generate_salt, password_to_hmac
 | 
					from basicswap.contrib.rpcauth import generate_salt, password_to_hmac
 | 
				
			||||||
from tests.basicswap.test_xmr import test_delay_event, callnoderpc
 | 
					from tests.basicswap.test_xmr import test_delay_event, callnoderpc
 | 
				
			||||||
from mnemonic import Mnemonic
 | 
					from basicswap.contrib.mnemonic import Mnemonic
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from tests.basicswap.test_btc_xmr import TestFunctions
 | 
					from tests.basicswap.test_btc_xmr import TestFunctions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -152,7 +152,6 @@ class Test(TestFunctions):
 | 
				
			|||||||
        if not cls.restore_instance:
 | 
					        if not cls.restore_instance:
 | 
				
			||||||
            seed_hex = cls.nav_seeds[i]
 | 
					            seed_hex = cls.nav_seeds[i]
 | 
				
			||||||
            mnemonic = Mnemonic('english').to_mnemonic(bytes.fromhex(seed_hex))
 | 
					            mnemonic = Mnemonic('english').to_mnemonic(bytes.fromhex(seed_hex))
 | 
				
			||||||
            print('mnemonic', mnemonic)
 | 
					 | 
				
			||||||
            extra_opts.append(f'-importmnemonic={mnemonic}')
 | 
					            extra_opts.append(f'-importmnemonic={mnemonic}')
 | 
				
			||||||
            data_dir = prepareDataDir(cfg.TEST_DATADIRS, i, 'navcoin.conf', 'nav_', base_p2p_port=NAV_BASE_PORT, base_rpc_port=NAV_BASE_RPC_PORT)
 | 
					            data_dir = prepareDataDir(cfg.TEST_DATADIRS, i, 'navcoin.conf', 'nav_', base_p2p_port=NAV_BASE_PORT, base_rpc_port=NAV_BASE_RPC_PORT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -22,6 +22,7 @@ from coincurve.ecdsaotves import (
 | 
				
			|||||||
from coincurve.keys import (
 | 
					from coincurve.keys import (
 | 
				
			||||||
    PrivateKey)
 | 
					    PrivateKey)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from basicswap.contrib.mnemonic import Mnemonic
 | 
				
			||||||
from basicswap.util import i2b, h2b
 | 
					from basicswap.util import i2b, h2b
 | 
				
			||||||
from basicswap.util.address import decodeAddress
 | 
					from basicswap.util.address import decodeAddress
 | 
				
			||||||
from basicswap.util.crypto import ripemd160, hash160, blake256
 | 
					from basicswap.util.crypto import ripemd160, hash160, blake256
 | 
				
			||||||
@ -32,6 +33,7 @@ from basicswap.util.rfc2440 import rfc2440_hash_password
 | 
				
			|||||||
from basicswap.util_xmr import encode_address as xmr_encode_address
 | 
					from basicswap.util_xmr import encode_address as xmr_encode_address
 | 
				
			||||||
from basicswap.interface.btc import BTCInterface
 | 
					from basicswap.interface.btc import BTCInterface
 | 
				
			||||||
from basicswap.interface.xmr import XMRInterface
 | 
					from basicswap.interface.xmr import XMRInterface
 | 
				
			||||||
 | 
					from tests.basicswap.mnemonics import mnemonics
 | 
				
			||||||
from tests.basicswap.util import REQUIRED_SETTINGS
 | 
					from tests.basicswap.util import REQUIRED_SETTINGS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from basicswap.basicswap_util import (
 | 
					from basicswap.basicswap_util import (
 | 
				
			||||||
@ -489,7 +491,6 @@ class Test(unittest.TestCase):
 | 
				
			|||||||
            assert (blake256(data).hex() == expect_hash)
 | 
					            assert (blake256(data).hex() == expect_hash)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_extkey(self):
 | 
					    def test_extkey(self):
 | 
				
			||||||
 | 
					 | 
				
			||||||
        test_key = 'XPARHAr37YxmFP8wyjkaHAQWmp84GiyLikL7EL8j9BCx4LkB8Q1Bw5Kr8sA1GA3Ym53zNLcaxxFHr6u81JVTeCaD61c6fKS1YRAuti8Zu5SzJCjh'
 | 
					        test_key = 'XPARHAr37YxmFP8wyjkaHAQWmp84GiyLikL7EL8j9BCx4LkB8Q1Bw5Kr8sA1GA3Ym53zNLcaxxFHr6u81JVTeCaD61c6fKS1YRAuti8Zu5SzJCjh'
 | 
				
			||||||
        test_key_c0 = 'XPARHAt1XMcNYAwP5wEnQXknBAkGSzaetdZt2eoJZehdB4WXfV1xbSjpgHe44AivmumcSejW5KaYx6L5M6MyR1WyXrsWTwaiUEfHq2RrqCfXj3ZW'
 | 
					        test_key_c0 = 'XPARHAt1XMcNYAwP5wEnQXknBAkGSzaetdZt2eoJZehdB4WXfV1xbSjpgHe44AivmumcSejW5KaYx6L5M6MyR1WyXrsWTwaiUEfHq2RrqCfXj3ZW'
 | 
				
			||||||
        test_key_c0_p = 'PPARTKPL4rp5WLnrYP6jZfuRjx6jrmvbsz5QdHofPfFqJdm918mQwdPLq6Dd9TkdbQeKUqjbHWkyzWe7Pftd7itzm7ETEoUMq4cbG4fY9FKH1YSU'
 | 
					        test_key_c0_p = 'PPARTKPL4rp5WLnrYP6jZfuRjx6jrmvbsz5QdHofPfFqJdm918mQwdPLq6Dd9TkdbQeKUqjbHWkyzWe7Pftd7itzm7ETEoUMq4cbG4fY9FKH1YSU'
 | 
				
			||||||
@ -519,6 +520,12 @@ class Test(unittest.TestCase):
 | 
				
			|||||||
        ek_c0_p_data = decodeAddress(test_key_c0_p)[4:]
 | 
					        ek_c0_p_data = decodeAddress(test_key_c0_p)[4:]
 | 
				
			||||||
        assert (m_0.encode_p() == ek_c0_p_data)
 | 
					        assert (m_0.encode_p() == ek_c0_p_data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_mnemonic(self):
 | 
				
			||||||
 | 
					        entropy0: bytes = Mnemonic('english').to_entropy(mnemonics[0])
 | 
				
			||||||
 | 
					        assert (entropy0.hex() == '0002207e9b744ea2d7ab41702f31f000')
 | 
				
			||||||
 | 
					        mnemonic_recovered: str = Mnemonic('english').to_mnemonic(entropy0)
 | 
				
			||||||
 | 
					        assert (mnemonic_recovered == mnemonics[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == '__main__':
 | 
					if __name__ == '__main__':
 | 
				
			||||||
    unittest.main()
 | 
					    unittest.main()
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user