<signed_datafeed_article>
<datafeed_article>
<datafeed_name>edgecase_datafeed</datafeed_name>
<datafeed_article_id>192</datafeed_article_id>
<date>2020-11-10
<note>This is the date at the time of creation of this datafeed article. A checkpoint article containing a hash of this datafeed article may be created on this date or at a later date.</note>
</date>
<previous_checkpoint>
<datafeed_article_id>144</datafeed_article_id>
<checkpoint_id>9</checkpoint_id>
<date>2020-05-28</date>
<transaction>
<blockchain_name>bitcoin</blockchain_name>
<transaction_id>b27618deae05910f529240cc6960aeb87f017b12d302327253ee893825ce2bd4</transaction_id>
<block_height>632100</block_height>
<source_address>1HtwyqFWNVDoSEVqZwjBRRAV2oEsi8aQXr</source_address>
<destination_address>13MfGs39pR5aEK4iKdoLjVYXKwi6Y3uyPq</destination_address>
</transaction>
</previous_checkpoint>
<signed_article>
<article>
<title>Validating_a_standard_Bitcoin_address</title>
<author_name>stjohn_piano</author_name>
<date>2020-11-10</date>
<signed_by_author>yes</signed_by_author>
<content>



<heading_lines>
GOAL
</heading_lines>


Develop a tool for validating a standard Bitcoin address. This tool should have a good command-line interface and a test suite.




<heading_lines>
CONTENTS
</heading_lines>


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




<heading_lines>
SUMMARY
</heading_lines>


I developed:
- a tool for validating 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.










<heading_lines>
DOWNLOADABLE ASSETS
</heading_lines>




<bold_lines>
Assets of this article:
</bold_lines>

List:

<datablock_lines>
- validate_address.py
- test_validate_address.py
</datablock_lines>

Asset: A tool that validates a Bitcoin address from a private key. Python 2.7.12.
<link>
<type>asset</type>
<filename>validate_address.py</filename>
<text>validate_address.py</text>
<sha256>0155782d74eb16202eda0c05067e0ec6c4cf839062e619c261627b15d621c7ee</sha256>
</link>

Asset: A test suite for the validation tool. Python 2.7.12, Pytest 4.6.11.
<link>
<type>asset</type>
<filename>test_validate_address.py</filename>
<text>test_validate_address.py</text>
<sha256>95d445cc5cb951ae9a4219212a5828d0122051c167a041bebd836c7ddb89e672</sha256>
</link>




<bold_lines>
Assets of other articles:
</bold_lines>

List:

<datablock_lines>
- bitcoin_functions_2.py
- bjorn_edstrom_ripemd160.py
- ecdsa-0.10.tar.gz
- pypy_sha256.py
</datablock_lines>

Asset: A library of functions for handling standard Bitcoin data types. Python 2.7. 
<link>
<type>asset_of_another_article</type>
<article_title>Creating_a_Bitcoin_transaction_with_two_outputs</article_title>
<datafeed>edgecase</datafeed>
<datafeed_article_id>85</datafeed_article_id>
<filename>bitcoin_functions_2.py</filename>
<text>bitcoin_functions_2.py</text>
<sha256>653f38ebab7f7c176ce7925364b5916c32b8e97ab5d4c00a66b990a0b6e0074c</sha256>
</link>

Asset: An implementation of RIPEMD-160, written by <latin-1>B_j_o:r_n_  E_d_s_t_r_o:m_</latin-1>. Python 2.7. 
<link>
<type>asset_of_another_article</type>
<article_title>Reading_and_verifying_a_standard_raw_bitcoin_transaction</article_title>
<datafeed>edgecase</datafeed>
<datafeed_article_id>51</datafeed_article_id>
<filename>bjorn_edstrom_ripemd160.py</filename>
<text>bjorn_edstrom_ripemd160.py</text>
<sha256>a5ca6eb289989861e30806ff7e39165622bd366a1c6cd5edd2dbd7dfc4877666</sha256>
</link>

Asset: A Python implementation of ECDSA cryptography, written by Peter Pearson. Python 2.7. 
<link>
<type>asset_of_another_article</type>
<article_title>Reading_and_verifying_a_standard_raw_bitcoin_transaction</article_title>
<datafeed>edgecase</datafeed>
<datafeed_article_id>51</datafeed_article_id>
<filename>ecdsa-0.10.tar.gz</filename>
<text>ecdsa-0.10.tar.gz</text>
<sha256>67dae9e1af2b0fd71bc9a378654f7dc89211c1c5aee71e160f8cfce1fa6d6980</sha256>
</link>

