diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml
index 0ddd342..b247009 100644
--- a/.github/workflows/pythonpackage.yml
+++ b/.github/workflows/pythonpackage.yml
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: [3.6, 3.7, 3.8, 3.9]
+ python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
steps:
- uses: actions/checkout@master
@@ -25,6 +25,6 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- pip install -e .[tests]
+ pip install -e .[cli,tests,docs]
- name: Test with pytest
run: pytest
diff --git a/.travis.yml b/.travis.yml
index 32023d4..9c4e24b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,11 +1,9 @@
language: python
-dist: xenial
-sudo: true
+dist: bionic
+before_install:
+ - python -m pip install --upgrade pip
install:
- - sudo add-apt-repository -y ppa:deadsnakes/ppa
- - sudo apt-get update
- - sudo apt-get install -y python$TRAVIS_PYTHON_VERSION-dev
- - pip install -e .[tests,docs] tox-travis coveralls
+ - pip install -e .[cli,tests,docs] tox-travis coveralls
matrix:
include:
- name: "3.6"
@@ -16,5 +14,7 @@ matrix:
python: 3.8
- name: "3.9"
python: 3.9
-script: pytest
-after_success: if [ "${TRAVIS_PYTHON_VERSION}" == "3.9" ]; then coveralls; fi;
\ No newline at end of file
+ - name: "3.10"
+ python: 3.10
+script: tox
+after_success: if [ "${TRAVIS_PYTHON_VERSION}" == "3.10" ]; then coveralls; fi;
\ No newline at end of file
diff --git a/README.md b/README.md
index 406156c..b430564 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,16 @@
-
# Hierarchical Deterministic Wallet
-[![Build Status](https://travis-ci.org/meherett/python-hdwallet.svg?branch=master)](https://travis-ci.org/meherett/python-hdwallet?branch=master)
+[![Build Status](https://app.travis-ci.com/meherett/python-hdwallet.svg?branch=master)](https://app.travis-ci.com/meherett/python-hdwallet)
[![PyPI Version](https://img.shields.io/pypi/v/hdwallet.svg?color=blue)](https://pypi.org/project/hdwallet)
-[![Documentation Status](https://readthedocs.org/projects/hdwallet/badge/?version=master)](https://hdwallet.readthedocs.io/en/master/?badge=master)
+[![Documentation Status](https://readthedocs.org/projects/hdwallet/badge/?version=master)](https://hdwallet.readthedocs.io)
+[![PyPI License](https://img.shields.io/pypi/l/hdwallet?color=black)](https://pypi.org/project/hdwallet)
[![PyPI Python Version](https://img.shields.io/pypi/pyversions/hdwallet.svg)](https://pypi.org/project/hdwallet)
-[![Coverage Status](https://coveralls.io/repos/github/meherett/python-hdwallet/badge.svg?branch=master)](https://coveralls.io/github/meherett/python-hdwallet?branch=master)
+[![Coverage Status](https://coveralls.io/repos/github/meherett/python-hdwallet/badge.svg?branch=master)](https://coveralls.io/github/meherett/python-hdwallet)
Python-based library for the implementation of a hierarchical deterministic wallet generator for more than 140+ multiple cryptocurrencies.
-It allows the handling of multiple coins, multiple accounts, external and internal chains per account and millions of addresses per the chain.
+It allows the handling of multiple coins, multiple accounts, external and internal chains per account and millions of addresses per chain.
+
+
For more info see the BIP specs.
@@ -19,17 +21,23 @@ For more info see the BIP specs.
| [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) | Hierarchical Deterministic Wallets |
| [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) | Multi-Account Hierarchy for Deterministic Wallets |
| [BIP49](https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki) | Derivation scheme for P2WPKH-nested-in-P2SH based accounts |
-| [BIP84](https://github.com/bitcoin/bips/blob/master/bip-0048.mediawiki) | Derivation scheme for P2WPKH based accounts |
-| [BIP141](https://github.com/bitcoin/bips/blob/master/bip-0014.mediawiki) | Segregated Witness (Consensus layer) |
+| [BIP84](https://github.com/bitcoin/bips/blob/master/bip-0084.mediawiki) | Derivation scheme for P2WPKH based accounts |
+| [BIP141](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki) | Segregated Witness (Consensus layer) |
## Installation
-PIP to install `hdwallet` globally, for Linux `sudo` may be required:
+The easiest way to install `hdwallet` is via pip:
```
pip install hdwallet
```
+To install `hdwallet` command line interface globally, for Linux `sudo` may be required:
+
+```
+pip install hdwallet[cli]
+```
+
If you want to run the latest version of the code, you can install from the git:
```
@@ -40,14 +48,14 @@ For the versions available, see the [tags on this repository](https://github.com
## Quick Usage
-Simple Bitcoin mainnet hierarchical deterministic wallet generator:
+Simple Bitcoin mainnet HDWallet generator:
```python
#!/usr/bin/env python3
from hdwallet import HDWallet
from hdwallet.utils import generate_entropy
-from hdwallet.symbols import BTC
+from hdwallet.symbols import BTC as SYMBOL
from typing import Optional
import json
@@ -62,7 +70,7 @@ ENTROPY: str = generate_entropy(strength=STRENGTH)
PASSPHRASE: Optional[str] = None # "meherett"
# Initialize Bitcoin mainnet HDWallet
-hdwallet: HDWallet = HDWallet(symbol=BTC)
+hdwallet: HDWallet = HDWallet(symbol=SYMBOL, use_default_path=False)
# Get Bitcoin HDWallet from entropy
hdwallet.from_entropy(
entropy=ENTROPY, language=LANGUAGE, passphrase=PASSPHRASE
@@ -168,7 +176,7 @@ for address_index in range(10):
```shell script
Mnemonic: bright demand olive glance crater key head glory quantum leisure intact age
-Base HD Path: m/44'/60'/0'/0/{address_index}
+Base HD Path: m/44'/60'/0'/0/{address_index}
(0) m/44'/60'/0'/0/0 0x3a149f0c5dc5c0F1E29e573215C23710dE9c4f87 0xa45f9af43912fdd5e88c492226be082029f257681d4b3e73b68be535d2fb0526
(1) m/44'/60'/0'/0/1 0x9e8A4fD9bA74DbB0c7F465EF56b47489793AA102 0x6e5ab2a3ae20c7b3a1c0645b03689e88e8cdff16f6a39d6a420bfebc20e8a941
@@ -185,18 +193,37 @@ Base HD Path: m/44'/60'/0'/0/{address_index}
[Click this to see more examples :)](https://github.com/meherett/python-hdwallet/blob/master/examples)
+## Development
+
+To get started, just fork this repo, clone it locally, and run:
+
+```
+pip install -e .[cli,tests,docs] -r requirements.txt
+```
+
+## Testing
+
+You can run the tests with:
+
+```
+pytest
+```
+
+Or use `tox` to run the complete suite against the full set of build targets, or pytest to run specific
+tests against a specific version of Python.
+
## Contributing
-Feel free to open an [issue](https://github.com/meherett/hdwallet/issues) if you find a problem,
-or a pull request if you've solved an issue. And also any help in testing, development,
-documentation and other tasks is highly appreciated and useful to the project.
+Feel free to open an [issue](https://github.com/meherett/hdwallet/issues) if you find a problem,
+or a pull request if you've solved an issue. And also any help in testing, development,
+documentation and other tasks is highly appreciated and useful to the project.
There are tasks for contributors of all experience levels.
For more information, see the [CONTRIBUTING.md](https://github.com/meherett/hdwallet/blob/master/CONTRIBUTING.md) file.
## Available Cryptocurrencies
-This library simplifies the process of creating a new HDWallet's for:
+This library simplifies the process of creating a new hierarchical deterministic wallets for:
| Cryptocurrencies | Symbols | Mainnet | Testnet | Segwit | Coin Type | Default Paths |
| :---------------------------------------------------------------- | :------------------: | :-----: | :-----: | :----: | :-------: | :------------------: |
@@ -323,6 +350,7 @@ This library simplifies the process of creating a new HDWallet's for:
| Syscoin | `SYS` | Yes | No | Yes | 57 | `m/44'/57'/0'/0/0` |
| TOA Coin | `TOA` | Yes | No | No | 159 | `m/44'/159'/0'/0/0` |
| Thought AI | `THT` | Yes | No | No | 502 | `m/44'/502'/0'/0/0` |
+| [Tron](https://github.com/tronprotocol/java-tron) | `TRX` | Yes | No | No | 195 | `m/44'/195'/0'/0/0` |
| Twins | `TWINS`, `TWINSTEST` | Yes | Yes | No | 970 | `m/44'/970'/0'/0/0` |
| Ultimate Secure Cash | `USC` | Yes | No | No | 112 | `m/44'/112'/0'/0/0` |
| Unobtanium | `UNO` | Yes | No | No | 92 | `m/44'/92'/0'/0/0` |
@@ -337,24 +365,17 @@ This library simplifies the process of creating a new HDWallet's for:
| XUEZ | `XUEZ` | Yes | No | No | 225 | `m/44'/225'/0'/0/0` |
| [XinFin](https://github.com/XinFinOrg/XDPoSChain) | `XDC` | Yes | No | Yes | 550 | `m/44'/550'/0'/0/0` |
| ZClassic | `ZCL` | Yes | No | No | 147 | `m/44'/147'/0'/0/0` |
-| Zcash | `ZEC` | Yes | No | No | 133 | `m/44'/133'/0'/0/0` |
+| [Zcash](https://github.com/zcash/zcash) | `ZEC`, `ZECTEST` | Yes | Yes | No | 133 | `m/44'/133'/0'/0/0` |
| Zencash | `ZEN` | Yes | No | No | 121 | `m/44'/121'/0'/0/0` |
## Donations
-If You found this tool helpful consider making a donation:
+If You found this tool helpful consider making a donation:
-Ethereum (ETH) or Tether (USDT-ERC20) address:
-
-```text
-0x342798bbe9731a91e0557fa8ab0bce1eae6d6ae3
-```
-
-Bitcoin (BTC) address:
-
-```text
-3GGNPvgbSpMHShcaZJGDXQn5wUJyTz7uoC
-```
+| Coins | Addresses |
+| ----------------------------- | :----------------------------------------: |
+| Bitcoin `BTC` | 3GGNPvgbSpMHShcaZJGDXQn5wUJyTz7uoC |
+| Ethereum `ETH`, Tether `USDT` | 0x342798bbe9731a91e0557fa8ab0bce1eae6d6ae3 |
## License
diff --git a/docs/cli.rst b/docs/cli.rst
new file mode 100644
index 0000000..85f2c08
--- /dev/null
+++ b/docs/cli.rst
@@ -0,0 +1,10 @@
+============================
+Command Line Interface (CLI)
+============================
+
+.. image:: static/gif/hdwallet.gif
+ :alt: HDWallet CLI
+
+.. click:: hdwallet.cli.__main__:main
+ :prog: hdwallet
+ :show-nested:
\ No newline at end of file
diff --git a/docs/conf.py b/docs/conf.py
index 187c00e..bed7e1f 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -14,6 +14,9 @@
import sys
import datetime
+from hdwallet import (
+ __version__, __author__
+)
sys.path.insert(0, os.path.abspath("../.."))
sys.path.insert(1, os.path.abspath("./extensions"))
@@ -22,11 +25,11 @@
# -- Project information -----------------------------------------------------
project = "HDWallet"
-copyright = f"{datetime.datetime.now().year}, Meheret Tesfaye Batu"
-author = "Meheret Tesfaye"
+copyright = f"{datetime.datetime.now().year}, {__author__}"
+author = __author__
# The full version, including alpha/beta/rc tags
-release = "1.3.0"
+release = __version__
# The master toctree document.
master_doc = "toctree"
@@ -37,7 +40,8 @@
# extensions coming with Sphinx (named "sphinx.ext.*") or your custom
# ones.
extensions = [
- "sphinx.ext.autodoc"
+ "sphinx.ext.autodoc",
+ "sphinx_click.ext"
]
# Add any paths that contain templates here, relative to this directory.
@@ -52,15 +56,15 @@
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
-html_theme = "sphinx_rtd_theme"
+html_theme = "furo"
# Product logo name
# html_logo = "static/png/hdwallet.png"
# Theme options
html_theme_options = {
# "canonical_url": "",
# "analytics_id": "UA-XXXXXXX-1", # Provided by Google in your dashboard
- "logo_only": False,
- "display_version": True,
+ # "logo_only": False,
+ # "display_version": True,
# "prev_next_buttons_location": "bottom",
# "style_external_links": False,
# "vcs_pageview_mode": "",
@@ -71,12 +75,26 @@
# "navigation_depth": 4,
# "includehidden": True,
# "titles_only": False
+
+ # "light_css_variables": {
+ # "color-brand-primary": "darkblue",
+ # "color-brand-content": "darkblue",
+ # "color-admonition-background": "black",
+ # },
+ # "dark_css_variables": {
+ # "color-brand-primary": "green",
+ # "color-brand-content": "green",
+ # "color-admonition-background": "white",
+ # },
+ # "sidebar_hide_name": True,
+ # "navigation_with_keys": True,
+ # "announcement": "Important announcement!",
}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ["static", "static/css"]
+html_static_path = ["static", "static/css", "static/gif"]
# Autodoc member order
autodoc_member_order = "bysource"
diff --git a/docs/cryptocurrencies.rst b/docs/cryptocurrencies.rst
index 8165035..ad7105f 100644
--- a/docs/cryptocurrencies.rst
+++ b/docs/cryptocurrencies.rst
@@ -1,11 +1,15 @@
:orphan:
-==========================
-Available Cryptocurrencies
-==========================
+================
+Cryptocurrencies
+================
This library simplifies the process of generating a new HDWallet's for:
+.. note::
+
+ All Cryptocurrencies testnet networks default paths are set to ``m/44'/1'/0'/0/0`` value.
+
.. list-table::
:widths: 25 25 25 25 15 25 50
:header-rows: 1
@@ -878,6 +882,13 @@ This library simplifies the process of generating a new HDWallet's for:
- No
- 502
- m/44'/502'/0'/0/0
+ * - `Tron `_
+ - TRX
+ - Yes
+ - No
+ - No
+ - 195
+ - m/44'/195'/0'/0/0
* - Twins
- TWINS, TWINSTEST
- Yes
@@ -977,9 +988,9 @@ This library simplifies the process of generating a new HDWallet's for:
- 147
- m/44'/147'/0'/0/0
* - Zcash
- - ZEC
+ - ZEC, ZECTEST
+ - Yes
- Yes
- - No
- No
- 133
- m/44'/133'/0'/0/0
@@ -990,5 +1001,3 @@ This library simplifies the process of generating a new HDWallet's for:
- No
- 121
- m/44'/121'/0'/0/0
-
-**NOTICE:** All Cryptocurrencies testnet networks default paths are set to **`m/44'/1'/0'/0/0`** value.
diff --git a/docs/index.rst b/docs/index.rst
index b59b75c..0a06737 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -2,14 +2,119 @@
Hierarchical Deterministic Wallet
=================================
+|Build Status| |PyPI Version| |Documentation Status| |PyPI License| |PyPI Python Version| |Coverage Status|
+
+.. |Build Status| image:: https://travis-ci.org/meherett/python-hdwallet.svg?branch=master
+ :target: https://travis-ci.org/meherett/python-hdwallet?branch=master
+
+.. |PyPI Version| image:: https://img.shields.io/pypi/v/hdwallet.svg?color=blue
+ :target: https://pypi.org/project/hdwallet
+
+.. |Documentation Status| image:: https://readthedocs.org/projects/hdwallet/badge/?version=master
+ :target: https://hdwallet.readthedocs.io/en/master/?badge=master
+
+.. |PyPI License| image:: https://img.shields.io/pypi/l/hdwallet?color=black
+ :target: https://pypi.org/project/hdwallet
+
+.. |PyPI Python Version| image:: https://img.shields.io/pypi/pyversions/hdwallet.svg
+ :target: https://pypi.org/project/hdwallet
+
+.. |Coverage Status| image:: https://coveralls.io/repos/github/meherett/python-hdwallet/badge.svg?branch=master
+ :target: https://coveralls.io/github/meherett/python-hdwallet?branch=master
+
Python-based library for the implementation of a hierarchical deterministic wallet generator for over 140+ multiple cryptocurrencies.
It allows the handling of multiple coins, multiple accounts, external and internal chains per account and millions of addresses per the chain.
+Simple Bitcoin mainnet HDWallet generator:
+
+::
+
+ #!/usr/bin/env python3
+
+ from hdwallet import HDWallet
+ from hdwallet.utils import generate_entropy
+ from hdwallet.symbols import BTC as SYMBOL
+ from typing import Optional
+
+ import json
+
+ # Choose strength 128, 160, 192, 224 or 256
+ STRENGTH: int = 160 # Default is 128
+ # Choose language english, french, italian, spanish, chinese_simplified, chinese_traditional, japanese or korean
+ LANGUAGE: str = "korean" # Default is english
+ # Generate new entropy hex string
+ ENTROPY: str = generate_entropy(strength=STRENGTH)
+ # Secret passphrase for mnemonic
+ PASSPHRASE: Optional[str] = None # "meherett"
+
+ # Initialize Bitcoin mainnet HDWallet
+ hdwallet: HDWallet = HDWallet(symbol=SYMBOL, use_default_path=False)
+ # Get Bitcoin HDWallet from entropy
+ hdwallet.from_entropy(
+ entropy=ENTROPY, language=LANGUAGE, passphrase=PASSPHRASE
+ )
+
+ # Derivation from path
+ # hdwallet.from_path("m/44'/0'/0'/0/0")
+ # Or derivation from index
+ hdwallet.from_index(44, hardened=True)
+ hdwallet.from_index(0, hardened=True)
+ hdwallet.from_index(0, hardened=True)
+ hdwallet.from_index(0)
+ hdwallet.from_index(0)
+
+ # Print all Bitcoin HDWallet information's
+ print(json.dumps(hdwallet.dumps(), indent=4, ensure_ascii=False))
+
+.. raw:: html
+
+
+ Output
+
+.. code-block:: python
+
+ {
+ "cryptocurrency": "Bitcoin",
+ "symbol": "BTC",
+ "network": "mainnet",
+ "strength": 160,
+ "entropy": "c5b0d0ee698f3f72b6265f1bc591f8f2d7afa6dd",
+ "mnemonic": "주일 액수 명단 천둥 해수욕장 전망 추천 직업 그룹 단위 신체 파란색 시청 천천히 스트레스",
+ "language": "korean",
+ "passphrase": null,
+ "seed": "5a9b9667ccd07b3c641b1ba95e9119dd1d5a3034fd46cd2f27fc1f160c7dcd824fc0ab4710a9ae90582dffc3b0803bcbc0a8160feeaab4c70511c5035859decf",
+ "root_xprivate_key": "xprv9s21ZrQH143K2qMHU8aghJ4MoQR5g5mowXbeP2vCP937bseZGX929dmJudL7u4xRxtKvh58pxz1PhtCbWW2yUH14jdduKVMV9FkBMpM2Hyw",
+ "root_xpublic_key": "xpub661MyMwAqRbcFKRkaA7h4S16MSFa5YVfJkXFBRKowUa6Ufyhp4TGhS5nkvkLXSmdNjoszzDkU26WW2rg1zBsQBt6Pv3T8oLEAExGHD3hcQs",
+ "xprivate_key": "xprvA2YyMZWyPK2xo4eZgyypp2CzcHnxNzGbruGg7vmgaAVCtBtrjwzuhXJBNM3FrwBh85ajxHErNR6ByN77WJARpC1HDC7kTwa2yr7Mu9Pz5Qq",
+ "xpublic_key": "xpub6FYKm53sDgbG1Yj2o1WqBA9jAKdSnSzTE8CGvKBJ8W2BkzE1HVKAFKcfDcCHKpL5BQRg2HjbNSt55jpFshY7W1KFtp7zjB3DhNAmiFv6kzB",
+ "uncompressed": "081016370b45d7e23bd89b07d6886036f5e4df9a129eee3b488c177ba7881856e24d337b280f9d32539a22445e567543b39b708edf5289442f36dcde958a3433",
+ "compressed": "03081016370b45d7e23bd89b07d6886036f5e4df9a129eee3b488c177ba7881856",
+ "chain_code": "cf9ee427ed8073e009a5743056e8cf19167f67ca5082c2c6635b391e9a4e0b0d",
+ "private_key": "f79495fda777197ce73551bcd8e162ceca19167575760d3cc2bced4bf2a213dc",
+ "public_key": "03081016370b45d7e23bd89b07d6886036f5e4df9a129eee3b488c177ba7881856",
+ "wif": "L5WyVfBu8Sz3iGZtrwJVSP2wDJmu7HThGd1EGekFBnviWgzLXpJd",
+ "finger_print": "ac13e305",
+ "semantic": "p2pkh",
+ "path": "m/44'/0'/0'/0/0",
+ "hash": "ac13e305a88bd9968f1c058fcf5d9a6b1b9ef484",
+ "addresses": {
+ "p2pkh": "1Ggs3kkNrPPWoW17iDFQWgMdw3CD8BzBiv",
+ "p2sh": "3GQVUFePz517Hf61Vsa9H2tHj5jw5y6ngV",
+ "p2wpkh": "bc1q4sf7xpdg30vedrcuqk8u7hv6dvdeaayy3uw5cj",
+ "p2wpkh_in_p2sh": "3JyV5aSgdVYEjQodPWHfvehQ5227EDr3sN",
+ "p2wsh": "bc1qnk0s9q4379n6v9vg0lnhdu5qhjyx99u2xm238pmckmjg9v29q54saddzp9",
+ "p2wsh_in_p2sh": "3MmsEoP7GLHzuLVgkAtcRtyXLTWh8zNAcd"
+ }
+ }
+
+.. raw:: html
+
+
For more info see the BIP specs.
.. list-table::
- :widths: 15 80
+ :widths: 10 185
:header-rows: 1
* - BIP's
@@ -26,5 +131,5 @@ For more info see the BIP specs.
- Derivation scheme for P2WPKH-nested-in-P2SH based accounts
* - `BIP84 `_
- Derivation scheme for P2WPKH based accounts
- * - `BIP141 `_
+ * - `BIP141 `_
- Segregated Witness (Consensus layer)
diff --git a/docs/install.rst b/docs/install.rst
index 0a54b6d..e23b637 100644
--- a/docs/install.rst
+++ b/docs/install.rst
@@ -2,13 +2,36 @@
Installing HDWallet
===================
-The easiest way to install HDWallet is via pip:
+The easiest way to install ``hdwallet`` is via pip:
::
$ pip install hdwallet
+To install ``hdwallet`` command line interface globally, for Linux `sudo` may be required:
+
+::
+
+ $ pip install hdwallet[cli]
+
+
+After you have installed, type ``hdwallet`` to verify that it worked:
+
+::
+
+ $ hdwallet
+ Usage: hdwallet [OPTIONS] COMMAND [ARGS]...
+
+ Options:
+ -v, --version Show HDWallet version and exit.
+ -h, --help Show this message and exit.
+
+ Commands:
+ generate (g) Select Generate for HDWallet.
+ list (l) Select List for HDWallet information.
+
+
If you want to run the latest version of the code, you can install from git:
::
@@ -25,4 +48,4 @@ We welcome pull requests. To get started, just fork this `github repository
install.rst
- Cryptocurrencies
+ cli.rst
+ Available Cryptocurrencies
.. toctree::
:maxdepth: 3
diff --git a/examples/from_entropy.py b/examples/from_entropy.py
index 3b83069..66d7817 100644
--- a/examples/from_entropy.py
+++ b/examples/from_entropy.py
@@ -2,7 +2,7 @@
from hdwallet import HDWallet
from hdwallet.utils import generate_entropy
-from hdwallet.symbols import BTC
+from hdwallet.symbols import TRX as SYMBOL
from typing import Optional
import json
@@ -17,7 +17,7 @@
PASSPHRASE: Optional[str] = None # "meherett"
# Initialize Bitcoin mainnet HDWallet
-hdwallet: HDWallet = HDWallet(symbol=BTC)
+hdwallet: HDWallet = HDWallet(symbol=SYMBOL)
# Get Bitcoin HDWallet from entropy
hdwallet.from_entropy(
entropy=ENTROPY, language=LANGUAGE, passphrase=PASSPHRASE
diff --git a/examples/from_private_key.py b/examples/from_private_key.py
index 05f5480..421b7eb 100644
--- a/examples/from_private_key.py
+++ b/examples/from_private_key.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
from hdwallet import HDWallet
-from hdwallet.symbols import QTUM
+from hdwallet.symbols import QTUM as SYMBOL
import json
@@ -9,7 +9,7 @@
PRIVATE_KEY: str = "f86d5afe2a457c29357485ebf853a1e5ff5f6fcf1ba4d7d1412665e01449902e"
# Initialize Qtum mainnet HDWallet
-hdwallet: HDWallet = HDWallet(symbol=QTUM)
+hdwallet: HDWallet = HDWallet(symbol=SYMBOL)
# Get Qtum HDWallet from private key
hdwallet.from_private_key(private_key=PRIVATE_KEY)
@@ -25,8 +25,6 @@
print("Public Key:", hdwallet.public_key())
print("Wallet Important Format:", hdwallet.wif())
print("Finger Print:", hdwallet.finger_print())
-print("Semantic:", hdwallet.semantic())
-print("Path:", hdwallet.path())
print("Hash:", hdwallet.hash())
print("P2PKH Address:", hdwallet.p2pkh_address())
print("P2SH Address:", hdwallet.p2sh_address())
diff --git a/examples/from_public_key.py b/examples/from_public_key.py
index 2aee74d..cf76dc6 100644
--- a/examples/from_public_key.py
+++ b/examples/from_public_key.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
from hdwallet import HDWallet
-from hdwallet.symbols import ETH
+from hdwallet.symbols import ETH as SYMBOL
import json
@@ -9,7 +9,7 @@
PUBLIC_KEY = "034f6922d19e8134de23eb98396921c02cdcf67e8c0ff23dfd955839cd557afd10"
# Initialize Ethereum mainnet HDWallet
-hdwallet: HDWallet = HDWallet(symbol=ETH)
+hdwallet: HDWallet = HDWallet(symbol=SYMBOL)
# Get Ethereum HDWallet from public key
hdwallet.from_public_key(public_key=PUBLIC_KEY)
@@ -23,7 +23,6 @@
print("Compressed:", hdwallet.compressed())
print("Public Key:", hdwallet.public_key())
print("Finger Print:", hdwallet.finger_print())
-print("Semantic:", hdwallet.semantic())
print("Hash:", hdwallet.hash())
print("P2PKH Address:", hdwallet.p2pkh_address())
print("P2SH Address:", hdwallet.p2sh_address())
diff --git a/examples/from_root_xprivate_key.py b/examples/from_root_xprivate_key.py
deleted file mode 100644
index dc6258f..0000000
--- a/examples/from_root_xprivate_key.py
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/usr/bin/env python3
-
-from hdwallet import HDWallet as HDWallet
-from hdwallet.utils import is_root_xprivate_key
-from hdwallet.symbols import BTC
-
-import json
-
-# Strict for root xpublic key
-STRICT: bool = True
-# Bitcoin root xprivate key
-XPRIVATE_KEY: str = "xprv9s21ZrQH143K24t96gCaezzt1QQmnqiEGm8m6TP8yb8e3TmGfkCgcLEVss" \
- "kufMW9R4KH27pD1kyyEfJkYz1eiPwjhFzB4gtabH3PzMSmXSM"
-# Bitcoin not root xprivate key
-# XPRIVATE_KEY: str = "xprvA3KRgVDh45mbQT1VmWPx73YeAWM4629Q2D9pMuqjFMnjTqDGhKiww6H532rg" \
-# "YRNj37fngd4Mvp7GfUD8rKeQzUZjCWeisT92tX8FfjWx3BL"
-
-if STRICT:
- # Check root xprivate key
- assert is_root_xprivate_key(xprivate_key=XPRIVATE_KEY, symbol=BTC, semantic="p2pkh"), "Invalid root xprivate key."
-
-# Initialize Bitcoin mainnet HDWallet
-hdwallet: HDWallet = HDWallet(symbol=BTC)
-# Get Bitcoin HDWallet from root xprivate key
-hdwallet.from_root_xprivate_key(xprivate_key=XPRIVATE_KEY, strict=STRICT)
-
-# Derivation from path
-# hdwallet.from_path("m/44'/0'/0'/0/0")
-# Or derivation from index
-hdwallet.from_index(44, hardened=True)
-hdwallet.from_index(0, hardened=True)
-hdwallet.from_index(0, hardened=True)
-hdwallet.from_index(0)
-hdwallet.from_index(0)
-
-# Print all Bitcoin HDWallet information's
-# print(json.dumps(hdwallet.dumps(), indent=4, ensure_ascii=False))
-
-print("Cryptocurrency:", hdwallet.cryptocurrency())
-print("Symbol:", hdwallet.symbol())
-print("Network:", hdwallet.network())
-print("Root XPrivate Key:", hdwallet.root_xprivate_key())
-print("Root XPublic Key:", hdwallet.root_xpublic_key())
-print("XPrivate Key:", hdwallet.xprivate_key())
-print("XPublic Key:", hdwallet.xpublic_key())
-print("Uncompressed:", hdwallet.uncompressed())
-print("Compressed:", hdwallet.compressed())
-print("Chain Code:", hdwallet.chain_code())
-print("Private Key:", hdwallet.private_key())
-print("Public Key:", hdwallet.public_key())
-print("Wallet Important Format:", hdwallet.wif())
-print("Finger Print:", hdwallet.finger_print())
-print("Semantic:", hdwallet.semantic())
-print("Path:", hdwallet.path())
-print("Hash:", hdwallet.hash())
-print("P2PKH Address:", hdwallet.p2pkh_address())
-print("P2SH Address:", hdwallet.p2sh_address())
-print("P2WPKH Address:", hdwallet.p2wpkh_address())
-print("P2WPKH In P2SH Address:", hdwallet.p2wpkh_in_p2sh_address())
-print("P2WSH Address:", hdwallet.p2wsh_address())
-print("P2WSH In P2SH Address:", hdwallet.p2wsh_in_p2sh_address())
diff --git a/examples/from_root_xpublic_key.py b/examples/from_root_xpublic_key.py
deleted file mode 100644
index ad2ba34..0000000
--- a/examples/from_root_xpublic_key.py
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/usr/bin/env python3
-
-from hdwallet import HDWallet as HDWallet
-from hdwallet.utils import is_root_xpublic_key
-from hdwallet.symbols import BTC
-
-import json
-
-# Strict for root xpublic key
-STRICT: bool = True
-# Bitcoin root xpublic key
-XPUBLIC_KEY: str = "xpub661MyMwAqRbcEqD3v24ZWHGDMqqAfbDbmnUFJXfbpxGZaAshq7evA7fB75CHFbNHSot" \
- "LadDZw6M6ic4ZkdN6jQ2KMGR66Z2EybgdLFjNrpf"
-# Bitcoin not root xpublic key
-# XPUBLIC_KEY: str = "xpub6FbWJtnc3eJHBwfTqhaE9yQNkmi56UDy9Rm1pbhvuSSigr6xKihuFpnnf4jz8G9ba2m3wFaF" \
-# "Gj7eH7FE451Jo5hPJhbaCdmxoBwWbFzk1Sn"
-
-if STRICT:
- # Check root xpublic key
- assert is_root_xpublic_key(xpublic_key=XPUBLIC_KEY, symbol=BTC, semantic="p2pkh"), "Invalid root xpublic key."
-
-# Initialize Bitcoin mainnet HDWallet
-hdwallet: HDWallet = HDWallet(symbol=BTC)
-# Get Bitcoin HDWallet from root xpublic key
-hdwallet.from_root_xpublic_key(xpublic_key=XPUBLIC_KEY, strict=STRICT)
-
-# Derivation from path
-# hdwallet.from_path("m/44/0/0/0/0")
-# Or derivation from index
-hdwallet.from_index(44, hardened=False)
-hdwallet.from_index(0, hardened=False)
-hdwallet.from_index(0, hardened=False)
-hdwallet.from_index(0)
-hdwallet.from_index(0)
-
-# Print all Bitcoin HDWallet information's
-# print(json.dumps(hdwallet.dumps(), indent=4, ensure_ascii=False))
-
-print("Cryptocurrency:", hdwallet.cryptocurrency())
-print("Symbol:", hdwallet.symbol())
-print("Network:", hdwallet.network())
-print("Root XPublic Key:", hdwallet.root_xpublic_key())
-print("XPublic Key:", hdwallet.xpublic_key())
-print("Uncompressed:", hdwallet.uncompressed())
-print("Compressed:", hdwallet.compressed())
-print("Chain Code:", hdwallet.chain_code())
-print("Public Key:", hdwallet.public_key())
-print("Finger Print:", hdwallet.finger_print())
-print("Semantic:", hdwallet.semantic())
-print("Path:", hdwallet.path())
-print("Hash:", hdwallet.hash())
-print("P2PKH Address:", hdwallet.p2pkh_address())
-print("P2SH Address:", hdwallet.p2sh_address())
-print("P2WPKH Address:", hdwallet.p2wpkh_address())
-print("P2WPKH In P2SH Address:", hdwallet.p2wpkh_in_p2sh_address())
-print("P2WSH Address:", hdwallet.p2wsh_address())
-print("P2WSH In P2SH Address:", hdwallet.p2wsh_in_p2sh_address())
diff --git a/examples/from_seed.py b/examples/from_seed.py
index 18766b1..9104176 100644
--- a/examples/from_seed.py
+++ b/examples/from_seed.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
from hdwallet import HDWallet
-from hdwallet.symbols import DOGE
+from hdwallet.symbols import DOGE as SYMBOL
import json
@@ -10,7 +10,7 @@
"8723bc545a4bd51f5cd29a3e8bd1433bd1d26e6bf866ff53d1493f"
# Initialize Dogecoin mainnet HDWallet
-hdwallet: HDWallet = HDWallet(symbol=DOGE)
+hdwallet: HDWallet = HDWallet(symbol=SYMBOL)
# Get Dogecoin HDWallet from seed
hdwallet.from_seed(seed=SEED)
diff --git a/examples/from_wif.py b/examples/from_wif.py
index 2c27e7b..517ffc4 100644
--- a/examples/from_wif.py
+++ b/examples/from_wif.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
from hdwallet import HDWallet
-from hdwallet.symbols import BTCTEST
+from hdwallet.symbols import BTCTEST as SYMBOL
import json
@@ -9,7 +9,7 @@
WALLET_IMPORTANT_FORMAT: str = "cVpnZ6XRfL5VVggwZDyndAU5KGVdT2TP1j1HB3td6ZKWCbh5wYvf"
# Initialize Bitcoin testnet HDWallet
-hdwallet: HDWallet = HDWallet(symbol=BTCTEST)
+hdwallet: HDWallet = HDWallet(symbol=SYMBOL)
# Get Bitcoin HDWallet from wallet important format
hdwallet.from_wif(wif=WALLET_IMPORTANT_FORMAT)
@@ -25,8 +25,6 @@
print("Public Key:", hdwallet.public_key())
print("Wallet Important Format:", hdwallet.wif())
print("Finger Print:", hdwallet.finger_print())
-print("Semantic:", hdwallet.semantic())
-print("Path:", hdwallet.path())
print("Hash:", hdwallet.hash())
print("P2PKH Address:", hdwallet.p2pkh_address())
print("P2SH Address:", hdwallet.p2sh_address())
diff --git a/examples/from_xprivate_key.py b/examples/from_xprivate_key.py
index 5eb1913..2af64e1 100644
--- a/examples/from_xprivate_key.py
+++ b/examples/from_xprivate_key.py
@@ -1,25 +1,46 @@
#!/usr/bin/env python3
-from hdwallet import HDWallet
-from hdwallet.symbols import ETH
+from hdwallet import HDWallet as HDWallet
+from hdwallet.utils import is_root_xprivate_key
+from hdwallet.symbols import BTC as SYMBOL
import json
-# Ethereum xprivate key
-XPRIVATE_KEY = "xprvA3KRgVDh45mbQT1VmWPx73YeAWM4629Q2D9pMuqjFMnjTqDGhKiww6H" \
- "532rgYRNj37fngd4Mvp7GfUD8rKeQzUZjCWeisT92tX8FfjWx3BL"
+# Strict for root xpublic key
+STRICT: bool = True
+# Bitcoin root xprivate key
+XPRIVATE_KEY: str = "xprv9s21ZrQH143K24t96gCaezzt1QQmnqiEGm8m6TP8yb8e3TmGfkCgcLEVss" \
+ "kufMW9R4KH27pD1kyyEfJkYz1eiPwjhFzB4gtabH3PzMSmXSM"
+# Bitcoin non-root xprivate key
+# XPRIVATE_KEY: str = "yprvAMZNWbcSVmxMiVoKgQuKmemTpEz8dJs3v8hmgkRVUjncqkXsgoxyqZ8rDb" \
+# "eXzMqRQZEsTcB4T5iQQx7WazLyy3KiHZrdcHo6DmGAibeMxQV"
-# Initialize Ethereum mainnet HDWallet
-hdwallet: HDWallet = HDWallet(symbol=ETH)
-# Get Ethereum HDWallet from xprivate key
-hdwallet.from_xprivate_key(xprivate_key=XPRIVATE_KEY)
+if STRICT:
+ # Check root xprivate key
+ assert is_root_xprivate_key(xprivate_key=XPRIVATE_KEY, symbol=SYMBOL), "Invalid Root XPrivate Key."
-# Print all Ethereum HDWallet information's
+# Initialize Bitcoin mainnet HDWallet
+hdwallet: HDWallet = HDWallet(symbol=SYMBOL)
+# Get Bitcoin HDWallet from xprivate key
+hdwallet.from_xprivate_key(xprivate_key=XPRIVATE_KEY, strict=STRICT)
+
+# Derivation from path
+# hdwallet.from_path("m/44'/0'/0'/0/0")
+# Or derivation from index
+hdwallet.from_index(44, hardened=True)
+hdwallet.from_index(0, hardened=True)
+hdwallet.from_index(0, hardened=True)
+hdwallet.from_index(0)
+hdwallet.from_index(0)
+
+# Print all Bitcoin HDWallet information's
# print(json.dumps(hdwallet.dumps(), indent=4, ensure_ascii=False))
print("Cryptocurrency:", hdwallet.cryptocurrency())
print("Symbol:", hdwallet.symbol())
print("Network:", hdwallet.network())
+print("Root XPrivate Key:", hdwallet.root_xprivate_key())
+print("Root XPublic Key:", hdwallet.root_xpublic_key())
print("XPrivate Key:", hdwallet.xprivate_key())
print("XPublic Key:", hdwallet.xpublic_key())
print("Uncompressed:", hdwallet.uncompressed())
diff --git a/examples/from_xpublic_key.py b/examples/from_xpublic_key.py
index c12d4e0..04e9079 100644
--- a/examples/from_xpublic_key.py
+++ b/examples/from_xpublic_key.py
@@ -1,25 +1,45 @@
#!/usr/bin/env python3
-from hdwallet import HDWallet
-from hdwallet.symbols import ETH
+from hdwallet import HDWallet as HDWallet
+from hdwallet.utils import is_root_xpublic_key
+from hdwallet.symbols import BTC as SYMBOL
import json
-# Ethereum xpublic key
-XPUBLIC_KEY = "xpub6GYVAAuBNfDxWKfSoNPR262M6uW7wWTuxE6LLRtgdBZkvmgzWFGhk41NKHydAxa" \
- "6RMZP3pY2318KG4iUfZa22nUA4q8hfrqhrDpBUJcfvWu"
+# Strict for root xpublic key
+STRICT: bool = True
+# Bitcoin root xpublic key
+XPUBLIC_KEY: str = "xpub661MyMwAqRbcEqD3v24ZWHGDMqqAfbDbmnUFJXfbpxGZaAshq7evA7fB75CHFbNHSot" \
+ "LadDZw6M6ic4ZkdN6jQ2KMGR66Z2EybgdLFjNrpf"
+# Bitcoin non-root xpublic key
+# XPUBLIC_KEY: str = "zpub6uxKjJ8pnanQKU2betFrDPVmcVUvVgyAhgWS74iaN7yUE8RADoRRnztyVEQtnzi9Fh1Vp" \
+# "6iJ8RT6mMqjGnS6AxGjud3P2DLzpMHUw2zT1n2"
-# Initialize Ethereum mainnet HDWallet
-hdwallet: HDWallet = HDWallet(symbol=ETH)
-# Get Ethereum HDWallet from xpublic key
-hdwallet.from_xpublic_key(xpublic_key=XPUBLIC_KEY)
+if STRICT:
+ # Check root xpublic key
+ assert is_root_xpublic_key(xpublic_key=XPUBLIC_KEY, symbol=SYMBOL), "Invalid Root XPublic Key."
-# Print all Ethereum HDWallet information's
+# Initialize Bitcoin mainnet HDWallet
+hdwallet: HDWallet = HDWallet(symbol=SYMBOL)
+# Get Bitcoin HDWallet from xpublic key
+hdwallet.from_xpublic_key(xpublic_key=XPUBLIC_KEY, strict=STRICT)
+
+# Derivation from path
+# hdwallet.from_path("m/44/0/0/0/0")
+# Or derivation from index
+hdwallet.from_index(44, hardened=False)
+hdwallet.from_index(0, hardened=False)
+hdwallet.from_index(0, hardened=False)
+hdwallet.from_index(0)
+hdwallet.from_index(0)
+
+# Print all Bitcoin HDWallet information's
# print(json.dumps(hdwallet.dumps(), indent=4, ensure_ascii=False))
print("Cryptocurrency:", hdwallet.cryptocurrency())
print("Symbol:", hdwallet.symbol())
print("Network:", hdwallet.network())
+print("Root XPublic Key:", hdwallet.root_xpublic_key())
print("XPublic Key:", hdwallet.xpublic_key())
print("Uncompressed:", hdwallet.uncompressed())
print("Compressed:", hdwallet.compressed())
@@ -27,6 +47,7 @@
print("Public Key:", hdwallet.public_key())
print("Finger Print:", hdwallet.finger_print())
print("Semantic:", hdwallet.semantic())
+print("Path:", hdwallet.path())
print("Hash:", hdwallet.hash())
print("P2PKH Address:", hdwallet.p2pkh_address())
print("P2SH Address:", hdwallet.p2sh_address())
diff --git a/hdwallet/__init__.py b/hdwallet/__init__.py
index ce4c736..dd35aca 100644
--- a/hdwallet/__init__.py
+++ b/hdwallet/__init__.py
@@ -1,9 +1,32 @@
#!/usr/bin/env python3
from .hdwallet import (
- HDWallet, BIP32HDWallet, BIP44HDWallet, BIP49HDWallet, BIP84HDWallet, BIP141HDWallet
+ HDWallet,
+ BIP32HDWallet,
+ BIP44HDWallet,
+ BIP49HDWallet,
+ BIP84HDWallet,
+ BIP141HDWallet
)
-__all__ = [
- "HDWallet", "BIP32HDWallet", "BIP44HDWallet", "BIP49HDWallet", "BIP84HDWallet", "BIP141HDWallet"
+# HDWallet Information's
+__version__: str = "v2.1.1"
+__license__: str = "ISCL"
+__author__: str = "Meheret Tesfaye Batu"
+__email__: str = "meherett@zoho.com"
+__description__: str = "Python-based library for the implementation of a hierarchical deterministic wallet " \
+ "generator for more than 140+ multiple cryptocurrencies."
+
+__all__: list = [
+ "__version__",
+ "__license__",
+ "__author__",
+ "__email__",
+ "__description__",
+ "HDWallet",
+ "BIP32HDWallet",
+ "BIP44HDWallet",
+ "BIP49HDWallet",
+ "BIP84HDWallet",
+ "BIP141HDWallet"
]
diff --git a/hdwallet/cli/__init__.py b/hdwallet/cli/__init__.py
new file mode 100644
index 0000000..9a7b09f
--- /dev/null
+++ b/hdwallet/cli/__init__.py
@@ -0,0 +1,12 @@
+#!/usr/bin/env python3
+
+import textwrap
+import click
+import sys
+
+
+__all__ = [
+ "textwrap",
+ "click",
+ "sys"
+]
diff --git a/hdwallet/cli/__main__.py b/hdwallet/cli/__main__.py
new file mode 100644
index 0000000..71b1d60
--- /dev/null
+++ b/hdwallet/cli/__main__.py
@@ -0,0 +1,221 @@
+#!/usr/bin/env python
+
+from click_aliases import ClickAliasedGroup
+from typing import Optional
+
+from hdwallet import __version__
+from hdwallet.cli.generate.hdwallet import generate_hdwallet
+from hdwallet.cli.generate.addresses import generate_addresses
+from hdwallet.cli.list.cryptocurrencies import list_cryptocurrencies
+from hdwallet.cli.list.languages import list_languages
+from hdwallet.cli.list.strengths import list_strengths
+from hdwallet.cli import click
+
+
+CONTEXT_SETTINGS = dict(
+ help_option_names=["-h", "--help"],
+)
+
+
+def print_version(ctx, param, value):
+ if not value or ctx.resilient_parsing:
+ return
+ click.echo(__version__)
+ ctx.exit()
+
+
+@click.group(cls=ClickAliasedGroup,
+ options_metavar="[OPTIONS]", context_settings=CONTEXT_SETTINGS)
+@click.option("-v", "--version", is_flag=True, callback=print_version,
+ expose_value=False, help="Show HDWallet version and exit.")
+def main():
+ pass
+
+
+@main.group("generate", aliases=["g"], cls=ClickAliasedGroup, options_metavar="[OPTIONS]",
+ short_help="Select Generate for HDWallet.", invoke_without_command=True)
+@click.option("-s", "--symbol", type=str, default="BTC",
+ help="Set Cryptocurrency ticker symbol.")
+@click.option("-sg", "--strength", type=int, default=128,
+ help="Set Strength for entropy, choose strength 128, 160, 192, 224 or 256 only.", show_default=True)
+@click.option("-e", "--entropy", type=str, default=None,
+ help="Set Master key from entropy hex string.", show_default=True)
+@click.option("-m", "--mnemonic", type=str, default=None,
+ help="Set Master key from mnemonic words.", show_default=True)
+@click.option("-l", "--language", type=str, default="english",
+ help="Set Language for mnemonic, choose language english, french, italian, spanish, "
+ "chinese_simplified, chinese_traditional, japanese or korean only.", show_default=True)
+@click.option("-pa", "--passphrase", type=str, default=None,
+ help="Set Passphrase for mnemonic.", show_default=True)
+@click.option("-sd", "--seed", type=str, default=None,
+ help="Set Master key from seed hex string.", show_default=True)
+@click.option("-xprv", "--xprivate-key", type=str, default=None,
+ help="Set Master key from xprivate key.", show_default=True)
+@click.option("-xpub", "--xpublic-key", type=str, default=None,
+ help="Set Master key from xpublic key.", show_default=True)
+@click.option("-st", "--strict", type=bool, default=False,
+ help="Set Strict for root keys.", show_default=True)
+@click.option("-ac", "--account", type=int, default=0,
+ help="Set derivation from account.", show_default=True)
+@click.option("-ch", "--change", type=bool, default=False,
+ help="Set Derivation from change.", show_default=True)
+@click.option("-ad", "--address", type=int, default=0,
+ help="Set Derivation from address.", show_default=True)
+@click.option("-p", "--path", type=str, default=None,
+ help="Set Master key derivation path.", show_default=True)
+@click.option("-prv", "--private-key", type=str, default=None,
+ help="Set Master key from private key.", show_default=True)
+@click.option("-pub", "--public-key", type=str, default=None,
+ help="Set Master key from public key.", show_default=True)
+@click.option("-w", "--wif", type=str, default=None,
+ help="Set Master key from wallet important format.", show_default=True)
+@click.option("-sm", "--semantic", type=str, default="p2pkh",
+ help="Set Semantic for xprivate and xpublic keys.", show_default=True)
+@click.pass_context
+def generate(
+ context: click.core.Context,
+ symbol: str,
+ strength: int,
+ entropy: Optional[str],
+ mnemonic: Optional[str],
+ language: Optional[str],
+ passphrase: Optional[str],
+ seed: Optional[str],
+ xprivate_key: Optional[str],
+ xpublic_key: Optional[str],
+ strict: bool,
+ account: int,
+ change: bool,
+ address: int,
+ path: Optional[str],
+ private_key: Optional[str],
+ public_key: Optional[str],
+ wif: Optional[str],
+ semantic: str
+):
+ if context.invoked_subcommand is None:
+ return generate_hdwallet(
+ symbol=symbol,
+ strength=strength,
+ entropy=entropy,
+ mnemonic=mnemonic,
+ language=language,
+ passphrase=passphrase,
+ seed=seed,
+ xprivate_key=xprivate_key,
+ xpublic_key=xpublic_key,
+ strict=strict,
+ account=account,
+ change=change,
+ address=address,
+ path=path,
+ private_key=private_key,
+ public_key=public_key,
+ wif=wif,
+ semantic=semantic
+ )
+
+
+@generate.command("addresses", aliases=["a"], options_metavar="[OPTIONS]",
+ short_help="Select Addresses for generation HDWallet addresses.")
+@click.option("-s", "--symbol", type=str, default="BTC",
+ help="Set Cryptocurrency ticker symbol.")
+@click.option("-sg", "--strength", type=int, default=128,
+ help="Set Strength for entropy, choose strength 128, 160, 192, 224 or 256 only.", show_default=True)
+@click.option("-e", "--entropy", type=str, default=None,
+ help="Set Master key from entropy hex string.", show_default=True)
+@click.option("-m", "--mnemonic", type=str, default=None,
+ help="Set Master key from mnemonic words.", show_default=True)
+@click.option("-l", "--language", type=str, default="english",
+ help="Set Language for mnemonic, choose language english, french, italian, spanish, "
+ "chinese_simplified, chinese_traditional, japanese or korean only.", show_default=True)
+@click.option("-pa", "--passphrase", type=str, default=None,
+ help="Set Passphrase for mnemonic.", show_default=True)
+@click.option("-sd", "--seed", type=str, default=None,
+ help="Set Master key from seed hex string.", show_default=True)
+@click.option("-xprv", "--xprivate-key", type=str, default=None,
+ help="Set Master key from xprivate key.", show_default=True)
+@click.option("-xpub", "--xpublic-key", type=str, default=None,
+ help="Set Master key from xpublic key.", show_default=True)
+@click.option("-st", "--strict", type=bool, default=False,
+ help="Set Strict for root keys.", show_default=True)
+@click.option("-ac", "--account", type=int, default=0,
+ help="Set derivation from account.", show_default=True)
+@click.option("-ch", "--change", type=bool, default=False,
+ help="Set Derivation from change.", show_default=True)
+@click.option("-p", "--path", type=str, default=None,
+ help="Set Master key derivation path.", show_default=True)
+@click.option("-se", "--semantic", type=str, default="p2pkh",
+ help="Set Semantic for xprivate and xpublic keys.", show_default=True)
+@click.option("-h", "--hardened", type=bool, default=False,
+ help="Set Hardened for addresses.", show_default=True)
+@click.option("-si", "--start-index", type=int, default=0,
+ help="Set Start from address index.", show_default=True)
+@click.option("-ei", "--end-index", type=int, default=20,
+ help="Set End to address index.", show_default=True)
+@click.option("-sh", "--show", type=str, default="path,addresses:p2pkh,public_key,wif",
+ help="Set Value key of generated HDWallet data to show.", show_default=True)
+def addresses(
+ symbol: str,
+ strength: int,
+ entropy: Optional[str],
+ mnemonic: Optional[str],
+ language: Optional[str],
+ passphrase: Optional[str],
+ seed: Optional[str],
+ xprivate_key: Optional[str],
+ xpublic_key: Optional[str],
+ strict: bool,
+ account: int,
+ change: bool,
+ path: Optional[str],
+ semantic: str,
+ start_index: int,
+ end_index: int,
+ hardened: bool,
+ show: str
+):
+ return generate_addresses(
+ symbol=symbol,
+ strength=strength,
+ entropy=entropy,
+ mnemonic=mnemonic,
+ language=language,
+ passphrase=passphrase,
+ seed=seed,
+ xprivate_key=xprivate_key,
+ xpublic_key=xpublic_key,
+ strict=strict,
+ account=account,
+ change=change,
+ path=path,
+ semantic=semantic,
+ start_index=start_index,
+ end_index=end_index,
+ hardened=hardened,
+ show=show
+ )
+
+
+@main.group("list", aliases=["l"], cls=ClickAliasedGroup, options_metavar="[OPTIONS]",
+ short_help="Select List for HDWallet information.", invoke_without_command=True)
+def list():
+ pass
+
+
+@list.command("cryptocurrencies", aliases=["c"], options_metavar="[OPTIONS]",
+ short_help="List Available cryptocurrencies of HDWallet.")
+def cryptocurrencies():
+ return list_cryptocurrencies()
+
+
+@list.command("languages", aliases=["l"], options_metavar="[OPTIONS]",
+ short_help="List Languages of mnemonic words.")
+def languages():
+ return list_languages()
+
+
+@list.command("strengths", aliases=["s"], options_metavar="[OPTIONS]",
+ short_help="List Strengths of mnemonic words.")
+def strengths():
+ return list_strengths()
diff --git a/hdwallet/cli/generate/__init__.py b/hdwallet/cli/generate/__init__.py
new file mode 100644
index 0000000..4265cc3
--- /dev/null
+++ b/hdwallet/cli/generate/__init__.py
@@ -0,0 +1 @@
+#!/usr/bin/env python
diff --git a/hdwallet/cli/generate/addresses.py b/hdwallet/cli/generate/addresses.py
new file mode 100644
index 0000000..60881f3
--- /dev/null
+++ b/hdwallet/cli/generate/addresses.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+
+from typing import Optional
+
+from hdwallet import HDWallet
+from hdwallet.derivations import (
+ Derivation, BIP32Derivation
+)
+from hdwallet.cryptocurrencies import (
+ Cryptocurrency, get_cryptocurrency
+)
+from hdwallet.utils import generate_mnemonic
+from hdwallet.cli import (
+ click, sys
+)
+
+
+def generate_addresses(
+ symbol: str,
+ strength: int,
+ entropy: Optional[str],
+ mnemonic: Optional[str],
+ language: Optional[str],
+ passphrase: Optional[str],
+ seed: Optional[str],
+ xprivate_key: Optional[str],
+ xpublic_key: Optional[str],
+ strict: bool,
+ account: int,
+ change: bool,
+ path: Optional[str],
+ semantic: str,
+ start_index: int,
+ end_index: int,
+ hardened: bool,
+ show: str
+):
+ try:
+ hdwallet: HDWallet = HDWallet(
+ symbol=symbol, semantic=semantic
+ )
+ if entropy:
+ hdwallet.from_entropy(
+ entropy=entropy, language=language, passphrase=passphrase
+ )
+ elif mnemonic:
+ hdwallet.from_mnemonic(
+ mnemonic=mnemonic, language=language, passphrase=passphrase
+ )
+ elif seed:
+ hdwallet.from_seed(
+ seed=seed
+ )
+ elif xprivate_key:
+ hdwallet.from_xprivate_key(
+ xprivate_key=xprivate_key, strict=strict
+ )
+ elif xpublic_key:
+ hdwallet.from_xpublic_key(
+ xpublic_key=xpublic_key, strict=strict
+ )
+ else:
+ mnemonic = generate_mnemonic(language=language, strength=strength)
+ hdwallet.from_mnemonic(
+ mnemonic=mnemonic, language=language, passphrase=passphrase
+ )
+
+ for index in range(start_index, end_index):
+ if path:
+ derivation: Derivation = Derivation(path=path)
+ derivation.from_index(index=index, hardened=hardened)
+ hdwallet.from_path(path=derivation)
+ else:
+ cryptocurrency: Cryptocurrency = get_cryptocurrency(symbol=symbol)
+ bip32_derivation: BIP32Derivation = BIP32Derivation(
+ purpose=(
+ 44, False if xpublic_key else True
+ ),
+ coin_type=(
+ cryptocurrency.COIN_TYPE.INDEX,
+ False if xpublic_key else cryptocurrency.COIN_TYPE.HARDENED
+ ),
+ account=(
+ account, False if xpublic_key else True
+ ),
+ change=change,
+ address=index
+ )
+ hdwallet.from_path(path=bip32_derivation)
+
+ rows: str = ""
+ dumps = hdwallet.dumps()
+ for i, key in enumerate([keys.split(":") for keys in show.split(",")]):
+ rows += (
+ f"{dumps[key[0]][key[1]] if len(key) == 2 else dumps[key[0]]}"
+ if i == 0 else
+ f" {dumps[key[0]][key[1]] if len(key) == 2 else dumps[key[0]]}"
+ )
+ click.echo(rows)
+
+ hdwallet.clean_derivation()
+
+ except TimeoutError as exception:
+ click.echo(click.style(f"Error: {str(exception)}"), err=True)
+ sys.exit()
diff --git a/hdwallet/cli/generate/hdwallet.py b/hdwallet/cli/generate/hdwallet.py
new file mode 100644
index 0000000..c83db28
--- /dev/null
+++ b/hdwallet/cli/generate/hdwallet.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+
+from typing import Optional
+
+import json
+
+from hdwallet import HDWallet
+from hdwallet.derivations import (
+ Derivation, BIP32Derivation
+)
+from hdwallet.cryptocurrencies import (
+ Cryptocurrency, get_cryptocurrency
+)
+from hdwallet.utils import generate_mnemonic
+from hdwallet.cli import (
+ click, sys
+)
+
+
+def generate_hdwallet(
+ symbol: str,
+ strength: Optional[int],
+ entropy: Optional[str],
+ mnemonic: Optional[str],
+ language: Optional[str],
+ passphrase: Optional[str],
+ seed: Optional[str],
+ xprivate_key: Optional[str],
+ xpublic_key: Optional[str],
+ strict: Optional[bool],
+ account: int,
+ change: bool,
+ address: int,
+ path: Optional[str],
+ private_key: Optional[str],
+ public_key: Optional[str],
+ wif: Optional[str],
+ semantic: str
+):
+ try:
+ hdwallet: HDWallet = HDWallet(
+ symbol=symbol, semantic=semantic
+ )
+ if entropy:
+ hdwallet.from_entropy(
+ entropy=entropy, language=language, passphrase=passphrase
+ )
+ elif mnemonic:
+ hdwallet.from_mnemonic(
+ mnemonic=mnemonic, language=language, passphrase=passphrase
+ )
+ elif seed:
+ hdwallet.from_seed(
+ seed=seed
+ )
+ elif xprivate_key:
+ hdwallet.from_xprivate_key(
+ xprivate_key=xprivate_key, strict=strict
+ )
+ elif xpublic_key:
+ hdwallet.from_xpublic_key(
+ xpublic_key=xpublic_key, strict=strict
+ )
+ elif private_key:
+ hdwallet.from_private_key(
+ private_key=private_key
+ )
+ elif public_key:
+ hdwallet.from_public_key(
+ public_key=public_key
+ )
+ elif wif:
+ hdwallet.from_wif(
+ wif=wif
+ )
+ else:
+ mnemonic = generate_mnemonic(language=language, strength=strength)
+ hdwallet.from_mnemonic(
+ mnemonic=mnemonic, language=language, passphrase=passphrase
+ )
+
+ if wif or private_key or public_key:
+ pass
+ else:
+ if path:
+ derivation: Derivation = Derivation(path=path)
+ hdwallet.from_path(path=derivation)
+ else:
+ cryptocurrency: Cryptocurrency = get_cryptocurrency(symbol=symbol)
+ bip32_derivation: BIP32Derivation = BIP32Derivation(
+ purpose=(
+ 44, False if xpublic_key else True
+ ),
+ coin_type=(
+ cryptocurrency.COIN_TYPE.INDEX,
+ False if xpublic_key else cryptocurrency.COIN_TYPE.HARDENED
+ ),
+ account=(
+ account, False if xpublic_key else True
+ ),
+ change=change,
+ address=address
+ )
+ hdwallet.from_path(path=bip32_derivation)
+
+ click.echo(json.dumps(hdwallet.dumps(), indent=4, ensure_ascii=False))
+
+ except Exception as exception:
+ click.echo(click.style(f"Error: {str(exception)}"), err=True)
+ sys.exit()
diff --git a/hdwallet/cli/list/__init__.py b/hdwallet/cli/list/__init__.py
new file mode 100644
index 0000000..4265cc3
--- /dev/null
+++ b/hdwallet/cli/list/__init__.py
@@ -0,0 +1 @@
+#!/usr/bin/env python
diff --git a/hdwallet/cli/list/cryptocurrencies.py b/hdwallet/cli/list/cryptocurrencies.py
new file mode 100644
index 0000000..d55a60e
--- /dev/null
+++ b/hdwallet/cli/list/cryptocurrencies.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+
+from tabulate import tabulate
+
+import inspect
+
+from hdwallet import cryptocurrencies
+from hdwallet.cli import click
+
+
+def list_cryptocurrencies():
+
+ documents, table, headers = [], [], [
+ "Cryptocurrency", "Symbol", "Mainnet", "Testnet", "Segwit", "Coin Type", "Default Path"
+ ]
+
+ for name, cryptocurrency in inspect.getmembers(cryptocurrencies):
+ if inspect.isclass(cryptocurrency):
+ if issubclass(cryptocurrency, cryptocurrencies.Cryptocurrency) \
+ and cryptocurrency != cryptocurrencies.Cryptocurrency:
+
+ if cryptocurrency.NETWORK == "mainnet":
+ document: dict = {
+ "name": cryptocurrency.NAME,
+ "symbol": cryptocurrency.SYMBOL,
+ "source_code": cryptocurrency.SOURCE_CODE,
+ "mainnet": "Yes" if cryptocurrency.NETWORK == "mainnet" else "No",
+ "testnet": "Yes" if cryptocurrency.NETWORK == "testnet" else "No",
+ "segwit": "Yes" if cryptocurrency.SEGWIT_ADDRESS.HRP else "No",
+ "coin_type": cryptocurrency.COIN_TYPE.INDEX,
+ "default_path": cryptocurrency.DEFAULT_PATH
+ }
+ documents.append(document)
+ elif cryptocurrency.NETWORK == "testnet":
+ for index, document in enumerate(documents):
+ if document["name"] == cryptocurrency.NAME:
+ documents[index]["symbol"] = f"{document['symbol']}, {cryptocurrency.SYMBOL}"
+ documents[index]["testnet"] = "Yes"
+ else:
+ raise Exception("Invalid cryptocurrency network type.")
+
+ for document in documents:
+ table.append([
+ document["name"],
+ document["symbol"],
+ document["mainnet"],
+ document["testnet"],
+ document["segwit"],
+ document["coin_type"],
+ document["default_path"]
+ ])
+
+ click.echo(tabulate(table, headers, tablefmt="github"))
diff --git a/hdwallet/cli/list/languages.py b/hdwallet/cli/list/languages.py
new file mode 100644
index 0000000..11aed27
--- /dev/null
+++ b/hdwallet/cli/list/languages.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+from tabulate import tabulate
+
+from hdwallet.cli import click
+
+
+def list_languages():
+
+ click.echo(tabulate(
+ [
+ ["Chinese Simplified"],
+ ["Chinese Traditional"],
+ ["English"],
+ ["French"],
+ ["Italian"],
+ ["Japanese"],
+ ["Korean"],
+ ["Spanish"],
+ ],
+ [
+ "Language"
+ ],
+ tablefmt="github"
+ ))
diff --git a/hdwallet/cli/list/strengths.py b/hdwallet/cli/list/strengths.py
new file mode 100644
index 0000000..d0e9114
--- /dev/null
+++ b/hdwallet/cli/list/strengths.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+
+from tabulate import tabulate
+
+from hdwallet.cli import click
+
+
+def list_strengths():
+
+ click.echo(tabulate(
+ [
+ [128, 12],
+ [160, 15],
+ [192, 18],
+ [224, 21],
+ [256, 24],
+ ],
+ [
+ "Strength",
+ "Words"
+ ],
+ tablefmt="github"
+ ))
diff --git a/hdwallet/cryptocurrencies.py b/hdwallet/cryptocurrencies.py
index 1099f4b..9ba2f2f 100644
--- a/hdwallet/cryptocurrencies.py
+++ b/hdwallet/cryptocurrencies.py
@@ -3213,18 +3213,18 @@ class LitecoinMainnet(Cryptocurrency):
})
EXTENDED_PRIVATE_KEY = ExtendedPrivateKey({
- "P2PKH": 0x019d9cfe,
- "P2SH": 0x019d9cfe,
- "P2WPKH": 0x04b2430c,
- "P2WPKH_IN_P2SH": 0x01b26792,
+ "P2PKH": 0x488ade4,
+ "P2SH": 0x488ade4,
+ "P2WPKH": None,
+ "P2WPKH_IN_P2SH": None,
"P2WSH": None,
"P2WSH_IN_P2SH": None
})
EXTENDED_PUBLIC_KEY = ExtendedPublicKey({
- "P2PKH": 0x019da462,
- "P2SH": 0x019da462,
- "P2WPKH": 0x04b24746,
- "P2WPKH_IN_P2SH": 0x01b26ef6,
+ "P2PKH": 0x488b21e,
+ "P2SH": 0x488b21e,
+ "P2WPKH": None,
+ "P2WPKH_IN_P2SH": None,
"P2WSH": None,
"P2WSH_IN_P2SH": None
})
@@ -3245,33 +3245,33 @@ class LitecoinTestnet(Cryptocurrency):
"HARDENED": True
})
- SCRIPT_ADDRESS = 0xc4
+ SCRIPT_ADDRESS = 0x3a
PUBLIC_KEY_ADDRESS = 0x6f
SEGWIT_ADDRESS = SegwitAddress({
- "HRP": "litecointestnet",
+ "HRP": "tltc",
"VERSION": 0x00
})
EXTENDED_PRIVATE_KEY = ExtendedPrivateKey({
- "P2PKH": 0x0436ef7d,
- "P2SH": 0x0436ef7d,
- "P2WPKH": 0x04358394,
- "P2WPKH_IN_P2SH": 0x04358394,
+ "P2PKH": 0x04358394,
+ "P2SH": 0x04358394,
+ "P2WPKH": None,
+ "P2WPKH_IN_P2SH": None,
"P2WSH": None,
"P2WSH_IN_P2SH": None
})
EXTENDED_PUBLIC_KEY = ExtendedPublicKey({
- "P2PKH": 0x0436f6e1,
- "P2SH": 0x0436f6e1,
- "P2WPKH": 0x043587cf,
- "P2WPKH_IN_P2SH": 0x043587cf,
+ "P2PKH": 0x043587cf,
+ "P2SH": 0x043587cf,
+ "P2WPKH": None,
+ "P2WPKH_IN_P2SH": None,
"P2WSH": None,
"P2WSH_IN_P2SH": None
})
MESSAGE_PREFIX = "\x19Litecoin Signed Message:\n"
DEFAULT_PATH = f"m/44'/{str(COIN_TYPE)}/0'/0/0"
- WIF_SECRET_KEY = 0xb0
+ WIF_SECRET_KEY = 0xef
class LitecoinZMainnet(Cryptocurrency):
@@ -5594,6 +5594,48 @@ class ThoughtAIMainnet(Cryptocurrency):
WIF_SECRET_KEY = 0x7b
+class TronMainnet(Cryptocurrency):
+
+ NAME = "Tron"
+ SYMBOL = "TRX"
+ NETWORK = "mainnet"
+ SOURCE_CODE = "https://github.com/tronprotocol/java-tron"
+
+ COIN_TYPE = CoinType({
+ "INDEX": 195,
+ "HARDENED": True
+ })
+
+ SCRIPT_ADDRESS = 0x05
+ PUBLIC_KEY_ADDRESS = 0x41
+
+ SEGWIT_ADDRESS = SegwitAddress({
+ "HRP": "bc",
+ "VERSION": 0x00
+ })
+
+ EXTENDED_PRIVATE_KEY = ExtendedPrivateKey({
+ "P2PKH": 0x0488ade4,
+ "P2SH": 0x0488ade4,
+ "P2WPKH": 0x04b2430c,
+ "P2WPKH_IN_P2SH": 0x049d7878,
+ "P2WSH": 0x02aa7a99,
+ "P2WSH_IN_P2SH": 0x0295b005
+ })
+ EXTENDED_PUBLIC_KEY = ExtendedPublicKey({
+ "P2PKH": 0x0488b21e,
+ "P2SH": 0x0488b21e,
+ "P2WPKH": 0x04b24746,
+ "P2WPKH_IN_P2SH": 0x049d7cb2,
+ "P2WSH": 0x02aa7ed3,
+ "P2WSH_IN_P2SH": 0x0295b43f
+ })
+
+ MESSAGE_PREFIX = None
+ DEFAULT_PATH = f"m/44'/{str(COIN_TYPE)}/0'/0/0"
+ WIF_SECRET_KEY = 0x80
+
+
class TwinsMainnet(Cryptocurrency):
NAME = "Twins"
@@ -6239,7 +6281,7 @@ class ZcashMainnet(Cryptocurrency):
NAME = "Zcash"
SYMBOL = "ZEC"
NETWORK = "mainnet"
- SOURCE_CODE = None
+ SOURCE_CODE = "https://github.com/zcash/zcash"
COIN_TYPE = CoinType({
"INDEX": 133,
"HARDENED": True
@@ -6274,6 +6316,46 @@ class ZcashMainnet(Cryptocurrency):
WIF_SECRET_KEY = 0x80
+class ZcashTestnet(Cryptocurrency):
+
+ NAME = "Zcash"
+ SYMBOL = "ZECTEST"
+ NETWORK = "testnet"
+ SOURCE_CODE = "https://github.com/zcash/zcash"
+ COIN_TYPE = CoinType({
+ "INDEX": 1,
+ "HARDENED": True
+ })
+
+ SCRIPT_ADDRESS = 0x1cba
+ PUBLIC_KEY_ADDRESS = 0x1d25
+ SEGWIT_ADDRESS = SegwitAddress({
+ "HRP": None,
+ "VERSION": 0x00
+ })
+
+ EXTENDED_PRIVATE_KEY = ExtendedPrivateKey({
+ "P2PKH": 0x4358394,
+ "P2SH": 0x4358394,
+ "P2WPKH": None,
+ "P2WPKH_IN_P2SH": None,
+ "P2WSH": None,
+ "P2WSH_IN_P2SH": None
+ })
+ EXTENDED_PUBLIC_KEY = ExtendedPublicKey({
+ "P2PKH": 0x43587cf,
+ "P2SH": 0x43587cf,
+ "P2WPKH": None,
+ "P2WPKH_IN_P2SH": None,
+ "P2WSH": None,
+ "P2WSH_IN_P2SH": None
+ })
+
+ MASSAGE_PREFIX = "\x18Zcash Signed Message:\n"
+ DEFAULT_PATH = f"m/44'/{str(COIN_TYPE)}/0'/0/0"
+ WIF_SECRET_KEY = 0xef
+
+
class ZencashMainnet(Cryptocurrency):
NAME = "Zencash"
diff --git a/hdwallet/derivations.py b/hdwallet/derivations.py
index 9a49013..b34a6d4 100644
--- a/hdwallet/derivations.py
+++ b/hdwallet/derivations.py
@@ -27,7 +27,7 @@ class Derivation:
>>> Derivation()
>>> str(Derivation())
- ""
+ "\0\0\0\0"
>>> str(Derivation(path="m/44'/0'/0'/0/0", semantic="p2pkh"))
"m/44'/0'/0'/0/0"
@@ -106,6 +106,8 @@ def from_index(self, index: int, hardened: bool = False) -> "Derivation":
if not isinstance(index, int):
raise DerivationError("Bad derivation index, Please import only int type!")
+ if self.PATH == "\0\0\0\0":
+ self.PATH = ""
self.PATH += (
(f"/{index}'" if hardened else f"/{index}")
if self.PATH.startswith("m/") else
@@ -127,7 +129,7 @@ def clean_derivation(self) -> "Derivation":
>>> derivation.clean_derivation()
>>> str(derivation)
- ""
+ "\0\0\0\0"
"""
self.PATH = "\0\0\0\0"
@@ -510,7 +512,7 @@ class BIP84Derivation(BIP32Derivation):
>>> BIP84Derivation(cryptocurrency=BitcoinMainnet)
>>> str(BIP84Derivation(cryptocurrency=BitcoinMainnet))
- "m/49'/0'/0'/0/0"
+ "m/84'/0'/0'/0/0"
"""
PURPOSE: int = 84
@@ -547,7 +549,7 @@ class BIP141Derivation(Derivation):
>>> BIP141Derivation(cryptocurrency=BitcoinMainnet)
>>> str(BIP141Derivation(cryptocurrency=BitcoinMainnet))
- "m/49'/0'/0'/0/0"
+ "m/44'/0'/0'/0/0"
"""
def __init__(self, cryptocurrency: Any, path: Union[str, Derivation] = None, semantic: str = "p2wpkh"):
diff --git a/hdwallet/hdwallet.py b/hdwallet/hdwallet.py
index 5e226b7..97e7d04 100644
--- a/hdwallet/hdwallet.py
+++ b/hdwallet/hdwallet.py
@@ -1,5 +1,21 @@
#!/usr/bin/env python3
+"""
+Copyright © 2021, Meheret Tesfaye Batu
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+"""
+
from ecdsa.curves import SECP256k1
from ecdsa.ellipticcurve import Point
from ecdsa.keys import (
@@ -39,15 +55,14 @@
Cryptocurrency, get_cryptocurrency, SegwitAddress
)
from .derivations import (
- Derivation, BIP32Derivation, BIP44Derivation,
- BIP49Derivation, BIP84Derivation, BIP141Derivation
+ Derivation, BIP32Derivation, BIP44Derivation, BIP49Derivation, BIP84Derivation, BIP141Derivation
)
from .exceptions import (
SemanticError, DerivationError
)
from .utils import (
get_bytes, is_entropy, is_mnemonic, get_entropy_strength, _unhexlify, is_root_xpublic_key,
- get_mnemonic_language, is_root_xprivate_key, get_mnemonic_strength
+ get_mnemonic_language, is_root_xprivate_key, get_mnemonic_strength, get_semantic
)
MIN_ENTROPY_LEN: int = 128
@@ -79,7 +94,7 @@ class HDWallet:
"""
def __init__(self, symbol: str = "BTC", cryptocurrency: Any = None,
- semantic: str = "p2pkh", use_default_path: bool = False):
+ semantic: Optional[str] = None, use_default_path: bool = False):
self._cryptocurrency: Any = None
if cryptocurrency:
if not issubclass(cryptocurrency, Cryptocurrency):
@@ -115,6 +130,10 @@ def __init__(self, symbol: str = "BTC", cryptocurrency: Any = None,
self._depth: int = 0
self._index: int = 0
+ self._root_depth: int = 0
+ self._root_parent_fingerprint: bytes = b"\0\0\0\0"
+ self._root_index: int = 0
+
def from_entropy(self, entropy: str, language: str = "english", passphrase: str = None) -> "HDWallet":
"""
Master from Entropy hex string.
@@ -140,7 +159,7 @@ def from_entropy(self, entropy: str, language: str = "english", passphrase: str
if language and language not in ["english", "french", "italian", "japanese",
"chinese_simplified", "chinese_traditional", "korean", "spanish"]:
raise ValueError("Invalid language, choose only the following options 'english', 'french', 'italian', "
- "'spanish', 'chinese_simplified', 'chinese_traditional', 'japanese or 'korean' languages.")
+ "'spanish', 'chinese_simplified', 'chinese_traditional', 'japanese' or 'korean' languages.")
self._strength = get_entropy_strength(entropy=entropy)
self._entropy, self._language = unhexlify(entropy), language
@@ -148,6 +167,8 @@ def from_entropy(self, entropy: str, language: str = "english", passphrase: str
mnemonic = Mnemonic(language=self._language).to_mnemonic(data=self._entropy)
self._mnemonic = unicodedata.normalize("NFKD", mnemonic)
self._seed = Mnemonic.to_seed(mnemonic=self._mnemonic, passphrase=self._passphrase)
+ if self._semantic is None:
+ self._semantic = "p2pkh"
return self.from_seed(seed=hexlify(self._seed).decode())
def from_mnemonic(self, mnemonic: str, language: str = None, passphrase: str = None) -> "HDWallet":
@@ -179,6 +200,8 @@ def from_mnemonic(self, mnemonic: str, language: str = None, passphrase: str = N
self._entropy = Mnemonic(language=self._language).to_entropy(self._mnemonic)
self._passphrase = str(passphrase) if passphrase else str()
self._seed = Mnemonic.to_seed(mnemonic=self._mnemonic, passphrase=self._passphrase)
+ if self._semantic is None:
+ self._semantic = "p2pkh"
return self.from_seed(seed=hexlify(self._seed).decode())
def from_seed(self, seed: str) -> "HDWallet":
@@ -213,15 +236,17 @@ def from_seed(self, seed: str) -> "HDWallet":
self._public_key = self.compressed()
if self._from_class:
self.from_path(path=self._path_class)
+ if self._semantic is None:
+ self._semantic = "p2pkh"
return self
- def from_root_xprivate_key(self, xprivate_key: str, strict: bool = True) -> "HDWallet":
+ def from_xprivate_key(self, xprivate_key: str, strict: bool = False) -> "HDWallet":
"""
- Master from Root XPrivate Key.
+ Master from XPrivate Key.
- :param xprivate_key: Root xprivate key.
+ :param xprivate_key: Root or Non-Root XPrivate key.
:type xprivate_key: str
- :param strict: Strict for must be root xprivate key, default to ``True``.
+ :param strict: Strict for must be root xprivate key, default to ``False``.
:type strict: bool
:returns: HDWallet -- Hierarchical Deterministic Wallet instance.
@@ -229,18 +254,25 @@ def from_root_xprivate_key(self, xprivate_key: str, strict: bool = True) -> "HDW
>>> from hdwallet import HDWallet
>>> from hdwallet.symbols import BTC
>>> hdwallet = HDWallet(symbol=BTC)
- >>> hdwallet.from_root_xprivate_key(xprivate_key="xprv9s21ZrQH143K3xPGUzpogJeKtRdjHkK6muBJo8v7rEVRzT83xJgNcLpMoJXUf9wJFKfuHR4SGvfgdShh4t9VmjjrE9usBunK3LfNna31LGF")
+ >>> hdwallet.from_xprivate_key(xprivate_key="xprv9s21ZrQH143K3xPGUzpogJeKtRdjHkK6muBJo8v7rEVRzT83xJgNcLpMoJXUf9wJFKfuHR4SGvfgdShh4t9VmjjrE9usBunK3LfNna31LGF")
"""
if not is_root_xprivate_key(xprivate_key=xprivate_key, symbol=self._cryptocurrency.SYMBOL):
if strict:
raise ValueError("Invalid root xprivate key.")
- else:
- print("Warning: The xprivate key is not root xprivate key.")
_deserialize_xprivate_key = self._deserialize_xprivate_key(xprivate_key=xprivate_key)
- self._depth, self._parent_fingerprint, self._index = (0, b"\0\0\0\0", 0)
+ self._root_depth, self._root_parent_fingerprint, self._root_index = (
+ int.from_bytes(_deserialize_xprivate_key[1], "big"),
+ _deserialize_xprivate_key[2],
+ struct.unpack(">L", _deserialize_xprivate_key[3])[0]
+ )
+ self._depth, self._parent_fingerprint, self._index = (
+ int.from_bytes(_deserialize_xprivate_key[1], "big"),
+ _deserialize_xprivate_key[2],
+ struct.unpack(">L", _deserialize_xprivate_key[3])[0]
+ )
self._i = _deserialize_xprivate_key[5] + _deserialize_xprivate_key[4]
self._root_private_key = (_deserialize_xprivate_key[5], _deserialize_xprivate_key[4])
self._private_key, self._chain_code = self._i[:32], self._i[32:]
@@ -251,15 +283,20 @@ def from_root_xprivate_key(self, xprivate_key: str, strict: bool = True) -> "HDW
if self._from_class:
self.from_path(path=self._path_class)
self._public_key = self.compressed()
+ self._semantic = get_semantic(
+ _cryptocurrency=self._cryptocurrency,
+ version=_deserialize_xprivate_key[0],
+ key_type="private_key"
+ )
return self
- def from_root_xpublic_key(self, xpublic_key: str, strict: bool = True) -> "HDWallet":
+ def from_xpublic_key(self, xpublic_key: str, strict: bool = False) -> "HDWallet":
"""
- Master from Root XPublic Key.
+ Master from XPublic Key.
- :param xpublic_key: Root xpublic key.
+ :param xpublic_key: Root or Non-Root XPublic key.
:type xpublic_key: str
- :param strict: Strict for must be root xpublic key, default to ``True``.
+ :param strict: Strict for must be root xpublic key, default to ``False``.
:type strict: bool
:returns: HDWallet -- Hierarchical Deterministic Wallet instance.
@@ -267,7 +304,7 @@ def from_root_xpublic_key(self, xpublic_key: str, strict: bool = True) -> "HDWal
>>> from hdwallet import HDWallet
>>> from hdwallet.symbols import BTC
>>> hdwallet = HDWallet(symbol=BTC)
- >>> hdwallet.from_root_xpublic_key(xpublic_key="xpub661MyMwAqRbcGSTjb2Mp3Sb4STUDhD2x986ubXKjQa2QsFTCVqzdA98qeZjcncHT1AaZcMSjiP1HJ16jH97q72RwyFfiNhmG8zQ6KBB5PaQ")
+ >>> hdwallet.from_xpublic_key(xpublic_key="xpub661MyMwAqRbcGSTjb2Mp3Sb4STUDhD2x986ubXKjQa2QsFTCVqzdA98qeZjcncHT1AaZcMSjiP1HJ16jH97q72RwyFfiNhmG8zQ6KBB5PaQ")
"""
@@ -276,7 +313,16 @@ def from_root_xpublic_key(self, xpublic_key: str, strict: bool = True) -> "HDWal
raise ValueError("Invalid root xpublic key.")
_deserialize_xpublic_key = self._deserialize_xpublic_key(xpublic_key=xpublic_key)
- self._depth, self._parent_fingerprint, self._index = (0, b"\0\0\0\0", 0)
+ self._root_depth, self._root_parent_fingerprint, self._root_index = (
+ int.from_bytes(_deserialize_xpublic_key[1], "big"),
+ _deserialize_xpublic_key[2],
+ struct.unpack(">L", _deserialize_xpublic_key[3])[0]
+ )
+ self._depth, self._parent_fingerprint, self._index = (
+ int.from_bytes(_deserialize_xpublic_key[1], "big"),
+ _deserialize_xpublic_key[2],
+ struct.unpack(">L", _deserialize_xpublic_key[3])[0]
+ )
self._chain_code = _deserialize_xpublic_key[4]
self._verified_key = ecdsa.VerifyingKey.from_string(
_deserialize_xpublic_key[5], curve=SECP256k1
@@ -287,65 +333,13 @@ def from_root_xpublic_key(self, xpublic_key: str, strict: bool = True) -> "HDWal
if self._use_default_path:
self.from_path(path=self._cryptocurrency.DEFAULT_PATH)
if self._from_class:
- self.from_path(path=self._path_class)
+ self.from_path(path=str(self._path_class).replace("'", ""))
self._public_key = self.compressed()
- return self
-
- def from_xprivate_key(self, xprivate_key: str) -> "HDWallet":
- """
- Master from XPrivate Key.
-
- :param xprivate_key: XPrivate key.
- :type xprivate_key: str
-
- :returns: HDWallet -- Hierarchical Deterministic Wallet instance.
-
- >>> from hdwallet import HDWallet
- >>> from hdwallet.symbols import BTC
- >>> hdwallet = HDWallet(symbol=BTC)
- >>> hdwallet.from_xprivate_key(xprivate_key="xprvA3BYGWQ9FmhyaNRRXB2f1LphNPnaY9T6gngw4BaTbkFtscSH4RCuJhgWUSKs9S6ciGioHd4TX4UeyUg53MkfN9Xh38xkS1j2Wb9YKsYpJHQ")
-
- """
-
- _deserialize_xprivate_key = self._deserialize_xprivate_key(xprivate_key=xprivate_key)
- self._depth, self._parent_fingerprint, self._index = (
- int.from_bytes(_deserialize_xprivate_key[1], "big"),
- _deserialize_xprivate_key[2],
- struct.unpack(">L", _deserialize_xprivate_key[3])[0]
+ self._semantic = get_semantic(
+ _cryptocurrency=self._cryptocurrency,
+ version=_deserialize_xpublic_key[0],
+ key_type="public_key"
)
- self._private_key, self._chain_code = _deserialize_xprivate_key[5], _deserialize_xprivate_key[4]
- self._key = ecdsa.SigningKey.from_string(_deserialize_xprivate_key[5], curve=SECP256k1)
- self._verified_key = self._key.get_verifying_key()
- self._public_key = self.compressed()
- return self
-
- def from_xpublic_key(self, xpublic_key: str) -> "HDWallet":
- """
- Master from XPublic Key.
-
- :param xpublic_key: XPublic key.
- :type xpublic_key: str
-
- :returns: HDWallet -- Hierarchical Deterministic Wallet instance.
-
- >>> from hdwallet import HDWallet
- >>> from hdwallet.symbols import BTC
- >>> hdwallet = HDWallet(symbol=BTC)
- >>> hdwallet.from_xpublic_key(xprivate_key="xpub661MyMwAqRbcGSTjb2Mp3Sb4STUDhD2x986ubXKjQa2QsFTCVqzdA98qeZjcncHT1AaZcMSjiP1HJ16jH97q72RwyFfiNhmG8zQ6KBB5PaQ")
-
- """
-
- _deserialize_xpublic_key = self._deserialize_xpublic_key(xpublic_key=xpublic_key)
- self._depth, self._parent_fingerprint, self._index = (
- int.from_bytes(_deserialize_xpublic_key[1], "big"),
- _deserialize_xpublic_key[2],
- struct.unpack(">L", _deserialize_xpublic_key[3])[0]
- )
- self._chain_code = _deserialize_xpublic_key[4]
- self._verified_key = ecdsa.VerifyingKey.from_string(
- _deserialize_xpublic_key[5], curve=SECP256k1
- )
- self._public_key = self.compressed()
return self
def from_wif(self, wif: str) -> "HDWallet":
@@ -430,7 +424,7 @@ def from_path(self, path: Union[str, Derivation]) -> "HDWallet":
>>> from hdwallet import HDWallet
>>> from hdwallet.symbols import BTC
>>> hdwallet = HDWallet(symbol=BTC)
- >>> hdwallet.from_root_xprivate_key(root_xprivate_key="xprv9s21ZrQH143K3xPGUzpogJeKtRdjHkK6muBJo8v7rEVRzT83xJgNcLpMoJXUf9wJFKfuHR4SGvfgdShh4t9VmjjrE9usBunK3LfNna31LGF")
+ >>> hdwallet.from_xprivate_key(xprivate_key="xprv9s21ZrQH143K3xPGUzpogJeKtRdjHkK6muBJo8v7rEVRzT83xJgNcLpMoJXUf9wJFKfuHR4SGvfgdShh4t9VmjjrE9usBunK3LfNna31LGF")
>>> hdwallet.from_path(path="m/44'/0'/'0/0/0")
"""
@@ -463,7 +457,7 @@ def from_index(self, index: int, hardened: bool = False) -> "HDWallet":
>>> from hdwallet import HDWallet
>>> from hdwallet.symbols import BTC
>>> hdwallet = HDWallet(symbol=BTC)
- >>> hdwallet.from_root_xprivate_key(root_xprivate_key="xprv9s21ZrQH143K3xPGUzpogJeKtRdjHkK6muBJo8v7rEVRzT83xJgNcLpMoJXUf9wJFKfuHR4SGvfgdShh4t9VmjjrE9usBunK3LfNna31LGF")
+ >>> hdwallet.from_xprivate_key(xprivate_key="xprv9s21ZrQH143K3xPGUzpogJeKtRdjHkK6muBJo8v7rEVRzT83xJgNcLpMoJXUf9wJFKfuHR4SGvfgdShh4t9VmjjrE9usBunK3LfNna31LGF")
>>> hdwallet.from_index(index=44, hardened=True)
>>> hdwallet.from_index(index=0, hardened=True)
>>> hdwallet.from_index(index=0, hardened=True)
@@ -485,18 +479,18 @@ def from_index(self, index: int, hardened: bool = False) -> "HDWallet":
def _derive_key_by_index(self, index) -> Optional["HDWallet"]:
if not self._root_private_key and not self._root_public_key:
- raise PermissionError("You can't drive this master key.")
+ raise ValueError("You can't drive this master key.")
i_str = struct.pack(">L", index)
if index & BIP32KEY_HARDEN:
if self._key is None:
- raise DerivationError("Hardened derivation path is invalid with root xpublic key")
+ raise DerivationError("Hardened derivation path is invalid for xpublic key.")
data = b"\0" + self._key.to_string() + i_str
else:
data = unhexlify(self.public_key()) + i_str
if not self._chain_code:
- raise PermissionError("You can't drive xprivate_key and private_key.")
+ raise ValueError("You can't drive xprivate_key and private_key.")
i = hmac.new(self._chain_code, data, hashlib.sha512).digest()
il, ir = i[:32], i[32:]
@@ -579,17 +573,21 @@ def root_xprivate_key(self, encoded: bool = True) -> Optional[str]:
"xprv9s21ZrQH143K3xPGUzpogJeKtRdjHkK6muBJo8v7rEVRzT83xJgNcLpMoJXUf9wJFKfuHR4SGvfgdShh4t9VmjjrE9usBunK3LfNna31LGF"
"""
+ if self._semantic is None:
+ return None
version = self._cryptocurrency.EXTENDED_PRIVATE_KEY.__getattribute__(
self._semantic.upper()
)
if version is None:
- raise NotImplementedError(self)
+ raise NotImplementedError(
+ f"{self.__class__.__name__} is not implemented for {self._cryptocurrency.NAME} {self._cryptocurrency.NETWORK} cryptocurrency."
+ )
if not self._i:
return None
secret_key, chain_code = self._i[:32], self._i[32:]
- depth = bytes(bytearray([0]))
- parent_fingerprint = b"\0\0\0\0"
- index = struct.pack(">L", 0)
+ depth = bytes(bytearray([self._root_depth]))
+ parent_fingerprint = self._root_parent_fingerprint
+ index = struct.pack(">L", self._root_index)
data = b"\x00" + secret_key
return self._serialize_xkeys(
_unhexlify(version), depth, parent_fingerprint, index, chain_code, data, encoded
@@ -613,11 +611,15 @@ def root_xpublic_key(self, encoded: bool = True) -> Optional[str]:
"xpub661MyMwAqRbcGSTjb2Mp3Sb4STUDhD2x986ubXKjQa2QsFTCVqzdA98qeZjcncHT1AaZcMSjiP1HJ16jH97q72RwyFfiNhmG8zQ6KBB5PaQ"
"""
+ if self._semantic is None:
+ return None
version = self._cryptocurrency.EXTENDED_PUBLIC_KEY.__getattribute__(
self._semantic.upper()
)
if version is None:
- raise NotImplementedError(self)
+ raise NotImplementedError(
+ f"{self.__class__.__name__} is not implemented for {self._cryptocurrency.NAME} {self._cryptocurrency.NETWORK} cryptocurrency."
+ )
if self._root_public_key:
data, chain_code = (
self._root_public_key[0], self._root_public_key[1]
@@ -627,9 +629,9 @@ def root_xpublic_key(self, encoded: bool = True) -> Optional[str]:
else:
secret_key, chain_code = self._i[:32], self._i[32:]
data = unhexlify(self.public_key(private_key=secret_key.hex()))
- depth = bytes(bytearray([0]))
- parent_fingerprint = b"\0\0\0\0"
- index = struct.pack(">L", 0)
+ depth = bytes(bytearray([self._root_depth]))
+ parent_fingerprint = self._root_parent_fingerprint
+ index = struct.pack(">L", self._root_index)
return self._serialize_xkeys(
_unhexlify(version), depth, parent_fingerprint, index, chain_code, data, encoded
)
@@ -652,11 +654,15 @@ def xprivate_key(self, encoded=True) -> Optional[str]:
"xprvA3BYGWQ9FmhyaNRRXB2f1LphNPnaY9T6gngw4BaTbkFtscSH4RCuJhgWUSKs9S6ciGioHd4TX4UeyUg53MkfN9Xh38xkS1j2Wb9YKsYpJHQ"
"""
+ if self._semantic is None:
+ return None
version = self._cryptocurrency.EXTENDED_PRIVATE_KEY.__getattribute__(
self._semantic.upper()
)
if version is None:
- raise NotImplementedError(self)
+ raise NotImplementedError(
+ f"{self.__class__.__name__} is not implemented for {self._cryptocurrency.NAME} {self._cryptocurrency.NETWORK} cryptocurrency."
+ )
depth = bytes(bytearray([self._depth]))
parent_fingerprint = self._parent_fingerprint
index = struct.pack(">L", self._index)
@@ -686,11 +692,15 @@ def xpublic_key(self, encoded: bool = True) -> Optional[str]:
"xpub6GAtg1w369GGnrVtdCZfNUmRvRd4wcAx41cXrZz5A5nskQmRbxX9rVzzKiRU4JruirBrfm4KQXNSU7GfqL1tzZWpZYe9Zo4xKGJYohWoQe7"
"""
+ if self._semantic is None:
+ return None
version = self._cryptocurrency.EXTENDED_PUBLIC_KEY.__getattribute__(
self._semantic.upper()
)
if version is None:
- raise NotImplementedError(self)
+ raise NotImplementedError(
+ f"{self.__class__.__name__} is not implemented for {self._cryptocurrency.NAME} {self._cryptocurrency.NETWORK} cryptocurrency."
+ )
depth = bytes(bytearray([self._depth]))
parent_fingerprint = self._parent_fingerprint
index = struct.pack(">L", self._index)
@@ -709,7 +719,7 @@ def clean_derivation(self) -> "HDWallet":
>>> from hdwallet import HDWallet
>>> from hdwallet.symbols import BTC
>>> hdwallet = HDWallet(symbol=BTC)
- >>> hdwallet.from_root_xprivate_key(root_xprivate_key="xprv9s21ZrQH143K3xPGUzpogJeKtRdjHkK6muBJo8v7rEVRzT83xJgNcLpMoJXUf9wJFKfuHR4SGvfgdShh4t9VmjjrE9usBunK3LfNna31LGF")
+ >>> hdwallet.from_xprivate_key(xprivate_key="xprv9s21ZrQH143K3xPGUzpogJeKtRdjHkK6muBJo8v7rEVRzT83xJgNcLpMoJXUf9wJFKfuHR4SGvfgdShh4t9VmjjrE9usBunK3LfNna31LGF")
>>> hdwallet.from_path(path="m/44'/0'/'0/0/0")
>>> hdwallet.path()
"m/44'/0'/'0/0/0"
@@ -719,13 +729,21 @@ def clean_derivation(self) -> "HDWallet":
None
"""
- if self._i:
- self._path, self._depth, self._parent_fingerprint, self._index = (
- "m", 0, b"\0\0\0\0", 0
+ if self._root_private_key:
+ self._path, self._path_class, self._depth, self._parent_fingerprint, self._index = (
+ "m", "m", 0, b"\0\0\0\0", 0
)
- self._private_key, self._chain_code = self._i[:32], self._i[32:]
+ self._private_key, self._chain_code = self._root_private_key
self._key = ecdsa.SigningKey.from_string(self._private_key, curve=SECP256k1)
self._verified_key = self._key.get_verifying_key()
+ elif self._root_public_key:
+ self._path, self._path_class, self._depth, self._parent_fingerprint, self._index = (
+ "m", "m", 0, b"\0\0\0\0", 0
+ )
+ self._chain_code = self._root_public_key[1]
+ self._verified_key = ecdsa.VerifyingKey.from_string(
+ self._root_public_key[0], curve=SECP256k1
+ )
return self
def uncompressed(self, compressed: Optional[str] = None) -> str:
@@ -1083,19 +1101,23 @@ def p2pkh_address(self) -> str:
if self._cryptocurrency.SYMBOL in ["ETH", "ETHTEST"]:
keccak_256 = sha3.keccak_256()
- # keccak_256.update(unhexlify(self.compressed()))
keccak_256.update(unhexlify(self.uncompressed()))
address = keccak_256.hexdigest()[24:]
return checksum_encode(address, crypto="eth")
elif self._cryptocurrency.SYMBOL in ["XDC", "XDCTEST"]:
keccak_256 = sha3.keccak_256()
- # keccak_256.update(unhexlify(self.compressed()))
keccak_256.update(unhexlify(self.uncompressed()))
address = keccak_256.hexdigest()[24:]
return checksum_encode(address, crypto="xdc")
+ elif self._cryptocurrency.SYMBOL in ["TRX"]:
+ keccak_256 = sha3.keccak_256()
+ keccak_256.update(unhexlify(self.uncompressed()))
+ address = keccak_256.hexdigest()[24:]
+ network_hash160_bytes = _unhexlify(self._cryptocurrency.PUBLIC_KEY_ADDRESS) + bytearray.fromhex(address)
+ return ensure_string(base58.b58encode_check(network_hash160_bytes))
compressed_public_key = unhexlify(self.compressed())
- public_key_hash = hashlib.new('ripemd160', sha256(compressed_public_key).digest()).digest()
+ public_key_hash = hashlib.new("ripemd160", sha256(compressed_public_key).digest()).digest()
network_hash160_bytes = _unhexlify(self._cryptocurrency.PUBLIC_KEY_ADDRESS) + public_key_hash
return ensure_string(base58.b58encode_check(network_hash160_bytes))
@@ -1115,9 +1137,9 @@ def p2sh_address(self) -> str:
"""
compressed_public_key = unhexlify(self.compressed())
- public_key_hash = hashlib.new('ripemd160', sha256(compressed_public_key).digest()).hexdigest()
+ public_key_hash = hashlib.new("ripemd160", sha256(compressed_public_key).digest()).hexdigest()
public_key_hash_script = unhexlify("76a914" + public_key_hash + "88ac")
- script_hash = hashlib.new('ripemd160', sha256(public_key_hash_script).digest()).digest()
+ script_hash = hashlib.new("ripemd160", sha256(public_key_hash_script).digest()).digest()
network_hash160_bytes = _unhexlify(self._cryptocurrency.SCRIPT_ADDRESS) + script_hash
return ensure_string(base58.b58encode_check(network_hash160_bytes))
@@ -1137,7 +1159,7 @@ def p2wpkh_address(self) -> Optional[str]:
"""
compressed_public_key = unhexlify(self.compressed())
- public_key_hash = hashlib.new('ripemd160', sha256(compressed_public_key).digest()).digest()
+ public_key_hash = hashlib.new("ripemd160", sha256(compressed_public_key).digest()).digest()
if self._cryptocurrency.SEGWIT_ADDRESS.HRP is None:
return None
return ensure_string(encode(self._cryptocurrency.SEGWIT_ADDRESS.HRP, 0, public_key_hash))
diff --git a/hdwallet/libs/base58.py b/hdwallet/libs/base58.py
index 98f0a39..c025f8b 100644
--- a/hdwallet/libs/base58.py
+++ b/hdwallet/libs/base58.py
@@ -67,15 +67,18 @@ def decode(data):
data = bytes(data, "ascii")
val = 0
- for (i, c) in enumerate(data[::-1]):
- val += __base58_alphabet_bytes.find(c) * (__base58_radix ** i)
+ prefix = 0
+ for c in data:
+ val = (val * __base58_radix) + __base58_alphabet_bytes.find(c)
+ if val == 0:
+ prefix += 1
dec = bytearray()
- while val >= 256:
+ while val > 0:
val, mod = divmod(val, 256)
dec.append(mod)
- if val:
- dec.append(val)
+
+ dec.extend(bytearray(prefix))
return bytes(dec[::-1])
diff --git a/hdwallet/symbols.py b/hdwallet/symbols.py
index e90c462..c51d469 100644
--- a/hdwallet/symbols.py
+++ b/hdwallet/symbols.py
@@ -246,6 +246,8 @@
TOA = "TOA"
# Thought AI
THT = "THT"
+# TRX
+TRX = "TRX"
# Twins
TWINS, TWINSTEST = "TWINS", "TWINSTEST"
# Ultimate Secure Cash
@@ -275,7 +277,7 @@
# ZClassic
ZCL = "ZCL"
# Zcash
-ZEC = "ZEC"
+ZEC, ZECTEST = "ZEC", "ZECTEST"
# Zencash
ZEN = "ZEN"
@@ -404,6 +406,7 @@
"SYS",
"TOA",
"THT",
+ "TRX",
"TWINS", "TWINSTEST",
"USC",
"UNO",
@@ -418,6 +421,6 @@
"XUEZ",
"XDC",
"ZCL",
- "ZEC",
+ "ZEC", "ZECTEST",
"ZEN"
]
diff --git a/hdwallet/utils.py b/hdwallet/utils.py
index 16edefa..969c55f 100644
--- a/hdwallet/utils.py
+++ b/hdwallet/utils.py
@@ -7,11 +7,14 @@
import string
import os
+import inspect
import unicodedata
import binascii
-from .exceptions import SemanticError
-from .cryptocurrencies import get_cryptocurrency
+from hdwallet import cryptocurrencies
+from .cryptocurrencies import (
+ get_cryptocurrency, Cryptocurrency
+)
from .libs.base58 import check_decode
# Alphabet and digits.
@@ -25,6 +28,20 @@ def _unhexlify(integer: int):
return unhexlify("%x" % integer)
+def get_semantic(_cryptocurrency: Cryptocurrency, version: bytes, key_type: str) -> str:
+ for name, cryptocurrency in inspect.getmembers(cryptocurrencies):
+ if inspect.isclass(cryptocurrency):
+ if issubclass(cryptocurrency, cryptocurrencies.Cryptocurrency) and cryptocurrency == _cryptocurrency:
+ if key_type == "private_key":
+ for key, value in inspect.getmembers(cryptocurrency.EXTENDED_PRIVATE_KEY):
+ if value == int(version.hex(), 16):
+ return key.lower()
+ elif key_type == "public_key":
+ for key, value in inspect.getmembers(cryptocurrency.EXTENDED_PUBLIC_KEY):
+ if value == int(version.hex(), 16):
+ return key.lower()
+
+
def get_bytes(string: AnyStr) -> bytes:
if isinstance(string, bytes):
byte = string
@@ -119,7 +136,10 @@ def is_entropy(entropy: str) -> bool:
True
"""
- return len(unhexlify(entropy)) in [16, 20, 24, 28, 32]
+ try:
+ return len(unhexlify(entropy)) in [16, 20, 24, 28, 32]
+ except:
+ return False
def is_mnemonic(mnemonic: str, language: Optional[str] = None) -> bool:
@@ -298,39 +318,31 @@ def mnemonic_to_entropy(mnemonic: str, language: Optional[str] = None) -> str:
return Mnemonic(language=language).to_entropy(mnemonic).hex()
-def is_root_xprivate_key(xprivate_key: str, symbol: str, semantic: str = "p2pkh") -> bool:
- if semantic not in ["p2pkh", "p2sh", "p2wpkh", "p2wpkh_in_p2sh", "p2wsh", "p2wsh_in_p2sh"]:
- raise SemanticError(
- "Wrong extended semantic",
- "choose only the following options 'p2pkh', 'p2sh', 'p2wpkh', 'p2wpkh_in_p2sh', 'p2wsh' or 'p2wsh_in_p2sh' semantics."
- )
- decoded_xprivate_key = check_decode(xprivate_key).hex()
- if len(decoded_xprivate_key) != 156: # 78
+def is_root_xprivate_key(xprivate_key: str, symbol: str) -> bool:
+ decoded_xprivate_key = check_decode(xprivate_key)
+ if len(decoded_xprivate_key) != 78: # 78, 156
raise ValueError("Invalid xprivate key.")
cryptocurrency = get_cryptocurrency(symbol=symbol)
+ semantic = get_semantic(_cryptocurrency=cryptocurrency, version=decoded_xprivate_key[:4], key_type="private_key")
version = cryptocurrency.EXTENDED_PRIVATE_KEY.__getattribute__(
semantic.upper()
)
if version is None:
raise NotImplementedError(semantic)
raw = f"{_unhexlify(version).hex()}000000000000000000"
- return decoded_xprivate_key.startswith(raw)
+ return decoded_xprivate_key.hex().startswith(raw)
-def is_root_xpublic_key(xpublic_key: str, symbol: str, semantic: str = "p2pkh") -> bool:
- if semantic not in ["p2pkh", "p2sh", "p2wpkh", "p2wpkh_in_p2sh", "p2wsh", "p2wsh_in_p2sh"]:
- raise SemanticError(
- "Wrong extended semantic",
- "choose only the following options 'p2pkh', 'p2sh', 'p2wpkh', 'p2wpkh_in_p2sh', 'p2wsh' or 'p2wsh_in_p2sh' semantics."
- )
- decoded_xpublic_key = check_decode(xpublic_key).hex()
- if len(decoded_xpublic_key) != 156: # 78
+def is_root_xpublic_key(xpublic_key: str, symbol: str) -> bool:
+ decoded_xpublic_key = check_decode(xpublic_key)
+ if len(decoded_xpublic_key) != 78: # 78, 156
raise ValueError("Invalid xpublic key.")
cryptocurrency = get_cryptocurrency(symbol=symbol)
+ semantic = get_semantic(_cryptocurrency=cryptocurrency, version=decoded_xpublic_key[:4], key_type="public_key")
version = cryptocurrency.EXTENDED_PUBLIC_KEY.__getattribute__(
semantic.upper()
)
if version is None:
raise NotImplementedError(semantic)
raw = f"{_unhexlify(version).hex()}000000000000000000"
- return decoded_xpublic_key.startswith(raw)
+ return decoded_xpublic_key.hex().startswith(raw)
diff --git a/setup.py b/setup.py
index 6219079..9b70d38 100644
--- a/setup.py
+++ b/setup.py
@@ -4,6 +4,12 @@
setup, find_packages
)
+# Project URLs
+project_urls = {
+ "Tracker": "https://github.com/meherett/python-hdwallet/issues",
+ "Documentation": "https://hdwallet.readthedocs.io"
+}
+
# README.md
with open("README.md", "r", encoding="utf-8") as readme:
long_description: str = readme.read()
@@ -14,27 +20,39 @@
setup(
name="hdwallet",
- version="1.3.0",
- description="Python-based library for the implementation of a "
- "hierarchical deterministic wallet generator for more than 140+ multiple cryptocurrencies.",
+ version="v2.1.1",
+ description="Python-based library for the implementation of a hierarchical deterministic wallet "
+ "generator for more than 140+ multiple cryptocurrencies.",
long_description=long_description,
long_description_content_type="text/markdown",
license="ISCL",
- author="Meheret Tesfaye",
+ author="Meheret Tesfaye Batu",
author_email="meherett@zoho.com",
url="https://github.com/meherett/python-hdwallet",
- keywords=["cryptography", "hd", "bip32", "bip44", "bip39", "wallet", "cryptocurrencies"],
+ project_urls=project_urls,
+ keywords=[
+ "cryptography", "cli", "wallet", "bip32", "bip44", "bip39", "hdwallet", "cryptocurrencies", "bitcoin", "ethereum"
+ ],
+ entry_points={
+ "console_scripts": ["hdwallet=hdwallet.cli.__main__:main"]
+ },
python_requires=">=3.6,<4",
packages=find_packages(),
install_requires=requirements,
extras_require={
+ "cli": [
+ "click>=8.0.3,<9",
+ "click-aliases>=1.0.1,<2",
+ "tabulate>=0.8.9,<1"
+ ],
"tests": [
- "pytest>=6.2.2,<7",
- "pytest-cov>=2.11.1,<3"
+ "pytest>=6.2.5,<7",
+ "pytest-cov>=3.0.0,<4"
],
"docs": [
- "sphinx>=3.5.1,<4",
- "sphinx-rtd-theme>=0.5.1,<1"
+ "sphinx>=4.4.0,<5",
+ "furo==2022.2.14.1",
+ "sphinx-click>=3.1.0,<4"
]
},
classifiers=[
@@ -44,6 +62,7 @@
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
"Topic :: Software Development :: Libraries :: Python Modules"
]
)
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..5f7ce86
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1 @@
+#!/usr/bin/env python3
\ No newline at end of file
diff --git a/tests/cli/__init__.py b/tests/cli/__init__.py
new file mode 100644
index 0000000..5f7ce86
--- /dev/null
+++ b/tests/cli/__init__.py
@@ -0,0 +1 @@
+#!/usr/bin/env python3
\ No newline at end of file
diff --git a/tests/cli/generate/__init__.py b/tests/cli/generate/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/cli/generate/test_addresses.py b/tests/cli/generate/test_addresses.py
new file mode 100644
index 0000000..52bb3b1
--- /dev/null
+++ b/tests/cli/generate/test_addresses.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+
+import json
+import os
+
+from hdwallet.cli.__main__ import main as cli_main
+
+# Test Values
+base_path = os.path.dirname(__file__)
+file_path = os.path.abspath(os.path.join(base_path, "..", "..", "values.json"))
+values = open(file_path, "r", encoding="utf-8")
+_ = json.loads(values.read())
+values.close()
+
+
+def test_addresses(cli_tester):
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "generate", "addresses",
+ "--symbol", _["bitcoin"]["mainnet"]["symbol"],
+ "--entropy", _["bitcoin"]["mainnet"]["entropy"],
+ "--passphrase", _["bitcoin"]["mainnet"]["passphrase"],
+ "--language", _["bitcoin"]["mainnet"]["language"]
+ ]
+ )
+
+ dumps: dict = _["bitcoin"]["mainnet"]
+
+ del dumps["root_xprivate_key_hex"]
+ del dumps["root_xpublic_key_hex"]
+ del dumps["xprivate_key_hex"]
+ del dumps["xpublic_key_hex"]
+
+ addresses: str = ""
+ for address in _["bitcoin"]["addresses"]:
+ addresses += f"{address}\n"
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output == addresses
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "generate", "addresses",
+ "--symbol", _["bitcoin"]["mainnet"]["symbol"],
+ "--mnemonic", _["bitcoin"]["mainnet"]["mnemonic"],
+ "--passphrase", _["bitcoin"]["mainnet"]["passphrase"],
+ "--language", _["bitcoin"]["mainnet"]["language"]
+ ]
+ )
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output == addresses
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "generate", "addresses",
+ "--symbol", _["bitcoin"]["mainnet"]["symbol"],
+ "--seed", _["bitcoin"]["mainnet"]["seed"]
+ ]
+ )
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output == addresses
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "generate", "addresses",
+ "--symbol", _["bitcoin"]["mainnet"]["symbol"],
+ "--xprivate-key", _["bitcoin"]["mainnet"]["root_xprivate_key"]
+ ]
+ )
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output == addresses
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "generate", "addresses",
+ "--symbol", _["bitcoin"]["mainnet"]["symbol"],
+ "--xpublic-key", _["bitcoin"]["mainnet"]["root_xpublic_key"]
+ ]
+ )
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output
diff --git a/tests/cli/generate/test_hdwallet.py b/tests/cli/generate/test_hdwallet.py
new file mode 100644
index 0000000..33ab587
--- /dev/null
+++ b/tests/cli/generate/test_hdwallet.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python3
+
+import json
+import os
+
+from hdwallet.cli.__main__ import main as cli_main
+
+# Test Values
+base_path = os.path.dirname(__file__)
+file_path = os.path.abspath(os.path.join(base_path, "..", "..", "values.json"))
+values = open(file_path, "r", encoding="utf-8")
+_ = json.loads(values.read())
+values.close()
+
+
+def test_hdwallet(cli_tester):
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "generate",
+ "--symbol", _["bitcoin"]["mainnet"]["symbol"],
+ "--entropy", _["bitcoin"]["mainnet"]["entropy"],
+ "--passphrase", _["bitcoin"]["mainnet"]["passphrase"],
+ "--language", _["bitcoin"]["mainnet"]["language"]
+ ]
+ )
+
+ dumps: dict = _["bitcoin"]["mainnet"]
+
+ del dumps["root_xprivate_key_hex"]
+ del dumps["root_xpublic_key_hex"]
+ del dumps["xprivate_key_hex"]
+ del dumps["xpublic_key_hex"]
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output == str(json.dumps(dumps, indent=4, ensure_ascii=False)) + "\n"
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "generate",
+ "--symbol", _["bitcoin"]["mainnet"]["symbol"],
+ "--mnemonic", _["bitcoin"]["mainnet"]["mnemonic"],
+ "--passphrase", _["bitcoin"]["mainnet"]["passphrase"],
+ "--language", _["bitcoin"]["mainnet"]["language"]
+ ]
+ )
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output == str(json.dumps(dumps, indent=4, ensure_ascii=False)) + "\n"
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "generate",
+ "--symbol", _["bitcoin"]["mainnet"]["symbol"],
+ "--seed", _["bitcoin"]["mainnet"]["seed"]
+ ]
+ )
+
+ dumps["strength"] = None
+ dumps["entropy"] = None
+ dumps["mnemonic"] = None
+ dumps["language"] = None
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output == str(json.dumps(dumps, indent=4, ensure_ascii=False)) + "\n"
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "generate",
+ "--symbol", _["bitcoin"]["mainnet"]["symbol"],
+ "--xprivate-key", _["bitcoin"]["mainnet"]["root_xprivate_key"]
+ ]
+ )
+
+ dumps["strength"] = None
+ dumps["entropy"] = None
+ dumps["mnemonic"] = None
+ dumps["language"] = None
+ dumps["seed"] = None
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output == str(json.dumps(dumps, indent=4, ensure_ascii=False)) + "\n"
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "generate",
+ "--symbol", _["bitcoin"]["mainnet"]["symbol"],
+ "--xpublic-key", _["bitcoin"]["mainnet"]["root_xpublic_key"]
+ ]
+ )
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "generate",
+ "--symbol", _["bitcoin"]["mainnet"]["symbol"],
+ "--private-key", _["bitcoin"]["mainnet"]["private_key"]
+ ]
+ )
+
+ dumps["root_xprivate_key"] = None
+ dumps["root_xpublic_key"] = None
+ dumps["xprivate_key"] = None
+ dumps["xpublic_key"] = None
+ dumps["chain_code"] = None
+ dumps["path"] = None
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output == str(json.dumps(dumps, indent=4, ensure_ascii=False)) + "\n"
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "generate",
+ "--symbol", _["bitcoin"]["mainnet"]["symbol"],
+ "--wif", _["bitcoin"]["mainnet"]["wif"]
+ ]
+ )
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output == str(json.dumps(dumps, indent=4, ensure_ascii=False)) + "\n"
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "generate",
+ "--symbol", _["bitcoin"]["mainnet"]["symbol"],
+ "--public-key", _["bitcoin"]["mainnet"]["public_key"]
+ ]
+ )
+
+ dumps["private_key"] = None
+ dumps["wif"] = None
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output == str(json.dumps(dumps, indent=4, ensure_ascii=False)) + "\n"
diff --git a/tests/cli/list/__init__.py b/tests/cli/list/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/cli/list/test_cryptocurrencies.py b/tests/cli/list/test_cryptocurrencies.py
new file mode 100644
index 0000000..e8fc25f
--- /dev/null
+++ b/tests/cli/list/test_cryptocurrencies.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+
+
+from hdwallet.cli.__main__ import main as cli_main
+
+
+def test_cryptocurrencies(cli_tester):
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "list", "cryptocurrencies"
+ ]
+ )
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "l", "cryptocurrencies"
+ ]
+ )
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "list", "c"
+ ]
+ )
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "l", "c"
+ ]
+ )
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output
diff --git a/tests/cli/list/test_languages.py b/tests/cli/list/test_languages.py
new file mode 100644
index 0000000..2c9537d
--- /dev/null
+++ b/tests/cli/list/test_languages.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+
+
+from hdwallet.cli.__main__ import main as cli_main
+
+
+def test_languages(cli_tester):
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "list", "languages"
+ ]
+ )
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "l", "languages"
+ ]
+ )
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "list", "l"
+ ]
+ )
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "l", "l"
+ ]
+ )
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output
diff --git a/tests/cli/list/test_strengths.py b/tests/cli/list/test_strengths.py
new file mode 100644
index 0000000..d3419dd
--- /dev/null
+++ b/tests/cli/list/test_strengths.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+
+
+from hdwallet.cli.__main__ import main as cli_main
+
+
+def test_strengths(cli_tester):
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "list", "strengths"
+ ]
+ )
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "l", "strengths"
+ ]
+ )
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "list", "s"
+ ]
+ )
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output
+
+ hdwallet = cli_tester.invoke(
+ cli_main, [
+ "l", "s"
+ ]
+ )
+
+ assert hdwallet.exit_code == 0
+ assert hdwallet.output
diff --git a/tests/cli/test_cli.py b/tests/cli/test_cli.py
new file mode 100644
index 0000000..adf54fb
--- /dev/null
+++ b/tests/cli/test_cli.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python3
+
+from hdwallet.cli.__main__ import main as cli_main
+from hdwallet import __version__
+
+
+def test_hdwallet_cli(cli_tester):
+
+ assert cli_tester.invoke(cli_main).exit_code == 0
+
+ assert cli_tester.invoke(cli_main, ["generate"]).exit_code == 0
+
+ version = cli_tester.invoke(cli_main, ["--version"])
+ assert version.exit_code == 0
+ assert version.output == "%s\n" % __version__
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..9c6c3d1
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,20 @@
+#!/usr/bin/python3
+
+from pathlib import Path
+from click.testing import CliRunner
+
+import os
+import pytest
+
+
+@pytest.fixture(scope="module")
+def project_path():
+ original_path = os.getcwd()
+ os.chdir(original_path + "/tests")
+ yield Path(original_path + "/tests")
+ os.chdir(original_path)
+
+
+@pytest.fixture(scope="module")
+def cli_tester():
+ return CliRunner()
diff --git a/tests/hdwallet/__init__.py b/tests/hdwallet/__init__.py
new file mode 100644
index 0000000..5f7ce86
--- /dev/null
+++ b/tests/hdwallet/__init__.py
@@ -0,0 +1 @@
+#!/usr/bin/env python3
\ No newline at end of file
diff --git a/tests/test_from_entropy.py b/tests/hdwallet/test_from_entropy.py
similarity index 98%
rename from tests/test_from_entropy.py
rename to tests/hdwallet/test_from_entropy.py
index fc50085..30a1020 100644
--- a/tests/test_from_entropy.py
+++ b/tests/hdwallet/test_from_entropy.py
@@ -7,7 +7,7 @@
# Test Values
base_path: str = os.path.dirname(__file__)
-file_path: str = os.path.abspath(os.path.join(base_path, "values.json"))
+file_path: str = os.path.abspath(os.path.join(base_path, "../values.json"))
values = open(file_path, "r", encoding="utf-8")
_: dict = json.loads(values.read())
values.close()
diff --git a/tests/test_from_mnemonic.py b/tests/hdwallet/test_from_mnemonic.py
similarity index 97%
rename from tests/test_from_mnemonic.py
rename to tests/hdwallet/test_from_mnemonic.py
index 3bfcafd..2bad1df 100644
--- a/tests/test_from_mnemonic.py
+++ b/tests/hdwallet/test_from_mnemonic.py
@@ -7,7 +7,7 @@
# Test Values
base_path: str = os.path.dirname(__file__)
-file_path: str = os.path.abspath(os.path.join(base_path, "values.json"))
+file_path: str = os.path.abspath(os.path.join(base_path, "../values.json"))
values = open(file_path, "r", encoding="utf-8")
_: dict = json.loads(values.read())
values.close()
diff --git a/tests/test_from_private_key.py b/tests/hdwallet/test_from_private_key.py
similarity index 95%
rename from tests/test_from_private_key.py
rename to tests/hdwallet/test_from_private_key.py
index fe3295d..4251a0e 100644
--- a/tests/test_from_private_key.py
+++ b/tests/hdwallet/test_from_private_key.py
@@ -7,7 +7,7 @@
# Test Values
base_path: str = os.path.dirname(__file__)
-file_path: str = os.path.abspath(os.path.join(base_path, "values.json"))
+file_path: str = os.path.abspath(os.path.join(base_path, "../values.json"))
values = open(file_path, "r", encoding="utf-8")
_: dict = json.loads(values.read())
values.close()
@@ -47,7 +47,7 @@ def test_from_private_key():
assert hdwallet.public_key() == _["bitcoin"]["mainnet"]["public_key"]
assert hdwallet.wif() == _["bitcoin"]["mainnet"]["wif"]
assert hdwallet.finger_print() == _["bitcoin"]["mainnet"]["finger_print"]
- assert hdwallet.semantic() == _["bitcoin"]["mainnet"]["semantic"]
+ assert hdwallet.semantic() is None
assert hdwallet.path() == None
assert hdwallet.hash() == _["bitcoin"]["mainnet"]["hash"]
assert hdwallet.p2pkh_address() == _["bitcoin"]["mainnet"]["addresses"]["p2pkh"]
@@ -71,6 +71,7 @@ def test_from_private_key():
dumps["root_xpublic_key"] = None
dumps["xprivate_key"] = None
dumps["xpublic_key"] = None
+ dumps["semantic"] = None
dumps["chain_code"] = None
dumps["path"] = None
del dumps["root_xprivate_key_hex"]
diff --git a/tests/test_from_public_key.py b/tests/hdwallet/test_from_public_key.py
similarity index 96%
rename from tests/test_from_public_key.py
rename to tests/hdwallet/test_from_public_key.py
index 3c75696..214857f 100644
--- a/tests/test_from_public_key.py
+++ b/tests/hdwallet/test_from_public_key.py
@@ -7,7 +7,7 @@
# Test Values
base_path: str = os.path.dirname(__file__)
-file_path: str = os.path.abspath(os.path.join(base_path, "values.json"))
+file_path: str = os.path.abspath(os.path.join(base_path, "../values.json"))
values = open(file_path, "r", encoding="utf-8")
_: dict = json.loads(values.read())
values.close()
@@ -50,7 +50,7 @@ def test_from_public_key():
assert hdwallet.public_key(compressed=False, private_key=_["bitcoin"]["testnet"]["private_key"]) == _["bitcoin"]["testnet"]["uncompressed"]
assert hdwallet.wif() is None
assert hdwallet.finger_print() == _["bitcoin"]["testnet"]["finger_print"]
- assert hdwallet.semantic() == _["bitcoin"]["testnet"]["semantic"]
+ assert hdwallet.semantic() is None
assert hdwallet.path() == None
assert hdwallet.hash() == _["bitcoin"]["testnet"]["hash"]
assert hdwallet.p2pkh_address() == _["bitcoin"]["testnet"]["addresses"]["p2pkh"]
@@ -74,6 +74,7 @@ def test_from_public_key():
dumps["root_xpublic_key"] = None
dumps["xprivate_key"] = None
dumps["xpublic_key"] = None
+ dumps["semantic"] = None
dumps["chain_code"] = None
dumps["private_key"] = None
dumps["wif"] = None
diff --git a/tests/test_from_seed.py b/tests/hdwallet/test_from_seed.py
similarity index 97%
rename from tests/test_from_seed.py
rename to tests/hdwallet/test_from_seed.py
index a85a76e..3eeeb10 100644
--- a/tests/test_from_seed.py
+++ b/tests/hdwallet/test_from_seed.py
@@ -7,7 +7,7 @@
# Test Values
base_path: str = os.path.dirname(__file__)
-file_path: str = os.path.abspath(os.path.join(base_path, "values.json"))
+file_path: str = os.path.abspath(os.path.join(base_path, "../values.json"))
values = open(file_path, "r", encoding="utf-8")
_: dict = json.loads(values.read())
values.close()
diff --git a/tests/test_from_wif.py b/tests/hdwallet/test_from_wif.py
similarity index 95%
rename from tests/test_from_wif.py
rename to tests/hdwallet/test_from_wif.py
index e4d666c..8a4d3a0 100644
--- a/tests/test_from_wif.py
+++ b/tests/hdwallet/test_from_wif.py
@@ -7,7 +7,7 @@
# Test Values
base_path: str = os.path.dirname(__file__)
-file_path: str = os.path.abspath(os.path.join(base_path, "values.json"))
+file_path: str = os.path.abspath(os.path.join(base_path, "../values.json"))
values = open(file_path, "r", encoding="utf-8")
_: dict = json.loads(values.read())
values.close()
@@ -47,7 +47,7 @@ def test_from_wallet_important_format():
assert hdwallet.public_key() == _["bitcoin"]["mainnet"]["public_key"]
assert hdwallet.wif() == _["bitcoin"]["mainnet"]["wif"]
assert hdwallet.finger_print() == _["bitcoin"]["mainnet"]["finger_print"]
- assert hdwallet.semantic() == _["bitcoin"]["mainnet"]["semantic"]
+ assert hdwallet.semantic() is None
assert hdwallet.path() == None
assert hdwallet.hash() == _["bitcoin"]["mainnet"]["hash"]
assert hdwallet.p2pkh_address() == _["bitcoin"]["mainnet"]["addresses"]["p2pkh"]
@@ -71,6 +71,7 @@ def test_from_wallet_important_format():
dumps["root_xpublic_key"] = None
dumps["xprivate_key"] = None
dumps["xpublic_key"] = None
+ dumps["semantic"] = None
dumps["chain_code"] = None
dumps["path"] = None
del dumps["root_xprivate_key_hex"]
diff --git a/tests/test_from_root_xprivate_key.py b/tests/hdwallet/test_from_xprivate_key.py
similarity index 50%
rename from tests/test_from_root_xprivate_key.py
rename to tests/hdwallet/test_from_xprivate_key.py
index 043a051..17a4b68 100644
--- a/tests/test_from_root_xprivate_key.py
+++ b/tests/hdwallet/test_from_xprivate_key.py
@@ -7,18 +7,18 @@
# Test Values
base_path: str = os.path.dirname(__file__)
-file_path: str = os.path.abspath(os.path.join(base_path, "values.json"))
+file_path: str = os.path.abspath(os.path.join(base_path, "../values.json"))
values = open(file_path, "r", encoding="utf-8")
_: dict = json.loads(values.read())
values.close()
-def test_from_root_xprivate_key():
+def test_from_xprivate_key():
hdwallet: HDWallet = HDWallet(
symbol=_["bitcoin"]["mainnet"]["symbol"]
)
- hdwallet.from_root_xprivate_key(
+ hdwallet.from_xprivate_key(
xprivate_key=_["bitcoin"]["mainnet"]["root_xprivate_key"], strict=True
)
hdwallet.from_path(
@@ -75,3 +75,65 @@ def test_from_root_xprivate_key():
del dumps["xpublic_key_hex"]
assert hdwallet.dumps() == dumps
+
+ hdwallet: HDWallet = HDWallet(
+ symbol=_["bitcoin"]["testnet"]["symbol"]
+ )
+
+ hdwallet.from_xprivate_key(
+ xprivate_key=_["bitcoin"]["testnet"]["xprivate_key"], strict=False
+ )
+
+ assert hdwallet.cryptocurrency() == _["bitcoin"]["testnet"]["cryptocurrency"]
+ assert hdwallet.symbol() == _["bitcoin"]["testnet"]["symbol"]
+ assert hdwallet.network() == _["bitcoin"]["testnet"]["network"]
+ assert hdwallet.strength() is None
+ assert hdwallet.entropy() is None
+ assert hdwallet.mnemonic() is None
+ assert hdwallet.language() is None
+ assert hdwallet.passphrase() is None
+ assert hdwallet.seed() is None
+ assert hdwallet.root_xprivate_key(encoded=False) == _["bitcoin"]["testnet"]["xprivate_key_hex"]
+ assert hdwallet.root_xprivate_key() == _["bitcoin"]["testnet"]["xprivate_key"]
+ assert hdwallet.root_xpublic_key(encoded=False) == _["bitcoin"]["testnet"]["xpublic_key_hex"]
+ assert hdwallet.root_xpublic_key() == _["bitcoin"]["testnet"]["xpublic_key"]
+ assert hdwallet.xprivate_key(encoded=False) == _["bitcoin"]["testnet"]["xprivate_key_hex"]
+ assert hdwallet.xprivate_key() == _["bitcoin"]["testnet"]["xprivate_key"]
+ assert hdwallet.xpublic_key(encoded=False) == _["bitcoin"]["testnet"]["xpublic_key_hex"]
+ assert hdwallet.xpublic_key() == _["bitcoin"]["testnet"]["xpublic_key"]
+ assert hdwallet.uncompressed() == _["bitcoin"]["testnet"]["uncompressed"]
+ assert hdwallet.compressed() == _["bitcoin"]["testnet"]["compressed"]
+ assert hdwallet.chain_code() == _["bitcoin"]["testnet"]["chain_code"]
+ assert hdwallet.private_key() == _["bitcoin"]["testnet"]["private_key"]
+ assert hdwallet.public_key() == _["bitcoin"]["testnet"]["public_key"]
+ assert hdwallet.wif() == _["bitcoin"]["testnet"]["wif"]
+ assert hdwallet.finger_print() == _["bitcoin"]["testnet"]["finger_print"]
+ assert hdwallet.semantic() == _["bitcoin"]["testnet"]["semantic"]
+ assert hdwallet.path() == None
+ assert hdwallet.hash() == _["bitcoin"]["testnet"]["hash"]
+ assert hdwallet.p2pkh_address() == _["bitcoin"]["testnet"]["addresses"]["p2pkh"]
+ assert hdwallet.p2sh_address() == _["bitcoin"]["testnet"]["addresses"]["p2sh"]
+ assert hdwallet.p2wpkh_address() == _["bitcoin"]["testnet"]["addresses"]["p2wpkh"]
+ assert hdwallet.p2wpkh_in_p2sh_address() == _["bitcoin"]["testnet"]["addresses"]["p2wpkh_in_p2sh"]
+ assert hdwallet.p2wsh_address() == _["bitcoin"]["testnet"]["addresses"]["p2wsh"]
+ assert hdwallet.p2wsh_in_p2sh_address() == _["bitcoin"]["testnet"]["addresses"]["p2wsh_in_p2sh"]
+
+ assert isinstance(hdwallet.dumps(), dict)
+
+ dumps: dict = _["bitcoin"]["testnet"]
+
+ dumps["strength"] = None
+ dumps["entropy"] = None
+ dumps["mnemonic"] = None
+ dumps["language"] = None
+ dumps["passphrase"] = None
+ dumps["seed"] = None
+ dumps["path"] = None
+ dumps["root_xprivate_key"] = _["bitcoin"]["testnet"]["xprivate_key"]
+ dumps["root_xpublic_key"] = _["bitcoin"]["testnet"]["xpublic_key"]
+ del dumps["root_xprivate_key_hex"]
+ del dumps["root_xpublic_key_hex"]
+ del dumps["xprivate_key_hex"]
+ del dumps["xpublic_key_hex"]
+
+ assert hdwallet.dumps() == dumps
diff --git a/tests/hdwallet/test_from_xpublic_key.py b/tests/hdwallet/test_from_xpublic_key.py
new file mode 100644
index 0000000..940ef06
--- /dev/null
+++ b/tests/hdwallet/test_from_xpublic_key.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+
+import json
+import os
+
+from hdwallet import HDWallet
+
+# Test Values
+base_path: str = os.path.dirname(__file__)
+file_path: str = os.path.abspath(os.path.join(base_path, "../values.json"))
+values = open(file_path, "r", encoding="utf-8")
+_: dict = json.loads(values.read())
+values.close()
+
+
+def test_from_xpublic_key():
+
+ hdwallet: HDWallet = HDWallet(
+ symbol=_["bitcoin"]["mainnet"]["symbol"]
+ )
+ # account_extended_xpublic_key path m/44'/0'/0'
+ hdwallet.from_xpublic_key(
+ xpublic_key=_["bitcoin"]["mainnet"]["account_extended_xpublic_key"]
+ )
+ # the xpublic_key path m/44'/0'/0'/0/0
+ hdwallet.from_path(
+ path="m/0/0"
+ )
+
+ assert hdwallet.cryptocurrency() == _["bitcoin"]["mainnet"]["cryptocurrency"]
+ assert hdwallet.symbol() == _["bitcoin"]["mainnet"]["symbol"]
+ assert hdwallet.network() == _["bitcoin"]["mainnet"]["network"]
+ # root_xpublic_key see hdwallet.from_xpublic_key(first_from_xpublic_key)
+ assert hdwallet.root_xpublic_key() == _["bitcoin"]["mainnet"]["account_extended_xpublic_key"]
+
+ assert hdwallet.public_key() == _["bitcoin"]["mainnet"]["public_key"]
+ assert hdwallet.p2pkh_address() == _["bitcoin"]["mainnet"]["addresses"]["p2pkh"]
+ assert hdwallet.p2sh_address() == _["bitcoin"]["mainnet"]["addresses"]["p2sh"]
+ assert hdwallet.p2wpkh_address() == _["bitcoin"]["mainnet"]["addresses"]["p2wpkh"]
+ assert hdwallet.p2wpkh_in_p2sh_address() == _["bitcoin"]["mainnet"]["addresses"]["p2wpkh_in_p2sh"]
+ assert hdwallet.p2wsh_address() == _["bitcoin"]["mainnet"]["addresses"]["p2wsh"]
+ assert hdwallet.p2wsh_in_p2sh_address() == _["bitcoin"]["mainnet"]["addresses"]["p2wsh_in_p2sh"]
+
+ assert isinstance(hdwallet.dumps(), dict)
+
diff --git a/tests/test_base58.py b/tests/test_base58.py
index 2b602c0..f321e76 100644
--- a/tests/test_base58.py
+++ b/tests/test_base58.py
@@ -7,7 +7,7 @@
import pytest
from hdwallet.libs.base58 import (
- check_encode, check_decode, string_to_int
+ check_encode, check_decode, decode, encode, string_to_int
)
@@ -29,3 +29,9 @@ def test_base58():
with pytest.raises(TypeError, match="string argument without an encoding"):
assert string_to_int(str("meherett"))
+
+
+ assert decode("111233QC4") == b'\x00\x00\x00(\x7f\xb4\xcd'
+
+
+ assert encode(decode("111233QC4")) == "111233QC4"
diff --git a/tests/test_derivations.py b/tests/test_derivations.py
new file mode 100644
index 0000000..07bd1be
--- /dev/null
+++ b/tests/test_derivations.py
@@ -0,0 +1,169 @@
+#!/usr/bin/env python3
+
+import pytest
+
+from hdwallet.cryptocurrencies import BitcoinMainnet
+from hdwallet.derivations import (
+ Derivation, BIP32Derivation, BIP44Derivation, BIP49Derivation, BIP84Derivation, BIP141Derivation
+)
+from hdwallet.exceptions import SemanticError
+
+
+def test_derivations():
+
+ assert str(
+ Derivation(path="m/44'/0'/0'/0/0", semantic="p2pkh")
+ ) == "m/44'/0'/0'/0/0"
+ assert str(
+ Derivation(semantic="p2pkh").from_path(
+ path="m/44'/0'/0'/0/0"
+ )
+ ) == "m/44'/0'/0'/0/0"
+ assert str(
+ Derivation(semantic="p2pkh").from_index(
+ index=44, hardened=True
+ ).from_index(
+ index=0, hardened=True
+ ).from_index(
+ index=0, hardened=True
+ ).from_index(
+ index=0, hardened=False
+ ).from_index(
+ index=0, hardened=False
+ )
+ ) == "m/44'/0'/0'/0/0"
+ assert str(
+ Derivation(path="m/44'/0'/0'/0/0", semantic="p2pkh").clean_derivation()
+ ) == "\0\0\0\0"
+
+ assert str(
+ BIP32Derivation(purpose=44, coin_type=0, account=0, change=False, address=0)
+ ) == "m/44'/0'/0'/0/0"
+ assert str(
+ BIP32Derivation(purpose=(44, False), coin_type=(0, False), account=0, change=False, address=0)
+ ) == "m/44/0/0'/0/0"
+
+ bip32_derivation: BIP32Derivation = BIP32Derivation().from_purpose(
+ purpose=44, hardened=True
+ ).from_coin_type(
+ coin_type=0, hardened=True
+ ).from_account(
+ account=0, hardened=True
+ ).from_change(
+ change=False
+ ).from_address(
+ address=0, hardened=False
+ )
+ assert str(bip32_derivation) == "m/44'/0'/0'/0/0"
+ assert bip32_derivation.purpose() == "44'"
+ assert bip32_derivation.coin_type() == "0'"
+ assert bip32_derivation.account() == "0'"
+ assert bip32_derivation.change() == 0
+ assert bip32_derivation.address() == "0"
+ assert bip32_derivation.SEMANTIC == "p2pkh"
+
+ bip32_derivation: BIP32Derivation = BIP32Derivation(
+ purpose=(44, True), coin_type=(0, True), account=(0, True), change=False, address=(0, False)
+ )
+ assert str(bip32_derivation) == "m/44'/0'/0'/0/0"
+ assert bip32_derivation.purpose() == "44'"
+ assert bip32_derivation.coin_type() == "0'"
+ assert bip32_derivation.account() == "0'"
+ assert bip32_derivation.change() == 0
+ assert bip32_derivation.address() == "0"
+ assert bip32_derivation.SEMANTIC == "p2pkh"
+
+ bip44_derivation: BIP44Derivation = BIP44Derivation(
+ cryptocurrency=BitcoinMainnet
+ ).from_account(
+ account=0, hardened=True
+ ).from_change(
+ change=False
+ ).from_address(
+ address=0, hardened=False
+ )
+ assert str(bip44_derivation) == "m/44'/0'/0'/0/0"
+ assert bip44_derivation.purpose() == "44'"
+ assert bip44_derivation.coin_type() == "0'"
+ assert bip44_derivation.account() == "0'"
+ assert bip44_derivation.change() == 0
+ assert bip44_derivation.address() == "0"
+ assert bip44_derivation.SEMANTIC == "p2pkh"
+
+ bip44_derivation: BIP44Derivation = BIP44Derivation(
+ cryptocurrency=BitcoinMainnet, account=(0, True), change=False, address=(0, False)
+ )
+ assert str(bip44_derivation) == "m/44'/0'/0'/0/0"
+ assert bip44_derivation.purpose() == "44'"
+ assert bip44_derivation.coin_type() == "0'"
+ assert bip44_derivation.account() == "0'"
+ assert bip44_derivation.change() == 0
+ assert bip44_derivation.address() == "0"
+ assert bip44_derivation.SEMANTIC == "p2pkh"
+
+ bip49_derivation: BIP49Derivation = BIP49Derivation(
+ cryptocurrency=BitcoinMainnet
+ ).from_account(
+ account=0, hardened=True
+ ).from_change(
+ change=False
+ ).from_address(
+ address=0, hardened=False
+ )
+ assert str(bip49_derivation) == "m/49'/0'/0'/0/0"
+ assert bip49_derivation.purpose() == "49'"
+ assert bip49_derivation.coin_type() == "0'"
+ assert bip49_derivation.account() == "0'"
+ assert bip49_derivation.change() == 0
+ assert bip49_derivation.address() == "0"
+ assert bip49_derivation.SEMANTIC == "p2wpkh_in_p2sh"
+
+ bip49_derivation: BIP49Derivation = BIP49Derivation(
+ cryptocurrency=BitcoinMainnet, account=(0, True), change=False, address=(0, False)
+ )
+ assert str(bip49_derivation) == "m/49'/0'/0'/0/0"
+ assert bip49_derivation.purpose() == "49'"
+ assert bip49_derivation.coin_type() == "0'"
+ assert bip49_derivation.account() == "0'"
+ assert bip49_derivation.change() == 0
+ assert bip49_derivation.address() == "0"
+ assert bip49_derivation.SEMANTIC == "p2wpkh_in_p2sh"
+
+ bip84_derivation: BIP84Derivation = BIP84Derivation(
+ cryptocurrency=BitcoinMainnet
+ ).from_account(
+ account=0, hardened=True
+ ).from_change(
+ change=False
+ ).from_address(
+ address=0, hardened=False
+ )
+ assert str(bip84_derivation) == "m/84'/0'/0'/0/0"
+ assert bip84_derivation.purpose() == "84'"
+ assert bip84_derivation.coin_type() == "0'"
+ assert bip84_derivation.account() == "0'"
+ assert bip84_derivation.change() == 0
+ assert bip84_derivation.address() == "0"
+ assert bip84_derivation.SEMANTIC == "p2wpkh"
+
+ bip84_derivation: BIP84Derivation = BIP84Derivation(
+ cryptocurrency=BitcoinMainnet, account=(0, True), change=False, address=(0, False)
+ )
+ assert str(bip84_derivation) == "m/84'/0'/0'/0/0"
+ assert bip84_derivation.purpose() == "84'"
+ assert bip84_derivation.coin_type() == "0'"
+ assert bip84_derivation.account() == "0'"
+ assert bip84_derivation.change() == 0
+ assert bip84_derivation.address() == "0"
+ assert bip84_derivation.SEMANTIC == "p2wpkh"
+
+ bip141_derivation: BIP141Derivation = BIP141Derivation(
+ cryptocurrency=BitcoinMainnet, semantic="p2wsh_in_p2sh"
+ )
+ assert str(bip141_derivation) == "m/44'/0'/0'/0/0"
+ assert bip141_derivation.SEMANTIC == "p2wsh_in_p2sh"
+
+ with pytest.raises(SemanticError, match="Wrong extended semantic, choose only the following options 'p2wpkh', 'p2wpkh_in_p2sh', 'p2wsh' or 'p2wsh_in_p2sh' semantics."):
+ BIP141Derivation(
+ cryptocurrency=BitcoinMainnet, semantic="p2pkh"
+ )
diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py
new file mode 100644
index 0000000..8dd2699
--- /dev/null
+++ b/tests/test_exceptions.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python3
+
+from hdwallet.exceptions import (
+ NetworkError, DerivationError, SemanticError, AddressError, SymbolError
+)
+
+import pytest
+
+
+def test_exceptions():
+
+ with pytest.raises(NetworkError, match="error"):
+ raise NetworkError("error")
+ with pytest.raises(NetworkError, match="error, error"):
+ raise NetworkError("error", "error")
+ with pytest.raises(DerivationError, match="error"):
+ raise DerivationError("error")
+ with pytest.raises(DerivationError, match="error, error"):
+ raise DerivationError("error", "error")
+ with pytest.raises(SemanticError, match="error"):
+ raise SemanticError("error")
+ with pytest.raises(SemanticError):
+ raise SemanticError("error", "error")
+ with pytest.raises(AddressError, match="error"):
+ raise AddressError("error")
+ with pytest.raises(AddressError, match="error, error"):
+ raise AddressError("error", "error")
+ with pytest.raises(SymbolError, match="error"):
+ raise SymbolError("error")
+ with pytest.raises(SymbolError, match="error, error"):
+ raise SymbolError("error", "error")
diff --git a/tests/test_from_root_xpublic_key.py b/tests/test_from_root_xpublic_key.py
deleted file mode 100644
index 6d5892c..0000000
--- a/tests/test_from_root_xpublic_key.py
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/usr/bin/env python3
-
-import json
-import os
-
-from hdwallet import HDWallet
-
-# Test Values
-base_path: str = os.path.dirname(__file__)
-file_path: str = os.path.abspath(os.path.join(base_path, "values.json"))
-values = open(file_path, "r", encoding="utf-8")
-_: dict = json.loads(values.read())
-values.close()
-
-
-def test_from_root_xpublic_key():
-
- hdwallet: HDWallet = HDWallet(
- symbol=_["bitcoin"]["mainnet"]["symbol"]
- )
- hdwallet.from_root_xpublic_key(
- xpublic_key=_["bitcoin"]["mainnet"]["root_xpublic_key"], strict=True
- )
- hdwallet.from_path(
- path="m/44/0/0/0/0"
- )
-
- assert hdwallet.cryptocurrency() == _["bitcoin"]["mainnet"]["cryptocurrency"]
- assert hdwallet.symbol() == _["bitcoin"]["mainnet"]["symbol"]
- assert hdwallet.network() == _["bitcoin"]["mainnet"]["network"]
- assert hdwallet.strength() is None
- assert hdwallet.entropy() is None
- assert hdwallet.mnemonic() is None
- assert hdwallet.language() is None
- assert hdwallet.passphrase() is None
- assert hdwallet.seed() is None
- assert hdwallet.root_xprivate_key(encoded=False) is None
- assert hdwallet.root_xprivate_key() is None
- assert hdwallet.root_xpublic_key(encoded=False) == "0488b21e000000000000000000ad41ef910cdcae932cb4060777b4284ee38f5b29c5fb60fda8416f298a14702c02949b9f64223e124eb9a8383fba0b21b5845fcfbdc84dec7692d21c716410eab0"
- assert hdwallet.root_xpublic_key() == "xpub661MyMwAqRbcGGUtsoFw2d6ARvD2ABd7z327zxt2XiBBwMx9GAuNrrE7tbRuWF5MjjZ1BzDsRdaSHc9nVKAgHzQrv6pwYW3Hd7LSzbh8sWS"
- assert hdwallet.xprivate_key(encoded=False) is None
- assert hdwallet.xprivate_key() is None
- assert hdwallet.xpublic_key(encoded=False) == "0488b21e052c0269af000000006c95c19e932b9e8f3d834e874526768ca1b3d89933ad71fd8253bcca67ac283d038f24175db513b40c75503c25040e5f0ea4d38e912d1f83daf5fd8c4b9512ad87"
- assert hdwallet.xpublic_key() == "xpub6FjoSaU1JaG6fC6wTYmb1mJzaZxSunxASN7nTRHhFynh33gKRfmmNrtQ82s8YouLCrEniskjumfACiiTyVmi4aXyLL8HvLdZc8mjKsbzT9z"
- assert hdwallet.uncompressed() == "8f24175db513b40c75503c25040e5f0ea4d38e912d1f83daf5fd8c4b9512ad8750a64d9e0ee3555225e4130c7e36a443ec20330bf0be1e4de913e31e00202993"
- assert hdwallet.compressed() == "038f24175db513b40c75503c25040e5f0ea4d38e912d1f83daf5fd8c4b9512ad87"
- assert hdwallet.chain_code() == "6c95c19e932b9e8f3d834e874526768ca1b3d89933ad71fd8253bcca67ac283d"
- assert hdwallet.private_key() is None
- assert hdwallet.public_key() == "038f24175db513b40c75503c25040e5f0ea4d38e912d1f83daf5fd8c4b9512ad87"
- assert hdwallet.wif() is None
- assert hdwallet.finger_print() == "4e749a26"
- assert hdwallet.semantic() == "p2pkh"
- assert hdwallet.path() == "m/44/0/0/0/0"
- assert hdwallet.hash() == "4e749a26934bca5091a05ee6f55e7d0e21482647"
- assert hdwallet.p2pkh_address() == "189qPd6J81ns9LEGx6kun7Xtg1bJV8GJXh"
- assert hdwallet.p2sh_address() == "3C71bNRojv3Gc7zHvWygas4AFt34rKezcF"
- assert hdwallet.p2wpkh_address() == "bc1qfe6f5f5nf099pydqtmn02hnapcs5sfj86dpqjm"
- assert hdwallet.p2wpkh_in_p2sh_address() == "3NykoodgJ7Li43JPt5xsezQz8xfwwwFZUs"
- assert hdwallet.p2wsh_address() == "bc1qazm6kznlgs06exh4cq2qxh567xrffppwujje5zg84upnng4essusd08nhz"
- assert hdwallet.p2wsh_in_p2sh_address() == "32yGj8ncXBBTjXqg188ZHxd1xffoQDcjin"
-
- assert isinstance(hdwallet.dumps(), dict)
diff --git a/tests/test_from_xprivate_key.py b/tests/test_from_xprivate_key.py
deleted file mode 100644
index 939c717..0000000
--- a/tests/test_from_xprivate_key.py
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/usr/bin/env python3
-
-import json
-import os
-
-from hdwallet import HDWallet
-
-# Test Values
-base_path: str = os.path.dirname(__file__)
-file_path: str = os.path.abspath(os.path.join(base_path, "values.json"))
-values = open(file_path, "r", encoding="utf-8")
-_: dict = json.loads(values.read())
-values.close()
-
-
-def test_from_xprivate_key():
-
- hdwallet: HDWallet = HDWallet(
- symbol=_["bitcoin"]["testnet"]["symbol"]
- )
-
- hdwallet.from_xprivate_key(
- xprivate_key=_["bitcoin"]["testnet"]["xprivate_key"]
- )
-
- assert hdwallet.cryptocurrency() == _["bitcoin"]["testnet"]["cryptocurrency"]
- assert hdwallet.symbol() == _["bitcoin"]["testnet"]["symbol"]
- assert hdwallet.network() == _["bitcoin"]["testnet"]["network"]
- assert hdwallet.strength() is None
- assert hdwallet.entropy() is None
- assert hdwallet.mnemonic() is None
- assert hdwallet.language() is None
- assert hdwallet.passphrase() is None
- assert hdwallet.seed() is None
- assert hdwallet.root_xprivate_key(encoded=False) is None
- assert hdwallet.root_xprivate_key() is None
- assert hdwallet.root_xpublic_key(encoded=False) is None
- assert hdwallet.root_xpublic_key() is None
- assert hdwallet.xprivate_key(encoded=False) == _["bitcoin"]["testnet"]["xprivate_key_hex"]
- assert hdwallet.xprivate_key() == _["bitcoin"]["testnet"]["xprivate_key"]
- assert hdwallet.xpublic_key(encoded=False) == _["bitcoin"]["testnet"]["xpublic_key_hex"]
- assert hdwallet.xpublic_key() == _["bitcoin"]["testnet"]["xpublic_key"]
- assert hdwallet.uncompressed() == _["bitcoin"]["testnet"]["uncompressed"]
- assert hdwallet.compressed() == _["bitcoin"]["testnet"]["compressed"]
- assert hdwallet.chain_code() == _["bitcoin"]["testnet"]["chain_code"]
- assert hdwallet.private_key() == _["bitcoin"]["testnet"]["private_key"]
- assert hdwallet.public_key() == _["bitcoin"]["testnet"]["public_key"]
- assert hdwallet.wif() == _["bitcoin"]["testnet"]["wif"]
- assert hdwallet.finger_print() == _["bitcoin"]["testnet"]["finger_print"]
- assert hdwallet.semantic() == _["bitcoin"]["testnet"]["semantic"]
- assert hdwallet.path() == None
- assert hdwallet.hash() == _["bitcoin"]["testnet"]["hash"]
- assert hdwallet.p2pkh_address() == _["bitcoin"]["testnet"]["addresses"]["p2pkh"]
- assert hdwallet.p2sh_address() == _["bitcoin"]["testnet"]["addresses"]["p2sh"]
- assert hdwallet.p2wpkh_address() == _["bitcoin"]["testnet"]["addresses"]["p2wpkh"]
- assert hdwallet.p2wpkh_in_p2sh_address() == _["bitcoin"]["testnet"]["addresses"]["p2wpkh_in_p2sh"]
- assert hdwallet.p2wsh_address() == _["bitcoin"]["testnet"]["addresses"]["p2wsh"]
- assert hdwallet.p2wsh_in_p2sh_address() == _["bitcoin"]["testnet"]["addresses"]["p2wsh_in_p2sh"]
-
- assert isinstance(hdwallet.dumps(), dict)
-
- dumps: dict = _["bitcoin"]["testnet"]
-
- dumps["strength"] = None
- dumps["entropy"] = None
- dumps["mnemonic"] = None
- dumps["language"] = None
- dumps["passphrase"] = None
- dumps["seed"] = None
- dumps["root_xprivate_key"] = None
- dumps["root_xpublic_key"] = None
- dumps["path"] = None
- del dumps["root_xprivate_key_hex"]
- del dumps["root_xpublic_key_hex"]
- del dumps["xprivate_key_hex"]
- del dumps["xpublic_key_hex"]
-
- assert hdwallet.dumps() == dumps
diff --git a/tests/test_from_xpublic_key.py b/tests/test_from_xpublic_key.py
deleted file mode 100644
index d8b9055..0000000
--- a/tests/test_from_xpublic_key.py
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/usr/bin/env python3
-
-import json
-import os
-
-from hdwallet import HDWallet
-
-# Test Values
-base_path: str = os.path.dirname(__file__)
-file_path: str = os.path.abspath(os.path.join(base_path, "values.json"))
-values = open(file_path, "r", encoding="utf-8")
-_: dict = json.loads(values.read())
-values.close()
-
-
-def test_from_xpublic_key():
-
- hdwallet: HDWallet = HDWallet(
- symbol=_["bitcoin"]["testnet"]["symbol"]
- )
-
- hdwallet.from_xpublic_key(
- xpublic_key=_["bitcoin"]["testnet"]["xpublic_key"]
- )
-
- assert hdwallet.cryptocurrency() == _["bitcoin"]["testnet"]["cryptocurrency"]
- assert hdwallet.symbol() == _["bitcoin"]["testnet"]["symbol"]
- assert hdwallet.network() == _["bitcoin"]["testnet"]["network"]
- assert hdwallet.strength() is None
- assert hdwallet.entropy() is None
- assert hdwallet.mnemonic() is None
- assert hdwallet.language() is None
- assert hdwallet.passphrase() is None
- assert hdwallet.seed() is None
- assert hdwallet.root_xprivate_key(encoded=False) is None
- assert hdwallet.root_xprivate_key() is None
- assert hdwallet.root_xpublic_key(encoded=False) is None
- assert hdwallet.root_xpublic_key() is None
- assert hdwallet.xprivate_key(encoded=False) is None
- assert hdwallet.xprivate_key() is None
- assert hdwallet.xpublic_key(encoded=False) == _["bitcoin"]["testnet"]["xpublic_key_hex"]
- assert hdwallet.xpublic_key() == _["bitcoin"]["testnet"]["xpublic_key"]
- assert hdwallet.uncompressed() == _["bitcoin"]["testnet"]["uncompressed"]
- assert hdwallet.uncompressed(compressed=_["bitcoin"]["testnet"]["compressed"]) == _["bitcoin"]["testnet"]["uncompressed"]
- assert hdwallet.compressed() == _["bitcoin"]["testnet"]["compressed"]
- assert hdwallet.compressed(uncompressed=_["bitcoin"]["testnet"]["uncompressed"]) == _["bitcoin"]["testnet"]["compressed"]
- assert hdwallet.chain_code() == _["bitcoin"]["testnet"]["chain_code"]
- assert hdwallet.private_key() is None
- assert hdwallet.public_key() == _["bitcoin"]["testnet"]["public_key"]
- assert hdwallet.wif() is None
- assert hdwallet.finger_print() == _["bitcoin"]["testnet"]["finger_print"]
- assert hdwallet.semantic() == _["bitcoin"]["testnet"]["semantic"]
- assert hdwallet.path() == None
- assert hdwallet.hash() == _["bitcoin"]["testnet"]["hash"]
- assert hdwallet.p2pkh_address() == _["bitcoin"]["testnet"]["addresses"]["p2pkh"]
- assert hdwallet.p2sh_address() == _["bitcoin"]["testnet"]["addresses"]["p2sh"]
- assert hdwallet.p2wpkh_address() == _["bitcoin"]["testnet"]["addresses"]["p2wpkh"]
- assert hdwallet.p2wpkh_in_p2sh_address() == _["bitcoin"]["testnet"]["addresses"]["p2wpkh_in_p2sh"]
- assert hdwallet.p2wsh_address() == _["bitcoin"]["testnet"]["addresses"]["p2wsh"]
- assert hdwallet.p2wsh_in_p2sh_address() == _["bitcoin"]["testnet"]["addresses"]["p2wsh_in_p2sh"]
-
- assert isinstance(hdwallet.dumps(), dict)
-
- dumps: dict = _["bitcoin"]["testnet"]
-
- dumps["strength"] = None
- dumps["entropy"] = None
- dumps["mnemonic"] = None
- dumps["language"] = None
- dumps["passphrase"] = None
- dumps["seed"] = None
- dumps["root_xprivate_key"] = None
- dumps["root_xpublic_key"] = None
- dumps["xprivate_key"] = None
- dumps["private_key"] = None
- dumps["wif"] = None
- dumps["path"] = None
- del dumps["root_xprivate_key_hex"]
- del dumps["root_xpublic_key_hex"]
- del dumps["xprivate_key_hex"]
- del dumps["xpublic_key_hex"]
-
- assert hdwallet.dumps() == dumps
diff --git a/tests/test_symbols.py b/tests/test_symbols.py
new file mode 100644
index 0000000..4d9236d
--- /dev/null
+++ b/tests/test_symbols.py
@@ -0,0 +1,165 @@
+# !/usr/bin/env python3
+
+from hdwallet.symbols import *
+
+
+def test_symbols():
+
+ assert ANON == "ANON"
+ assert AGM == "AGM"
+ assert XAX == "XAX"
+ assert AYA == "AYA"
+ assert AC == "AC"
+ assert ATOM == "ATOM"
+ assert AUR == "AUR"
+ assert AXE == "AXE"
+ assert BTA == "BTA"
+ assert BEET == "BEET"
+ assert BELA == "BELA"
+ assert BTDX == "BTDX"
+ assert BSD == "BSD"
+ assert BCH == "BCH"
+ assert BTG == "BTG"
+ assert BTC == "BTC"
+ assert BTCTEST == "BTCTEST"
+ assert XBC == "XBC"
+ assert BSV == "BSV"
+ assert BTCZ == "BTCZ"
+ assert BTX == "BTX"
+ assert BLK == "BLK"
+ assert BST == "BST"
+ assert BND == "BND"
+ assert BNDTEST == "BNDTEST"
+ assert BOLI == "BOLI"
+ assert BRIT == "BRIT"
+ assert CPU == "CPU"
+ assert CDN == "CDN"
+ assert CCN == "CCN"
+ assert CLAM == "CLAM"
+ assert CLUB == "CLUB"
+ assert CMP == "CMP"
+ assert CRP == "CRP"
+ assert CRAVE == "CRAVE"
+ assert DASH == "DASH"
+ assert DASHTEST == "DASHTEST"
+ assert ONION == "ONION"
+ assert DFC == "DFC"
+ assert DNR == "DNR"
+ assert DMD == "DMD"
+ assert DGB == "DGB"
+ assert DGC == "DGC"
+ assert DOGE == "DOGE"
+ assert DOGETEST == "DOGETEST"
+ assert EDRC == "EDRC"
+ assert ECN == "ECN"
+ assert EMC2 == "EMC2"
+ assert ELA == "ELA"
+ assert NRG == "NRG"
+ assert ETH == "ETH"
+ assert ERC == "ERC"
+ assert EXCL == "EXCL"
+ assert FIX == "FIX"
+ assert FIXTEST == "FIXTEST"
+ assert FTC == "FTC"
+ assert FRST == "FRST"
+ assert FLASH == "FLASH"
+ assert FJC == "FJC"
+ assert GCR == "GCR"
+ assert GAME == "GAME"
+ assert GBX == "GBX"
+ assert GRC == "GRC"
+ assert GRS == "GRS"
+ assert GRSTEST == "GRSTEST"
+ assert NLG == "NLG"
+ assert HNC == "HNC"
+ assert THC == "THC"
+ assert HUSH == "HUSH"
+ assert IXC == "IXC"
+ assert INSN == "INSN"
+ assert IOP == "IOP"
+ assert JBS == "JBS"
+ assert KOBO == "KOBO"
+ assert KMD == "KMD"
+ assert LBC == "LBC"
+ assert LINX == "LINX"
+ assert LCC == "LCC"
+ assert LTC == "LTC"
+ assert LTCTEST == "LTCTEST"
+ assert LTZ == "LTZ"
+ assert LKR == "LKR"
+ assert LYNX == "LYNX"
+ assert MZC == "MZC"
+ assert MEC == "MEC"
+ assert MNX == "MNX"
+ assert MONA == "MONA"
+ assert MONK == "MONK"
+ assert XMY == "XMY"
+ assert NIX == "NIX"
+ assert NMC == "NMC"
+ assert NAV == "NAV"
+ assert NEBL == "NEBL"
+ assert NEOS == "NEOS"
+ assert NRO == "NRO"
+ assert NYC == "NYC"
+ assert NVC == "NVC"
+ assert NBT == "NBT"
+ assert NSR == "NSR"
+ assert OK == "OK"
+ assert OMNI == "OMNI"
+ assert OMNITEST == "OMNITEST"
+ assert ONX == "ONX"
+ assert PPC == "PPC"
+ assert PSB == "PSB"
+ assert PHR == "PHR"
+ assert PINK == "PINK"
+ assert PIVX == "PIVX"
+ assert PIVXTEST == "PIVXTEST"
+ assert POSW == "POSW"
+ assert POT == "POT"
+ assert PRJ == "PRJ"
+ assert PUT == "PUT"
+ assert QTUM == "QTUM"
+ assert QTUMTEST == "QTUMTEST"
+ assert RBTC == "RBTC"
+ assert RBTCTEST == "RBTCTEST"
+ assert RPD == "RPD"
+ assert RVN == "RVN"
+ assert RDD == "RDD"
+ assert RBY == "RBY"
+ assert SAFE == "SAFE"
+ assert SLS == "SLS"
+ assert SCRIBE == "SCRIBE"
+ assert SDC == "SDC"
+ assert SDCTEST == "SDCTEST"
+ assert SLM == "SLM"
+ assert SLMTEST == "SLMTEST"
+ assert SMLY == "SMLY"
+ assert SLR == "SLR"
+ assert STASH == "STASH"
+ assert STRAT == "STRAT"
+ assert STRATTEST == "STRATTEST"
+ assert SUGAR == "SUGAR"
+ assert SUGARTEST == "SUGARTEST"
+ assert SYS == "SYS"
+ assert TOA == "TOA"
+ assert THT == "THT"
+ assert TRX == "TRX"
+ assert TWINS == "TWINS"
+ assert TWINSTEST == "TWINSTEST"
+ assert USC == "USC"
+ assert UNO == "UNO"
+ assert VASH == "VASH"
+ assert VC == "VC"
+ assert XVG == "XVG"
+ assert VTC == "VTC"
+ assert VIA == "VIA"
+ assert VIATEST == "VIATEST"
+ assert VIVO == "VIVO"
+ assert XWC == "XWC"
+ assert WC == "WC"
+ assert XUEZ == "XUEZ"
+ assert XDC == "XDC"
+ assert ZCL == "ZCL"
+ assert ZEC == "ZEC"
+ assert ZECTEST == "ZECTEST"
+ assert ZEN == "ZEN"
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 6e6dba0..f390e5f 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -6,8 +6,8 @@
from hdwallet.utils import (
get_bytes, generate_mnemonic, generate_entropy, is_entropy, get_mnemonic_strength,
- get_entropy_strength, is_mnemonic, get_mnemonic_language,
- is_root_xprivate_key, is_root_xpublic_key
+ get_entropy_strength, is_mnemonic, get_mnemonic_language, entropy_to_mnemonic, mnemonic_to_entropy,
+ is_root_xprivate_key, is_root_xpublic_key, generate_passphrase
)
# Test Values
@@ -40,6 +40,8 @@ def test_utils():
symbol=_["bitcoin"]["mainnet"]["symbol"]
)
+ assert len(generate_passphrase(length=19999)) == 19999
+
def test_utils_entropy():
@@ -55,6 +57,10 @@ def test_utils_entropy():
assert get_entropy_strength(entropy["entropy"]) == entropy["strength"]
assert is_entropy(entropy["entropy"])
+ assert mnemonic_to_entropy(
+ mnemonic=_["bitcoin"]["mainnet"]["mnemonic"], language=_["bitcoin"]["mainnet"]["language"]
+ ) == _["bitcoin"]["mainnet"]["entropy"]
+
def test_utils_mnemonic():
@@ -73,3 +79,7 @@ def test_utils_mnemonic():
assert not is_mnemonic(mnemonic["mnemonic"], "korean")
assert is_mnemonic(mnemonic["mnemonic"], mnemonic["language"])
assert get_mnemonic_language(mnemonic["mnemonic"]) == mnemonic["language"]
+
+ assert entropy_to_mnemonic(
+ entropy=_["bitcoin"]["mainnet"]["entropy"], language=_["bitcoin"]["mainnet"]["language"]
+ ) == _["bitcoin"]["mainnet"]["mnemonic"]
diff --git a/tests/values.json b/tests/values.json
index 64c0217..e77fd82 100644
--- a/tests/values.json
+++ b/tests/values.json
@@ -93,6 +93,8 @@
"root_xprivate_key_hex": "0488ade4000000000000000000ad41ef910cdcae932cb4060777b4284ee38f5b29c5fb60fda8416f298a14702c007a286fa06bdb8cc821b1d34d88547b36bf7ab434c632db7ee21fc77b4130a852",
"root_xpublic_key": "xpub661MyMwAqRbcGGUtsoFw2d6ARvD2ABd7z327zxt2XiBBwMx9GAuNrrE7tbRuWF5MjjZ1BzDsRdaSHc9nVKAgHzQrv6pwYW3Hd7LSzbh8sWS",
"root_xpublic_key_hex": "0488b21e000000000000000000ad41ef910cdcae932cb4060777b4284ee38f5b29c5fb60fda8416f298a14702c02949b9f64223e124eb9a8383fba0b21b5845fcfbdc84dec7692d21c716410eab0",
+ "account_extended_xprivate_key":"xprv9yo5N7Q158CUL131kN4rVZsa2n1cKWMkt7UgqVGvrY91GEhyuZCbRFboXhDfYUaDFVwXwMzDCTj3LAprcY3HLZLRF8Q59p4LhR62QaR46Mn",
+ "account_extended_xpublic_key":"xpub6CnRmcvtuVkmYV7UrPbrrhpJaor6iy5cFLQHdsgYQsfz9338T6Wqy3vHP1MXCEPMKzq1C8fErhrGnzEYarzsF814HnCzNF6vqo9JiGjjvE2",
"xprivate_key": "xprvA3nwFVTnTFsvwcFaFJd18aFoAguPwgn3N5eWRx785nSHshmxogPYyCzuHc4mu7rZvPArT4WjjakcwfzmH3oJBH71DmaeEGo4bbNgbq3FsRV",
"xprivate_key_hex": "0488ade405b9e49a9c00000000732993021308a753144a66f61475a5e52ce5c8a42b6c03fe43d5b30956c3722000730305af76b25e3b5fbc1169e6cb77d34a3fd7851368c11074381a359e287d3a",
"xpublic_key": "xpub6GnHezzgHdSEA6L3MLA1ViCXiijtM9VtjJa7ELWje7yGkW77MDhoX1KP8sbcbXeaXXtri6kfDWPLiPuntxaw5cjaPbpiv2rm2wdq1uUwERQ",
@@ -152,6 +154,28 @@
"p2wsh": "tb1qlp932cdsy78jfka7gcv9gk50cdku5g6thxjpzstcwn9gnqd5ll9qzhuqvw",
"p2wsh_in_p2sh": "2NBzP3SsouDEVe41fYJBbGGPS18tk6mqZSh"
}
- }
+ },
+ "addresses": [
+ "m/44'/0'/0'/0/0 16W7JeQXEzmYKX8UMFMrg6FwNSPduVdYHo 02b506c3585ebaaf5553b5e3b767c958c6eec4d0eba8a9b9fe90ff2e512a70c30c L15H89HiXh6m4CNPkMvAg1Wduscw5wuvktACPzrHowDHufwPgR7k",
+ "m/44'/0'/0'/0/1 19rtbaqSprjGpE3pxUovx38t64dYLxJ5H4 023cef50818c538a76eaec937f566acbabb0f2047fc2a082c11a9dad398c15eab4 Ky6pHMChq9sT8EhM42rzCab9SxHbFU4CM7rXhZT7fRakAeS47eob",
+ "m/44'/0'/0'/0/2 1FBUJ5QkwgBbFgHnXR2BWnyjYAh8KDXbTk 0349e0525ee723e4e9db2fb1b01e8fe5a6bc73067c024ff6d3bee7036a14758059 Kxfgz2ZAXiJ3V5Uy4hL7YKssJV3VXf9N3k72DV8XQoF6ndzxYa2U",
+ "m/44'/0'/0'/0/3 1JBW45zCCCHZ58z9AnEdbqCQ4TBnq5hVfk 03028b9e813e4d7bdee5a2930272673decc0736820e4a65676ac2af3fef89b49f1 L2P9gZUoobY1Z9mmUNfParxeMDg5rejvptD7fZYbam8CrgnFJ6CM",
+ "m/44'/0'/0'/0/4 1Dxp9jGJqpZMybuj6RoD4qozUEbYMeJgET 0398e6783af803f5125c0d0eecaffd663d5873c961b65493fa31d29ade7eee2aea L3wFzjZDvf1znTFkfcDk7YRHDpG186o4Wfy6zzRSyerGGjdYqXG3",
+ "m/44'/0'/0'/0/5 17FymopXFtsz3FidDTXVYymEksfRVA8Prr 031900f3d2c249dd54c3c59c506bbdda65e61f614841c0254d309d5f206160b459 KxagjpYP8VGb4M2U1Q4tnZ8vuvt3BnwnzsiGen67kwZprMUeYCBx",
+ "m/44'/0'/0'/0/6 1ABU3xfqxYPMx37tK3ytNFMfF4EJhNCe4F 03c016cdac08271650e39748544ccb16bbcf89a470ca03aaed9cb61cb4eec79122 Kxong4YotFdirnM4eYKSq5Kyq55eKnqUqnyvEsskvb63Lwyk7oay",
+ "m/44'/0'/0'/0/7 178cLTCrTBRs1KD6rvkrGrtGBFibzwcWHS 03857c2f52bf8363566ecdf0c2f1aac249a8a09ad9b442f46782e397cd7ddf3b4f L1WyWVRf3sHMeJs6FRbE2fxq2Ld2PPUwQcqDHMKs7NY76naFjQtP",
+ "m/44'/0'/0'/0/8 1raCHQ7gUGKS8LCxSp18Dw6pWq28Mubc2 0209683884b033ea08fb9ce44c8fc77fdad2c7935ffab765b76976f2404d84a58d L2A1nHGoLGCMBS9BJioAEuHGQH4NYLqxEnSaBKYWrXmhL4xg8h5G",
+ "m/44'/0'/0'/0/9 16rs2as6bHkKxJCKkeFKB4dMra2QMGJifc 03055297a3a7cbbd6ff32958f5d82f0cd4f1a29330fbd83d67038e406c27eb7412 L4nDdMuCKnxM9oqGtse989sS6fwyX2UxgafgZBnc71Z6mhiMudBx",
+ "m/44'/0'/0'/0/10 13TZhvvzcK5WYqT88tpmc7zcirKRMB65Fc 03b2d37804e7c04b12035c9d87cb8f8ba6fdf11744b2b37a52b023073eeee2fe4b L3NHZhyLjvum8nbKeGSYMzyjQritbsbtm8iPMAtH8sVRkqNw8VfR",
+ "m/44'/0'/0'/0/11 1LEkeCdpvD3FRRbCajLqdEDwTnKjxEte4Z 02f88c7d8fc5b83f2f24c3868d3c5aba8ec4b8bef75338e09efe7f19f2653c10fb KxWhP2iqkbkS34MffJU94w1hsy6ixoyhh1xKWeRm1ZqX92khrv88",
+ "m/44'/0'/0'/0/12 1KAcnth9Ebr5bALRmFtWCRnd1vivgyGAnt 022f2f6a8a57c355b057d38c4e263760a50e085ba6b3afa25aa8c13fcb861c882f Kx1JcdaLVPt4EFseRYf6bhP8EwpNWjEkgLMLaNQT6HrU4hnCGe7g",
+ "m/44'/0'/0'/0/13 1AUMAcfiiXVqrthwtAPEJNEMoLQXYkLpx3 02695fd7ac3c09a3b568ec374965331d3146740ec938d1d3166cdf420e23688914 KxdtUhqVGD6HKUsCtaFm1vNXLzDQxWDzpTqhBk9y5RY1ACGj7BtW",
+ "m/44'/0'/0'/0/14 1E8Ywe96WNrGDb3V1zA8LLBbDqir1TBREo 02b2589ecb684684a332ad6753710af251e5455d387b60facbc78ea60998d4704a KxEZKXvR4pW3eVmuog714cHgJwxqm87uUcjrUU8e28uPzhq88qxX",
+ "m/44'/0'/0'/0/15 16oBYcHjoQ721NWPwuojmLuQbTmbFbsnyX 030f4b6d7f4ab090a9a4f912f6dc65d2b221b724fdb29fc06974aa011c3c072c20 Ky2Kpb9ephAmjyP2RsUtLfydwWZmhEWBkszfPfQJJPQFdmu8LyVD",
+ "m/44'/0'/0'/0/16 18dfhagZDyQcpFHN9XXG8WLJemhiYpkPSi 033b25fefbb04de60b7bffe61d0bcedb48916c08fa96f76a80c4e9288b61cb6044 KyAitBv7M7jVqtZXrJxypnZipfg9BdZhEVmUPDWvWRSN4KsB2gcH",
+ "m/44'/0'/0'/0/17 1ACpQPMNLewMbXWvVg78kZZKMf64bu8qtc 026691563d2ee9b7a5ae095b443a1199ff53d612928412d8965e163b78c31b89d6 L5dBkDYHfrTVy2ZKT9Xy8V5m34QTg3J2dbhhXMe8wMmAawLFpPGT",
+ "m/44'/0'/0'/0/18 14xgtqtqxvB8QFgaTihdTAgD9Wxm5tbonU 0306b36921313c59e4495bc1c4d9f6dc9c9303a8740767c7a1844c36c506b5f9e7 KwS6KDcNUA7WQ1FTZv9G3A6uWrPzEcYpiGQW5R8j5CkcjvmFduJh",
+ "m/44'/0'/0'/0/19 1BUauGHKJ69j7UsqcwX4p3NmELZuPbkY2d 03ded79d490a0c59ad79549acae0cbe9c8b102185c254f27a26993a5911f7cd038 L22FD2vSP6c2cxbD3ssyJZSKBvXxzjCGnGqAihdowkb3UADZn6q1"
+ ]
}
}
\ No newline at end of file
diff --git a/tox.ini b/tox.ini
index 3c126b6..1307a76 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = python36,python37,python38,python39
+envlist = python36,python37,python38,python39,python310
[travis]
python =
@@ -7,23 +7,39 @@ python =
3.7: python37
3.8: python38
3.9: python39
+ 3.10: python310
[testenv:python36]
install_command =
- python -m pip install -e .[tests,docs] {opts} {packages}
+ python -m pip install --upgrade pip {opts} {packages}
+ pip install -e . {opts} {packages}
+extras = cli,tests,docs
commands = python -m pytest
[testenv:python37]
install_command =
- python -m pip install -e .[tests,docs] {opts} {packages}
+ python -m pip install --upgrade pip {opts} {packages}
+ pip install -e . {opts} {packages}
+extras = cli,tests,docs
commands = python -m pytest
[testenv:python38]
install_command =
- python -m pip install -e .[tests,docs] {opts} {packages}
+ python -m pip install --upgrade pip {opts} {packages}
+ pip install -e . {opts} {packages}
+extras = cli,tests,docs
commands = python -m pytest
[testenv:python39]
install_command =
- python -m pip install -e .[tests,docs] {opts} {packages}
+ python -m pip install --upgrade pip {opts} {packages}
+ pip install -e . {opts} {packages}
+extras = cli,tests,docs
+commands = python -m pytest
+
+[testenv:python310]
+install_command =
+ python -m pip install --upgrade pip {opts} {packages}
+ pip install -e . {opts} {packages}
+extras = cli,tests,docs
commands = python -m pytest