From e8eb914517602dbc86e6cd6b9d06741e68ce874e Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 15:52:51 -0400 Subject: [PATCH 01/36] a0 ver --- Cargo.lock | 2 +- Cargo.toml | 2 +- bittensor_wallet/__init__.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e9dcfcc..b47d4f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -352,7 +352,7 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bittensor_wallet" -version = "2.0.0" +version = "2.0.0-a0" dependencies = [ "ansible-vault", "base64 0.22.1", diff --git a/Cargo.toml b/Cargo.toml index 95e1ab9..0716a42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bittensor_wallet" -version = "2.0.0" +version = "2.0.0-a0" edition = "2021" [lib] diff --git a/bittensor_wallet/__init__.py b/bittensor_wallet/__init__.py index eb5c5fb..67fcfbd 100644 --- a/bittensor_wallet/__init__.py +++ b/bittensor_wallet/__init__.py @@ -11,4 +11,4 @@ wallet as wallet, ) -__version__ = "2.0.0" +__version__ = "2.0.0-a0" From 22657c07b61f35d8536032b4f92f2e351c6efe01 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 16:02:45 -0400 Subject: [PATCH 02/36] replace with pyproject.toml --- pyproject.toml | 72 +++++++++++++++++++++++++++++++++++++++ setup.py | 92 -------------------------------------------------- 2 files changed, 72 insertions(+), 92 deletions(-) create mode 100644 pyproject.toml delete mode 100644 setup.py diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..a8da919 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,72 @@ +[project] +name = "btwallet" +version = "2.0.0-a0" +description = "" +readme = "README.md" +license = {file = "LICENSE"} +keywords = ["substrate", "scale", "codec", "bittensor", "wallet"] + +dependencies = [ + "termcolor", + "eth-utils<2.3.0", + "password_strength", + "cryptography~=42.0.5", + "ansible~=6.7", + "ansible_vault~=2.1", + "munch~=2.5.0", + "rich" +] +requires-python = ">= 3.9" + +authors = [ + {name = "Cameron Fairchild", email = "cameron@opentensor.dev"}, + {name = "Opentensor Foundation", email = "hello@bittensor.com"}, +] +maintainers = [ + {name = "Cameron Fairchild", email = "cameron@opentensor.dev"} +] +classifiers = [ + "Development Status :: 1 - Alpha", + "Intended Audience :: Developers", + "Topic :: Software Development :: Build Tools", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Topic :: Scientific/Engineering", + "Topic :: Scientific/Engineering :: Mathematics", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Software Development", + "Topic :: Software Development :: Libraries", + "Topic :: Software Development :: Libraries :: Python Modules",] + +[project.urls] +Repository = "https://github.com/opentensor/btwallet" + +[build-system] +requires = ["maturin>=1.0,<2.0"] +build-backend = "maturin" + +[project.optional-dependencies] +dev = [ + "pytest==7.2.0", + "pytest-asyncio==0.23.7", + "pytest-mock==3.12.0", + "pytest-split==0.8.0", + "pytest-xdist==3.0.2", + "pytest-rerunfailures==10.2", + "coveralls==3.3.1", + "pytest-cov==4.0.0", + "ddt==1.6.0", + "hypothesis==6.81.1", + "flake8==7.0.0", + "mypy==1.8.0", + "types-retry==0.9.9.4", + "freezegun==1.5.0", + "httpx==0.27.0", + "ruff==0.4.7", + "aioresponses==0.7.6", + "factory-boy==3.3.0", + "maturin==1.7.0" +] \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 1e93044..0000000 --- a/setup.py +++ /dev/null @@ -1,92 +0,0 @@ -# The MIT License (MIT) -# Copyright © 2023 Yuma Rao -# -# 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 setuptools import setup, find_packages -from os import path -from io import open -import codecs -import re -import os -import pathlib - - -def read_requirements(path_): - requirements_ = [] - - with pathlib.Path(path_).open() as requirements_txt: - for line in requirements_txt: - if line.startswith("git+"): - pkg_name = re.search(r"egg=([a-zA-Z0-9_-]+)", line.strip()).group(1) - requirements_.append(pkg_name + " @ " + line.strip()) - else: - requirements_.append(line.strip()) - - return requirements_ - - -requirements = read_requirements("requirements/prod.txt") -extra_requirements_dev = read_requirements("requirements/dev.txt") - -here = path.abspath(path.dirname(__file__)) - -with open(path.join(here, "README.md"), encoding="utf-8") as f: - long_description = f.read() - - -# loading version from setup.py -with codecs.open( - os.path.join(here, "btwallet/__init__.py"), encoding="utf-8" -) as init_file: - version_match = re.search( - r"^__version__ = ['\"]([^'\"]*)['\"]", init_file.read(), re.M - ) - version_string = version_match.group(1) - -setup( - name="btwallet", - version=version_string, - description="", - long_description=long_description, - long_description_content_type="text/markdown", - url="https://github.com/opentensor/bittensor", - author="bittensor.com", - packages=find_packages(exclude=["tests", "tests.*", "test_*.py"]), - include_package_data=True, - author_email="", - license="MIT", - python_requires=">=3.9", - install_requires=requirements, - extras_require={ - "dev": extra_requirements_dev, - }, - classifiers=[ - "Development Status :: 1 - Alpha", - "Intended Audience :: Developers", - "Topic :: Software Development :: Build Tools", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Topic :: Scientific/Engineering", - "Topic :: Scientific/Engineering :: Mathematics", - "Topic :: Scientific/Engineering :: Artificial Intelligence", - "Topic :: Software Development", - "Topic :: Software Development :: Libraries", - "Topic :: Software Development :: Libraries :: Python Modules", - ], -) From 0cb26357f6bdfddebfcd87afd796f172bbbd8c27 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 16:06:48 -0400 Subject: [PATCH 03/36] try new CI generation --- .github/workflows/CI.yml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 468b05f..43b5fa5 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -1,4 +1,4 @@ -# This file is autogenerated by maturin v1.7.1 +# This file is autogenerated by maturin v1.7.4 # To update, run # # maturin generate-ci github @@ -156,14 +156,25 @@ jobs: name: Release runs-on: ubuntu-latest environment: release - if: "startsWith(github.ref, 'refs/tags/')" + if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }} needs: [linux, musllinux, windows, macos, sdist] permissions: + # Use to sign the release artifacts id-token: write + # Used to upload release artifacts + contents: write + # Used to generate artifact attestation + attestations: write steps: - uses: actions/download-artifact@v4 + - name: Generate artifact attestation + uses: actions/attest-build-provenance@v1 + with: + subject-path: 'wheels-*/*' - name: Publish to PyPI + if: "startsWith(github.ref, 'refs/tags/')" uses: PyO3/maturin-action@v1 with: command: upload args: --non-interactive --skip-existing wheels-*/* + From b92098b99078b35fd1bae9325d31a0454a73f9b1 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 16:11:14 -0400 Subject: [PATCH 04/36] use newer action --- .github/workflows/CI.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 43b5fa5..0a11580 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -41,7 +41,7 @@ jobs: with: python-version: 3.x - name: Build wheels - uses: PyO3/maturin-action@v1 + uses: PyO3/maturin-action@v1.44.0 with: target: ${{ matrix.platform.target }} args: --release --out dist --find-interpreter @@ -72,7 +72,7 @@ jobs: with: python-version: 3.x - name: Build wheels - uses: PyO3/maturin-action@v1 + uses: PyO3/maturin-action@v1.44.0 with: target: ${{ matrix.platform.target }} args: --release --out dist --find-interpreter @@ -100,7 +100,7 @@ jobs: python-version: 3.x architecture: ${{ matrix.platform.target }} - name: Build wheels - uses: PyO3/maturin-action@v1 + uses: PyO3/maturin-action@v1.44.0 with: target: ${{ matrix.platform.target }} args: --release --out dist --find-interpreter @@ -126,7 +126,7 @@ jobs: with: python-version: 3.x - name: Build wheels - uses: PyO3/maturin-action@v1 + uses: PyO3/maturin-action@v1.44.0 with: target: ${{ matrix.platform.target }} args: --release --out dist --find-interpreter @@ -142,7 +142,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Build sdist - uses: PyO3/maturin-action@v1 + uses: PyO3/maturin-action@v1.44.0 with: command: sdist args: --out dist @@ -173,7 +173,7 @@ jobs: subject-path: 'wheels-*/*' - name: Publish to PyPI if: "startsWith(github.ref, 'refs/tags/')" - uses: PyO3/maturin-action@v1 + uses: PyO3/maturin-action@v1.44.0 with: command: upload args: --non-interactive --skip-existing wheels-*/* From 48cef4c7fc12f84312d51441db3742a528ea4f88 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 16:20:01 -0400 Subject: [PATCH 05/36] add openssl install step --- .github/workflows/CI.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 0a11580..7c0e382 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -37,6 +37,8 @@ jobs: target: ppc64le steps: - uses: actions/checkout@v4 + - name: Install OpenSSL + run: sudo apt-get update && sudo apt-get install -y libssl-dev - uses: actions/setup-python@v5 with: python-version: 3.x @@ -68,6 +70,8 @@ jobs: target: armv7 steps: - uses: actions/checkout@v4 + - name: Install OpenSSL + run: sudo apt-get update && sudo apt-get install -y libssl-dev - uses: actions/setup-python@v5 with: python-version: 3.x From 41b7f33f2decbe5cd05de6ebc76bdceffe549b45 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 16:20:48 -0400 Subject: [PATCH 06/36] remove windows as a target --- .github/workflows/CI.yml | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 7c0e382..b091e04 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -88,33 +88,6 @@ jobs: name: wheels-musllinux-${{ matrix.platform.target }} path: dist - windows: - runs-on: ${{ matrix.platform.runner }} - strategy: - matrix: - platform: - - runner: windows-latest - target: x64 - - runner: windows-latest - target: x86 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: 3.x - architecture: ${{ matrix.platform.target }} - - name: Build wheels - uses: PyO3/maturin-action@v1.44.0 - with: - target: ${{ matrix.platform.target }} - args: --release --out dist --find-interpreter - sccache: 'true' - - name: Upload wheels - uses: actions/upload-artifact@v4 - with: - name: wheels-windows-${{ matrix.platform.target }} - path: dist - macos: runs-on: ${{ matrix.platform.runner }} strategy: @@ -161,7 +134,7 @@ jobs: runs-on: ubuntu-latest environment: release if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }} - needs: [linux, musllinux, windows, macos, sdist] + needs: [linux, musllinux, macos, sdist] permissions: # Use to sign the release artifacts id-token: write From ac899c7ff1a402bce29348518bfe477b0ff5f241 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 16:23:35 -0400 Subject: [PATCH 07/36] add pkg config --- .github/workflows/CI.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index b091e04..43c8e33 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -38,7 +38,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install OpenSSL - run: sudo apt-get update && sudo apt-get install -y libssl-dev + run: sudo apt-get update && sudo apt-get install -y libssl-dev pkg-config - uses: actions/setup-python@v5 with: python-version: 3.x @@ -71,7 +71,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install OpenSSL - run: sudo apt-get update && sudo apt-get install -y libssl-dev + run: sudo apt-get update && sudo apt-get install -y libssl-dev pkg-config - uses: actions/setup-python@v5 with: python-version: 3.x From b4bd57c3eb336da406536ab1de64c9fda30855e8 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 17:08:55 -0400 Subject: [PATCH 08/36] no sudo --- .github/workflows/CI.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 43c8e33..acbd597 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -38,7 +38,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install OpenSSL - run: sudo apt-get update && sudo apt-get install -y libssl-dev pkg-config + run: apt update && apt install -y openssl libssl-dev pkg-config - uses: actions/setup-python@v5 with: python-version: 3.x @@ -71,7 +71,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install OpenSSL - run: sudo apt-get update && sudo apt-get install -y libssl-dev pkg-config + run: apt update && apt install -y openssl libssl-dev pkg-config - uses: actions/setup-python@v5 with: python-version: 3.x From ba1b03988253dde55d93f47d5be28ca03f1df7e4 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 17:10:23 -0400 Subject: [PATCH 09/36] add back sudo --- .github/workflows/CI.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index acbd597..af614a0 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -38,7 +38,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install OpenSSL - run: apt update && apt install -y openssl libssl-dev pkg-config + run: sudo apt update && sudo apt install -y openssl libssl-dev pkg-config - uses: actions/setup-python@v5 with: python-version: 3.x @@ -71,7 +71,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install OpenSSL - run: apt update && apt install -y openssl libssl-dev pkg-config + run: sudo apt update && sudo apt install -y openssl libssl-dev pkg-config - uses: actions/setup-python@v5 with: python-version: 3.x From d10a76b08305f986b84572e376baa7514da1da3b Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 17:14:17 -0400 Subject: [PATCH 10/36] remove openssl from command --- .github/workflows/CI.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index af614a0..8aa2752 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -37,8 +37,6 @@ jobs: target: ppc64le steps: - uses: actions/checkout@v4 - - name: Install OpenSSL - run: sudo apt update && sudo apt install -y openssl libssl-dev pkg-config - uses: actions/setup-python@v5 with: python-version: 3.x @@ -70,8 +68,6 @@ jobs: target: armv7 steps: - uses: actions/checkout@v4 - - name: Install OpenSSL - run: sudo apt update && sudo apt install -y openssl libssl-dev pkg-config - uses: actions/setup-python@v5 with: python-version: 3.x From cd196918147b2859556a02c70e5eb514b603d955 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 17:17:52 -0400 Subject: [PATCH 11/36] add to before script --- .github/workflows/CI.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 8aa2752..cf3f0e2 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -47,6 +47,7 @@ jobs: args: --release --out dist --find-interpreter sccache: 'true' manylinux: auto + before-script-linux: apt update && apt install -y openssl libssl-dev pkg-config - name: Upload wheels uses: actions/upload-artifact@v4 with: @@ -78,6 +79,7 @@ jobs: args: --release --out dist --find-interpreter sccache: 'true' manylinux: musllinux_1_2 + before-script-linux: apt update && apt install -y openssl libssl-dev pkg-config - name: Upload wheels uses: actions/upload-artifact@v4 with: From d48f66c501bf092f4b33d7bad3772ee6bc057932 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 17:24:18 -0400 Subject: [PATCH 12/36] try adding to cargo --- .github/workflows/CI.yml | 4 ++-- Cargo.lock | 11 +++++++++++ Cargo.toml | 1 + 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index cf3f0e2..8cc009c 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -47,7 +47,7 @@ jobs: args: --release --out dist --find-interpreter sccache: 'true' manylinux: auto - before-script-linux: apt update && apt install -y openssl libssl-dev pkg-config + before-script-linux: apt update && apt install -y libssl-dev pkg-config - name: Upload wheels uses: actions/upload-artifact@v4 with: @@ -79,7 +79,7 @@ jobs: args: --release --out dist --find-interpreter sccache: 'true' manylinux: musllinux_1_2 - before-script-linux: apt update && apt install -y openssl libssl-dev pkg-config + before-script-linux: apt update && apt install -y libssl-dev pkg-config - name: Upload wheels uses: actions/upload-artifact@v4 with: diff --git a/Cargo.lock b/Cargo.lock index b47d4f4..b7be00b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -361,6 +361,7 @@ dependencies = [ "dirs", "fernet", "hex", + "openssl", "passwords", "pbkdf2 0.12.2", "pkcs8", @@ -1485,6 +1486,15 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "openssl-src" +version = "300.3.2+3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a211a18d945ef7e648cc6e0058f4c548ee46aab922ea203e0d30e966ea23647b" +dependencies = [ + "cc", +] + [[package]] name = "openssl-sys" version = "0.9.103" @@ -1493,6 +1503,7 @@ checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", + "openssl-src", "pkg-config", "vcpkg", ] diff --git a/Cargo.toml b/Cargo.toml index 0716a42..3125338 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ base64 = "0.22.1" scrypt = "0.11.0" pkcs8 = "0.10.2" schnorrkel = "0.11.4" +openssl = { version = "0.10", features = ["vendored"] } [features] extension-module = ["pyo3/extension-module"] From dae1629b65e9b555a4eb27ef037a011e51add66d Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 17:26:40 -0400 Subject: [PATCH 13/36] use apt-get --- .github/workflows/CI.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 8cc009c..4b3275b 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -47,7 +47,7 @@ jobs: args: --release --out dist --find-interpreter sccache: 'true' manylinux: auto - before-script-linux: apt update && apt install -y libssl-dev pkg-config + before-script-linux: apt-get update && apt-get install -y libssl-dev pkg-config - name: Upload wheels uses: actions/upload-artifact@v4 with: @@ -79,7 +79,7 @@ jobs: args: --release --out dist --find-interpreter sccache: 'true' manylinux: musllinux_1_2 - before-script-linux: apt update && apt install -y libssl-dev pkg-config + before-script-linux: apt-get update && apt-get install -y libssl-dev pkg-config - name: Upload wheels uses: actions/upload-artifact@v4 with: From 61d1229b7122a5299db7002a9113c28bd9ff2f88 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 17:34:42 -0400 Subject: [PATCH 14/36] try with yum --- .github/workflows/CI.yml | 4 ++-- Cargo.lock | 11 ----------- Cargo.toml | 1 - 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 4b3275b..6af7a8f 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -47,7 +47,7 @@ jobs: args: --release --out dist --find-interpreter sccache: 'true' manylinux: auto - before-script-linux: apt-get update && apt-get install -y libssl-dev pkg-config + before-script-linux: (apt-get update && apt-get install -y libssl-dev pkg-config) & (yum install openssl openssl-devel -y) - name: Upload wheels uses: actions/upload-artifact@v4 with: @@ -79,7 +79,7 @@ jobs: args: --release --out dist --find-interpreter sccache: 'true' manylinux: musllinux_1_2 - before-script-linux: apt-get update && apt-get install -y libssl-dev pkg-config + before-script-linux: (apt-get update && apt-get install -y libssl-dev pkg-config) & (yum install openssl openssl-devel -y) - name: Upload wheels uses: actions/upload-artifact@v4 with: diff --git a/Cargo.lock b/Cargo.lock index b7be00b..b47d4f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -361,7 +361,6 @@ dependencies = [ "dirs", "fernet", "hex", - "openssl", "passwords", "pbkdf2 0.12.2", "pkcs8", @@ -1486,15 +1485,6 @@ dependencies = [ "syn 2.0.77", ] -[[package]] -name = "openssl-src" -version = "300.3.2+3.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a211a18d945ef7e648cc6e0058f4c548ee46aab922ea203e0d30e966ea23647b" -dependencies = [ - "cc", -] - [[package]] name = "openssl-sys" version = "0.9.103" @@ -1503,7 +1493,6 @@ checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", - "openssl-src", "pkg-config", "vcpkg", ] diff --git a/Cargo.toml b/Cargo.toml index 3125338..0716a42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,6 @@ base64 = "0.22.1" scrypt = "0.11.0" pkcs8 = "0.10.2" schnorrkel = "0.11.4" -openssl = { version = "0.10", features = ["vendored"] } [features] extension-module = ["pyo3/extension-module"] From 8923ec8e912a19cc12af0a3ee130dcda6db077d0 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 17:37:27 -0400 Subject: [PATCH 15/36] use & at the end --- .github/workflows/CI.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 6af7a8f..090033b 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -47,7 +47,7 @@ jobs: args: --release --out dist --find-interpreter sccache: 'true' manylinux: auto - before-script-linux: (apt-get update && apt-get install -y libssl-dev pkg-config) & (yum install openssl openssl-devel -y) + before-script-linux: (apt-get update && apt-get install -y libssl-dev pkg-config) & (yum install openssl openssl-devel -y) & - name: Upload wheels uses: actions/upload-artifact@v4 with: @@ -79,7 +79,7 @@ jobs: args: --release --out dist --find-interpreter sccache: 'true' manylinux: musllinux_1_2 - before-script-linux: (apt-get update && apt-get install -y libssl-dev pkg-config) & (yum install openssl openssl-devel -y) + before-script-linux: (apt-get update && apt-get install -y libssl-dev pkg-config) & (yum install openssl openssl-devel -y) & - name: Upload wheels uses: actions/upload-artifact@v4 with: From 66f2c3b5df144564d8d36f7d4deba5403d6640d8 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 17:38:58 -0400 Subject: [PATCH 16/36] use || --- .github/workflows/CI.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 090033b..47d17a6 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -47,7 +47,7 @@ jobs: args: --release --out dist --find-interpreter sccache: 'true' manylinux: auto - before-script-linux: (apt-get update && apt-get install -y libssl-dev pkg-config) & (yum install openssl openssl-devel -y) & + before-script-linux: (apt-get update && apt-get install -y libssl-dev pkg-config) || (yum install openssl openssl-devel -y) - name: Upload wheels uses: actions/upload-artifact@v4 with: @@ -79,7 +79,7 @@ jobs: args: --release --out dist --find-interpreter sccache: 'true' manylinux: musllinux_1_2 - before-script-linux: (apt-get update && apt-get install -y libssl-dev pkg-config) & (yum install openssl openssl-devel -y) & + before-script-linux: (apt-get update && apt-get install -y libssl-dev pkg-config) || (yum install openssl openssl-devel -y) - name: Upload wheels uses: actions/upload-artifact@v4 with: From f61f0027850d33779970422fd254ff564c30eb89 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 17:41:14 -0400 Subject: [PATCH 17/36] add openssl --- .github/workflows/CI.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 47d17a6..1997745 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -47,7 +47,7 @@ jobs: args: --release --out dist --find-interpreter sccache: 'true' manylinux: auto - before-script-linux: (apt-get update && apt-get install -y libssl-dev pkg-config) || (yum install openssl openssl-devel -y) + before-script-linux: (apt-get update && apt-get install -y openssl libssl-dev pkg-config) || (yum install openssl openssl-devel -y) - name: Upload wheels uses: actions/upload-artifact@v4 with: @@ -79,7 +79,7 @@ jobs: args: --release --out dist --find-interpreter sccache: 'true' manylinux: musllinux_1_2 - before-script-linux: (apt-get update && apt-get install -y libssl-dev pkg-config) || (yum install openssl openssl-devel -y) + before-script-linux: (apt-get update && apt-get install -y openssl libssl-dev pkg-config) || (yum install openssl openssl-devel -y) - name: Upload wheels uses: actions/upload-artifact@v4 with: From b55c439af6adbbdbedfe145ea0ede480b54ba438 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 17:47:19 -0400 Subject: [PATCH 18/36] remove openssl --- .github/workflows/CI.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 1997745..0847cf0 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -47,7 +47,7 @@ jobs: args: --release --out dist --find-interpreter sccache: 'true' manylinux: auto - before-script-linux: (apt-get update && apt-get install -y openssl libssl-dev pkg-config) || (yum install openssl openssl-devel -y) + before-script-linux: (apt-get update && apt-get install -y pkg-config libssl-dev) || (yum install openssl openssl-devel -y) - name: Upload wheels uses: actions/upload-artifact@v4 with: @@ -79,7 +79,7 @@ jobs: args: --release --out dist --find-interpreter sccache: 'true' manylinux: musllinux_1_2 - before-script-linux: (apt-get update && apt-get install -y openssl libssl-dev pkg-config) || (yum install openssl openssl-devel -y) + before-script-linux: (apt-get update && apt-get install -y pkg-config libssl-dev) || (yum install openssl openssl-devel -y) - name: Upload wheels uses: actions/upload-artifact@v4 with: From f5347ab47a93c4c9e9158972aadceb7c53ef5f81 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 17:50:53 -0400 Subject: [PATCH 19/36] only x86_64 --- .github/workflows/CI.yml | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 0847cf0..793109d 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -25,16 +25,16 @@ jobs: platform: - runner: ubuntu-latest target: x86_64 - - runner: ubuntu-latest - target: x86 - - runner: ubuntu-latest - target: aarch64 - - runner: ubuntu-latest - target: armv7 - - runner: ubuntu-latest - target: s390x - - runner: ubuntu-latest - target: ppc64le + # - runner: ubuntu-latest + # target: x86 + # - runner: ubuntu-latest + # target: aarch64 + # - runner: ubuntu-latest + # target: armv7 + # - runner: ubuntu-latest + # target: s390x + # - runner: ubuntu-latest + # target: ppc64le steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 @@ -61,12 +61,12 @@ jobs: platform: - runner: ubuntu-latest target: x86_64 - - runner: ubuntu-latest - target: x86 - - runner: ubuntu-latest - target: aarch64 - - runner: ubuntu-latest - target: armv7 + # - runner: ubuntu-latest + # target: x86 + # - runner: ubuntu-latest + # target: aarch64 + # - runner: ubuntu-latest + # target: armv7 steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 From d8045c70e5e2e0c81abe40d72266678cd4569224 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 17:56:12 -0400 Subject: [PATCH 20/36] add apt-utils install --- .github/workflows/CI.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 793109d..62dd333 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -47,7 +47,7 @@ jobs: args: --release --out dist --find-interpreter sccache: 'true' manylinux: auto - before-script-linux: (apt-get update && apt-get install -y pkg-config libssl-dev) || (yum install openssl openssl-devel -y) + before-script-linux: (apt-get update && apt-get install apt-utils && apt-get install -y pkg-config libssl-dev) || (yum install openssl openssl-devel -y) - name: Upload wheels uses: actions/upload-artifact@v4 with: @@ -79,7 +79,7 @@ jobs: args: --release --out dist --find-interpreter sccache: 'true' manylinux: musllinux_1_2 - before-script-linux: (apt-get update && apt-get install -y pkg-config libssl-dev) || (yum install openssl openssl-devel -y) + before-script-linux: (apt-get update && apt-get install apt-utils && apt-get install -y pkg-config libssl-dev) || (yum install openssl openssl-devel -y) - name: Upload wheels uses: actions/upload-artifact@v4 with: From 1649894da160c83f81efe63e73b87cc4be90cbe1 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 18:12:25 -0400 Subject: [PATCH 21/36] use -y --- .github/workflows/CI.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 62dd333..fc9f0b3 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -47,7 +47,7 @@ jobs: args: --release --out dist --find-interpreter sccache: 'true' manylinux: auto - before-script-linux: (apt-get update && apt-get install apt-utils && apt-get install -y pkg-config libssl-dev) || (yum install openssl openssl-devel -y) + before-script-linux: (apt-get update && apt-get install -y apt-utils && apt-get install -y pkg-config libssl-dev) || (yum install openssl openssl-devel -y) - name: Upload wheels uses: actions/upload-artifact@v4 with: @@ -79,7 +79,7 @@ jobs: args: --release --out dist --find-interpreter sccache: 'true' manylinux: musllinux_1_2 - before-script-linux: (apt-get update && apt-get install apt-utils && apt-get install -y pkg-config libssl-dev) || (yum install openssl openssl-devel -y) + before-script-linux: (apt-get update && apt-get install -y apt-utils && apt-get install -y pkg-config libssl-dev) || (yum install openssl openssl-devel -y) - name: Upload wheels uses: actions/upload-artifact@v4 with: From bfd1b19a8a8bf40939b2b5bec34f56d7c103ac1d Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 18:20:34 -0400 Subject: [PATCH 22/36] remove musllinux --- .github/workflows/CI.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index fc9f0b3..fb82b26 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -132,7 +132,8 @@ jobs: runs-on: ubuntu-latest environment: release if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }} - needs: [linux, musllinux, macos, sdist] + #needs: [linux, macos, musllinux, sdist] + needs: [linux, macos, sdist] permissions: # Use to sign the release artifacts id-token: write From a09c9a37a0167e84412e5b34540f6fa70ebf7bc0 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 18:21:07 -0400 Subject: [PATCH 23/36] comment out musllinux too --- .github/workflows/CI.yml | 62 ++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index fb82b26..9cc1c6f 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -54,37 +54,37 @@ jobs: name: wheels-linux-${{ matrix.platform.target }} path: dist - musllinux: - runs-on: ${{ matrix.platform.runner }} - strategy: - matrix: - platform: - - runner: ubuntu-latest - target: x86_64 - # - runner: ubuntu-latest - # target: x86 - # - runner: ubuntu-latest - # target: aarch64 - # - runner: ubuntu-latest - # target: armv7 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: 3.x - - name: Build wheels - uses: PyO3/maturin-action@v1.44.0 - with: - target: ${{ matrix.platform.target }} - args: --release --out dist --find-interpreter - sccache: 'true' - manylinux: musllinux_1_2 - before-script-linux: (apt-get update && apt-get install -y apt-utils && apt-get install -y pkg-config libssl-dev) || (yum install openssl openssl-devel -y) - - name: Upload wheels - uses: actions/upload-artifact@v4 - with: - name: wheels-musllinux-${{ matrix.platform.target }} - path: dist + # musllinux: + # runs-on: ${{ matrix.platform.runner }} + # strategy: + # matrix: + # platform: + # - runner: ubuntu-latest + # target: x86_64 + # # - runner: ubuntu-latest + # # target: x86 + # # - runner: ubuntu-latest + # # target: aarch64 + # # - runner: ubuntu-latest + # # target: armv7 + # steps: + # - uses: actions/checkout@v4 + # - uses: actions/setup-python@v5 + # with: + # python-version: 3.x + # - name: Build wheels + # uses: PyO3/maturin-action@v1.44.0 + # with: + # target: ${{ matrix.platform.target }} + # args: --release --out dist --find-interpreter + # sccache: 'true' + # manylinux: musllinux_1_2 + # before-script-linux: (apt-get update && apt-get install -y apt-utils && apt-get install -y pkg-config libssl-dev) || (yum install openssl openssl-devel -y) + # - name: Upload wheels + # uses: actions/upload-artifact@v4 + # with: + # name: wheels-musllinux-${{ matrix.platform.target }} + # path: dist macos: runs-on: ${{ matrix.platform.runner }} From 3e669db8ccf01b6c262a05b5303b6a82589bb79a Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 18:32:57 -0400 Subject: [PATCH 24/36] rename package and file --- .github/workflows/{CI.yml => release.yml} | 0 pyproject.toml | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{CI.yml => release.yml} (100%) diff --git a/.github/workflows/CI.yml b/.github/workflows/release.yml similarity index 100% rename from .github/workflows/CI.yml rename to .github/workflows/release.yml diff --git a/pyproject.toml b/pyproject.toml index a8da919..f8c42c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "btwallet" +name = "bittensor-wallet" version = "2.0.0-a0" description = "" readme = "README.md" From 61f519c4b029434764245302aa1cc4f17d122d9f Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 18:40:20 -0400 Subject: [PATCH 25/36] fix classifiers --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f8c42c7..50d7732 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ maintainers = [ {name = "Cameron Fairchild", email = "cameron@opentensor.dev"} ] classifiers = [ - "Development Status :: 1 - Alpha", + "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Topic :: Software Development :: Build Tools", "License :: OSI Approved :: MIT License", @@ -34,6 +34,7 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Topic :: Scientific/Engineering", "Topic :: Scientific/Engineering :: Mathematics", "Topic :: Scientific/Engineering :: Artificial Intelligence", From aecaeaec495549f8c7311919d8c18f56f4cdd3af Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 18:48:38 -0400 Subject: [PATCH 26/36] remove btwallet dir --- btwallet/__init__.py | 23 - btwallet/config.py | 408 --------------- btwallet/errors.py | 20 - btwallet/keyfile.py | 736 --------------------------- btwallet/mock/__init__.py | 36 -- btwallet/mock/keyfile_mock.py | 91 ---- btwallet/mock/wallet_mock.py | 127 ----- btwallet/utils.py | 104 ---- btwallet/wallet.py | 933 ---------------------------------- 9 files changed, 2478 deletions(-) delete mode 100644 btwallet/__init__.py delete mode 100644 btwallet/config.py delete mode 100644 btwallet/errors.py delete mode 100644 btwallet/keyfile.py delete mode 100644 btwallet/mock/__init__.py delete mode 100644 btwallet/mock/keyfile_mock.py delete mode 100644 btwallet/mock/wallet_mock.py delete mode 100644 btwallet/utils.py delete mode 100644 btwallet/wallet.py diff --git a/btwallet/__init__.py b/btwallet/__init__.py deleted file mode 100644 index 4b35cff..0000000 --- a/btwallet/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License (MIT) -# Copyright © 2024 Opentensor Foundation -# -# 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 bittensor_wallet.wallet import Wallet - - -__version__ = "0.1.0" - -__all__ = ["Wallet", "__version__"] diff --git a/btwallet/config.py b/btwallet/config.py deleted file mode 100644 index a87863b..0000000 --- a/btwallet/config.py +++ /dev/null @@ -1,408 +0,0 @@ -# The MIT License (MIT) -# Copyright © 2024 Opentensor Foundation -# -# 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. - -"""Implementation of the config class, which manages the configuration of different Bittensor modules.""" - -import argparse -import copy -import os -import sys -from copy import deepcopy -from typing import List, Optional, Dict, Any, TypeVar, Type - -import yaml -from munch import DefaultMunch - - -class InvalidConfigFile(Exception): - """In place of YAMLError""" - - -class Config(DefaultMunch): - """Implementation of the config class, which manages the configuration of different Bittensor modules.""" - - __is_set: Dict[str, bool] - - """ - Translates the passed parser into a nested Bittensor config. - - Args: - parser (argparse.ArgumentParser): - Command line parser object. - strict (bool): - If ``true``, the command line arguments are strictly parsed. - args (list of str): - Command line arguments. - default (Optional[Any]): - Default value for the Config. Defaults to ``None``. - This default will be returned for attributes that are undefined. - Returns: - config (Config): - Nested config object created from parser arguments. - """ - - def __init__( - self, - parser: argparse.ArgumentParser = None, - args: Optional[List[str]] = None, - strict: bool = False, - default: Optional[Any] = None, - ) -> None: - super().__init__(default) - - self["__is_set"] = {} - - if parser is None: - return - - # Optionally add config specific arguments - try: - parser.add_argument( - "--config", - type=str, - help="If set, defaults are overridden by passed file.", - ) - except Exception: - # this can fail if --config has already been added. - pass - - try: - parser.add_argument( - "--strict", - action="store_true", - help="""If flagged, config will check that only exact arguments have been set.""", - default=False, - ) - except Exception: - # this can fail if --strict has already been added. - pass - - try: - parser.add_argument( - "--no_version_checking", - action="store_true", - help="Set ``true`` to stop cli version checking.", - default=False, - ) - except Exception: - # this can fail if --no_version_checking has already been added. - pass - - try: - parser.add_argument( - "--no_prompt", - dest="no_prompt", - action="store_true", - help="Set ``true`` to stop cli from prompting the user.", - default=False, - ) - except Exception: - # this can fail if --no_version_checking has already been added. - pass - - # Get args from argv if not passed in. - if args is None: - args = sys.argv[1:] - - # Check for missing required arguments before proceeding - missing_required_args = self.__check_for_missing_required_args(parser, args) - if missing_required_args: - # Handle missing required arguments gracefully - raise ValueError( - f"Missing required arguments: {', '.join(missing_required_args)}" - ) - - # 1.1 Optionally load defaults if the --config is set. - try: - config_file_path = ( - str(os.getcwd()) - + "/" - + vars(parser.parse_known_args(args)[0])["config"] - ) - except Exception: - config_file_path = None - - # Parse args not strict - config_params = Config.__parse_args__(args=args, parser=parser, strict=False) - - # 2. Optionally check for --strict - # strict=True when passed in OR when --strict is set - strict = config_params.strict or strict - - if config_file_path is not None: - config_file_path = os.path.expanduser(config_file_path) - try: - with open(config_file_path) as f: - params_config = yaml.safe_load(f) - print("Loading config defaults from: {}".format(config_file_path)) - parser.set_defaults(**params_config) - except Exception as e: - print("Error in loading: {} using default parser settings".format(e)) - - # 2. Continue with loading in params. - params = Config.__parse_args__(args=args, parser=parser, strict=strict) - - _config = self - - # Splits params and add to config - Config.__split_params__(params=params, _config=_config) - - # Make the is_set map - _config["__is_set"] = {} - - # Reparse args using default of unset - parser_no_defaults = copy.deepcopy(parser) - - # Only command as the arg, else no args - default_param_args = ( - [_config.get("command")] - if _config.get("command") is not None and _config.get("subcommand") is None - else [] - ) - if _config.get("command") is not None and _config.get("subcommand") is not None: - default_param_args = [_config.get("command"), _config.get("subcommand")] - - # Get all args by name - default_params = parser.parse_args(args=default_param_args) - - all_default_args = default_params.__dict__.keys() | [] - # Make a dict with keys as args and values as argparse.SUPPRESS - defaults_as_suppress = {key: argparse.SUPPRESS for key in all_default_args} - # Set the defaults to argparse.SUPPRESS, should remove them from the namespace - parser_no_defaults.set_defaults(**defaults_as_suppress) - parser_no_defaults._defaults.clear() # Needed for quirk of argparse - - # Check for subparsers and do the same - if parser_no_defaults._subparsers is not None: - for action in parser_no_defaults._subparsers._actions: - # Should only be the "command" subparser action - if isinstance(action, argparse._SubParsersAction): - # Set the defaults to argparse.SUPPRESS, should remove them from the namespace - # Each choice is the keyword for a command, we need to set the defaults for each of these - # Note: we also need to clear the _defaults dict for each, this is a quirk of argparse - cmd_parser: argparse.ArgumentParser - for cmd_parser in action.choices.values(): - # If this choice is also a subparser, set defaults recursively - if cmd_parser._subparsers: - for action in cmd_parser._subparsers._actions: - # Should only be the "command" subparser action - if isinstance(action, argparse._SubParsersAction): - cmd_parser: argparse.ArgumentParser - for cmd_parser in action.choices.values(): - cmd_parser.set_defaults(**defaults_as_suppress) - cmd_parser._defaults.clear() # Needed for quirk of argparse - else: - cmd_parser.set_defaults(**defaults_as_suppress) - cmd_parser._defaults.clear() # Needed for quirk of argparse - - # Reparse the args, but this time with the defaults as argparse.SUPPRESS - params_no_defaults = Config.__parse_args__( - args=args, parser=parser_no_defaults, strict=strict - ) - - # Diff the params and params_no_defaults to get the is_set map - _config["__is_set"] = { - arg_key: True - for arg_key in [ - k - for k, _ in filter( - lambda kv: kv[1] != argparse.SUPPRESS, - params_no_defaults.__dict__.items(), - ) - ] - } - - @staticmethod - def __split_params__(params: argparse.Namespace, _config: "Config"): - # Splits params on dot syntax i.e neuron.axon_port and adds to _config - for arg_key, arg_val in params.__dict__.items(): - split_keys = arg_key.split(".") - head = _config - keys = split_keys - while len(keys) > 1: - if ( - hasattr(head, keys[0]) and head[keys[0]] is not None - ): # Needs to be Config - head = getattr(head, keys[0]) - keys = keys[1:] - else: - head[keys[0]] = Config() - head = head[keys[0]] - keys = keys[1:] - if len(keys) == 1: - head[keys[0]] = arg_val - - @staticmethod - def __parse_args__( - args: List[str], parser: argparse.ArgumentParser = None, strict: bool = False - ) -> argparse.Namespace: - """Parses the passed args use the passed parser. - - Args: - args (List[str]): - List of arguments to parse. - parser (argparse.ArgumentParser): - Command line parser object. - strict (bool): - If ``true``, the command line arguments are strictly parsed. - Returns: - Namespace: - Namespace object created from parser arguments. - """ - if not strict: - params, unrecognized = parser.parse_known_args(args=args) - params_list = list(params.__dict__) - # bug within argparse itself, does not correctly set value for boolean flags - for unrec in unrecognized: - if unrec.startswith("--") and unrec[2:] in params_list: - # Set the missing boolean value to true - setattr(params, unrec[2:], True) - else: - params = parser.parse_args(args=args) - - return params - - def __deepcopy__(self, memo) -> "Config": - _default = self.__default__ - - config_state = self.__getstate__() - config_copy = Config() - memo[id(self)] = config_copy - - config_copy.__setstate__(config_state) - config_copy.__default__ = _default - - config_copy["__is_set"] = deepcopy(self["__is_set"], memo) - - return config_copy - - def __repr__(self) -> str: - return self.__str__() - - @staticmethod - def _remove_private_keys(d): - if "__parser" in d: - d.pop("__parser", None) - if "__is_set" in d: - d.pop("__is_set", None) - for k, v in list(d.items()): - if isinstance(v, dict): - Config._remove_private_keys(v) - return d - - def __str__(self) -> str: - # remove the parser and is_set map from the visible config - visible = copy.deepcopy(self.toDict()) - visible.pop("__parser", None) - visible.pop("__is_set", None) - cleaned = Config._remove_private_keys(visible) - return "\n" + yaml.dump(cleaned, sort_keys=False) - - def copy(self) -> "Config": - return copy.deepcopy(self) - - def to_string(self, items) -> str: - """Get string from items""" - return "\n" + yaml.dump(items.toDict()) - - def update_with_kwargs(self, kwargs): - """Add config to self""" - for key, val in kwargs.items(): - self[key] = val - - @classmethod - def _merge(cls, a, b): - """Merge two configurations recursively. - If there is a conflict, the value from the second configuration will take precedence. - """ - for key in b: - if key in a: - if isinstance(a[key], dict) and isinstance(b[key], dict): - a[key] = cls._merge(a[key], b[key]) - else: - a[key] = b[key] - else: - a[key] = b[key] - return a - - def merge(self, b): - """ - Merges the current config with another config. - - Args: - b: Another config to merge. - """ - self._merge(self, b) - - @classmethod - def merge_all(cls, configs: List["Config"]) -> "Config": - """ - Merge all configs in the list into one config. - If there is a conflict, the value from the last configuration in the list will take precedence. - - Args: - configs (list of config): - List of configs to be merged. - - Returns: - config: - Merged config object. - """ - result = cls() - for cfg in configs: - result.merge(cfg) - return result - - def is_set(self, param_name: str) -> bool: - """ - Returns a boolean indicating whether the parameter has been set or is still the default. - """ - if param_name not in self.get("__is_set"): - return False - else: - return self.get("__is_set")[param_name] - - def __check_for_missing_required_args( - self, parser: argparse.ArgumentParser, args: List[str] - ) -> List[str]: - required_args = self.__get_required_args_from_parser(parser) - missing_args = [arg for arg in required_args if not any(arg in s for s in args)] - return missing_args - - @staticmethod - def __get_required_args_from_parser(parser: argparse.ArgumentParser) -> List[str]: - required_args = [] - for action in parser._actions: - if action.required: - # Prefix the argument with '--' if it's a long argument, or '-' if it's short - prefix = "--" if len(action.dest) > 1 else "-" - required_args.append(prefix + action.dest) - return required_args - - -T = TypeVar("T", bound="DefaultConfig") - - -class DefaultConfig(Config): - """A Config with a set of default values.""" - - @classmethod - def default(cls: Type[T]) -> T: - """ - Get default config. - """ - raise NotImplementedError("Function default is not implemented.") diff --git a/btwallet/errors.py b/btwallet/errors.py deleted file mode 100644 index 01bd3f3..0000000 --- a/btwallet/errors.py +++ /dev/null @@ -1,20 +0,0 @@ -# The MIT License (MIT) -# Copyright © 2024 Opentensor Foundation -# -# 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. - - -class KeyFileError(Exception): - """Error thrown when the keyfile is corrupt, non-writable, non-readable or the password used to decrypt is invalid.""" diff --git a/btwallet/keyfile.py b/btwallet/keyfile.py deleted file mode 100644 index 0cf849f..0000000 --- a/btwallet/keyfile.py +++ /dev/null @@ -1,736 +0,0 @@ -# The MIT License (MIT) -# Copyright © 2024 Opentensor Foundation -# -# 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. - -import base64 -import getpass -import json -import os -import stat -from pathlib import Path -from typing import Optional - -from ansible.parsing.vault import AnsibleVaultError -from ansible_vault import Vault -from cryptography.exceptions import InvalidSignature, InvalidKey -from cryptography.fernet import Fernet, InvalidToken -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC -from nacl import pwhash, secret -from password_strength import PasswordPolicy -from rich.console import Console -from rich.prompt import Confirm -from substrateinterface import Keypair -from substrateinterface.utils.ss58 import ss58_encode -from termcolor import colored - -from .errors import KeyFileError -from .utils import SS58_FORMAT - -NACL_SALT = b"\x13q\x83\xdf\xf1Z\t\xbc\x9c\x90\xb5Q\x879\xe9\xb1" -console = Console() - - -# sdk - deprecated import -# cli - no -# wallet - inside keyfile.py -def serialized_keypair_to_keyfile_data(keypair: "Keypair") -> bytes: - """Serializes keypair object into keyfile data. - - Args: - keypair (Keypair): The keypair object to be serialized. - Returns: - data (bytes): Serialized keypair data. - """ - json_data = { - "accountId": "0x" + keypair.public_key.hex() if keypair.public_key else None, - "publicKey": "0x" + keypair.public_key.hex() if keypair.public_key else None, - "privateKey": "0x" + keypair.private_key.hex() if keypair.private_key else None, - "secretPhrase": keypair.mnemonic if keypair.mnemonic else None, - "secretSeed": ( - "0x" - + ( - keypair.seed_hex - if isinstance(keypair.seed_hex, str) - else keypair.seed_hex.hex() - ) - if keypair.seed_hex - else None - ), - "ss58Address": keypair.ss58_address if keypair.ss58_address else None, - } - data = json.dumps(json_data).encode() - return data - - -# sdk - deprecated import -# cli - no -# wallet - inside keyfile.py -def deserialize_keypair_from_keyfile_data(keyfile_data: bytes) -> "Keypair": - """Deserializes Keypair object from passed keyfile data. - - Args: - keyfile_data (bytes): The keyfile data as bytes to be loaded. - Returns: - keypair (Keypair): The Keypair loaded from bytes. - Raises: - KeyFileError: Raised if the passed bytes cannot construct a keypair object. - """ - keyfile_data_ = keyfile_data.decode() - try: - keyfile_dict = dict(json.loads(keyfile_data_)) - except Exception: - string_value = str(keyfile_data_) - if string_value[:2] == "0x": - string_value = ss58_encode(string_value) - keyfile_dict = { - "accountId": None, - "publicKey": None, - "privateKey": None, - "secretPhrase": None, - "secretSeed": None, - "ss58Address": string_value, - } - else: - raise KeyFileError( - f"Keypair could not be created from keyfile data: {string_value}" - ) - - if "secretSeed" in keyfile_dict and keyfile_dict["secretSeed"] is not None: - return Keypair.create_from_seed(keyfile_dict["secretSeed"]) - - elif "secretPhrase" in keyfile_dict and keyfile_dict["secretPhrase"] is not None: - return Keypair.create_from_mnemonic(mnemonic=keyfile_dict["secretPhrase"]) - - elif keyfile_dict.get("privateKey", None) is not None: - # May have the above dict keys also, but we want to preserve the first two - return Keypair.create_from_private_key( - keyfile_dict["privateKey"], ss58_format=SS58_FORMAT - ) - - if "ss58Address" in keyfile_dict and keyfile_dict["ss58Address"] is not None: - return Keypair(ss58_address=keyfile_dict["ss58Address"]) - - else: - raise KeyFileError( - "Keypair could not be created from keyfile data: {}".format(keyfile_dict) - ) - - -# sdk - deprecated import -# cli - no -# wallet - inside keyfile.py -def validate_password(password: str) -> bool: - """Validates the password against a password policy. - - Args: - password (str): The password to verify. - Returns: - valid (bool): ``True`` if the password meets validity requirements. - """ - policy = PasswordPolicy.from_names(strength=0.20, entropybits=10, length=6) - if not password: - return False - tested_pass = policy.password(password) - result = tested_pass.test() - if len(result) > 0: - print( - colored( - "Password not strong enough. Try increasing the length of the password or the password complexity" - ) - ) - return False - password_verification = getpass.getpass("Retype your password: ") - if password != password_verification: - print("Passwords do not match") - return False - return True - - -# sdk - deprecated import -# cli - no -# wallet - inside keyfile.py -def ask_password_to_encrypt() -> str: - """Prompts the user to enter a password for key encryption. - - Returns: - password (str): The valid password entered by the user. - """ - valid = False - while not valid: - password = getpass.getpass("Specify password for key encryption: ") - valid = validate_password(password) - return password - - -# sdk - deprecated import -# cli - no -# wallet - inside keyfile.py -def keyfile_data_is_encrypted_nacl(keyfile_data: bytes) -> bool: - """Returns true if the keyfile data is NaCl encrypted. - - Args: - keyfile_data ( bytes, required ): - Bytes to validate. - Returns: - is_nacl (bool): - ``True`` if data is ansible encrypted. - """ - return keyfile_data[: len("$NACL")] == b"$NACL" - - -# sdk - deprecated import -# cli - no -# wallet - inside keyfile.py -def keyfile_data_is_encrypted_ansible(keyfile_data: bytes) -> bool: - """Returns true if the keyfile data is ansible encrypted. - - Args: - keyfile_data (bytes): The bytes to validate. - Returns: - is_ansible (bool): True if the data is ansible encrypted. - """ - return keyfile_data[:14] == b"$ANSIBLE_VAULT" - - -# sdk - deprecated import -# cli - no -# wallet - inside keyfile.py -def keyfile_data_is_encrypted_legacy(keyfile_data: bytes) -> bool: - """Returns true if the keyfile data is legacy encrypted. - Args: - keyfile_data (bytes): The bytes to validate. - Returns: - is_legacy (bool): ``True`` if the data is legacy encrypted. - """ - return keyfile_data[:6] == b"gAAAAA" - - -# sdk - deprecated import -# cli - no -# wallet - inside keyfile.py -def keyfile_data_is_encrypted(keyfile_data: bytes) -> bool: - """Returns ``true`` if the keyfile data is encrypted. - - Args: - keyfile_data (bytes): The bytes to validate. - Returns: - is_encrypted (bool): ``True`` if the data is encrypted. - """ - return ( - keyfile_data_is_encrypted_nacl(keyfile_data) - or keyfile_data_is_encrypted_ansible(keyfile_data) - or keyfile_data_is_encrypted_legacy(keyfile_data) - ) - - -# sdk - deprecated import -# cli - no -# wallet - inside keyfile.py -def keyfile_data_encryption_method(keyfile_data: bytes) -> str: - """Returns type of encryption method as a string. - - Args: - keyfile_data (bytes, required): Bytes to validate - Returns: - encryption_method (str): returns the name of encryption method. - """ - - if keyfile_data_is_encrypted_nacl(keyfile_data): - return "NaCl" - elif keyfile_data_is_encrypted_ansible(keyfile_data): - return "Ansible Vault" - elif keyfile_data_is_encrypted_legacy(keyfile_data): - return "legacy" - - -# sdk - deprecated import -# cli - no -# wallet - inside keyfile.py -def legacy_encrypt_keyfile_data( - keyfile_data: bytes, password: Optional[str] = None -) -> bytes: - password = ask_password_to_encrypt() if password is None else password - with console.status( - ":exclamation_mark: Encrypting key with legacy encryption method..." - ): - vault = Vault(password) - return vault.vault.encrypt(keyfile_data) - - -# sdk - deprecated import -# cli - no -# wallet - inside keyfile.py -def encrypt_keyfile_data(keyfile_data: bytes, password: Optional[str] = None) -> bytes: - """Encrypts the passed keyfile data using ansible vault. - - Args: - keyfile_data (bytes): The bytes to encrypt. - password (str, optional): The password used to encrypt the data. If ``None``, asks for user input. - Returns: - encrypted_data (bytes): The encrypted data. - """ - password = ask_password_to_encrypt() if password is None else password - password = bytes(password, "utf-8") - kdf = pwhash.argon2i.kdf - key = kdf( - secret.SecretBox.KEY_SIZE, - password, - NACL_SALT, - opslimit=pwhash.argon2i.OPSLIMIT_SENSITIVE, - memlimit=pwhash.argon2i.MEMLIMIT_SENSITIVE, - ) - box = secret.SecretBox(key) - encrypted = box.encrypt(keyfile_data) - return b"$NACL" + encrypted - - -# sdk - deprecated import -# cli - no -# wallet - inside keyfile.py -def get_coldkey_password_from_environment(coldkey_name: str) -> Optional[str]: - """Retrieves the cold key password from the environment variables. - - Args: - coldkey_name (str): The name of the cold key. - Returns: - password (str): The password retrieved from the environment variables, or ``None`` if not found. - """ - envs = { - normalized_env_name: env_value - for env_name, env_value in os.environ.items() - if (normalized_env_name := env_name.upper()).startswith("BT_COLD_PW_") - } - return envs.get(f"BT_COLD_PW_{coldkey_name.replace('-', '_').upper()}") - - -# sdk - deprecated import -# cli - no -# wallet - inside keyfile.py -def decrypt_keyfile_data( - keyfile_data: bytes, - password: Optional[str] = None, - coldkey_name: Optional[str] = None, -) -> bytes: - """Decrypts the passed keyfile data using ansible vault. - - Args: - keyfile_data (bytes): The bytes to decrypt. - password (str, optional): The password used to decrypt the data. If ``None``, asks for user input. - coldkey_name (str, optional): The name of the cold key. If provided, retrieves the password from environment variables. - - Returns: - decrypted_data (bytes): The decrypted data. - - Raises: - KeyFileError: Raised if the file is corrupted or if the password is incorrect. - """ - if coldkey_name is not None and password is None: - password = get_coldkey_password_from_environment(coldkey_name) - - try: - password = ( - getpass.getpass("Enter password to unlock key: ") - if password is None - else password - ) - with console.status(":key: Decrypting key..."): - # NaCl SecretBox decrypt. - if keyfile_data_is_encrypted_nacl(keyfile_data): - password = bytes(password, "utf-8") - kdf = pwhash.argon2i.kdf - key = kdf( - secret.SecretBox.KEY_SIZE, - password, - NACL_SALT, - opslimit=pwhash.argon2i.OPSLIMIT_SENSITIVE, - memlimit=pwhash.argon2i.MEMLIMIT_SENSITIVE, - ) - box = secret.SecretBox(key) - decrypted_keyfile_data = box.decrypt(keyfile_data[len("$NACL") :]) - # Ansible decrypt. - elif keyfile_data_is_encrypted_ansible(keyfile_data): - vault = Vault(password) - try: - decrypted_keyfile_data = vault.load(keyfile_data) - except AnsibleVaultError: - raise KeyFileError("Invalid password") - # Legacy decrypt. - elif keyfile_data_is_encrypted_legacy(keyfile_data): - __SALT = ( - b"Iguesscyborgslikemyselfhaveatendencytobeparanoidaboutourorigins" - ) - kdf = PBKDF2HMAC( - algorithm=hashes.SHA256(), - salt=__SALT, - length=32, - iterations=10000000, - backend=default_backend(), - ) - key = base64.urlsafe_b64encode(kdf.derive(password.encode())) - cipher_suite = Fernet(key) - decrypted_keyfile_data = cipher_suite.decrypt(keyfile_data) - # Unknown. - else: - raise KeyFileError(f"keyfile data: {keyfile_data.decode()} is corrupt.") - - except (InvalidSignature, InvalidKey, InvalidToken): - raise KeyFileError("Invalid password") - - if not isinstance(decrypted_keyfile_data, bytes): - decrypted_keyfile_data = json.dumps(decrypted_keyfile_data).encode() - return decrypted_keyfile_data - - -# wallet - Wallet, Keyfile -# sdk - deprecated, test deprecated ? Passably the pyo3/maturing impl not so important, BUT GOOD TO HAVE -# cli - bittensor_cli/src/commands/wallets.py -class Keyfile: - """Defines an interface for a substrate interface keypair stored on device.""" - - def __init__(self, path: str, name: str): - self.path = os.path.expanduser(path) - self.name = Path(self.path).parent.stem - self._name = name - - def __str__(self): - if not self.exists_on_device(): - return "keyfile (empty, {})>".format(self.path) - if self.is_encrypted(): - return "Keyfile ({} encrypted, {})>".format( - keyfile_data_encryption_method(self._read_keyfile_data_from_file()), - self.path, - ) - else: - return "keyfile (decrypted, {})>".format(self.path) - - def __repr__(self): - return self.__str__() - - @property - def keypair(self) -> "Keypair": - """Returns the keypair from path, decrypts data if the file is encrypted. - - Returns: - keypair (Keypair): The keypair stored under the path. - Raises: - KeyFileError: Raised if the file does not exist, is not readable, writable, corrupted, or if the password is incorrect. - """ - return self.get_keypair() - - @property - def data(self) -> bytes: - """Returns the keyfile data under path. - - Returns: - keyfile_data (bytes): The keyfile data stored under the path. - Raises: - KeyFileError: Raised if the file does not exist, is not readable, or writable. - """ - return self._read_keyfile_data_from_file() - - @property - def keyfile_data(self) -> bytes: - """Returns the keyfile data under path. - - Returns: - keyfile_data (bytes): The keyfile data stored under the path. - Raises: - KeyFileError: Raised if the file does not exist, is not readable, or writable. - """ - return self._read_keyfile_data_from_file() - - def set_keypair( - self, - keypair: "Keypair", - encrypt: bool = True, - overwrite: bool = False, - password: Optional[str] = None, - ): - """Writes the keypair to the file and optionally encrypts data. - - Args: - keypair (Keypair): The keypair to store under the path. - encrypt (bool, optional): If ``True``, encrypts the file under the path. Default is ``True``. - overwrite (bool, optional): If ``True``, forces overwrite of the current file. Default is ``False``. - password (str, optional): The password used to encrypt the file. If ``None``, asks for user input. - Raises: - KeyFileError: Raised if the file does not exist, is not readable, writable, or if the password is incorrect. - """ - self.make_dirs() - keyfile_data = serialized_keypair_to_keyfile_data(keypair) - if encrypt: - keyfile_data = encrypt_keyfile_data(keyfile_data, password) - self._write_keyfile_data_to_file(keyfile_data, overwrite=overwrite) - - def get_keypair(self, password: Optional[str] = None) -> "Keypair": - """Returns the keypair from the path, decrypts data if the file is encrypted. - - Args: - password (str, optional): The password used to decrypt the file. If ``None``, asks for user input. - Returns: - keypair (Keypair): The keypair stored under the path. - Raises: - KeyFileError: Raised if the file does not exist, is not readable, writable, corrupted, or if the password is incorrect. - """ - keyfile_data = self._read_keyfile_data_from_file() - if keyfile_data_is_encrypted(keyfile_data): - decrypted_keyfile_data = decrypt_keyfile_data( - keyfile_data, password, coldkey_name=self.name - ) - else: - decrypted_keyfile_data = keyfile_data - return deserialize_keypair_from_keyfile_data(decrypted_keyfile_data) - - def make_dirs(self): - """Creates directories for the path if they do not exist.""" - directory = os.path.dirname(self.path) - if not os.path.exists(directory): - os.makedirs(directory) - - def exists_on_device(self) -> bool: - """Returns ``True`` if the file exists on the device. - - Returns: - on_device (bool): ``True`` if the file is on the device. - """ - if not os.path.isfile(self.path): - return False - return True - - def is_readable(self) -> bool: - """Returns ``True`` if the file under path is readable. - - Returns: - readable (bool): ``True`` if the file is readable. - """ - if not self.exists_on_device(): - return False - if not os.access(self.path, os.R_OK): - return False - return True - - def is_writable(self) -> bool: - """Returns ``True`` if the file under path is writable. - - Returns: - writable (bool): ``True`` if the file is writable. - """ - if os.access(self.path, os.W_OK): - return True - return False - - def is_encrypted(self) -> bool: - """Returns ``True`` if the file under path is encrypted. - - Returns: - encrypted (bool): ``True`` if the file is encrypted. - """ - if not self.exists_on_device(): - return False - if not self.is_readable(): - return False - return keyfile_data_is_encrypted(self._read_keyfile_data_from_file()) - - def _may_overwrite(self) -> bool: - """Asks the user if it is okay to overwrite the file. - - Returns: - may_overwrite (bool): ``True`` if the user allows overwriting the file. - """ - choice = input("File {} already exists. Overwrite? (y/N) ".format(self.path)) - return choice == "y" - - def check_and_update_encryption( - self, print_result: bool = True, no_prompt: bool = False - ): - """Check the version of keyfile and update if needed. - - Args: - print_result (bool): - Print the checking result or not. - no_prompt (bool): - Skip if no prompt. - Raises: - KeyFileError: - Raised if the file does not exist, is not readable, writable. - Returns: - result (bool): - Return ``True`` if the keyfile is the most updated with nacl, else ``False``. - """ - if not self.exists_on_device(): - if print_result: - console.print(f"Keyfile does not exist. {self.path}") - return False - if not self.is_readable(): - if print_result: - console.print(f"Keyfile is not redable. {self.path}") - return False - if not self.is_writable(): - if print_result: - console.print(f"Keyfile is not writable. {self.path}") - return False - - update_keyfile = False - if not no_prompt: - keyfile_data = self._read_keyfile_data_from_file() - - # If the key is not nacl encrypted. - if keyfile_data_is_encrypted( - keyfile_data - ) and not keyfile_data_is_encrypted_nacl(keyfile_data): - terminate = False - console.print( - f"You may update the keyfile to improve the security for storing your keys.\nWhile the key and the password stays the same, it would require providing your password once.\n:key:{self}\n" - ) - update_keyfile = Confirm.ask("Update keyfile?") - if update_keyfile: - stored_mnemonic = False - while not stored_mnemonic: - console.print( - "\nPlease make sure you have the mnemonic stored in case an error occurs during the transfer.", - style="white on red", - ) - stored_mnemonic = Confirm.ask("Have you stored the mnemonic?") - if not stored_mnemonic and not Confirm.ask( - "You must proceed with a stored mnemonic, retry and continue this keyfile update?" - ): - terminate = True - break - - decrypted_keyfile_data = None - password = None - while decrypted_keyfile_data is None and not terminate: - try: - password = getpass.getpass( - "\nEnter password to update keyfile: " - ) - decrypted_keyfile_data = decrypt_keyfile_data( - keyfile_data, coldkey_name=self.name, password=password - ) - except KeyFileError: - if not Confirm.ask( - "Invalid password, retry and continue this keyfile update?" - ): - terminate = True - break - - if not terminate and password: - encrypted_keyfile_data = encrypt_keyfile_data( - decrypted_keyfile_data, password=password - ) - self._write_keyfile_data_to_file( - encrypted_keyfile_data, overwrite=True - ) - - if print_result or update_keyfile: - keyfile_data = self._read_keyfile_data_from_file() - if not keyfile_data_is_encrypted(keyfile_data): - if print_result: - console.print(f"\nKeyfile is not encrypted. \n:key: {self}") - return False - elif keyfile_data_is_encrypted_nacl(keyfile_data): - if print_result: - console.print( - f"\n:white_heavy_check_mark: Keyfile is updated. \n:key: {self}" - ) - return True - else: - if print_result: - console.print( - f'\n:cross_mark: Keyfile is outdated, please update with "btcli wallet update" \n:key: {self}' - ) - return False - return False - - def encrypt(self, password: Optional[str] = None): - """Encrypts the file under the path. - - Args: - password (str, optional): The password for encryption. If ``None``, asks for user input. - Raises: - KeyFileError: Raised if the file does not exist, is not readable, or writable. - """ - if not self.exists_on_device(): - raise KeyFileError("Keyfile at: {} does not exist".format(self.path)) - if not self.is_readable(): - raise KeyFileError("Keyfile at: {} is not readable".format(self.path)) - if not self.is_writable(): - raise KeyFileError("Keyfile at: {} is not writable".format(self.path)) - keyfile_data = self._read_keyfile_data_from_file() - if not keyfile_data_is_encrypted(keyfile_data): - as_keypair = deserialize_keypair_from_keyfile_data(keyfile_data) - keyfile_data = serialized_keypair_to_keyfile_data(as_keypair) - keyfile_data = encrypt_keyfile_data(keyfile_data, password) - self._write_keyfile_data_to_file(keyfile_data, overwrite=True) - - def decrypt(self, password: Optional[str] = None): - """Decrypts the file under the path. - - Args: - password (str, optional): The password for decryption. If ``None``, asks for user input. - Raises: - KeyFileError: Raised if the file does not exist, is not readable, writable, corrupted, or if the password is incorrect. - """ - if not self.exists_on_device(): - raise KeyFileError("Keyfile at: {} does not exist".format(self.path)) - if not self.is_readable(): - raise KeyFileError("Keyfile at: {} is not readable".format(self.path)) - if not self.is_writable(): - raise KeyFileError("Keyfile at: {} is not writable".format(self.path)) - keyfile_data = self._read_keyfile_data_from_file() - if keyfile_data_is_encrypted(keyfile_data): - keyfile_data = decrypt_keyfile_data( - keyfile_data, password, coldkey_name=self.name - ) - as_keypair = deserialize_keypair_from_keyfile_data(keyfile_data) - keyfile_data = serialized_keypair_to_keyfile_data(as_keypair) - self._write_keyfile_data_to_file(keyfile_data, overwrite=True) - - def _read_keyfile_data_from_file(self) -> bytes: - """Reads the keyfile data from the file. - - Returns: - keyfile_data (bytes): The keyfile data stored under the path. - Raises: - KeyFileError: Raised if the file does not exist or is not readable. - """ - if not self.exists_on_device(): - raise KeyFileError("Keyfile at: {} does not exist".format(self.path)) - if not self.is_readable(): - raise KeyFileError("Keyfile at: {} is not readable".format(self.path)) - with open(self.path, "rb") as file: - data = file.read() - return data - - def _write_keyfile_data_to_file(self, keyfile_data: bytes, overwrite: bool = False): - """Writes the keyfile data to the file. - - Args: - keyfile_data (bytes): The byte data to store under the path. - overwrite (bool, optional): If ``True``, overwrites the data without asking for permission from the user. Default is ``False``. - Raises: - KeyFileError: Raised if the file is not writable or the user responds No to the overwrite prompt. - """ - # Check overwrite. - if self.exists_on_device() and not overwrite: - if not self._may_overwrite(): - raise KeyFileError("Keyfile at: {} is not writable".format(self.path)) - with open(self.path, "wb") as keyfile: - keyfile.write(keyfile_data) - # Set file permissions. - os.chmod(self.path, stat.S_IRUSR | stat.S_IWUSR) diff --git a/btwallet/mock/__init__.py b/btwallet/mock/__init__.py deleted file mode 100644 index 985d777..0000000 --- a/btwallet/mock/__init__.py +++ /dev/null @@ -1,36 +0,0 @@ -# The MIT License (MIT) -# Copyright © 2024 Opentensor Foundation -# -# 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 .wallet_mock import ( - MockKeyfile, - MockWallet, - get_mock_wallet, - get_mock_keypair, - get_mock_hotkey, - get_mock_coldkey, -) - - -__all__ = [ - "MockKeyfile", - "MockWallet", - "get_mock_wallet", - "get_mock_keypair", - "get_mock_hotkey", - "get_mock_coldkey", -] diff --git a/btwallet/mock/keyfile_mock.py b/btwallet/mock/keyfile_mock.py deleted file mode 100644 index 29126d4..0000000 --- a/btwallet/mock/keyfile_mock.py +++ /dev/null @@ -1,91 +0,0 @@ -# The MIT License (MIT) -# Copyright © 2024 Opentensor Foundation -# -# 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 typing import Optional - -from substrateinterface import Keypair - -from bittensor_wallet.keyfile import serialized_keypair_to_keyfile_data, Keyfile - - -class MockKeyfile(Keyfile): - """Defines an interface to a mocked keyfile object (nothing is created on device) keypair is treated as non encrypted and the data is just the string version.""" - - def __init__(self, path: str): - super().__init__(path) - - self._mock_keypair = Keypair.create_from_mnemonic( - mnemonic="arrive produce someone view end scout bargain coil slight festival excess struggle" - ) - self._mock_data = serialized_keypair_to_keyfile_data(self._mock_keypair) - - def __str__(self): - if not self.exists_on_device(): - return "Keyfile (empty, {})>".format(self.path) - if self.is_encrypted(): - return "Keyfile (encrypted, {})>".format(self.path) - else: - return "Keyfile (decrypted, {})>".format(self.path) - - def __repr__(self): - return self.__str__() - - @property - def keypair(self) -> "Keypair": - return self._mock_keypair - - @property - def data(self) -> bytes: - return bytes(self._mock_data) - - @property - def keyfile_data(self) -> bytes: - return bytes(self._mock_data) - - def set_keypair( - self, - keypair: "Keypair", - encrypt: bool = True, - overwrite: bool = False, - password: Optional[str] = None, - ): - self._mock_keypair = keypair - self._mock_data = serialized_keypair_to_keyfile_data(self._mock_keypair) - - def get_keypair(self, password: Optional[str] = None) -> "Keypair": - return self._mock_keypair - - def make_dirs(self): - return - - def exists_on_device(self) -> bool: - return True - - def is_readable(self) -> bool: - return True - - def is_writable(self) -> bool: - return True - - def is_encrypted(self) -> bool: - return False - - def encrypt(self, password: Optional[str] = None): - raise ValueError("Cannot encrypt a mock keyfile") - - def decrypt(self, password: Optional[str] = None): - return diff --git a/btwallet/mock/wallet_mock.py b/btwallet/mock/wallet_mock.py deleted file mode 100644 index 5c281a5..0000000 --- a/btwallet/mock/wallet_mock.py +++ /dev/null @@ -1,127 +0,0 @@ -# The MIT License (MIT) -# Copyright © 2024 Opentensor Foundation -# -# 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. - -import os -from typing import Optional - -from Crypto.Hash import keccak -from substrateinterface import Keypair - -from bittensor_wallet.utils import SS58_FORMAT -from .keyfile_mock import MockKeyfile -from ..keyfile import Keyfile -from ..wallet import Wallet - - -class MockWallet(Wallet): - """ - Mocked Version of the bittensor wallet class, meant to be used for testing - """ - - def __init__(self, *args, **kwargs) -> None: - pass - - def __new__(cls, *args, **kwargs) -> "MockWallet": - r"""Init bittensor wallet object containing a hot and coldkey. - Args: - _mock (required=True, default=False): - If true creates a mock wallet with random keys. - """ - super().__new__(cls, *args, **kwargs) - # For mocking. - cls._is_mock = True - cls._mocked_coldkey_keyfile = None - cls._mocked_hotkey_keyfile = None - - return cls - - @property - def hotkey_file(self) -> "Keyfile": - if self._is_mock: - if self._mocked_hotkey_keyfile is None: - self._mocked_hotkey_keyfile = MockKeyfile(path="MockedHotkey") - return self._mocked_hotkey_keyfile - else: - wallet_path = os.path.expanduser(os.path.join(self.path, self.name)) - hotkey_path = os.path.join(wallet_path, "hotkeys", self.hotkey_str) - return Keyfile(path=hotkey_path) - - @property - def coldkey_file(self) -> "Keyfile": - if self._is_mock: - if self._mocked_coldkey_keyfile is None: - self._mocked_coldkey_keyfile = MockKeyfile(path="MockedColdkey") - return self._mocked_coldkey_keyfile - else: - wallet_path = os.path.expanduser(os.path.join(self.path, self.name)) - coldkey_path = os.path.join(wallet_path, "coldkey") - return Keyfile(path=coldkey_path) - - @property - def coldkeypub_file(self) -> "Keyfile": - if self._is_mock: - if self._mocked_coldkey_keyfile is None: - self._mocked_coldkey_keyfile = MockKeyfile(path="MockedColdkeyPub") - return self._mocked_coldkey_keyfile - else: - wallet_path = os.path.expanduser(os.path.join(self.path, self.name)) - coldkeypub_path = os.path.join(wallet_path, "coldkeypub.txt") - return Keyfile(path=coldkeypub_path) - - -def get_mock_wallet(coldkey: "Keypair" = None, hotkey: "Keypair" = None): - wallet = MockWallet(name="mock_wallet", hotkey="mock", path="/tmp/mock_wallet") - - if not coldkey: - coldkey = Keypair.create_from_mnemonic(Keypair.generate_mnemonic()) - if not hotkey: - hotkey = Keypair.create_from_mnemonic(Keypair.generate_mnemonic()) - - wallet.set_coldkey(coldkey, encrypt=False, overwrite=True) - wallet.set_coldkeypub(coldkey, encrypt=False, overwrite=True) - wallet.set_hotkey(hotkey, encrypt=False, overwrite=True) - - return wallet - - -def get_mock_keypair(uid: int, test_name: Optional[str] = None) -> Keypair: - """ - Returns a mock keypair from uid and optional test_name. - If test_name is not provided, the uid is the only seed. - If test_name is provided, the uid is hashed with the test_name to create a unique seed for the test. - """ - if test_name is not None: - hashed_test_name: bytes = keccak.new( - digest_bits=256, data=test_name.encode("utf-8") - ).digest() - hashed_test_name_as_int: int = int.from_bytes( - hashed_test_name, byteorder="big", signed=False - ) - uid = uid + hashed_test_name_as_int - - return Keypair.create_from_seed( - seed_hex=int.to_bytes(uid, 32, "big", signed=False), - ss58_format=SS58_FORMAT, - ) - - -def get_mock_hotkey(uid: int) -> str: - return get_mock_keypair(uid).ss58_address - - -def get_mock_coldkey(uid: int) -> str: - return get_mock_keypair(uid).ss58_address diff --git a/btwallet/utils.py b/btwallet/utils.py deleted file mode 100644 index 9f5fe82..0000000 --- a/btwallet/utils.py +++ /dev/null @@ -1,104 +0,0 @@ -# The MIT License (MIT) -# Copyright © 2024 Opentensor Foundation -# -# 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 typing import Union - -from substrateinterface import Keypair as Keypair -from substrateinterface.utils import ss58 - -# Substrate ss58_format -SS58_FORMAT = 42 - - -def get_ss58_format(ss58_address: str) -> int: - """Returns the ss58 format of the given ss58 address.""" - return ss58.get_ss58_format(ss58_address) - - -def is_valid_ss58_address(address: str) -> bool: - """ - Checks if the given address is a valid ss58 address. - - Args: - address(str): The address to check. - - Returns: - True if the address is a valid ss58 address for Bittensor, False otherwise. - """ - try: - return ss58.is_valid_ss58_address( - address, valid_ss58_format=SS58_FORMAT - ) or ss58.is_valid_ss58_address( - address, valid_ss58_format=42 - ) # Default substrate ss58 format (legacy) - except IndexError: - return False - - -def is_valid_ed25519_pubkey(public_key: Union[str, bytes]) -> bool: - """ - Checks if the given public_key is a valid ed25519 key. - - Args: - public_key(Union[str, bytes]): The public_key to check. - - Returns: - True if the public_key is a valid ed25519 key, False otherwise. - - """ - try: - if isinstance(public_key, str): - if len(public_key) != 64 and len(public_key) != 66: - raise ValueError("a public_key should be 64 or 66 characters") - elif isinstance(public_key, bytes): - if len(public_key) != 32: - raise ValueError("a public_key should be 32 bytes") - else: - raise ValueError("public_key must be a string or bytes") - - keypair = Keypair(public_key=public_key, ss58_format=SS58_FORMAT) - - ss58_addr = keypair.ss58_address - return ss58_addr is not None - - except (ValueError, IndexError): - return False - - -def is_valid_bittensor_address_or_public_key(address: Union[str, bytes]) -> bool: - """ - Checks if the given address is a valid destination address. - - Args: - address(Union[str, bytes]): The address to check. - - Returns: - True if the address is a valid destination address, False otherwise. - """ - if isinstance(address, str): - # Check if ed25519 - if address.startswith("0x"): - return is_valid_ed25519_pubkey(address) - else: - # Assume ss58 address - return is_valid_ss58_address(address) - elif isinstance(address, bytes): - # Check if ed25519 - return is_valid_ed25519_pubkey(address) - else: - # Invalid address type - return False diff --git a/btwallet/wallet.py b/btwallet/wallet.py deleted file mode 100644 index 3e29d37..0000000 --- a/btwallet/wallet.py +++ /dev/null @@ -1,933 +0,0 @@ -# The MIT License (MIT) -# Copyright © 2024 Opentensor Foundation -# -# 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. - -"""Implementation of the wallet class, which manages balances with staking and transfer. Also manages hotkey and coldkey.""" - -import argparse -import copy -import os -from typing import Dict, Optional, Tuple, Union, overload - -from substrateinterface import Keypair -from termcolor import colored - -from .config import Config -from .errors import KeyFileError -from .keyfile import Keyfile -from .utils import ( - is_valid_bittensor_address_or_public_key, - get_ss58_format, - SS58_FORMAT, -) - -BT_WALLET_NAME = "default" -BT_WALLET_PATH = "~/.bittensor/wallets/" - - -def display_mnemonic_msg(keypair: Keypair, key_type: str): - """ - Display the mnemonic and a warning message to keep the mnemonic safe. - - Args: - keypair (Keypair): Keypair object. - key_type (str): Type of the key (coldkey or hotkey). - """ - mnemonic = keypair.mnemonic - mnemonic_green = colored(mnemonic, "green") - print( - colored( - "\nIMPORTANT: Store this mnemonic in a secure (preferable offline place), as anyone " - "who has possession of this mnemonic can use it to regenerate the key and access your tokens.\n", - "red", - ) - ) - print("The mnemonic to the new {} is:\n\n{}\n".format(key_type, mnemonic_green)) - print( - "You can use the mnemonic to recreate the key in case it gets lost. The command to use to regenerate the key using this mnemonic is:" - ) - print("btcli w regen_{} --mnemonic {}".format(key_type, mnemonic)) - print("") - - -class Wallet: - """ - The wallet class in the Bittensor framework handles wallet functionality, crucial for participating in the Bittensor network. - - It manages two types of keys: coldkey and hotkey, each serving different purposes in network operations. Each wallet contains a coldkey and a hotkey. - - The coldkey is the user's primary key for holding stake in their wallet and is the only way that users - can access Tao. Coldkeys can hold tokens and should be encrypted on your device. - - The coldkey is the primary key used for securing the wallet's stake in the Bittensor network (Tao) and - is critical for financial transactions like staking and unstaking tokens. It's recommended to keep the - coldkey encrypted and secure, as it holds the actual tokens. - - The hotkey, in contrast, is used for operational tasks like subscribing to and setting weights in the - network. It's linked to the coldkey through the metagraph and does not directly hold tokens, thereby - offering a safer way to interact with the network during regular operations. - - Args: - name (str): The name of the wallet, used to identify it among possibly multiple wallets. - hotkey (str): String identifier for the hotkey. - path (str): File system path where wallet keys are stored. - config (Config): Bittensor configuration object. - _hotkey, _coldkey, _coldkeypub (Keypair): Internal representations of the hotkey and coldkey. - - Methods: - create_if_non_existent, create, recreate: Methods to handle the creation of wallet keys. - get_coldkey, get_hotkey, get_coldkeypub: Methods to retrieve specific keys. - set_coldkey, set_hotkey, set_coldkeypub: Methods to set or update keys. - hotkey_file, coldkey_file, coldkeypub_file: Properties that return respective key file objects. - regenerate_coldkey, regenerate_hotkey, regenerate_coldkeypub: Methods to regenerate keys from different sources. - Config, help, add_args: Utility methods for configuration and assistance. - - The wallet class is a fundamental component for users to interact securely with the Bittensor network, facilitating both operational tasks and transactions involving value transfer across the network. - - Example Usage:: - - # Create a new wallet with default coldkey and hotkey names - my_wallet = wallet() - - # Access hotkey and coldkey - hotkey = my_wallet.get_hotkey() - coldkey = my_wallet.get_coldkey() - - # Set a new coldkey - my_wallet.new_coldkey(n_words=24) # number of seed words to use - - # Update wallet hotkey - my_wallet.set_hotkey(new_hotkey) - - # Print wallet details - print(my_wallet) - - # Access coldkey property, must use password to unlock - my_wallet.coldkey - """ - - def __init__( - self, - name: Optional[str] = None, - hotkey: Optional[str] = None, - path: Optional[str] = None, - config: Optional["Config"] = None, - ): - """ - Initialize the bittensor wallet object containing a hot and coldkey. - - Args: - name (str, optional): The name of the wallet to unlock for running bittensor. Defaults to ``default``. - hotkey (str, optional): The name of hotkey used to running the miner. Defaults to ``default``. - path (str, optional): The path to your bittensor wallets. Defaults to ``~/.bittensor/wallets/``. - config (Config, optional): config.Config(). Defaults to ``None``. - """ - if config is None: - config = Wallet.config() - - self.config = copy.deepcopy(config) - self.config.wallet.name = name or self.config.wallet.get( - "name", Wallet.config().name - ) - self.config.wallet.hotkey = hotkey or self.config.wallet.get( - "hotkey", Wallet.config().hotkey - ) - self.config.wallet.path = path or self.config.wallet.get( - "path", Wallet.config().path - ) - self.config.wallet.path = self.config.wallet.path.strip("'") - - self.name = self.config.wallet.name - self.path = self.config.wallet.path - self.hotkey_str = self.config.wallet.hotkey - - self._hotkey = None - self._coldkey = None - self._coldkeypub = None - - def __str__(self): - """ - Returns a human-readable string representation of the wallet object. - - Returns: - str: The string representation. - """ - return f"Wallet (Name: '{self.name}', Hotkey: '{self.hotkey_str}', Path: '{self.path}')" - - def __repr__(self): - """ - Returns the string representation of the wallet object. - - Returns: - str: The string representation. - """ - return f"name: '{self.name}', hotkey: '{self.hotkey_str}', path: '{self.path}'" - - @classmethod - def config(cls) -> "Config": - """ - Get config from the argument parser. - - Returns: - Config: Config object. - """ - parser = argparse.ArgumentParser() - cls.add_args(parser) - return Config(parser, args=[]) - - @classmethod - def help(cls): - """ - Print help to stdout. - """ - parser = argparse.ArgumentParser() - cls.add_args(parser) - print(cls.__new__.__doc__) - parser.print_help() - - @classmethod - def add_args(cls, parser: argparse.ArgumentParser, prefix: Optional[str] = None): - """ - Accept specific arguments from parser. - - Args: - parser (argparse.ArgumentParser): Argument parser object. - prefix (str): Argument prefix. - """ - prefix_str = "" if prefix is None else prefix + "." - try: - default_name = os.getenv("BT_WALLET_NAME") or BT_WALLET_NAME - default_hotkey = os.getenv("BT_WALLET_NAME") or BT_WALLET_NAME - default_path = os.getenv("BT_WALLET_PATH") or BT_WALLET_PATH - parser.add_argument( - "--" + prefix_str + "wallet.name", - required=False, - default=default_name, - help="The name of the wallet to unlock for running bittensor " - "(name mock is reserved for mocking this wallet)", - ) - parser.add_argument( - "--" + prefix_str + "wallet.hotkey", - required=False, - default=default_hotkey, - help="The name of the wallet's hotkey.", - ) - parser.add_argument( - "--" + prefix_str + "wallet.path", - required=False, - default=default_path, - help="The path to your bittensor wallets", - ) - except argparse.ArgumentError: - pass - - def create_if_non_existent( - self, coldkey_use_password: bool = True, hotkey_use_password: bool = False - ) -> "Wallet": - """ - Checks for existing coldkeypub and hotkeys, and creates them if non-existent. - - Args: - coldkey_use_password (bool, optional): Whether to use a password for coldkey. Defaults to ``True``. - hotkey_use_password (bool, optional): Whether to use a password for hotkey. Defaults to ``False``. - - Returns: - wallet: The wallet object. - """ - return self.create(coldkey_use_password, hotkey_use_password) - - # def create_with_rust( - # self, mnemonic_num_words: int = 12, cold_key_password: Optional[str] = None - # ) -> "Wallet": - # try: - # btwallet.create_hotkey_pair(num_words=mnemonic_num_words, name=self.name) - # except Exception as e: - # print(f"Error creating hotkey: {e}") - # - # try: - # btwallet.create_coldkey_pair(num_words=mnemonic_num_words, name=self.name, password=cold_key_password) - # except Exception as e: - # print(f"Error creating coldkey: {e}") - # - # try: - # btwallet.create_coldkey_pub_pair(num_words=mnemonic_num_words,name=self.name) - # except Exception as e: - # print(f"Error creating coldkeypub: {e}") - # - # print(f"Wallet created: {self}") - - def create( - self, coldkey_use_password: bool = True, hotkey_use_password: bool = False - ) -> "Wallet": - """ - Checks for existing coldkeypub and hotkeys, and creates them if non-existent. - - Args: - coldkey_use_password (bool, optional): Whether to use a password for coldkey. Defaults to ``True``. - hotkey_use_password (bool, optional): Whether to use a password for hotkey. Defaults to ``False``. - - Returns: - wallet: The wallet object. - """ - # ---- Setup Wallet. ---- - if ( - not self.coldkey_file.exists_on_device() - and not self.coldkeypub_file.exists_on_device() - ): - self.create_new_coldkey(n_words=12, use_password=coldkey_use_password) - else: - print(f"ColdKey for the wallet '{self.name}' already exist.") - if not self.hotkey_file.exists_on_device(): - self.create_new_hotkey(n_words=12, use_password=hotkey_use_password) - else: - print(f"HotKey for the wallet '{self.name}' already exist.") - return self - - def recreate( - self, coldkey_use_password: bool = True, hotkey_use_password: bool = False - ) -> "Wallet": - """ - Checks for existing coldkeypub and hotkeys and creates them if non-existent. - - Args: - coldkey_use_password (bool, optional): Whether to use a password for coldkey. Defaults to ``True``. - hotkey_use_password (bool, optional): Whether to use a password for hotkey. Defaults to ``False``. - - Returns: - wallet: The wallet object. - """ - # ---- Setup Wallet. ---- - self.create_new_coldkey(n_words=12, use_password=coldkey_use_password) - self.create_new_hotkey(n_words=12, use_password=hotkey_use_password) - return self - - @property - def hotkey_file(self) -> "Keyfile": - """ - Property that returns the hotkey file. - - Returns: - Keyfile: The hotkey file. - """ - wallet_path = os.path.expanduser(os.path.join(self.path, self.name)) - hotkey_path = os.path.join(wallet_path, "hotkeys", self.hotkey_str) - return Keyfile(path=hotkey_path) - - @property - def coldkey_file(self) -> "Keyfile": - """ - Property that returns the coldkey file. - - Returns: - Keyfile: The coldkey file. - """ - wallet_path = os.path.expanduser(os.path.join(self.path, self.name)) - coldkey_path = os.path.join(wallet_path, "coldkey") - return Keyfile(path=coldkey_path) - - @property - def coldkeypub_file(self) -> "Keyfile": - """ - Property that returns the coldkeypub file. - - Returns: - Keyfile: The coldkeypub file. - """ - wallet_path = os.path.expanduser(os.path.join(self.path, self.name)) - coldkeypub_path = os.path.join(wallet_path, "coldkeypub.txt") - return Keyfile(path=coldkeypub_path) - - def set_hotkey( - self, keypair: "Keypair", encrypt: bool = False, overwrite: bool = False - ): - """ - Sets the hotkey for the wallet. - - Args: - keypair (Keypair): The hotkey keypair. - encrypt (bool, optional): Whether to encrypt the hotkey. Defaults to ``False``. - overwrite (bool, optional): Whether to overwrite an existing hotkey. Defaults to ``False``. - """ - self._hotkey = keypair - self.hotkey_file.set_keypair(keypair, encrypt=encrypt, overwrite=overwrite) - - def set_coldkeypub( - self, keypair: "Keypair", encrypt: bool = False, overwrite: bool = False - ): - """ - Sets the coldkeypub for the wallet. - - Args: - keypair (Keypair): The coldkeypub keypair. - encrypt (bool, optional): Whether to encrypt the coldkeypub. Defaults to ``False``. - overwrite (bool, optional): Whether to overwrite an existing coldkeypub. Defaults to ``False``. - - Returns: - Keyfile: The coldkeypub file. - """ - self._coldkeypub = Keypair(ss58_address=keypair.ss58_address) - self.coldkeypub_file.set_keypair( - self._coldkeypub, encrypt=encrypt, overwrite=overwrite - ) - return self.coldkeypub_file - - def set_coldkey( - self, - keypair: "Keypair", - encrypt: bool = True, - overwrite: bool = False, - ) -> "Keyfile": - """ - Sets the coldkey for the wallet. - - Args: - keypair (Keypair): The coldkey keypair. - encrypt (bool, optional): Whether to encrypt the coldkey. Defaults to ``True``. - overwrite (bool, optional): Whether to overwrite an existing coldkey. Defaults to ``False``. - - Returns: - Keyfile: The coldkey file. - """ - self._coldkey = keypair - self.coldkey_file.set_keypair( - self._coldkey, encrypt=encrypt, overwrite=overwrite - ) - return self.coldkey_file - - def get_coldkey(self, password: Optional[str] = None) -> "Keypair": - """ - Gets the coldkey from the wallet. - - Args: - password (str, optional): The password to decrypt the coldkey. Defaults to ``None``. - - Returns: - Keypair: The coldkey keypair. - """ - return self.coldkey_file.get_keypair(password=password) - - # def get_coldkey_with_rust(self, password: Optional[str] = None) -> "Keypair": - # """ - # Gets the coldkey from the wallet. - # - # Args: - # password (str, optional): The password to decrypt the coldkey. Defaults to ``None``. - # - # Returns: - # Keypair: The coldkey keypair. - # """ - # coldkey_file = KeyfileRust(name=self.name) - # return coldkey_file.get_keypair_with_rust(key_type="coldkey", password=password) - - # def get_hotkey_with_rust(self, password: Optional[str] = None) -> "Keypair": - # """ - # Gets the coldkey from the wallet. - # - # Args: - # password (str, optional): The password to decrypt the coldkey. Defaults to ``None``. - # - # Returns: - # Keypair: The coldkey keypair. - # """ - # hotkey_file = KeyfileRust(name=self.name) - # return hotkey_file.get_keypair_with_rust(key_type="hotkey") - - def get_hotkey(self, password: Optional[str] = None) -> "Keypair": - """ - Gets the hotkey from the wallet. - - Args: - password (str, optional): The password to decrypt the hotkey. Defaults to ``None``. - - Returns: - Keypair: The hotkey keypair. - """ - return self.hotkey_file.get_keypair(password=password) - - def get_coldkeypub(self, password: Optional[str] = None) -> "Keypair": - """ - Gets the coldkeypub from the wallet. - - Args: - password (str, optional): The password to decrypt the coldkeypub. Defaults to ``None``. - - Returns: - Keypair: The coldkeypub keypair. - """ - return self.coldkeypub_file.get_keypair(password=password) - - # def get_coldkey_pub_with_rust(self, password: Optional[str] = None) -> "Keypair": - # coldkey_pub_file = KeyfileRust(name=self.name) - # return coldkey_pub_file.get_keypair_with_rust(key_type="coldkey_pub") - - @property - def hotkey(self) -> "Keypair": - """Loads the hotkey from wallet.path/wallet.name/hotkeys/wallet.hotkey or raises an error. - - Returns: - hotkey (Keypair): - hotkey loaded from Config arguments. - - Raises: - KeyFileError: Raised if the file is corrupt of non-existent. - CryptoKeyError: Raised if the user enters an incorrect password for an encrypted keyfile. - """ - if self._hotkey is None: - try: - self._hotkey = self.hotkey_file.keypair - except KeyFileError as error: - print(f"KeyFileError: {error.args[0] if error.args else str(error)}.") - return self._hotkey - - @property - def coldkey(self) -> "Keypair": - """Loads the coldkey from wallet.path/wallet.name/coldkey or raises an error. - - Returns: - coldkey (Keypair): coldkey loaded from Config arguments. - - Raises: - KeyFileError: Raised if the file is corrupt of non-existent. - CryptoKeyError: Raised if the user enters an incorrect password for an encrypted keyfile. - """ - if self._coldkey is None: - try: - self._coldkey = self.coldkey_file.keypair - except Exception as error: - print(f"Error: {error.args[0] if error.args else str(error)}.") - - return self._coldkey - - @property - def coldkeypub(self) -> "Keypair": - """Loads the coldkeypub from wallet.path/wallet.name/coldkeypub.txt or raises an error. - - Returns: - coldkeypub (Keypair): coldkeypub loaded from Config arguments. - - Raises: - KeyFileError: Raised if the file is corrupt of non-existent. - CryptoKeyError: Raised if the user enters an incorrect password for an encrypted keyfile. - """ - if self._coldkeypub is None: - try: - self._coldkeypub = self.coldkeypub_file.keypair - except KeyFileError as error: - print(f"KeyFileError: {error.args[0] if error.args else str(error)}.") - return self._coldkeypub - - def create_coldkey_from_uri( - self, - uri: str, - use_password: bool = True, - overwrite: bool = False, - suppress: bool = False, - ) -> "Wallet": - """Creates coldkey from uri string, optionally encrypts it with the user-provided password. - - Args: - uri: (str, required): URI string to use i.e., ``/Alice`` or ``/Bob``. - use_password (bool, optional): Is the created key password protected. - overwrite (bool, optional): Determines if this operation overwrites the coldkey under the same path ``//coldkey``. - suppress (bool, optional): If ``True``, suppresses the display of the mnemonic message. Defaults to ``False``. - - Returns: - wallet (Wallet): This object with newly created coldkey. - """ - keypair = Keypair.create_from_uri(uri) - if not suppress: - display_mnemonic_msg(keypair, "coldkey") - self.set_coldkey(keypair, encrypt=use_password, overwrite=overwrite) - self.set_coldkeypub(keypair, overwrite=overwrite) - return self - - def create_hotkey_from_uri( - self, - uri: str, - use_password: bool = False, - overwrite: bool = False, - suppress: bool = False, - ) -> "Wallet": - """Creates hotkey from uri string, optionally encrypts it with the user-provided password. - - Args: - uri: (str, required): URI string to use i.e., ``/Alice`` or ``/Bob`` - use_password (bool, optional): Is the created key password protected. - overwrite (bool, optional): Determines if this operation overwrites the hotkey under the same path ``//hotkeys/``. - suppress (bool, optional): If ``True``, suppresses the display of the mnemonic message. Defaults to ``False``. - - Returns: - wallet (Wallet): This object with newly created hotkey. - """ - keypair = Keypair.create_from_uri(uri) - if not suppress: - display_mnemonic_msg(keypair, "hotkey") - self.set_hotkey(keypair, encrypt=use_password, overwrite=overwrite) - return self - - def unlock_coldkey(self) -> Keypair: - """ - Unlocks the coldkey (prompts password if locked) - - Returns: - coldkey (Keypair): the unlocked coldkey Keypair. - """ - return self.coldkey - - def unlock_coldkeypub(self) -> Keypair: - """ - Unlocks the coldkeypub. - - Returns: - coldkeypub (Keypair): the unlocked coldkeypub Keypair. - """ - return self.coldkeypub - - def unlock_hotkey(self) -> Keypair: - """ - Unlocks the hotkey. - - Returns: - hotkey (Keypair): the unlocked hotkey Keypair. - """ - return self.hotkey - - def new_coldkey( - self, - n_words: int = 12, - use_password: bool = True, - overwrite: bool = False, - suppress: bool = False, - ) -> "Wallet": - """Creates a new coldkey, optionally encrypts it with the user-provided password and saves to disk. - - Args: - n_words: (int, optional): Number of mnemonic words to use. - use_password (bool, optional): Is the created key password protected. - overwrite (bool, optional): Determines if this operation overwrites the coldkey under the same path ``//coldkey``. - suppress (bool, optional): If ``True``, suppresses the display of the mnemonic message. Defaults to ``False``. - - Returns: - wallet (Wallet): This object with newly created coldkey. - """ - return self.create_new_coldkey(n_words, use_password, overwrite, suppress) - - def create_new_coldkey( - self, - n_words: int = 12, - use_password: bool = True, - overwrite: bool = False, - suppress: bool = False, - ) -> "Wallet": - """Creates a new coldkey, optionally encrypts it with the user-provided password and saves to disk. - - Args: - n_words: (int, optional): Number of mnemonic words to use. - use_password (bool, optional): Is the created key password protected. - overwrite (bool, optional): Determines if this operation overwrites the coldkey under the same path ``//coldkey``. - suppress (bool, optional): If ``True``, suppresses the display of the mnemonic message. Defaults to ``False``. - - Returns: - wallet (Wallet): This object with newly created coldkey. - """ - mnemonic = Keypair.generate_mnemonic(n_words) - keypair = Keypair.create_from_mnemonic(mnemonic) - if not suppress: - display_mnemonic_msg(keypair, "coldkey") - self.set_coldkey(keypair, encrypt=use_password, overwrite=overwrite) - self.set_coldkeypub(keypair, overwrite=overwrite) - return self - - def new_hotkey( - self, - n_words: int = 12, - use_password: bool = False, - overwrite: bool = False, - suppress: bool = False, - ) -> "Wallet": - """Creates a new hotkey, optionally encrypts it with the user-provided password and saves to disk. - - Args: - n_words: (int, optional): Number of mnemonic words to use. - use_password (bool, optional): Is the created key password protected. - overwrite (bool, optional): Determines if this operation overwrites the hotkey under the same path ``//hotkeys/``. - suppress (bool, optional): If ``True``, suppresses the display of the mnemonic message. Defaults to ``False``. - - Returns: - wallet (Wallet): - This object with newly created hotkey. - """ - return self.create_new_hotkey(n_words, use_password, overwrite, suppress) - - def create_new_hotkey( - self, - n_words: int = 12, - use_password: bool = False, - overwrite: bool = False, - suppress: bool = False, - ) -> "Wallet": - """Creates a new hotkey, optionally encrypts it with the user-provided password and saves to disk. - - Args: - n_words: (int, optional): Number of mnemonic words to use. - use_password (bool, optional): Is the created key password protected. - overwrite (bool, optional): Will this operation overwrite the hotkey under the same path //hotkeys/ - suppress (bool, optional): If ``True``, suppresses the display of the mnemonic message. Defaults to ``False``. - - Returns: - wallet (Wallet): - This object with newly created hotkey. - """ - mnemonic = Keypair.generate_mnemonic(n_words) - keypair = Keypair.create_from_mnemonic(mnemonic) - - if not suppress: - display_mnemonic_msg(keypair, "hotkey") - - self.set_hotkey(keypair, encrypt=use_password, overwrite=overwrite) - return self - - def regenerate_coldkeypub( - self, - ss58_address: Optional[str] = None, - public_key: Optional[Union[str, bytes]] = None, - overwrite: bool = False, - ) -> "Wallet": - """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. - - Args: - ss58_address: (str, optional): Address as ``ss58`` string. - public_key: (str | bytes, optional): Public key as hex string or bytes. - overwrite (bool, optional) (default: False): Determines if this operation overwrites the coldkeypub (if exists) under the same path ``//coldkeypub``. - - Returns: - wallet (Wallet): - Newly re-generated wallet with coldkeypub. - - """ - if ss58_address is None and public_key is None: - raise ValueError("Either ss58_address or public_key must be passed") - - if not is_valid_bittensor_address_or_public_key( - ss58_address if ss58_address is not None else public_key - ): - raise ValueError( - f"Invalid {'ss58_address' if ss58_address is not None else 'public_key'}." - ) - - if ss58_address is not None: - ss58_format = get_ss58_format(ss58_address) - keypair = Keypair( - ss58_address=ss58_address, - public_key=public_key, - ss58_format=ss58_format, - ) - else: - keypair = Keypair( - ss58_address=ss58_address, - public_key=public_key, - ss58_format=SS58_FORMAT, - ) - - # No need to encrypt the public key - self.set_coldkeypub(keypair, overwrite=overwrite) - return self - - @overload - def regenerate_coldkey( - self, - mnemonic: Optional[Union[list, str]] = None, - use_password: bool = True, - overwrite: bool = False, - suppress: bool = False, - ) -> "Wallet": ... - - @overload - def regenerate_coldkey( - self, - seed: Optional[str] = None, - use_password: bool = True, - overwrite: bool = False, - suppress: bool = False, - ) -> "Wallet": ... - - @overload - def regenerate_coldkey( - self, - json: Optional[Tuple[Union[str, Dict], str]] = None, - use_password: bool = True, - overwrite: bool = False, - suppress: bool = False, - ) -> "Wallet": ... - - def regenerate_coldkey( - self, - use_password: bool = True, - overwrite: bool = False, - suppress: bool = False, - **kwargs, - ) -> "Wallet": - """Regenerates the coldkey from the passed mnemonic or seed, or JSON encrypts it with the user's password and saves the file. - - Args: - use_password (bool, optional): Is the created key password protected. - overwrite (bool, optional): Determines if this operation overwrites the coldkey under the same path ``//coldkey``. - suppress (bool, optional): If ``True``, suppresses the display of the mnemonic message. Defaults to ``False``. - kwargs["mnemonic"]: (Union[list, str], optional item of kwargs): Key mnemonic as list of words or string space separated words. - kwargs["seed"]: (str, optional item of kwargs): Seed as hex string. - kwargs["json"]: (Tuple[Union[str, Dict], str], optional item of kwargs): Restore from encrypted JSON backup as ``(json_data: Union[str, Dict], passphrase: str)`` - - Returns: - wallet (Wallet): - This object with newly created coldkey. - - Note: - Uses priority order: ``mnemonic > seed > json``. - """ - if len(kwargs) == 0: - raise ValueError("Must pass either mnemonic, seed, or json") - - # Get from kwargs - mnemonic: Optional[Union[list, str]] = kwargs.get("mnemonic", None) - seed: Optional[str] = kwargs.get("seed", None) - json: Optional[Tuple[Union[str, Dict], str]] = kwargs.get("json", None) - - if mnemonic is None and seed is None and json is None: - raise ValueError("Must pass either mnemonic, seed, or json") - - if mnemonic is not None: - if isinstance(mnemonic, str): - mnemonic = mnemonic.split() - - elif isinstance(mnemonic, list) and len(mnemonic) == 1: - mnemonic = mnemonic[0].split() - - if len(mnemonic) not in [12, 15, 18, 21, 24]: - raise ValueError( - "Mnemonic has invalid size. This should be 12,15,18,21 or 24 words" - ) - keypair = Keypair.create_from_mnemonic( - " ".join(mnemonic), ss58_format=SS58_FORMAT - ) - - if not suppress: - display_mnemonic_msg(keypair, "coldkey") - - elif seed is not None: - keypair = Keypair.create_from_seed(seed, ss58_format=SS58_FORMAT) - - else: - # json is not None - if ( - not isinstance(json, tuple) - or len(json) != 2 - or not isinstance(json[0], (str, dict)) - or not isinstance(json[1], str) - ): - raise ValueError( - "json must be a tuple of (json_data: str | Dict, passphrase: str)" - ) - - json_data, passphrase = json - keypair = Keypair.create_from_encrypted_json( - json_data, passphrase, ss58_format=SS58_FORMAT - ) - - self.set_coldkey(keypair, encrypt=use_password, overwrite=overwrite) - self.set_coldkeypub(keypair, overwrite=overwrite) - return self - - @overload - def regenerate_hotkey( - self, - *, - mnemonic: Optional[Union[list, str]] = None, - use_password: bool = True, - overwrite: bool = False, - suppress: bool = False, - ) -> "Wallet": ... - - @overload - def regenerate_hotkey( - self, - *, - seed: Optional[str] = None, - use_password: bool = True, - overwrite: bool = False, - suppress: bool = False, - ) -> "Wallet": ... - - @overload - def regenerate_hotkey( - self, - *, - json: Optional[Tuple[Union[str, Dict], str]] = None, - use_password: bool = True, - overwrite: bool = False, - suppress: bool = False, - ) -> "Wallet": ... - - def regenerate_hotkey( - self, - mnemonic: Optional[str] = None, - seed: Optional[str] = None, - json: Optional[Tuple[str, str]] = None, - use_password: bool = True, - overwrite: bool = False, - suppress: bool = False, - ) -> "Wallet": - """Regenerates the hotkey from passed mnemonic or seed, encrypts it with the user's password and saves the file. - - Args: - mnemonic (Union[list, str], optional): Key mnemonic as list of words or string space separated words. - seed (str, optional): Seed as hex string. - json (Tuple[Union[str, Dict], str], optional): Restore from encrypted JSON backup as ``(json_data: Union[str, Dict], passphrase: str)``. - use_password (bool, optional): Is the created key password protected. - overwrite (bool, optional): Determines if this operation overwrites the hotkey under the same path ``//hotkeys/``. - suppress (bool, optional): If ``True``, suppresses the display of the mnemonic message. Defaults to ``False``. - - Returns: - wallet (Wallet): - This object with newly created hotkey. - """ - if mnemonic is None and seed is None and json is None: - raise ValueError("Must pass either mnemonic, seed, or json") - - if mnemonic is not None: - keypair = Keypair.create_from_mnemonic(mnemonic) - if not suppress: - display_mnemonic_msg(keypair, "hotkey") - - elif seed is not None: - keypair = Keypair.create_from_seed(seed) - - else: - # json is not None - if ( - not isinstance(json, tuple) - or len(json) != 2 - or not isinstance(json[0], (str, dict)) - or not isinstance(json[1], str) - ): - raise ValueError( - "json must be a tuple of (json_data: str | Dict, passphrase: str)" - ) - - json_data, passphrase = json - keypair = Keypair.create_from_encrypted_json(json_data, passphrase) - - self.set_hotkey(keypair, encrypt=use_password, overwrite=overwrite) - return self From 79baf07cc580569363109fc3b159689c9bdd946d Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 18:50:36 -0400 Subject: [PATCH 27/36] bump version a bit --- Cargo.lock | 2 +- Cargo.toml | 2 +- bittensor_wallet/__init__.py | 2 +- pyproject.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b47d4f4..abad9fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -352,7 +352,7 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bittensor_wallet" -version = "2.0.0-a0" +version = "2.0.0-a1" dependencies = [ "ansible-vault", "base64 0.22.1", diff --git a/Cargo.toml b/Cargo.toml index 0716a42..964893a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bittensor_wallet" -version = "2.0.0-a0" +version = "2.0.0-a1" edition = "2021" [lib] diff --git a/bittensor_wallet/__init__.py b/bittensor_wallet/__init__.py index 67fcfbd..fd02421 100644 --- a/bittensor_wallet/__init__.py +++ b/bittensor_wallet/__init__.py @@ -11,4 +11,4 @@ wallet as wallet, ) -__version__ = "2.0.0-a0" +__version__ = "2.0.0-a1" diff --git a/pyproject.toml b/pyproject.toml index 50d7732..4cbaa00 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "bittensor-wallet" -version = "2.0.0-a0" +version = "2.0.0-a1" description = "" readme = "README.md" license = {file = "LICENSE"} From 2b57e4383d780aa60565f601d0513a0d709163c4 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 19:08:35 -0400 Subject: [PATCH 28/36] try ignoring tests dir --- pyproject.toml | 10 +++++++++- requirements/dev.txt | 3 ++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4cbaa00..4e6557f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,6 +49,13 @@ Repository = "https://github.com/opentensor/btwallet" requires = ["maturin>=1.0,<2.0"] build-backend = "maturin" +[tool.setuptools] +include-package-data = false + +[tool.setuptools.packages.find] +include = ["bittensor_wallet*", "src*"] +exclude = ["tests*"] + [project.optional-dependencies] dev = [ "pytest==7.2.0", @@ -69,5 +76,6 @@ dev = [ "ruff==0.4.7", "aioresponses==0.7.6", "factory-boy==3.3.0", - "maturin==1.7.0" + "maturin==1.7.0", + "py-bip39-bindings==0.1.11", ] \ No newline at end of file diff --git a/requirements/dev.txt b/requirements/dev.txt index cb791db..9fa4df0 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -16,4 +16,5 @@ httpx==0.27.0 ruff==0.4.7 aioresponses==0.7.6 factory-boy==3.3.0 -maturin==1.7.0 \ No newline at end of file +maturin==1.7.0 +py-bip39-bindings==0.1.11 \ No newline at end of file From 2fbe3986876d8501fd8d458ab4a09da230b0195e Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 19:09:12 -0400 Subject: [PATCH 29/36] bump ver --- Cargo.lock | 2 +- Cargo.toml | 2 +- bittensor_wallet/__init__.py | 2 +- pyproject.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index abad9fb..a0181be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -352,7 +352,7 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bittensor_wallet" -version = "2.0.0-a1" +version = "2.0.0-a2" dependencies = [ "ansible-vault", "base64 0.22.1", diff --git a/Cargo.toml b/Cargo.toml index 964893a..9216844 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bittensor_wallet" -version = "2.0.0-a1" +version = "2.0.0-a2" edition = "2021" [lib] diff --git a/bittensor_wallet/__init__.py b/bittensor_wallet/__init__.py index fd02421..f0d5dea 100644 --- a/bittensor_wallet/__init__.py +++ b/bittensor_wallet/__init__.py @@ -11,4 +11,4 @@ wallet as wallet, ) -__version__ = "2.0.0-a1" +__version__ = "2.0.0-a2" diff --git a/pyproject.toml b/pyproject.toml index 4e6557f..4cbfa48 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "bittensor-wallet" -version = "2.0.0-a1" +version = "2.0.0-a2" description = "" readme = "README.md" license = {file = "LICENSE"} From 617c91b48e65e09817ccb6627a2f2a82de9e9ed8 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 19:11:38 -0400 Subject: [PATCH 30/36] try with maturin config --- pyproject.toml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4cbfa48..1a82ccb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,10 +49,7 @@ Repository = "https://github.com/opentensor/btwallet" requires = ["maturin>=1.0,<2.0"] build-backend = "maturin" -[tool.setuptools] -include-package-data = false - -[tool.setuptools.packages.find] +[tool.maturin] include = ["bittensor_wallet*", "src*"] exclude = ["tests*"] From c728cd792a9bae86d50aff720f3e0230dd8c22c8 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 19:23:50 -0400 Subject: [PATCH 31/36] add bip39 --- pyproject.toml | 3 ++- requirements/prod.txt | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1a82ccb..d31ee0a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,8 @@ dependencies = [ "ansible~=6.7", "ansible_vault~=2.1", "munch~=2.5.0", - "rich" + "rich", + "py-bip39-bindings==0.1.11" ] requires-python = ">= 3.9" diff --git a/requirements/prod.txt b/requirements/prod.txt index d73c9be..7355510 100644 --- a/requirements/prod.txt +++ b/requirements/prod.txt @@ -1,3 +1,4 @@ +py-bip39-bindings==0.1.11 termcolor eth-utils<2.3.0 substrate-interface~=1.7.9 From af8d3ee808cc2ec2b5a1e886ac52c3bfac70fc69 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 19:23:54 -0400 Subject: [PATCH 32/36] bump ver --- Cargo.lock | 2 +- Cargo.toml | 2 +- bittensor_wallet/__init__.py | 2 +- pyproject.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a0181be..34c7fe2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -352,7 +352,7 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bittensor_wallet" -version = "2.0.0-a2" +version = "2.0.0-a3" dependencies = [ "ansible-vault", "base64 0.22.1", diff --git a/Cargo.toml b/Cargo.toml index 9216844..7b5930a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bittensor_wallet" -version = "2.0.0-a2" +version = "2.0.0-a3" edition = "2021" [lib] diff --git a/bittensor_wallet/__init__.py b/bittensor_wallet/__init__.py index f0d5dea..b65377c 100644 --- a/bittensor_wallet/__init__.py +++ b/bittensor_wallet/__init__.py @@ -11,4 +11,4 @@ wallet as wallet, ) -__version__ = "2.0.0-a2" +__version__ = "2.0.0-a3" diff --git a/pyproject.toml b/pyproject.toml index d31ee0a..ace50f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "bittensor-wallet" -version = "2.0.0-a2" +version = "2.0.0-a3" description = "" readme = "README.md" license = {file = "LICENSE"} From 13c21ae99ef3338c3d69468d0097a453326c0134 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 19:26:08 -0400 Subject: [PATCH 33/36] test with more platforms --- .github/workflows/release.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9cc1c6f..897a9d0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,16 +25,16 @@ jobs: platform: - runner: ubuntu-latest target: x86_64 - # - runner: ubuntu-latest - # target: x86 - # - runner: ubuntu-latest - # target: aarch64 - # - runner: ubuntu-latest - # target: armv7 - # - runner: ubuntu-latest - # target: s390x - # - runner: ubuntu-latest - # target: ppc64le + - runner: ubuntu-latest + target: x86 + - runner: ubuntu-latest + target: aarch64 + - runner: ubuntu-latest + target: armv7 + - runner: ubuntu-latest + target: s390x + - runner: ubuntu-latest + target: ppc64le steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 From 24d19bae774a762d41940570e9b69c913bc5c230 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 19:27:04 -0400 Subject: [PATCH 34/36] bump ver --- Cargo.lock | 2 +- Cargo.toml | 2 +- bittensor_wallet/__init__.py | 2 +- pyproject.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 34c7fe2..28701f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -352,7 +352,7 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bittensor_wallet" -version = "2.0.0-a3" +version = "2.0.0-a4" dependencies = [ "ansible-vault", "base64 0.22.1", diff --git a/Cargo.toml b/Cargo.toml index 7b5930a..9ba81cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bittensor_wallet" -version = "2.0.0-a3" +version = "2.0.0-a4" edition = "2021" [lib] diff --git a/bittensor_wallet/__init__.py b/bittensor_wallet/__init__.py index b65377c..3a4bb1a 100644 --- a/bittensor_wallet/__init__.py +++ b/bittensor_wallet/__init__.py @@ -11,4 +11,4 @@ wallet as wallet, ) -__version__ = "2.0.0-a3" +__version__ = "2.0.0-a4" diff --git a/pyproject.toml b/pyproject.toml index ace50f5..b80b757 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "bittensor-wallet" -version = "2.0.0-a3" +version = "2.0.0-a4" description = "" readme = "README.md" license = {file = "LICENSE"} From eff1a7c22cab8f10ff5f20a045d7e3ce13cf54e3 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 19:30:58 -0400 Subject: [PATCH 35/36] remove other platforms --- .github/workflows/release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 897a9d0..bebb0a1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,10 +31,10 @@ jobs: target: aarch64 - runner: ubuntu-latest target: armv7 - - runner: ubuntu-latest - target: s390x - - runner: ubuntu-latest - target: ppc64le + # - runner: ubuntu-latest + # target: s390x + # - runner: ubuntu-latest + # target: ppc64le steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 From 9f2d3b78a87f2d4e61c7818f2993ecb382181c8c Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 3 Oct 2024 19:33:33 -0400 Subject: [PATCH 36/36] comment out other platforms --- .github/workflows/release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bebb0a1..c3321af 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,10 +27,10 @@ jobs: target: x86_64 - runner: ubuntu-latest target: x86 - - runner: ubuntu-latest - target: aarch64 - - runner: ubuntu-latest - target: armv7 + # - runner: ubuntu-latest + # target: aarch64 + # - runner: ubuntu-latest + # target: armv7 # - runner: ubuntu-latest # target: s390x # - runner: ubuntu-latest