bittensor-wallet
Advanced tools
@@ -55,2 +55,3 @@ from typing import Any, Optional | ||
| def get_hotkey(self, password: Optional[str] = None) -> "Keypair": ... | ||
| def get_hotkeypub(self, password: Optional[str] = None) -> "Keypair": ... | ||
| def set_coldkey( | ||
@@ -78,2 +79,8 @@ self, | ||
| ) -> None: ... | ||
| def set_hotkeypub( | ||
| self, | ||
| keypair: "Keypair", | ||
| encrypt: bool = False, | ||
| overwrite: bool = False, | ||
| ) -> None: ... | ||
| @property | ||
@@ -86,2 +93,4 @@ def coldkey(self) -> "Keypair": ... | ||
| @property | ||
| def hotkeypub(self) -> "Keypair": ... | ||
| @property | ||
| def coldkey_file(self) -> "Keyfile": ... | ||
@@ -93,2 +102,4 @@ @property | ||
| @property | ||
| def hotkeypub_file(self) -> "Keyfile": ... | ||
| @property | ||
| def name(self) -> str: ... | ||
@@ -120,2 +131,3 @@ @property | ||
| def unlock_hotkey(self) -> "Keypair": ... | ||
| def unlock_hotkeypub(self) -> "Keypair": ... | ||
| def new_coldkey( | ||
@@ -185,1 +197,7 @@ self, | ||
| ) -> "Wallet": ... | ||
| def regenerate_hotkeypub( | ||
| self, | ||
| ss58_address: Optional[str] = None, | ||
| public_key: Optional[bytes] = None, | ||
| overwrite: Optional[bool] = False, | ||
| ) -> "Wallet": ... |
+1
-1
| [package] | ||
| name = "bittensor_wallet" | ||
| version = "3.0.11" | ||
| version = "3.1.0" | ||
| edition = "2021" | ||
@@ -5,0 +5,0 @@ |
+7
-0
| # Changelog | ||
| ## 3.1.0 /2025-07-07 | ||
| ## What's Changed | ||
| * Add `hotkeypub` to bittensor-wallet by @basfroman in https://github.com/opentensor/btwallet/pull/156 | ||
| **Full Changelog**: https://github.com/opentensor/btwallet/compare/v3.0.11...v3.1.0 | ||
| ## 3.0.11 /2025-06-26 | ||
@@ -4,0 +11,0 @@ |
+1
-1
| Metadata-Version: 2.4 | ||
| Name: bittensor-wallet | ||
| Version: 3.0.11 | ||
| Version: 3.1.0 | ||
| Classifier: Development Status :: 5 - Production/Stable | ||
@@ -5,0 +5,0 @@ Classifier: Intended Audience :: Developers |
@@ -480,3 +480,3 @@ use std::{borrow::Cow, env, str}; | ||
| module.add_class::<Wallet>()?; | ||
| // Add submodules to the main module | ||
@@ -1004,2 +1004,11 @@ register_config_module(&module)?; | ||
| #[pyo3(signature = (password=None))] | ||
| fn get_hotkeypub(&self, password: Option<String>) -> PyResult<PyKeypair> { | ||
| let keypair = self | ||
| .inner | ||
| .get_hotkeypub(password) | ||
| .map_err(|e| PyErr::new::<PyKeyFileError, _>(e))?; | ||
| Ok(PyKeypair { inner: keypair }) | ||
| } | ||
| #[pyo3(signature = (keypair, encrypt=true, overwrite=false, save_coldkey_to_env=false, coldkey_password=None))] | ||
@@ -1057,2 +1066,14 @@ fn set_coldkey( | ||
| #[pyo3(signature = (keypair, encrypt=false, overwrite=false))] | ||
| fn set_hotkeypub( | ||
| &mut self, | ||
| keypair: PyKeypair, | ||
| encrypt: bool, | ||
| overwrite: bool, | ||
| ) -> PyResult<()> { | ||
| self.inner | ||
| .set_hotkeypub(keypair.inner, encrypt, overwrite) | ||
| .map_err(|e| PyErr::new::<PyKeyFileError, _>(e)) | ||
| } | ||
| // Getters | ||
@@ -1083,2 +1104,10 @@ #[getter(coldkey)] | ||
| #[getter(hotkeypub)] | ||
| fn hotkeypub_py_property(&self) -> PyResult<PyKeypair> { | ||
| let keypair = self.inner.hotkeypub_property().map_err(|e| { | ||
| PyErr::new::<PyKeyFileError, _>(format!("Failed to get hotkeypub: {:?}", e)) | ||
| })?; | ||
| Ok(PyKeypair { inner: keypair }) | ||
| } | ||
| #[getter] | ||
@@ -1109,2 +1138,10 @@ fn coldkey_file(&self) -> PyResult<PyKeyfile> { | ||
| #[getter] | ||
| fn hotkeypub_file(&self) -> PyResult<PyKeyfile> { | ||
| self.inner | ||
| .hotkeypub_file() | ||
| .map(|inner| PyKeyfile { inner }) | ||
| .map_err(|e| PyErr::new::<PyKeyFileError, _>(e)) | ||
| } | ||
| #[getter] | ||
| fn name(&self) -> String { | ||
@@ -1226,2 +1263,16 @@ self.inner.get_name() | ||
| #[pyo3(text_signature = "($self)")] | ||
| fn unlock_hotkeypub(&mut self) -> PyResult<PyKeypair> { | ||
| self.inner | ||
| .unlock_hotkeypub() | ||
| .map(|inner| PyKeypair { inner }) | ||
| .map_err(|e| match e { | ||
| KeyFileError::DecryptionError(_) => PyErr::new::<PyPasswordError, _>(format!( | ||
| "Decryption failed: {}", | ||
| e.to_string() | ||
| )), | ||
| _ => PyErr::new::<PyKeyFileError, _>(format!("Failed to unlock hotkey: {:?}", e)), | ||
| }) | ||
| } | ||
| #[pyo3( | ||
@@ -1386,2 +1437,19 @@ name = "create_new_coldkey", | ||
| } | ||
| #[pyo3(signature = (ss58_address=None, public_key=None, overwrite=None))] | ||
| fn regenerate_hotkeypub( | ||
| &mut self, | ||
| ss58_address: Option<String>, | ||
| public_key: Option<String>, | ||
| overwrite: Option<bool>, | ||
| ) -> PyResult<Self> { | ||
| let new_inner_wallet = self | ||
| .inner | ||
| .regenerate_hotkeypub(ss58_address, public_key, overwrite.unwrap_or(false)) | ||
| .map_err(|e| PyErr::new::<PyKeyFileError, _>(e))?; | ||
| self.inner = new_inner_wallet; | ||
| Ok(Wallet { | ||
| inner: self.inner.clone(), | ||
| }) | ||
| } | ||
| } |
+143
-38
@@ -40,2 +40,3 @@ use colored::Colorize; | ||
| _hotkey: Option<Keypair>, | ||
| _hotkeypub: Option<Keypair>, | ||
| } | ||
@@ -99,2 +100,3 @@ | ||
| _hotkey: None, | ||
| _hotkeypub: None, | ||
| } | ||
@@ -151,3 +153,3 @@ } | ||
| /// Checks for existing coldkeypub and hotkeys, and creates them if non-existent. | ||
| /// Checks for existing coldkeypub, hotkeypub, hotkeys, and creates them if non-existent. | ||
| pub fn create_if_non_existent( | ||
@@ -218,3 +220,6 @@ &mut self, | ||
| if overwrite || !self.hotkey_file()?.exists_on_device()? { | ||
| if overwrite | ||
| || !self.hotkey_file()?.exists_on_device()? | ||
| && !self.hotkeypub_file()?.exists_on_device()? | ||
| { | ||
| self.create_new_hotkey( | ||
@@ -319,2 +324,16 @@ 12, | ||
| pub fn hotkeypub_file(&self) -> Result<Keyfile, KeyFileError> { | ||
| // concatenate wallet path | ||
| let wallet_path = self._path.join(&self.name); | ||
| // concatenate hotkeypub path | ||
| let hotkeypub_path = wallet_path.join("hotkeypub.txt"); | ||
| Keyfile::new( | ||
| hotkeypub_path.to_string_lossy().into_owned(), | ||
| Some("hotkeypub.txt".to_string()), | ||
| false, | ||
| ) | ||
| } | ||
| /// Returns the coldkeypub file. | ||
@@ -361,2 +380,8 @@ pub fn coldkeypub_file(&self) -> Result<Keyfile, KeyFileError> { | ||
| /// Returns the hotkeypub from wallet.path/wallet.name/hotkeypub.txt or raises an error. | ||
| pub fn hotkeypub_property(&self) -> Result<Keypair, KeyFileError> { | ||
| let hotkeypub_file = self.hotkeypub_file()?; | ||
| hotkeypub_file.get_keypair(None) | ||
| } | ||
| /// Returns the name of the wallet | ||
@@ -441,2 +466,23 @@ pub fn get_name(&self) -> String { | ||
| /// Sets the hotkeypub for the wallet. | ||
| pub fn set_hotkeypub( | ||
| &mut self, | ||
| keypair: Keypair, | ||
| encrypt: bool, | ||
| overwrite: bool, | ||
| ) -> Result<(), KeyFileError> { | ||
| let ss58_address = keypair | ||
| .ss58_address() | ||
| .ok_or_else(|| KeyFileError::Generic("Failed to get ss58_address".to_string()))?; | ||
| let hotkeypub_keypair = Keypair::new(Some(ss58_address), None, None, 42, None, 1) | ||
| .map_err(|e| KeyFileError::Generic(e.to_string()))?; | ||
| self._hotkeypub = Some(hotkeypub_keypair.clone()); | ||
| self.hotkeypub_file() | ||
| .map_err(|e| KeyFileError::Generic(e.to_string()))? | ||
| .set_keypair(hotkeypub_keypair, encrypt, overwrite, None) | ||
| .map_err(|e| KeyFileError::Generic(e.to_string()))?; | ||
| Ok(()) | ||
| } | ||
| /// Gets the coldkey from the wallet. | ||
@@ -457,2 +503,7 @@ pub fn get_coldkey(&self, password: Option<String>) -> Result<Keypair, KeyFileError> { | ||
| /// Gets the hotkeypub from the wallet. | ||
| pub fn get_hotkeypub(&self, password: Option<String>) -> Result<Keypair, KeyFileError> { | ||
| self.hotkeypub_file()?.get_keypair(password) | ||
| } | ||
| /// Creates coldkey from uri string, optionally encrypts it with the user-provided password. | ||
@@ -514,2 +565,3 @@ pub fn create_coldkey_from_uri( | ||
| )?; | ||
| self.set_hotkeypub(keypair, false, overwrite)?; | ||
| Ok(self.clone()) | ||
@@ -557,2 +609,15 @@ } | ||
| /// Unlocks the hotkeypub. | ||
| pub fn unlock_hotkeypub(&mut self) -> Result<Keypair, KeyFileError> { | ||
| if self._hotkeypub.is_none() { | ||
| let hotkeypub_file = self.hotkeypub_file()?; | ||
| self._hotkeypub = Some(hotkeypub_file.get_keypair(None)?); | ||
| } | ||
| let _hotkeypub = self | ||
| ._hotkeypub | ||
| .clone() | ||
| .ok_or_else(|| KeyFileError::Generic("Hotkey file doesn't exist.".to_string()))?; | ||
| Ok(_hotkeypub) | ||
| } | ||
| /// Creates a new coldkey, optionally encrypts it with the user-provided password and saves to disk. | ||
@@ -665,39 +730,5 @@ pub fn new_coldkey( | ||
| )?; | ||
| Ok(self.clone()) | ||
| } | ||
| /// Regenerates the coldkeypub from the passed ss58_address or public_key and saves the file. | ||
| /// Requires either ss58_address or public_key to be passed. | ||
| pub fn regenerate_coldkeypub( | ||
| &mut self, | ||
| ss58_address: Option<String>, | ||
| public_key: Option<String>, | ||
| overwrite: bool, | ||
| ) -> Result<Self, WalletError> { | ||
| if ss58_address.is_none() && public_key.is_none() { | ||
| return Err(WalletError::InvalidInput( | ||
| "Either ss58_address or public_key must be passed.".to_string(), | ||
| )); | ||
| } | ||
| self.set_hotkeypub(keypair.clone(), false, overwrite)?; | ||
| let address_to_string = ss58_address | ||
| .as_ref() | ||
| .or(public_key.as_ref()) | ||
| .ok_or_else(|| WalletError::InvalidInput("No address provided".to_string()))?; | ||
| if !is_valid_bittensor_address_or_public_key(address_to_string) { | ||
| return Err(WalletError::InvalidInput(format!( | ||
| "Invalid {}.", | ||
| if ss58_address.is_some() { | ||
| "ss58_address" | ||
| } else { | ||
| "public_key" | ||
| } | ||
| ))); | ||
| } | ||
| let keypair = Keypair::new(ss58_address, public_key, None, 42, None, 1) | ||
| .map_err(|e| WalletError::KeyGeneration(e.to_string()))?; | ||
| self.set_coldkeypub(keypair, false, overwrite)?; | ||
| Ok(self.clone()) | ||
@@ -752,2 +783,39 @@ } | ||
| /// Regenerates the coldkeypub from the passed ss58_address or public_key and saves the file. | ||
| /// Requires either ss58_address or public_key to be passed. | ||
| pub fn regenerate_coldkeypub( | ||
| &mut self, | ||
| ss58_address: Option<String>, | ||
| public_key: Option<String>, | ||
| overwrite: bool, | ||
| ) -> Result<Self, WalletError> { | ||
| if ss58_address.is_none() && public_key.is_none() { | ||
| return Err(WalletError::InvalidInput( | ||
| "Either ss58_address or public_key must be passed.".to_string(), | ||
| )); | ||
| } | ||
| let address_to_string = ss58_address | ||
| .as_ref() | ||
| .or(public_key.as_ref()) | ||
| .ok_or_else(|| WalletError::InvalidInput("No address provided".to_string()))?; | ||
| if !is_valid_bittensor_address_or_public_key(address_to_string) { | ||
| return Err(WalletError::InvalidInput(format!( | ||
| "Invalid {}.", | ||
| if ss58_address.is_some() { | ||
| "ss58_address" | ||
| } else { | ||
| "public_key" | ||
| } | ||
| ))); | ||
| } | ||
| let keypair = Keypair::new(ss58_address, public_key, None, 42, None, 1) | ||
| .map_err(|e| WalletError::KeyGeneration(e.to_string()))?; | ||
| self.set_coldkeypub(keypair, false, overwrite)?; | ||
| Ok(self.clone()) | ||
| } | ||
| /// Regenerates the hotkey from passed mnemonic or seed, encrypts it with the user's password and saves the file. | ||
@@ -788,3 +856,3 @@ pub fn regenerate_hotkey( | ||
| self.set_hotkey( | ||
| keypair, | ||
| keypair.clone(), | ||
| use_password, | ||
@@ -795,5 +863,42 @@ overwrite, | ||
| )?; | ||
| self.set_hotkeypub(keypair.clone(), false, overwrite)?; | ||
| Ok(self.clone()) | ||
| } | ||
| /// Regenerates the hotkeypub from the passed ss58_address or public_key and saves the file. | ||
| /// Requires either ss58_address or public_key to be passed. | ||
| pub fn regenerate_hotkeypub( | ||
| &mut self, | ||
| ss58_address: Option<String>, | ||
| public_key: Option<String>, | ||
| overwrite: bool, | ||
| ) -> Result<Self, WalletError> { | ||
| if ss58_address.is_none() && public_key.is_none() { | ||
| return Err(WalletError::InvalidInput( | ||
| "Either ss58_address or public_key must be passed.".to_string(), | ||
| )); | ||
| } | ||
| let address_to_string = ss58_address | ||
| .as_ref() | ||
| .or(public_key.as_ref()) | ||
| .ok_or_else(|| WalletError::InvalidInput("No address provided".to_string()))?; | ||
| if !is_valid_bittensor_address_or_public_key(address_to_string) { | ||
| return Err(WalletError::InvalidInput(format!( | ||
| "Invalid {}.", | ||
| if ss58_address.is_some() { | ||
| "ss58_address" | ||
| } else { | ||
| "public_key" | ||
| } | ||
| ))); | ||
| } | ||
| let keypair = Keypair::new(ss58_address, public_key, None, 42, None, 1) | ||
| .map_err(|e| WalletError::KeyGeneration(e.to_string()))?; | ||
| self.set_hotkeypub(keypair, false, overwrite)?; | ||
| Ok(self.clone()) | ||
| } | ||
| } |
@@ -196,3 +196,2 @@ import argparse | ||
| # Decrypt data... | ||
| print(">>> kf.data", kf.data) | ||
| json_data = json.loads(kf.data) | ||
@@ -199,0 +198,0 @@ assert set(json_data.keys()) == { |
+53
-5
@@ -9,2 +9,3 @@ import json | ||
| from bittensor_wallet import Wallet, keyfile | ||
| from bittensor_wallet.errors import KeyFileError | ||
@@ -158,10 +159,17 @@ | ||
| # Call | ||
| result = mock_wallet.unlock_coldkeypub() | ||
| coldkeypub = mock_wallet.unlock_coldkeypub() | ||
| hotkeypub = mock_wallet.unlock_hotkeypub() | ||
| # Assertions | ||
| assert result.ss58_address == mock_wallet.get_coldkeypub().ss58_address | ||
| assert result.public_key == mock_wallet.get_coldkeypub().public_key | ||
| assert result.ss58_format == mock_wallet.get_coldkeypub().ss58_format | ||
| assert result.crypto_type == mock_wallet.get_coldkeypub().crypto_type | ||
| assert coldkeypub.ss58_address == mock_wallet.get_coldkeypub().ss58_address | ||
| assert coldkeypub.public_key == mock_wallet.get_coldkeypub().public_key | ||
| assert coldkeypub.ss58_format == mock_wallet.get_coldkeypub().ss58_format | ||
| assert coldkeypub.crypto_type == mock_wallet.get_coldkeypub().crypto_type | ||
| assert hotkeypub.ss58_address == mock_wallet.get_hotkeypub().ss58_address | ||
| assert hotkeypub.public_key == mock_wallet.get_hotkeypub().public_key | ||
| assert hotkeypub.ss58_format == mock_wallet.get_hotkeypub().ss58_format | ||
| assert hotkeypub.crypto_type == mock_wallet.get_hotkeypub().crypto_type | ||
| def test_wallet_string_representation_with_default_arguments(): | ||
@@ -232,1 +240,41 @@ """Tests wallet string representation with default arguments.""" | ||
| assert w.coldkeypub.ss58_address is not None | ||
| def test_regenerate_hotkeypub(tmp_path): | ||
| """Tests any type of regenerating.""" | ||
| # Preps | ||
| wallet_name = "test_wallet_new" | ||
| wallet_hotkey = "test_hotkey_new" | ||
| wallet_path = (tmp_path / "test_wallets_new").resolve().as_posix() | ||
| # Call | ||
| w = Wallet(name=wallet_name, hotkey=wallet_hotkey, path=wallet_path) | ||
| with pytest.raises(KeyFileError): | ||
| _ = w.coldkey | ||
| with pytest.raises(KeyFileError): | ||
| _ = w.hotkey | ||
| with pytest.raises(KeyFileError): | ||
| _ = w.coldkeypub | ||
| with pytest.raises(KeyFileError): | ||
| _ = w.hotkeypub | ||
| w.create(coldkey_use_password=False) | ||
| ss58_coldkey = w.coldkey.ss58_address | ||
| ss58_coldkeypub = w.coldkeypub.ss58_address | ||
| ss58_hotkey = w.hotkey.ss58_address | ||
| ss58_hotkeypub = w.hotkeypub.ss58_address | ||
| w.regenerate_hotkeypub(ss58_address=ss58_hotkey, overwrite=True) | ||
| new_ss58_hotkeypub = w.hotkeypub.ss58_address | ||
| # Assert | ||
| assert ss58_coldkey == ss58_coldkeypub | ||
| assert ss58_hotkey == ss58_hotkeypub | ||
| assert ss58_hotkeypub == new_ss58_hotkeypub |
Sorry, the diff of this file is too big to display
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
377351
2.3%3526
1.44%