Asset: A Python implementation of SHA256. Author unknown. Python 2.7. 
<link>
<type>asset_of_another_article</type>
<article_title>Reading_and_verifying_a_standard_raw_bitcoin_transaction</article_title>
<datafeed>edgecase</datafeed>
<datafeed_article_id>51</datafeed_article_id>
<filename>pypy_sha256.py</filename>
<text>pypy_sha256.py</text>
<sha256>2bbd4a83b69625e2f7ece5201475af2803f0ea11f3815c83d8afab3773d8f47b</sha256>
</link>




Dependency tree:
<datablock_lines>
- test_validate_address.py
-- validate_address.py
--- bitcoin_functions_2.py
---- bjorn_edstrom_ripemd160.py
---- ecdsa-0.10.tar.gz
---- pypy_sha256.py
</datablock_lines>










<heading_lines>
HOW TO USE THE TOOLS
</heading_lines>



<bold_lines>
Help:
</bold_lines>

<code_lines>
python validate_address.py --help
</code_lines>



<bold_lines>
Main:
</bold_lines>

<code_lines>
python validate_address.py -f address.txt
</code_lines>

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

<code_lines>
python validate_address.py
</code_lines>



<bold_lines>
Test:
</bold_lines>

<code_lines>
pytest -q test_validate_address.py
</code_lines>

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



<bold_lines>
Examples:
</bold_lines>

<bash_lines>

<input_lines>
stjohn@judgement:work$ python validate_address.py --addressFilePath address.txt
</input_lines>


[same as above, but in shorthand]
<input_lines>
stjohn@judgement:work$ python validate_address.py -f address.txt
</input_lines>


<input_lines>
stjohn@judgement:work$ cat address.txt
</input_lines>
1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT


[default address filepath is 'address.txt']
<input_lines>
stjohn@judgement:work$ python validate_address.py
</input_lines>


<input_lines>
stjohn@judgement:work$ python validate_address.py --addressString 1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT
</input_lines>


<input_lines>
stjohn@judgement:work$ python validate_address.py --addressString 1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT --debug
</input_lines>
INFO [81: validateBitcoinAddress] Address: 1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT
DEBUG [89: validateBitcoinAddress] Number of leading '1' characters: 1
DEBUG [92: validateBitcoinAddress] base10 value inside address: 6230568508398901441201089632142185462656304983477985328510
INFO [95: validateBitcoinAddress] Address value in hex: fe1a2ba2103e4b61d8ad73a980cc47626ae6a0aa39adbd7e
INFO [100: validateBitcoinAddress] Checksum: 39adbd7e
DEBUG [102: validateBitcoinAddress] Address value in hex without checksum: fe1a2ba2103e4b61d8ad73a980cc47626ae6a0aa
DEBUG [105: validateBitcoinAddress] Address value with leading zero bytes added back on: 00fe1a2ba2103e4b61d8ad73a980cc47626ae6a0aa
DEBUG [108: validateBitcoinAddress] Recalculated checksum: 39adbd7e
DEBUG [115: validateBitcoinAddress] versionByte: 00
INFO [120: validateBitcoinAddress] publicKeyHash: fe1a2ba2103e4b61d8ad73a980cc47626ae6a0aa
INFO [124: validateBitcoinAddress] Address 1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT validated. No problems found.


[invalid address - last character has been changed from 'T' to 'S'.]
<input_lines>
stjohn@judgement:work$ python validate_address.py --addressString 1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbS
</input_lines>
<preserve_whitespace_lines>
Traceback (most recent call last):
  File "validate_address.py", line 172, in \<module\>
    main()
  File "validate_address.py", line 69, in main
    validateBitcoinAddress(args.addressString)
  File "validate_address.py", line 112, in validateBitcoinAddress
    raise ValueError(msg)
ValueError: Recalculated checksum (39adbd7d) does not match original checksum (39adbd7e).
</preserve_whitespace_lines>


<input_lines>
stjohn@judgement:work$ pytest -q test_validate_address.py
</input_lines>
....................                                                     [100%]
20 passed in 0.08 seconds

