edgecase
Author: StJohn Piano
Published: 2020-11-05
Datafeed Article 189
This article has been digitally signed by Edgecase Datafeed.
This article has been digitally signed by its author.
1073 words - 591 lines - 15 pages




GOAL



Develop a tool for generating a Bitcoin address from a private key. This tool should have a good command-line interface and a test suite.




CONTENTS



- Goal
- Contents
- Summary
- Downloadable Assets
- How To Use The Tools
- Project Log




SUMMARY



I developed:
- a tool for deriving a Bitcoin address from a private key.
- an accompanying test suite.

The tool has a useful command-line interface.

Please see the Downloadable Assets section for the tool and the test suite, and the How To Use The Tools section for instructions and examples.










DOWNLOADABLE ASSETS





Assets of this article:

List:

- private_key_to_address.py
- test_private_key_to_address.py


Asset: A tool that derives a Bitcoin address from a private key. Python 2.7.12.
private_key_to_address.py [paywalled]

Asset: A test suite for the derivation tool. Python 2.7.12, Pytest 4.6.11.
test_private_key_to_address.py




Assets of other articles:

List:

- bitcoin_functions_2.py
- bjorn_edstrom_ripemd160.py
- ecdsa-0.10.tar.gz
- pypy_sha256.py


Asset: A library of functions for handling standard Bitcoin data types. Python 2.7.
bitcoin_functions_2.py [paywalled]

Asset: An implementation of RIPEMD-160, written by Björn Edström. Python 2.7.
bjorn_edstrom_ripemd160.py [paywalled]

Asset: A Python implementation of ECDSA cryptography, written by Peter Pearson. Python 2.7.
ecdsa-0.10.tar.gz [paywalled]

Asset: A Python implementation of SHA256. Author unknown. Python 2.7.
pypy_sha256.py [paywalled]




Dependency tree:
- test_private_key_to_address.py
-- private_key_to_address.py
--- bitcoin_functions_2.py
---- bjorn_edstrom_ripemd160.py
---- ecdsa-0.10.tar.gz
---- pypy_sha256.py











HOW TO USE THE TOOLS




Help:

python private_key_to_address.py --help




Main:

python private_key_to_address.py -f privateKey.txt


Note: By default, the tool looks for a file named 'privateKey.txt' in its local directory, so in the above command the filepath argument is optional.

python private_key_to_address.py




Test:

pytest -q test_private_key_to_address.py


Note: The test suite file needs to be in the same directory as the tool file.



Examples:

stjohn@judgement:work$ python private_key_to_address.py --privateKeyFilePath privateKey.txt

1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT


[same as above, but in shorthand]
stjohn@judgement:work$ python private_key_to_address.py -f privateKey.txt

1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT


stjohn@judgement:work$ cat privateKey.txt

089f7ffa2076aeb36d09a0d095296116cf6e3a93a1fcce0ca31e7628c43001d8


[default privateKey filepath is 'privateKey.txt'.]
stjohn@judgement:work$ python private_key_to_address.py

1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT


stjohn@judgement:work$ python private_key_to_address.py --privateKeyString 089f7ffa2076aeb36d09a0d095296116cf6e3a93a1fcce0ca31e7628c43001d8

1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT


stjohn@judgement:work$ python private_key_to_address.py -f privateKey.txt --debug

INFO [92: deriveBitcoinAddress] Private key: 089f7ffa2076aeb36d09a0d095296116cf6e3a93a1fcce0ca31e7628c43001d8
INFO [94: deriveBitcoinAddress] Private key (WIF): 5Ht5r13wvzsUJKPoqCasYH1hpBCjKMgQkXE4e538CcQLHum2Uyz
INFO [96: deriveBitcoinAddress] Bitcoin address: 1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT
1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT


stjohn@judgement:work$ pytest -q test_private_key_to_address.py

........................ [100%]
24 passed in 2.94 seconds





















PROJECT LOG




The Edgecase Bitcoin Storage Toolset version 2 already contains a tool for generating a bitcoin address from a private key, called:
generate_bitcoin_address_4.py

I'll reuse the underlying code from it and its dependencies.

The previous project Generating entropy from dice #2 contains an good command-line tool and test suite. I'll use the same approach.

