1import hashlib
2
3from .subpaths import subpaths_for_path_range
4
5from pycoin.encoding.bytes32 import from_bytes_32, to_bytes_32
6from pycoin.encoding.hash import double_sha256
7from pycoin.encoding.hexbytes import b2h
8from pycoin.key.Key import Key
9
10
11def initial_key_to_master_key(initial_key):
12    """
13    initial_key:
14        a hex string of length 32
15    """
16    b = initial_key.encode("utf8")
17    orig_input = b
18    for i in range(100000):
19        b = hashlib.sha256(b + orig_input).digest()
20    return from_bytes_32(b)
21
22
23class ElectrumWallet(Key):
24    def __init__(self, initial_key=None, master_private_key=None, public_pair=None, master_public_key=None):
25        if [initial_key, public_pair, master_private_key, master_public_key].count(None) != 3:
26            raise ValueError(
27                "exactly one of initial_key, master_private_key, master_public_key must be non-None")
28        self._initial_key = initial_key
29
30        if initial_key is not None:
31            master_private_key = initial_key_to_master_key(initial_key)
32        if master_public_key:
33            public_pair = tuple(from_bytes_32(master_public_key[idx:idx+32]) for idx in (0, 32))
34        super(ElectrumWallet, self).__init__(
35            secret_exponent=master_private_key, public_pair=public_pair, is_compressed=False)
36
37    @classmethod
38    def deserialize(class_, blob):
39        if len(blob) == 32:
40            return class_(master_private_key=from_bytes_32(blob))
41        if len(blob) == 64:
42            return class_(master_public_key=blob)
43
44    def serialize(self):
45        if self._secret_exponent:
46            return to_bytes_32(self._secret_exponent)
47        return self.master_public_key()
48
49    def secret_exponent(self):
50        if self._secret_exponent is None and self._initial_key:
51            self._secret_exponent = initial_key_to_master_key(b2h(self._initial_key))
52        return self._secret_exponent
53
54    def master_private_key(self):
55        return self.secret_exponent()
56
57    def master_public_key(self):
58        return self.sec()[1:]
59
60    def public_copy(self):
61        if self.secret_exponent() is None:
62            return self
63        return self.__class__(public_pair=self.public_pair())
64
65    def subkey_for_path(self, path):
66        return self.subkey(path)
67
68    def subkey(self, path):
69        """
70        path:
71            of the form "K" where K is an integer index, or "K/N" where N is usually
72            a 0 (deposit address) or 1 (change address)
73        """
74        t = path.split("/")
75        if len(t) == 2:
76            n, for_change = t
77        else:
78            n, = t
79            for_change = 0
80        b = (str(n) + ':' + str(for_change) + ':').encode("utf8") + self.master_public_key()
81        offset = from_bytes_32(double_sha256(b))
82        if self.secret_exponent():
83            return self.__class__(
84                master_private_key=((self.master_private_key() + offset) % self._generator.order())
85            )
86        p1 = offset * self._generator
87        x, y = self.public_pair()
88        p2 = self._generator.Point(x, y)
89        p = p1 + p2
90        return self.__class__(public_pair=p)
91
92    def subkeys(self, path):
93        """
94        A generalized form that can return multiple subkeys.
95        """
96        for _ in subpaths_for_path_range(path, hardening_chars="'pH"):
97            yield self.subkey(_)
98
99    def __repr__(self):
100        return "Electrum<E:%s>" % b2h(self.master_public_key())
101