tradehub.wallet.Wallet.__init__()   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 14
nop 3
dl 0
loc 24
rs 9.7
c 0
b 0
f 0
1
"""
2
Description:
3
4
    Wallet Class for signing, generating, and interacting with a Tradehub account.
5
    This client is called as part of the Authenticated Client and the Demex Client.
6
    This class is designed to generate mnemonics, convert mnemonics, private keys, public keys, and addresses.
7
8
Usage::
9
10
    from tradehub.wallet import Wallet
11
"""
12
13
import base64
14
import bech32
15
import ecdsa
16
import hashlib
17
import hdwallets
18
import mnemonic
19
20
from tradehub.utils import sort_and_stringify_json
21
22
23
class Wallet(object):
24
    """
25
    This class allows the user to interact with the Demex and Tradehub wallet functions.
26
    Execution of this function is as follows::
27
28
        Wallet(mnemonic='lorem ipsum dolor consectetur adipiscing eiusmod tempor incididunt labore magna',
29
               network='mainnet')
30
    """
31
32
    _DEFAULT_DERIVATION_PATH = "m/44'/118'/0'/0/0"
33
34
    def __init__(self, mnemonic: str = None, network: str = "testnet"):
35
        """
36
        :param mnemonic: The 12 or 24 word seed required to access your wallet and trade on Demex
37
        :type mnemonic: str
38
        :param network: The network you want to interact with. Accepts "testnet" or "mainnet".
39
        :type network: str
40
        """
41
        self.DEFAULT_BECH32_PREFIX_DICT = {
42
            "main": "swth",
43
            "mainnet": "swth",
44
            "test": "tswth",
45
            "testnet": "tswth",
46
        }
47
        self.DEFAULT_BECH32_PREFIX = self.DEFAULT_BECH32_PREFIX_DICT[network.lower()]
48
49
        if mnemonic and len(mnemonic.split()) in [12, 24]:
50
            self._mnemonic = mnemonic
51
        else:
52
            self._mnemonic = self.generate_12_word_wallet()
53
54
        self._private_key = self.mnemonic_to_private_key(mnemonic_phrase=self._mnemonic)
55
        self.public_key = self.private_key_to_public_key(private_key=self._private_key)
56
        self.base64_public_key = base64.b64encode(self.public_key).decode("utf-8")
57
        self.address = self.private_key_to_address(private_key=self._private_key)
58
59
    def generate_12_word_wallet(self):
60
        """
61
        Function to generate a 12 word mnemonic.
62
63
        Execution of this function is as follows::
64
65
            generate_12_word_wallet()
66
67
        The expected return result for this function is as follows::
68
69
            venture consider cool fury front middle junk person suit assist garbage category
70
71
        :return: 12 word String that should be highly protected if storing any funds.
72
        """
73
        return mnemonic.Mnemonic(language="english").generate(strength=128)
74
75
    def generate_24_word_wallet(self):
76
        """
77
        Function to generate a 24 word mnemonic.
78
79
        Execution of this function is as follows::
80
81
            generate_24_word_wallet()
82
83
        The expected return result for this function is as follows::
84
85
            refuse flag merge fiction choose dream frown gauge need fabric once pizza actual armed reopen couple family fury reopen slush blue try focus minute
86
87
        :return: 24 word String that should be highly protected if storing any funds.
88
        """
89
        return mnemonic.Mnemonic(language="english").generate(strength=256)
90
91
    def mnemonic_to_private_key(self, mnemonic_phrase: str = None, wallet_path: str = _DEFAULT_DERIVATION_PATH) -> bytes:
92
        """
93
        Function to find the private key based on the mnemonic passed to the function.
94
95
        Execution of this function is as follows::
96
97
            mnemonic_to_private_key(mnemonic_phrase='venture consider cool fury front middle junk person suit assist garbage category',
98
                                    wallet_path=_DEFAULT_DERIVATION_PATH)
99
100
        The expected return result for this function is as follows::
101
102
            b'\x15\xcf\xdd\xdf\xead\x88\xd2y!\xdb\xb61\xa6\x98\xeeQm\x05\xed\x8d%43!\n\xccS\xcbsf\x90'
103
104
        :param mnemonic_phrase: String mnemonic phrase for a tradehub wallet.
105
        :param wallet_path: String Derivation path to generated the private key from the mnemonic.
106
        :return: Byte value that is equal to the private key.
107
        """
108
        mnemonic_bytes = mnemonic.Mnemonic.to_seed(mnemonic_phrase, passphrase="")
109
        hd_wallet = hdwallets.BIP32.from_seed(mnemonic_bytes)