Work machine:
- Name: Judgement
- Windows 10
- Windows Subsystem for Linux (WSL): Ubuntu 16.04
- I'm working in the WSL Ubuntu terminal.

My Python version:
stjohn@judgement:work$ python --version

Python 2.7.12


My Pytest version:
stjohn@judgement:work$ pytest --version

This is pytest version 4.6.11, imported from /home/stjohn/.local/lib/python2.7/site-packages/pytest.pyc


Create a project directory, called:
"generating_a_standard_bitcoin_address_2"

Browse to the linked EBSTv2 article and download the various assets for the address generation tool.

Dependency tree:
- generate_bitcoin_address_4.py
-- bitcoin_functions_2.py
--- bjorn_edstrom_ripemd160.py
--- ecdsa-0.10.tar.gz
--- pypy_sha256.py


Create a new directory named "work" in the project directory.


Unpack the zipped tape archive file ecdsa-0.10.tar.gz, by opening a terminal, changing directory to the work directory, and running the following command:
tar -zxvf ecdsa-0.10.tar.gz


This unpacking produced a new directory named "ecdsa-0.10" in the work directory. The directory "ecdsa-0.10" contains a directory named "ecdsa". Copy the "ecdsa" directory into the work directory, using the following command:
cp -r ecdsa-0.10/ecdsa .


Delete ecdsa-0.10 and ecdsa-0.10.tar.gz.




I'll use the data from the previous project Bitcoin address test set when writing tests.




[development work occurs here.]




I've written a tool for generating a bitcoin address from a private key, and an accompanying test suite:
- private_key_to_address.py
- test_private_key_to_address.py




Let's record some examples of using
private_key_to_address.py




stjohn@judgement:work$ python private_key_to_address.py --help

usage: private_key_to_address.py [-h] [-f PRIVATEKEYFILEPATH] [-p PRIVATEKEYSTRING] [-l {debug,info,warning,error}] [-d] Derive a Bitcoin address from a Bitcoin private key. optional arguments: -h, --help show this help message and exit -f PRIVATEKEYFILEPATH, --privateKeyFilePath PRIVATEKEYFILEPATH path to file containing the private key (default: 'privateKey.txt'). -p PRIVATEKEYSTRING, --privateKeyString PRIVATEKEYSTRING private key passed as a direct argument. This overrides --privateKeyFilePath. -l {debug,info,warning,error}, --logLevel {debug,info,warning,error} Choose logging level (default: 'error'). -d, --debug Sets logLevel to 'debug'. This overrides --logLevel. Supplementary Notes: - A standard Bitcoin address is an uncompressed single-signature Pay-To-Public-Key-Hash (P2PKH) address. - A Bitcoin private key is a sequence of 32 bytes. In hex, 1 byte is written as two characters. So, a private key written in hex is 64 characters. -- Example: 089f7ffa2076aeb36d09a0d095296116cf6e3a93a1fcce0ca31e7628c43001d8





stjohn@judgement:work$ cat privateKey.txt

089f7ffa2076aeb36d09a0d095296116cf6e3a93a1fcce0ca31e7628c43001d8


stjohn@judgement:work$ python private_key_to_address.py --privateKeyFilePath privateKey.txt

1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT


[same, but in shorthand]
stjohn@judgement:work$ python private_key_to_address.py -f privateKey.txt

1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT


[Note: By default, the tool looks for a file named 'privateKey.txt' in its local directory, so the filepath argument is optional.]
stjohn@judgement:work$ python private_key_to_address.py

1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT


stjohn@judgement:work$ python private_key_to_address.py --privateKeyString 089f7ffa2076aeb36d09a0d095296116cf6e3a93a1fcce0ca31e7628c43001d8

1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT


[same, but in shorthand]
stjohn@judgement:work$ python private_key_to_address.py -p 089f7ffa2076aeb36d09a
0d095296116cf6e3a93a1fcce0ca31e7628c43001d8

1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT


stjohn@judgement:work$ python private_key_to_address.py -f privateKey.txt --debug

INFO [92: deriveBitcoinAddress] Private key: 089f7ffa2076aeb36d09a0d095296116cf6e3a93a1fcce0ca31e7628c43001d8
INFO [94: deriveBitcoinAddress] Private key (WIF): 5Ht5r13wvzsUJKPoqCasYH1hpBCjKMgQkXE4e538CcQLHum2Uyz
INFO [96: deriveBitcoinAddress] Bitcoin address: 1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT
1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT


stjohn@judgement:work$ python private_key_to_address.py -f privateKey.txt --logLevel=info

- Private key: 089f7ffa2076aeb36d09a0d095296116cf6e3a93a1fcce0ca31e7628c43001d8
- Private key (WIF): 5Ht5r13wvzsUJKPoqCasYH1hpBCjKMgQkXE4e538CcQLHum2Uyz
- Bitcoin address: 1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT
1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT









And some examples of testing the tool.


stjohn@judgement:work$ pytest test_private_key_to_address.py

============================= test session starts ==============================
platform linux2 -- Python 2.7.12, pytest-4.6.11, py-1.9.0, pluggy-0.13.1
rootdir: /mnt/c/Users/User/Desktop/stuff/articles/generating_a_standard_bitcoin_address_2/work
collected 24 items

test_private_key_to_address.py ........................ [100%]

========================== 24 passed in 1.52 seconds ===========================




stjohn@judgement:work$ pytest test_private_key_to_address.py --quiet

........................ [100%]
24 passed in 1.44 seconds




stjohn@judgement:work$ pytest test_private_key_to_address.py::test_one --quiet

. [100%]
1 passed in 0.03 seconds




stjohn@judgement:work$ pytest -o log_cli=true --log-cli-level=DEBUG --log-format="%(levelname)s [%(lineno)s: %(funcName)s] %(message)s" test_private_key_to_address.py::test_one

============================= test session starts ==============================
platform linux2 -- Python 2.7.12, pytest-4.6.11, py-1.9.0, pluggy-0.13.1
rootdir: /mnt/c/Users/User/Desktop/stuff/articles/generating_a_standard_bitcoin_address_2/work
collected 1 item

test_private_key_to_address.py::test_one
-------------------------------- live log call ---------------------------------
INFO [92: deriveBitcoinAddress] Private key: 0000000000000000000000000000000000000000000000000000000000000001
INFO [94: deriveBitcoinAddress] Private key (WIF): 5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf
INFO [96: deriveBitcoinAddress] Bitcoin address: 1EHNa6Q4Jz2uvNExL497mE43ikXhwF6kZm
PASSED [100%]

=========================== 1 passed in 0.05 seconds ===========================




stjohn@judgement:work$ pytest -o log_cli=true --log-cli-level=DEBUG --log-format="%(levelname)s [%(lineno)s: %(funcName)s] %(message)s" test_private_key_to_address.py::test_pair_1

============================= test session starts ==============================
platform linux2 -- Python 2.7.12, pytest-4.6.11, py-1.9.0, pluggy-0.13.1
rootdir: /mnt/c/Users/User/Desktop/stuff/articles/generating_a_standard_bitcoin_address_2/work
collected 1 item

test_private_key_to_address.py::test_pair_1
-------------------------------- live log call ---------------------------------
INFO [92: deriveBitcoinAddress] Private key: 0000000000000000000000007468655f6c6962726172795f6f665f626162656c
INFO [94: deriveBitcoinAddress] Private key (WIF): 5HpHagT65TZzG1PH3CdWbVZG75U59A8FphH7EtKwbFCZf3p6bjg
INFO [96: deriveBitcoinAddress] Bitcoin address: 1CTumCMjzBfccCJBTkHoPQmAwEqU9Uj2sQ
PASSED [100%]

=========================== 1 passed in 0.11 seconds ===========================









Good. That's the end of this project.










[start of notes]



List of previous tools for deriving Bitcoin addresses from private keys, together with the projects in which they were published:


Tool 1: generate_bitcoin_address.py [paywalled]
Project: Generating a standard Bitcoin address


Tool 2: generate_bitcoin_address_2.py [paywalled]
Project: Verifying a signed deed of the GPG 1.4.10 source code


Tool 3: generate_bitcoin_address_3.py [paywalled]
Project: Creating and signing a standard raw Bitcoin transaction: Iteration #2


Tool 4: generate_bitcoin_address_4.py [paywalled]
Project: Edgecase Bitcoin Storage Toolset version 2


[end of notes]