edgecase
Author: StJohn Piano
Published: 2020-06-21
Datafeed Article 148
This article has been digitally signed by Edgecase Datafeed.
This article has been digitally signed by its author.
626 words - 355 lines - 9 pages - 3 comments




BRIEF SUMMARY



This article contains the assets for Edgecase Bitcoin Storage Toolset (EBST) version 2. See the Downloadable Assets section.

Capabilities:
- Designed for offline use
- Address generation
- Creation of signed transactions with multiple inputs and multiple outputs
- Can send bitcoin to nonstandard P2SH addresses




CONTENTS



- Brief Summary
- Contents
- Summary
- Downloadable Assets
- Dependency Trees




SUMMARY



Let "EBST" = Edgecase Bitcoin Storage Toolset.

EBST version 1 consists of the set of assets listed in the article Recipe for storing bitcoin on paper using a Raspberry Pi.
- Capabilities: Designed for offline use, address generation, creation of signed transactions, can use a non-standard P2SH address as its destination.
- Limitations: Transactions must have exactly one input and one output.

EBST version 2 is an expansion of version 1 that handles multiple inputs and multiple outputs.

This expansion was developed in these articles:
- Creating a Bitcoin transaction with two outputs
- Creating a Bitcoin transaction with two inputs

In this article, I have gathered together the set of assets that comprise EBST version 2. I've made some additional changes (e.g. removing default values, updating import statements, changing comments).

I've also made the adjustment described in Bitcoin transaction test set, in the Notes section, in the part "Adjustment to transaction_2.py".

I have tested EBST version 2 against:
- the addresses in Bitcoin address test set
- the transactions in Bitcoin transaction test set
- the transactions in Bitcoin nonstandard transaction test set
All tests were successful.








DOWNLOADABLE ASSETS





Assets of this article:

List:

- convert_dice_rolls_to_hex_bytes_3.py
- create_nonstandard_transaction_3.py
- create_transaction_3.py
- generate_bitcoin_address_4.py
- nonstandard_bitcoin_functions_2.py
- nonstandard_transaction_3.py
- transaction_3.py


Asset: A script that converts dice roll results into bytes.
convert_dice_rolls_to_hex_bytes_3.py [paywalled]

Asset: A script that creates and signs a nonstandard Bitcoin transaction with one or more standard P2PKH inputs and one or more outputs. Outputs can be standard P2PKH outputs or nonstandard P2SH outputs.
create_nonstandard_transaction_3.py [paywalled]

Asset: A script that creates and signs a standard Bitcoin transaction with one or more inputs and one or more outputs.
create_transaction_3.py [paywalled]

Asset: A script that generates a standard Bitcoin address from a private key.
generate_bitcoin_address_4.py [paywalled]

Asset: A library of functions for handling nonstandard Bitcoin data types.
nonstandard_bitcoin_functions_2.py [paywalled]

Asset: A library of classes for nonstandard Bitcoin transactions.
nonstandard_transaction_3.py [paywalled]

Asset: A library of classes for standard Bitcoin transactions.
transaction_3.py [paywalled]




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.
bitcoin_functions_2.py [paywalled]

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

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

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








DEPENDENCY TREES





Address generation assets (standard addresses) dependency tree:

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





Transaction creation assets (standard transactions) dependency tree:

- create_transaction_3.py
-- bitcoin_functions_2.py
--- bjorn_edstrom_ripemd160.py
--- ecdsa-0.10.tar.gz
--- pypy_sha256.py
-- transaction_3.py
--- bitcoin_functions_2.py
[...]
--- ecdsa-0.10.tar.gz





Transaction creation assets (nonstandard transactions) dependency tree:

- create_nonstandard_transaction_3.py
-- bitcoin_functions_2.py
--- bjorn_edstrom_ripemd160.py
--- ecdsa-0.10.tar.gz
--- pypy_sha256.py
-- nonstandard_bitcoin_functions_2.py
--- bitcoin_functions_2.py
[...]
-- nonstandard_transaction_3.py
--- bitcoin_functions_2.py
[...]
--- ecdsa-0.10.tar.gz
--- nonstandard_bitcoin_functions_2.py
[...]
-- transaction_3.py
--- bitcoin_functions_2.py
[...]
--- ecdsa-0.10.tar.gz





Combined list of assets:

- bitcoin_functions_2.py
- bjorn_edstrom_ripemd160.py
- convert_dice_rolls_to_hex_bytes_3.py
- create_nonstandard_transaction_3.py
- create_transaction_3.py
- ecdsa-0.10.tar.gz
- generate_bitcoin_address_4.py
- nonstandard_bitcoin_functions_2.py
- nonstandard_transaction_3.py
- pypy_sha256.py
- transaction_3.py












Correction:

The asset create_nonstandard_transaction_3.py is missing a line containing:
{


Lines 57-58 are currently:
		},
		"txid": "",


Instead, they should be:
		},
		{
		"txid": "",


Note:

In the file
bitcoin_functions_2.py
, the functions
- validate_hex_length()
- validate_hex_length_domain()

both contain this line:
n = len(input) / 2 # number of hex bytes

which won't handle non-even numbers of hex bytes properly (it will round down).

However, the validate_hex() function, called first in both cases, uses this line:
if len(input) % 2 == 0:

to check the evenness, and this will catch an odd number of hex bytes and raise an error.

Problems in bitcoin_functions_2.py:




1) The function
validate_bitcoin_amount
doesn't reject inputs with too many decimal places.

Example: The value
0.002000010
is not rejected for having 9 decimal places.

This leads to a bug: The function
bitcoin_to_satoshi
produces an incorrect result if it receives an input with too many decimal places. This function is used in the Input and Output creation methods in transaction_3.py.

Example: The value
0.002000010
will be converted to 2000010 satoshi (instead of 200001 satoshi), and later back to 0.02000010 bitcoin i.e. it's been multiplied by 10.

This bug will not raise an error during operation. If an input amount is incorrect, the resulting transaction will be invalid. However, an incorrect output amount could redirect a large amount of bitcoin from the change address to an external address.

A solution:

	# Check whether the amount has too many decimal places.
	if '.' in amount:
		decimalPlaces = len(amount.split('.')[1])
		if decimalPlaces > 8:
			raise ValueError("Bitcoin amount ({a}) has {d} decimal places.".format(a=amount, d=decimalPlaces))


^ This can be added just after the "apart from the period, input can contain only digits." check in
validate_bitcoin_amount
.




2) The function
validate_bitcoin_amount
has error messages that don't correctly display Bitcoin amounts.

Example:
ERROR: The minimum input amount is 0.000000. Input: 0.000000001


The minimum input amount is rounded to 6 decimal places. It should be
0.00000001
.

A solution: Replace
%f
in the error messages with
%.8f
.




3) On reflection, the function
validate_bitcoin_private_key_hex
should only accept values that are exactly 32 bytes. A convenience option to not include leading zeros can be implemented at a higher level. The internal library code should only handle private keys of 32 bytes.

A solution: Replace the
validate_hex
and
validate_hex_length_domain
checks with a single
validate_hex_length(input, 32)
check.

This function is also used to validate the random values supplied to the
sign
method of the Transaction class in transaction_3.py. Changing it would cause this method to require 32-byte random values. This is ok. The internal library code should also only handle random values that are exactly 32 bytes.

Note: In transaction_3.py, the
create_from_txid
method in the Input class accepts a less-than-32-byte private key and pads it to 32 bytes. Later it confirms that the length is 32 bytes. These two steps can be replaced with a single use of the new version of
validate_bitcoin_private_key_hex
.