</bash_lines>




















<heading_lines>
PROJECT LOG
</heading_lines>


This project is going to be very similar to <link>
<type>article</type>
<article_title>Generating_a_standard_Bitcoin_address_#2</article_title>
<datafeed>edgecase</datafeed>
<datafeed_article_id>189</datafeed_article_id>
<text>Generating a standard Bitcoin address #2</text>
</link>. 

My working definition of a standard Bitcoin address:
- An uncompressed single-signature Pay-To-Public-Key-Hash (P2PKH) address.

Standard addresses start with the character '1'.

Example standard Bitcoin address (34 characters):
<datablock_lines>
1AGygbyEFYduWkkmZbbvirgS9kuBBMLJCP
</datablock_lines>

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:
<bash_lines>
<input_lines>
stjohn@judgement:work$ python --version
</input_lines>
Python 2.7.12
</bash_lines>

My Pytest version:
<bash_lines>
<input_lines>
stjohn@judgement:work$ pytest --version
</input_lines>
This is pytest version 4.6.11, imported from /home/stjohn/.local/lib/python2.7/site-packages/pytest.pyc
</bash_lines>

Create a project directory, called:
"validating_a_standard_bitcoin_address"

Relevant: In the previous project <link>
<type>article</type>
<article_title>Generating_a_standard_Bitcoin_address</article_title>
<datafeed>edgecase</datafeed>
<datafeed_article_id>54</datafeed_article_id>
<text>Generating a standard Bitcoin address</text>
</link>, in the Notes / Discoveries section, there is a part named "Algorithm for generating a Bitcoin address". 

Relevant: In the previous project <link>
<type>article</type>
<article_title>Creating_and_signing_a_standard_raw_Bitcoin_transaction</article_title>
<datafeed>edgecase</datafeed>
<datafeed_article_id>56</datafeed_article_id>
<text>Creating and signing a standard raw Bitcoin transaction</text>
</link>, in the Notes / Discoveries section, there is a part named "Algorithm for deriving the public key hash from a standard Bitcoin address".

Browse to <link>
<type>article</type>
<article_title>Edgecase_Bitcoin_Storage_Toolset_version_2</article_title>
<datafeed>edgecase</datafeed>
<datafeed_article_id>148</datafeed_article_id>
<text>Edgecase Bitcoin Storage Toolset version 2</text>
</link> and download the following assets:

Dependency tree:
<datablock_lines>
- bitcoin_functions_2.py
-- bjorn_edstrom_ripemd160.py
-- ecdsa-0.10.tar.gz
-- pypy_sha256.py
</datablock_lines>

bitcoin_functions_2.py contains various functions that I'll use in this project. 
- One in particular, base58check_to_hex, is very close to what I want for this project.

Create a new directory named "work" in the project directory. Move the downloaded assets into the work 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:
<code_lines>
tar -zxvf ecdsa-0.10.tar.gz
</code_lines>

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:
<code_lines>
cp -r ecdsa-0.10/ecdsa .
</code_lines>

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

I'll use the data from the previous project <link>
<type>article</type>
<article_title>Bitcoin_address_test_set</article_title>
<datafeed>edgecase</datafeed>
<datafeed_article_id>146</datafeed_article_id>
<text>Bitcoin address test set</text>
</link> when writing tests. 

List of things to validate:
- The address begins with a '1' character.
- The public key hash that it contains is 20 characters.
- The double-SHA256 checksum at the end is 4 characters.
- The checksum is correct.




[development work occurs here.]




I've written a tool for validating a bitcoin address, and an accompanying test suite:
- validate_address.py
- test_validate_address.py




Let's record some examples of using
validate_address.py




<bash_lines>

