1- <div align =" center " >
2-
3- # Rust Components for Bittensor Wallet SDK <!-- omit in toc -->
4-
5- [ ![ Discord Chat] ( https://img.shields.io/discord/308323056592486420.svg )] ( https://discord.gg/bittensor )
6- [ ![ License: MIT] ( https://img.shields.io/badge/License-MIT-yellow.svg )] ( https://opensource.org/licenses/MIT )
7- [ ![ PyPI version] ( https://badge.fury.io/py/bittensor-wallet.svg )] ( https://badge.fury.io/py/bittensor-wallet )
8-
9- ---
10-
11- ## Internet-scale Neural Networks <!-- omit in toc -->
12-
13- [ Bittensor SDK] ( https://github.com/opentensor/bittensor/tree/master ) • [ BTCLI] ( https://github.com/opentensor/btcli ) • [ Research] ( https://bittensor.com/whitepaper )
14-
15- </div >
16-
17- The Bittensor Wallet SDK is a Python interface for a powerful Rust-based Bittensor wallet functionality. You do not need to know Rust to use this Wallet SDK. However, if you want to contribute to the Rust components of this Wallet SDK, the Rust source is located in the [ src] ( ./src ) directory.
18-
19- ## Documentation
20-
21- For a full documentation for ` btwallet ` Python SDK see the [ Bittensor Wallet SDK section] ( https://docs.bittensor.com/btcli ) on the developer documentation site.
22-
231## Rust Development
242
253To build and test the Rust components of the project, you can use the following commands:
@@ -30,297 +8,3 @@ To build and test the Rust components of the project, you can use the following
308* ` cargo fmt ` - Formats the code.
319* ` cargo clippy ` - Runs the linter.
3210* ` cargo clippy --fix ` - Fixes the code.
33-
34- ## Using the Rust components in Python
35-
36- ``` python
37- from bittensor_wallet import config, errors, keyfile, keypair, utils, wallet
38-
39- print (utils.SS58_FORMAT )
40-
41- myconf = config.Config()
42- print (myconf)
43-
44- mywallet = wallet.Wallet(config = myconf)
45- print (mywallet)
46-
47- try :
48- mywallet.unlock_coldkey()
49- mywallet.unlock_coldkeypub()
50- mywallet.unlock_hotkey()
51- except errors.KeyFileError:
52- print (" Failed unlocking." )
53- ```
54-
55- ## keypair::KeyPair
56-
57- ### Tests for Keypair
58- ``` python
59- from bittensor_wallet import Keypair as WKeypair
60- from substrateinterface import Keypair as SKeypair
61-
62- kps = SKeypair.create_from_mnemonic(" stool feel open east woman high can denial forget screen trust salt" )
63- kpw = WKeypair.create_from_mnemonic(" stool feel open east woman high can denial forget screen trust salt" )
64- assert kps.ss58_address == kpw.ss58_address
65- assert kps.seed_hex == kpw.seed_hex
66- assert kps.public_key == kpw.public_key
67-
68- kps = SKeypair.create_from_seed(" 0x023d5fbd7981676587a9f7232aeae1087ac7c265f9658fb643b6f5e61961dfbf" )
69- kpw = WKeypair.create_from_seed(" 0x023d5fbd7981676587a9f7232aeae1087ac7c265f9658fb643b6f5e61961dfbf" )
70- assert kps.ss58_address == kpw.ss58_address
71- assert kps.seed_hex == kpw.seed_hex
72- assert kps.public_key == kpw.public_key
73-
74- # substrateinterface has a bug -> can't create the KP without `ss58_format` passed
75- kps = SKeypair.create_from_private_key(" 0x2b400f61c21cbaad4d5cb2dcbb4ef4fcdc238b98d04d48c6d2a451ebfd306c0eed845edcc69b0a19a6905afed0dd84c16ebd0f458928f2e91a6b67b95fc0b42f" , ss58_format = 42 )
76- kpw = WKeypair.create_from_private_key(" 0x2b400f61c21cbaad4d5cb2dcbb4ef4fcdc238b98d04d48c6d2a451ebfd306c0eed845edcc69b0a19a6905afed0dd84c16ebd0f458928f2e91a6b67b95fc0b42f" )
77- assert kps.ss58_address == kpw.ss58_address
78- assert kps.seed_hex == kpw.seed_hex
79- assert kps.public_key == kpw.public_key
80-
81- kps = SKeypair.create_from_uri(" //Alice" )
82- kpw = WKeypair.create_from_uri(" //Alice" )
83- assert kps.ss58_address == kpw.ss58_address
84- assert kps.seed_hex == kpw.seed_hex
85- assert kps.public_key == kpw.public_key
86-
87- # substrateinterface has a bug -> can't create the KP without `ss58_format` passed
88- from_private_key_new_kps = SKeypair(public_key = kps.public_key.hex(), ss58_format = 42 )
89- from_private_key_new_kpw = WKeypair(public_key = kps.public_key.hex())
90- assert from_private_key_new_kps.ss58_address == from_private_key_new_kpw.ss58_address
91- assert from_private_key_new_kps.seed_hex == from_private_key_new_kpw.seed_hex
92- assert from_private_key_new_kps.public_key == from_private_key_new_kpw.public_key
93-
94-
95- from_address_kps = SKeypair(ss58_address = " 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" )
96- from_address_kpw = WKeypair(ss58_address = " 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" )
97- assert from_address_kps.ss58_address == from_address_kpw.ss58_address
98- assert from_address_kps.seed_hex == from_address_kpw.seed_hex
99- assert from_address_kps.public_key == from_address_kpw.public_key
100-
101- # check signature
102- assert kps.verify(" asd" , kpw.sign(" asd" )) == True
103-
104- # check verify
105- assert kpw.verify(" asd" , kps.sign(" asd" )) == True
106-
107- # check create_from_encrypted_json
108- from substrateinterface.base import Keypair as S_Keypair
109- from bittensor_wallet import Keypair as W_Keypair
110-
111- data = ' {"encoded":"Z1yzxASuj21ej3CANbZKc3ibDaOpQPMahTT0qkniyZgAgAAAAQAAAAgAAACSDgflXWKXrX36EmX9XcA6cRpkN+oZX30/9FhtNP17krIG/yHLKmDnL1km1W/nZ+BpC7Qid6IuBvbZeboFyewFeXsKtcoY/bRY6nx/cLB5BND9WpXXS6Enf4RXAX7vPu/BY+o2z7VwPaXyFARfyPTiqJKqLDJWm3W5ZlvK0ks8FBv66mWEBYc+lLx8jvuzDNkdD3pnV3G802OwwHTy","encoding":{"content":["pkcs8","sr25519"],"type":["scrypt","xsalsa20-poly1305"],"version":"3"},"address":"5CuByUQBWZci5AtXonuHHhcbRL3yxM5xDdJsTNaYN3vPDY6f","meta":{"genesisHash":null,"name":"test","whenCreated":1727395683981}} '
112- passphrase = " Password123"
113-
114- skp = S_Keypair.create_from_encrypted_json(data, passphrase)
115- wkp = W_Keypair.create_from_encrypted_json(data, passphrase)
116-
117- assert skp.ss58_format == wkp.ss58_format
118- assert skp.public_key == wkp.public_key
119- assert skp.public_key.hex() == wkp.public_key.hex()
120- ```
121- ### Check signature and verify with ScaleBytes
122- ``` python
123- from scalecodec.base import ScaleBytes
124- from bittensor_wallet import Keypair as WKeypair
125- from substrateinterface import Keypair as SKeypair
126-
127- kps = SKeypair.create_from_uri(" //Alice" )
128- kpw = WKeypair.create_from_uri(" //Alice" )
129-
130- message = ScaleBytes(b " my message" )
131-
132- # cross check
133- assert kps.verify(message, kpw.sign(message)) == True
134- assert kpw.verify(message, kps.sign(message)) == True
135-
136- # itself check
137- assert kpw.verify(message, kpw.sign(message)) == True
138- assert kps.verify(message, kps.sign(message)) == True
139- ```
140- ## utils.rs
141-
142- ### Tests for utils' functions
143- ``` python
144- # check utils functions
145- from bittensor_wallet import get_ss58_format, is_valid_ss58_address, is_valid_ed25519_pubkey, is_valid_bittensor_address_or_public_key
146-
147- # check get_ss58_format
148- assert get_ss58_format(" 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" ) == 42
149-
150- # check is_valid_ss58_address
151- assert is_valid_ss58_address(" 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" ) == True
152- assert is_valid_ss58_address(" blabla" ) == False
153-
154- # check is_valid_ed25519_pubkey
155- assert is_valid_ed25519_pubkey(" a" * 64 ) == True
156- assert is_valid_ed25519_pubkey(" 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" ) == False
157- assert is_valid_ed25519_pubkey(" 0x86eb46a3f42935d901acc3b8910ca301d969b76cfc2a80f0eac733a8eda7ed24" ) == True
158- assert is_valid_ed25519_pubkey(" 86eb46a3f42935d901acc3b8910ca301d969b76cfc2a80f0eac733a8eda7ed24" ) == True
159- assert is_valid_ed25519_pubkey(" " ) == False
160- try :
161- is_valid_ed25519_pubkey()
162- except TypeError :
163- # TypeError: is_valid_ed25519_pubkey() missing 1 required positional argument: 'public_key'
164- ...
165- # check is_valid_bittensor_address_or_public_key
166- assert is_valid_bittensor_address_or_public_key(" blabla" ) == False
167- assert is_valid_bittensor_address_or_public_key(" a" * 64 ) == False
168- assert is_valid_bittensor_address_or_public_key(b " 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" ) == False
169- assert is_valid_bittensor_address_or_public_key(" 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" ) == True
170- assert is_valid_bittensor_address_or_public_key(100 ) == False
171- ```
172-
173- ## keyfile.rs
174-
175- ### Tests for keyfile.rs functions
176-
177- #### Test serialization and deserialization
178- ``` python
179- from bittensor_wallet import Keyfile, Keypair, serialized_keypair_to_keyfile_data, deserialize_keypair_from_keyfile_data
180- kp = Keypair.create_from_mnemonic(" stool feel open east woman high can denial forget screen trust salt" )
181- kf_data = serialized_keypair_to_keyfile_data(kp)
182- assert isinstance (kf_data, bytes )
183- kp_2 = deserialize_keypair_from_keyfile_data(kf_data)
184- assert isinstance (kp_2, Keypair)
185- assert kp.ss58_address == kp_2.ss58_address
186- assert kp.seed_hex == kp_2.seed_hex
187- assert kp.public_key == kp_2.public_key
188- ```
189-
190- #### Test Keyfile encrypt and decrypt
191- ``` python
192- # test keyfile encryption and decryption
193- from bittensor_wallet import Keyfile
194- # KF is an already encrypted key
195- kf = Keyfile(" /Users/daniel/.bittensor/wallets/game_wallet/coldkey" , name = " default" )
196- assert kf.data[:5 ] == b " $NACL"
197- kf.decrypt(" testing" )
198- # Decrypt data...
199- assert kf.data[1 :13 ] == b ' "publicKey":'
200- kf.encrypt(" testing" )
201- # Encryption data...
202- assert kf.data[:5 ] == b " $NACL"
203- ```
204-
205- #### Test Keyfile validate_password and ask_password
206- ``` python
207- from bittensor_wallet import validate_password, ask_password
208- ask_password()
209- # Specify password for key encryption: {password specified here}
210- validate_password(" test" )
211- # False, Password not strong enough
212- validate_password(" asdf45as6d4f52asd6f54" )
213- # True
214- ```
215-
216-
217- #### Test Keyfile keyfile_data_is_encrypted and keyfile_data_encryption_method
218- ``` python
219- from bittensor_wallet import Keyfile, keyfile_data_is_encrypted, keyfile_data_encryption_method
220- # KF is an already encrypted key NACL
221- kf = Keyfile(" /Users/daniel/.bittensor/wallets/game_wallet/coldkey" , name = " default" )
222- assert keyfile_data_is_encrypted(kf.data) == True
223- assert keyfile_data_encryption_method(kf.data) == ' NaCl'
224- ```
225-
226- #### Test Keyfile legacy_encrypt_keyfile_data and keyfile_data_encryption_method
227- ``` python
228- from bittensor_wallet import Keyfile, keyfile_data_is_encrypted, keyfile_data_encryption_method, legacy_encrypt_keyfile_data
229- # KF is an already encrypted key NACL
230- kf = Keyfile(" /Users/daniel/.bittensor/wallets/validator/coldkey" , name = " default" )
231- assert keyfile_data_is_encrypted(kf.data) == False
232- legacy_enc_kf_data = legacy_encrypt_keyfile_data(kf.data, " testing" )
233- # :exclamation_mark: Encrypting key with legacy encryption method...
234- assert keyfile_data_encryption_method(legacy_enc_kf_data) == ' Ansible Vault'
235- ```
236-
237- #### Test Keyfile get_coldkey_password_from_environment
238- ``` python
239- import os
240- from bittensor_wallet import get_coldkey_password_from_environment
241- assert get_coldkey_password_from_environment(" some-pw" ) == None
242- os.environ[" BT_COLD_PW_SOME_PW" ] = " SOMEPASSWORD"
243- assert get_coldkey_password_from_environment(" some-pw" ) == " SOMEPASSWORD"
244- ```
245-
246-
247- #### Test Keyfile encrypt_keyfile_data and decrypt_keyfile_data
248- ``` python
249- from bittensor_wallet import Keyfile, decrypt_keyfile_data, encrypt_keyfile_data, keyfile_data_is_encrypted
250- kf = Keyfile(" /Users/daniel/.bittensor/wallets/validator/coldkey" , name = " default" )
251- assert keyfile_data_is_encrypted(kf.data) == False
252- encrypted_kf_data = encrypt_keyfile_data(kf.data, " somePassword" )
253- # Encryption data...
254- assert keyfile_data_is_encrypted(encrypted_kf_data) == True
255- decrypted_kf_data = decrypt_keyfile_data(encrypted_kf_data, " somePassword" )
256- # Decrypt data...
257- assert decrypted_kf_data == kf.data
258- ```
259-
260- ### Tests for keyfile::Keyfile
261-
262- #### Test Keyfile is_encrypted, decrypt, encrypt and check_and_update_encryption
263- ``` python
264- from bittensor_wallet import Keyfile, Keypair
265- kf = Keyfile(" /Users/daniel/.bittensor/wallets/newkeyfile" , name = " default" )
266- kp = Keypair.create_from_mnemonic(" stool feel open east woman high can denial forget screen trust salt" )
267- kf.set_keypair(kp, False , False )
268-
269- assert kf.is_encrypted() == False
270- kf.encrypt(" somepassword" )
271- # Encryption data...
272-
273- assert kf.is_encrypted() == True
274-
275- kf.decrypt(" somepassword" )
276- # Decrypt data...
277-
278- assert kf.is_encrypted() == False
279-
280- kf.check_and_update_encryption(True , True )
281- # Keyfile is not encrypted.
282- # False
283- ```
284-
285- #### Test Keyfile make_dirs, is_writable, is_readable, get_keypair and exists_on_device
286- ``` python
287- from bittensor_wallet import Keyfile, Keypair
288- kf = Keyfile(" /Users/daniel/.bittensor/wallets/newkeyfile" , name = " default" )
289- kp = Keypair.create_from_mnemonic(" stool feel open east woman high can denial forget screen trust salt" )
290- kf.set_keypair(kp, False , False )
291-
292- assert kf.exists_on_device() == False
293- assert kf.is_writable() == False
294- assert kf.is_readable() == False
295-
296- kf.make_dirs()
297-
298- assert kf.exists_on_device() == True
299- assert kf.is_writable() == True
300- assert kf.is_readable() == True
301- ```
302-
303- ### Config parsing test
304- ``` python
305- import argparse
306- import bittensor as bt
307-
308- parser = argparse.ArgumentParser(description = ' My parser' )
309-
310- bt.wallet.add_args(parser)
311- bt.subtensor.add_args(parser)
312- bt.axon.add_args(parser)
313- bt.logging.add_args(parser)
314-
315- config = bt.config(parser)
316-
317- config.wallet.name = " new_wallet_name"
318- config.wallet.hotkey = " new_hotkey"
319- config.wallet.path = " /some/not_default/path"
320-
321- wallet = bt.wallet(config = config)
322-
323- assert wallet.name == config.wallet.name
324- assert wallet.hotkey_str == config.wallet.hotkey
325- assert wallet.path == config.wallet.path
326- ```
0 commit comments