├── bitcoinlib ├── config │ ├── VERSION │ ├── __init__.py │ ├── secp256k1.py │ └── opcodes.py ├── tools │ ├── __init__.py │ ├── mnemonic_key_create.py │ ├── import_database.py │ ├── sign_raw.py │ └── wallet_multisig_2of3.py ├── __init__.py ├── services │ ├── __init__.py │ ├── bitflyer.py │ ├── bitcoinlibtest.py │ └── baseclient.py └── data │ ├── config.ini │ └── providers.examples-nownodes.json ├── tests ├── block250000.pickle ├── block330000.pickle ├── block625007.pickle ├── block629999.pickle ├── block722010.pickle ├── testing.postgresql ├── bitcoinlib_encrypted.db ├── __init__.py ├── config.ini.unittest ├── config_encryption.ini.unittest ├── test_custom.py ├── electrum_keys.json ├── test_networks.py ├── import_test.tx ├── bip38_protected_key_tests.json ├── test_db.py └── test_mnemonic.py ├── docs ├── _static │ ├── classes-overview.jpg │ ├── classes-overview.odt │ ├── classes-overview-detailed.jpg │ ├── classes-overview-detailed.odt │ ├── classes-overview.rst │ ├── manuals.setup-bcoin.rst │ ├── manuals.setup-blockbook.rst │ ├── service-providers.rst │ ├── manuals.setup-electrumx.rst │ ├── manuals.security.rst │ ├── manuals.caching.rst │ ├── manuals.add-provider.rst │ ├── manuals.databases.rst │ ├── script-types-overview.rst │ ├── manuals.sqlcipher.rst │ ├── manuals.setup-bitcoind-connection.rst │ ├── manuals.faq.rst │ └── manuals.install.rst ├── _templates │ ├── breadcrumbs.html │ └── layout.html ├── requirements.txt ├── Makefile ├── make.bat └── conf.py ├── pyproject.toml ├── requirements.txt ├── MANIFEST.in ├── docker ├── README.rst ├── alpine │ └── Dockerfile ├── fedora │ └── Dockerfile ├── ubuntu │ └── Dockerfile ├── kali │ └── Dockerfile └── mint │ └── Dockerfile ├── examples ├── service_execution_times.py ├── wallets_bumpfee.py ├── database.py ├── blockchain_parse.py ├── wallet_bitcoind_connected_wallets2.py ├── database_encrypted.py ├── networks.py ├── wallets_mnemonic.py ├── wallets_transactions.py ├── blockchain_parse_bcoin.py ├── blocks.py ├── bitcoind_regtest.py ├── message_signing.py ├── values.py ├── transactions_decompose_simple.py ├── wallets_delete_unconfirmed_txs.py ├── services.py ├── wallet_bitcoind_connected_wallets.py ├── bitcoind_client_transactions.py ├── mnemonic.py ├── bip38_encrypted_wif_private_key.py ├── wallets_segwit_testnet.py ├── encoding.py ├── wallet_multisig_3of5.py ├── scripts.py ├── keys.py └── wallets_multisig.py ├── .readthedocs.yml ├── .github └── workflows │ ├── unittests_windows.yaml │ ├── unittests-noscrypt.yaml │ ├── unittests-mysql.yaml │ ├── unittests-postgresql.yaml │ └── unittests.yaml ├── .coveragerc ├── __init__.py ├── .gitignore └── setup.cfg /bitcoinlib/config/VERSION: -------------------------------------------------------------------------------- 1 | 0.7.6 -------------------------------------------------------------------------------- /tests/block250000.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1200wd/bitcoinlib/HEAD/tests/block250000.pickle -------------------------------------------------------------------------------- /tests/block330000.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1200wd/bitcoinlib/HEAD/tests/block330000.pickle -------------------------------------------------------------------------------- /tests/block625007.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1200wd/bitcoinlib/HEAD/tests/block625007.pickle -------------------------------------------------------------------------------- /tests/block629999.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1200wd/bitcoinlib/HEAD/tests/block629999.pickle -------------------------------------------------------------------------------- /tests/block722010.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1200wd/bitcoinlib/HEAD/tests/block722010.pickle -------------------------------------------------------------------------------- /tests/testing.postgresql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1200wd/bitcoinlib/HEAD/tests/testing.postgresql -------------------------------------------------------------------------------- /tests/bitcoinlib_encrypted.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1200wd/bitcoinlib/HEAD/tests/bitcoinlib_encrypted.db -------------------------------------------------------------------------------- /docs/_static/classes-overview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1200wd/bitcoinlib/HEAD/docs/_static/classes-overview.jpg -------------------------------------------------------------------------------- /docs/_static/classes-overview.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1200wd/bitcoinlib/HEAD/docs/_static/classes-overview.odt -------------------------------------------------------------------------------- /docs/_static/classes-overview-detailed.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1200wd/bitcoinlib/HEAD/docs/_static/classes-overview-detailed.jpg -------------------------------------------------------------------------------- /docs/_static/classes-overview-detailed.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1200wd/bitcoinlib/HEAD/docs/_static/classes-overview-detailed.odt -------------------------------------------------------------------------------- /docs/_templates/breadcrumbs.html: -------------------------------------------------------------------------------- 1 | {%- extends "sphinx_rtd_theme/breadcrumbs.html" %} 2 | 3 | {% block breadcrumbs_aside %} 4 | {% endblock %} -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | requests>=2.25.0 2 | SQLAlchemy>=2.0.20 3 | fastecdsa>=2.3.0 4 | sphinx>=7.2.0 5 | sphinx_rtd_theme>=2.0.0 6 | numpy>=1.22.0 7 | pycryptodome>=3.19.0 8 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [tool.coverage.run] 6 | branch = true 7 | relative_files = true 8 | debug = true 9 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests>=2.25.0 2 | fastecdsa>=2.3.0 3 | scrypt>=0.8.20 4 | pycryptodome>=3.19.0 5 | SQLAlchemy>=2.0.20 6 | numpy>=1.26.0 7 | sphinx>=7.2.0 8 | psycopg>=3.1.16 9 | coveralls >= 4.0.1 10 | mysql-connector-python>=8.4.0 11 | mysqlclient>=2.2.0 12 | sphinx_rtd_theme>=2.0.0 13 | Cython>=3.0.8 14 | win-unicode-console;platform_system=="Windows" 15 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | include LICENSE 3 | include CHANGELOG 4 | include *.py 5 | include requirements.txt 6 | recursive-include bitcoinlib/config * 7 | recursive-include bitcoinlib/data * 8 | recursive-include bitcoinlib/services *.py 9 | recursive-include bitcoinlib/wordlist *.txt 10 | recursive-include bitcoinlib/tools *.py 11 | recursive-include tests *.json *.py *.pickle 12 | recursive-include docs *.pdf 13 | -------------------------------------------------------------------------------- /docker/README.rst: -------------------------------------------------------------------------------- 1 | Dockerfiles 2 | =========== 3 | 4 | You can find some basic Dockerfiles here for various system images. 5 | 6 | These are used for testing and are not optimized for size and configuration. If you run the container it will 7 | run all unittests. 8 | 9 | .. code-block:: bash 10 | 11 | $ cd 12 | $ docker build -t bitcoinlib . 13 | $ docker run -it bitcoinlib 14 | -------------------------------------------------------------------------------- /docs/_static/classes-overview.rst: -------------------------------------------------------------------------------- 1 | Classes Overview 2 | ================ 3 | 4 | These are the main Bitcoinlib classes 5 | 6 | .. image:: ../_static/classes-overview.jpg 7 | 8 | This is an overview of all BitcoinLib classes. 9 | 10 | .. image:: ../_static/classes-overview-detailed.jpg 11 | 12 | So most classes can be used individually and without database setup. The Wallet class needs a proper database setup 13 | and is dependent upon most other classes. -------------------------------------------------------------------------------- /docker/alpine/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | LABEL org.opencontainers.image.authors="info@1200wd.com" 3 | 4 | WORKDIR /code 5 | 6 | RUN apk add --no-cache git python3-dev gmp-dev python3 py3-pip gcc musl-dev libpq-dev 7 | RUN apk add --no-cache postgresql postgresql-contrib mariadb-dev mysql-client 8 | 9 | RUN git clone https://github.com/1200wd/bitcoinlib.git 10 | 11 | WORKDIR /code/bitcoinlib 12 | RUN python3 -m pip install .[dev] --break-system-packages 13 | 14 | CMD ["python3", "-m", "unittest"] 15 | -------------------------------------------------------------------------------- /docker/fedora/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM fedora:latest 2 | LABEL org.opencontainers.image.authors="info@1200wd.com" 3 | 4 | WORKDIR /code 5 | 6 | RUN yum update -y; yum clean all 7 | 8 | RUN yum install -y python3-devel gmp-devel python3-pip git gcc 9 | 10 | RUN yum install -y postgresql postgresql-server mariadb-server libpq-devel mariadb-devel 11 | 12 | RUN git clone https://github.com/1200wd/bitcoinlib.git 13 | 14 | WORKDIR /code/bitcoinlib 15 | RUN python3 -m pip install .[dev] 16 | 17 | CMD ["python3", "-m", "unittest"] 18 | -------------------------------------------------------------------------------- /examples/service_execution_times.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Service - Measure execution time per service provider for simple blockcount query 6 | # 7 | # © 2021 - 2024 December - 1200 Web Development 8 | # 9 | 10 | from bitcoinlib.services.services import Service 11 | 12 | 13 | for provider in Service().providers: 14 | try: 15 | srv = Service(provider_name=provider, cache_uri='') 16 | except: 17 | pass 18 | else: 19 | print(provider, srv.blockcount(), srv.execution_time) -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Optionally build your docs in additional formats such as PDF and ePub 9 | formats: all 10 | 11 | build: 12 | os: ubuntu-22.04 13 | tools: 14 | python: "3.11" 15 | 16 | # Optionally set the version of Python and requirements required to build your docs 17 | python: 18 | install: 19 | - requirements: docs/requirements.txt 20 | 21 | sphinx: 22 | # Path to your Sphinx configuration file. 23 | configuration: docs/conf.py 24 | -------------------------------------------------------------------------------- /docs/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "!layout.html" %} 2 | 3 | 4 | {% block rootrellink %} 5 |
  • home
  • 6 |
  • search
  • 7 | {% endblock %} 8 | 9 | 10 | {% block relbar1 %} 11 | 12 |
    13 |

    Bitcoinlib

    14 |
    15 | {{ super() }} 16 | {% endblock %} 17 | 18 | {# put the sidebar before the body #} 19 | {% block sidebar1 %}{{ sidebar() }}{% endblock %} 20 | {% block sidebar2 %}{% endblock %} -------------------------------------------------------------------------------- /.github/workflows/unittests_windows.yaml: -------------------------------------------------------------------------------- 1 | name: Bitcoinlib Tests Windows 2 | on: [push] 3 | 4 | jobs: 5 | build: 6 | 7 | runs-on: windows-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v4 11 | - name: Set up Python 12 | uses: actions/setup-python@v5 13 | with: 14 | python-version: '3.11' 15 | architecture: 'x64' 16 | - name: Install dependencies 17 | run: | 18 | python -m pip install .[dev] 19 | - name: Test with coverage 20 | env: 21 | BCL_CONFIG_FILE: ${{ github.workspace }}/tests/config.ini.unittest 22 | PYTHONUTF8: 1 23 | run: coverage run --source=bitcoinlib -m unittest -v 24 | -------------------------------------------------------------------------------- /docker/ubuntu/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | LABEL org.opencontainers.image.authors="info@1200wd.com" 3 | 4 | WORKDIR /code 5 | 6 | RUN apt-get update && apt-get upgrade -y 7 | RUN apt-get install -y \ 8 | software-properties-common git \ 9 | build-essential python3-dev libgmp3-dev python3-pip 10 | 11 | ENV TZ=Europe/Brussels 12 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 13 | RUN apt-get install -y postgresql postgresql-contrib mariadb-server libpq-dev libmysqlclient-dev pkg-config 14 | RUN apt-get clean 15 | 16 | RUN git clone https://github.com/1200wd/bitcoinlib.git 17 | 18 | WORKDIR /code/bitcoinlib 19 | RUN python3 -m pip install .[dev] 20 | 21 | CMD ["python3", "-m", "unittest"] 22 | -------------------------------------------------------------------------------- /docker/kali/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM kalilinux/kali-last-release 2 | LABEL org.opencontainers.image.authors="info@1200wd.com" 3 | 4 | WORKDIR /code 5 | 6 | RUN apt-get update && apt-get upgrade -y 7 | RUN apt-get install -y \ 8 | software-properties-common git \ 9 | build-essential python3-dev libgmp3-dev python3-pip 10 | 11 | #ENV TZ=Europe/Brussels 12 | #RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 13 | RUN apt-get install -y postgresql postgresql-contrib default-libmysqlclient-dev default-mysql-server pkg-config libpq-dev 14 | RUN apt-get clean 15 | 16 | RUN git clone https://github.com/1200wd/bitcoinlib.git 17 | 18 | WORKDIR /code/bitcoinlib 19 | RUN python3 -m pip install .[dev] 20 | 21 | CMD ["python3", "-m", "unittest"] 22 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | relative_files = True 3 | omit = 4 | *clw.py 5 | *import_database.py 6 | *mnemonic_key_create.py 7 | *sign_raw.py 8 | *benchmark.py 9 | *wallet_multisig_2of3.py 10 | *bitcoind.py 11 | *dogecoind.py 12 | *bcoin.py 13 | *litecoind.py 14 | *authproxy.py 15 | *cryptoid.py 16 | *chainso.py 17 | *litecoreio.py 18 | *litecoinblockexplorer.py 19 | *bitflyer.py 20 | *blocksmurfer.py 21 | *electrumx.py 22 | *blockchair.py 23 | [report] 24 | exclude_lines = 25 | pragma: no cover 26 | def __repr__ 27 | if self.debug: 28 | if settings.DEBUG 29 | raise AssertionError 30 | raise NotImplementedError 31 | if 0: 32 | if __name__ == .__main__.: 33 | if not PY3 34 | except ImportError 35 | -------------------------------------------------------------------------------- /docker/mint/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM linuxmintd/mint22-amd64 2 | LABEL org.opencontainers.image.authors="info@1200wd.com" 3 | 4 | WORKDIR /code 5 | 6 | RUN apt-get update && apt-get upgrade -y 7 | RUN apt-get install -y \ 8 | software-properties-common git \ 9 | build-essential python3-dev libgmp3-dev python3-pip python3.12-venv 10 | 11 | ENV TZ=Europe/Brussels 12 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 13 | RUN apt-get install -y postgresql postgresql-contrib mariadb-server libpq-dev pkg-config default-libmysqlclient-dev 14 | RUN apt-get clean 15 | 16 | RUN git clone https://github.com/1200wd/bitcoinlib.git 17 | 18 | WORKDIR /code/bitcoinlib 19 | RUN python3 -m venv /opt/venv 20 | RUN /opt/venv/bin/python3 -m pip install .[dev] 21 | 22 | CMD ["/opt/venv/bin/python3", "-m", "unittest"] -------------------------------------------------------------------------------- /.github/workflows/unittests-noscrypt.yaml: -------------------------------------------------------------------------------- 1 | name: Bitcoinlib Tests Ubuntu - No scrypt 2 | on: [push] 3 | 4 | jobs: 5 | build: 6 | 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v4 11 | - name: Set up Python 12 | uses: actions/setup-python@v5 13 | with: 14 | python-version: '3.10' 15 | architecture: 'x64' 16 | - name: Install dependencies 17 | run: | 18 | python -m pip install .[dev] 19 | pip uninstall -y scrypt 20 | - name: Test with coverage 21 | env: 22 | BCL_CONFIG_FILE: ${{ github.workspace }}/tests/config_encryption.ini.unittest 23 | DB_FIELD_ENCRYPTION_KEY: 11223344556677889900aabbccddeeff11223344556677889900aabbccddeeff 24 | run: coverage run --source=bitcoinlib -m unittest -v 25 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = '-E' 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = Bitcoinlib 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | ifeq ($(shell if which $(SPHINXBUILD) >/dev/null 2>&1; then echo 1; else echo 0; fi),0) 12 | $(error "The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed") 13 | endif 14 | 15 | # Put it first so that "make" without argument is like "make help". 16 | help: 17 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 18 | 19 | .PHONY: help Makefile clean 20 | 21 | clean: 22 | @rm -rf $(BUILDDIR) 23 | 24 | # Catch-all target: route all unknown targets to Sphinx using the new 25 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 26 | %: Makefile 27 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /bitcoinlib/tools/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # © 2018 April - 1200 Web Development 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as 8 | # published by the Free Software Foundation, either version 3 of the 9 | # License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | # 19 | 20 | from bitcoinlib.tools import clw 21 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # © 2016 - 2025 May - 1200 Web Development 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as 8 | # published by the Free Software Foundation, either version 3 of the 9 | # License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | # 19 | 20 | from . import bitcoinlib 21 | 22 | __all__ = ["bitcoinlib"] 23 | -------------------------------------------------------------------------------- /examples/wallets_bumpfee.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Bump transaction fee of a wallet transaction 6 | # 7 | # © 2024 June - 1200 Web Development 8 | # 9 | 10 | from bitcoinlib.wallets import wallet_create_or_open 11 | 12 | 13 | # Create wallet and transaction 14 | w = wallet_create_or_open("wallet_transaction_bumpfee_example", network='bitcoinlib_test') 15 | w.get_key() 16 | w.utxos_update() 17 | 18 | t = w.send_to('blt1q855uwmunslpe0vv83na78v8tuxw9knhvn4g3mq', 2000000, fee=10000) 19 | print(f"Transaction fee before: {t.fee}") 20 | 21 | # Bump fee with standard advised amount 22 | t.bumpfee() 23 | print(f"Transaction fee after bump #1: {t.fee}") 24 | 25 | # Duplicate fee 26 | t.bumpfee(extra_fee = t.fee*2) 27 | print(f"Transaction fee after bump #2: {t.fee}") 28 | 29 | # Raise fee with 0.1%. Raises error...: 30 | print("Error when raising fee with 0,1%") 31 | t.bumpfee(t.fee*1.001) 32 | 33 | -------------------------------------------------------------------------------- /.github/workflows/unittests-mysql.yaml: -------------------------------------------------------------------------------- 1 | name: Bitcoinlib Tests Ubuntu - MySQL 2 | on: [push] 3 | 4 | jobs: 5 | test: 6 | runs-on: ubuntu-latest 7 | 8 | steps: 9 | - uses: actions/checkout@v4 10 | - name: Set up Python 11 | uses: actions/setup-python@v5 12 | with: 13 | python-version: '3.10' 14 | architecture: 'x64' 15 | - name: Set up MySQL 16 | env: 17 | DB_DATABASE: bitcoinlib_test 18 | DB_USER: root 19 | DB_PASSWORD: root 20 | run: | 21 | sudo /etc/init.d/mysql start 22 | mysql -e 'CREATE DATABASE ${{ env.DB_DATABASE }};' -u${{ env.DB_USER }} -p${{ env.DB_PASSWORD }} 23 | - name: Install dependencies 24 | run: | 25 | python -m pip install .[dev] 26 | - name: Test with coverage 27 | env: 28 | BCL_CONFIG_FILE: ${{ github.workspace }}/tests/config.ini.unittest 29 | UNITTEST_DATABASE: mysql 30 | run: coverage run --source=bitcoinlib -m unittest -v 31 | -------------------------------------------------------------------------------- /examples/database.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES -Database direct queries 6 | # 7 | # © 2022 November - 1200 Web Development 8 | # 9 | 10 | 11 | from bitcoinlib.wallets import * 12 | from sqlalchemy.sql import text 13 | 14 | print("\n=== Query database directly to use limit on large databases ===") 15 | wallet_delete_if_exists('wallet_query', force=True) 16 | if wallet_exists('wallet_query'): 17 | w = Wallet('wallet_query') 18 | else: 19 | pk = 'tobacco defy swarm leaf flat pyramid velvet pen minor twist maximum extend' 20 | w = Wallet.create( 21 | keys=pk, network='bitcoinlib_test', 22 | name='wallet_query') 23 | w.get_keys(number_of_keys=50) 24 | w.utxos_update() 25 | 26 | wallet_id = w.wallet_id 27 | 28 | db = Db() 29 | res = db.session.execute(text("SELECT * FROM transactions WHERE wallet_id= %d LIMIT 5" % wallet_id)) 30 | for row in res: 31 | print(row[1].hex()) 32 | -------------------------------------------------------------------------------- /bitcoinlib/tools/mnemonic_key_create.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # Multisig 3-of-5 wallet with Mnemonic passphrase keys 6 | # 7 | # © 2017 November - 1200 Web Development 8 | # 9 | 10 | from bitcoinlib.mnemonic import Mnemonic 11 | from bitcoinlib.keys import HDKey 12 | 13 | NETWORK = 'testnet' 14 | KEY_STRENGHT = 128 15 | 16 | words = Mnemonic().generate(KEY_STRENGHT) 17 | print("A Mnemonic passphrase has been generated. Please write down and store carefully: \n%s" % words) 18 | password = input("\nEnter a password if you would like to protect passphrase []: ") 19 | 20 | seed = Mnemonic().to_seed(words, password) 21 | hdkey = HDKey.from_seed(seed, network=NETWORK) 22 | public_account_wif = hdkey.public_master_multisig() 23 | print("\nPrivate key: \n%s" % hdkey.wif_private()) 24 | # print("Public key: \n%s" % hdkey.wif_public()) 25 | print("Public account key to share with other cosigners for a multisig BIP45 wallet: \n%s" % public_account_wif.wif()) 26 | -------------------------------------------------------------------------------- /bitcoinlib/config/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # © 2016 November - 1200 Web Development 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as 8 | # published by the Free Software Foundation, either version 3 of the 9 | # License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | # 19 | 20 | import bitcoinlib.config.config 21 | import bitcoinlib.config.secp256k1 22 | import bitcoinlib.config.opcodes 23 | 24 | __all__ = ["config", "secp256k1", "opcodes"] 25 | -------------------------------------------------------------------------------- /examples/blockchain_parse.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Deserialize and Verify all transactions from the latest block 6 | # Just use for testing and experimenting, this library is not optimized for blockchain parsing! 7 | # 8 | # © 2018 - 2025 May - 1200 Web Development 9 | # 10 | 11 | from bitcoinlib.services.services import * 12 | 13 | 14 | srv = Service(network='testnet', strict=False) 15 | latest_block = srv.blockcount() 16 | block = srv.getblock(latest_block, parse_transactions=False) 17 | transactions = block.transactions 18 | 19 | 20 | MAX_TRANSACTIONS = 100 21 | count = 0 22 | count_segwit = 0 23 | for txid in transactions[:MAX_TRANSACTIONS]: 24 | print("\n=== Deserialize transaction #%d (segwit %d) ===" % (count, count_segwit)) 25 | count += 1 26 | t = srv.gettransaction(txid) 27 | t.verify() 28 | t.info() 29 | if t.witness_type != 'legacy': 30 | count_segwit += 1 31 | if not t.verified: 32 | input("Transaction could not be verified, press any key to continue") 33 | -------------------------------------------------------------------------------- /examples/wallet_bitcoind_connected_wallets2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Using Bitcoin Core wallets with Bitcoinlib 6 | # 7 | # Method 2 - Create wallet in Bitcoin Core, export public keys to bitcoinlib and easily manage wallet from bitcoinlib. 8 | # 9 | # © 2024 May - 1200 Web Development 10 | # 11 | 12 | from bitcoinlib.wallets import * 13 | from bitcoinlib.services.bitcoind import BitcoindClient 14 | 15 | # 16 | # Settings and Initialization 17 | # 18 | 19 | # Create wallet in Bitcoin Core and export descriptors 20 | # $ bitcoin-cli createwallet wallet_bitcoincore2 21 | # $ bitcoin-cli -rpcwallet=wallet_bitcoincore2 listdescriptors 22 | 23 | # Now copy the descriptor of the public master key, which looks like: wpkh([.../84h/1h/0h] 24 | pkwif = 'tpubDDuQM8y9z4VQW5FS13BXGMxUwkUKEXc8KG5xzzbe6UsssrJDKJEygqbgMATnn6ZDwLXQ5PQipH989qWRTzFhPPZMiHxYYrG14X34vc24pD6' 25 | 26 | # You can create the wallet and manage it from bitcoinlib 27 | w = wallet_create_or_open("wallet_bitcoincore2", keys=pkwif, witness_type='segwit') 28 | w.providers=['bitcoind'] 29 | w.scan(scan_gap_limit=1) 30 | w.info() 31 | -------------------------------------------------------------------------------- /.github/workflows/unittests-postgresql.yaml: -------------------------------------------------------------------------------- 1 | name: Bitcoinlib Tests Ubuntu - PostgreSQL 2 | on: [push] 3 | 4 | jobs: 5 | test: 6 | runs-on: ubuntu-latest 7 | 8 | services: 9 | postgres: 10 | image: postgres 11 | 12 | env: 13 | POSTGRES_PASSWORD: postgres 14 | POSTGRES_USER: postgres 15 | POSTGRES_DB: bitcoinlib_test 16 | 17 | options: >- 18 | --health-cmd pg_isready 19 | --health-interval 10s 20 | --health-timeout 5s 21 | --health-retries 5 22 | ports: 23 | - 5432:5432 24 | 25 | steps: 26 | - uses: actions/checkout@v4 27 | - name: Set up Python 28 | uses: actions/setup-python@v5 29 | with: 30 | python-version: '3.10' 31 | architecture: 'x64' 32 | - name: Install dependencies 33 | run: | 34 | python -m pip install .[dev] 35 | - name: Test with coverage 36 | env: 37 | BCL_CONFIG_FILE: ${{ github.workspace }}/tests/config.ini.unittest 38 | UNITTEST_DATABASE: postgresql 39 | DB_FIELD_ENCRYPTION_PASSWORD: verybadpassword 40 | run: coverage run --source=bitcoinlib -m unittest -v 41 | -------------------------------------------------------------------------------- /examples/database_encrypted.py: -------------------------------------------------------------------------------- 1 | # Encrypt private key fields in Key table in database using symmetric AES encryption. 2 | # - Add database_encryption_enabled=True in .bitcoinlib/config.ini 3 | # - Supply encryption key in DB_FIELD_ENCRYPTION_KEY environment variable 4 | # - Key must be a 32 byte hexadecimal string 5 | # - Only encrypts the private and wif field in the DbKey database, for full database encryption please use the 6 | # db_password argument in the Wallet class 7 | 8 | import os 9 | 10 | # Method #1 - Set environment variable in code 11 | os.environ['DB_FIELD_ENCRYPTION_KEY'] = '11223344556677889900aabbccddeeff11223344556677889900aabbccddeeff' 12 | 13 | # Method #2 - Use hardware wallet, yubikey, file on separate physical device, etc. to store password 14 | 15 | # Method #3 - Ask user for a password use the sha256 hash as encryption key 16 | # import hashlib 17 | # pwd = input("Password? ") 18 | # os.environ['DB_FIELD_ENCRYPTION_KEY'] = hashlib.sha256(bytes(pwd, 'utf8')).hexdigest() 19 | 20 | from bitcoinlib.wallets import wallet_create_or_open 21 | 22 | wallet = wallet_create_or_open('wallet_private_keys_encrypted') 23 | wallet.new_key() 24 | wallet.info() 25 | print(wallet.main_key.wif) 26 | 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | .coveralls.yml 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | 56 | # Sphinx documentation 57 | docs/_build/ 58 | *.odt# 59 | .~* 60 | docs/source/* 61 | 62 | # PyBuilder 63 | target/ 64 | 65 | #Ipython Notebook 66 | .ipynb_checkpoints 67 | 68 | .idea 69 | *.sqlite 70 | .pypirc 71 | bitcoinlib.iml 72 | venv/ 73 | 74 | # Visual studio 75 | .vs 76 | -------------------------------------------------------------------------------- /.github/workflows/unittests.yaml: -------------------------------------------------------------------------------- 1 | name: Bitcoinlib Tests Ubuntu 2 | on: [push] 3 | 4 | jobs: 5 | test: 6 | runs-on: ubuntu-latest 7 | 8 | strategy: 9 | matrix: 10 | python: ["3.8", "3.12"] 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Set up Python 15 | uses: actions/setup-python@v5 16 | with: 17 | python-version: ${{ matrix.python }} 18 | architecture: 'x64' 19 | - name: Install dependencies 20 | run: | 21 | python -m pip install .[dev] 22 | - name: Test with coverage 23 | env: 24 | BCL_CONFIG_FILE: ${{ github.workspace }}/tests/config.ini.unittest 25 | run: coverage run --source=bitcoinlib -m unittest -v 26 | 27 | - name: Coveralls Parallel 28 | uses: coverallsapp/github-action@v2 29 | with: 30 | parallel: true 31 | flag-name: run-${{ join(matrix.*, '-') }} 32 | github-token: ${{ github.token }} 33 | 34 | coveralls_finish: 35 | needs: test 36 | runs-on: ubuntu-latest 37 | steps: 38 | - name: Coveralls Finished 39 | uses: coverallsapp/github-action@v2 40 | with: 41 | parallel-finished: true 42 | debug: true 43 | carryforward: "run-3.8,run-3.12" 44 | -------------------------------------------------------------------------------- /bitcoinlib/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # © 2018 - 2025 May - 1200 Web Development 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as 8 | # published by the Free Software Foundation, either version 3 of the 9 | # License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | # 19 | 20 | import bitcoinlib.encoding 21 | import bitcoinlib.mnemonic 22 | import bitcoinlib.keys 23 | import bitcoinlib.transactions 24 | import bitcoinlib.wallets 25 | import bitcoinlib.tools 26 | import bitcoinlib.blocks 27 | import bitcoinlib.values 28 | 29 | __all__ = ["keys", "transactions", "wallets", "encoding", "mnemonic", "tools", "blocks", "values"] 30 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # © 2018 January - 1200 Web Development 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as 8 | # published by the Free Software Foundation, either version 3 of the 9 | # License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | # 19 | 20 | import tests.test_custom 21 | import tests.test_encoding 22 | import tests.test_keys 23 | import tests.test_mnemonic 24 | import tests.test_wallets 25 | import tests.test_transactions 26 | import tests.test_services 27 | import tests.test_tools 28 | import tests.test_db 29 | import tests.test_blocks 30 | import tests.test_values 31 | import tests.test_script 32 | -------------------------------------------------------------------------------- /bitcoinlib/tools/import_database.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # IMPORT DATABASE - Extract wallet keys and information from old Bitcoinlib database and move to actual database 5 | # © 2024 February - 1200 Web Development 6 | # 7 | # TODO: Currently skips multisig wallets 8 | 9 | 10 | import sqlalchemy as sa 11 | from sqlalchemy.sql import text 12 | from bitcoinlib.main import * 13 | from bitcoinlib.wallets import Wallet, wallet_create_or_open 14 | 15 | 16 | DATABASE_TO_IMPORT = 'sqlite:///' + os.path.join(str(BCL_DATABASE_DIR), 'bitcoinlib_test.sqlite') 17 | 18 | def import_database(): 19 | print(DATABASE_TO_IMPORT) 20 | engine = sa.create_engine(DATABASE_TO_IMPORT) 21 | con = engine.connect() 22 | 23 | wallets = con.execute(text( 24 | 'SELECT w.name, k.private, w.owner, w.network_name, k.account_id, k.address, w.witness_type FROM wallets AS w ' 25 | 'INNER JOIN keys AS k ON w.main_key_id = k.id WHERE multisig=0')).fetchall() 26 | 27 | for wallet in wallets: 28 | print("Import wallet %s" % wallet[0]) 29 | w = wallet_create_or_open(wallet[0], wallet[1], wallet[2], wallet[3], wallet[4], witness_type=wallet[6]) 30 | 31 | 32 | if __name__ == '__main__': 33 | import_database() 34 | -------------------------------------------------------------------------------- /examples/networks.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Network class 6 | # 7 | # © 2017 - 2019 February - 1200 Web Development 8 | # 9 | 10 | from bitcoinlib.networks import * 11 | 12 | # 13 | # Network examples 14 | # 15 | 16 | network = Network('bitcoin') 17 | print("\n=== Get all WIF prefixes ===") 18 | print("WIF Prefixes: %s" % network_values_for('prefix_wif')) 19 | 20 | print("\n=== Get all HDkey private prefixes ===") 21 | print("HDkey private prefixes: %s" % network_values_for('prefix_wif')) 22 | 23 | print("\n=== Get network(s) for WIF prefix B0 ===") 24 | print("WIF Prefixes: %s" % network_by_value('prefix_wif', 'B0')) 25 | 26 | print("\n=== Get HD key private prefix for current network ===") 27 | print("self.prefix_hdkey_private: %s" % network.wif_prefix()) 28 | 29 | print("\n=== Network parameters ===") 30 | for k in network.__dir__(): 31 | if k[:1] != '_': 32 | v = eval('network.%s' % k) 33 | if not callable(v): 34 | print("%25s: %s" % (k, v)) 35 | 36 | wif = 'Zpub74CSuvLPQxWkdW7bivQAhomXZTzbE8quAakKRg1C3x7uDcCCeh7zPp1tZrtJrscihJRASZWjZQ7nPQj1SHTn8gkzAHPZL3dC' \ 37 | 'MbMQLFwMKVV' 38 | print("\n=== Search for WIF prefix ===") 39 | print("WIF: %s" % wif) 40 | print(wif_prefix_search(wif)) 41 | -------------------------------------------------------------------------------- /examples/wallets_mnemonic.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Mnemonic Wallets 6 | # 7 | # © 2018 February - 1200 Web Development 8 | # 9 | 10 | import os 11 | from bitcoinlib.wallets import Wallet, BCL_DATABASE_DIR 12 | from bitcoinlib.mnemonic import Mnemonic 13 | 14 | # 15 | # Create Wallets 16 | # 17 | 18 | # First recreate database to avoid already exist errors 19 | test_databasefile = os.path.join(BCL_DATABASE_DIR, 'bitcoinlib.test.sqlite') 20 | test_database = 'sqlite:///' + test_databasefile 21 | if os.path.isfile(test_databasefile): 22 | os.remove(test_databasefile) 23 | 24 | print("\n=== Create a simple Mnemonic wallet ===") 25 | passphrase = Mnemonic().generate() 26 | print("Your private key passphrase is:", passphrase) 27 | password = input("Enter password to protect passphrase: ") 28 | wlt = Wallet.create('mnwlttest1', keys=passphrase, password=password, network='bitcoinlib_test', 29 | db_uri=test_database) 30 | wlt.get_key() 31 | wlt.utxos_update() # Create some test UTXOs 32 | wlt.info() 33 | to_key = wlt.get_key() 34 | print("\n- Create transaction (send to own wallet)") 35 | t = wlt.send_to(to_key.address, 50000000) 36 | t.info() 37 | 38 | print("\n- Successfully send, updated wallet info:") 39 | wlt.info() 40 | -------------------------------------------------------------------------------- /examples/wallets_transactions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Wallets and Transactions 6 | # 7 | # © 2018 February - 1200 Web Development 8 | # 9 | 10 | import os 11 | from pprint import pprint 12 | from bitcoinlib.wallets import Wallet, BCL_DATABASE_DIR 13 | 14 | 15 | # 16 | # Create Wallets 17 | # 18 | 19 | # First recreate database to avoid already exist errors 20 | test_databasefile = os.path.join(BCL_DATABASE_DIR, 'bitcoinlib.test.sqlite') 21 | test_database = 'sqlite:///' + test_databasefile 22 | if os.path.isfile(test_databasefile): 23 | os.remove(test_databasefile) 24 | 25 | print("\n=== Create a wallet and a simple transaction ===") 26 | wlt = Wallet.create('wlttest1', network='bitcoinlib_test', db_uri=test_database) 27 | wlt.get_key() 28 | wlt.utxos_update() # Create some test UTXOs 29 | wlt.info() 30 | to_key = wlt.get_key() 31 | print("\n- Create transaction (send to own wallet)") 32 | t = wlt.send_to(to_key.address, 50000000) 33 | t.info() 34 | 35 | print("\n- Successfully send, updated wallet info:") 36 | wlt.info() 37 | 38 | 39 | print("\n=== Create a wallet, generate 6 UTXOs and create a sweep transaction ===") 40 | wlt = Wallet.create('wlttest2', network='bitcoinlib_test', db_uri=test_database) 41 | wlt.get_keys(number_of_keys=3) 42 | wlt.utxos_update() # Create some test UTXOs 43 | wlt.info() 44 | to_key = wlt.get_key() 45 | print("\n- Create transaction to sweep wallet") 46 | t = wlt.sweep('21Cr5enTHDejL7rQfyzMHQK3i7oAN3TZWDb') 47 | t.info() 48 | 49 | print("\n- Successfully send, updated wallet info:") 50 | wlt.info() 51 | -------------------------------------------------------------------------------- /examples/blockchain_parse_bcoin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Deserialize and Verify all transactions from the specified blocks using Bcoin provider 6 | # 7 | # Just use for testing and experimenting, this library is not optimized for blockchain parsing! 8 | # 9 | # © 2020 April - 1200 Web Development 10 | # 11 | 12 | import time 13 | from bitcoinlib.services.services import Service 14 | from pprint import pprint 15 | 16 | 17 | start_time = time.time() 18 | 19 | 20 | srv = Service(providers=['bcoin']) 21 | 22 | # Get latest block 23 | # blocks = [srv.blockcount()] 24 | 25 | # Get first block 26 | # blocks = [1] 27 | 28 | # Check first 100000 blocks 29 | # blocks = range(1, 100000) 30 | 31 | # Check some more recent blocks 32 | blocks = range(626001, 626002) 33 | 34 | 35 | for blockid in blocks: 36 | print("Getting block %s" % blockid) 37 | block = srv.getblock(blockid, parse_transactions=True, limit=99999) 38 | print("Found %d transactions" % block.tx_count) 39 | 40 | MAX_TRANSACTIONS = 10000 41 | count = 0 42 | count_segwit = 0 43 | 44 | for t in block.transactions[:MAX_TRANSACTIONS]: 45 | print("=== Deserialize transaction %s (#%d, segwit %d) ===" % (t.txid, count, count_segwit)) 46 | count += 1 47 | t.verify() 48 | # t.info() 49 | if t.witness_type != 'legacy': 50 | count_segwit += 1 51 | if not t.verified: 52 | print(50 * "!") 53 | print("Transaction could not be verified!!") 54 | 55 | 56 | print("--- %s seconds ---" % (time.time() - start_time)) 57 | -------------------------------------------------------------------------------- /bitcoinlib/config/secp256k1.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # Parameters secp256k1 5 | # © 2016 November - 1200 Web Development 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU Affero General Public License as 9 | # published by the Free Software Foundation, either version 3 of the 10 | # License, or (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU Affero General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU Affero General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | # import ecdsa 22 | 23 | 24 | # Parameters secp256k1 25 | # from http://www.secg.org/sec2-v2.pdf, par 2.4.1 26 | secp256k1_p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F # field size 27 | secp256k1_n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 # order 28 | secp256k1_b = 7 29 | secp256k1_a = 0 30 | secp256k1_Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 # generator point x 31 | secp256k1_Gy = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 # generator point y 32 | 33 | # secp256k1_curve = ecdsa.ellipticcurve.CurveFp(secp256k1_p, secp256k1_a, secp256k1_b) 34 | # secp256k1_generator = ecdsa.ellipticcurve.Point(secp256k1_curve, secp256k1_Gx, secp256k1_Gy, secp256k1_n) 35 | -------------------------------------------------------------------------------- /bitcoinlib/services/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # © 2017 May - 1200 Web Development 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as 8 | # published by the Free Software Foundation, either version 3 of the 9 | # License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | # 19 | 20 | import bitcoinlib.services.baseclient 21 | import bitcoinlib.services.authproxy 22 | import bitcoinlib.services.bitcoinlibtest 23 | import bitcoinlib.services.bitcoind 24 | import bitcoinlib.services.dogecoind 25 | import bitcoinlib.services.litecoind 26 | import bitcoinlib.services.bitgo 27 | import bitcoinlib.services.blockchaininfo 28 | import bitcoinlib.services.blockcypher 29 | import bitcoinlib.services.cryptoid 30 | import bitcoinlib.services.litecoreio 31 | import bitcoinlib.services.blockchair 32 | import bitcoinlib.services.bcoin 33 | import bitcoinlib.services.bitaps 34 | import bitcoinlib.services.litecoinblockexplorer 35 | import bitcoinlib.services.blockstream 36 | import bitcoinlib.services.blocksmurfer 37 | import bitcoinlib.services.mempool 38 | import bitcoinlib.services.bitflyer 39 | import bitcoinlib.services.blockbook 40 | import bitcoinlib.services.electrumx 41 | import bitcoinlib.services.nownodes 42 | import bitcoinlib.services.blockbook1 43 | -------------------------------------------------------------------------------- /docs/_static/manuals.setup-bcoin.rst: -------------------------------------------------------------------------------- 1 | How to connect Bitcoinlib to a Bcoin node 2 | ========================================= 3 | 4 | Bcoin is a full bitcoin node implementation, which can be used to parse the blockchain, send transactions and run a 5 | wallet. With a Bcoin node you can retrieve transaction and utxo information for specific addresses, this is not easily 6 | possible with a `Bitcoind `_ node. So if you want to use Bitcoinlib's 7 | wallet functionality for instance and not be dependent on external providers the best option is to run a local 8 | Bcoin node, `Blockbook `_ or `ElectrumX `_ server. 9 | 10 | 11 | Install Bcoin node 12 | ------------------ 13 | 14 | You can find some instructions on how to install a bcoin node on https://media.blocksmurfer.io/install-bcoin-node-ubuntu.html. 15 | 16 | There are also some Docker images available. We have created a Docker image with the most optimal settings for 17 | bitcoinlib. You can install them with the following command. 18 | 19 | .. code-block:: bash 20 | 21 | docker pull blocksmurfer/bcoin 22 | 23 | 24 | Use Bcoin node with Bitcoinlib 25 | ------------------------------ 26 | 27 | To use Bcoin with bitcoinlib add the credentials to the providers.json configuration file in the .bitcoinlib directory. 28 | 29 | .. code-block:: json 30 | 31 | "bcoin": { 32 | "provider": "bcoin", 33 | "network": "bitcoin", 34 | "client_class": "BcoinClient", 35 | "provider_coin_id": "", 36 | "url": "https://user:pass@localhost:8332/", 37 | "api_key": "", 38 | "priority": 20, 39 | "denominator": 100000000, 40 | "network_overrides": null, 41 | "timeout": 0 42 | }, 43 | 44 | You can increase the priority so the Service object always connects to the Bcoin node first. 45 | -------------------------------------------------------------------------------- /examples/blocks.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Block examples 6 | # 7 | # © 2020 - 2024 June - 1200 Web Development 8 | # 9 | 10 | from pprint import pprint 11 | from bitcoinlib.blocks import * 12 | from bitcoinlib.services.services import * 13 | 14 | 15 | print("=== Create Block object (block 120000) ===") 16 | # def __init__(self, block_hash, version, prev_block, merkle_root, time, bits, nonce, transactions=None, 17 | # height=None, confirmations=None, network=DEFAULT_NETWORK): 18 | b = Block(block_hash='0000000000000e07595fca57b37fea8522e95e0f6891779cfd34d7e537524471', version=1, 19 | prev_block='000000000000337828025b947973252acf8d668b3bb459c1c6e70b2e5827bca4', 20 | merkle_root='6dbba50b72ad0569c2449090a371516e3865840e905483cac0f54d96944eee28', 21 | time=1303687201, bits=453031340, nonce=4273989260, height=120000) 22 | pprint(b.as_dict()) 23 | 24 | 25 | print("=== Parse raw block (block 100) ===") 26 | raw_block = "010000007de867cc8adc5cc8fb6b898ca4462cf9fd667d7830a275277447e60800000000338f121232e169d3100edd82004dc2a1f0e1f030c6c488fa61eafa930b0528fe021f7449ffff001d36b4af9a0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d02fd04ffffffff0100f2052a01000000434104f5eeb2b10c944c6b9fbcfff94c35bdeecd93df977882babc7f3a2cf7f5c81d3b09a68db7f0e04f21de5d4230e75e6dbe7ad16eefe0d4325a62067dc6f369446aac00000000" 27 | b = Block.parse_bytes(bytes.fromhex(raw_block), height=100, parse_transactions=True) 28 | pprint(b.as_dict()) 29 | 30 | 31 | print("=== Get Bitcoin block with height 430000 from service providers ===") 32 | srv = Service() 33 | b = srv.getblock(430000) 34 | pprint(b.as_dict()) 35 | 36 | 37 | print("=== Get Litecoin block with height 1000000 from service providers ===") 38 | srv = Service(network='litecoin') 39 | b = srv.getblock(1000000) 40 | pprint(b.as_dict()) 41 | -------------------------------------------------------------------------------- /docs/_static/manuals.setup-blockbook.rst: -------------------------------------------------------------------------------- 1 | How to connect Bitcoinlib to a Blockbook server 2 | =============================================== 3 | 4 | Trezor's Blockbook is a back-end service for Trezor Suite and can also be used as back-end service provider for 5 | Bitcoinlib. Blockbook indexes addresses, transactions, unspent outputs and balances and can be used for fast 6 | blockchain queries. Blockbook also support Litecoin, Dash, Dogecoin and various testnets. 7 | 8 | If you want to use Bitcoinlib as a wallet, run many blockchain queries or write an application which is dependent on 9 | frequent requests to blockchain service providers you should use a Blockbook or 10 | `ElectrumX `_ service or install a 11 | `Bcoin `_ node. 12 | 13 | 14 | Install Blockbook server 15 | ------------------------ 16 | 17 | You can find some instructions on how to install a Blockbook server on 18 | https://media.blocksmurfer.io/blockbook-setup-as-bitcoinlib-backend.html. 19 | 20 | You will need a powerful server with enough memory and diskspace. Blockbook runs a full Bitcoin core node on the 21 | background, and maintains a large RocksDB database to store additional blockchain data. But once installed and 22 | synchronised it runs fast and smooth. 23 | 24 | 25 | Use Blockbook with Bitcoinlib 26 | ----------------------------- 27 | 28 | To use Blockbook with bitcoinlib add the credentials to the providers.json configuration file in the .bitcoinlib directory. 29 | 30 | .. code-block:: json 31 | 32 | "blockbook": { 33 | "provider": "blockbook", 34 | "network": "bitcoin", 35 | "client_class": "BlockbookClient", 36 | "provider_coin_id": "", 37 | "url": "https://:9130/", 38 | "api_key": "", 39 | "priority": 20, 40 | "denominator": 100000000, 41 | "network_overrides": null, 42 | "timeout", 0 43 | } 44 | 45 | You can increase the priority so the Service object always connects to the Blockbook service first. 46 | -------------------------------------------------------------------------------- /examples/bitcoind_regtest.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Bitcoind regtest network example 6 | # 7 | # © 2023 December - 1200 Web Development 8 | # 9 | 10 | from bitcoinlib.services.bitcoind import * 11 | from bitcoinlib.wallets import wallet_create_or_open 12 | from bitcoinlib.services.services import Service 13 | from pprint import pprint 14 | 15 | bdc = BitcoindClient(base_url='http://rpcuser:pwd@localhost:18444') 16 | 17 | walletname = 'regtesttestwallet' 18 | walletbcl = 'regtestwallet' 19 | 20 | print("Current blockheight is %d" % bdc.proxy.getblockcount()) 21 | 22 | print("Open or create a new wallet") 23 | wallets = bdc.proxy.listwallets() 24 | if walletname not in wallets: 25 | wallet = bdc.proxy.createwallet(walletname) 26 | 27 | address = bdc.proxy.getnewaddress() 28 | print("Mine 50 blocks and generate regtest coins to address %s" % address) 29 | bdc.proxy.generatetoaddress(50, address) 30 | 31 | print("Current blockheight is %d" % bdc.proxy.getblockcount()) 32 | print("Current balance is %d" % bdc.proxy.getbalance()) 33 | 34 | w = wallet_create_or_open(walletname, network='regtest') 35 | address2 = w.get_key().address 36 | print("\nSend 10 rBTC to address %s" % address2) 37 | bdc.proxy.settxfee(0.00002500) 38 | txid = bdc.proxy.sendtoaddress(address2, 10) 39 | print("Resulting txid: %s" % txid) 40 | tx = bdc.proxy.gettransaction(txid) 41 | pprint(tx) 42 | 43 | 44 | print("\n\nConnect to bitcoind regtest with Service class and retrieve new transaction and utxo info") 45 | srv = Service(network='regtest', providers=['bitcoind'], cache_uri='') 46 | print("Blockcount %d" % srv.blockcount()) 47 | b = srv.getblock(500) 48 | pprint(b.as_dict()) 49 | b.transactions[0].info() 50 | 51 | t = srv.gettransaction(txid) 52 | t.info() 53 | 54 | utxos = srv.getutxos(address) 55 | print(srv.getbalance(address)) 56 | for utxo in utxos: 57 | print(utxo['txid'], utxo['value'], utxo['confirmations'], utxo['block_height']) -------------------------------------------------------------------------------- /bitcoinlib/tools/sign_raw.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # Import and sign multisig transaction with private key wif or passphrase 6 | # 7 | # © 2019-2023 April - 1200 Web Development 8 | # 9 | 10 | from pprint import pprint 11 | from bitcoinlib.transactions import Transaction 12 | from bitcoinlib.mnemonic import Mnemonic 13 | from bitcoinlib.keys import HDKey 14 | from bitcoinlib.services.services import Service 15 | from bitcoinlib.wallets import wallet_create_or_open, wallet_delete_if_exists 16 | 17 | network = 'testnet' 18 | 19 | # # Example wallet 20 | # phrase1 = 'meadow bag inquiry eyebrow exotic onion skill clerk dish hunt caught road' 21 | # phrase2 = 'east amount soap pause erosion invite mom finger oak still vast bacon' 22 | # password2 = 'test' 23 | # k2 = HDKey.from_passphrase(phrase2, network=network, password=password2, key_type='single').public() 24 | # # wallet_delete_if_exists('Sign_raw_testwallet', force=True) 25 | # w = wallet_create_or_open('Sign_raw_testwallet', [phrase1, k2], network=network, cosigner_id=0) 26 | # w.get_key() 27 | # w.utxos_update() 28 | # w.info() 29 | # t = w.sweep(w.new_key().address, 10000, fee=1000) 30 | # raw_tx = t.raw_hex() 31 | # t.info() 32 | 33 | # Raw partially signed transaction 34 | raw_tx = '' 35 | if not raw_tx: 36 | raw_tx = input("Paste raw transaction hex: ") 37 | 38 | t = Transaction.import_raw(raw_tx) 39 | 40 | key_str = input("Enter private key or mnemonic passphrase: ") 41 | if len(key_str.split(" ")) < 2: 42 | hdkey = HDKey(key_str) 43 | else: 44 | password = input("Enter password []:") 45 | seed = Mnemonic().to_seed(key_str, password) 46 | hdkey = HDKey.from_seed(seed, network=network) 47 | 48 | t.sign(hdkey) 49 | t.info() 50 | 51 | print("Raw signed transaction: ") 52 | print(t.raw_hex()) 53 | 54 | if input("Try to send transaction [y/n] ") in ['y', 'Y']: 55 | srv = Service(network=network) 56 | res = srv.sendrawtransaction(t.raw()) 57 | pprint(res) 58 | -------------------------------------------------------------------------------- /docs/_static/service-providers.rst: -------------------------------------------------------------------------------- 1 | How to use Service Providers? 2 | ============================= 3 | 4 | Bitcoinlib uses external service providers to get address, transaction and blockchain information for your wallet or other application. 5 | 6 | **If you install Bitcoinlib it uses external and free service providers, so you do not have to do anything extra to get it working.** 7 | 8 | However those free providers have transaction limits, slow responses, downtimes or changing Api's which can cause unexpected results. For larges wallets, complicated projects with a lot of requests or production environments, you basically have three options. 9 | 10 | 11 | Setup a local node 12 | ------------------ 13 | 14 | You need a server with a large storage and a stable connection at your home or a hosting provider. And then follow the manual to setup an `Blockbook `_, 15 | `ElectrumX `_ or `Bcoin `_ server. 16 | 17 | You can also setup a `Bitcoind Node `_, but a standard node cannot get utxo's or address information for external wallet addresses. 18 | 19 | 20 | Get a payed subscription 21 | ------------------------ 22 | 23 | There a many providers which offer a payed subscription, many unfortunately are really expensive. 24 | 25 | At the time of writing Nownodes offers a $20 a month subscription. You can create an account and use the example providers configuration file at https://github.com/1200wd/bitcoinlib/blob/master/bitcoinlib/data/providers.examples-nownodes.json 26 | 27 | We have not tested other payed service providers at the moment, but please let us know if you found any good options. 28 | 29 | 30 | Add another service Provider 31 | ---------------------------- 32 | 33 | You can also `Add another service Provider `_ to this library by updating settings and write a new service provider class. 34 | 35 | If you tested the new provider thoroughly don't forget to create a pull request ;) -------------------------------------------------------------------------------- /docs/_static/manuals.setup-electrumx.rst: -------------------------------------------------------------------------------- 1 | How to connect Bitcoinlib to an ElectrumX server 2 | ================================================ 3 | 4 | ElectrumX is a backend for the well-known Electrum Bitcoin wallets. It can also be used as back-end service provider 5 | for Bitcoinlib wallets. ElectrumX uses a running Bitcoin core node and adds an extra layer with indexes to allow 6 | for fast address, unspent outputs and transactions queries. ElectrumX is focussed on Bitcoin wallets and has no option 7 | to query blocks and large non-standard transactions. 8 | 9 | If you want to use Bitcoinlib as a wallet, run many blockchain queries or write an application which is dependent on 10 | frequent requests to blockchain service providers you should use a `Blockbook `_, 11 | ElectrumX or `Bcoin `_ server. 12 | 13 | 14 | Install ElectrumX server 15 | ------------------------ 16 | 17 | You can find instructions on how to install a ElectrumX server on 18 | https://media.blocksmurfer.io/install-electrumx-as-bitcoinlib-backend.html 19 | 20 | 21 | Use ElectrumX with Bitcoinlib 22 | ----------------------------- 23 | 24 | To use ElectrumX with bitcoinlib add the credentials to the providers.json configuration file in the .bitcoinlib directory. 25 | 26 | .. code-block:: json 27 | 28 | "localhost.electrumx": { 29 | "provider": "electrumx", 30 | "network": "bitcoin", 31 | "client_class": "ElectrumxClient", 32 | "provider_coin_id": "", 33 | "url": "localhost:50001", 34 | "api_key": "", 35 | "priority": 100, 36 | "denominator": 1, 37 | "network_overrides": null 38 | }, 39 | 40 | You can increase the priority so the Service object always connects to the ElectrumX service first. 41 | 42 | ElectrumX also support Bitcoin testnet, testnet4, regtest and signet. Other coins such as Dogecoin, Litecoin 43 | and Dash are also supported. To setup simply update the ports and add add the coin as argument when calling ElectrumX, 44 | for instance for testnet4 use: electrumx_server --testnet4 45 | -------------------------------------------------------------------------------- /tests/config.ini.unittest: -------------------------------------------------------------------------------- 1 | # 2 | # BITCOINLIB - Configuration file for Unit Testing 3 | # © 2020 March - 1200 Web Development 4 | # 5 | # Paths to data, logs and configuration files, all paths are relative to bitcoinlib source directory if no 6 | # absolute path is provided. 7 | # 8 | # In this configuration file you can overwrite default settings, for a normal installation it is not necessary to 9 | # change anything here. 10 | # 11 | 12 | [locations] 13 | # Location of BitcoinLib data, configuration and log files. Relative paths will be based in installation directory 14 | ;data_dir=~/.bitcoinlib 15 | 16 | # Default directory for database files. Relative paths will be based in user or bitcoinlib installation directory. Only used for sqlite files. 17 | ;database_dir=database 18 | 19 | # Default database file for wallets, keys and transactions. Relative paths will be based in 'database_dir' 20 | ;default_databasefile=bitcoinlib.sqlite 21 | ;default_databasefile_cache==bitcoinlib_cache.sqlite 22 | 23 | 24 | [common] 25 | # Allow database threads in SQLite databases 26 | ;allow_database_threads=True 27 | 28 | # Time for request to service providers in seconds 29 | timeout_requests=2 30 | 31 | # Default language for Mnemonic passphrases 32 | ;default_language=english 33 | 34 | # Default network when creating wallets, transaction, keys, etc. 35 | ;default_network=bitcoin 36 | 37 | # Default witness_type for new wallets and keys 38 | ;default_witness_type=legacy 39 | 40 | # Use caching for service providers 41 | ;service_caching_enabled=True 42 | 43 | # Encrypt private key field in database using symmetrically EAS encryption. 44 | # You need to set the password in the DB_FIELD_ENCRYPTION_KEY environment variable. 45 | ;database_encryption_enabled=False 46 | 47 | [logs] 48 | # Enable own logging for this library. If true logs will be stored in the log/bitcoinlib.log file. 49 | # Set to False if this library is part of another library or software and you want to handle logs yourself. 50 | ;enable_bitcoinlib_logging=True 51 | 52 | # Log file name. Relative paths will be based in data_dir directory 53 | ;log_file=~bitcoinlib.log 54 | 55 | # Loglevel for this library, options: CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET 56 | ;loglevel=WARNING 57 | -------------------------------------------------------------------------------- /tests/config_encryption.ini.unittest: -------------------------------------------------------------------------------- 1 | # 2 | # BITCOINLIB - Configuration file for Unit Testing 3 | # © 2020 March - 1200 Web Development 4 | # 5 | # Paths to data, logs and configuration files, all paths are relative to bitcoinlib source directory if no 6 | # absolute path is provided. 7 | # 8 | # In this configuration file you can overwrite default settings, for a normal installation it is not necessary to 9 | # change anything here. 10 | # 11 | 12 | [locations] 13 | # Location of BitcoinLib data, configuration and log files. Relative paths will be based in installation directory 14 | ;data_dir=~/.bitcoinlib 15 | 16 | # Default directory for database files. Relative paths will be based in user or bitcoinlib installation directory. Only used for sqlite files. 17 | ;database_dir=database 18 | 19 | # Default database file for wallets, keys and transactions. Relative paths will be based in 'database_dir' 20 | ;default_databasefile=bitcoinlib.sqlite 21 | ;default_databasefile_cache==bitcoinlib_cache.sqlite 22 | 23 | 24 | [common] 25 | # Allow database threads in SQLite databases 26 | ;allow_database_threads=True 27 | 28 | # Time for request to service providers in seconds 29 | timeout_requests=2 30 | 31 | # Default language for Mnemonic passphrases 32 | ;default_language=english 33 | 34 | # Default network when creating wallets, transaction, keys, etc. 35 | ;default_network=bitcoin 36 | 37 | # Default witness_type for new wallets and keys 38 | ;default_witness_type=legacy 39 | 40 | # Use caching for service providers 41 | ;service_caching_enabled=True 42 | 43 | # Encrypt private key field in database using symmetrically EAS encryption. 44 | # You need to set the password in the DB_FIELD_ENCRYPTION_KEY environment variable. 45 | database_encryption_enabled=True 46 | 47 | [logs] 48 | # Enable own logging for this library. If true logs will be stored in the log/bitcoinlib.log file. 49 | # Set to False if this library is part of another library or software and you want to handle logs yourself. 50 | ;enable_bitcoinlib_logging=True 51 | 52 | # Log file name. Relative paths will be based in data_dir directory 53 | ;log_file=~bitcoinlib.log 54 | 55 | # Loglevel for this library, options: CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET 56 | ;loglevel=WARNING 57 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = bitcoinlib 3 | version = 0.7.6 4 | url = http://github.com/1200wd/bitcoinlib 5 | author = 1200wd 6 | author_email = info@1200wd.com 7 | license= GNU3 8 | description = Bitcoin cryptocurrency Library 9 | long_description = file: README.rst 10 | keywords = bitcoin library cryptocurrency wallet crypto keys segwit transactions blocks 11 | classifiers = 12 | Development Status :: 4 - Beta 13 | License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+) 14 | Intended Audience :: Developers 15 | Intended Audience :: Financial and Insurance Industry 16 | Intended Audience :: Science/Research 17 | Intended Audience :: Information Technology 18 | License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+) 19 | Natural Language :: English 20 | Operating System :: OS Independent 21 | Operating System :: Microsoft :: Windows 22 | Operating System :: POSIX 23 | Programming Language :: Python :: 3.8 24 | Programming Language :: Python :: 3.9 25 | Programming Language :: Python :: 3.10 26 | Programming Language :: Python :: 3.11 27 | Programming Language :: Python :: 3.12 28 | Topic :: Software Development :: Libraries :: Python Modules 29 | Topic :: Security :: Cryptography 30 | Topic :: Office/Business :: Financial :: Accounting 31 | 32 | [options] 33 | packages = find: 34 | zip_safe = True 35 | include_package_data = True 36 | install_requires = 37 | requests >= 2.25.0 38 | fastecdsa >= 2.3.0;platform_system!="Windows" 39 | ecdsa >= 0.18;platform_system=="Windows" 40 | pycryptodome >= 3.19.0 41 | SQLAlchemy >= 2.0.20 42 | numpy == 1.24.4;python_version<"3.9" 43 | numpy >= 1.24.4, <2;python_version>="3.9" 44 | 45 | [options.extras_require] 46 | dev = 47 | scrypt >= 0.8.20;platform_system!="Windows" 48 | sphinx >= 7.1.0;python_version<"3.9" 49 | sphinx >= 7.2.0;python_version>="3.9" 50 | psycopg >= 3.1.16 51 | coveralls >= 4.0.1 52 | mysql-connector-python >= 8.4.0 53 | mysqlclient >= 2.2.0 54 | sphinx_rtd_theme >= 2.0.0 55 | Cython>=3.0.8 56 | win-unicode-console;platform_system=="Windows" 57 | 58 | [options.entry_points] 59 | console_scripts = 60 | cli-wallet = bitcoinlib.tools.clw:main 61 | clw = bitcoinlib.tools.clw:main 62 | -------------------------------------------------------------------------------- /docs/_static/manuals.security.rst: -------------------------------------------------------------------------------- 1 | Frequently Asked Questions 2 | ========================== 3 | 4 | Ten tips for more privacy and security when using Bitcoin and Bitcoinlib: 5 | 6 | 1. Run your own `Bitcoin core `_, 7 | `Bcoin node `_ or `Blockbook server `_, so you are not depending on external Blockchain API service providers anymore. 8 | This not only increases your privacy, but also makes your application much faster and more reliable. And as extra bonus 9 | you support the Bitcoin network. 10 | 2. Use multi-signature wallets. So you are able to store your private keys in separate (offline) locations. 11 | 3. Use a minimal amount of inputs when creating a transaction. This is default behavior of the Bitcoinlib Wallet 12 | object. You can set a hard limit when sending from a wallet with the max_utxos=1 attribute. 13 | 4. Use a random number of change outputs and shuffle order of inputs and outputs. This way it is not visible 14 | which output is the change output. In the Wallet object you can set the number_of_change_outputs to zero to 15 | generate a random number of change outputs. 16 | 5. `Encrypt your database or private keys `_ with SQLCipher or AES. 17 | 6. Use password protected private keys. For instance use a password when 18 | `creating wallets `_. 19 | 7. Backup private keys and passwords! We have no proof but I assume more bitcoins are lost because of lost private keys than there are lost due to hacking... 20 | 8. When using Bitcoinlib wallets the private keys are stored in a database. Make sure the database is in a safe location 21 | and check encryption, access rights, etc. Also check tip 2 and 5 again and see how you can minimize risks. 22 | 9. Test, try, review. Before working with any real value carefully test your applications using the testnet or small value transactions. 23 | 10. Read these tips, read some more about `Security `_ and `Privacy `_ 24 | and then think thoroughly about the best wallet setup, which is always a tradeoff between security, privacy and usability. 25 | -------------------------------------------------------------------------------- /examples/message_signing.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Sign Bitcoin Messages Examples 6 | # 7 | # © 2025 December - 1200 Web Development 8 | # 9 | 10 | from bitcoinlib.keys import HDKey, signed_message_parse, verify_message 11 | from bitcoinlib.wallets import wallet_create_or_open 12 | 13 | # 14 | # === Sign and verify a message with a wallet === 15 | # 16 | 17 | message = "This is a example of a signed message by a Bitcoinlib wallet" 18 | w = wallet_create_or_open('message-signing-wallet') 19 | sig = w.sign_message(message) 20 | signed_message = sig.as_signed_message(message) 21 | print(signed_message) 22 | 23 | # This prints a signed message such as: 24 | # 25 | # -----BEGIN BITCOIN SIGNED MESSAGE----- 26 | # This is a example of a signed message by a Bitcoinlib wallet 27 | # -----BEGIN SIGNATURE----- 28 | # bc1q065ed8cnxf9rr2v0qhatr2guruulr0jpgjycfu 29 | # J4YKfaKqHDJCidfWXYTRC191QYZ3slu8VzgFyUb6m7QJn7Yn2sif7Od1slvnhBV6pLqj2cnISyahVCIrrHQNe0I= 30 | # -----END BITCOIN SIGNED MESSAGE----- 31 | # 32 | # You can check if is valid with a Bitcoin node, online on some websites (https://checkmsg.org) or with Bitcoinlib: 33 | # 34 | 35 | message, signature, address, network = signed_message_parse(signed_message) 36 | print(f"Signature verified by verify_message method: {verify_message(message, signature, address, network)}") 37 | 38 | print(f"Signature verified by Bitcoinlib Wallet: {w.verify_message(message, sig)}") 39 | 40 | 41 | # 42 | # === Sign a message with a private key, and verify with public key === 43 | # 44 | 45 | message = "Message to sign with a private Key" 46 | print(f"\n\nSign this message with a private key: {message}") 47 | priv_key = HDKey() 48 | sig = priv_key.sign_message(message) 49 | 50 | pub_key = priv_key.public() 51 | print(f"Verified with Public Key: {pub_key.verify_message(message, sig)}") 52 | 53 | 54 | # 55 | # === Sign a message with a private key, and verify with only an address === 56 | # 57 | 58 | message = "Message to sign with a private Key, verify with only an address" 59 | print(f"\n\nSign this message with a private key (2): {message}") 60 | priv_key = HDKey() 61 | sig = priv_key.sign_message(message) 62 | 63 | addr = priv_key.address() 64 | print(f"Base64 encoded signature: {sig.as_base64()}") 65 | print(f"Address from private key: {addr}") 66 | print(f"Verified with address: {verify_message(message, sig.as_base64(), addr)}") 67 | -------------------------------------------------------------------------------- /examples/values.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Value class examples 6 | # 7 | # © 2020 December - 1200 Web Development 8 | # 9 | 10 | from bitcoinlib.values import Value 11 | 12 | 13 | print("A couple of ways to create a Value class with 10 bitcoins") 14 | print(Value("10")) 15 | print(Value("10 BTC")) 16 | print(Value("10000 mBTC", denominator=1)) 17 | print(Value(10)) 18 | print(Value("0.01 kBTC", denominator=1)) 19 | 20 | 21 | print("\nSome ways to represent a value of 0.12 BTC") 22 | print(Value("0.12 BTC")) 23 | print(Value("0.12 BTC", denominator=1)) 24 | print(Value("0.12 BTC", denominator='m')) 25 | print(Value("0.12 BTC", denominator='sat')) 26 | 27 | 28 | print("\nWith the str() method you can format the Value as string in any format") 29 | v = Value("512 mBTC") 30 | print(v.str()) 31 | print(v.str(denominator=1)) 32 | print(v.str(denominator=1, decimals=3)) 33 | print(v.str(denominator='sat')) 34 | print(v.str(denominator=0.01)) # a centi-bitcoin, not really standard, but you could use it 35 | print(v.str(denominator=1, currency_repr='symbol')) 36 | print(v.str(denominator=1, currency_repr='name')) 37 | 38 | print("\nThe str_unit() is a shorter version of str(denominator=1)") 39 | v = Value("512 mBTC") 40 | print(v.str(denominator=1)) 41 | print(v.str_unit()) 42 | 43 | print("\nThe denominator can also be determined automatically with str(denominator='auto') or str_auto()") 44 | print(Value('0.0000012 BTC').str_auto()) 45 | print(Value('0.0000012 BTC').str(denominator='auto')) 46 | print(Value('0.0005 BTC').str_auto()) 47 | 48 | print("\nYou can use any arithmetic operators on Value object") 49 | print(Value('50000 sat') == Value('5000 fin')) # 1 Satoshi equals 10 Finney, see https://en.bitcoin.it/wiki/Units 50 | print(Value('1 btc') > Value('2 btc')) 51 | print(Value('1000 LTC') / 5) 52 | print(Value('0.002 BTC') + 0.02) 53 | print(int(Value("10.1 BTC"))) 54 | print(float(Value("10.1 BTC"))) 55 | print(round(Value("10.123 BTC"), 2).str()) 56 | print(Value("10.1 BTC")) 57 | 58 | print("\nUnder the hood bitcoin works with values in the smallest denominator Satoshi's.") 59 | print("\nYou can get them with the value_sat() attribute") 60 | v = Value("512 mBTC") 61 | print(v.value_sat) 62 | 63 | print("When serializing transactions or scripts there is a bytes or hex version available") 64 | print(Value('12345 sat').to_bytes()) 65 | print(Value('12345 sat').to_hex()) 66 | -------------------------------------------------------------------------------- /tests/test_custom.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # Unit Tests Custom classes 5 | # © 2018 February - 1200 Web Development 6 | # 7 | 8 | 9 | from datetime import datetime 10 | 11 | 12 | class CustomAssertions: 13 | 14 | def assertDictEqualExt(self, result_dict, expected_dict, none_allowed=None): 15 | """ 16 | Compare dictionaries, skip items not found in expected dictionary. 17 | 18 | Lists and recursion's in dictionaries are allowed. 19 | 20 | :param result_dict: First dictionary with results 21 | :type result_dict: dict 22 | :param expected_dict: Second dictionary with expected values 23 | :type expected_dict: dict 24 | :param none_allowed: List of fields for which None value in result_dict is allowed 25 | :type none_allowed: list 26 | 27 | :return bool: Dictionaries are identical? 28 | """ 29 | if none_allowed is None: 30 | none_allowed = [] 31 | if not isinstance(expected_dict, dict): 32 | if expected_dict == result_dict: 33 | return True 34 | else: 35 | raise AssertionError("Different value for %s != %s" % (result_dict, expected_dict)) 36 | expected_keys = expected_dict.keys() 37 | for k in result_dict: 38 | if k not in expected_keys: 39 | continue 40 | if isinstance(result_dict[k], dict): 41 | self.assertDictEqualExt(result_dict[k], expected_dict[k], none_allowed) 42 | elif isinstance(result_dict[k], list): 43 | for i in range(len(result_dict[k])): 44 | if not isinstance(expected_dict[k], list): 45 | raise AssertionError("No list expected for %s attribute, expected '%s' but received: '%s'" % 46 | (k, expected_dict[k], result_dict[k])) 47 | self.assertDictEqualExt(result_dict[k][i], expected_dict[k][i], none_allowed) 48 | elif result_dict[k] != expected_dict[k]: 49 | if isinstance(result_dict[k], datetime): 50 | if result_dict[k].date() == expected_dict[k].date(): 51 | continue 52 | if result_dict[k] is not None or k not in none_allowed: 53 | raise AssertionError("Different value for '%s': %s != %s" % (k, result_dict[k], expected_dict[k])) 54 | -------------------------------------------------------------------------------- /examples/transactions_decompose_simple.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Decompose a simple transaction 6 | # 7 | # © 2017 September - 1200 Web Development 8 | # 9 | 10 | from pprint import pprint 11 | from bitcoinlib.transactions import * 12 | 13 | 14 | print("\n=== Example of a basic raw transaction with 1 input and 2 outputs (destination and change address). ===") 15 | rt = '01000000' # Version bytes in Little-Endian (reversed) format 16 | # --- INPUTS --- 17 | rt += '01' # Number of UTXO's inputs 18 | # Previous transaction hash (Little Endian format): 19 | rt += 'a3919372c9807d92507289d71bdd38f10682a49c47e50dc0136996b43d8aa54e' 20 | rt += '01000000' # Index number of previous transaction 21 | # - INPUT: SCRIPTSIG - 22 | rt += '6a' # Size of following unlocking script (ScripSig) 23 | rt += '47' # PUSHDATA 47 - Push following 47 bytes signature to stack 24 | rt += '30' # DER encoded Signature - Sequence 25 | rt += '44' # DER encoded Signature - Length 26 | rt += '02' # DER encoded Signature - Integer 27 | rt += '20' # DER encoded Signature - Length of X: 28 | rt += '1f6e18f4532e14f328bc820cb78c53c57c91b1da9949fecb8cf42318b791fb38' 29 | rt += '02' # DER encoded Signature - Integer 30 | rt += '20' # DER encoded Signature - Length of Y: 31 | rt += '45e78c9e55df1cf3db74bfd52ff2add2b59ba63e068680f0023e6a80ac9f51f4' 32 | rt += '01' # SIGHASH_ALL 33 | # - INPUT: PUBLIC KEY - 34 | rt += '21' # PUSHDATA 21 - Push following 21 bytes public key to stack: 35 | rt += '0239a18d586c34e51238a7c9a27a342abfb35e3e4aa5ac6559889db1dab2816e9d' 36 | rt += 'feffffff' # Sequence 37 | # --- OUTPUTS --- 38 | rt += '02' # Number of outputs 39 | rt += '3ef5980400000000' # Output value in Little Endian format 40 | rt += '19' # Script length, of following scriptPubKey: 41 | rt += '76a914af8e14a2cecd715c363b3a72b55b59a31e2acac988ac' 42 | rt += '90940d0000000000' # Output value #2 in Little Endian format 43 | rt += '19' # Script length, of following scriptPubKey: 44 | rt += '76a914f0d34949650af161e7cb3f0325a1a8833075165088ac' 45 | rt += 'b7740f00' # Locktime 46 | 47 | print("\nImport and deserialize raw transaction") 48 | t = Transaction.parse_hex(rt) 49 | pprint(t.as_dict()) 50 | output_script = t.outputs[0].lock_script 51 | print("\nOutput 1st Script Type: %s " % Script.parse_bytes(output_script).script_types[0]) 52 | print("Output 1st Script String: %s" % Script.parse_bytes(output_script)) 53 | print("\nt.verified() ==> %s" % t.verify()) 54 | -------------------------------------------------------------------------------- /examples/wallets_delete_unconfirmed_txs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Delete aged unconfirmed transactions from wallet 6 | # 7 | # © 2024 June - 1200 Web Development 8 | # 9 | 10 | from time import sleep 11 | from bitcoinlib.wallets import * 12 | 13 | 14 | # Create wallet and add utxo's 15 | pkm = 'monitor orphan turtle stage december special' 16 | wallet_delete_if_exists("wallet_remove_old_unconfirmed_transactions", force=True) 17 | w = Wallet.create("wallet_remove_old_unconfirmed_transactions", keys=pkm, network='bitcoinlib_test') 18 | w.get_keys(number_of_keys=4) 19 | utxos = [ 20 | { 21 | "address": "blt1q4dugy6d7qz7226mk6ast3nz23z7ctd80mymle3", 22 | "script": "", 23 | "confirmations": 2, 24 | "output_n": 1, 25 | "txid": "e6192f6dafa689ac8889b466d2dd3eb2bb55b76c7305b4a2a6a31de6c9991aeb", 26 | "value": 1829810 27 | }, 28 | { 29 | "address": "blt1q82l3c2d37yjxe0r9a7qn9v7c9y7hnaxp398kc0", 30 | "script": "", 31 | "confirmations": 0, 32 | "output_n": 0, 33 | "txid": "5891c85595193d0565fe418d5c5192c1297eafbef36c28bcab2ac3341ee68e71", 34 | "value": 2389180 35 | }, 36 | { 37 | "address": "blt1qdtez8t797m74ar8wuvedw50jmycefwstfk8ulz", 38 | "script": "", 39 | "confirmations": 100, 40 | "output_n": 0, 41 | "txid": "7e87a63a0233615a5719a782a0b1c85de521151d8648e7d7244155a2caf7dd47", 42 | "value": 99389180 43 | }, 44 | { 45 | "address": "blt1qdtez8t797m74ar8wuvedw50jmycefwstfk8ulz", 46 | "script": "", 47 | "confirmations": 100, 48 | "output_n": 0, 49 | "txid": "a4ef4aef09839a681419b80d5b6228b0089af39a4483896c9ac106192ac1ec34", 50 | "value": 838180 51 | }, 52 | ] 53 | w.utxos_update(utxos=utxos) 54 | w.send_to('blt1qvtaw9m9ut96ykt2n2kdra8jpv3m5z2s8krqwsv', 50000, broadcast=True) 55 | w.info() 56 | 57 | print("We now have a wallet with 5 utxo's, of which 3 are unconfirmed") 58 | print(f"UTXO count: {len(w.utxos())}") 59 | 60 | print("Try to remove unconfirmed utxo's which are more then 1 hour old (doesn't delete anything)") 61 | w.transactions_remove_unconfirmed(1) 62 | print(f"UTXO count: {len(w.utxos())}") 63 | 64 | sleep(1) # sleep a little to avoid glitches in the time-matrix 65 | print("Remove all unconfirmed utxo's, and mark previous outputs a unspent again") 66 | w.transactions_remove_unconfirmed(0) 67 | print(f"UTXO count: {len(w.utxos())}") 68 | 69 | w.info() -------------------------------------------------------------------------------- /docs/_static/manuals.caching.rst: -------------------------------------------------------------------------------- 1 | Caching 2 | ======= 3 | 4 | Results from queries to service providers are stored in a cache database. Once transactions are confirmed and stored 5 | on the blockchain they are immutable, so they can be stored in a local cache for an indefinite time. 6 | 7 | What is cached? 8 | --------------- 9 | 10 | The cache stores transactions, but also addresses information and transactions-address relations. This speeds up 11 | the gettransactions(), getutxos() and getbalance() method since all old transactions can be read from cache, and we 12 | only have to check if new transactions are available for a certain address. 13 | 14 | The latest block - block number of the last block on the network - is stored in cache for 60 seconds. So the Service 15 | object only checks for a new block every minute. 16 | 17 | The fee estimation for a specific network is stored for 10 minutes. 18 | 19 | 20 | Using other databases 21 | --------------------- 22 | 23 | By default the cache is stored in an SQLite database in the database folder: ~/.bitcoinlib/databases/bitcoinlib_cache.sqlite 24 | The location and type of database can be changed in the config.ini with the default_databasefile_cache variable. 25 | 26 | Other types of databases can be used as well, check 27 | http://bitcoinlib.readthedocs.io/en/latest/_static/manuals.databases.html for more information. 28 | 29 | 30 | Disable caching 31 | --------------- 32 | 33 | Caching is enabled by default. To disable caching set the environment variable SERVICE_CACHING_ENABLED to False or 34 | set this variable (service_caching_enabled) in the config.ini file placed in your .bitcoinlib/ directory. 35 | 36 | 37 | Troubleshooting 38 | --------------- 39 | 40 | Nothing is cached, what is the problem? 41 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 42 | 43 | - If the min_providers parameter is set to 2 or more caching will be disabled. 44 | - If a service providers returns an incomplete result no cache will be stored. 45 | - If the after_txid parameter is used in gettransactions() or getutxos() no cache will be stored if this 46 | 'after_txid' transaction is not found in the cache. Because the transaction cache has to start from the first 47 | transaction for a certain address and no gaps can occur. 48 | 49 | I get incomplete or incorrect results! 50 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 51 | 52 | - Please post an issue in the Github issue-tracker so we can take a look. 53 | - You can delete the database in ~/.bitcoinlib/databases/bitcoinlib_cache.sqlite for an easy fix, or disable caching 54 | if that really doesn't work out. 55 | -------------------------------------------------------------------------------- /tests/electrum_keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "1PtJoRLNv2NHZ9nb6AWS4RTy743S5m319F": "KyAzUHt2QEc7ui5mxofv6LKZMevr1JJuKzLQGcQzWGaT5GcruLxF", 3 | "1DXQSY5UhFdWHUxMGjuFQSx4CexzVAj1N3": "KxZriLH8Dgd1ZSQgihoNBNBU9rNc4boMWBShdi2xJB53rVsj63kT", 4 | "121SfwAydXcZPcKXSg1nvhdz7qGRuyj1Bd": "L1DxkdDULcWEmgpD8bBs94MZGYaMo6x8yHyr4t8ZEWaTgP5wwf2J", 5 | "12fPKMowzB69nL216Ph4Hvb5y2Ss3KAUP4": "KyrEkba2sGLYvKPNRGK12z4qzKAt6EftYPx9YeLaVWpVK7MGBsk4", 6 | "1Ay3p8Hw8V7safg57o9HhtLG4fvvtT7wGq": "KwGLJfvaW6hEqkPV3yZwmqELKwevHtfAorxpDhEY89f4fEGY3jtg", 7 | "1HpnbPRnhrnDL52aAKNtgKBYrx9XDeFH8L": "L3K6xnNYXnSKaEAwPerkMFSurNCcmLZpB6YAMFP5kJHWUmrwJQhG", 8 | "1C3Xfr4JXxqBzgXDbxuFFPf9jHYK9WNoUr": "KzjuPDTQvJ7MMxWQsv5oRHMJNEE8Tu5aWKu9T79vKfxaieJgVcEb", 9 | "1H5fuFeDmhndxsfMkpUxdEv646Fxb8ZDfu": "Kz3U49iVfXJNH9XYieAKQhbi3C4jAWddBGkob3MWi8gawVmhJjLd", 10 | "1Q6xS6119qZsSLrz8E8gQqi22iHSvT4Xqx": "Ky3ASZKMPQAZQGAoCeyS3pFesEwD68sziLL2sZVzMarmnxv5w2rz", 11 | "1BAd5sV94Ud7KQQd6ij6pKn8QwNF4L8WMT": "L5BzgZ48VyJ4TJMZCdsm6K6XYZ3m5CPCwvQngUayhhYzSVB4TUxh", 12 | "1Cg5gvP387jNpmqeXWbJFQATA7AtFWjd1": "L3YBAoFxjpyy73b9AFnzVGwHKyqagUufqtXTMcxrC1ZftmGqKpKn", 13 | "17ohPH7LVoSE9V8WAyESRSnYG1HX2gUx3E": "L4dnPcsDcCvTFXdauMrbHY2rfaoffXQcYRbLicFRpen32WyLtoEa", 14 | "1NdYptESFaTrvkseewCGLBJyfxRVR2LJ9y": "L3W2EDXT4HHttdroENWqFUYfM3n5XLviLjxGjBEbS71YkdPzSdt5", 15 | "1EvvZ7UjhjSwmib9wJXfEUdmQsukwuNHx7": "L3JsZcQr4xYEfuBitJmwj71YxWMvRAvzckSuNqeR5xSwpxymrBEv", 16 | "1BgWQGdsJunKWe1Y4NBUDM7hcxMPD7p96H": "L1M5pCXza7TTwczGeLypzKHS5wDjyiWip4qxiGzPusLGTz2sXJJ8", 17 | "13emzc9kDTsGvfbwFAt1FdcZt32XXjRvtn": "KwT5tWpmmARWtqWFL9jnhFrgYthCBCccEyXeh6vNpQmcsZPz36CB", 18 | "1GjQz6xEWh562wcHiUJ4LtRpAmsZbbnK6n": "KxTH3bG1RQgPb3e1mt7MfuyPXVaHsujXa7CMrKxCNNorwAvmERgV", 19 | "15Z1rrhLvTiUr9zft7vgY9xiH6GYoVH3S6": "L4AAqobtUd21qKuWxogJXycf1up5SFkVfpwozUtomif6Hx39BpXH", 20 | "1GcJUfD957MvusrNY1PEp6QZ5MMCFjJZyg": "KxvQKjQ3Yso6BRbpLWJTfAofkMXZtXVYKN3LjzgzkFVyWwgZpyDw", 21 | "1LKTuwS5feBNfrNuD2hTsbqoLXYo53XP7U": "L2oEPFRCUNXAmmddE3vVP9QAgbJYxM6JeJYxZRdnzq7EVXyBGqMP", 22 | "1M8KDfF4rcoyv8msdXPWz38HGLEn89kaxc": "L59v4KXFHRU1xNhgiDrerEmUDYL9eKMhY3yQxkYqsMZjxpwyCpBz", 23 | "14PbNQmbZexrUBHrhbpk3BjcTEpRVyqU7a": "L5HmE1YBr7LLaX1X6KkeETashhEP5NmXbKM2seT3mpBrnwzTAqxa", 24 | "13XtU64DuyrfARb1LbQiRWj3TsdAsvanjM": "L5WQy5VbErSjfS2w3KirD7mTw4qpjASQw1whyhYjhNxJdnoyqMf9", 25 | "14XBjkyca2uYGz4norjek3MoASxKzQ5cQz": "KwUjRySDPRKeYFSr5yq3M6NyPdzVjCJcMthrnnzpJN9pmuGrDvZJ", 26 | "1KgNtL7W8YjsvarU3j6i18xfEHkNKJqpGC": "Kzi4kq3MmvwsaXPkWBcPdFCH11Bke9WDyu34nTNk8JweJwoXjPuZ", 27 | "1BcQQykSaig3ApJwdiavT3EG6Pdvkud7Nf": "L2P6G5ENuFZyVBHPdLbZmhtrDE4YHj9Adu9eGb7GWX7VhDy9enPU" 28 | } -------------------------------------------------------------------------------- /examples/services.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Creating and Using Cryptocurrency Wallets 6 | # 7 | # © 2017 November - 1200 Web Development 8 | # 9 | 10 | from pprint import pprint 11 | from bitcoinlib.services.services import * 12 | 13 | 14 | # Tests for specific provider 15 | srv = Service(network='bitcoin', providers=['blocksmurfer']) 16 | print("Estimated bitcoin transaction fee:", srv.estimatefee(3)) 17 | 18 | # Get Balance and UTXO's for given bitcoin testnet3 addresses 19 | address = 'mqR6Dndmez8WMpb1hBJbGbrQ2mpAU73hQC' 20 | srv = Service(network='testnet', min_providers=5) 21 | print("Balance of address %s: %s" % (address, srv.getbalance(address))) 22 | print("\nAll results as dict:") 23 | pprint(srv.results) 24 | print("\nUTXOs list:") 25 | pprint(srv.getutxos(address)) 26 | 27 | # GET Raw Transaction data for given Transaction ID 28 | t = 'd3c7fbd3a4ca1cca789560348a86facb3bb21dcd75ed38e85235fb6a32802955' 29 | print("\nGET Raw Transaction:") 30 | pprint(Service(network='testnet', min_providers=2).getrawtransaction(t)) 31 | 32 | # SEND Raw Transaction data (UTXO's already spent, so should give 'missing inputs' error) 33 | rt = '010000000108004b4c0394a211d4ec0d344b70bf1e3b1ce1731d11d1d30279ab0c0f6d9fd7000000006c493046022100ab18a72f7' \ 34 | '87e4c8ea5d2f983b99df28d27e13482b91fd6d48701c055af92f525022100d1c26b8a779896a53a026248388896501e724e46407f' \ 35 | '14a4a1b6478d3293da24012103e428723c145e61c35c070da86faadaf0fab21939223a5e6ce3e1cfd76bad133dffffffff0240420' \ 36 | 'f00000000001976a914bbaeed8a02f64c9d40462d323d379b8f27ad9f1a88ac905d1818000000001976a914046858970a72d33817' \ 37 | '474c0e24e530d78716fc9c88ac00000000' 38 | print("\nSEND Raw Transaction:") 39 | srv = Service(network='testnet') 40 | if srv.sendrawtransaction(rt): 41 | print("Transaction send, result: ") 42 | pprint(srv.results) 43 | else: 44 | print("Transaction could not be send, errors:") 45 | pprint(srv.errors) 46 | 47 | # Get current estimated networks fees 48 | print("\nCurrent estimated networks fees:") 49 | srv = Service(min_providers=10) 50 | srv.estimatefee(5) 51 | pprint(srv.results) 52 | 53 | # Test address with huge number of UTXO's 54 | # addresslst = '16ZbpCEyVVdqu8VycWR8thUL2Rd9JnjzHt' 55 | # addresslst = '1KwA4fS4uVuCNjCtMivE7m5ATbv93UZg8V' 56 | # srv = Service(network='bitcoin', min_providers=10) 57 | # utxos = srv.getutxos(addresslst) 58 | # results = srv.results 59 | # for res in results: 60 | # print(res, len(results[res])) 61 | 62 | # Get transactions by hash 63 | srv = Service() 64 | res = srv.gettransaction('2ae77540ec3ef7b5001de90194ed0ade7522239fe0fc57c12c772d67274e2700') 65 | pprint(res) 66 | -------------------------------------------------------------------------------- /bitcoinlib/data/config.ini: -------------------------------------------------------------------------------- 1 | # 2 | # BITCOINLIB - Configuration file 3 | # © 2024 December - 1200 Web Development 4 | # 5 | # Paths to data, logs and configuration files, all paths are relative to bitcoinlib source directory if no 6 | # absolute path is provided. 7 | # 8 | # In this configuration file you can overwrite default settings, for a normal installation it is not necessary to 9 | # change anything here. 10 | # 11 | 12 | [locations] 13 | # Default directory for database files. Relative paths will be based in user or bitcoinlib installation directory. 14 | # Only used for sqlite files. Default installation directory is in /.bitcoinlib 15 | ;database_dir=database 16 | 17 | # Default database file for wallets, keys and transactions. Relative paths for sqlite will be based in 'database_dir'. 18 | # You can use SQLite, PostgreSQL and MariaDB (MySQL) databases 19 | ;default_databasefile=bitcoinlib.sqlite 20 | ;default_databasefile=postgresql+psycopg://postgres:bitcoinlib@localhost:5432/bitcoinlib 21 | 22 | # For caching SQLite or PostgreSQL databases can be used. 23 | ;default_databasefile_cache=bitcoinlib_cache.sqlite 24 | ;default_databasefile_cache==postgresql+psycopg://postgres:bitcoinlib@localhost:5432/bitcoinlib_cache 25 | 26 | [common] 27 | # Allow database threads in SQLite databases 28 | ;allow_database_threads=True 29 | 30 | # Time for request to service providers in seconds 31 | ;timeout_requests=5 32 | 33 | # Default language for Mnemonic passphrases 34 | ;default_language=english 35 | 36 | # Default network when creating wallets, transaction, keys, etc. 37 | ;default_network=bitcoin 38 | 39 | # Default witness_type for new wallets and keys 40 | ;default_witness_type=legacy 41 | 42 | # Use caching for service providers 43 | ;service_caching_enabled=True 44 | 45 | # Maximum number of errors before service request fails 46 | ;service_max_errors=4 47 | 48 | # Maximum number of transactions per service request 49 | ;max_transactions=20 50 | 51 | # Number of seconds block_count is cached 52 | ;block_count_cache_time=3 53 | 54 | # Encrypt private key field in database using symmetrically EAS encryption. 55 | # You need to set the password in the DB_FIELD_ENCRYPTION_KEY environment variable. 56 | ;database_encryption_enabled=False 57 | 58 | [logs] 59 | # Enable own logging for this library. If true logs will be stored in the log/bitcoinlib.log file. 60 | # Set to False if this library is part of another library or software and you want to handle logs yourself. 61 | ;enable_bitcoinlib_logging=True 62 | 63 | # Log file name. Relative paths will be based in data_dir directory 64 | ;log_file=~bitcoinlib.log 65 | 66 | # Loglevel for this library, options: CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET 67 | ;loglevel=WARNING 68 | -------------------------------------------------------------------------------- /examples/wallet_bitcoind_connected_wallets.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Using Bitcoin Core wallets with Bitcoinlib 6 | # 7 | # Method 1 - Create wallet in Bitcoin Core and use the same wallet in Bitcoinlib using the bitcoin node to 8 | # receive and send bitcoin transactions. Only works for legacy wallets. 9 | # 10 | # © 2024 April - 1200 Web Development 11 | # 12 | 13 | from bitcoinlib.wallets import * 14 | from bitcoinlib.services.bitcoind import BitcoindClient 15 | 16 | # 17 | # Settings and Initialization 18 | # 19 | 20 | # Call bitcoin-cli dumpwallet and look for extended private masterkey at top off the export. Then copy the 21 | # bitcoin core node masterseed here: 22 | pkwif = 'tprv8ZgxMBicQKsPe2iVrERVdAgjcqHhxvZcWeS2Va6nvgddpDH1r33A4aTtdYYkoFDY6CCf5fogwLYmAdQQNxkk7W3ygwFd6hquJVLmmpbJRp2' 23 | enable_verify_wallet = False 24 | 25 | # Put connection string with format http://bitcoinlib:password@localhost:18332) 26 | # to Bitcoin Core node in the following file: 27 | bitcoind_url = open(os.path.join(os.path.expanduser('~'), ".bitcoinlib/.bitcoind_connection_string")).read() 28 | bcc = BitcoindClient(base_url=bitcoind_url) 29 | lastblock = bcc.proxy.getblockcount() 30 | print("Connected to bitcoind, last block: " + str(lastblock)) 31 | 32 | # 33 | # Create a copy of the Bitcoin Core Wallet in Bitcoinlib 34 | # 35 | w = wallet_create_or_open('wallet_bitcoincore', pkwif, network='testnet', witness_type='segwit', 36 | key_path=KEY_PATH_BITCOINCORE) 37 | addr = bcc.proxy.getnewaddress() 38 | addrinfo = bcc.proxy.getaddressinfo(addr) 39 | bcl_addr = w.key_for_path(addrinfo['hdkeypath']).address 40 | 41 | # Verify if we are using the same wallet 42 | if enable_verify_wallet and addr == bcl_addr: 43 | print("Address %s with path %s, is identical in Bitcoin core and Bitcoinlib" % (addr, addrinfo['hdkeypath'])) 44 | elif not addr == bcl_addr: 45 | print ("Address %s with path %s, is NOT identical in Bitcoin core and Bitcoinlib" % (addr, addrinfo['hdkeypath'])) 46 | raise ValueError("Wallets not identical in Bitcoin core and Bitcoinlib") 47 | 48 | # 49 | # Using wallets 50 | # 51 | 52 | # Now pick an address from your wallet and send some testnet coins to it, for example by using another wallet or a 53 | # testnet faucet. 54 | w.providers = ['bitcoind'] 55 | w.scan() 56 | # w.info() 57 | 58 | if not w.balance(): 59 | print("No testnet coins available") 60 | else: 61 | print("Found testnet coins. Wallet balance: %d" % w.balance()) 62 | # Send some coins to our own wallet 63 | t = w.send_to(w.get_key().address, 1000, fee=200, broadcast=True) 64 | t.info() 65 | 66 | # If you now run bitcoin-cli listunspent 0, you should see the 1 or 2 new utxo's for this transaction. 67 | -------------------------------------------------------------------------------- /docs/_static/manuals.add-provider.rst: -------------------------------------------------------------------------------- 1 | Add a new Service Provider 2 | ========================== 3 | 4 | The Service class connects to providers such as Blockchain.info or Blockchair.com to retrieve transaction, 5 | network, block, address information, etc 6 | 7 | The Service class automatically selects a provider which has requested method available and selects another 8 | provider if method fails. 9 | 10 | 11 | Steps to add a new provider 12 | --------------------------- 13 | 14 | * The preferred way is to create a github clone and update code there (and do a pull request...) 15 | * Add the provider settings in the providers.json file in the configuration directory. 16 | 17 | Example: 18 | 19 | .. code-block:: json 20 | 21 | { 22 | "bitgo": { 23 | "provider": "bitgo", 24 | "network": "bitcoin", 25 | "client_class": "BitGo", 26 | "provider_coin_id": "", 27 | "url": "https://www.bitgo.com/api/v1/", 28 | "api_key": "", 29 | "priority": 10, 30 | "denominator": 1, 31 | "network_overrides": null 32 | "timeout": 0 33 | } 34 | } 35 | 36 | * Create a new Service class in bitcoinlib.services. Create a method for available API calls and rewrite output 37 | if needed. 38 | 39 | Example: 40 | 41 | .. code-block:: python 42 | 43 | from bitcoinlib.services.baseclient import BaseClient 44 | 45 | PROVIDERNAME = 'bitgo' 46 | 47 | 48 | class BitGoClient(BaseClient): 49 | 50 | def __init__(self, network, base_url, denominator, api_key=''): 51 | super(self.__class__, self).\ 52 | __init__(network, PROVIDERNAME, base_url, denominator, api_key) 53 | 54 | def compose_request(self, category, data, cmd='', variables=None, method='get'): 55 | if data: 56 | data = '/' + data 57 | url_path = category + data 58 | if cmd: 59 | url_path += '/' + cmd 60 | return self.request(url_path, variables, method=method) 61 | 62 | def estimatefee(self, blocks): 63 | res = self.compose_request('tx', 'fee', variables={'numBlocks': blocks}) 64 | return res['feePerKb'] 65 | 66 | * Add this service class to __init__.py 67 | 68 | .. code-block:: python 69 | 70 | import bitcoinlib.services.bitgo 71 | 72 | * Remove install.log file in bitcoinlib's log directory, this will copy all provider settings next time you run 73 | the bitcoin library. See 'initialize_lib' method in main.py 74 | * Specify a new provider and create a service class object to test your new class and its method 75 | 76 | .. code-block:: python 77 | 78 | from bitcoinlib import services 79 | 80 | srv = Service(providers=['blockchair']) 81 | print(srv.estimatefee(5)) 82 | 83 | -------------------------------------------------------------------------------- /tests/test_networks.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # Unit Tests for Bitcoinlib Network Class 5 | # © 2018 - 2025 May - 1200 Web Development 6 | # 7 | 8 | import unittest 9 | from bitcoinlib.networks import * 10 | 11 | 12 | class TestNetworks(unittest.TestCase): 13 | 14 | def test_networks_prefix_wif_network_by_value(self): 15 | self.assertEqual(network_by_value('prefix_wif', '80')[:1], ['bitcoin']) 16 | self.assertEqual(network_by_value('prefix_wif', '10'), []) 17 | 18 | def test_networks_prefix_bech32_network_by_value(self): 19 | self.assertEqual(network_by_value('prefix_bech32', 'tb'), ['testnet', 'testnet4', 'signet']) 20 | 21 | def test_networks_prefix_bech32_network_by_value_sorted(self): 22 | self.assertEqual(network_by_value('prefix_bech32', 'ltc'), ['litecoin', 'litecoin_legacy']) 23 | 24 | def test_networks_prefix_hdkey_wif(self): 25 | network = Network('bitcoin') 26 | self.assertEqual(network.wif_prefix(is_private=True), b'\x04\x88\xad\xe4') 27 | self.assertEqual(network.wif_prefix(is_private=False), b'\x04\x88\xb2\x1e') 28 | 29 | def test_networks_network_value_for(self): 30 | prefixes = network_values_for('prefix_wif') 31 | expected_prefixes = [b'\xb0', b'\xef', b'\x99', b'\x80'] 32 | for expected in expected_prefixes: 33 | self.assertIn(expected, prefixes) 34 | self.assertEqual(network_values_for('denominator')[0], 1e-8) 35 | self.assertIn('BTC', network_values_for('currency_code')) 36 | 37 | def test_network_defined(self): 38 | self.assertTrue(network_defined('bitcoin')) 39 | self.assertFalse(network_defined('bitcoiiin')) 40 | self.assertRaisesRegex(NetworkError, "Network bitcoiin not found in network definitions", Network, 'bitcoiin') 41 | 42 | def test_wif_prefix_search(self): 43 | exp_dict = { 44 | 'is_private': True, 45 | 'multisig': False, 46 | 'network': 'bitcoin', 47 | 'prefix': '0488ADE4', 48 | 'prefix_str': 'xprv', 49 | 'script_type': 'p2pkh', 50 | 'witness_type': 'legacy'} 51 | self.assertEqual(wif_prefix_search('0488ADE4', network='bitcoin', multisig=False)[0], exp_dict) 52 | self.assertEqual(wif_prefix_search('lettrythisstrangestring', network='bitcoin', multisig=False), []) 53 | 54 | def test_network_dunders(self): 55 | n1 = Network('bitcoin') 56 | n2 = Network('litecoin') 57 | self.assertFalse(n1 == n2) 58 | self.assertTrue(n1 == 'bitcoin') 59 | self.assertFalse(n2 == 'bitcoin') 60 | self.assertTrue(n1 != 'dogecoin') 61 | self.assertEqual(str(n1), "") 62 | self.assertTrue(hash(n1)) 63 | 64 | 65 | if __name__ == '__main__': 66 | unittest.main() 67 | -------------------------------------------------------------------------------- /bitcoinlib/config/opcodes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # Script opcode definitions 5 | # © 2017 - 2021 February - 1200 Web Development 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU Affero General Public License as 9 | # published by the Free Software Foundation, either version 3 of the 10 | # License, or (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU Affero General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU Affero General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | 22 | _opcodes = [ 23 | ("OP_0", 0), ("OP_PUSHDATA1", 76), "OP_PUSHDATA2", "OP_PUSHDATA4", "OP_1NEGATE", "OP_RESERVED", "OP_1", 24 | "OP_2", "OP_3", "OP_4", "OP_5", "OP_6", "OP_7", "OP_8", "OP_9", "OP_10", "OP_11", "OP_12", "OP_13", "OP_14", 25 | "OP_15", "OP_16", "OP_NOP", "OP_VER", "OP_IF", "OP_NOTIF", "OP_VERIF", "OP_VERNOTIF", "OP_ELSE", "OP_ENDIF", 26 | "OP_VERIFY", "OP_RETURN", "OP_TOALTSTACK", "OP_FROMALTSTACK", "OP_2DROP", "OP_2DUP", "OP_3DUP", "OP_2OVER", 27 | "OP_2ROT", "OP_2SWAP", "OP_IFDUP", "OP_DEPTH", "OP_DROP", "OP_DUP", "OP_NIP", "OP_OVER", "OP_PICK", "OP_ROLL", 28 | "OP_ROT", "OP_SWAP", "OP_TUCK", "OP_CAT", "OP_SUBSTR", "OP_LEFT", "OP_RIGHT", "OP_SIZE", "OP_INVERT", "OP_AND", 29 | "OP_OR", "OP_XOR", "OP_EQUAL", "OP_EQUALVERIFY", "OP_RESERVED1", "OP_RESERVED2", "OP_1ADD", "OP_1SUB", "OP_2MUL", 30 | "OP_2DIV", "OP_NEGATE", "OP_ABS", "OP_NOT", "OP_0NOTEQUAL", "OP_ADD", "OP_SUB", "OP_MUL", "OP_DIV", "OP_MOD", 31 | "OP_LSHIFT", "OP_RSHIFT", "OP_BOOLAND", "OP_BOOLOR", "OP_NUMEQUAL", "OP_NUMEQUALVERIFY", "OP_NUMNOTEQUAL", 32 | "OP_LESSTHAN", "OP_GREATERTHAN", "OP_LESSTHANOREQUAL", "OP_GREATERTHANOREQUAL", "OP_MIN", "OP_MAX", "OP_WITHIN", 33 | "OP_RIPEMD160", "OP_SHA1", "OP_SHA256", "OP_HASH160", "OP_HASH256", "OP_CODESEPARATOR", "OP_CHECKSIG", 34 | "OP_CHECKSIGVERIFY", "OP_CHECKMULTISIG", "OP_CHECKMULTISIGVERIFY", "OP_NOP1", "OP_CHECKLOCKTIMEVERIFY", 35 | "OP_CHECKSEQUENCEVERIFY", "OP_NOP4", "OP_NOP5", "OP_NOP6", "OP_NOP7", "OP_NOP8", "OP_NOP9", "OP_NOP10", 36 | ("OP_INVALIDOPCODE", 0xFF) 37 | ] 38 | 39 | 40 | def op(): 41 | pass 42 | 43 | 44 | def _set_opcodes(): 45 | idx = 0 46 | opcodenames = {} 47 | for opcode in _opcodes: 48 | if isinstance(opcode, tuple): 49 | var, idx = opcode 50 | else: 51 | var = opcode 52 | opcodenames.update({idx: var}) 53 | setattr(op, var.lower(), idx) 54 | idx += 1 55 | return opcodenames 56 | 57 | 58 | opcodenames = _set_opcodes() 59 | 60 | OP_N_CODES = range(op.op_1, op.op_16) 61 | 62 | opcodeints = dict((v,k) for k,v in opcodenames.items()) 63 | -------------------------------------------------------------------------------- /examples/bitcoind_client_transactions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Use bitcoind as service provider 6 | # 7 | # © 2018 - 2025 May - 1200 Web Development 8 | # 9 | 10 | from pprint import pprint 11 | from bitcoinlib.transactions import * 12 | from bitcoinlib.services.bitcoind import BitcoindClient 13 | 14 | # Provide a connection URL to your bitcoind instance, or leave empty to search service on localhost 15 | # Connection URL Example: 16 | # http://user:password@server_url:18332 (use port 8332 for mainnet) 17 | base_url = '' 18 | bdc = BitcoindClient(base_url=base_url) 19 | 20 | # Deserialize and verify a transaction 21 | txid = 'd323c517751b118984bb3f709d762cd17e55326f2bcb4e8b82a9145b361a6ff2' 22 | rt = bdc.getrawtransaction(txid) 23 | print("Raw: %s" % rt) 24 | t = Transaction.parse_hex(rt) 25 | pprint(t.as_dict()) 26 | print("Verified: %s" % t.verify()) 27 | 28 | # Deserialize transactions in latest block with bitcoind client 29 | MAX_TRANSACTIONS_VIEW = 100 30 | error_count = 0 31 | if MAX_TRANSACTIONS_VIEW: 32 | print("\n=== DESERIALIZE LAST BLOCKS TRANSACTIONS ===") 33 | blockhash = bdc.proxy.getbestblockhash() 34 | bestblock = bdc.proxy.getblock(blockhash) 35 | print('... %d transactions found' % len(bestblock['tx'])) 36 | ci = 0 37 | ct = len(bestblock['tx']) 38 | for txid in bestblock['tx']: 39 | ci += 1 40 | print("\n[%d/%d] Deserialize txid %s" % (ci, ct, txid)) 41 | rt = bdc.getrawtransaction(txid) 42 | print("Raw: %s" % rt) 43 | t = Transaction.parse_hex(rt, strict=False) 44 | pprint(t.as_dict()) 45 | try: 46 | print("Verified: %s" % t.verify()) 47 | except TransactionError as Err: 48 | print("Verification Error:", Err) 49 | if ci > MAX_TRANSACTIONS_VIEW: 50 | break 51 | print("=== %d raw transactions deserialised ===" % 52 | (ct if ct < MAX_TRANSACTIONS_VIEW else MAX_TRANSACTIONS_VIEW)) 53 | print("=== errorcount %d" % error_count) 54 | print("=== D O N E ===") 55 | 56 | # Deserialize transactions in the bitcoind mempool client 57 | print("\n=== DESERIALIZE MEMPOOL TRANSACTIONS ===") 58 | newtxs = bdc.proxy.getrawmempool() 59 | ci = 0 60 | ct = len(newtxs) 61 | print("Found %d transactions in mempool" % len(newtxs)) 62 | for txid in newtxs: 63 | ci += 1 64 | print("[%d/%d] Deserialize txid %s" % (ci, ct, txid)) 65 | try: 66 | rt = bdc.getrawtransaction(txid) 67 | print("- raw %s" % rt) 68 | t = Transaction.parse_hex(rt) 69 | pprint(t.as_dict()) 70 | except Exception as e: 71 | print("Error when importing raw transaction %d, error %s", (txid, e)) 72 | error_count += 1 73 | if ci > MAX_TRANSACTIONS_VIEW: 74 | break 75 | print("=== %d mempool transactions deserialised ===" % 76 | (ct if ct < MAX_TRANSACTIONS_VIEW else MAX_TRANSACTIONS_VIEW)) 77 | print("=== errorcount %d" % error_count) 78 | print("=== D O N E ===") 79 | -------------------------------------------------------------------------------- /docs/_static/manuals.databases.rst: -------------------------------------------------------------------------------- 1 | Using MySQL or PostgreSQL databases 2 | =================================== 3 | 4 | Bitcoinlib uses the SQLite database by default, because it is easy to use and requires no installation. 5 | 6 | But you can also use other databases. At this moment Bitcoinlib is tested with MySQL and PostgreSQL. 7 | 8 | The database URI can be passed to the Wallet or Service object, or you can set the database URI for wallets and / or cache in configuration file at ~/.bitcoinlib/config.ini 9 | 10 | Using MariaDB / MySQL database 11 | ------------------------------ 12 | 13 | We assume you have a MySQL server at localhost. Unlike with the SQLite database MySQL databases are not created automatically, so create one from the mysql command prompt: 14 | 15 | .. code-block:: mysql 16 | 17 | mysql> create database bitcoinlib; 18 | 19 | Now create a user for your application and grant this user access. And of course replace the password 'secret' with 20 | a better password. 21 | 22 | .. code-block:: mysql 23 | 24 | mysql> create user bitcoinlib@localhost identified by 'secret'; 25 | mysql> grant all on bitcoinlib.* to bitcoinlib@localhost with grant option; 26 | 27 | In your application you can create a database link. The database tables are created when you first run the application 28 | 29 | .. code-block:: python 30 | 31 | db_uri = 'mysql://bitcoinlib:secret@localhost:3306/bitcoinlib' 32 | w = wallet_create_or_open('wallet_mysql', db_uri=db_uri) 33 | w.info() 34 | 35 | At the moment it is not possible to use MySQL database for `caching `_, because the BLOB transaction ID's are used as primary key. For caching you need to use a PostgreSQL or SQLite database. 36 | 37 | Using PostgreSQL database 38 | ------------------------- 39 | 40 | First create a user and the database from a shell. We assume you have a PostgreSQL server running at your Linux machine. 41 | 42 | .. code-block:: bash 43 | 44 | $ su - postgres 45 | postgres@localhost:~$ createuser --interactive --pwprompt 46 | Enter name of role to add: bitcoinlib 47 | Enter password for new role: 48 | Enter it again: 49 | Shall the new role be a superuser? (y/n) n 50 | Shall the new role be allowed to create databases? (y/n) n 51 | Shall the new role be allowed to create more new roles? (y/n) n 52 | $ createdb bitcoinlib 53 | 54 | And assume you unwisely have chosen the password 'secret' you can use the database as follows: 55 | 56 | .. code-block:: python 57 | 58 | db_uri = 'postgresql+psycopg://bitcoinlib:secret@localhost:5432/' 59 | w = wallet_create_or_open('wallet_mysql', db_uri=db_uri) 60 | w.info() 61 | 62 | Please note 'postgresql+psycopg' has to be used as scheme, because SQLalchemy uses the latest version 3 of psycopg, if not provided it will use psycopg2. 63 | 64 | PostgreSQL can also be used for `caching `_ of service requests. The URI can be passed to the Service object or provided in the configuration file (~/.bitcoiinlib/config.ini) 65 | 66 | .. code-block:: python 67 | 68 | srv = Service(cache_uri='postgresql+psycopg://postgres:postgres@localhost:5432/) 69 | res = srv.gettransactions('12spqcvLTFhL38oNJDDLfW1GpFGxLdaLCL') 70 | 71 | 72 | Encrypt database or private keys 73 | -------------------------------- 74 | 75 | If you are using wallets with private keys it is advised to use an encrypted database and / or to encrypt the private key fields. 76 | 77 | Please read `Encrypt Database or Private Keys `_ for more information. 78 | -------------------------------------------------------------------------------- /examples/mnemonic.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Mnemonic class examples 6 | # 7 | # © 2017 - 2019 January - 1200 Web Development 8 | # 9 | 10 | from bitcoinlib.mnemonic import * 11 | from bitcoinlib.keys import HDKey 12 | from bitcoinlib.encoding import to_hexstring 13 | 14 | # 15 | # Mnemonic examples 16 | # 17 | 18 | # Convert hexadecimal to Mnemonic and back again to hex 19 | print("\nConvert hexadecimal to Mnemonic and back again to hex") 20 | pk = '7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f' 21 | words = Mnemonic().to_mnemonic(pk) 22 | print("Hex %s" % pk) 23 | print("Checksum bin %s" % Mnemonic().checksum(pk)) 24 | print("Mnemonic %s" % words) 25 | print("Seed for HD Key %s" % to_hexstring(Mnemonic().to_seed(words, 'test'))) 26 | print("Back to Entropy %s" % to_hexstring(Mnemonic().to_entropy(words))) 27 | 28 | # Generate a random Mnemonic HD Key 29 | print("\nGenerate a random Mnemonic HD Key") 30 | entsize = 128 31 | words = Mnemonic('english').generate(entsize) 32 | print("Your Mnemonic is %s" % words) 33 | print(" (An average of %d tries is needed to brute-force this password)" % ((2 ** entsize) // 2)) 34 | seed = Mnemonic().to_seed(words) 35 | hdk = HDKey.from_seed(seed) 36 | print("Seed for HD Key %s" % to_hexstring(seed)) 37 | print("HD Key WIF is %s" % hdk.wif()) 38 | print("HD Key WIF is %s (method 2)" % HDKey.from_passphrase(words).wif()) 39 | 40 | # Generate a key from a Mnemonic passphrase 41 | print("\nGenerate a key from a Mnemonic passphrase") 42 | words = "type fossil omit food supply enlist move perfect direct grape clean diamond" 43 | print("Your Mnemonic is %s" % words) 44 | seed = Mnemonic().to_seed(words) 45 | hdk = HDKey.from_seed(seed) 46 | print("Seed for HD Key %s" % to_hexstring(seed)) 47 | print("HD Key WIF is %s" % hdk.wif()) 48 | 49 | # Let's talk Spanish 50 | print("\nGenerate a key from a Spanish Mnemonic passphrase") 51 | words = "laguna afirmar talón resto peldaño deuda guerra dorado catorce avance oasis barniz" 52 | print("Your Mnemonic is %s" % words) 53 | seed = Mnemonic(language='spanish').to_seed(words) 54 | hdk = HDKey.from_seed(seed) 55 | print("Seed for HD Key %s" % to_hexstring(seed)) 56 | print("HD Key WIF is %s" % hdk.wif()) 57 | 58 | # Want some Chinese? 59 | print("\nGenerate a key from a Chinese Mnemonic passphrase") 60 | words = "信 收 曉 捐 炭 祖 瘋 原 強 則 岩 蓄" 61 | print("Your Mnemonic is %s" % words) 62 | seed = Mnemonic(language='chinese_traditional').to_seed(words) 63 | hdk = HDKey.from_seed(seed) 64 | print("Seed for HD Key %s" % to_hexstring(seed)) 65 | print("HD Key WIF is %s" % hdk.wif()) 66 | 67 | # Spanish Unicode mnemonic sentence 68 | print("\nGenerate a key from a Spanish UNICODE Mnemonic passphrase") 69 | words = u"guion cruz envío papel otoño percha hazaña salir joya gorra íntimo actriz" 70 | print("Your Mnemonic is %s" % words) 71 | seed = Mnemonic(language='spanish').to_seed(words, '1200 web development') 72 | hdk = HDKey.from_seed(seed) 73 | print("Seed for HD Key %s" % to_hexstring(seed)) 74 | print("HD Key WIF is %s" % hdk.wif()) 75 | 76 | # And Japanese 77 | print("\nGenerate a key from a Japanese UNICODE Mnemonic passphrase") 78 | words = "あじわう ちしき たわむれる おくさま しゃそう うんこう ひてい みほん たいほ てのひら りこう わかれる かいすいよく こもん ねもと" 79 | print("Your Mnemonic is %s" % words) 80 | seed = Mnemonic(language='japanese').to_seed(words, '1200 web development') 81 | hdk = HDKey.from_seed(seed) 82 | print("Seed for HD Key %s" % to_hexstring(seed)) 83 | print("HD Key WIF is %s" % hdk.wif()) 84 | -------------------------------------------------------------------------------- /docs/_static/script-types-overview.rst: -------------------------------------------------------------------------------- 1 | Script types 2 | ============ 3 | 4 | This is an overview script types used in transaction Input and Outputs. 5 | 6 | They are defined in main.py 7 | 8 | 9 | Locking scripts 10 | --------------- 11 | 12 | Scripts lock funds in transaction outputs (UTXO's). 13 | Also called ScriptSig. 14 | 15 | 16 | +-------------+---------------------------+-----------+-------------------+------------+ 17 | | Lock Script | Script to Unlock | Encoding | Key type / Script | Prefix BTC | 18 | +=============+===========================+===========+===================+============+ 19 | | p2pkh | Pay to Public Key Hash | base58 | Public key hash | 1 | 20 | +-------------+---------------------------+-----------+-------------------+------------+ 21 | | p2sh | Pay to Script Hash | base58 | Redeemscript hash | 3 | 22 | +-------------+---------------------------+-----------+-------------------+------------+ 23 | | p2wpkh | Pay to Wallet Pub Key Hash| bech32 | Public key hash | bc | 24 | +-------------+---------------------------+-----------+-------------------+------------+ 25 | | p2wsh | Pay to Wallet Script Hash | bech32 | Redeemscript hash | bc | 26 | +-------------+---------------------------+-----------+-------------------+------------+ 27 | | multisig | Multisig Script | base58 | Multisig script | 3 | 28 | +-------------+---------------------------+-----------+-------------------+------------+ 29 | | pubkey | Public Key (obsolete) | base58 | Public Key | 1 | 30 | +-------------+---------------------------+-----------+-------------------+------------+ 31 | | nulldata | Nulldata | n/a | OP_RETURN script | n/a | 32 | +-------------+---------------------------+-----------+-------------------+------------+ 33 | 34 | 35 | Unlocking scripts 36 | ----------------- 37 | 38 | Scripts used in transaction inputs to unlock funds from previous outputs. 39 | Also called ScriptPubKey. 40 | 41 | +---------------+---------------------------+----------------+-------------------------+ 42 | | Locking sc. | Name | Unlocks | Key type / Script | 43 | +===============+===========================+================+=========================+ 44 | | sig_pubkey | Signature, Public Key | p2pkh | Sign. + Public key | 45 | +---------------+---------------------------+----------------+-------------------------+ 46 | | p2sh_multisig | Pay to Script Hash | p2sh, multisig | Multisig + Redeemscript | 47 | +---------------+---------------------------+----------------+-------------------------+ 48 | | p2sh_p2wpkh | Pay to Wallet Pub Key Hash| p2wpkh | PK Hash + Redeemscript | 49 | +---------------+---------------------------+----------------+-------------------------+ 50 | | p2sh_p2wsh | Multisig script | p2wsh | Redeemscript | 51 | +---------------+---------------------------+----------------+-------------------------+ 52 | | signature | Sig for public key (old) | pubkey | Signature | 53 | +---------------+---------------------------+----------------+-------------------------+ 54 | 55 | 56 | Bitcoinlib script support 57 | ------------------------- 58 | 59 | The 'pubkey' lockscript and 'signature' unlocking script are ancient and not supported by BitcoinLib at 60 | the moment. 61 | 62 | Using different encodings for addresses than the one listed in the Locking Script table is possible but 63 | not advised: It is not standard and not sufficiently tested. 64 | -------------------------------------------------------------------------------- /tests/import_test.tx: -------------------------------------------------------------------------------- 1 | {'block_hash': None, 2 | 'block_height': None, 3 | 'coinbase': False, 4 | 'confirmations': None, 5 | 'date': None, 6 | 'fee': 12366, 7 | 'fee_per_kb': 33333, 8 | 'flag': None, 9 | 'input_total': 100000000, 10 | 'inputs': [{'address': '23K38iGiHEHFfHh7SSUd46RWGQNHwha9kAt', 11 | 'compressed': True, 12 | 'double_spend': False, 13 | 'encoding': 'base58', 14 | 'index_n': 0, 15 | 'locktime_cltv': None, 16 | 'locktime_csv': None, 17 | 'output_n': 0, 18 | 'prev_txid': '12821f8ac330e4eddb9f87ea29456b31ec300e232d2c63880f669a9b15e3741f', 19 | 'public_hash': 'e2cf42c85bb53cff8d3b75bdabb31e2c8a00cb8a', 20 | 'public_keys': ['0289d3f95b15f53c666a4b70391e9a7cf6c771f6177d745557750a4160929a932e', 21 | '0331271d364803fd05e4a5b95acb2b0f200e9634dd75e95a577477762b8dacbcd3', 22 | '03547034e1e807362c5edd66d6951381ac2bde926b5244d5ce9cb1a82a4240bc89'], 23 | 'redeemscript': '52210289d3f95b15f53c666a4b70391e9a7cf6c771f6177d745557750a4160929a932e210331271d364803fd05e4a5b95acb2b0f200e9634dd75e95a577477762b8dacbcd32103547034e1e807362c5edd66d6951381ac2bde926b5244d5ce9cb1a82a4240bc8953ae', 24 | 'script': '', 25 | 'script_code': '', 26 | 'script_type': 'p2sh_multisig', 27 | 'sequence': 4294967295, 28 | 'signatures': ['1862f7a0b7d161954431662fb63db86247cead6dfc6b6c8b9b1c2479297ad3b50a35dd0ec056a43da44d3d04e22181ea59ad9225d2fc4541424d464622a0a6f2'], 29 | 'sigs_required': 2, 30 | 'sort': True, 31 | 'unlocking_script': '', 32 | 'locking_script': '52210289d3f95b15f53c666a4b70391e9a7cf6c771f6177d745557750a4160929a932e210331271d364803fd05e4a5b95acb2b0f200e9634dd75e95a577477762b8dacbcd32103547034e1e807362c5edd66d6951381ac2bde926b5244d5ce9cb1a82a4240bc8953ae', 33 | 'valid': None, 34 | 'value': 100000000, 35 | 'witness': '', 36 | 'witness_type': 'legacy'}], 37 | 'locktime': 0, 38 | 'network': 'bitcoinlib_test', 39 | 'output_total': 99987634, 40 | 'outputs': [{'address': '23K38iGiHEHFfHh7SSUd46RWGQNHwha9kAt', 41 | 'output_n': 0, 42 | 'public_hash': 'e2cf42c85bb53cff8d3b75bdabb31e2c8a00cb8a', 43 | 'public_key': '', 44 | 'script': 'a914e2cf42c85bb53cff8d3b75bdabb31e2c8a00cb8a87', 45 | 'script_type': 'p2sh', 46 | 'spending_index_n': None, 47 | 'spending_txid': None, 48 | 'spent': False, 49 | 'value': 50000000}, 50 | {'address': '239M1DxQuxJcMHtYBdG6A81bfXQrrCNa2rr', 51 | 'output_n': 1, 52 | 'public_hash': '787f3d509665fd64ea9c7f2670ef9f60133290fe', 53 | 'public_key': '', 54 | 'script': 'a914787f3d509665fd64ea9c7f2670ef9f60133290fe87', 55 | 'script_type': 'p2sh', 56 | 'spending_index_n': None, 57 | 'spending_txid': None, 58 | 'spent': False, 59 | 'value': 49987634}], 60 | 'raw': '01000000011f74e3159b9a660f88632c2d230e30ec316b4529ea879fdbede430c38a1f82120000000000ffffffff0280f0fa020000000017a914e2cf42c85bb53cff8d3b75bdabb31e2c8a00cb8a8732c0fa020000000017a914787f3d509665fd64ea9c7f2670ef9f60133290fe8700000000', 61 | 'size': 371, 62 | 'status': 'new', 63 | 'txhash': '', 64 | 'txid': '2e07be62d933f5b257ac066b874df651cd6e6763795c24036904024a2b44180b', 65 | 'verified': False, 66 | 'version': 1, 67 | 'vsize': 371, 68 | 'witness_type': 'legacy' 69 | } 70 | 71 | -------------------------------------------------------------------------------- /examples/bip38_encrypted_wif_private_key.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Bip38 encrypted private keys 6 | # 7 | # © 2024 March - 1200 Web Development 8 | # 9 | 10 | from bitcoinlib.keys import * 11 | 12 | # 13 | # Example #1 - BIP38 key - no EC multiplication 14 | # 15 | private_wif = "L3QtG7mpcV1AiGCuRCi34HgwTtWDPWNe6m8Wi58S1LzavAsu3v1x" 16 | password = "bitcoinlib" 17 | expected_encrypted_wif = "6PYRg5u7XPXoL9v8nXbBJkzjcMtCqSDM1p9MJttpXb42W1DNt33iX8tosj" 18 | k = HDKey(private_wif, witness_type='legacy') 19 | encrypted_wif = k.encrypt(password=password) 20 | assert(encrypted_wif == expected_encrypted_wif) 21 | print("Encrypted WIF: %s" % encrypted_wif) 22 | 23 | k2 = HDKey(encrypted_wif, password=password, witness_type='legacy') 24 | assert(k2.wif_key() == private_wif) 25 | print("Decrypted WIF: %s" % k2.wif_key()) 26 | 27 | 28 | # 29 | # Example #2 - EC multiplied BIP38 key encryption - not lot and sequence 30 | # from https://bip38.readthedocs.io/en/v0.3.0/index.html 31 | # 32 | passphrase = "meherett" 33 | owner_salt = "75ed1cdeb254cb38" 34 | seed = "99241d58245c883896f80843d2846672d7312e6195ca1a6c" 35 | compressed = False 36 | expected_intermediate_password = "passphraseondJwvQGEWFNrNJRPi4G5XAL5SU777GwTNtqmDXqA3CGP7HXfH6AdBxxc5WUKC" 37 | expected_encrypted_wif = "6PfP7T3iQ5jLJLsH5DneySLLF5bhd879DHW87Pxzwtwvn2ggcncxsNKN5c" 38 | expected_confirmation_code = "cfrm38V5NZfTZKRaRDTvFAMkNKqKAxTxdDjDdb5RpFfXrVRw7Nov5m2iP3K1Eg5QQRxs52kgapy" 39 | expected_private_key = "5Jh21edvnWUXFjRz8mDVN3CSPp1CyTuUSFBKZeWYU726R6MW3ux" 40 | 41 | intermediate_password = bip38_intermediate_password(passphrase, owner_salt=owner_salt) 42 | assert(intermediate_password == expected_intermediate_password) 43 | print("\nIntermediate Password: %s" % intermediate_password) 44 | 45 | res = bip38_create_new_encrypted_wif(intermediate_password, compressed=compressed, seed=seed) 46 | assert(res['encrypted_wif'] == expected_encrypted_wif) 47 | print("Encrypted WIF: %s" % res['encrypted_wif']) 48 | assert(res['confirmation_code'] == expected_confirmation_code) 49 | print("Confirmation Code: %s" % res['confirmation_code']) 50 | 51 | k = HDKey(res['encrypted_wif'], password=passphrase, compressed=compressed, witness_type='legacy') 52 | assert(k.wif_key() == expected_private_key) 53 | print("Private WIF: %s" % k.wif_key()) 54 | 55 | # 56 | # Example #2 - EC multiplied BIP38 key encryption - with lot and sequence 57 | # from https://bip38.readthedocs.io/en/v0.3.0/index.html 58 | # 59 | passphrase = "meherett" 60 | owner_salt = "75ed1cdeb254cb38" 61 | seed = "99241d58245c883896f80843d2846672d7312e6195ca1a6c" 62 | compressed = True 63 | lot = 369861 64 | sequence = 1 65 | expected_intermediate_password = "passphraseb7ruSNDGP7cmnFHQpmos7TeAy26AFN4GyRTBqq6hiaFbQzQBvirD9oHsafQvzd" 66 | expected_encrypted_wif = "6PoEPBnJjm8UAiSGWQEKKNq9V2GMHqKkTcUqUFzsaX7wgjpQWR2qWPdnpt" 67 | expected_confirmation_code = "cfrm38VWx5xH1JFm5EVE3mzQvDPFkz7SqNiaFxhyUfp3Fjc2wdYmK7dGEWoW6irDPSrwoaxB5zS" 68 | expected_private_key = "KzFbTBirbEEtEPgWL3xhohUcrg6yUmJupAGrid7vBP9F2Vh8GTUB" 69 | 70 | intermediate_password = bip38_intermediate_password(passphrase, lot=lot, sequence=sequence, owner_salt=owner_salt) 71 | assert(intermediate_password == expected_intermediate_password) 72 | print("\nIntermediate Password: %s" % intermediate_password) 73 | 74 | res = bip38_create_new_encrypted_wif(intermediate_password, compressed=compressed, seed=seed) 75 | assert(res['encrypted_wif'] == expected_encrypted_wif) 76 | print("Encrypted WIF: %s" % res['encrypted_wif']) 77 | assert(res['confirmation_code'] == expected_confirmation_code) 78 | print("Confirmation Code: %s" % res['confirmation_code']) 79 | 80 | k = HDKey(res['encrypted_wif'], password=passphrase, compressed=compressed, witness_type='legacy') 81 | assert(k.wif_key() == expected_private_key) 82 | print("Private WIF: %s" % k.wif_key()) 83 | -------------------------------------------------------------------------------- /bitcoinlib/services/bitflyer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # Blocksmurfer client 5 | # © 2020 Januari - 1200 Web Development 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU Affero General Public License as 9 | # published by the Free Software Foundation, either version 3 of the 10 | # License, or (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU Affero General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU Affero General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | import logging 22 | from datetime import datetime 23 | from bitcoinlib.services.baseclient import BaseClient, ClientError 24 | from bitcoinlib.transactions import Transaction 25 | from bitcoinlib.keys import Address 26 | 27 | PROVIDERNAME = 'bitflyer' 28 | 29 | 30 | _logger = logging.getLogger(__name__) 31 | 32 | 33 | class BitflyerClient(BaseClient): 34 | 35 | def __init__(self, network, base_url, denominator, *args): 36 | super(self.__class__, self).__init__(network, PROVIDERNAME, base_url, denominator, *args) 37 | 38 | def compose_request(self, function, parameter='', parameter2='', method='get'): 39 | url_path = function 40 | if parameter: 41 | url_path += '/' + str(parameter) 42 | if parameter2: 43 | url_path += '/' + str(parameter2) 44 | return self.request(url_path, method=method) 45 | 46 | def getbalance(self, addresslist): 47 | balance = 0 48 | for address in addresslist: 49 | res = self.compose_request('address', address) 50 | balance += res['unconfirmed_balance'] 51 | return balance 52 | 53 | # def getutxos(self, address, after_txid='', limit=MAX_TRANSACTIONS): 54 | 55 | # def gettransaction(self, txid, block_count=None): 56 | # tx = self.compose_request('tx', txid) 57 | # # tx_date = None if not tx.get('received_date') else datetime.strptime(tx['received_date'], 58 | # # "%Y-%m-%dT%H:%M:%S.%f") 59 | # t = Transaction(locktime=tx['lock_time'], version=tx['version'], network=self.network, 60 | # # fee=tx['fees'], size=tx['size'], txid=tx['tx_hash'], date=tx_date, 61 | # confirmations=tx['confirmed'], block_height=tx['block_height'], 62 | # status='confirmed' if tx['confirmed'] else 'unconfirmed') 63 | # for ti in tx['inputs']: 64 | # a = Address.parse(ti['address']) 65 | # t.add_input(prev_txid=ti['prev_hash'], output_n=ti['prev_index'], unlocking_script=ti['script'], 66 | # value=ti['value'], address=ti['address'], sequence=ti['sequence'], 67 | # witness_type=a.witness_type, strict=self.strict) 68 | # if 'segwit' in [i.witness_type for i in t.inputs] or 'p2sh-segwit' in [i.witness_type for i in t.inputs]: 69 | # t.witness_type = 'segwit' 70 | # for to in tx['outputs']: 71 | # t.add_output(value=to['value'], address=to['address'], lock_script=to['script'], strict=self.strict) 72 | # t.update_totals() 73 | # return t 74 | 75 | # def gettransactions(self, address, after_txid='', limit=MAX_TRANSACTIONS): 76 | 77 | # def getrawtransaction(self, txid): 78 | 79 | # def sendrawtransaction(self, rawtx): 80 | 81 | # def estimatefee(self, blocks): 82 | 83 | def blockcount(self): 84 | res = self.compose_request('block', 'latest') 85 | return res['height'] 86 | 87 | # def mempool(self, txid): 88 | 89 | # def getblock(self, blockid, parse_transactions, page, limit): 90 | 91 | # def getrawblock(self, blockid): 92 | 93 | # def isspent(self, txid, output_n): 94 | 95 | # def getinfo(self): 96 | -------------------------------------------------------------------------------- /examples/wallets_segwit_testnet.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Segregated Witness Wallets 6 | # 7 | # 1200 Web Development 8 | # © 2018 - 2025 July 9 | # 10 | # Create 4 different Segregated Witness wallets of which 2 Native segwit wallets and 2 wallets with P2SH embedded 11 | # segwit scripts so every wallet can send payments to them. 12 | # 13 | # These four wallet types will be created: 14 | # * P2WPKH - Pay-to-wallet-public-key-hash, Native SegWit single key 15 | # * P2WSH - Pay-to-wallet-script-hash, Native SegWit multisig/script 16 | # * P2SH-P2WPKH - P2WPKH nested in a P2SH script 17 | # * P2SH-P2WSH - P2WSH nested in P2SH script 18 | # 19 | # If you deposit at least 0.001 TBTC test bitcoins to the first wallet, several transactions will be created for every 20 | # kind of segwit wallet 21 | # 22 | 23 | from bitcoinlib.wallets import * 24 | from bitcoinlib.keys import HDKey 25 | from time import sleep 26 | 27 | 28 | tx_fee = 500 29 | tx_amount = 1000 30 | wif = 'tprv8ZgxMBicQKsPdd7kWYnxC5BTucY6fESWSA9tWwtKiSpasvL1WDbtHNEU8sZDTWcoxG2qYzBA5HFWzR2NoxgG2MTyR8PeCry266DbmjF8pT4' 31 | wif2 = 'tprv8ZgxMBicQKsPe2Fpzm7zK6WsUqcYGZsZe3vwvQGLEqe8eunrxJXXxaw3pF283uQ9J7EhTVazDhKVquwk8a5K1rSx3T9qZJiNHkzJz3sRrWd' 32 | 33 | # 34 | # CREATE WALLETS 35 | # 36 | 37 | # Segwit P2SH-P2WPKH Wallet 38 | w1 = wallet_create_or_open('segwit_testnet_p2sh_p2wpkh', keys=wif, witness_type='p2sh-segwit', network='testnet') 39 | w1_key = w1.get_key() 40 | 41 | # Segwit Native P2WPKH Wallet 42 | w2 = wallet_create_or_open('segwit_testnet_p2wpkh', keys=wif, witness_type='segwit', network='testnet') 43 | w2_key = w2.get_key() 44 | 45 | # Segwit Native P2WSH Wallet 46 | w3 = wallet_create_or_open('segwit_testnet_p2wsh', 47 | keys=[wif, HDKey(wif2).public_master_multisig(witness_type='segwit').public()], 48 | witness_type='segwit', network='testnet') 49 | w3_key = w3.get_key() 50 | 51 | # Segwit P2SH-P2WSH Wallet 52 | w4 = wallet_create_or_open('segwit_testnet_p2sh_p2wsh', 53 | keys=[wif, HDKey(wif2).public_master_multisig(witness_type='p2sh-segwit').public()], 54 | witness_type='p2sh-segwit', network='testnet') 55 | w4_key = w4.get_key() 56 | 57 | 58 | # 59 | # SEND TRANSACTIONS 60 | # 61 | 62 | w1.utxos_update() 63 | w1.info() 64 | if not w1.utxos(): 65 | print("No UTXO'S found, please make a test-bitcoin deposit to %s. Minimum amount needed is %d sathosi" % 66 | (w1_key.address, (4 * (tx_fee + tx_amount)))) 67 | else: 68 | print("Open balance: %s" % w1.balance()) 69 | if w1.balance() < ((tx_fee+tx_amount)*4): 70 | print("Balance to low, please deposit at least %s to %s" % 71 | (((tx_fee+tx_amount)*4)-w1.balance(), w1_key.address)) 72 | print("Sending transaction from wallet #1 to wallet #2:") 73 | t = w1.send_to(w2_key.address, 4 * tx_amount, fee=tx_fee, broadcast=True, min_confirms=0) 74 | t.info() 75 | 76 | while True: 77 | w2.utxos_update() 78 | print("waiting for tx broadcast") 79 | sleep(3) 80 | if w2.utxos(): 81 | print("Sending transaction from wallet #2 to wallet #3:") 82 | t2 = w2.sweep(w3_key.address, fee=tx_fee, broadcast=True, min_confirms=0) 83 | t2.info() 84 | break 85 | 86 | while True: 87 | w3.utxos_update() 88 | print("waiting for tx broadcast") 89 | sleep(3) 90 | if w3.utxos(): 91 | print("Sending transaction from wallet #3 to wallet #4:") 92 | t3 = w3.sweep(w4_key.address, fee=tx_fee, broadcast=True, min_confirms=0) 93 | t3.sign(wif2) 94 | t3.send() 95 | t3.info() 96 | break 97 | 98 | while True: 99 | w4.utxos_update() 100 | print("waiting for tx broadcast") 101 | sleep(3) 102 | if w4.utxos(): 103 | print("Sending transaction from wallet #4 to wallet #1:") 104 | t4 = w4.sweep(w1_key.address, fee=tx_fee, broadcast=True, min_confirms=0) 105 | t4.sign(wif2) 106 | t4.send() 107 | t4.info() 108 | break 109 | -------------------------------------------------------------------------------- /examples/encoding.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Encoding helper methods 6 | # 7 | # © 2017 September - 1200 Web Development 8 | # 9 | 10 | from bitcoinlib.encoding import * 11 | 12 | # 13 | # Change Base conversion examples 14 | # 15 | examples = [ 16 | ('4c52127a72fb42b82439ab18697dcfcfb96ac63ba8209833b2e29f2302b8993f45e743412d65c7a571da70259d4f6795e98af20e6e' 17 | '57603314a662a49c198199', 16, 256), 18 | ('LRzrûB¸$9«i}ÏϹjÆ;¨ ˜3²âŸ#¸™?EçCA-eÇ¥qÚp%Og•éŠònW`3¦b¤œ™', 256, 16), 19 | # ('LRzrûB¸$9«i}ÏϹjÆ;¨ ˜3²âŸ#¸™?EçCA-eÇ¥qÚp%Og•éŠònW`3¦b¤œ™', 16, 256), # Test EncodingError 20 | ('L1odb1uUozbfK2NrsMyhJfvRsxGM2AxixgPL8vG9BUBnE6W1VyTX', 58, 16), 21 | ('FF', 16, 10), 22 | ('AF', 16, 2), 23 | (200, 10, 16, 2), 24 | (200, 10, 16, 4), 25 | ('thisisfunny', 32, 3), 26 | ('1LeNnaRtV52nNtZXvtw6PaGKpk46hU1Xmx', 58, 16), 27 | ('1LeNnaRtV52nNtZXvtw6PaGKpk46hU1Xmx', 58, 32), 28 | ('1LeNnaRtV52nNtZXvtw6PaGKpk46hU1Xmx', 58, 256), 29 | ('1LeNnaRtV52nNtZXvtw6PaGKpk46hU1Xmx', 58, 2048), 30 | ([b'\0', b'\x12', b'L'], 256, 16, 6), 31 | ("為 宋 暴 治 伯 及 灘 冶 忙 逃 湘 艇 例 讓 忠", 256, 16), 32 | (b'\x00\t\xc6\xe7\x11\x18\xd8\xf1+\xeck\\a\x88K5g|\n\n\xe3*\x02\x1f\x87', 256, 58), 33 | (b'\0', 256, 10), 34 | ("\x00\x01\tfw`\x06\x95=UgC\x9e^9\xf8j\r';\xee\xd6\x19g\xf6", 256, 58), 35 | (b'LR\x12zr\xfbB\xb8$9\xab\x18i}\xcf\xcf\xb9j\xc6;\xa8 \x983\xb2\xe2\x9f#\x02\xb8\x99?E\xe7CA-e\xc7\xa5q' 36 | b'\xdap%\x9dOg\x95\xe9\x8a\xf2\x0enW`3\x14\xa6b\xa4\x9c\x19\x81\x99', 256, 16), 37 | ] 38 | 39 | print("\n=== Change base: convert from base N to base M ===") 40 | for example in examples: 41 | print("\n>>> change_base%s # Change from base%d to base%d" % 42 | (example, example[1], example[2])) 43 | print("%s" % change_base(*example)) 44 | 45 | # 46 | # Address and Script conversion examples 47 | # 48 | print("\n=== Conversion of Bitcoin Addresses to Public Key Hashes ===") 49 | addrs = ['1KcBA4i4Qqu1oRjobyWU3R5UXUorLQ3jUg', '1111111111111111111114oLvT2', 50 | '1QLbz7JHiBTspS962RLKV8GndWFwi5j6Qr'] 51 | for addr in addrs: 52 | print("Public Key Hash of address '%s' is '%s'" % (addr, addr_to_pubkeyhash(addr, True))) 53 | 54 | print("\n=== From Public Key Hashes to address ===") 55 | print(pubkeyhash_to_addr('13d215d212cd5188ae02c5635faabdc4d7d4ec91')) 56 | print(pubkeyhash_to_addr('00' * 20)) 57 | 58 | print("\n=== Create PKH from redeemscript ===") 59 | redeemscript = '5221023dd6aeaa2acb92cbea35820361e5fd07af10f4b01c985adec30848b424756a6c210381cd2bb2a38d939fa677a5dcc' \ 60 | '981ee0630b32b956b2e6dc3e1c028e6d09db5a72103d2c6d31cabe4025c25879010465f501194b352823c553660d303adfa' \ 61 | '9a26ad3c53ae' 62 | print(to_hexstring(hash160(to_bytes(redeemscript)))) 63 | 64 | # 65 | # Other type conversions and normalizations 66 | # 67 | 68 | der_signature = '3045022100f952ff1b290c54d8b9fd35573b50f1af235632c595bb2f10b34127fb82f66d18022068b59150f825a81032c' \ 69 | '22ce2db091d6fd47294c9e2144fa0291949402e3003ce' 70 | print("\n=== Convert DER encoded signature ===") 71 | print(convert_der_sig(to_bytes(der_signature))) 72 | 73 | print("\n=== Varbyte Int conversions ===") 74 | print("Number 1000 as Varbyte Integer (hexstring): %s" % to_hexstring(int_to_varbyteint(1000))) 75 | print("Converted back (3 is size in bytes: 1 size byte + integer in bytes): ", varbyteint_to_int(to_bytes('fde803'))) 76 | 77 | # Normalize data 78 | print("\n=== Normalizations ===") 79 | data = [ 80 | u"guion cruz envío papel otoño percha hazaña salir joya gorra íntimo actriz", 81 | u'\u2167', 82 | u'\uFDFA', 83 | "あじわう ちしき たわむれる おくさま しゃそう うんこう ひてい みほん たいほ てのひら りこう わかれる かいすいよく こもん ねもと", 84 | '12345', 85 | ] 86 | 87 | for dt in data: 88 | print("\nInput data", dt) 89 | print("Normalized unicode string (normalize_string): ", normalize_string(dt)) 90 | print("Normalized variable (normalize_var): ", normalize_var(dt)) 91 | 92 | 93 | # Convert Bech32 address to Public key hash 94 | address = "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4" 95 | pkh = "0014751e76e8199196d454941c45d1b3a323f1433bd6" 96 | pkh_converted = addr_bech32_to_pubkeyhash(address, prefix='bc', include_witver=True, as_hex=True) 97 | print(pkh, " == ", pkh_converted) 98 | addr = pubkeyhash_to_addr_bech32(pkh_converted, address[:2].lower()) 99 | -------------------------------------------------------------------------------- /bitcoinlib/data/providers.examples-nownodes.json: -------------------------------------------------------------------------------- 1 | { 2 | "bitcoinlib_test": { 3 | "provider": "bitcoinlibtest", 4 | "network": "bitcoinlib_test", 5 | "client_class": "BitcoinLibTestClient", 6 | "provider_coin_id": "", 7 | "url": "local", 8 | "api_key": "", 9 | "priority": 10, 10 | "denominator": 100000000, 11 | "network_overrides": null, 12 | "timeout": 0 13 | }, 14 | "nownodes.litecoin": { 15 | "provider": "nownodes", 16 | "network": "litecoin", 17 | "client_class": "NownodesClient", 18 | "provider_coin_id": "", 19 | "url": "https://ltc.nownodes.io/", 20 | "api_key": "", 21 | "priority": 10, 22 | "denominator": 1, 23 | "network_overrides": null, 24 | "timeout": 0 25 | }, 26 | "nownodes.litecoin.blockbook": { 27 | "provider": "blockbook1", 28 | "network": "litecoin", 29 | "client_class": "BlockbookClient", 30 | "provider_coin_id": "", 31 | "url": "https://ltcbook.nownodes.io/api/", 32 | "api_key": "", 33 | "priority": 10, 34 | "denominator": 1, 35 | "network_overrides": null, 36 | "timeout": 0 37 | }, 38 | "nownodes.litecoin_legacy": { 39 | "provider": "nownodes", 40 | "network": "litecoin_legacy", 41 | "client_class": "NownodesClient", 42 | "provider_coin_id": "", 43 | "url": "https://ltc.nownodes.io/", 44 | "api_key": "", 45 | "priority": 10, 46 | "denominator": 1, 47 | "network_overrides": null, 48 | "timeout": 0 49 | }, 50 | "nownodes.litecoin_legacy.blockbook": { 51 | "provider": "blockbook1", 52 | "network": "litecoin_legacy", 53 | "client_class": "BlockbookClient", 54 | "provider_coin_id": "", 55 | "url": "https://ltcbook.nownodes.io/api/", 56 | "api_key": "", 57 | "priority": 10, 58 | "denominator": 1, 59 | "network_overrides": null, 60 | "timeout": 0 61 | }, 62 | "nownodes.litecoin.testnet": { 63 | "provider": "nownodes", 64 | "network": "litecoin-testnet", 65 | "client_class": "NownodesClient", 66 | "provider_coin_id": "", 67 | "url": "https://ltc-testnet.nownodes.io/", 68 | "api_key": "", 69 | "priority": 10, 70 | "denominator": 1, 71 | "network_overrides": null, 72 | "timeout": 0 73 | }, 74 | "nownodes.litecoin.testnet.blockbook": { 75 | "provider": "blockbook1", 76 | "network": "litecoin_testnet", 77 | "client_class": "BlockbookClient", 78 | "provider_coin_id": "", 79 | "url": "https://ltcbook-testnet.nownodes.io/api/", 80 | "api_key": "", 81 | "priority": 10, 82 | "denominator": 1, 83 | "network_overrides": null, 84 | "timeout": 0 85 | }, 86 | "nownodes": { 87 | "provider": "nownodes", 88 | "network": "bitcoin", 89 | "client_class": "NownodesClient", 90 | "provider_coin_id": "", 91 | "url": "https://btc.nownodes.io/", 92 | "api_key": "", 93 | "priority": 10, 94 | "denominator": 1, 95 | "network_overrides": null, 96 | "timeout": 0 97 | }, 98 | "nownodes.blockbook": { 99 | "provider": "blockbook1", 100 | "network": "bitcoin", 101 | "client_class": "BlockbookClient", 102 | "provider_coin_id": "", 103 | "url": "https://btcbook.nownodes.io/api/", 104 | "api_key": "", 105 | "priority": 10, 106 | "denominator": 1, 107 | "network_overrides": null, 108 | "timeout": 0 109 | }, 110 | "nownodes.testnet": { 111 | "provider": "nownodes", 112 | "network": "testnet", 113 | "client_class": "NownodesClient", 114 | "provider_coin_id": "", 115 | "url": "https://btc-testnet.nownodes.io/", 116 | "api_key": "", 117 | "priority": 10, 118 | "denominator": 1, 119 | "network_overrides": null, 120 | "timeout": 0 121 | }, 122 | "nownodes.testnet.blockbook": { 123 | "provider": "blockbook1", 124 | "network": "testnet", 125 | "client_class": "BlockbookClient", 126 | "provider_coin_id": "", 127 | "url": "https://btcbook-testnet.nownodes.io/api/", 128 | "api_key": "", 129 | "priority": 10, 130 | "denominator": 1, 131 | "network_overrides": null, 132 | "timeout": 0 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /examples/wallet_multisig_3of5.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # Create Multisig 3-of-5 wallet 6 | # 7 | # © 2017 - 2023 May - 1200 Web Development 8 | # 9 | 10 | from pprint import pprint 11 | from bitcoinlib.wallets import wallet_exists, Wallet, wallet_delete_if_exists 12 | from bitcoinlib.keys import HDKey 13 | 14 | WALLET_NAME = "Multisig_3of5" 15 | NETWORK = 'testnet' 16 | WITNESS_TYPE = 'p2sh-segwit' 17 | SIGS_N = 5 18 | SIGS_REQUIRED = 3 19 | 20 | 21 | # COSIGNER DICTIONARY 22 | # Create keys with mnemonic_key_create.py on separate instances and insert public or private key in dict 23 | # 24 | cosigners = [ 25 | { 26 | 'name': 'Anita', 27 | 'key_type': 'bip44', 28 | 'key': 'moral juice congress aerobic denial beyond purchase spider slide dwarf yard online' 29 | }, 30 | { 31 | 'name': 'Bart', 32 | 'key_type': 'bip44', 33 | 'key': 'concert planet pause then raccoon wait security stuff trim guilt deposit ranch' 34 | }, 35 | { 36 | 'name': 'Chris', 37 | 'key_type': 'bip44', 38 | 'key': 'surprise gasp certain ugly era confirm castle zoo near bread adapt deliver' 39 | }, 40 | { 41 | 'name': 'Daan', 42 | 'key_type': 'bip44', 43 | 'key': 'tprv8ZgxMBicQKsPeS4DjbqVkrV6u4i7wCvM6iUeiSPTFLuuN94bQjdmdmGrZ9cz29wjVc4oHqLZq9yd1Q1urjbpjTBVVFBK4TaGxy9kN68rUee' 44 | }, 45 | { 46 | 'name': 'Paper-Backup', 47 | 'key_type': 'single', 48 | 'key': 'nurse other famous achieve develop interest kangaroo jealous alpha machine ability swarm' 49 | }, 50 | ] 51 | COSIGNER_NAME_THIS_WALLET = 'Chris' 52 | 53 | # wallet_delete_if_exists(WALLET_NAME) 54 | if not wallet_exists(WALLET_NAME): 55 | # This wallets key list, use tools/mnemonic_key_create.py to create your own. 56 | # 57 | cosigners_private = [] 58 | key_list = [] 59 | for cosigner in cosigners: 60 | if not cosigner['key']: 61 | raise ValueError("Please create private keys with mnemonic_key_create.py and add to COSIGNERS definitions") 62 | if len(cosigner['key'].split(" ")) > 1: 63 | hdkey = HDKey.from_passphrase(cosigner['key'], key_type=cosigner['key_type'], witness_type=WITNESS_TYPE, 64 | network=NETWORK) 65 | else: 66 | hdkey = HDKey(cosigner['key'], key_type=cosigner['key_type'], witness_type=WITNESS_TYPE, network=NETWORK) 67 | if cosigner['name'] != COSIGNER_NAME_THIS_WALLET: 68 | if hdkey.key_type == 'single': 69 | hdkey = hdkey.public() 70 | else: 71 | hdkey = hdkey.public_master_multisig() 72 | cosigner['hdkey'] = hdkey 73 | key_list.append(hdkey) 74 | 75 | if len(key_list) != SIGS_N: 76 | raise ValueError("Number of cosigners (%d) is different then expected. SIG_N=%d" % (len(key_list), SIGS_N)) 77 | wallet3o5 = Wallet.create(WALLET_NAME, key_list, sigs_required=SIGS_REQUIRED, witness_type=WITNESS_TYPE, 78 | network=NETWORK) 79 | wallet3o5.new_key() 80 | print("\n\nA multisig wallet with 1 key has been created on this system") 81 | else: 82 | wallet3o5 = Wallet(WALLET_NAME) 83 | 84 | print("\nUpdating UTXO's...") 85 | wallet3o5.utxos_update() 86 | wallet3o5.info() 87 | utxos = wallet3o5.utxos() 88 | wallet3o5.info() 89 | 90 | # Creating transactions just like in a normal wallet, then send raw transaction to other cosigners. They 91 | # can sign the transaction with their own key and pass it on to the next signer or broadcast it to the network. 92 | # You can use bitcoinlib/tools/sign_raw.py to import and sign a raw transaction. 93 | 94 | t = None 95 | if utxos: 96 | print("\nNew unspent outputs found!") 97 | print("Now a new transaction will be created to sweep this wallet and send bitcoins to a testnet faucet") 98 | send_to_address = '2NGZrVvZG92qGYqzTLjCAewvPZ7JE8S8VxE' 99 | t = wallet3o5.sweep(send_to_address, min_confirms=0, broadcast=False) 100 | print("Now send the raw transaction hex to one of the other cosigners to sign using sign_raw.py") 101 | print("Raw transaction: %s" % t.raw_hex()) 102 | else: 103 | print("Please send funds to %s, so we can create a transaction" % wallet3o5.get_key().address) 104 | print("Restart this program when funds are send...") 105 | 106 | # Sign the transaction with 2 other cosigner keys and push the transaction 107 | if t: 108 | t.sign(cosigners[0]['key']) 109 | t.sign(cosigners[4]['key']) 110 | t.send() 111 | t.info() 112 | -------------------------------------------------------------------------------- /docs/_static/manuals.sqlcipher.rst: -------------------------------------------------------------------------------- 1 | Encrypt Database or Private Keys 2 | ================================ 3 | 4 | If your database contains private keys it is a good idea to encrypt your data. This will not be done automatically. At the moment you have 2 options: 5 | 6 | - Encrypt the database with SQLCipher. The database is fully encrypted and you need to provide the password in the Database URI when opening the database. 7 | - Use a normal database but all private key data will be stored AES encrypted in the database. A key to encrypt and decrypt needs to be provided in the Environment. 8 | 9 | Encrypt database with SQLCipher 10 | ------------------------------- 11 | 12 | To protect your data such as the private keys you can use SQLCipher to encrypt the full database. SQLCipher is a 13 | SQLite extension which uses 256-bit AES encryption and also works together with SQLAlchemy. 14 | 15 | Is quite easy to setup and use with Bitcoinlib. First install the required packages, the following works on Ubuntu, but 16 | your system might require other packages. Please read https://www.zetetic.net/sqlcipher/ for installations instructions. 17 | 18 | .. code-block:: bash 19 | 20 | $ sudo apt install sqlcipher libsqlcipher0 libsqlcipher-dev 21 | $ pip install sqlcipher3-binary 22 | # Previous, but now unmaintained: $ pip install pysqlcipher3 23 | 24 | 25 | **Create an Encrypted Database for your Wallet** 26 | 27 | Now you can simply create and use an encrypted database by supplying a password as an argument to the Wallet object: 28 | 29 | .. code-block:: python 30 | 31 | password = 'secret' 32 | db_uri = '/home/user/.bitcoinlib/database/bcl_encrypted.db' 33 | wlt = wallet_create_or_open('bcltestwlt4', network='bitcoinlib_test', db_uri=db_uri, db_password=password) 34 | 35 | 36 | **Encrypt using Database URI** 37 | 38 | You can also use a SQLCipher database URI to create and query an encrypted database: 39 | 40 | .. code-block:: python 41 | 42 | password = 'secret' 43 | filename = '/home/user/.bitcoinlib/database/bcl_encrypted.db' 44 | db_uri = 'sqlite+pysqlcipher://:%s@/%s?cipher=aes-256-cfb&kdf_iter=64000' % (password, filename) 45 | wlt = Wallet.create('bcltestwlt4', network='bitcoinlib_test', db_uri=db_uri) 46 | 47 | If you look at the contents of the SQLite database you can see it is encrypted. 48 | 49 | .. code-block:: bash 50 | 51 | $ cat ~/.bitcoinlib/database/bcl_encrypted.db 52 | 53 | 54 | 55 | Encrypt private key fields with AES 56 | ----------------------------------- 57 | 58 | It is also possible to just encrypt the private keys in the database with secure AES encryption. You need to provide a key or password as environment variable. 59 | 60 | * You can skip this step if you want, but this provides an extra warning / check when no encryption key is found: Enable database encryption in Bitcoinlib configuration settings at ~/.bitcoinlib/config.ini 61 | 62 | .. code-block:: text 63 | 64 | # Encrypt private key field in database using symmetrically EAS encryption. 65 | # You need to set the password in the DB_FIELD_ENCRYPTION_KEY environment variable. 66 | database_encryption_enabled=True 67 | 68 | You can provide an encryption key directly or use a password to create a key: 69 | 70 | 1. Generate a secure 32 bytes encryption key yourself with Bitcoinlib: 71 | 72 | .. code-block:: python 73 | 74 | >>> from bitcoinlib.keys import Key 75 | >>> Key().private_hex() 76 | '2414966ea9f2de189a61953c333f61013505dfbf8e383b5ed6cb1981d5ec2620' 77 | 78 | This key needs to be stored in the environment when creating or accessing a wallet. No extra arguments have to be provided to the Wallet class, the data is encrypted and decrypted at database level. 79 | 80 | 2. You can also just provide a password, and let Bitcoinlib create a key for you. You will need to pass the DB_FIELD_ENCRYPTION_PASSWORD environment variable. 81 | 82 | There are several ways to store the key in an Environment variable, on Linux you can do: 83 | 84 | .. code-block:: bash 85 | 86 | $ export DB_FIELD_ENCRYPTION_KEY='2414966ea9f2de189a61953c333f61013505dfbf8e383b5ed6cb1981d5ec2620' 87 | 88 | or 89 | 90 | .. code-block:: bash 91 | 92 | $ export DB_FIELD_ENCRYPTION_PASSWORD=ineedtorememberthispassword 93 | 94 | Or in Windows: 95 | 96 | .. code-block:: bash 97 | 98 | $ setx DB_FIELD_ENCRYPTION_KEY '2414966ea9f2de189a61953c333f61013505dfbf8e383b5ed6cb1981d5ec2620' 99 | 100 | Environment variables can also be stored in an .env key, in a virtual environment or in Python code itself. However anyone with access to the key can decrypt your private keys. 101 | 102 | Please make sure to remember and backup your encryption key or password, if you lose your key the private keys can not be recovered! 103 | -------------------------------------------------------------------------------- /bitcoinlib/services/bitcoinlibtest.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # BitcoinLib Test Network for Unit Tests 5 | # © 2018 February - 1200 Web Development 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU Affero General Public License as 9 | # published by the Free Software Foundation, either version 3 of the 10 | # License, or (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU Affero General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU Affero General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | import logging 22 | import hashlib 23 | from bitcoinlib.services.baseclient import BaseClient 24 | from bitcoinlib.main import MAX_TRANSACTIONS 25 | from bitcoinlib.encoding import addr_to_pubkeyhash, addr_bech32_to_pubkeyhash, double_sha256, to_bytes 26 | 27 | _logger = logging.getLogger(__name__) 28 | 29 | PROVIDERNAME = 'bitcoinlib' 30 | 31 | 32 | class BitcoinLibTestClient(BaseClient): 33 | """ 34 | Dummy service client for bitcoinlib test network. Only used for testing. 35 | 36 | Does not make any connection to a service provider, so can be used offline. 37 | 38 | """ 39 | 40 | def __init__(self, network, base_url, denominator, *args): 41 | super(self.__class__, self).__init__(network, PROVIDERNAME, base_url, denominator, *args) 42 | 43 | def getbalance(self, addresslist): 44 | """ 45 | Dummy getbalance method for bitcoinlib testnet 46 | 47 | :param addresslist: List of addresses 48 | :type addresslist: list 49 | 50 | :return int: 51 | """ 52 | return self.units * len(addresslist) 53 | 54 | def _get_txid(self, address, n): 55 | try: 56 | pkh = str(n).encode() + addr_to_pubkeyhash(address)[1:] 57 | except Exception: 58 | pkh = str(n).encode() + addr_bech32_to_pubkeyhash(address)[1:] 59 | return hashlib.sha256(pkh).hexdigest() 60 | 61 | def getutxos(self, address, after_txid='', limit=10, utxos_per_address=2): 62 | """ 63 | Dummy method to retreive UTXO's. This method creates a new UTXO for each address provided out of the 64 | testnet void, which can be used to create test transactions for the bitcoinlib testnet. 65 | 66 | :param address: Address string 67 | :type address: str 68 | :param after_txid: Transaction ID of last known transaction. Only check for utxos after given tx id. Default: Leave empty to return all utxos. If used only provide a single address 69 | :type after_txid: str 70 | :param limit: Maximum number of utxo's to return 71 | :type limit: int 72 | 73 | :return list: The created UTXO set 74 | """ 75 | utxos = [] 76 | for n in range(utxos_per_address): 77 | txid = self._get_txid(address, n) 78 | utxos.append( 79 | { 80 | 'address': address, 81 | 'txid': txid, 82 | 'confirmations': 10, 83 | 'output_n': 0, 84 | 'index': 0, 85 | 'value': 1 * self.units, 86 | 'script': '', 87 | } 88 | ) 89 | return utxos 90 | 91 | # def gettransaction(self, tx_id): 92 | 93 | # def gettransactions(self, address, after_txid='', limit=MAX_TRANSACTIONS): 94 | 95 | def sendrawtransaction(self, rawtx): 96 | """ 97 | Dummy method to send transactions on the bitcoinlib testnet. The bitcoinlib testnet does not exists, 98 | so it just returns the transaction hash. 99 | 100 | :param rawtx: A raw transaction hash 101 | :type rawtx: bytes, str 102 | 103 | :return str: Transaction hash 104 | """ 105 | txid = double_sha256(to_bytes(rawtx))[::-1].hex() 106 | return { 107 | 'txid': txid, 108 | 'response_dict': {} 109 | } 110 | 111 | def estimatefee(self, blocks): 112 | """ 113 | Dummy estimate fee method for the bitcoinlib testnet. 114 | 115 | :param blocks: Number of blocks 116 | :type blocks: int 117 | 118 | :return int: Fee as 100000 // number of blocks 119 | """ 120 | return 100000 // blocks 121 | 122 | def blockcount(self): 123 | return 1 124 | 125 | def mempool(self, txid=''): 126 | return [txid] 127 | -------------------------------------------------------------------------------- /docs/_static/manuals.setup-bitcoind-connection.rst: -------------------------------------------------------------------------------- 1 | How to connect bitcoinlib to a Bitcoin node 2 | =========================================== 3 | 4 | This manual explains how to connect to a bitcoind server on your localhost or a remote server. 5 | 6 | Running your own bitcoin node allows you to create a large number of requests, faster response times, 7 | and more control, privacy and independence. However you need to install and maintain it and it uses 8 | a lot of resources. 9 | 10 | .. warning:: 11 | With a standard Bitcoin node you can only retrieve block and transaction information. You can not 12 | query the node for information about specific addresses. So it is not suitable to run in combination with a Bitcoinlib 13 | wallet. If you would like to use Bitcoinlib wallets and not be dependent on external providers you should use a 14 | `Bcoin node `_, `ElectrumX `_ server 15 | or `Blockbook server `_ server instead. 16 | 17 | 18 | Bitcoin node settings 19 | --------------------- 20 | 21 | This manual assumes you have a full bitcoin node up and running. 22 | For more information on how to install a full node read https://bitcoin.org/en/full-node 23 | 24 | Please make sure you have server and txindex option set to 1. 25 | 26 | Generate a RPC authorization configuration string online: https://jlopp.github.io/bitcoin-core-rpc-auth-generator/ 27 | or with the Python tool you can find in the Bitcoin repository: https://github.com/bitcoin/bitcoin/blob/master/share/rpcauth/rpcauth.py 28 | 29 | So your bitcoin.conf file for testnet should look something like this. For mainnet use port 8332, 30 | and remove the 'testnet=1' line. 31 | 32 | .. code-block:: text 33 | 34 | server=1 35 | port=18332 36 | txindex=1 37 | testnet=1 38 | rpcauth=bitcoinlib:01cf8eb434e3c9434e244daf3fc1cc71$9cdfb346b76935569683c12858e13147eb5322399580ba51d2d878148a880d1d 39 | rpcbind=0.0.0.0 40 | rpcallowip=192.168.0.0/24 41 | 42 | To increase your privacy and security, and for instance if you run a Bitcoin node on your home network, you can 43 | use TOR. Bitcoind has TOR support build in, and it is ease to setup. 44 | See https://en.bitcoin.it/wiki/Setting_up_a_Tor_hidden_service 45 | 46 | If you have a TOR service running you can add these lines to your bitcoin.conf settings to only use TOR. 47 | 48 | .. code-block:: text 49 | 50 | proxy=127.0.0.1:9050 51 | bind=127.0.0.1 52 | onlynet=onion 53 | 54 | 55 | Connect using provider settings 56 | ------------------------------- 57 | 58 | Connection settings can be added to the service provider settings file in 59 | .bitcoinlib/config/providers.json 60 | 61 | Example: 62 | 63 | .. code-block:: json 64 | 65 | { 66 | "bitcoind.testnet": { 67 | "provider": "bitcoind", 68 | "network": "testnet", 69 | "client_class": "BitcoindClient", 70 | "url": "http://user:password@server_url:18332", 71 | "api_key": "", 72 | "priority": 11, 73 | "denominator": 100000000, 74 | "network_overrides": null 75 | "timeout": 0 76 | } 77 | } 78 | 79 | 80 | Connect using base_url argument 81 | ------------------------------- 82 | 83 | You can also directly pass connection string with the 'base_url' argument in the BitcoindClient object. 84 | 85 | This provides more flexibility but also the responsibility to store user and password information in a secure way. 86 | 87 | .. code-block:: python 88 | 89 | from bitcoinlib.services.bitcoind import BitcoindClient 90 | 91 | base_url = 'http://user:password@server_url:18332' 92 | bdc = BitcoindClient(base_url=base_url) 93 | txid = 'e0cee8955f516d5ed333d081a4e2f55b999debfff91a49e8123d20f7ed647ac5' 94 | rt = bdc.getrawtransaction(txid) 95 | print("Raw: %s" % rt) 96 | 97 | 98 | You can directly r 99 | 100 | .. code-block:: python 101 | 102 | from bitcoinlib.services.bitcoind import BitcoindClient 103 | 104 | # Retrieve some blockchain information and statistics 105 | bdc.proxy.getblockchaininfo() 106 | bdc.proxy.getchaintxstats() 107 | bdc.proxy.getmempoolinfo() 108 | 109 | # Add a node to the node list 110 | bdc.proxy.addnode('blocksmurfer.io', 'add') 111 | 112 | 113 | 114 | Please note: Using a remote bitcoind server 115 | ------------------------------------------- 116 | 117 | Using RPC over a public network is unsafe, so since bitcoind version 0.18 remote RPC for all network interfaces 118 | are disabled. The rpcallowip option cannot be used to listen on all network interfaces and rpcbind has to be used to 119 | define specific IP addresses to listen on. See https://bitcoin.org/en/release/v0.18.0#configuration-option-changes 120 | 121 | You could setup an openvpn or ssh tunnel to connect to a remote server to avoid this issues. 122 | -------------------------------------------------------------------------------- /docs/_static/manuals.faq.rst: -------------------------------------------------------------------------------- 1 | Frequently Asked Questions 2 | ========================== 3 | 4 | Can I use Bitcoinlib on my system? 5 | ---------------------------------- 6 | 7 | BitcoinLib is platform independent and should run on your system. 8 | Bitcoinlib is mainly developed on Ubuntu linux and runs unittests on every commit on Ubuntu and Windows. 9 | Dockerfiles are available for Alpine, Kali and Fedora. You can find all dockerfiles on https://github.com/1200wd/bitcoinlib/tree/master/docker 10 | 11 | I run into an error 'x' when installing Bitcoinlib 12 | -------------------------------------------------- 13 | 14 | 1. Check the `installation page `_ and see if you have installed all the requirements. 15 | 2. Install the required packages one-by-one using pip install, and see if you get any specific errors. 16 | 3. Check for help in `Github Discussions `_. 17 | 4. See if you find any known `issue `_. 18 | 5. If it doesn't work out, do not hesitate to ask your question in the github discussions or post an issue! 19 | 20 | Does Bitcoinlib support 'x'-coin 21 | -------------------------------- 22 | 23 | Bitcoinlib main focus is on Bitcoin. But besides Bitcoin it supports Litecoin and Dogecoin. For testing 24 | it supports the Bitcoin testing networks: testnet3, testnet4, regtest and signet. For other coins the Litecoin testnet and Dogecoin testnet is supported. 25 | 26 | Support for Dash, Bitcoin Cash and Bitcoin SV has been dropped. There are currently no plans to support other coins. Main problem with supporting new coins is the lack of service provides with a working and stable API. 27 | 28 | My wallet transactions are not (correctly) updating! 29 | ---------------------------------------------------- 30 | 31 | Most likely cause is a problem with a specific service provider. 32 | 33 | Please set log level to 'debug' and check the logs in bitcoinlib.log to see if you can pin down the specific error. 34 | You could then disable the provider and post the `issue `_. 35 | 36 | To avoid these kinds of errors it is advised to run your local `Bcoin node `_, 37 | `Blockbook `_ or `ElectrumX `_ server. 38 | 39 | With a local Bcoin node or Blockbook server you do not depend on external Service providers which increases reliability, security, speed and privacy. 40 | 41 | Provider 'x' does not work 42 | -------------------------- 43 | 44 | If you encounter errors when updating transactions, utxo's, blocks, etc there is probably a problem with a specific provider. You can check the logs in bitcoinlib.log and see which provider has problems. To solve this you can: 45 | 46 | * Set priority = 0 for this provider to temporary disable it 47 | * Remove the provider from the providers.json file in you local .bitcoinlib directory 48 | * Use the exclude_provider option when you calling the Service class: 49 | 50 | .. code-block:: python 51 | 52 | srv = Service(exclude_providers=['blocksmurfer']) 53 | 54 | * Or use a specific provider, for instance your local Blockbook server: 55 | 56 | .. code-block:: python 57 | 58 | srv = Service(providers=['blockbook']) 59 | 60 | 61 | Can I use Bitcoinlib with another database besides SQLite? 62 | ---------------------------------------------------------- 63 | 64 | Yes, the library can also work with PostgreSQL or MySQL / MariaDB databases. 65 | For more information see: `Databases `_. 66 | 67 | I have imported a private key from another wallet but the address is different 68 | ------------------------------------------------------------------------------ 69 | 70 | If you have imported a private key from another wallet in Bitcoinlib and the address in Bitcoinlib does not match, then this is probably because the wallets use different key paths or keys at different levels. 71 | 72 | * Check if the level and type of the key it the same? Is it a masterkey (level 0), a master public key (level 3), or a key of an address (level 5) 73 | * Does the wallet uses the same key paths? Bitcoinlib uses the default BIP84 keys path in “m/84’/0’/0’/0/0” format. You can specify a different key path or witness type when creating a wallet 74 | 75 | I found a bug! 76 | -------------- 77 | 78 | Please help out project and post your `issue `_ on Github. 79 | Try to include all code and data so we can reproduce and solve the issue. 80 | 81 | I have another question 82 | ----------------------- 83 | 84 | Maybe your question already has an answer om `Github Discussions `_. 85 | Or search for an answer is this `documentation `_. 86 | 87 | If that does not answer your question, please post your question on the 88 | `Github Discussions Q&A `_. 89 | 90 | 91 | 92 | .. 93 | My transaction is not confirming 94 | Is Bitcoinlib secure? 95 | Donations? 96 | 97 | -------------------------------------------------------------------------------- /tests/bip38_protected_key_tests.json: -------------------------------------------------------------------------------- 1 | {"valid": [ 2 | { 3 | "passphrase": "TestingOneTwoThree", 4 | "bip38": "6PRVWUbkzzsbcVac2qwfssoUJAN1Xhrg6bNk8J7Nzm5H7kxEbn2Nh2ZoGg", 5 | "wif": "5KN7MzqK5wt2TP1fQCYyHBtDrXdJuXbUzm4A9rKAteGu3Qi5CVR", 6 | "address": "1Jq6MksXQVWzrznvZzxkV6oY57oWXD9TXB", 7 | "description": "no EC multiply / no compression #1" 8 | }, 9 | { 10 | "passphrase": "Satoshi", 11 | "bip38": "6PRNFFkZc2NZ6dJqFfhRoFNMR9Lnyj7dYGrzdgXXVMXcxoKTePPX1dWByq", 12 | "wif": "5HtasZ6ofTHP6HCwTqTkLDuLQisYPah7aUnSKfC7h4hMUVw2gi5", 13 | "address": "1AvKt49sui9zfzGeo8EyL8ypvAhtR2KwbL", 14 | "description": "no EC multiply / no compression #2" 15 | }, 16 | { 17 | "passphrase": "TestingOneTwoThree", 18 | "bip38": "6PYNKZ1EAgYgmQfmNVamxyXVWHzK5s6DGhwP4J5o44cvXdoY7sRzhtpUeo", 19 | "wif": "L44B5gGEpqEDRS9vVPz7QT35jcBG2r3CZwSwQ4fCewXAhAhqGVpP", 20 | "address": "164MQi977u9GUteHr4EPH27VkkdxmfCvGW", 21 | "description": "no EC multiply / compression #1" 22 | }, 23 | { 24 | "passphrase": "Satoshi", 25 | "bip38": "6PYLtMnXvfG3oJde97zRyLYFZCYizPU5T3LwgdYJz1fRhh16bU7u6PPmY7", 26 | "wif": "KwYgW8gcxj1JWJXhPSu4Fqwzfhp5Yfi42mdYmMa4XqK7NJxXUSK7", 27 | "address": "1HmPbwsvG5qJ3KJfxzsZRZWhbm1xBMuS8B", 28 | "description": "no EC multiply / compression #2" 29 | }, 30 | { 31 | "passphrase": "TestingOneTwoThree", 32 | "passphrase_code": "passphrasepxFy57B9v8HtUsszJYKReoNDV6VHjUSGt8EVJmux9n1J3Ltf1gRxyDGXqnf9qm", 33 | "bip38": "6PfQu77ygVyJLZjfvMLyhLMQbYnu5uguoJJ4kMCLqWwPEdfpwANVS76gTX", 34 | "wif": "5K4caxezwjGCGfnoPTZ8tMcJBLB7Jvyjv4xxeacadhq8nLisLR2", 35 | "address": "6PfQu77ygVyJLZjfvMLyhLMQbYnu5uguoJJ4kMCLqWwPEdfpwANVS76gTX", 36 | "description": "EC multiply / no compression / no lot sequence numbers #1", 37 | "test_encrypt": false 38 | }, 39 | { 40 | "passphrase": "Satoshi", 41 | "passphrase_code": "passphraseoRDGAXTWzbp72eVbtUDdn1rwpgPUGjNZEc6CGBo8i5EC1FPW8wcnLdq4ThKzAS", 42 | "bip38": "6PfLGnQs6VZnrNpmVKfjotbnQuaJK4KZoPFrAjx1JMJUa1Ft8gnf5WxfKd", 43 | "wif": "5KJ51SgxWaAYR13zd9ReMhJpwrcX47xTJh2D3fGPG9CM8vkv5sH", 44 | "address": "1CqzrtZC6mXSAhoxtFwVjz8LtwLJjDYU3V", 45 | "description": "EC multiply / no compression / no lot sequence numbers #2", 46 | "test_encrypt": false 47 | }, 48 | { 49 | "passphrase": "MOLON LABE", 50 | "passphrase_code": "passphraseaB8feaLQDENqCgr4gKZpmf4VoaT6qdjJNJiv7fsKvjqavcJxvuR1hy25aTu5sX", 51 | "bip38": "6PgNBNNzDkKdhkT6uJntUXwwzQV8Rr2tZcbkDcuC9DZRsS6AtHts4Ypo1j", 52 | "wif": "5JLdxTtcTHcfYcmJsNVy1v2PMDx432JPoYcBTVVRHpPaxUrdtf8", 53 | "address": "1Jscj8ALrYu2y9TD8NrpvDBugPedmbj4Yh", 54 | "description": "EC multiply / no compression / lot sequence numbers #1", 55 | "test_encrypt": false 56 | } 57 | ], 58 | "invalid": { 59 | "decrypt": [], 60 | "encrypt": [], 61 | "verify": [ 62 | { 63 | "description": "Invalid base58", 64 | "exception": "Invalid checksum", 65 | "base58": "6PgGWtx25kUg8QWvwuJAgorN6k9FbE25rv5dMRwu5SKMnfpfVe5marXXXX" 66 | }, 67 | { 68 | "description": "Length > 39", 69 | "exception": "Invalid BIP38 data length", 70 | "hex": "0142c000000000000000000000000000000000000000000000000000000000000000000000000000", 71 | "base58": "QmxDezFMDL7ExfYmsETsQXAtBbw5YE1CDyA8pm1AGpMpVVUpsVy1yXv4VTL" 72 | }, 73 | { 74 | "description": "Length < 39", 75 | "exception": "Invalid BIP38 data length", 76 | "hex": "0142c00000000000000000000000000000000000000000000000000000000000000000000000", 77 | "base58": "2DnNxWcx4Prn8wmjbkvtYGDALsq8BMWxQ33KnXkeH8vrxE41psDLXRmK3" 78 | }, 79 | { 80 | "description": "prefix !== 0x01", 81 | "exception": "Invalid BIP38 prefix", 82 | "hex": "0242c0000000000000000000000000000000000000000000000000000000000000000000000000", 83 | "base58": "AfE1YY4Wr2FLAENaH9PVaLRdyk714V4rhwiJMSGyQCGFB3rhGDCs2R7c4s" 84 | }, 85 | { 86 | "description": "flag !== 0xc0 && flag !== 0xe0", 87 | "exception": "Invalid BIP38 type", 88 | "hex": "0101ff000000000000000000000000000000000000000000000000000000000000000000000000", 89 | "base58": "5JjnYkbFBmUnhGeDMVhR7aSitLToe1odEfXDBeg4RMK6JmAm9g7rkm7qY3" 90 | }, 91 | { 92 | "description": "EC Mult: ~(flag & 0x24)", 93 | "exception": "Invalid BIP38 type", 94 | "hex": "0101db000000000000000000000000000000000000000000000000000000000000000000000000", 95 | "base58": "5JbtdQFKSemRTqMuWrJgSfzE8AX2jdz1KiZuMmuUcv9iXha1s6UarQTciW" 96 | }, 97 | { 98 | "description": "EC Mult: ~(flag & 0x24)", 99 | "exception": "Invalid BIP38 type", 100 | "hex": "010135000000000000000000000000000000000000000000000000000000000000000000000000", 101 | "base58": "5HyV7HSYdHUgLf7w36mxMHDPH9muTgUYHEj6cEogKMuV7ae8VRM3VEg56w" 102 | } 103 | ] 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /examples/scripts.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Script and Stack Class 6 | # 7 | # © 2021 - 2024 June - 1200 Web Development 8 | # 9 | 10 | from bitcoinlib.scripts import * 11 | from bitcoinlib.transactions import Transaction 12 | 13 | # 14 | # Stack Class Examples 15 | # 16 | # The Script object uses script language to perform operation on the Stack object. 17 | # 18 | 19 | st = Stack.from_ints([2, 3]) 20 | print("\nStack st %s" % st) 21 | st.op_add() 22 | print("Stack after running op_add %s" % st) 23 | 24 | st = Stack([b'\x99']) 25 | print("\nStack st %s" % st) 26 | st.op_dup() 27 | print("Stack after running op_dup %s" % st) 28 | 29 | st = Stack([b'The quick brown fox jumps over the lazy dog']) 30 | print("\nStack st %s" % st) 31 | st.op_ripemd160() 32 | print("Stack in hex after running op_ripemd160 %s" % st) 33 | print(st[0].hex()) 34 | 35 | st = Stack.from_ints([8, 2, 7]) 36 | print("\nStack st %s" % st) 37 | st.op_within() 38 | print("Stack in hex after running op_within. Checks if last item is between previous 2 numbers, returns 1 if True: %s" % 39 | st) 40 | 41 | txid = '0d12fdc4aac9eaaab9730999e0ce84c3bd5bb38dfd1f4c90c613ee177987429c' 42 | key = 'b2da575054fb5daba0efde613b0b8e37159b8110e4be50f73cbe6479f6038f5b' 43 | sig = '70b55404702ffa86ecfa4e88e0f354004a0965a5eea5fbbd297436001ae920df5da0917d7bd645c2a09671894375e3d3533138e8de09bc89cb251cbfae4cc523' 44 | st = Stack([bytes.fromhex(sig), bytes.fromhex(key)]) 45 | print("\nSignature verified: %s" % st.op_checksigverify(bytes.fromhex(txid))) 46 | 47 | 48 | # For more Stack examples see unittests in test_script. 49 | # For more information about op-methods: https://en.bitcoin.it/wiki/Script 50 | 51 | 52 | # 53 | # Script Class Examples 54 | # 55 | # The Script object uses script language to perform operation on the Stack object. 56 | # 57 | 58 | sc = [op.op_8, op.op_16, op.op_dup, op.op_1] 59 | s = Script(sc) 60 | print("\nScript %s" % s) 61 | print("Evaluate: %s" % s.evaluate()) 62 | print("Stack after evaluation: %s" % s.stack) 63 | 64 | sc = [op.op_8, encode_num(512), op.op_16, op.op_2dup] 65 | s = Script(sc) 66 | print("\nScript %s" % s) 67 | print("Evaluate: %s" % s.evaluate()) 68 | print("Stack after evaluation: %s" % s.stack) 69 | 70 | script = '493046022100cf4d7571dd47a4d47f5cb767d54d6702530a3555726b27b6ac56117f5e7808fe0221008cbb42233bb04d7f28a' \ 71 | '715cf7c938e238afde90207e9d103dd9018e12cb7180e03' 72 | s = Script.parse(script) 73 | print("\nScript hex %s" % script) 74 | print("Parsed script: %s" % s) 75 | 76 | print("\nVerify input 0 of transaction c8ea60ae943d84a8620a4ce3d3e12813293cdc48f6811dbc1c30578dfd1b2717") 77 | traw = '010000000182406edfc43449e2f94097867316cbc631dfdf9dc57dcc125297b0b59d3a2eda240000008b483045022100e05371e4d640d351d62699573811d93858b057eb01852d6c0b45d21d0ee90bb102201dc0b5ae1fee4dc1e7787e5cbbba2021387f52a9368856386931d4f8d9bdd938014104c4b7a7f7bb2c899f4aeab75b41567c040ae79506d43ee72f650c95b6319e47402f0ba88d1c5a294d075885442679dc24882ea37c31e0dbc82cfd51ed185d7e94ffffffff02ab4b0000000000001976a914ee493bd17ae7fa7fdabe4adb2b861ad7a8b954ad88acc5a5e70b000000001976a9147ddb236e7877d5040e2a59e4be544c65934e573a88ac00000000' 78 | t = Transaction.parse_hex(traw) 79 | i = t.inputs[0] 80 | transaction_hash_input_0 = transaction_hash = t.signature_hash(i.index_n) 81 | s = Script.parse(i.unlocking_script + i.locking_script) 82 | print("Validation script input 0: %s" % s) 83 | print("Evaluate: %s" % s.evaluate(transaction_hash_input_0)) 84 | 85 | print("\nCreate redeemscript:") 86 | key1 = '5JruagvxNLXTnkksyLMfgFgf3CagJ3Ekxu5oGxpTm5mPfTAPez3' 87 | key2 = '5JX3qAwDEEaapvLXRfbXRMSiyRgRSW9WjgxeyJQWwBugbudCwsk' 88 | key3 = '5JjHVMwJdjPEPQhq34WMUhzLcEd4SD7HgZktEh8WHstWcCLRceV' 89 | keylist = [Key(k) for k in [key1, key2, key3]] 90 | print("Keys: %s" % keylist) 91 | redeemscript = Script(keys=keylist, sigs_required=2, script_types=['multisig']) 92 | print("Redeemscript hex: %s" % redeemscript.serialize().hex()) 93 | print("Redeemscript: %s" % redeemscript) 94 | 95 | 96 | # 97 | # Deserialize input and output transaction scripts 98 | # 99 | 100 | print("\n=== Determine Script Types ===") 101 | script = '76a914f0d34949650af161e7cb3f0325a1a8833075165088ac' 102 | s = Script.parse_hex(script) 103 | print("\np2pkh: %s" % s.script_types[0]) 104 | print(s) 105 | 106 | script = '473044022034519a85fb5299e180865dda936c5d53edabaaf6d15cd1740aac9878b76238e002207345fcb5a62deeb8d9d80e5b41' \ 107 | '2bd24d09151c2008b7fef10eb5f13e484d1e0d01210207c9ece04a9b5ef3ff441f3aad6bb63e323c05047a820ab45ebbe61385aa' \ 108 | '7446' 109 | s = Script.parse_hex(script) 110 | print("\nsig_pubkey: %s" % s.script_types[0]) 111 | print(s) 112 | 113 | script = '6a20985f23805edd2938e5bd9f744d36ccb8be643de00b369b901ae0b3fea911a1dd' 114 | s = Script.parse_hex(script) 115 | print("\nnulldata: %s" % s.script_types[0]) 116 | print(s) 117 | 118 | script = '5121032487c2a32f7c8d57d2a93906a6457afd00697925b0e6e145d89af6d3bca330162102308673d16987eaa010e540901cc6' \ 119 | 'fe3695e758c19f46ce604e174dac315e685a52ae' 120 | s = Script.parse_hex(script) 121 | print("\nmultisig: %s" % s.script_types[0]) 122 | print(s) 123 | 124 | script = b"\x00\x14\xdc'M\xf8\x110Ke\xbd\x95\x1fq\xa3\x81\x0e\xc1\x91\x0b\xd7\x96" 125 | s = Script.parse(script, is_locking=True) 126 | print("\np2wpkh: %s" % s.script_types[0]) 127 | print(s) 128 | 129 | script = b'\x00 X|\x82_z\xb2\xdcV!\x0f\x92q\x15\x85\xed\x0cj\x84\x930]~\xa7\xb2\xd4\xb3a\x1e\\\xda\x85*' 130 | s = Script.parse(script, is_locking=True) 131 | print("\np2wsh: %s" % s.script_types[0]) 132 | print(s) 133 | -------------------------------------------------------------------------------- /tests/test_db.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # Unit Tests for Database 5 | # © 2019 December - 1200 Web Development 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU Affero General Public License as 9 | # published by the Free Software Foundation, either version 3 of the 10 | # License, or (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU Affero General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU Affero General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | import unittest 22 | from bitcoinlib.db import * 23 | from bitcoinlib.db_cache import * 24 | from bitcoinlib.wallets import Wallet, WalletError, WalletTransaction 25 | from bitcoinlib.transactions import Input, Output 26 | from bitcoinlib.services.services import Service 27 | try: 28 | import mysql.connector 29 | import psycopg 30 | from psycopg import sql 31 | except ImportError as e: 32 | print("Could not import all modules. Error: %s" % e) 33 | 34 | 35 | DATABASE_NAME = 'bitcoinlib_tmp' 36 | DATABASE_CACHE_NAME = 'bitcoinlib_cache_tmp' 37 | 38 | def database_init(dbname=DATABASE_NAME): 39 | session.close_all_sessions() 40 | if os.getenv('UNITTEST_DATABASE') == 'postgresql': 41 | con = psycopg.connect(user='postgres', host='localhost', password='postgres', autocommit=True) 42 | cur = con.cursor() 43 | try: 44 | cur.execute(sql.SQL("DROP DATABASE IF EXISTS {}").format(sql.Identifier(dbname))) 45 | cur.execute(sql.SQL("CREATE DATABASE {}").format(sql.Identifier(dbname))) 46 | except Exception as e: 47 | print("Error exception %s" % str(e)) 48 | pass 49 | cur.close() 50 | con.close() 51 | return 'postgresql+psycopg://postgres:postgres@localhost:5432/' + dbname 52 | elif os.getenv('UNITTEST_DATABASE') == 'mysql': 53 | con = mysql.connector.connect(user='root', host='localhost', password='root') 54 | cur = con.cursor() 55 | cur.execute("DROP DATABASE IF EXISTS {}".format(dbname)) 56 | cur.execute("CREATE DATABASE {}".format(dbname)) 57 | con.commit() 58 | cur.close() 59 | con.close() 60 | return 'mysql://root:root@localhost:3306/' + dbname 61 | else: 62 | dburi = os.path.join(str(BCL_DATABASE_DIR), '%s.sqlite' % dbname) 63 | if os.path.isfile(dburi): 64 | try: 65 | os.remove(dburi) 66 | except PermissionError: 67 | db_obj = Db(dburi) 68 | db_obj.drop_db(True) 69 | db_obj.session.close() 70 | db_obj.engine.dispose() 71 | return dburi 72 | 73 | class TestDb(unittest.TestCase): 74 | 75 | @classmethod 76 | def setUpClass(cls): 77 | cls.database_uri = database_init(DATABASE_NAME) 78 | cls.database_cache_uri = database_init(DATABASE_CACHE_NAME) 79 | 80 | def test_database_create_drop(self): 81 | dbtmp = Db(self.database_uri) 82 | Wallet.create("tmpwallet", db_uri=self.database_uri) 83 | self.assertRaisesRegex(WalletError, "Wallet with name 'tmpwallet' already exists", 84 | Wallet.create, 'tmpwallet', db_uri=self.database_uri) 85 | dbtmp.drop_db(yes_i_am_sure=True) 86 | Wallet.create("tmpwallet", db_uri=self.database_uri) 87 | 88 | def test_database_cache_create_drop(self): 89 | if os.getenv('UNITTEST_DATABASE') == 'mysql': 90 | self.skipTest('MySQL does not allow indexing on LargeBinary fields, so caching is not possible') 91 | dbtmp = DbCache(self.database_cache_uri) 92 | srv = Service(cache_uri=self.database_cache_uri, exclude_providers=['bitgo']) 93 | t = srv.gettransaction('68104dbd6819375e7bdf96562f89290b41598df7b002089ecdd3c8d999025b13') 94 | if t: 95 | self.assertGreaterEqual(srv.results_cache_n, 0) 96 | srv.gettransaction('68104dbd6819375e7bdf96562f89290b41598df7b002089ecdd3c8d999025b13') 97 | self.assertGreaterEqual(srv.results_cache_n, 1) 98 | dbtmp.drop_db() 99 | self.assertRaisesRegex(Exception, "", srv.gettransaction, 100 | '68104dbd6819375e7bdf96562f89290b41598df7b002089ecdd3c8d999025b13') 101 | 102 | def test_database_transaction_integers(self): 103 | db = Db(self.database_uri) 104 | w = Wallet.create('StrangeTransactions', account_id=0x7fffffff, db_uri=db.db_uri) 105 | inp = Input('68104dbd6819375e7bdf96562f89290b41598df7b002089ecdd3c8d999025b13', 0x7fffffff, 106 | value=0xffffffff, index_n=0x7fffffff, sequence=0xffffffff) 107 | outp = Output(0xffffffff, '37jKPSmbEGwgfacCr2nayn1wTaqMAbA94Z', output_n=0xffffffff) 108 | wt = WalletTransaction(w, 0x7fffffff, locktime=0xffffffff, fee=0xffffffff, confirmations=0x7fffffff, 109 | input_total= 2100000000001000, block_height=0x7fffffff, version=0x7fffffff, 110 | output_total=2100000000000000, size=0x07fffffff, inputs=[inp], outputs=[outp]) 111 | self.assertTrue(wt.store()) 112 | 113 | 114 | if __name__ == '__main__': 115 | unittest.main() 116 | -------------------------------------------------------------------------------- /bitcoinlib/services/baseclient.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # Base Client 5 | # © 2016 - 2024 May - 1200 Web Development 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU Affero General Public License as 9 | # published by the Free Software Foundation, either version 3 of the 10 | # License, or (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU Affero General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU Affero General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | import requests 22 | import urllib3 23 | from urllib.parse import urlencode 24 | import json 25 | from bitcoinlib.main import * 26 | from bitcoinlib.networks import Network 27 | from bitcoinlib.keys import Address 28 | 29 | _logger = logging.getLogger(__name__) 30 | 31 | # Disable warnings about insecure requests, as we only connect to familiar sources and local nodes 32 | urllib3.disable_warnings() 33 | 34 | 35 | class ClientError(Exception): 36 | def __init__(self, msg=''): 37 | self.msg = msg 38 | _logger.info(msg) 39 | 40 | def __str__(self): 41 | return self.msg 42 | 43 | 44 | class BaseClient(object): 45 | 46 | def __init__(self, network, provider, base_url, denominator, api_key='', provider_coin_id='', 47 | network_overrides=None, timeout=TIMEOUT_REQUESTS, latest_block=None, strict=True, wallet_name=''): 48 | try: 49 | self.network = network 50 | if not isinstance(network, Network): 51 | self.network = Network(network) 52 | self.provider = provider 53 | 54 | self.base_url = base_url 55 | self.resp = None 56 | self.units = denominator 57 | self.api_key = api_key 58 | self.provider_coin_id = provider_coin_id 59 | self.network_overrides = {} 60 | self.timeout = timeout 61 | self.latest_block = latest_block 62 | if network_overrides is not None: 63 | self.network_overrides = network_overrides 64 | self.strict = strict 65 | self.wallet_name = wallet_name 66 | except Exception: 67 | raise ClientError("This Network is not supported by %s Client" % provider) 68 | 69 | def request(self, url_path, variables=None, method='get', secure=True, post_data='', header=None): 70 | url_vars = '' 71 | url = self.base_url + url_path 72 | if not url or not self.base_url: 73 | raise ClientError("No (complete) url provided: %s" % url) 74 | headers = { 75 | 'User-Agent': 'BitcoinLib/%s' % BITCOINLIB_VERSION, 76 | 'Accept': 'application/json', 77 | "Referrer": "https://www.github.com/1200wd/bitcoinlib", 78 | } 79 | if self.api_key: 80 | headers["Api-Key"] = self.api_key 81 | if header: 82 | headers.update(header) 83 | if method == 'get': 84 | if variables is None: 85 | variables = {} 86 | if variables: 87 | url_vars = '?' + urlencode(variables) 88 | url += url_vars 89 | log_url = url if '@' not in url else url.split('@')[1] 90 | _logger.info("Url get request %s" % log_url) 91 | self.resp = requests.get(url, timeout=self.timeout, verify=secure, headers=headers) 92 | elif method == 'post': 93 | log_url = url if '@' not in url else url.split('@')[1] 94 | _logger.info("Url post request %s" % log_url) 95 | self.resp = requests.post(url, json=dict(variables), data=post_data, timeout=self.timeout, verify=secure, 96 | headers=headers) 97 | 98 | resp_text = self.resp.text 99 | if len(resp_text) > 1000: 100 | resp_text = self.resp.text[:970] + '... truncated, length %d' % len(resp_text) 101 | _logger.debug("Response [%d] %s" % (self.resp.status_code, resp_text)) 102 | log_url = url if '@' not in url else url.split('@')[1] 103 | if self.resp.status_code == 429: 104 | raise ClientError("Maximum number of requests reached for %s with url %s, response [%d] %s" % 105 | (self.provider, log_url, self.resp.status_code, resp_text)) 106 | elif not(self.resp.status_code == 200 or self.resp.status_code == 201): 107 | raise ClientError("Error connecting to %s on url %s, response [%d] %s" % 108 | (self.provider, log_url, self.resp.status_code, resp_text)) 109 | try: 110 | if not self.resp.apparent_encoding and not self.resp.encoding: 111 | return self.resp.content 112 | return json.loads(self.resp.text) 113 | except ValueError or json.decoder.JSONDecodeError: 114 | return self.resp.text 115 | 116 | def _address_convert(self, address): 117 | if not isinstance(address, Address): 118 | return Address.parse(address, network_overrides=self.network_overrides, network=self.network.name) 119 | 120 | def _addresslist_convert(self, addresslist): 121 | addresslistconv = [] 122 | for address in addresslist: 123 | addresslistconv.append(self._address_convert(address)) 124 | return addresslistconv 125 | -------------------------------------------------------------------------------- /examples/keys.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Key and HDKey Class 6 | # 7 | # © 2017 - 2024 June - 1200 Web Development 8 | # 9 | 10 | from pprint import pprint 11 | from bitcoinlib.keys import * 12 | 13 | 14 | # Key Class Examples 15 | 16 | print("\n=== Generate random key ===") 17 | k = Key() 18 | k.info() 19 | 20 | print("\n=== Import Public key ===") 21 | K = Key('025c0de3b9c8ab18dd04e3511243ec2952002dbfadc864b9628910169d9b9b00ec') 22 | K.info() 23 | 24 | print("\n=== Import Private key as decimal ===") 25 | pk = 45552833878247474734848656701264879218668934469350493760914973828870088122784 26 | k = Key(import_key=pk, network='testnet') 27 | k.info() 28 | 29 | print("\n=== Import Private key as byte ===") 30 | pk = b':\xbaAb\xc7%\x1c\x89\x12\x07\xb7G\x84\x05Q\xa7\x199\xb0\xde\x08\x1f\x85\xc4\xe4L\xf7\xc1>A\xda\xa6\x01' 31 | k = Key(pk) 32 | k.info() 33 | 34 | print("\n=== Import Private WIF Key ===") 35 | k = Key('L1odb1uUozbfK2NrsMyhJfvRsxGM2AxixgPL8vG9BUBnE6W1VyTX') 36 | print("Private key %s" % k.wif()) 37 | print("Private key hex %s " % k.private_hex) 38 | print("Compressed %s\n" % k.compressed) 39 | 40 | print("\n=== Import Private Testnet Key ===") 41 | k = Key('92Pg46rUhgTT7romnV7iGW6W1gbGdeezqdbJCzShkCsYNzyyNcc', network='testnet') 42 | k.info() 43 | 44 | print("\n=== Import Private Litecoin key (network derived from key) ===") 45 | pk = 'T43gB4F6k1Ly3YWbMuddq13xLb56hevUDP3RthKArr7FPHjQiXpp' 46 | k = Key(import_key=pk, network='litecoin') 47 | k.info() 48 | 49 | print("\n=== Import uncompressed Private Key and Encrypt with BIP38 ===") 50 | k = Key('5KN7MzqK5wt2TP1fQCYyHBtDrXdJuXbUzm4A9rKAteGu3Qi5CVR') 51 | print("Private key %s" % k.wif()) 52 | print("Encrypted pk %s " % k.encrypt('TestingOneTwoThree')) 53 | print("Is Compressed %s\n" % k.compressed) 54 | 55 | print("\n=== Import and Decrypt BIP38 Key ===") 56 | k = Key('6PRVWUbkzzsbcVac2qwfssoUJAN1Xhrg6bNk8J7Nzm5H7kxEbn2Nh2ZoGg', password='TestingOneTwoThree') 57 | print("Private key %s" % k.wif()) 58 | print("Is Compressed %s\n" % k.compressed) 59 | 60 | # 61 | # Hierarchical Deterministic Key Class and Child Key Derivation Examples 62 | # 63 | print("\n=== Generate random HD Key on testnet ===") 64 | hdk = HDKey(network='testnet') 65 | print("Random BIP32 HD Key on testnet %s" % hdk.wif()) 66 | 67 | print("\n=== Import HD Key from seed ===") 68 | k = HDKey.from_seed('000102030405060708090a0b0c0d0e0f') 69 | print("HD Key WIF for seed 000102030405060708090a0b0c0d0e0f: %s" % k.wif()) 70 | print("Key type is : %s" % k.key_type) 71 | 72 | print("\n=== Generate random Litecoin key ===") 73 | lk = HDKey(network='litecoin') 74 | lk.info() 75 | 76 | print("\n=== Import simple private key as HDKey ===") 77 | k = HDKey('L5fbTtqEKPK6zeuCBivnQ8FALMEq6ZApD7wkHZoMUsBWcktBev73') 78 | print("HD Key WIF for Private Key L5fbTtqEKPK6zeuCBivnQ8FALMEq6ZApD7wkHZoMUsBWcktBev73: %s" % k.wif()) 79 | print("Key type is : %s" % k.key_type) 80 | 81 | print("\n=== Derive path with Child Key derivation ===") 82 | print("Derive path path 'm/0H/1':") 83 | print(" Private Extended WIF: %s" % k.key_for_path('m/0H/1').wif()) 84 | print(" Public Extended WIF : %s\n" % k.key_for_path('m/0H/1').wif_public()) 85 | 86 | print("\n=== Test Child Key Derivation ===") 87 | print("Use the 2 different methods to derive child keys. One through derivation from public parent, " 88 | "and one thought private parent. They should be the same.") 89 | K = HDKey('xpub6ASuArnXKPbfEVRpCesNx4P939HDXENHkksgxsVG1yNp9958A33qYoPiTN9QrJmWFa2jNLdK84bWmyqTSPGtApP8P' 90 | '7nHUYwxHPhqmzUyeFG') 91 | k = HDKey('xprv9wTYmMFdV23N21MM6dLNavSQV7Sj7meSPXx6AV5eTdqqGLjycVjb115Ec5LgRAXscPZgy5G4jQ9csyyZLN3PZLxoM' 92 | '1h3BoPuEJzsgeypdKj') 93 | 94 | index = 1000 95 | pub_with_pubparent = K.child_public(index).address() 96 | pub_with_privparent = k.child_private(index).address() 97 | if pub_with_privparent != pub_with_pubparent: 98 | print("Error index %4d: pub-child %s, priv-child %s" % (index, pub_with_privparent, pub_with_pubparent)) 99 | else: 100 | print("Child Key Derivation for key %d worked!" % index) 101 | print("%s == %s" % (pub_with_pubparent, pub_with_privparent)) 102 | 103 | 104 | # 105 | # Addresses 106 | # 107 | print("\n=== Deserialize address ===") 108 | pprint(deserialize_address('1HsZBGm6nNGG1Moc3TL6S9DSGbnPbsSyW3')) 109 | 110 | print("\n=== Deserialize bech32 address ===") 111 | pprint(deserialize_address('bc1qtlktwxgx3xu3r7fnt04q06e4gflpvmm70qw66rjckzyc0n54elxqsgqlpy')) 112 | 113 | print("\n=== Create addreses from public key ===") 114 | pk = HDKey().public_hex 115 | print("Public key: %s" % pk) 116 | print(Address(pk).address) 117 | print(Address(pk, script_type='p2sh').address) 118 | print(Address(pk, encoding='bech32').address) 119 | print(Address(pk, script_type='p2sh', encoding='bech32').address) 120 | print(Address(pk, encoding='bech32', network='litecoin').address) 121 | 122 | 123 | # 124 | # Multisig and segwit WIF key import 125 | # 126 | 127 | print("\n=== Import Segwit p2wpkh WIF key ===") 128 | wif = 'zprvAWgYBBk7JR8GkLNSb2QvWhAjydfXoCkSBhvHichpYbqXHDYECcySV5dg1Bw2ybwfJmoLfU1NVzbiD95DVwP34nXPScCzUrLCa3c3WXtkNri' 129 | k = HDKey(wif) 130 | print("Witness type derived from wif %s is segwit (%s)" % (wif, k.witness_type)) 131 | print("Encoding derived from wif is bech32 (%s)" % k.encoding) 132 | print("Segwit bech32 encoded address is %s" % k.address()) 133 | 134 | 135 | wif = 'Ypub6bjiQGLXZ4hTYTQY8eTwC82WSegNDbRiEaYpZQCekjpWqcD7CDU3NnX9SZuzqEUvnTum7X3ixhdMNmpBDTCsGkq38h2kWaGrHXouh7QV1Wx' 136 | k = HDKey(wif) 137 | print("\nWitness type derived from wif %s is p2sh-segwit (%s)" % (wif, k.witness_type)) 138 | print("Encoding derived from wif is base58 (%s)" % k.encoding) 139 | print("Segwit P2SH base58 encoded address is %s" % k.address()) 140 | -------------------------------------------------------------------------------- /bitcoinlib/tools/wallet_multisig_2of3.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # Create a multisig 2-of-3 wallet with Mnemonic passphrase keys, so wallet contains 3 keys and 2 signatures are 6 | # needed to sign a transaction / send a payment. 7 | # 8 | # Transaction are created and signed with 1 signature on the online PC, on the other offline PC the transaction is 9 | # signed with a second private key. The third key is a stored on a paper in case one of the others keys is lost. 10 | # 11 | # © 2017 - 2019 December - 1200 Web Development 12 | # 13 | 14 | from __future__ import print_function 15 | 16 | from pprint import pprint 17 | from bitcoinlib.wallets import wallet_exists, Wallet 18 | from bitcoinlib.mnemonic import Mnemonic 19 | from bitcoinlib.keys import HDKey 20 | 21 | WALLET_NAME = "Multisig-2of3" 22 | NETWORK = 'testnet' 23 | KEY_STRENGTH = 128 # Remove this line to use the default 256 bit key strength 24 | SIGNATURES_REQUIRED = 2 25 | WITNESS_TYPE = 'segwit' # Witness type can be legacy, p2sh-segwit or segwit 26 | 27 | # from bitcoinlib.wallets import wallet_delete_if_exists 28 | # wallet_delete_if_exists(WALLET_NAME, force=True) 29 | 30 | if not wallet_exists(WALLET_NAME): 31 | # Define cosigners, format (name, key_type, [password], wallet) 32 | cosigners = [ 33 | ('Offline PC', 'bip32', 'password'), 34 | ('Online PC', 'bip32', ''), 35 | ('Paper backup', 'single', ''), 36 | ] 37 | 38 | print("We will generate 3 private keys, to sign and send a transaction 2 keys are needed:" 39 | "\n- With 1 private key a wallet on this Offline PC is created" 40 | "\n- Use private key 2 to create a wallet on the Online PC" 41 | "\n- Store key 3 on a Paper in a safe in case one of the PC's is not available anymore" 42 | ) 43 | key_lists = {} 44 | w_id = 0 45 | for cosigner in cosigners: 46 | print("\n") 47 | words = Mnemonic().generate(KEY_STRENGTH) 48 | password = '' 49 | if cosigner[2] == 'password': 50 | password = input("Please give password for cosigner '%s': " % cosigner[0]) 51 | seed = Mnemonic().to_seed(words, password) 52 | hdkey = HDKey.from_seed(seed, network=NETWORK, key_type=cosigner[1], witness_type=WITNESS_TYPE) 53 | if cosigner[1] == 'bip32': 54 | public_account = hdkey.public_master_multisig(witness_type=WITNESS_TYPE) 55 | else: 56 | public_account = hdkey 57 | print("Key for cosigner '%s' generated. Please store both passphrase and password carefully!" % cosigner[0]) 58 | print("Passphrase: %s" % words) 59 | print("Password: %s" % ('*' * len(password))) 60 | print("Share this public key below with other cosigner") 61 | print("Public key: %s" % public_account.wif_public()) 62 | 63 | for w in cosigners: 64 | if cosigner[0] == w[0]: 65 | addkey = hdkey 66 | else: 67 | addkey = public_account.public() 68 | if w[0] not in key_lists: 69 | key_lists[w[0]] = [] 70 | if addkey not in key_lists[w[0]]: 71 | key_lists[w[0]].append(addkey) 72 | 73 | offline_wallet = Wallet.create(WALLET_NAME, key_lists['Offline PC'], sigs_required=SIGNATURES_REQUIRED, 74 | witness_type=WITNESS_TYPE, network=NETWORK) 75 | offline_wallet.new_key() 76 | 77 | print("\n\nA multisig wallet has been created on this system") 78 | offline_wallet.info() 79 | 80 | print("\n---> Please create a wallet on your Online PC like this:") 81 | print("from bitcoinlib.wallets import Wallet") 82 | print("from bitcoinlib.keys import HDKey") 83 | print("") 84 | print("key_list = [") 85 | for key in key_lists['Online PC']: 86 | if key.key_type == 'single': 87 | print(" HDKey('%s', key_type='single', witness_type='%s')" % (key.wif_private(), WITNESS_TYPE)) 88 | else: 89 | print(" '%s'," % key.wif_private()) 90 | print("]") 91 | print("wlt = Wallet.create('%s', key_list, sigs_required=2, witness_type='%s', network='%s')" % 92 | (WALLET_NAME, WITNESS_TYPE, NETWORK)) 93 | print("wlt.get_key()") 94 | print("wlt.info()") 95 | else: 96 | from bitcoinlib.config.config import BITCOINLIB_VERSION, BCL_DATABASE_DIR 97 | online_wallet = Wallet(WALLET_NAME, db_uri=BCL_DATABASE_DIR + '/bitcoinlib.tmp.sqlite') 98 | online_wallet.utxos_update() 99 | online_wallet.info() 100 | utxos = online_wallet.utxos() 101 | if utxos: 102 | print("\nNew unspent outputs found!") 103 | print("Now a new transaction will be created to sweep this wallet and send bitcoins to a testnet faucet") 104 | send_to_address = 'n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi' 105 | t = online_wallet.sweep(send_to_address, min_confirms=0) 106 | print(t.raw_hex()) 107 | print("Now copy-and-paste the raw transaction hex to your Offline PC and sign it there with a second signature:") 108 | print("\nfrom bitcoinlib.wallets import Wallet") 109 | print("") 110 | print("wlt = Wallet('%s')" % WALLET_NAME) 111 | print("utxos = ", end='') 112 | pprint(utxos) 113 | print("") 114 | print("wlt.utxos_update(utxos=utxos)") 115 | print("t = wlt.transaction_import_raw('%s')" % t.raw_hex()) 116 | print("t.sign()") 117 | print("") 118 | print("# Push the following raw transaction to the blockchain network on any online PC:") 119 | print("print(t.raw_hex())") 120 | else: 121 | print("\nPlease send funds to %s, so we can create a transaction" % online_wallet.get_key().address) 122 | print("\nRestart this program when funds are send...") 123 | -------------------------------------------------------------------------------- /examples/wallets_multisig.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # 5 | # EXAMPLES - Creating and using Multisignature Wallets 6 | # 7 | # © 2017 - 2025 May - 1200 Web Development 8 | # 9 | 10 | import os 11 | from pprint import pprint 12 | from bitcoinlib.wallets import * 13 | 14 | test_databasefile = os.path.join(BCL_DATABASE_DIR, 'bitcoinlib.test.sqlite') 15 | test_database = 'sqlite:///' + test_databasefile 16 | if os.path.isfile(test_databasefile): 17 | os.remove(test_databasefile) 18 | 19 | # 20 | # Create a multi-signature wallet using Bitcoinlib testnet and then create a transaction 21 | # 22 | 23 | # Create 3 wallets with one private keys each, and 2 public keys corresponding with other wallets 24 | NETWORK = 'bitcoinlib_test' 25 | pk1 = HDKey(network=NETWORK) 26 | pk2 = HDKey(network=NETWORK) 27 | pk3 = HDKey(network=NETWORK) 28 | klist = [pk1, pk2.public_master_multisig(), pk3.public_master_multisig()] 29 | wl1 = Wallet.create('multisig_2of3_cosigner1', sigs_required=2, keys=klist, 30 | network=NETWORK, db_uri=test_database) 31 | klist = [pk1.public_master_multisig(), pk2, pk3.public_master_multisig()] 32 | wl2 = Wallet.create('multisig_2of3_cosigner2', sigs_required=2, keys=klist, 33 | network=NETWORK, db_uri=test_database) 34 | klist = [pk1.public_master_multisig(), pk2.public_master_multisig(), pk3] 35 | wl3 = Wallet.create('multisig_2of3_cosigner3', sigs_required=2, keys=klist, 36 | network=NETWORK, db_uri=test_database) 37 | 38 | # Generate a new key in each wallet, all these keys should be the same 39 | nk1 = wl1.new_key(cosigner_id=1) 40 | nk2 = wl2.new_key(cosigner_id=1) 41 | nk3 = wl3.new_key(cosigner_id=1) 42 | assert nk1.wif == nk2.wif == nk3.wif 43 | print("Created new multisig address: ", nk1.wif) 44 | 45 | # Create a transaction 46 | fee = 29348 47 | wl1.utxos_update() # On bitcoinlib testnet, this automatically creates an UTXO 48 | utxo = wl1.utxos()[0] 49 | output_arr = [('23Gd1mfrqgaYiPGkMm5n5UDRkCxruDAA8wo', utxo['value'] - fee)] 50 | input_arr = [(utxo['txid'], utxo['output_n'], utxo['key_id'], utxo['value'])] 51 | t = wl1.transaction_create(output_arr, input_arr, fee=fee) 52 | 53 | # Now sign transaction with first wallet, should not verify yet 54 | t.sign() 55 | pprint(t.as_dict()) 56 | print("Verified (False): ", t.verify()) 57 | 58 | # Import transaction (with first signature) in 3rd wallet and sign with wallet's private key 59 | wl3.utxos_update() 60 | t2 = wl3.transaction_import(t) 61 | t2.sign() 62 | print("Verified (True): ", t2.verify()) 63 | 64 | 65 | # 66 | # Create Multisig 2-of-2 testnet wallet, and sweep all UTXO's 67 | # 68 | 69 | # Create 2 cosigner multisig wallets 70 | NETWORK = 'bitcoinlib_test' 71 | pk1 = HDKey(network=NETWORK) 72 | pk2 = HDKey(network=NETWORK) 73 | wl1 = Wallet.create('multisig_2of2_cosigner1', sigs_required=2, 74 | keys=[pk1, pk2.public_master_multisig()], 75 | network=NETWORK, db_uri=test_database) 76 | wl2 = Wallet.create('multisig_2of2_cosigner2', sigs_required=2, 77 | keys=[pk1.public_master_multisig(), pk2], 78 | network=NETWORK, db_uri=test_database) 79 | nk1 = wl1.new_key() 80 | nk2 = wl2.new_key(cosigner_id=0) 81 | 82 | # Create a transaction 83 | wl1.utxos_update() 84 | utxos = wl1.utxos() 85 | if not utxos: 86 | print("Deposit testnet bitcoin to this address to create transaction: ", nk1.address) 87 | else: 88 | print("Utxo's found, now sweep wallet") 89 | res = wl1.sweep(wl1.new_key().address, min_confirms=0) 90 | assert res.txid 91 | wl2.utxos_update() 92 | wl2.new_key() 93 | t2 = wl2.transaction_import(res) 94 | t2.sign() 95 | print("Verified (True): ", t2.verify()) 96 | t2.send() 97 | print("Push transaction result: ", t2.status) 98 | 99 | # 100 | # Multisig wallet using single keys for cosigner wallet instead of BIP32 type key structures 101 | # 102 | 103 | NETWORK = 'bitcoinlib_test' 104 | priv1 = 'BC19UtECk2r9PVQYhYNpTmDcQWfMoieL4VJFz8DDQEMcVVpJvaFXvqAM21Z2tSX1s7wpbFRiKATdywhhttMZU1hnWgd2Kidacpb2KBYBUBkkqrDS' 105 | priv2 = 'BC19UtECk2r9PVQYhZJZ2KeQhbejZn2bGdTpxwm4c5yGSfFaphZCeKvQztKxV1YeaArPdFuzCGTvm52LuptuAptEMetENTucHaXgs3qga4h5gBXU' 106 | pk1 = HDKey(priv1, network=NETWORK) 107 | pk2 = HDKey(priv2, network=NETWORK, key_type='single') 108 | wl1 = Wallet.create('multisig_single_keys1', [pk1, pk2.public()], 109 | sigs_required=2, network=NETWORK, db_uri=test_database) 110 | wl2 = Wallet.create('multisig_single_keys2', [pk1.public_master_multisig(), pk2], 111 | sigs_required=2, network=NETWORK, db_uri=test_database) 112 | 113 | # Create multisig keys and update UTXO's 114 | wl1.new_key(cosigner_id=0) 115 | wl2.new_key(cosigner_id=0) 116 | wl1.utxos_update() 117 | wl2.utxos_update() 118 | 119 | # Create transaction and sign with both wallets, return address should be the same 120 | t = wl2.transaction_create([('23Gd1mfrqgaYiPGkMm5n5UDRkCxruDAA8wo', 5000000)]) 121 | t.sign() 122 | t2 = wl1.transaction_import(t) 123 | t2.sign() 124 | print("%s == %s: %s" % (t.outputs[1].address, t2.outputs[1].address, t.outputs[1].address == t2.outputs[1].address)) 125 | print("Verified (True): ", t2.verify()) 126 | 127 | 128 | # 129 | # Example of a multisig 2-of-3 segwit wallet 130 | # 131 | 132 | NETWORK = 'bitcoin' 133 | pk1 = HDKey(network=NETWORK, witness_type='segwit') # Private key for this wallet 134 | pk2 = HDKey(network=NETWORK, witness_type='segwit') # Wallet of cosigner 135 | pk3 = HDKey(network=NETWORK, witness_type='segwit', key_type='single') # Backup key on paper 136 | 137 | w = Wallet.create('Segwit-multisig-2-of-3-wallet', [pk1, pk2.public_master_multisig(), pk3.public()], 138 | sigs_required=2, network=NETWORK, db_uri=test_database) 139 | 140 | w.info() 141 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Bitcoinlib documentation build configuration file 5 | # 6 | # 7 | 8 | import os 9 | import sys 10 | sys.path.insert(0, os.path.abspath('.')) 11 | sys.path.insert(0, os.path.abspath('../')) 12 | 13 | # -- General configuration ------------------------------------------------ 14 | 15 | # If your documentation needs a minimal Sphinx version, state it here. 16 | # 17 | # needs_sphinx = '1.7.0' 18 | 19 | # Add any Sphinx extension module names here, as strings. They can be 20 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 21 | # ones. 22 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode'] 23 | 24 | # Add any paths that contain templates here, relative to this directory. 25 | templates_path = ['_templates'] 26 | 27 | # The suffix(es) of source filenames. 28 | # You can specify multiple suffix as a list of string: 29 | # 30 | # source_suffix = ['.rst', '.md'] 31 | source_suffix = '.rst' 32 | 33 | # The master toctree document. 34 | master_doc = 'index' 35 | 36 | # General information about the project. 37 | project = 'Bitcoinlib' 38 | copyright = '2017-2025, mccwdev' 39 | author = 'Cryp Toon (mccwdev)' 40 | 41 | # The version info for the project you're documenting, acts as replacement for 42 | # |version| and |release|, also used in various other places throughout the 43 | # built documents. 44 | # 45 | # The short X.Y version. 46 | version = '0.7' 47 | # The full version, including alpha/beta/rc tags. 48 | release = '0.7.6' 49 | 50 | # The language for content autogenerated by Sphinx. Refer to documentation 51 | # for a list of supported languages. 52 | # 53 | # This is also used if you do content translation via gettext catalogs. 54 | # Usually you set "language" from the command line for these cases. 55 | language = 'en' 56 | 57 | # List of patterns, relative to source directory, that match files and 58 | # directories to ignore when looking for source files. 59 | # This patterns also effect to html_static_path and html_extra_path 60 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 61 | 62 | # The name of the Pygments (syntax highlighting) style to use. 63 | pygments_style = 'sphinx' 64 | 65 | # If true, `todo` and `todoList` produce output, else they produce nothing. 66 | todo_include_todos = False 67 | 68 | 69 | # -- Options for HTML output ---------------------------------------------- 70 | 71 | # The theme to use for HTML and HTML Help pages. See the documentation for 72 | # a list of builtin themes. 73 | # 74 | html_theme = 'sphinx_rtd_theme' 75 | 76 | # Theme options are theme-specific and customize the look and feel of a theme 77 | # further. For a list of options available for each theme, see the# documentation. 78 | # 79 | # html_theme_options = {} 80 | 81 | # Add any paths that contain custom static files (such as style sheets) here, 82 | # relative to this directory. They are copied after the builtin static files, 83 | # so a file named "default.css" will overwrite the builtin "default.css". 84 | html_static_path = ['_static'] 85 | 86 | 87 | # -- Options for HTMLHelp output ------------------------------------------ 88 | 89 | # Output file base name for HTML help builder. 90 | htmlhelp_basename = 'Bitcoinlibdoc' 91 | 92 | 93 | # -- Options for LaTeX output --------------------------------------------- 94 | 95 | latex_elements = { 96 | # The paper size ('letterpaper' or 'a4paper'). 97 | # 98 | # 'papersize': 'letterpaper', 99 | 100 | # The font size ('10pt', '11pt' or '12pt'). 101 | # 102 | # 'pointsize': '10pt', 103 | 104 | # Additional stuff for the LaTeX preamble. 105 | # 106 | # 'preamble': '', 107 | 108 | # Latex figure (float) alignment 109 | # 110 | # 'figure_align': 'htbp', 111 | } 112 | 113 | # Grouping the document tree into LaTeX files. List of tuples 114 | # (source start file, target name, title, 115 | # author, documentclass [howto, manual, or own class]). 116 | latex_documents = [ 117 | (master_doc, 'Bitcoinlib.tex', 'Bitcoinlib Documentation', 118 | 'Cryp Toon (mccwdev)', 'manual'), 119 | ] 120 | 121 | 122 | # -- Options for manual page output --------------------------------------- 123 | 124 | # One entry per manual page. List of tuples 125 | # (source start file, name, description, authors, manual section). 126 | man_pages = [ 127 | (master_doc, 'bitcoinlib', 'Bitcoinlib Documentation', 128 | [author], 1) 129 | ] 130 | 131 | 132 | # -- Options for Texinfo output ------------------------------------------- 133 | 134 | # Grouping the document tree into Texinfo files. List of tuples 135 | # (source start file, target name, title, author, 136 | # dir menu entry, description, category) 137 | texinfo_documents = [ 138 | (master_doc, 'Bitcoinlib', 'Bitcoinlib Documentation', 139 | author, 'Bitcoinlib', 'One line description of project.', 140 | 'Miscellaneous'), 141 | ] 142 | 143 | 144 | def run_apidoc(_): 145 | from sphinx.ext.apidoc import main 146 | import os 147 | import sys 148 | import shutil 149 | sys.path.append(os.path.join(os.path.dirname(__file__), '..')) 150 | cur_dir = os.path.abspath(os.path.dirname(__file__)) 151 | module = '../bitcoinlib/' 152 | output_path = os.path.join(cur_dir, 'source') 153 | main(['-o', output_path, module, '--force', '--separate']) 154 | 155 | static_dir = os.path.join(cur_dir, '_static') 156 | static_dir_files = os.listdir(static_dir) 157 | dest_dir = os.path.join(output_path, '_static/') 158 | if not os.path.exists(dest_dir): 159 | os.makedirs(dest_dir) 160 | for filename in static_dir_files: 161 | full_filename = os.path.join(static_dir, filename) 162 | dest_filename = os.path.join(dest_dir, filename) 163 | shutil.copy(full_filename, dest_filename) 164 | 165 | 166 | def setup(app): 167 | app.connect('builder-inited', run_apidoc) 168 | 169 | 170 | autoclass_content = 'both' 171 | autodoc_mock_imports = ["bitcoinlib.tools"] 172 | -------------------------------------------------------------------------------- /tests/test_mnemonic.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BitcoinLib - Python Cryptocurrency Library 4 | # Unit Tests for Key, Encoding and Mnemonic Class 5 | # © 2016 - 2019 December - 1200 Web Development 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU Affero General Public License as 9 | # published by the Free Software Foundation, either version 3 of the 10 | # License, or (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU Affero General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU Affero General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | import os 22 | import unittest 23 | import json 24 | 25 | from bitcoinlib.keys import HDKey 26 | from bitcoinlib.encoding import change_base 27 | from bitcoinlib.mnemonic import Mnemonic 28 | 29 | 30 | class TestMnemonics(unittest.TestCase): 31 | 32 | def _check_list(self, language, vectors): 33 | mnemo = Mnemonic(language) 34 | for v in vectors: 35 | if v[0]: 36 | phrase = mnemo.to_mnemonic(v[0], check_on_curve=False) 37 | else: 38 | phrase = v[1] 39 | seed = change_base(mnemo.to_seed(phrase, v[4], validate=False), 256, 16) 40 | # print("Test %s => %s" % (v[0], phrase)) 41 | self.assertEqual(v[1], phrase) 42 | self.assertEqual(v[2], seed) 43 | k = HDKey.from_seed(seed, witness_type='legacy') 44 | self.assertEqual(k.wif(is_private=True), v[3]) 45 | 46 | # From Copyright (c) 2013 Pavol Rusnak 47 | def test_vectors(self): 48 | workdir = os.path.dirname(__file__) 49 | with open('%s/%s' % (workdir, 'mnemonics_tests.json')) as f: 50 | vectors = json.load(f) 51 | for lang in vectors.keys(): 52 | self._check_list(lang, vectors[lang]) 53 | 54 | def test_mnemonic_generate(self): 55 | phrase = Mnemonic(language='dutch').generate() 56 | self.assertEqual(len(phrase.split(' ')), 12) 57 | self.assertEqual(Mnemonic.detect_language(phrase), 'dutch') 58 | 59 | def test_mnemonic_generate_portuguese(self): 60 | phrase = Mnemonic(language='portuguese').generate() 61 | self.assertEqual(len(phrase.split(' ')), 12) 62 | self.assertEqual(Mnemonic.detect_language(phrase), 'portuguese') 63 | 64 | def test_mnemonic_generate_error(self): 65 | self.assertRaisesRegex(ValueError, 'Strength should be divisible by 32', Mnemonic().generate, 11) 66 | 67 | def test_mnemonic_to_entropy(self): 68 | phrase = 'usage grid neither voice worry armor sudden core excuse keen stand pudding' 69 | self.assertEqual(Mnemonic().to_entropy(phrase), b'\xef\x8c\xceP\xfa\xbf\xdc\x17\xf6!\x82O\x0f7RV') 70 | 71 | def test_mnemonic_to_mnemonic(self): 72 | self.assertEqual(Mnemonic().to_mnemonic('28acfc94465fd2f6774759d6897ec122'), 73 | 'chunk gun celery million wood kite tackle twenty story episode raccoon dutch') 74 | self.assertEqual(Mnemonic().to_mnemonic('28acfc94465fd2f6774759d6897ec122', add_checksum=False), 75 | 'action filter venture match garlic nut oven modify output dwarf wild cattle') 76 | self.assertRaisesRegex(ValueError, "Integer value of data should be in secp256k1 domain between 1 and " 77 | "secp256k1_n-1", Mnemonic().to_mnemonic, 78 | '28acfc94465fd2f6774759d6897ec12228acfc94465fd2f6774759d6897ec12228acfc94465fd2f6774') 79 | 80 | def test_mnemonic_to_seed_invalid_checksum(self): 81 | phrase = "runway truly foil future recall scatter garage over floor clutch shy boat" 82 | self.assertRaisesRegex(ValueError, "Invalid checksum 0110 for entropy", Mnemonic().to_seed, phrase) 83 | 84 | def test_mnemonic_exceptions(self): 85 | self.assertRaisesRegex(ValueError, "Strength should be divisible by 32", Mnemonic().generate, 20) 86 | self.assertRaisesRegex(ValueError, "Data length in bits should be divisible by 32", Mnemonic().checksum, 87 | 'aabbccddeeff') 88 | self.assertRaisesRegex(Warning, "Unrecognised word", 89 | Mnemonic().sanitize_mnemonic, 90 | 'action filter venture match garlic nut oven modify output dwarf wild fiets') 91 | self.assertRaisesRegex(Warning, "Could not detect language", 92 | Mnemonic().detect_language, 93 | 'floep fliep') 94 | 95 | def test_mnemonic_wordlists(self): 96 | self.assertEqual(Mnemonic().word(2047), 'zoo') 97 | self.assertEqual(Mnemonic(language='spanish').word(2047), 'zurdo') 98 | self.assertEqual(Mnemonic(language='french').word(2047), 'zoologie') 99 | self.assertEqual(Mnemonic(language='italian').word(2047), 'zuppa') 100 | self.assertEqual(Mnemonic(language='japanese').word(2047), 'われる') 101 | self.assertEqual(Mnemonic(language='chinese_simplified').word(2047), '歇') 102 | self.assertEqual(Mnemonic(language='chinese_traditional').word(2047), '歇') 103 | self.assertEqual(Mnemonic(language='portuguese').word(2047), 'zumbido') 104 | self.assertEqual(len(Mnemonic().wordlist()), 2048) 105 | 106 | def test_mnemonic_small_entropy_bug(self): 107 | for i in range(50): 108 | m = Mnemonic().generate(256) 109 | e = Mnemonic().to_entropy(m) 110 | self.assertEqual(len(e), 32) 111 | 112 | 113 | if __name__ == '__main__': 114 | unittest.main() 115 | -------------------------------------------------------------------------------- /docs/_static/manuals.install.rst: -------------------------------------------------------------------------------- 1 | Install and setup BitcoinLib 2 | ============================ 3 | 4 | Installation 5 | ------------ 6 | 7 | Install with pip 8 | ~~~~~~~~~~~~~~~~ 9 | 10 | .. code-block:: none 11 | 12 | $ pip install bitcoinlib 13 | 14 | Package can be found at https://pypi.org/project/bitcoinlib/ 15 | 16 | Install from source 17 | ~~~~~~~~~~~~~~~~~~~ 18 | 19 | Required packages for Ubuntu, for other systems see below: 20 | 21 | ``apt install build-essential python3-dev libgmp3-dev pkg-config postgresql postgresql-contrib mariadb-server libpq-dev libmysqlclient-dev pkg-config`` 22 | 23 | Create a virtual environment for instance on linux with virtualenv: 24 | 25 | .. code-block:: bash 26 | 27 | $ virtualenv -p ~/.virtualenvs/bitcoinlib 28 | $ source ~/.virtualenvs/bitcoinlib/bin/activate 29 | 30 | Then clone the repository and install dependencies: 31 | 32 | .. code-block:: bash 33 | 34 | $ git clone https://github.com/1200wd/bitcoinlib.git 35 | $ cd bitcoinlib 36 | $ python -m pip install . 37 | 38 | You can test your local installation by running all unittests: 39 | 40 | .. code-block:: bash 41 | 42 | $ python -m unittest 43 | 44 | 45 | Package dependencies 46 | ~~~~~~~~~~~~~~~~~~~~ 47 | 48 | Required Python Packages, are automatically installed upon installing bitcoinlib: 49 | 50 | * fastecdsa (or ecdsa on Windows) 51 | * sqlalchemy 52 | * requests 53 | * numpy 54 | * pycryptodome 55 | 56 | 57 | Other requirements Linux 58 | ~~~~~~~~~~~~~~~~~~~~~~~~ 59 | 60 | On Debian, Ubuntu or their derivatives: 61 | 62 | ``apt install build-essential python3-dev libgmp3-dev pkg-config postgresql postgresql-contrib mariadb-server libpq-dev libmysqlclient-dev pkg-config`` 63 | 64 | On Fedora, CentOS or RHEL: 65 | 66 | ``dnf install python3-devel gmp-devel`` 67 | 68 | On Alpine Linux, lightweight Linux used for Docker images: 69 | 70 | ``apk add python3-dev gmp-dev py3-pip gcc musl-dev libpq-dev postgresql postgresql-contrib mariadb-dev mysql-client`` 71 | 72 | On Kali linux: 73 | 74 | ``apt install libgmp3-dev postgresql postgresql-contrib libpq-dev pkg-config default-libmysqlclient-dev default-mysql-server`` 75 | 76 | 77 | Development environment 78 | ~~~~~~~~~~~~~~~~~~~~~~~ 79 | 80 | Install database packages for MySQL and PostgreSQL 81 | 82 | ``apt install mysql-server postgresql postgresql-contrib libmysqlclient-dev pkg-config libpq-dev`` 83 | 84 | Check for the latest version of the PostgreSQL dev server: 85 | 86 | ``apt install postgresql-server-dev-`` 87 | 88 | From library root directory install the Python requirements 89 | 90 | ``python -m pip install .[dev]`` 91 | 92 | Then run the unittests to see if everything works 93 | 94 | ``python -m unittest`` 95 | 96 | 97 | 98 | Other requirements Windows 99 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 100 | 101 | This library requires a Microsoft Visual C++ Compiler. For python version 3.5+ you will need Visual C++ 14.0. 102 | Install Microsoft Visual Studio and include the "Microsoft Visual C++ Build Tools" which can be downloaded from 103 | https://visualstudio.microsoft.com/downloads. Also see https://wiki.python.org/moin/WindowsCompilers 104 | 105 | The fastecdsa library is not enabled at this moment in the windows install, the slower ecdsa library is installed. 106 | Installation of fastecdsa on Windows is possible but not easy, read https://github.com/AntonKueltz/fastecdsa/issues/11 107 | for steps you could take to install this library. 108 | 109 | When using Python on Windows it needs to be set to UTF-8 mode. You can do this by adding the PYTHONUTF8=1 to the 110 | environment variables or use the -X utf8 command line option. Please see 111 | https://docs.python.org/3/using/windows.html#win-utf8-mode for more information. 112 | 113 | 114 | Update Bitcoinlib 115 | ----------------- 116 | 117 | Before you update make sure to backup your database! Also backup your settings files in ./bitcoinlib/config if you 118 | have made any changes. 119 | 120 | If you installed the library with pip upgrade with 121 | 122 | .. code-block:: none 123 | 124 | $ pip install bitcoinlib --upgrade 125 | 126 | Otherwise pull the git repository. 127 | 128 | After an update it might be necessary to update the config files. The config files will be overwritten 129 | with new versions if you delete the .bitcoinlib/install.log file. 130 | 131 | .. code-block:: none 132 | 133 | $ rm .bitcoinlib/install.log 134 | 135 | 136 | Troubleshooting 137 | --------------- 138 | 139 | Please make sure you have the Python development and SSL development packages installed, see 'Other requirements' 140 | above. 141 | 142 | You can also use pycryptodome, pyscrypt or scrypt. pyscript is a pure Python scrypt password-based key 143 | derivation library. It works but it is slow when using BIP38 password protected keys. 144 | 145 | If you run into issues do not hesitate to contact us or file an issue at https://github.com/1200wd/bitcoinlib/issues 146 | 147 | 148 | Change data directory 149 | --------------------- 150 | 151 | The default location for Bitcoinlib settings and data is in the user's dot bitcoinlib directory. 152 | For Linux this would be ~/.bitcoinlib. 153 | You can change the location of data, settings and database by specifying the directory in the system 154 | environment 'BCL_DATA_DIR' variable: 155 | 156 | .. code-block:: python 157 | 158 | os.environ['BCL_DATA_DIR'] = '/var/www/blocksmurfer/.bitcoinlib' 159 | # or a second directory in user home 160 | os.environ['BCL_DATA_DIR'] = '~/.bitcoinlib2' 161 | 162 | 163 | 164 | Service providers and local nodes 165 | --------------------------------- 166 | 167 | You can `Add another service Provider `_ to this library by updating settings 168 | and write a new service provider class. 169 | 170 | To increase reliability, speed and privacy or if you use this library in a production environment it 171 | is advised to run your own Bcoin or Bitcoin node. 172 | 173 | More setup information: 174 | 175 | * `Setup connection to Bcoin node `_ 176 | * `Setup connection to Bitcoin node `_ 177 | 178 | Some service providers require an API key to function or allow additional requests. 179 | You can add this key to the provider settings file in .bitcoinlib/providers.json 180 | --------------------------------------------------------------------------------