<input_lines>
stjohn@judgement:work$ python validate_address.py -h
</input_lines>
<preserve_whitespace_lines>
usage: validate_address.py [-h] [-f ADDRESSFILEPATH] [-s ADDRESSSTRING]
                           [-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 ADDRESSFILEPATH, --addressFilePath ADDRESSFILEPATH
                        path to file containing the address (default:
                        'address.txt').
  -s ADDRESSSTRING, --addressString ADDRESSSTRING
                        address passed as a direct argument. This overrides
                        --addressFilePath.
  -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.
- Standard addresses start with the character '1'.
- Example standard Bitcoin address (34 characters):
1AGygbyEFYduWkkmZbbvirgS9kuBBMLJCP
</preserve_whitespace_lines>


<input_lines>
stjohn@judgement:work$ python validate_address.py --addressString 1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT
</input_lines>


<input_lines>
stjohn@judgement:work$ python validate_address.py --addressString 1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT --debug
</input_lines>
INFO [81: validateBitcoinAddress] Address: 1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT
DEBUG [89: validateBitcoinAddress] Number of leading '1' characters: 1
DEBUG [92: validateBitcoinAddress] base10 value inside address: 6230568508398901441201089632142185462656304983477985328510
INFO [95: validateBitcoinAddress] Address value in hex: fe1a2ba2103e4b61d8ad73a980cc47626ae6a0aa39adbd7e
INFO [100: validateBitcoinAddress] Checksum: 39adbd7e
DEBUG [102: validateBitcoinAddress] Address value in hex without checksum: fe1a2ba2103e4b61d8ad73a980cc47626ae6a0aa
DEBUG [105: validateBitcoinAddress] Address value with leading zero bytes added back on: 00fe1a2ba2103e4b61d8ad73a980cc47626ae6a0aa
DEBUG [108: validateBitcoinAddress] Recalculated checksum: 39adbd7e
DEBUG [115: validateBitcoinAddress] versionByte: 00
INFO [120: validateBitcoinAddress] publicKeyHash: fe1a2ba2103e4b61d8ad73a980cc47626ae6a0aa
INFO [124: validateBitcoinAddress] Address 1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT validated. No problems found.


<input_lines>
stjohn@judgement:work$ cat address.txt
</input_lines>
1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT


<input_lines>
stjohn@judgement:work$ python validate_address.py --addressFilePath address.txt
</input_lines>


<input_lines>
stjohn@judgement:work$ python validate_address.py -f address.txt
</input_lines>


<input_lines>
stjohn@judgement:work$ python validate_address.py -f address.txt --logLevel=info
</input_lines>
- Address: 1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT
- Address value in hex: fe1a2ba2103e4b61d8ad73a980cc47626ae6a0aa39adbd7e
- Checksum: 39adbd7e
- publicKeyHash: fe1a2ba2103e4b61d8ad73a980cc47626ae6a0aa
- Address 1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT validated. No problems found.

</bash_lines>








And some examples of testing the tool.

<bash_lines>

<input_lines>
stjohn@judgement:work$ pytest test_validate_address.py
</input_lines>
============================= 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/validating_a_bitcoin_address/work
collected 20 items

test_validate_address.py ....................                            [100%]

========================== 20 passed in 0.13 seconds ===========================




<input_lines>
stjohn@judgement:work$ pytest test_validate_address.py -q
</input_lines>
....................                                                     [100%]
20 passed in 0.08 seconds




<input_lines>
stjohn@judgement:work$ pytest test_validate_address.py::test_valid_address
</input_lines>
============================= 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/validating_a_bitcoin_address/work
collected 1 item

test_validate_address.py .                                               [100%]

=========================== 1 passed in 0.04 seconds ===========================




<input_lines>
stjohn@judgement:work$ pytest -o log_cli=true --log-cli-level=DEBUG --log-format="%(levelname)s [%(lineno)s: %(funcName)s] %(message)s" test_validate_address.py::test_valid_address
</input_lines>
============================= 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/validating_a_bitcoin_address/work
collected 1 item

test_validate_address.py::test_valid_address
-------------------------------- live log call ---------------------------------
INFO [81: validateBitcoinAddress] Address: 1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT
DEBUG [89: validateBitcoinAddress] Number of leading '1' characters: 1
DEBUG [92: validateBitcoinAddress] base10 value inside address: 6230568508398901441201089632142185462656304983477985328510
INFO [95: validateBitcoinAddress] Address value in hex: fe1a2ba2103e4b61d8ad73a980cc47626ae6a0aa39adbd7e
INFO [100: validateBitcoinAddress] Checksum: 39adbd7e
DEBUG [102: validateBitcoinAddress] Address value in hex without checksum: fe1a2ba2103e4b61d8ad73a980cc47626ae6a0aa
DEBUG [105: validateBitcoinAddress] Address value with leading zero bytes added back on: 00fe1a2ba2103e4b61d8ad73a980cc47626ae6a0aa
DEBUG [108: validateBitcoinAddress] Recalculated checksum: 39adbd7e
DEBUG [115: validateBitcoinAddress] versionByte: 00
INFO [120: validateBitcoinAddress] publicKeyHash: fe1a2ba2103e4b61d8ad73a980cc47626ae6a0aa
INFO [124: validateBitcoinAddress] Address 1QAZz5b4MzqG64Loq3qsvAQiwHqweY7mbT validated. No problems found.
PASSED                                                                   [100%]

=========================== 1 passed in 0.04 seconds ===========================

</bash_lines>










Good. That's the end of this project.










</content>
</article>
<author_signature>
iQIcBAABCgAGBQJfqtu7AAoJEC8RP+HmG9MXFTEQALlgZO9MxUZ4f/vl2WlNq++5
db/LejBbe/jk3GkrvUFxeZ8m/9gnPCfcO2BxKuKzSJakc7WsDy4CdPPqeI1DCKCq
1DlTM+0uvpnwtgL419v1YPEMO0fBEV5q7ZExFbCWoEWRod04Dh0sHnVXx+T719/4
ZIaAsSGydp6BPKr+R1s7EErGER+15uUzM05z8K7OZ3pttq+YyrNOvhbwMb6d0Aed
UlfCQ1256z4at95ziQDYM9Fi6MALjR/zbCj9Pzj3XObxVmNljnSBk+aprJXY1W0g
Pt9AUMSVOgUdCslGH+ZZhTJmqqVp3ceLm9A5YWNuC2ceyscVoalmgBCaX8emity2
keR6KDwTEwF+KH/tFcZPS43o8vfcdO8hxEMhhefXvGQ9BASQ7EgwZwpu0D8H1GLc
INZLyCGKIL60062Yw+0/Mfr4AniWuDNYGfIVDSPMw3WEWcB0TydPIgxAgzCtLiyM
eTQNslj6aN9UdFPgyhqKIIGxQYoqNjt7IGuXW7QEyKb7LCOdJwHv0sIg9CN8ViAh
IH0eD2EwXjOxcLIl19DxLI9XozZX2ZzLPOiNFd1wJ4Vb4Ta7zx58bCzyZgR0087D
JEzbyh2uUDmwDIcTnUn0+9MK1DbbHYRau/IZ3X+F5Bti3swpQeQdPzgF2ugD4l0B
AaZjWW3ufWrS4rRgMjOT
=1lwP
</author_signature>
</signed_article>
</datafeed_article>
<datafeed_signature>
iQIcBAABCgAGBQJfqtxhAAoJECL1OzZgiBhwejMP/An1wdyM+N8Ay8Z8608UeHKD
uGEtSmYMlBa2zd504CuIKbiIgCUiao5bu3v8kwSy+Y8Pfc/+/dWEc3010rCJlBNz
2ocavO3BF+qplDmxc/1/RgRA0KjAdQyraZOe9wSHQeZOO2CBOq8h06vCrg4KoIux
3WbGdXCsorpV6k41SU1tJx+iwBfnDOXSJUy4uYPvYd79eIFYYGcjN13A8N+rrwzk
b4UoZtoj/P9qa1zPMroi3GW/ZybeszezA7h90PP7fo+3PYdu08ALAQsx5nxSzzA/
1HUvTSoAur7/VBglMJE+o7Y+ExpyrT0NqFkExDER5H6+6nMlGo863abtWWvYJ7fV
Nk/TWOm9wWU6JCmZxKu/SyTWgUiCVe+E4AQBa4RHnLbaduYTIv3HUnwbs2/27mF4
LTWSVZVHO3tBAQy3aeRYMyrEQWUv7Rw05dhTsyUFALSlYrKQ1f83IdJ2xHNu0ZnN
59wifJFnu2W1iL0FDAsmHSEsg7zeaA0rmkpG10n06fQQ3mdtlmjfaAJ0M5TsLvds
/bTanbWF8AT/rgB2o95TYJ9CvqIJuYVoCGlmnD93LQbJBL9Mx9jdbU8pLcYh865d
0WOwJBfoyZ29SSTEBqGkzWhwCUMq3UN3Nsqz1xyWLcxPZDqH44esOEzaSsfVMCBC
6VWoeynEZYR1tCWmxyxJ
=R3V8
</datafeed_signature>
</signed_datafeed_article>