110
        self._private_key = hd_wallet.get_privkey_from_path(wallet_path)
111
        return self._private_key
112
113
    def private_key_to_public_key(self, private_key: bytes = None) -> bytes:
114
        """
115
        Function to find the public key based on the private key passed to the function.
116
117
        Execution of this function is as follows::
118
119
            private_key_to_public_key(private_key=b'\x15\xcf\xdd\xdf\xead\x88\xd2y!\xdb\xb61\xa6\x98\xeeQm\x05\xed\x8d%43!\n\xccS\xcbsf\x90')
120
121
        The expected return result for this function is as follows::
122
123
            b'\x02o\x1f\xfbL\x96\xe8\x1e\xb0\x12V\x80\xc7t\xfc\xb40R\xaeu\xf3{\xf6\xd7m]\xd1\xa9\x91\xa8\xe0Df'
124
125
        :param private_key: Byte representation of the wallets private key.
126
        :return: Binary value that is equal to the public key.
127
        """
128
        privkey_obj = ecdsa.SigningKey.from_string(self._private_key, curve=ecdsa.SECP256k1)
129
        self.public_key_obj = privkey_obj.get_verifying_key()
130
        self.public_key = self.public_key_obj.to_string("compressed")
131
        return self.public_key
132
133
    def public_key_to_address(self, public_key: bytes = None, hrp: str = None) -> str:
134
        """
135
        Function to find the readable address from the public key.
136
137
        Execution of this function is as follows::
138
139
            public_key_to_address(public_key=b'\x02o\x1f\xfbL\x96\xe8\x1e\xb0\x12V\x80\xc7t\xfc\xb40R\xaeu\xf3{\xf6\xd7m]\xd1\xa9\x91\xa8\xe0Df',
140
                                  hrp=None)
141
142
        The expected return result for this function is as follows::
143
144
            tswth1upcgussnx4p3jegwj3x2fccwlajwckkzgstrp8
145
146
        :param public_key: Byte representation of the wallets public key.
147
        :return: String and human readable Tradehub address.
148
        """
149
        if hrp is None:
150
            hrp = self.DEFAULT_BECH32_PREFIX
151
        s = hashlib.new("sha256", public_key).digest()
152
        r = hashlib.new("ripemd160", s).digest()
153
        five_bit_r = bech32.convertbits(r, 8, 5)
154
        assert five_bit_r is not None, "Unsuccessful bech32.convertbits call"
155
        return bech32.bech32_encode(hrp, five_bit_r)
156
157
    def private_key_to_address(self, private_key: bytes = None, hrp: str = None) -> str:
158
        """
159
        Function to find the readable address from the private key.
160
161
        Execution of this function is as follows::
162
163
            public_key_to_address(public_key=b'\x15\xcf\xdd\xdf\xead\x88\xd2y!\xdb\xb61\xa6\x98\xeeQm\x05\xed\x8d%43!\n\xccS\xcbsf\x90',
164
                                  hrp=None)
165
166
        The expected return result for this function is as follows::
167
168
            tswth1upcgussnx4p3jegwj3x2fccwlajwckkzgstrp8
169
170
        :param private_key: Byte representation of the wallets private key.
171
        :return: String and human readable Tradehub address.
172
        """
173
        if hrp is None:
174
            hrp = self.DEFAULT_BECH32_PREFIX
175
        public_key = self.private_key_to_public_key(private_key)
176
        return self.public_key_to_address(public_key=public_key, hrp=hrp)
177
178
    def _sign(self, message: dict) -> str:
179
        """
180
        Function to find the readable address from the public key.
181
182
        Execution of this function is as follows::
183
184
            _sign(message={'message': 'This is a Tradehub test.'})
185
186
        The expected return result for this function is as follows::
187
188
            XP4s5fCY6UKh/dvscYsDylhYeD64WOTYohEU3QuuUygPCiQF9uFS9mHgwLaTFfFL41mdTRZclxq6CETr8bcZ5w==
189
190
        :param message: Dictionary message to sign.
191
        :return: Signed message.
192
        """
193
        message_str = sort_and_stringify_json(message=message)
194
        message_bytes = message_str.encode("utf-8")
195
196
        private_key = ecdsa.SigningKey.from_string(self._private_key, curve=ecdsa.SECP256k1)
197
        signature_compact = private_key.sign_deterministic(
198
            message_bytes,
199
            hashfunc=hashlib.sha256,
200
            sigencode=ecdsa.util.sigencode_string_canonize
201
        )
202
203
        signature_base64_str = base64.b64encode(signature_compact).decode("utf-8")
204
        return signature_base64_str
205