Goal
Generate a Bitcoin address and use a small transaction to validate it.
Contents
- Goal
- Contents
- Brief Summary
- Summary
- Downloadable Assets
- Notes
- Thoughts
- Project Log
Brief Summary
During this project, I validated an address by transferring some bitcoin into and out of it. I could have then moved a larger amount of bitcoin into this address and be certain that I could later retrieve it.
I used an exchange service to transfer bitcoin into the address. I constructed and signed a raw Bitcoin transaction in order to transfer the bitcoin out of this address.
Please read the Summary section for a more thorough description of this project.
Summary
Original plan of action for this project:
- Generate an address: the "test address".
- Transfer some bitcoin to the test address. I'll call this "tx0". "tx" = transaction.
- Construct and sign a transaction that transfers bitcoin out of the test address to another "final" address. I'll call this "tx1".
- Broadcast the transaction. If the transaction is mined, then the address has been validated.
What I actually did during the project:
- Created a project directory:
using_a_transaction_to_validate_a_bitcoin_address
- Decided to use my current LocalBitcoins receiving address as the final address. It was a P2SH address (i.e. nonstandard), so creating transaction tx1 would require the create_nonstandard_transaction toolset.
- Within the project directory, created a work directory named "work".
- Obtained 5 dice and a tray with raised edges in which to roll them.
- Browsed to the previous article Generating entropy with dice and downloaded the following asset:
-- convert_dice_rolls_to_hex_bytes.py
- Browsed to the previous article Creating and signing a standard raw Bitcoin transaction: Iteration #2 and downloaded the following assets:
-- bjorn_edstrom_ripemd160.py
-- ecdsa-0.10.tar.gz
-- pypy_sha256.py
-- bitcoin_functions.py
-- generate_bitcoin_address_3.py
-- transaction.py
-- nonstandard_bitcoin_functions.py
-- nonstandard_transaction.py
-- create_nonstandard_transaction.py
- Moved these downloaded files to the work directory.
- Reading the relevant recipes in the articles containing these assets, I noted that I would need 64 bytes of entropy.
- In order to generate this entropy, I used the dice and tray acquired earlier and proceeded through the section Recipe For Generating Entropy Bytes Using Dice in the previous article Generating entropy with dice. This recipe is included as an excerpt at the relevant point in the Project Log section.
- I fixed some problems in the asset
convert_dice_rolls_to_hex_bytes.py,
creating the asset
convert_dice_rolls_to_hex_bytes_2.py. This new asset can be found in the Downloadable Assets section.
- I used Python to split the generated entropy into two sections of 32 bytes each. Details can be found in the "Using Python slicing to divide entropy bytes into pieces with particular lengths" part of the Notes section.
- I proceeded through Recipe 1 (Setup) in the section Recipes For Using Various Downloadable Assets in the previous article Creating and signing a standard raw Bitcoin transaction: Iteration #2. This recipe is included as an excerpt at the relevant point in the Project Log section.
- I generated the test address by proceeding through Recipe 2 (generate_bitcoin_address_3.py) in the section Recipes For Using Various Downloadable Assets in the previous article Creating and signing a standard raw Bitcoin transaction: Iteration #2. This recipe is included as an excerpt at the relevant point in the Project Log section.
- I calculated an amount of bitcoin to transfer to the test address that would cover expected costs, with extra so as to have room to manoeuvre.
- I used LocalBitcoins to transfer this amount of bitcoin to the test address. This was tx0.
- I constructed and signed a nonstandard transaction (tx1) that transferred the available bitcoin (minus a transaction fee) from the test address to the final address. I did this by proceeding through Recipe 4 (create_nonstandard_transaction.py), in the section Recipes For Using Various Downloadable Assets in the previous article Creating and signing a standard raw Bitcoin transaction: Iteration #2. This recipe is included as an excerpt at the relevant point in the Project Log section.
- I used a service to decode the transaction, indicating that the transaction was formatted correctly.
- I used a service to broadcast the transaction. Time taken for mining + 6-block-deep confirmation ~= 12.5 hours.
Overall results:
- The test address was successfully validated. I moved some bitcoin into it and successfully retrieved it. I could have then moved a larger amount of bitcoin into this address and be certain that I could later retrieve it.
Notes section:
The Notes section contains the following parts:
- Definitions and acryonyms
- Services used during this project
- Using Python slicing to divide entropy bytes into pieces with particular lengths
- Addresses generated and/or used in this project
- Transactions created during this project
- Dice rolling results
- Financial information for this project
Thoughts section:
The Thoughts section contains the following parts:
- Validating a Bitcoin address
- Does the hash in a Bitcoin address provide any protection?
- What is the chance of two people independently generating the same Bitcoin address?
Downloadable Assets
My work computer: Aineko, a 2008 Macbook running Mac OS X 10.6.8 Snow Leopard.
aineko:work stjohnpiano$ python --version
Python 2.7.13
The following asset was developed during this project and was run using Python 2.7.13 on Aineko.
Description: A script that converts dice roll results into bytes.
convert_dice_rolls_to_hex_bytes_2.py
This is a new version of convert_dice_rolls_to_hex_bytes.py, an asset of the previous article Generating entropy with dice.
Changes:
- Changed two hardcoded values to be the correct variable.
- Added a check that removes an extra hex character (half-byte) if it exists.
Note: During this project, this file was called
"convert_dice_rolls_to_hex_bytes.py".
Description: A set of 423 dice roll results produced during this project.
dice_rolls_2.txt
Note: During this project, this file was called "dice_rolls.txt".
Notes
Parts:
- Definitions and acryonyms
- Services used during this project
- Using Python slicing to divide entropy bytes into pieces with particular lengths
- Addresses generated and/or used in this project
- Transactions created during this project
- Dice rolling results
- Financial information for this project
Definitions and acryonyms
P2PKH = Pay-To-Public-Key-Hash
P2SH = Pay-To-Script-Hash
previous_output_hash = big-endian 32-byte double-SHA256 hash of a previous transaction.
txid = "transaction id" = little-endian form of the previous_output_hash
My working definition of a standard transaction:
- It has at least one input and at least one output.
- All input and output addresses are Pay-To-Public-Key-Hash (P2PKH).
- All input scriptSigs contain uncompressed public keys.
My working definition of a nonstandard transaction:
- It has at least one input and at least one output.
- At least one input or output address is not a standard Pay-To-Public-Key-Hash (P2PKH) address.
My working definition of a standard address:
- An uncompressed single-signature Pay-To-Public-Key-Hash (P2PKH) address.
Services used during this project
I used
localbitcoins.com
to store some bitcoin, transfer it to the test address, and receive it back again from the test address.
I used
bitcoinfees.earn.com
to choose fee rates for my transactions. It displays estimated delays (in blocks and in minutes) for ranges of fee rates (in satoshis / byte).
I used the search field at
live.blockcypher.com
to search for transactions by txid.
On the resulting transaction pages e.g.
live.blockcypher.com/btc/tx/bf7a16ef4bf8763b7bf7f02b03155c9a2f9b997d19e64a770bf3f7d3f24cc48b
I was able to click Advanced Details / API Call, which produced a raw text dump of the transaction, e.g.
api.blockcypher.com/v1/btc/main/txs/bf7a16ef4bf8763b7bf7f02b03155c9a2f9b997d19e64a770bf3f7d3f24cc48b?limit=50&includeHex=true
Note: I had to read through the transaction raw text dump in order to find the index of a particular unspent output. A service that allowed someone to look up the index of an unspent output would be useful.
I used
live.blockcypher.com/btc/decodetx
to decode a signed transaction. If the service can successfully decode a transaction, this indicates that the transaction is formatted correctly.
I used
live.blockcypher.com/btc/pushtx
to upload a signed transaction for broadcast to the Bitcoin network.
Using Python slicing to divide entropy bytes into pieces with particular lengths
64 bytes of entropy:
a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9
I want to divide this entropy into two pieces, each of 32 bytes. Note that 2 hex characters represent 1 byte, so 64 hex characters comprise 32 bytes.
aineko:~ stjohnpiano$ python
Python 2.7.13 (default, Dec 18 2016, 05:35:59)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> x = "a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9"
>>> len(x)/2 == 64
>>> True
>>> x[:64]
>>> 'a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121'
>>> x[64:]
>>> '258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9'
>>>
Entropy-bytes-1:
a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121
Entropy-bytes-2:
258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9
Addresses generated and/or used in this project
I generated one address, the "test address".
Test address:
- Private key (hex bytes):
a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121
- Bitcoin address:
1AGygbyEFYduWkkmZbbvirgS9kuBBMLJCP
Final address (My then-current LocalBitcoins receiving address):
3QpwGejV9Dyai72HDhXZVkYBWioyuLVjti
Transactions created during this project
I'll use:
- txid0 to mean the txid (transaction id) of tx0
- txid1 to mean the txid of tx1
tx0:
I transferred some bitcoin from my LocalBitcoins account to the test address. LocalBitcoins created a larger transaction, which included many other transfers, that executed this transfer.
txid0:
bf7a16ef4bf8763b7bf7f02b03155c9a2f9b997d19e64a770bf3f7d3f24cc48b
Link:
live.blockcypher.com/btc/tx/bf7a16ef4bf8763b7bf7f02b03155c9a2f9b997d19e64a770bf3f7d3f24cc48b
Additional details:
- block_hash:
00000000000000000001d5c84216c226186288fad3bedb057a25b393f44cae0d
- block_height:
547832
- confirmed:
2018-10-29T12:26:02Z
tx1:
tx1 transferred the available bitcoin (minus a transaction fee) from the test address to the final address.
txid1:
dfd0304f6bd018d8d97e63e387eeab0696b61d9632d50d28f1618daf434a7ba3
Link:
live.blockcypher.com/btc/tx/dfd0304f6bd018d8d97e63e387eeab0696b61d9632d50d28f1618daf434a7ba3
Additional details:
- block_hash:
000000000000000000204cb0026f5c7dd1ba847144528c79589493ff005351c3
- block_height:
547916
- size:
221 bytes
- fees:
221 satoshi
- confirmed:
2018-10-30T03:28:42Z
Actual fee rate for tx1 is 221 / 221 = 1 (satoshi / byte).
Time taken for mining + 6-block-deep confirmation ~= 12.5 hours.
The estimated delay for a transaction with this fee rate was:
1-8 blocks (0-110 minutes) for a fee rate of 1-2 satoshis / byte.
The signed transaction tx1:
01000000018bc44cf2d3f7f30b774ae6197d999b2f9a5c15032bf0f77b3b76f84bef167abf000000008a4730440220419ecae5ca010db8a2c7ec04cdebaa58a921b0102034db08d6456087c793494e022048cf345bf504905a23021b3dbd95d7a53d8786678505a3c5cd72e03ea259df230141049d8a4c3947fb596c2664218f279be9ba2c0421ca006888243877cdb0c0146202a2a2af299928cc634d58976db32023facf269c1fb7069d9af25c54c07e85eb8effffffff01b03301000000000017a914fdca5619962026e5d80a84d3e25f5cea931ce385870000000001000000
Dice rolling results
I found that listening to music made the dice rolling quite tolerable.
Using a set of 5 dice, and rolling them in a tray with raised edges, I generated 423 dice roll results. A file containing these results is included in the Downloadable Assets section.
I proceeded leisurely but steadily.
Time taken: 16:20
16*60 + 20 = 980 seconds
980 / 423 ~= 2.32 seconds per dice roll
expected entropy per dice roll ~= 1.3333 bits
(1.3333 bits of expected entropy per dice roll) / (2.32 seconds per dice roll)
= (1.3333 / 2.32) * (bits of expected entropy / second)
= 0.57 bits of expected entropy / second
With a rate of 0.57 bits of entropy per second, I would expect that to generate a Bitcoin address, which needs 32 bytes,
(32*8 bits) / (0.57 expected bits of entropy per second)
= 32*8 / 0.57
~= 449 seconds
~= 7.5 minutes
would be required.
Financial information for this project
LocalBitcoins:
- Deposit fee for incoming transactions: 0.00015 BTC
- Transaction fee for transferring bitcoin: 0.00005 BTC
Current USD / BTC price:
$6330.00
The LocalBitcoins transaction fee was deducted from my LocalBitcoins wallet balance, not from the transction amount.
I calculated an amount of bitcoin to transfer that would be large enough to pay the necessary costs, with extra so as to have room to manoeuvre.
Calculations:
- Using a high-end fee rate of 60 satoshi / byte, and an estimated transaction size of 223 bytes, the estimated high-end fee was:
(60 satoshi / byte) * (223 bytes) = 13380 satoshi
- Total cost in Bitcoin:
= (high-end) Bitcoin transaction fee + the LocalBitcoins deposit fee
= 13380 satoshi + 0.00015 BTC
= 0.00013380 BTC + 0.00015 BTC
= 0.00028380 BTC
- Total cost in US Dollars (USD):
(6330 USD / BTC) * 0.00028380 BTC ~= $1.80
- I decided to transfer $5-worth of bitcoin to the test address, so as to have room to manoeuvre.
- Final amount in Bitcoin:
$5 * (1 BTC / 6330 USD) ~= 0.00078989 BTC
Work:
- I transferred 0.00078989 bitcoin in tx0.
- LocalBitcoins charged a transaction fee of 0.00005 bitcoin for tx0. This was subtracted from my LocalBitcoins wallet balance, not from the amount that was actually sent.
- For tx1, I used a transaction fee of 221 satoshis (0.00000221 bitcoin).
- When it accepted and processed tx1, LocalBitcoins charged a deposit fee of 0.00015000 bitcoin.
- Total bitcoin spent:
= LocalBitcoins transaction fee for tx0 + tx1 transaction fee + LocalBitcoins deposit fee for tx1 = 0.00005000 + 0.00000221 + 0.00015000 = 0.00020221 bitcoin.
- Total bitcoin spent, in then-current US Dollar prices:
0.00020221 bitcoin * (6330 USD / BTC) ~= $1.28
Results:
- Total bitcoin spent during this project: 0.00020221 bitcoin.
- Equivalent cost in US Dollars of this amount of bitcoin: $1.28
Thoughts
Parts:
- Validating a Bitcoin address
- Does the hash in a Bitcoin address provide any protection?
- What is the chance of two people independently generating the same Bitcoin address?
Validating a Bitcoin address
Note: I normally use the phrase "Validating an X" to refer to confirming the validity of X's format. Here, however, I'm confirming the validity of the link between X and Y, where X is derived from Y. (A Bitcoin address is derived from a private key). Perhaps I should find another word to describe this type of check.
If a valid transaction can be created that transfers bitcoin out of an address, then that address is valid.
No matter how much analysis is performed, it is always possible that there may be an error / bug in the code + hardware stack that generates the Bitcoin address from the private key. If such an error exists, and an address is incorrectly calculated from a private key, and bitcoin is transferred to this address, it will not be possible to retrieve this bitcoin. It will now be controlled by an unknown private key.
Hypothetically, if this unknown private key were known, and an address could be generated correctly from it with no errors, then the resulting address would match the one that was incorrectly generated earlier.
The only way to be certain that bitcoin can be transferred out of an address is to do so. It is preferable to test the validity of an address using a small amount of value before using it to store a large amount of value.
An address is validated once some bitcoin has been moved into an address and then successfully spent from it. A larger amount of bitcoin can now be moved into this address and the owner can be certain that it can be retrieved. Even if the owner later discovers an error in the code stack used to create and sign a transaction, he/she knows that eventually this error could be fixed and a valid transaction could be created. The owner would only risk a temporary lack of access to the bitcoin, not its permanent loss.
Does the hash in a Bitcoin address provide any protection?
One notable aspect of the Bitcoin cryptosystem is that an address contains a hash of the public key, not the public key itself. This means that if bitcoin has been transferred to an address, but not spent from it, then an adversary only knows the hash of the public key.
Notes:
- The actual hash operation is RIPEMD-160( SHA256( public key ) ).
- For a good hash function, it is computationally infeasible to reverse the hash and derive the original value.
Any transaction that spends bitcoin (i.e. spends an unspent output) from this address will include the public key in its scriptSig. This means that as soon as bitcoin has been spent from an address, an adversary knows the public key.
I have read that this aspect of Bitcoin is a protection, and that it is best to use a new address for every transaction, so that an adversary never knows the public key of an address until the bitcoin in it has been transferred to a new address.
Is this true? How much and under what circumstances would knowledge of the public key help an adversary?
In the ECDSA cryptosystem used in Bitcoin, there are (approximately) 2^256 private keys. A particular public key might be derived from any one of these private keys.
This knowledge would be useful only if there were some mathematical weakness in the ECDSA cryptosystem. If such a weakness were discovered, a frontal attack might become possible on some (or all) known public keys. My current understanding is that a frontal attack on a cryptosystem usually involves finding a way to reduce the possibility space of the results of performing a reverse operation, to the point where this possibility space can feasibly be brute forced.
Note: A non-frontal / indirect attack usually involves sabotaging an opponent's entropy sources and/or software supply chain.
A mathematical weakness in ECDSA might exist. It might already be known, or it might be discovered some day. I have not heard or read of such a weakness, but many cryptosystems in the past have been broken, so it is wise to plan for a weakness to be discovered, or at least to consider the possibility carefully.
I'll define some terms:
- "hidden-key addresses" = "addresses from which no bitcoin has ever been spent"
- "known-key addresses" = "addresses from which bitcoin has been spent at least once"
I can see two possible reasons for Satoshi Nakamoto to have hashed the public key within addresses:
1) Protection against a potential future discovery of a weakness in ECDSA. Any bitcoin in hidden-key addresses will be safe if such a discovery is made. Any bitcoin in known-key addresses might be at risk.
2) Shorter addresses. Public keys are 64 bytes. RIPEMD-160 hashes are 20 bytes. Addresses are usually ~30 characters. Ignoring the complications of base58 encoding for a moment, these two numbers suggest that addresses would have been approximately 3 times their current size if Satoshi Nakamoto had not used hashing, i.e. ~90 characters.
In case (1), bitcoin in hidden-key addresses would not necessarily be safe if an attack against the hash function were also discovered, and could feasibly be used in conjunction with the attack on ECDSA. Admittedly, these two attacks would have to be compatible i.e. operate on the same or related possibility spaces. I have read that Satoshi Nakamoto used two hash functions (SHA256, then RIPEMD-160) in order to make this combined attack more difficult.
So: To attack a hidden-key address, an adversary would need to discover weaknesses in: ECDSA, SHA256, RIPEMD-160. These weaknesses would also have to be compatible. To attack a known-key address, an adversary would only need to discover a weakness in ECDSA.
Let's consider a dictionary attack. Can this be used more successfully if the public key is known?
A dictionary attack is a brute force attack against the possibility space of private keys that a human is likely to choose. Example: A memorable passphrase. A character sequence (e.g. letters, words, phrases, numbers) of up to 32 bytes can be used as a memorable private key. The attack is possible because an adversary can make educated guesses about which sequences of characters are likely to be used. Some examples: lines of famous poems, idioms, common passwords, names of famous people, your mother's maiden name, your birthplace.
Note: A dictionary attack can also be made if a sabotaged or low-quality entropy source is used and an adversary knows some details about this entropy source.
A dictionary attack is made against a public key by testing many private keys. The attack generates private keys and calculates the corresponding public keys. If the desired public key is found, the attack has succeeded. In order to make a dictionary attack against the hash of a public key, rather than just the public key, the adversary simply needs to do some more calculation per item to hash the public key. Then, if the desired public key hash has been found, the attack has succeeded. A dictionary attack will work just as well against the public key hash as against the public key. Therefore, the hash in an address offers almost no protection against a dictionary attack.
Conclusion: The hash in a Bitcoin address provides good protection against a potential future discovery of a weakness in the ECDSA cryptosystem.
What is the chance of two people independently generating the same Bitcoin address?
If two people both generate the same Bitcoin address, they will both control any bitcoin transferred to this address. In effect, this would be an unplanned "joint account".
Let's assume that both people have access to a high-quality entropy source, so all possible private keys have an equal chance of being generated.
Note: The question "What are the odds of two people generating the same address?" is equivalent to the question "What are the odds of one person generating the same address twice?".
Let's note down some values and do some calculations.
A public key is 512 bytes (64 bytes). It consists of two 256-bit (32-byte) values (X and Y) that specify a point on the elliptic curve. The corresponding private key is only 256 bits (32 bytes).
A bit can be either 0 or 1. A sequence of 512 bits can represent 2^512 possible values.
RIPEMD-160 hashes are 160 bits (20 bytes). A sequence of 160 bits can represent 2^160 possible values. Bitcoin addresses encode / contain a RIPEMD-160 hash value. There are therefore 2^160 possible Bitcoin addresses.
(2^512) / (2^160) = 2^(512-160) = 2^352
So, for every Bitcoin address, there are 2^352 public key values that hash to that address.
However, not all possible public key values are valid. There are (approximately) 2^256 private key values, and each of them corresponds to a single public key.
(2^256) / (2^160) = 2^(256-160) = 2^96
So, for every Bitcoin address, there are 2^96 valid public key values that hash to that addresses. There are also 2^96 private key values that lead to that address.
If a private key is generated using high-quality entropy, the chance that it will lead to a particular address is:
(number of private keys per address) / (number of possible private keys)
= (2^96) / (2^256)
= 2^(96-256)
= 2^(-160)
= 1 / (2^160)
So, 1 in 2^160.
Which is, unsurprisingly, 1 chance in [number of possible addresses].
Hm. I shall proceed carefully.
Let P(x) mean "The probability of outcome x".
1) Given a particular address, what are the chances of two people independently both generating it?
P(generate a particular address) * P(generate a particular address)
= (1 / (2^160)) * (1 / (2^160))
= 1 / (2^(160+160))
= 1 / (2^320)
So, 1 in 2^320.
2) What are the chances of two people independently generating the same address? (This could be any possible address).
I think this is the sum of:
P(both people generate address 1) +
P(both people generate address 2) +
...
P(both people generate address 2^160)
This is equivalent to multiplying P(generate a particular address) by (number of possible addresses). This is:
(1 / (2^320)) * (2^160)
= (2^(-320)) * (2^160)
= 2^(-320+160)
= 2^(-160)
So, 1 in 2^160.
Ok, that's the chance of two people generating the same address. What about this question: If someone generates a new private key, what is the chance that it will lead to an existing address?
Well, how many existing addresses are there?
The chart displayed at
www.blockchain.com/charts/n-unique-addresses
indicates that currently there are (approximately) 500000 unique addresses on the Bitcoin blockchain. Presumably, these are addresses that currently hold bitcoin, since the chart shows that the number of unique addresses was higher in the past.
2^19 = 524288 ~= 500000
Hm.
The chance of the outcome "generate any one of ~2^19 existing addresses" should be:
P(generate a particular address) * (number of existing addresses)
= (1 / (2^160)) * 2^19
= 2^(19-160)
= 2^(-141)
= 1 / (2^141)
So, 1 in 2^141.
Conclusion: The chance of two people independently generating the same address is 1 in 2^160. The chance of one person generating an existing address is (currently) 1 in 2^141.
Project Log
Create a project directory:
using_a_transaction_to_validate_a_bitcoin_address
If a valid transaction can be created that transfers bitcoin out of an address, then that address is valid.
No matter how much analysis is performed, it is always possible that there may be an error or bug in the code and hardware stack that generates the Bitcoin address from the private key. The only way to be certain that bitcoin can be transferred out of an address is to do so. It is therefore preferable to test the validity of an address using a small amount of value before using it to store a large amount of value.
One protection in the Bitcoin cryptosystem is that addresses contain hashes of public keys, not public keys. This means that if bitcoin has been transferred to an address, but not spent from it, then an adversary only knows the hash of the public key. As soon as bitcoin has been transferred out of the address, the public key is available to an adversary, since it is included in the transaction.
Does this matter? Well, it's an extra obstacle for an adversary trying to generate many addresses, see if they exist on the Bitcoin blockchain, check if there is unspent bitcoin stored in them, and steal it. RIPEMD-160 hashes are 160 bits (20 bytes) long. ECDSA public keys are 512 bits (64 bytes) long. Many public keys will map to the same hash value.
Hm. How many?
512 bits can represent 2^512 possible bit sequences. 160 bits can represent 2^160 possible bit sequences.
(2^512) / (2^160) = 2^(512-160) = 2^352
That's a lot. So, for every Bitcoin address, there are 2^352 public keys that might hash down to that address.
Hm. A public key consists of two 256-bit (32-byte) values for X and Y (specifying a point on the elliptic curve). The corresponding private key is only 32 bytes, not 64 bytes.
(2^256) / (2^160) = 2^(256-160) = 2^96
Technically, only 2^96 of those 2^352 public keys will actually be actual valid Bitcoin public keys that have a corresponding private key. However, an adversary won't know which of the 2^352 candidate public keys that hash to the known address hash are actual valid Bitcoin public keys.
Note: The actual candidate public keys are unknown to the adversary - 2^352 is simply the theoretical number of candidates, given a particular hash value.
Note: Not all 256-bit values are valid private keys, but most are. See Generating a standard Bitcoin address, Notes / Discoveries section, part "Notes on the nature, secure creation, and validity of Bitcoin private keys and transactions", item (1).
Now, if a transaction is broadcast from an address, then an adversary knows the public key. The 2^352 unknown candidate public keys are reduced to 1 known public key. However, there are still approximately 2^256 candidate private keys that might correspond to this public key. Given the size of this possibility space, a brute force attack is not a threat.
If low-quality entropy is used, then the known public key is vulnerable to a dictionary attack. Low-quality entropy example: a memorable passphrase. A dictionary attack is a brute force attack against the possibility space of private keys that a human is likely to choose. A 32-byte character sequence (e.g. letters, words, phrases, numbers) can be used as a memorable private key. The attack is possible because an adversary can make educated guesses about which sequences of characters are likely to be used. Some examples: lines of famous poems, idioms, common passwords, names of famous people, your mother's maiden name, your birthplace.
Note: A longer character sequence can be used and hashed when necessary (e.g. with SHA256) to reduce it to the length of a private key.
Hm. Wait. The 2^352 candidate public keys from earlier all hash to the same address. This means that they (or rather the 2^96 valid private keys) can all spend from that address. The hash, not the public key, is the value that is required in a valid transaction scriptSig. Any public key that hashes to this value will be accepted as valid in a transaction.
If high-quality entropy is used to generate the private key then it is still safe from a dictionary attack.
Hm. A dictionary attack would still work against the address / hash just as well as against the public key. The adversary simply needs to do some more calculation per item to derive the address / hash from the public key.
I conclude that the hash does not provide enough protection to justify keeping the public key secret i.e. not using a transaction to validate an address.
Note: I don't know whether Satoshi Nakamoto actually intended the RIPEMD-160 hash in the address to be a protection or if he simply wanted the address to be shorter.
Note: For every 160-bit hash in a Bitcoin address, there are 2^96 private keys that correspond to this address. If two people both generate the same private key, and transfer bitcoin to the corresponding address, they will both have access to the resulting bitcoin. A "joint account", in practice. What are the actual odds of accidentally having a joint account? Not high, but what are they?
Hm. Well, there are 2^160 addresses and (approximately) 2^256 private keys. For each address, there are 2^96 private keys that map to that address. Let's call these "joint-account" private keys.
Odds of an accidental joint account within the private key possibility space:
(Number of joint-account private keys per address) / (Number of possible private keys) = 2^96 / 2^256 = 2^(96-256) = 2^(-160)
= 1 / 2^160
Ah. Quite small. So, if I generate a private key using high-quality entropy, the odds of someone else also generating this key or a related joint-account key are 1 in 2^160.
Which... makes sense, given that there are 2^160 valid addresses.
Plan of action:
- Generate an address. This will be the "test address".
- Transfer some bitcoin to it. I'll call this "tx0".
- Construct and sign a transaction that transfers bitcoin out of the test address to another "final" address. I'll call this "tx1".
- Broadcast the transaction. If the transaction is mined, then the address has been validated.
I'll use toolsets developed during previous projects.
For generating entropy:
Generating entropy with dice
I'll need entropy for the Bitcoin private key and for signing the transaction.
For:
- generating a Bitcoin address from the private key.
- constructing and signing a single-input, single-output transaction (standard or nonstandard).
Creating and signing a standard raw Bitcoin transaction: Iteration #2
My work computer: Aineko, a 2008 Macbook running Mac OS X 10.6.8 Snow Leopard.
aineko:work stjohnpiano$ python --version
Python 2.7.13
My working definition of a standard transaction:
- It has at least one input and at least one output.
- All input and output addresses are Pay-To-Public-Key-Hash (P2PKH).
- All input scriptSigs contain uncompressed public keys.
My working definition of a nonstandard transaction:
- It has at least one input and at least one output.
- At least one input or output address is not a standard Pay-To-Public-Key-Hash (P2PKH) address.
I define a "standard" address to be an uncompressed single-signature Pay-To-Public-Key-Hash (P2PKH) address.
I'll transfer bitcoin from my LocalBitcoins account to the test address.
I'll use my current LocalBitcoins receiving address as the final address. It will be Pay-To-Script-Hash (P2SH) i.e. nonstandard, so I'll use the create_nonstandard_transaction toolset.
Log on to LocalBitcoins.
Current receiving address:
3QpwGejV9Dyai72HDhXZVkYBWioyuLVjti
Current deposit fee for incoming transactions: 0.00015 BTC
Transaction fee: 0.00005 BTC
In the project directory, create a work directory named "work".
Let's gather the toolsets.
I have 5 dice and a tray with raised edges in which to roll them.
Browse to:
Generating entropy with dice
Go to the Downloadable Assets section. Download the asset
- convert_dice_rolls_to_hex_bytes.py.
Move this asset into the work directory.
Note the section Recipe For Generating Entropy Bytes Using Dice.
Browse to:
Creating and signing a standard raw Bitcoin transaction: Iteration #2
Go to the Downloadable Assets section. Download the assets
- bjorn_edstrom_ripemd160.py
- ecdsa-0.10.tar.gz
- pypy_sha256.py
- bitcoin_functions.py
- generate_bitcoin_address_3.py
- transaction.py
- nonstandard_bitcoin_functions.py
- nonstandard_transaction.py
- create_nonstandard_transaction.py
Move these assets into the work directory.
Note the section Recipes For Using Various Downloadable Assets, particularly these recipes:
- 1: Setup
- 2: generate_bitcoin_address_3.py
- 4: create_nonstandard_transaction.py
A thought: I seem to recall that another reason to hash the Bitcoin public key is to protect against the potential future discovery of a mathematical flaw in ECDSA, which might allow an adversary to reduce the possibility space of candidate private keys for the known public key, and thus make a brute force attack more feasible. If only the hash is publicly available, i.e. no bitcoin has been spent from the address, then even if a flaw in ECDSA is discovered, any bitcoin in the address should remain safe. In this situation, the flaw would be exploitable only if compatible flaws had also been discovered in the SHA256 and RIPEMD-160 hash algorithms.
Reading the following recipes in Creating and signing a standard raw Bitcoin transaction: Iteration #2, in the section Recipes For Using Various Downloadable Assets:
- 1: Setup
- 2: generate_bitcoin_address_3.py
- 4: create_nonstandard_transaction.py
I note that recipes 2 and 4 each require 32 bytes of entropy.
For this project, I need to generate 1 address and construct 1 nonstandard transaction. Note that the transaction has only 1 input signature (which is the part that needs entropy). I will thus need 1*32 + 1*32 = 64 bytes of entropy.
I'll proceed through the section Recipe For Generating Entropy Bytes Using Dice in Generating entropy with dice in order to generate this entropy. I'll copy it here.
Excerpt:
Recipe For Generating Entropy Bytes Using Dice
Note: This recipe relies, at its base, on the algorithm described in the Notes section, in the "Algorithm for converting dice rolls to entropy bytes" part.
0. Obtain the script convert_dice_rolls_to_hex_bytes.py. This file is stored as an asset of this article. See the Downloadable Assets section.
1. Obtain one or more dice.
2. Choose a desired number of bytes of entropy. Multiply this number by 8 to convert to bits. Divide the result by the expected-bit-rate-per-dice-roll value (1.3333) to find the expected number of dice rolls that should generate this number of entropy bits. Multiply this result by 1.1 to add a 10% margin, then round up to the nearest integer.
Note: Some dice roll result values will be discarded so that the value domain becomes base4, which can be then be converted to base16 (i.e. hex bytes).
3. Perform this number of dice rolls and record the results in a text file. An individual dice roll must be recorded as one individual character from the list "123456". Whitespace (newline, tab, space) can be used to separate groups of dice roll values. Recommendation: Roll the dice on a flat tray with raised edges, so that they don't scatter too far.
4. In the script convert_dice_rolls_to_hex_bytes.py, scroll to the section of text that lies between
##### START CONTROLS
and
##### END CONTROLS
- Set the variabledesired_nto be the desired number of bytes of entropy.
- Set the variabledice_rolls_file_pathto be the path to the text file containing the dice roll results.
5. Open a terminal. Change directory to the directory containing this script. Run the script using the following command:
python convert_dice_rolls_to_hex_bytes.py
6. The output of the script should contain the generated entropy as hex bytes.
7. (optional) If more dice rolls are needed in order to reach the desired number of bytes of entropy, the script will calculate a suggested number of new dice rolls. Perform the dice rolls, add the results to the dice roll results text file, and run the script again.
Let's do step 2.
- I want 64 bytes of entropy.
- Multiply by 8: 64*8 = 512 bits
- Divide by 1.3333: 512/1.3333 ~= 384
- Multiply by 1.1: 384 * 1.1 = 422.4
- Round up to the nearest integer: round(422.4) = 423
Result: 423
Next: Step 3.
3. Perform this number of dice rolls and record the results in a text file. An individual dice roll must be recorded as one individual character from the list "123456". Whitespace (newline, tab, space) can be used to separate groups of dice roll values. Recommendation: Roll the dice on a flat tray with raised edges, so that they don't scatter too far.
I'll record the results here, and time how long it takes to get them. I'll use lines of 5 characters and groups of 5 lines. Each group will thus contain 5*5 = 25 dice rolls.
423/25 = 16.92 groups
33132
34312
22322
22614
21534
25334
54416
43453
66246
11646
35435
33653
33316
61312
14535
65363
36431
35325
51451
12663
31411
34611
45642
64615
52356
5 groups so far
32113
36446
32514
62531
64233
66163
11612
65551
31643
65544
44412
51312
16325
52636
14425
36524
64653
63565
43346
62446
64612
65266
54516
65216
56626
10 groups so far
31614
41145
45321
46352
34553
35263
61115
56142
62165
56632
41144
63453
56445
52632
41635
16326
65151
25421
53515
62545
55641
22326
16313
22644
21523
15 groups so far
24435
64234
46432
36526
56164
16 groups so far (16*25=400)
23 rolls to go
51326
45455
66425
31155
423
34312
22322
22614
21534
25334
54416
43453
66246
11646
35435
33653
33316
61312
14535
65363
36431
35325
51451
12663
31411
34611
45642
64615
52356
5 groups so far
32113
36446
32514
62531
64233
66163
11612
65551
31643
65544
44412
51312
16325
52636
14425
36524
64653
63565
43346
62446
64612
65266
54516
65216
56626
10 groups so far
31614
41145
45321
46352
34553
35263
61115
56142
62165
56632
41144
63453
56445
52632
41635
16326
65151
25421
53515
62545
55641
22326
16313
22644
21523
15 groups so far
24435
64234
46432
36526
56164
16 groups so far (16*25=400)
23 rolls to go
51326
45455
66425
31155
423
423 dice rolls.
Time taken: 16:20
16*60 + 20 = 980 seconds
980 / 423 ~= 2.32 seconds per dice roll
expected entropy per dice roll ~= 1.3333 bits
(1.3333 bits of expected entropy per dice roll) / (2.32 seconds per dice roll)
= (1.3333 / 2.32) * (bits of expected entropy / second)
= 0.57 bits of expected entropy / second
Compare with the result found in the previous project Generating entropy with dice:
- 0.89 bits of entropy per second
I did not proceed quickly, as in the previous project. I proceeded leisurely but steadily.
Using the rate of 0.57 bits of entropy per second, I would expect that to generate a Bitcoin address, which needs 32 bytes,
(32*8 bits) / (0.57 expected bits of entropy per second)
= 32*8 / 0.57
~= 449 seconds
~= 7.5 minutes
would be required.
I found that listening to music made the dice rolling quite tolerable.
I'll copy the dice results into a new file called "dice_rolls.txt" and save this file in the work directory. I'll remove the notes e.g. "10 groups so far".
dice_rolls.txt
33132
34312
22322
22614
21534
25334
54416
43453
66246
11646
35435
33653
33316
61312
14535
65363
36431
35325
51451
12663
31411
34611
45642
64615
52356
32113
36446
32514
62531
64233
66163
11612
65551
31643
65544
44412
51312
16325
52636
14425
36524
64653
63565
43346
62446
64612
65266
54516
65216
56626
31614
41145
45321
46352
34553
35263
61115
56142
62165
56632
41144
63453
56445
52632
41635
16326
65151
25421
53515
62545
55641
22326
16313
22644
21523
24435
64234
46432
36526
56164
51326
45455
66425
31155
423
Next: Step 4
4. In the script convert_dice_rolls_to_hex_bytes.py, scroll to the section of text that lies between
##### START CONTROLS
and
##### END CONTROLS
- Set the variabledesired_nto be the desired number of bytes of entropy.
- Set the variabledice_rolls_file_pathto be the path to the text file containing the dice roll results.
dice_rolls_file_path
is already set to be "dice_rolls.txt". Set
desired_n
to be 64.Next: Steps 5 & 6
5. Open a terminal. Change directory to the directory containing this script. Run the script using the following command:
python convert_dice_rolls_to_hex_bytes.py
6. The output of the script should contain the generated entropy as hex bytes.
Open a terminal. Change directory to the work directory.
aineko:work stjohnpiano$ python convert_dice_rolls_to_hex_bytes.py
### START CONVERSION OF DICE ROLLS TO HEX BYTES
- number of dice rolls: 423
- number of dice rolls in the list [1234]: 266
- number of hex bytes: 66
- desired number of hex bytes: 64
- the hex bytes produced are sufficient.
- hex byte output:
a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9fd836
- hex byte output shortened to the desired length (64 bytes):
a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121
- remaining hex bytes:
258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9fd836
Result: Desired amount of entropy (64 bytes) has been produced.
Entropy (64 bytes):
a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121
Recommendation: Perhaps preserve the 34 extra hex bytes in an entropy storage file.
Extra hex bytes: 258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9fd836
### END CONVERSION OF DICE ROLLS TO HEX BYTES
Hm. Remaining hex bytes should only be 2 hex bytes (4 hex characters).
Ah. There's an error in the code.
In the following section:
desired_bytes = data4[:32*2] | |
print desired_bytes | |
print "- remaining hex bytes:" | |
remaining = data4[32*2:] |
The number
32
in lines 99 and 102 should actually be
desired_n
. The value 32 was hardcoded, which didn't cause any problems during the previous project because I wanted a 32-byte result. In the code, substitute
desired_n
for
32
in lines 99 and 102. Then run the script again.
aineko:work stjohnpiano$ python convert_dice_rolls_to_hex_bytes.py
### START CONVERSION OF DICE ROLLS TO HEX BYTES
- number of dice rolls: 423
- number of dice rolls in the list [1234]: 266
- number of hex bytes: 66
- desired number of hex bytes: 64
- the hex bytes produced are sufficient.
- hex byte output:
a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9fd836
- hex byte output shortened to the desired length (64 bytes):
a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9
- remaining hex bytes:
fd836
Result: Desired amount of entropy (64 bytes) has been produced.
Entropy (64 bytes):
a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9
Recommendation: Perhaps preserve the 2 extra hex bytes in an entropy storage file.
Extra hex bytes: fd836
### END CONVERSION OF DICE ROLLS TO HEX BYTES
Hm. Why have an odd number of hex characters been left over?
Aha. I didn't include a check for that.
Immediately after the newline at the end of line 88, add the following section:
print "- number of hex characters after conversion: %d" % len(data4) | |
if len(data4) % 2 == 1: | |
# need an even number of hex characters for conversion to entire 8-bit bytes. | |
print "- there is one extra hex character that can't be used to form an entire 8-bit byte." | |
data4 = data4[:-1] # remove last character from string. | |
Then run the script again.
aineko:work stjohnpiano$ python convert_dice_rolls_to_hex_bytes.py
### START CONVERSION OF DICE ROLLS TO HEX BYTES
- number of dice rolls: 423
- number of dice rolls in the list [1234]: 266
- number of hex characters after conversion: 133
- there is one extra hex character that can't be used to form an entire 8-bit byte.
- number of hex bytes: 66
- desired number of hex bytes: 64
- the hex bytes produced are sufficient.
- hex byte output:
a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9fd83
- hex byte output shortened to the desired length (64 bytes):
a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9
- remaining hex bytes:
fd83
Result: Desired amount of entropy (64 bytes) has been produced.
Entropy (64 bytes):
a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9
Recommendation: Perhaps preserve the 2 extra hex bytes in an entropy storage file.
Extra hex bytes: fd83
### END CONVERSION OF DICE ROLLS TO HEX BYTES
Excellent.
The desired 64 bytes of entropy:
a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9
I want to divide this entropy into two pieces, each of 32 bytes. Note that 2 hex characters represent 1 byte.
aineko:~ stjohnpiano$ python
Python 2.7.13 (default, Dec 18 2016, 05:35:59)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> x = "a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9"
>>> x[:64]
>>> 'a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121'
>>> x[64:]
>>> '258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9'
>>>
Entropy-bytes-1:
a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121
Entropy-bytes-2:
258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9
Ok. I'll move on to recipe 1 (Setup), in the section Recipes For Using Various Downloadable Assets in the previous article Creating and signing a standard raw Bitcoin transaction: Iteration #2. I'll copy it here.
Excerpt:
Recipe 1: Setup
Initial condition: You must have Python 2.7.x installed on your computer. These code assets were tested under Python 2.7.13 running on Mac OS X 10.6.8 (Snow Leopard), and should run successfully on other versions of Python 2.7.
Steps:
- Create a work directory.
- Browse to the Downloadable Assets section of this article.
- Download the asset bjorn_edstrom_ripemd160.py.
- Download the asset ecdsa-0.10.tar.gz.
- Download the asset pypy_sha256.py.
- Download the asset bitcoin_functions.py.
- Move these assets into the work directory.
- Unpack the zipped tape archive file ecdsa-0.10.tar.gz. This can be done by opening a terminal, changing directory to the work directory, and running the following command:
tar -zxvf ecdsa-0.10.tar.gz
This unpacking should produce a new directory named ecdsa-0.10 in the work directory. The directory ecdsa-0.10 should contain a directory named ecdsa. Copy the ecdsa directory into the work directory.
I already have Python 2.7.13 installed on this computer.
I've already created a work directory. I've already downloaded the listed assets and moved them into the work directory.
Next: Unpack the zipped tape archive file ecdsa-0.10.tar.gz.
In the terminal, in the work directory:
aineko:work stjohnpiano$ tar -zxvf ecdsa-0.10.tar.gz
x ecdsa-0.10/
x ecdsa-0.10/ecdsa/
x ecdsa-0.10/ecdsa/__init__.py
x ecdsa-0.10/ecdsa/_version.py
x ecdsa-0.10/ecdsa/curves.py
x ecdsa-0.10/ecdsa/der.py
x ecdsa-0.10/ecdsa/ecdsa.py
x ecdsa-0.10/ecdsa/ellipticcurve.py
x ecdsa-0.10/ecdsa/keys.py
x ecdsa-0.10/ecdsa/numbertheory.py
x ecdsa-0.10/ecdsa/rfc6979.py
x ecdsa-0.10/ecdsa/six.py
x ecdsa-0.10/ecdsa/test_pyecdsa.py
x ecdsa-0.10/ecdsa/util.py
x ecdsa-0.10/LICENSE
x ecdsa-0.10/MANIFEST.in
x ecdsa-0.10/NEWS
x ecdsa-0.10/PKG-INFO
x ecdsa-0.10/README.md
x ecdsa-0.10/setup.py
aineko:work stjohnpiano$ ls -1
bitcoin_functions.py
bjorn_edstrom_ripemd160.py
convert_dice_rolls_to_hex_bytes.py
create_nonstandard_transaction.py
dice_rolls.txt
ecdsa-0.10
ecdsa-0.10.tar.gz
generate_bitcoin_address_3.py
nonstandard_bitcoin_functions.py
nonstandard_transaction.py
pypy_sha256.py
transaction.py
aineko:work stjohnpiano$ cp -r ecdsa-0.10/ecdsa ecdsa
aineko:work stjohnpiano$ ls -1
bitcoin_functions.py
bjorn_edstrom_ripemd160.py
convert_dice_rolls_to_hex_bytes.py
create_nonstandard_transaction.py
dice_rolls.txt
ecdsa
ecdsa-0.10
ecdsa-0.10.tar.gz
generate_bitcoin_address_3.py
nonstandard_bitcoin_functions.py
nonstandard_transaction.py
pypy_sha256.py
transaction.py
Setup complete.
Next: Recipe 2 (generate_bitcoin_address_3.py), in the section Recipes For Using Various Downloadable Assets in the previous article Creating and signing a standard raw Bitcoin transaction: Iteration #2. I'll copy it here.
Excerpt:
Recipe 2: generate_bitcoin_address_3.py
Notes:
- generate_bitcoin_address_3.py is a script that generates a standard Bitcoin address from a private key.
- All control values in this script are strings.
Steps:
- Work through Recipe 1: Setup.
- Generate a random private key that is between 1 and 32 bytes long. This requires some work (e.g. using dice) and is not covered in this recipe.
Note: Ideally a private key should be 32 bytes long. It can be raw bytes (printable ASCII characters) or hex bytes (hex characters). Hex characters must be lowercase. 1 byte is represented by 2 hex characters, so a 32-byte private key will be 64 hex characters long. I recommend hex bytes, as these can represent all possible private keys. I find that raw byte private keys are convenient during testing.
- Browse to the Downloadable Assets section of this article.
- Download the asset generate_bitcoin_address_3.py. Move it into the work directory.
- Open the file generate_bitcoin_address_3.py in a text editor. Scroll to the section of text that lies between
##### START CONTROLS
and
##### END CONTROLS
- If the private key is in raw bytes, set the variableprivate_key_typeto"raw_bytes"and set the variableprivate_key_raw_bytesto the private key.
- If the private key is in hex bytes, set the variableprivate_key_typeto"hex_bytes"and set the variableprivate_key_hex_bytesto the private key.
- Open a terminal and change directory to the work directory.
- Run the following command:
python generate_bitcoin_address_3.py
- The output should contain the Bitcoin address that corresponds to your private key.
Note: Please go to the "Addresses generated and/or used in this project" part of the Notes section to see two examples of the output of this script.
I've already worked through Recipe 1: Setup.
I'll use the first half of the entropy that I generated earlier, entropy-bytes-1, as the private key for generating an address.
Entropy-bytes-1:
a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121
I've already downloaded the asset generate_bitcoin_address_3.py and moved it into the work directory.
Next:
- Open the file generate_bitcoin_address_3.py in a text editor. Scroll to the section of text that lies between
##### START CONTROLS
and
##### END CONTROLS
[...]
- If the private key is in hex bytes, set the variableprivate_key_typeto"hex_bytes"and set the variableprivate_key_hex_bytesto the private key.
Done.
private_key_type
set to
"hex_bytes"
.
private_key_hex_bytes
set to
"a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121"
.Note: Unused input variables can be set to the empty string {""}, but there is no need to. I left
private_key_raw_bytes
set to
"roadside_picnic"
. Finally:
- Open a terminal and change directory to the work directory.
- Run the following command:
python generate_bitcoin_address_3.py
- The output should contain the Bitcoin address that corresponds to your private key.
aineko:work stjohnpiano$ python generate_bitcoin_address_3.py
### START GENERATION OF BITCOIN ADDRESS
Private key (hex bytes): a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121
Private key (32 hex bytes): a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121
Private key (WIF): 5K3pdySk3c2jz3i7jiULrMZN5JSUSEEF1M1bbetRRVAbE2vdEv9
Bitcoin address: 1AGygbyEFYduWkkmZbbvirgS9kuBBMLJCP
### END GENERATION OF BITCOIN ADDRESS
Excellent.
Test address:
- Private key (hex bytes):
a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121
- Bitcoin address:
1AGygbyEFYduWkkmZbbvirgS9kuBBMLJCP
Next: Calculate how much bitcoin to send to the test address.
LocalBitcoins:
- Current deposit fee for incoming transactions: 0.00015 BTC
- Transaction fee: 0.00005 BTC
Browse to:
bitcoinfees.earn.com
The fee rates are displayed in satoshi per byte of transaction data. A satoshi is 0.00000001 bitcoin.
Choose a high fee in order to have slack available. I'll attempt to use a low fee in the actual transaction.
A nonstandard transaction with one P2PKH input and one P2SH output should be approximately 223 bytes.
The fee rate range 1-2 satoshi / byte includes many transactions waiting for confirmation. Estimated delay = 1-8 blocks (0-110 minutes).
A fee rate of 60 satoshi / byte looks quite high.
I estimate that a fairly high fee will therefore be 223 bytes * 60 satoshi / byte = 223 * 60 = 13380 satoshi.
13380 satoshi * 1 BTC / 10^8 satoshi = 0.00013380 BTC
So, expected costs:
- LocalBitcoins transaction fee = 0.00005 BTC. This will be deducted from my LocalBitcoins wallet balance, not from the transction amount.
- Bitcoin network transaction fee (high-end) = 0.00013380 BTC.
- LocalBitcoins deposit fee = 0.00015 BTC.
I need to transfer at least this much to the test address:
(Bitcoin network transaction fee) + (LocalBitcoins deposit fee)
= 0.00013380 + 0.00015
= 0.00028380 BTC
Current USD / BTC price:
$6330.00
(6330 USD / BTC) * 0.00028380 BTC ~= $1.80
I'll transfer $5-worth of bitcoin to the test address, so as to have room to manoeuvre.
$5 * (1 BTC / 6330 USD) ~= 0.00078989 BTC
Next: Transfer bitcoin to the test address. This transaction will be tx0.
Log on to LocalBitcoins.
Go to Wallet / Send bitcoins.
Send 0.00078989 BTC to
1AGygbyEFYduWkkmZbbvirgS9kuBBMLJCP
Go to Wallet / Transactions.
In the new entry for
1AGygbyEFYduWkkmZbbvirgS9kuBBMLJCP,
the txid is shown:
bf7a16ef4bf8763b7bf7f02b03155c9a2f9b997d19e64a770bf3f7d3f24cc48b
Browse to:
live.blockcypher.com
Search for this txid.
Transaction page loads:
live.blockcypher.com/btc/tx/bf7a16ef4bf8763b7bf7f02b03155c9a2f9b997d19e64a770bf3f7d3f24cc48b
Confirmations: 0/6
Set timer for 1 hour.
Timer has ended.
Refresh page.
Confirmations: 3/6
Set timer for 30 minutes.
Timer has ended.
Refresh page.
Confirmations: 5/6
[do something else for a while]
Refresh page.
Confirmations: 6+
Click "Advanced Details". Click "API Call".
Result: tx0
{
"block_hash": "00000000000000000001d5c84216c226186288fad3bedb057a25b393f44cae0d",
"block_height": 547832,
"block_index": 445,
"hash": "bf7a16ef4bf8763b7bf7f02b03155c9a2f9b997d19e64a770bf3f7d3f24cc48b",
"hex": "0200000000010415795494c16526d2c85ded962bcf65c466b2aeda609f910a68e151cf9fc368fc0400000017160014f51ff6dbb63fd49528c4e8479223b5971a81d3bdfeffffff1a2e7248cd107cdb19b4b2adc299fdccfadacd70588189a9df521feb530ec0ac2c0000001716001459cef41e06f90ebbd80233d7c5d27cb0c61b14c3feffffff7c4bb59bf37b24da9b884268d36c22a35c02e1a82a5143f50bbbd5c8be1508f80b000000171600147ecc1a766adf7a5e2d39fb6d8f9e1764b4b8660dfeffffffa54da9a188478bef86ecbb552c5223b4c7b888fe06db55397428d5290b2740ea0e00000017160014bc2befde3f9ae93a1ce6d5a3e6a86e3141ce4b04feffffff178d340100000000001976a91465beb01aab71c9da76f38a383c65e3693dff54cc88ac344504000000000017a91492bc083c4181b5e4cac5f5a9b4fb6dae0219528e874d3203000000000017a914ca94b0fec8b339e8fa9d4ddb0516839ef9c11a2887bae914000000000017a914fc5ae2cb5ad9056548125bcf62793a4cb039491f8738f103000000000017a914289e594f2ea708b1136e36450a4afac3a25f702c8759a702000000000017a9140ac28ef322cc5b6b3fec615d0c197791de8b59f887cf1307000000000017a91451efb4c884c9078313bac407204ac9420bad3b22871ccb1a000000000017a914e5d58610ad31c9e14a5e8d10fcc6f2427942441287c5a20e000000000017a914a2d1ba0544ecb3c72e06bd849e338feefb3cb20e875ba609000000000017a9144ab0e6b6ccd7d6c4c09644614d419da56320d3e187106508000000000017a914624664348a70deff56ebaed44960bd2c233af2798790a803000000000017a9145766edfa8e799fdd6f925c5650668037edfed64c87f1eb0200000000001976a914793c2ad3dca515c7a3c2bda812093d01018b6b6d88ac18fc08000000000017a914d48dcb696fa38ec4e8b203dd28b55486c03deed887d09603000000000017a914ffaab934fdf254aed588a677ef9c911512bc6bc987eef60700000000001976a914887d561d2be51b0ed30d0026fb5163ddd016056188ac29c903000000000017a91472d57686bd1b99ea0726cf7b9909f61b56bf25e28761b30b00000000001976a9142cb7b141a37626469383f56594301c37063384d788ac20ed24000000000017a9147867a63bdc8e9afce16b36eb75246e786060c13a8780d72500000000001976a914e647bc4bde7f419a1bf3466a2bd4fe2e050e820a88acf61d05000000000017a914378ac2b2a75845101c26d610af7c77549c271d4387832608000000000017a91436505c965d7d34ebfa9cdc1e42049ecb395e2204871c5804000000000017a9143932127d76941a41a1735f1e46111f6caa9b7be78702483045022100901294bbd023a9735cc4fa48362f7541e93bf733ab20eaeeb72e82cddecf243202204ad45078a2ccfffc7e93e41df302a7a3c2639be8ce13ceb6cdd6fca999d10ed1012102387004774f595ee60e225060dd88751060a7f117c6f4727e611d9bcb1bc2364f0247304402205213132975412109ce138217f96132214d2d70031947926743df35fbd71b3a870220321e797e8083387e3dcbecfb593342b0755c48f8e290479025da61fdc1d0d2e6012103348282b1126af1d59fb06f709c905823d4b0bd54fdb5345a87f4761d54a4e7490247304402206d5e0db042ae03deccf3369bd158aa9ac507422c4b7e453d51feebb1c2d7e9190220056b7c8d89850d0c9ef809a02ec05f39e5b625fc3c4f42c56a5a65798e4792d4012102b08520b2b67f91bcb833ed69ce55e1156466a860af232c733a5aa144a100de5702483045022100d37376e64d126b2f807bac289ff3bcd84e3a8d65acaa714c1a7481c0aee13e63022030b1cfff272b1e9a84746cc3377290bec874f81f10c3447b8fe5c4b2f1e51160012102a2591442b520813c757c11f3c9c46c98247ac2fe06579808083bd35eeba19dd5f65b0800",
"addresses": [
"155SncYR4tDotM94nMeEtA6USbDJmFxPMZ",
"1AGygbyEFYduWkkmZbbvirgS9kuBBMLJCP",
"1C42qi6Cxtf6J71KJqRy2z1ospiSfsZG5s",
"1DSh3caioeSLGZQBmvMEqtdJLPTXnqmzt9",
"1MzcMPogSHBi3YTso4JuLeC9DnWZ8aMAGg",
"32fukciAjGHX6RKBkLueMhTM5WPuXr6EBr",
"35PneVK74Dfj6Mibhysb4scAiHrb4YsoFA",
"36eCgXejEAD6eCRRVAERaTZpaDxEvsseGV",
"36khKM15T4Z6YPWaCF2SLeHpziaRZd5pSp",
"36uSRZHNJbuXv9DCDSnuVGkKyzEii2uB5g",
"38Vwr3xvoMWZTLiK81eEwqJ2hgSqUB68a7",
"39AFpADXNvEJHqfcCkWzCEYb4tUN8umPex",
"39fA1uaPcVraJWsHWXY6SHbc8i7Un4wVeY",
"3AeeUE1qy98amc4BEPe6KT8i4nGjKHUCUc",
"3CACg5fVGNwyn67oWcux3ce79ezeLHpxdF",
"3CffBFFgR5aqGudHkgKFVNPKVrvP9eECSS",
"3CjYrYSYJUz3VTxoo4TNAmKGDNufizAhBr",
"3F2sXkekZ3tiDM5PNu678HrAGCCQSuxPxR",
"3F4sqFkdmjgiQvvx2JbRyTAR4RraSC5q5U",
"3F8LZHjZ1scGdRQXgK9epsNMxhGYyVKq2x",
"3Fo7HWeySwTaku4gi9kjnAPuvFP6YLdg28",
"3GXvdgZWsXgN7ysAN4xqQc4oPZBpfTTnVi",
"3LAAYYyyHa2DtXftFApoQT4Jqv7SZWXf9t",
"3M4u3GiVSy7M7LksF95g48BssuU44h4ndX",
"3NeGTMnMruADRsbQ6XdGP19EjzaAM3A5yj",
"3QhM5kQLJ1M9vdeHdHqnrSsgy88269atce",
"3QzrkXyLPUEDXon8iAF7cspG5UWa1tAECZ"
],
"total": 15579018,
"fees": 13664,
"size": 1012,
"preference": "medium",
"relayed_by": "18.191.80.195:8333",
"confirmed": "2018-10-29T12:26:02Z",
"received": "2018-10-29T12:01:38.373Z",
"ver": 2,
"lock_time": 547830,
"double_spend": false,
"vin_sz": 4,
"vout_sz": 23,
"confirmations": 7,
"confidence": 1,
"inputs": [
{
"prev_hash": "fc68c39fcf51e1680a919f60daaeb266c465cf2b96ed5dc8d22665c194547915",
"output_index": 4,
"script": "160014f51ff6dbb63fd49528c4e8479223b5971a81d3bd",
"output_value": 3325425,
"sequence": 4294967294,
"addresses": [
"3F2sXkekZ3tiDM5PNu678HrAGCCQSuxPxR"
],
"script_type": "pay-to-script-hash",
"age": 547829,
"witness": [
"3045022100901294bbd023a9735cc4fa48362f7541e93bf733ab20eaeeb72e82cddecf243202204ad45078a2ccfffc7e93e41df302a7a3c2639be8ce13ceb6cdd6fca999d10ed101",
"02387004774f595ee60e225060dd88751060a7f117c6f4727e611d9bcb1bc2364f"
]
},
{
"prev_hash": "acc00e53eb1f52dfa989815870cddafaccfd99c2adb2b419db7c10cd48722e1a",
"output_index": 44,
"script": "16001459cef41e06f90ebbd80233d7c5d27cb0c61b14c3",
"output_value": 4569075,
"sequence": 4294967294,
"addresses": [
"3Fo7HWeySwTaku4gi9kjnAPuvFP6YLdg28"
],
"script_type": "pay-to-script-hash",
"age": 547830,
"witness": [
"304402205213132975412109ce138217f96132214d2d70031947926743df35fbd71b3a870220321e797e8083387e3dcbecfb593342b0755c48f8e290479025da61fdc1d0d2e601",
"03348282b1126af1d59fb06f709c905823d4b0bd54fdb5345a87f4761d54a4e749"
]
},
{
"prev_hash": "f80815bec8d5bb0bf543512aa8e1025ca3226cd36842889bda247bf39bb54b7c",
"output_index": 11,
"script": "1600147ecc1a766adf7a5e2d39fb6d8f9e1764b4b8660d",
"output_value": 6636318,
"sequence": 4294967294,
"addresses": [
"3F8LZHjZ1scGdRQXgK9epsNMxhGYyVKq2x"
],
"script_type": "pay-to-script-hash",
"age": 547825,
"witness": [
"304402206d5e0db042ae03deccf3369bd158aa9ac507422c4b7e453d51feebb1c2d7e9190220056b7c8d89850d0c9ef809a02ec05f39e5b625fc3c4f42c56a5a65798e4792d401",
"02b08520b2b67f91bcb833ed69ce55e1156466a860af232c733a5aa144a100de57"
]
},
{
"prev_hash": "ea40270b29d528743955db06fe88b8c7b423522c55bbec86ef8b4788a1a94da5",
"output_index": 14,
"script": "160014bc2befde3f9ae93a1ce6d5a3e6a86e3141ce4b04",
"output_value": 1061864,
"sequence": 4294967294,
"addresses": [
"3CjYrYSYJUz3VTxoo4TNAmKGDNufizAhBr"
],
"script_type": "pay-to-script-hash",
"age": 547829,
"witness": [
"3045022100d37376e64d126b2f807bac289ff3bcd84e3a8d65acaa714c1a7481c0aee13e63022030b1cfff272b1e9a84746cc3377290bec874f81f10c3447b8fe5c4b2f1e5116001",
"02a2591442b520813c757c11f3c9c46c98247ac2fe06579808083bd35eeba19dd5"
]
}
],
"outputs": [
{
"value": 78989,
"script": "76a91465beb01aab71c9da76f38a383c65e3693dff54cc88ac",
"addresses": [
"1AGygbyEFYduWkkmZbbvirgS9kuBBMLJCP"
],
"script_type": "pay-to-pubkey-hash"
},
{
"value": 279860,
"script": "a91492bc083c4181b5e4cac5f5a9b4fb6dae0219528e87",
"spent_by": "665bfcfcf27b1ba7194640521aa1607baaf84888fe63b58dc472366e02d96509",
"addresses": [
"3F4sqFkdmjgiQvvx2JbRyTAR4RraSC5q5U"
],
"script_type": "pay-to-script-hash"
},
{
"value": 209485,
"script": "a914ca94b0fec8b339e8fa9d4ddb0516839ef9c11a2887",
"addresses": [
"3LAAYYyyHa2DtXftFApoQT4Jqv7SZWXf9t"
],
"script_type": "pay-to-script-hash"
},
{
"value": 1370554,
"script": "a914fc5ae2cb5ad9056548125bcf62793a4cb039491f87",
"spent_by": "e9463fb764232b4ad739286acd7900087e7bbff565a51f2218b28f87bc06faf9",
"addresses": [
"3QhM5kQLJ1M9vdeHdHqnrSsgy88269atce"
],
"script_type": "pay-to-script-hash"
},
{
"value": 258360,
"script": "a914289e594f2ea708b1136e36450a4afac3a25f702c87",
"spent_by": "bb09523932d9dbe5bb3bc29de5c7acfa6be598cd9a969009627f49722af54720",
"addresses": [
"35PneVK74Dfj6Mibhysb4scAiHrb4YsoFA"
],
"script_type": "pay-to-script-hash"
},
{
"value": 173913,
"script": "a9140ac28ef322cc5b6b3fec615d0c197791de8b59f887",
"addresses": [
"32fukciAjGHX6RKBkLueMhTM5WPuXr6EBr"
],
"script_type": "pay-to-script-hash"
},
{
"value": 463823,
"script": "a91451efb4c884c9078313bac407204ac9420bad3b2287",
"spent_by": "85da2f62898b615857bf08fcce68b4fde37b0ea8bd2a6f8e110ec154534fb55c",
"addresses": [
"39AFpADXNvEJHqfcCkWzCEYb4tUN8umPex"
],
"script_type": "pay-to-script-hash"
},
{
"value": 1755932,
"script": "a914e5d58610ad31c9e14a5e8d10fcc6f2427942441287",
"addresses": [
"3NeGTMnMruADRsbQ6XdGP19EjzaAM3A5yj"
],
"script_type": "pay-to-script-hash"
},
{
"value": 959173,
"script": "a914a2d1ba0544ecb3c72e06bd849e338feefb3cb20e87",
"spent_by": "19f2fc280309024cf4ba9e65f4ad1d7d5d2296083038a644a779f8f63af42cbc",
"addresses": [
"3GXvdgZWsXgN7ysAN4xqQc4oPZBpfTTnVi"
],
"script_type": "pay-to-script-hash"
},
{
"value": 632411,
"script": "a9144ab0e6b6ccd7d6c4c09644614d419da56320d3e187",
"spent_by": "25ef68f21dc2c6f4fd337980f457fac6bd37819ca486b26759f6e2d3c58a8b82",
"addresses": [
"38Vwr3xvoMWZTLiK81eEwqJ2hgSqUB68a7"
],
"script_type": "pay-to-script-hash"
},
{
"value": 550160,
"script": "a914624664348a70deff56ebaed44960bd2c233af27987",
"addresses": [
"3AeeUE1qy98amc4BEPe6KT8i4nGjKHUCUc"
],
"script_type": "pay-to-script-hash"
},
{
"value": 239760,
"script": "a9145766edfa8e799fdd6f925c5650668037edfed64c87",
"addresses": [
"39fA1uaPcVraJWsHWXY6SHbc8i7Un4wVeY"
],
"script_type": "pay-to-script-hash"
},
{
"value": 191473,
"script": "76a914793c2ad3dca515c7a3c2bda812093d01018b6b6d88ac",
"addresses": [
"1C42qi6Cxtf6J71KJqRy2z1ospiSfsZG5s"
],
"script_type": "pay-to-pubkey-hash"
},
{
"value": 588824,
"script": "a914d48dcb696fa38ec4e8b203dd28b55486c03deed887",
"spent_by": "ae0e732d1ab0038d75d21e60fb7dbb5f814cbdc65898ff429801ad4609e4a1aa",
"addresses": [
"3M4u3GiVSy7M7LksF95g48BssuU44h4ndX"
],
"script_type": "pay-to-script-hash"
},
{
"value": 235216,
"script": "a914ffaab934fdf254aed588a677ef9c911512bc6bc987",
"addresses": [
"3QzrkXyLPUEDXon8iAF7cspG5UWa1tAECZ"
],
"script_type": "pay-to-script-hash"
},
{
"value": 521966,
"script": "76a914887d561d2be51b0ed30d0026fb5163ddd016056188ac",
"addresses": [
"1DSh3caioeSLGZQBmvMEqtdJLPTXnqmzt9"
],
"script_type": "pay-to-pubkey-hash"
},
{
"value": 248105,
"script": "a91472d57686bd1b99ea0726cf7b9909f61b56bf25e287",
"addresses": [
"3CACg5fVGNwyn67oWcux3ce79ezeLHpxdF"
],
"script_type": "pay-to-script-hash"
},
{
"value": 766817,
"script": "76a9142cb7b141a37626469383f56594301c37063384d788ac",
"addresses": [
"155SncYR4tDotM94nMeEtA6USbDJmFxPMZ"
],
"script_type": "pay-to-pubkey-hash"
},
{
"value": 2420000,
"script": "a9147867a63bdc8e9afce16b36eb75246e786060c13a87",
"addresses": [
"3CffBFFgR5aqGudHkgKFVNPKVrvP9eECSS"
],
"script_type": "pay-to-script-hash"
},
{
"value": 2480000,
"script": "76a914e647bc4bde7f419a1bf3466a2bd4fe2e050e820a88ac",
"addresses": [
"1MzcMPogSHBi3YTso4JuLeC9DnWZ8aMAGg"
],
"script_type": "pay-to-pubkey-hash"
},
{
"value": 335350,
"script": "a914378ac2b2a75845101c26d610af7c77549c271d4387",
"spent_by": "373511cd1d5c1ba0fcd6f4d3caf5dd33224eb48c755c014bae66994121c3b368",
"addresses": [
"36khKM15T4Z6YPWaCF2SLeHpziaRZd5pSp"
],
"script_type": "pay-to-script-hash"
},
{
"value": 534147,
"script": "a91436505c965d7d34ebfa9cdc1e42049ecb395e220487",
"spent_by": "ae0e732d1ab0038d75d21e60fb7dbb5f814cbdc65898ff429801ad4609e4a1aa",
"addresses": [
"36eCgXejEAD6eCRRVAERaTZpaDxEvsseGV"
],
"script_type": "pay-to-script-hash"
},
{
"value": 284700,
"script": "a9143932127d76941a41a1735f1e46111f6caa9b7be787",
"spent_by": "196edd775db9c1b57f0573cf079b9cc286dbd9f0eec7e4da24157d17ba562747",
"addresses": [
"36uSRZHNJbuXv9DCDSnuVGkKyzEii2uB5g"
],
"script_type": "pay-to-script-hash"
}
]
}
Key details:
- block_hash:
00000000000000000001d5c84216c226186288fad3bedb057a25b393f44cae0d
- block_height: 547832
- confirmed: 2018-10-29T12:26:02Z
- received: 2018-10-29T12:01:38.373Z
- txid:
bf7a16ef4bf8763b7bf7f02b03155c9a2f9b997d19e64a770bf3f7d3f24cc48b
Next: Construct and sign a (nonstandard) transaction that transfers bitcoin out of the test address to the (nonstandard) final address.
Next: Recipe 4 (create_nonstandard_transaction.py), in the section Recipes For Using Various Downloadable Assets in the previous article Creating and signing a standard raw Bitcoin transaction: Iteration #2. I'll copy it here.
Excerpt:
Recipe 4: create_nonstandard_transaction.py
Notes:
- create_nonstandard_transaction.py is a script that creates and signs a nonstandard Bitcoin transaction with one input and one output. The input address must be a standard P2PKH address and the output address must be a nonstandard P2SH address. P2PKH addresses start with the character '1'. P2SH addresses start with the character '3'.
- All control values in this script are strings.
- New transactions spend unspent-outputs-of-previous-transactions.
- The bitcoin balance of an address is the sum of the unspent outputs sent by previous transactions to this address.
- To spend bitcoin from an address, the bitcoin amount must be constructed from complete unspent outputs. Any change can be sent in a new unspent output back to the original address or another that you control. Unspent outputs cannot be broken into smaller unspent outputs prior to spending - new unspent outputs (larger or smaller) must be created by a new transaction.
Definitions:
- input address: the address that currently contains the bitcoin that you want to transfer in this transaction.
- output address: the address to which the bitcoin will be transferred by this transaction.
- previous_output_hash: big-endian 32-byte double-SHA256 hash of a previous transaction.
- txid: "transaction id", the little-endian form of the previous_output_hash.
Steps:
- Work through Recipe 1: Setup.
- Gather the following pieces of information:
-- The txid of the previous transaction that transferred the unspent output to the input address.
-- The index of this unspent output in the previous transaction (the "previous_output_index"). This index is implicit and starts at zero.
-- The private key (in hex bytes) of the input address.
-- The bitcoin amount (or satoshi amount) contained within the unspent output. Let's call this the "input amount".
- Choose the following pieces of information:
-- The output address.
-- The bitcoin amount (or satoshi amount) that you wish to transfer to the output address. Let's call this the "output amount".
-- The change address. Currently, this is the same as the output address. Any value not assigned to an output address will be assigned to the change address. The fee will then be subtracted from the value assigned to the change address.
-- The fee (in satoshi) or fee_rate (in satoshi / byte). The fee must be an integer e.g."225". The fee_rate must be an integer e.g."1"or a float e.g."1.2". You should look up the range of fee rates for transactions that are currently being mined.
- Generate a random value that is between 1 and 32 bytes long. This requires some work (e.g. using dice) and is not covered in this recipe.
Note: This random value will be used for creating the ECDSA signature of the transaction. Every ECDSA signature you ever make should use a different random value. Ideally a random value should be 32 bytes long. It can be raw bytes (printable ASCII characters) or hex bytes (hex characters). Hex characters must be lowercase. 1 byte is represented by 2 hex characters, so a 32-byte random value will be 64 hex characters long. I recommend hex bytes, as these can represent all possible random values. I find that raw byte random values are convenient during testing.
- Browse to the Downloadable Assets section of this article.
- Download the asset transaction.py.
- Download the asset nonstandard_bitcoin_functions.py
- Download the asset nonstandard_transaction.py.
- Download the asset create_nonstandard_transaction.py
- Move these assets into the work directory.
- Open the file create_nonstandard_transaction.py in a text editor. Scroll to the section of text that lies between
##### START CONTROLS
and
##### END CONTROLS
- Set the variablerandom_valueto the random value.
- If the random value is in raw bytes, set the variablerandom_value_typeto"raw_bytes".
- If the random value is in hex bytes, set the variablerandom_value_typeto"hex_bytes".
- The variableinput_datais a dictionary that contains several other variables.
-- Set the value of the variableinput_data["txid"]to be the txid.
-- Set the value of the variableinput_data["previous_output_index"]to be the index of the unspent output.
-- Set the value of the variableinput_data["private_key_hex"]to be the private key.
-- If the input amount is in satoshi, set the value of the variableinput_data["satoshi_amount"]to be the input amount.
-- If the input amount is in bitcoin, set the value of the variableinput_data["bitcoin_amount"]to be the input amount.
Note: Currently, if both of the variablesinput_data["satoshi_amount"]andinput_data["bitcoin_amount"]are set, theninput_data["bitcoin_amount"]will take precedence. You can comment out the unused variable of the pair by placing a number sign ('#') at the start of the relevant line.
-- Set the value of the variableinput_data["input_type"]to be"p2pkh".
- The variableoutput_datais a dictionary that contains several other variables.
-- Set the value of the variableoutput_data["address"]to be the output address.
-- If the output amount is in satoshi, set the value of the variableoutput_data["satoshi_amount"]to be the output amount.
-- If the output amount is in bitcoin, set the value of the variableoutput_data["bitcoin_amount"]to be the output amount.
Note: Currently, if both of the variablesoutput_data["satoshi_amount"]andoutput_data["bitcoin_amount"]are set,output_data["bitcoin_amount"]will take precedence. You can comment out the unused variable of the pair by placing a number sign ('#') at the start of the relevant line.
-- Set the value of the variableoutput_data["output_type"]to be"p2sh".
- Set the variablechange_addressto be the change address.
- If you have chosen to set the fee as an absolute number of satoshi, set the variablefeeto be the fee amount and set the variablefee_typeto be"fee".
- If you have chosen to set the fee as a fee rate (in satoshi / byte), set the variablefee_rateto be the fee rate and set the variablefee_typeto be"fee_rate".
- Open a terminal and change directory to the work directory.
- Run the following command:
python create_nonstandard_transaction.py
- The output should contain the signed transaction as a hex byte sequence without spaces.
Note: Please go to the "Transactions created during this project" part of the Notes section to see an example of the output of this script.
I've already worked through Recipe 1: Setup.
Next:
- Gather the following pieces of information:
-- The txid of the previous transaction that transferred the unspent output to the input address.
-- The index of this unspent output in the previous transaction (the "previous_output_index"). This index is implicit and starts at zero.
-- The private key (in hex bytes) of the input address.
-- The bitcoin amount (or satoshi amount) contained within the unspent output. Let's call this the "input amount".
txid0:
bf7a16ef4bf8763b7bf7f02b03155c9a2f9b997d19e64a770bf3f7d3f24cc48b
In the earlier information about tx0, I see that:
- vout_sz = 23
i.e. there are 23 outputs.
One of these outputs is the one sent to the test address.
Searching through the earlier information for
1AGygbyEFYduWkkmZbbvirgS9kuBBMLJCP
I find:
"outputs": [
{
"value": 78989,
"script": "76a91465beb01aab71c9da76f38a383c65e3693dff54cc88ac",
"addresses": [
"1AGygbyEFYduWkkmZbbvirgS9kuBBMLJCP"
],
"script_type": "pay-to-pubkey-hash"
},
The particular output that I am interested in is the first one in the output list.
Outputs are implicitly 0-indexed, so this output has an index of 0.
So:
previous_output_index = 0
The private key in hex bytes of the input address is:
a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121
The satoshi amount contained in this output is shown in the datablock above, stored in the variable "value":
78989
Let's collect this information together:
1) txid of the previous transaction (txid0):
bf7a16ef4bf8763b7bf7f02b03155c9a2f9b997d19e64a770bf3f7d3f24cc48b
2) previous_output_index:
0
3) private key of the input address in hex bytes:
a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121
4) input amount (in satoshi):
78989
Next:
- Choose the following pieces of information:
-- The output address.
-- The bitcoin amount (or satoshi amount) that you wish to transfer to the output address. Let's call this the "output amount".
-- The change address. Currently, this is the same as the output address. Any value not assigned to an output address will be assigned to the change address. The fee will then be subtracted from the value assigned to the change address.
-- The fee (in satoshi) or fee_rate (in satoshi / byte). The fee must be an integer e.g."225". The fee_rate must be an integer e.g."1"or a float e.g."1.2". You should look up the range of fee rates for transactions that are currently being mined.
The output address is the final address (my current LocalBitcoins receiving address):
3QpwGejV9Dyai72HDhXZVkYBWioyuLVjti
The amount that I wish to transfer to the output address. Well, I'll set this to be the same as the input value, and the create_nonstandard_transaction.py script will subtract the fee from it.
So: output amount = input amount = 78989 (satoshi).
The change address. Currently, the only option supported is "change address is the single output address", so the change address is:
3QpwGejV9Dyai72HDhXZVkYBWioyuLVjti
I'll use the smallest fee rate in the set of reasonable fee rates reported by bitcoinfees.earn.com, which is 1 satoshi / byte. "Reasonable" means that transactions with this fee rate are currently being mined.
Let's collect this information together:
1) output address:
3QpwGejV9Dyai72HDhXZVkYBWioyuLVjti
2) output amount (satoshi):
78989
3) change address:
3QpwGejV9Dyai72HDhXZVkYBWioyuLVjti
4) fee rate (satoshi / byte):
1
Next:
- Generate a random value that is between 1 and 32 bytes long. This requires some work (e.g. using dice) and is not covered in this recipe.
Note: This random value will be used for creating the ECDSA signature of the transaction. Every ECDSA signature you ever make should use a different random value. Ideally a random value should be 32 bytes long. It can be raw bytes (printable ASCII characters) or hex bytes (hex characters). Hex characters must be lowercase. 1 byte is represented by 2 hex characters, so a 32-byte random value will be 64 hex characters long. I recommend hex bytes, as these can represent all possible random values. I find that raw byte random values are convenient during testing.
I'll use the second half of the entropy that I generated earlier, entropy-bytes-2, as the random value for creating the ECDSA signature of the transaction.
Entropy-bytes-2:
258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9
Next:
- Browse to the Downloadable Assets section of this article.
- Download the asset transaction.py.
- Download the asset nonstandard_bitcoin_functions.py
- Download the asset nonstandard_transaction.py.
- Download the asset create_nonstandard_transaction.py
- Move these assets into the work directory.
I've already downloaded the assets
- transaction.py
- nonstandard_bitcoin_functions.py
- nonstandard_transaction.py
- create_nonstandard_transaction.py
and moved them into the work directory.
Next:
- Open the file create_nonstandard_transaction.py in a text editor. Scroll to the section of text that lies between
##### START CONTROLS
and
##### END CONTROLS
- Set the variablerandom_valueto the random value.
- If the random value is in raw bytes, set the variablerandom_value_typeto"raw_bytes".
- If the random value is in hex bytes, set the variablerandom_value_typeto"hex_bytes".
- The variableinput_datais a dictionary that contains several other variables.
-- Set the value of the variableinput_data["txid"]to be the txid.
-- Set the value of the variableinput_data["previous_output_index"]to be the index of the unspent output.
-- Set the value of the variableinput_data["private_key_hex"]to be the private key.
-- If the input amount is in satoshi, set the value of the variableinput_data["satoshi_amount"]to be the input amount.
-- If the input amount is in bitcoin, set the value of the variableinput_data["bitcoin_amount"]to be the input amount.
Note: Currently, if both of the variablesinput_data["satoshi_amount"]andinput_data["bitcoin_amount"]are set, theninput_data["bitcoin_amount"]will take precedence. You can comment out the unused variable of the pair by placing a number sign ('#') at the start of the relevant line.
-- Set the value of the variableinput_data["input_type"]to be"p2pkh".
- The variableoutput_datais a dictionary that contains several other variables.
-- Set the value of the variableoutput_data["address"]to be the output address.
-- If the output amount is in satoshi, set the value of the variableoutput_data["satoshi_amount"]to be the output amount.
-- If the output amount is in bitcoin, set the value of the variableoutput_data["bitcoin_amount"]to be the output amount.
Note: Currently, if both of the variablesoutput_data["satoshi_amount"]andoutput_data["bitcoin_amount"]are set,output_data["bitcoin_amount"]will take precedence. You can comment out the unused variable of the pair by placing a number sign ('#') at the start of the relevant line.
-- Set the value of the variableoutput_data["output_type"]to be"p2sh".
- Set the variablechange_addressto be the change address.
- If you have chosen to set the fee as an absolute number of satoshi, set the variablefeeto be the fee amount and set the variablefee_typeto be"fee".
- If you have chosen to set the fee as a fee rate (in satoshi / byte), set the variablefee_rateto be the fee rate and set the variablefee_typeto be"fee_rate".
Done.
random_value
set to
"258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9"
.
random_value_type
set to
"hex_bytes"
.
input_data["txid"]
set to
"bf7a16ef4bf8763b7bf7f02b03155c9a2f9b997d19e64a770bf3f7d3f24cc48b"
.
input_data["previous_output_index"]
set to
"0"
.
input_data["private_key_hex"]
set to
"a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121"
.
input_data["satoshi_amount"]
set to
"78989"
.Comment out the unused variable
input_data["bitcoin_amount"]
by placing a number sign ('#') at the start of the relevant line. Note: It was actually already commented out.
input_data["input_type"]
set to
"p2pkh"
.
output_data["address"]
set to
"3QpwGejV9Dyai72HDhXZVkYBWioyuLVjti"
.
output_data["satoshi_amount"]
set to
"78989"
.Comment out the unused variable
output_data["bitcoin_amount"]
by placing a number sign ('#') at the start of the relevant line.
output_data["output_type"]
set to
"p2sh"
.
change_address
set to
"3QpwGejV9Dyai72HDhXZVkYBWioyuLVjti"
.
fee_rate
set to
"1"
.
fee_type
set to
"fee_rate"
. Results:
create_nonstandard_transaction.py
##### START CONTROLS | |
random_value = "258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9" | |
# random_value must be between 1 and 32 bytes. If random_value_type is "raw_bytes", then random_value must be between 1 and 32 ASCII characters. If random_value_type is "hex_bytes", then random_value must be an even number of hex characters and between 2 and 64 hex characters. | |
# Note: Every ECDSA signature (one for each input in a transaction) requires new random entropy. | |
# random_value_type options: ["raw_bytes", "hex_bytes"] | |
random_value_type = "hex_bytes" | |
input_data = { | |
"txid": "bf7a16ef4bf8763b7bf7f02b03155c9a2f9b997d19e64a770bf3f7d3f24cc48b", | |
"previous_output_index": "0", | |
"private_key_hex": "a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121", | |
"satoshi_amount": "78989", | |
#"bitcoin_amount": "0.00241777", | |
"input_type": "p2pkh", | |
} | |
output_data = { | |
"address": "3QpwGejV9Dyai72HDhXZVkYBWioyuLVjti", | |
"satoshi_amount": "78989", | |
#"bitcoin_amount": "0.00241", | |
"output_type": "p2sh", | |
} | |
change_address = "3QpwGejV9Dyai72HDhXZVkYBWioyuLVjti" | |
# note: the fee will be subtracted from the amount that is being sent to the change address. | |
fee = "225" # satoshi | |
fee_rate = "1" # satoshi / byte | |
# fee_type options: ["fee", "fee_rate"] | |
fee_type = "fee_rate" | |
##### END CONTROLS |
Finally:
- Open a terminal and change directory to the work directory.
- Run the following command:
python create_nonstandard_transaction.py
- The output should contain the signed transaction as a hex byte sequence without spaces.
In the terminal, in the work directory:
aineko:work stjohnpiano$ python create_nonstandard_transaction.py
### START CREATION OF NONSTANDARD BITCOIN TRANSACTION
- Fee type: fee_rate
- Fee rate: 1.0 (satoshi / byte)
- Number of inputs (i.e. as-yet-unspent outputs): 1
- Number of outputs: 1
- Change address: 3QpwGejV9Dyai72HDhXZVkYBWioyuLVjti
- Amount to be sent to the change address: 0.00078989
- Input addresses, with total-value-to-be-sent:
-- 1AGygbyEFYduWkkmZbbvirgS9kuBBMLJCP: 0.00078989
- Output addresses, with total-value-to-be-received:
-- 3QpwGejV9Dyai72HDhXZVkYBWioyuLVjti: 0.00078989
- Total value of all inputs: 0.00078989
- Total value of all outputs: 0.00078989
- Total value of all inputs exactly matches total value of all outputs.
- Estimated transaction size: 221 bytes
- Fee rate: 1.0 (satoshi / byte)
- Calculate 221 * 1.0 and round up to nearest satoshi.
- Final fee: 221 (satoshi)
- Final fee rate (using estimated transaction size): 1.0000 (satoshi per byte)
- Fee subtracted from amount to be sent to change address.
- New amount to be sent to change address: 78768 (satoshi)
Input 0:
Input (without signature):
- previous_output_hash: 8bc44cf2d3f7f30b774ae6197d999b2f9a5c15032bf0f77b3b76f84bef167abf
- previous_output_index: 00000000
- sequence: ffffffff
- private_key_hex: a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121
- public_key_hex: 049d8a4c3947fb596c2664218f279be9ba2c0421ca006888243877cdb0c0146202a2a2af299928cc634d58976db32023facf269c1fb7069d9af25c54c07e85eb8e
- script_length_scriptPubKey: 19
- scriptPubKey: 76a91465beb01aab71c9da76f38a383c65e3693dff54cc88ac
- script_length: None
- scriptSig: None
- address: 1AGygbyEFYduWkkmZbbvirgS9kuBBMLJCP
- previous_output_index_int: 0
- txid: bf7a16ef4bf8763b7bf7f02b03155c9a2f9b997d19e64a770bf3f7d3f24cc48b
Output 0:
Output:
- value: b033010000000000
- script_length: 17
- script: a914fdca5619962026e5d80a84d3e25f5cea931ce38587
- address: 3QpwGejV9Dyai72HDhXZVkYBWioyuLVjti
- bitcoin_amount: 0.00078768
- satoshi_amount: 78768
- redeem_script_hash_hex: fdca5619962026e5d80a84d3e25f5cea931ce385
Nonstandard Transaction (unsigned form):
- version: 01000000
- input_count: 01
- Input:
-- previous_output_hash: 8bc44cf2d3f7f30b774ae6197d999b2f9a5c15032bf0f77b3b76f84bef167abf
-- previous_output_index: 00000000
-- script_length: None
-- scriptSig: None
-- sequence: ffffffff
- output_count: 01
- Output [P2SH]:
-- value: b033010000000000
-- script_length: 17
-- script: a914fdca5619962026e5d80a84d3e25f5cea931ce38587
- block_lock_time: 00000000
Nonstandard Transaction (signable form):
- version: 01000000
- input_count: 01
- Input [to be used to sign this signable form]:
-- previous_output_hash: 8bc44cf2d3f7f30b774ae6197d999b2f9a5c15032bf0f77b3b76f84bef167abf
-- previous_output_index: 00000000
-- script_length_scriptPubKey: 19
-- scriptPubKey: 76a91465beb01aab71c9da76f38a383c65e3693dff54cc88ac
-- sequence: ffffffff
- output_count: 01
- Output [P2SH]:
-- value: b033010000000000
-- script_length: 17
-- script: a914fdca5619962026e5d80a84d3e25f5cea931ce38587
- block_lock_time: 00000000
- hash_type_4_byte: 01000000
Nonstandard Transaction (signed form):
- version: 01000000
- input_count: 01
- Input:
-- previous_output_hash: 8bc44cf2d3f7f30b774ae6197d999b2f9a5c15032bf0f77b3b76f84bef167abf
-- previous_output_index: 00000000
-- script_length: 8a
-- scriptSig: 4730440220419ecae5ca010db8a2c7ec04cdebaa58a921b0102034db08d6456087c793494e022048cf345bf504905a23021b3dbd95d7a53d8786678505a3c5cd72e03ea259df230141049d8a4c3947fb596c2664218f279be9ba2c0421ca006888243877cdb0c0146202a2a2af299928cc634d58976db32023facf269c1fb7069d9af25c54c07e85eb8e
-- sequence: ffffffff
- output_count: 01
- Output [P2SH]:
-- value: b033010000000000
-- script_length: 17
-- script: a914fdca5619962026e5d80a84d3e25f5cea931ce38587
- block_lock_time: 00000000
- hash_type_4_byte: 01000000
Signed transaction:
01000000018bc44cf2d3f7f30b774ae6197d999b2f9a5c15032bf0f77b3b76f84bef167abf000000008a4730440220419ecae5ca010db8a2c7ec04cdebaa58a921b0102034db08d6456087c793494e022048cf345bf504905a23021b3dbd95d7a53d8786678505a3c5cd72e03ea259df230141049d8a4c3947fb596c2664218f279be9ba2c0421ca006888243877cdb0c0146202a2a2af299928cc634d58976db32023facf269c1fb7069d9af25c54c07e85eb8effffffff01b03301000000000017a914fdca5619962026e5d80a84d3e25f5cea931ce385870000000001000000
### END CREATION OF BITCOIN TRANSACTION
Woops. The estimated size of a transaction with one P2PKH input and one P2SH output should actually have been 221 bytes, not 223 bytes.
The signed transaction (tx1):
01000000018bc44cf2d3f7f30b774ae6197d999b2f9a5c15032bf0f77b3b76f84bef167abf000000008a4730440220419ecae5ca010db8a2c7ec04cdebaa58a921b0102034db08d6456087c793494e022048cf345bf504905a23021b3dbd95d7a53d8786678505a3c5cd72e03ea259df230141049d8a4c3947fb596c2664218f279be9ba2c0421ca006888243877cdb0c0146202a2a2af299928cc634d58976db32023facf269c1fb7069d9af25c54c07e85eb8effffffff01b03301000000000017a914fdca5619962026e5d80a84d3e25f5cea931ce385870000000001000000
Let's see if a blockchain information service can decode this transaction.
Browse to:
live.blockcypher.com/btc/decodetx
Paste the signed transaction into the text box named "Transaction Hex". Network is set to "Bitcoin".
Click "Decode Transaction".
Result:
{
"addresses": [
"1AGygbyEFYduWkkmZbbvirgS9kuBBMLJCP",
"3QpwGejV9Dyai72HDhXZVkYBWioyuLVjti"
],
"block_height": -1,
"block_index": -1,
"confirmations": 0,
"double_spend": false,
"fees": 221,
"hash": "dfd0304f6bd018d8d97e63e387eeab0696b61d9632d50d28f1618daf434a7ba3",
"inputs": [
{
"addresses": [
"1AGygbyEFYduWkkmZbbvirgS9kuBBMLJCP"
],
"age": 547832,
"output_index": 0,
"output_value": 78989,
"prev_hash": "bf7a16ef4bf8763b7bf7f02b03155c9a2f9b997d19e64a770bf3f7d3f24cc48b",
"script": "4730440220419ecae5ca010db8a2c7ec04cdebaa58a921b0102034db08d6456087c793494e022048cf345bf504905a23021b3dbd95d7a53d8786678505a3c5cd72e03ea259df230141049d8a4c3947fb596c2664218f279be9ba2c0421ca006888243877cdb0c0146202a2a2af299928cc634d58976db32023facf269c1fb7069d9af25c54c07e85eb8e",
"script_type": "pay-to-pubkey-hash",
"sequence": 4294967295
}
],
"outputs": [
{
"addresses": [
"3QpwGejV9Dyai72HDhXZVkYBWioyuLVjti"
],
"script": "a914fdca5619962026e5d80a84d3e25f5cea931ce38587",
"script_type": "pay-to-script-hash",
"value": 78768
}
],
"preference": "low",
"received": "2018-10-29T14:48:24.765167289Z",
"relayed_by": "54.167.244.83",
"size": 221,
"total": 78768,
"ver": 1,
"vin_sz": 1,
"vout_sz": 1
}
Looks good. No immediate error reported.
Let's broadcast the signed transaction.
Browse to:
live.blockcypher.com/btc/pushtx
Paste the signed transaction into the text box named "Transaction Hex". Network is set to "Bitcoin".
Click "Broadcast Transaction".
Result:
Transaction page loads:
live.blockcypher.com/btc/tx/dfd0304f6bd018d8d97e63e387eeab0696b61d9632d50d28f1618daf434a7ba3
Details:
- Transaction Successfully Broadcst
- AMOUNT TRANSACTED: 0.00078768 BTC
- FEES: 0.00000221 BTC
- Confirmations: 0/6
- Miner Preference: LOW
- Size: 221 bytes
- Version: 1
- Relayed By: 54.167.244.83
Time: 14:53
Log into LocalBitcoins.
Browse to Wallet / Receive bitcoins.
An incoming transaction is listed.
- Time: 2:51 pm
- Address:
3QpwGejV9Dyai72HDhXZVkYBWioyuLVjti
- BTC: 0.00078768
- Confirmed in about: 30 minutes
In the previous project Creating and signing a standard raw Bitcoin transaction: Iteration #2, a similar transaction with a similar fee rate took about 6 hours to be mined and be confirmed 6 times.
Time: 23:24
Refresh transaction page.
- Confirmations: 0/6
Time: 08:46 (next day)
Refresh transaction page.
- Confirmations: 6+
Click "Advanced Details". Click "API Call".
Result: tx0
{
"block_hash": "000000000000000000204cb0026f5c7dd1ba847144528c79589493ff005351c3",
"block_height": 547916,
"block_index": 1771,
"hash": "dfd0304f6bd018d8d97e63e387eeab0696b61d9632d50d28f1618daf434a7ba3",
"hex": "01000000018bc44cf2d3f7f30b774ae6197d999b2f9a5c15032bf0f77b3b76f84bef167abf000000008a4730440220419ecae5ca010db8a2c7ec04cdebaa58a921b0102034db08d6456087c793494e022048cf345bf504905a23021b3dbd95d7a53d8786678505a3c5cd72e03ea259df230141049d8a4c3947fb596c2664218f279be9ba2c0421ca006888243877cdb0c0146202a2a2af299928cc634d58976db32023facf269c1fb7069d9af25c54c07e85eb8effffffff01b03301000000000017a914fdca5619962026e5d80a84d3e25f5cea931ce3858700000000",
"addresses": [
"1AGygbyEFYduWkkmZbbvirgS9kuBBMLJCP",
"3QpwGejV9Dyai72HDhXZVkYBWioyuLVjti"
],
"total": 78768,
"fees": 221,
"size": 221,
"preference": "low",
"relayed_by": "54.167.244.83",
"confirmed": "2018-10-30T03:28:42Z",
"received": "2018-10-29T14:51:09.984Z",
"ver": 1,
"double_spend": false,
"vin_sz": 1,
"vout_sz": 1,
"confirmations": 29,
"confidence": 1,
"inputs": [
{
"prev_hash": "bf7a16ef4bf8763b7bf7f02b03155c9a2f9b997d19e64a770bf3f7d3f24cc48b",
"output_index": 0,
"script": "4730440220419ecae5ca010db8a2c7ec04cdebaa58a921b0102034db08d6456087c793494e022048cf345bf504905a23021b3dbd95d7a53d8786678505a3c5cd72e03ea259df230141049d8a4c3947fb596c2664218f279be9ba2c0421ca006888243877cdb0c0146202a2a2af299928cc634d58976db32023facf269c1fb7069d9af25c54c07e85eb8e",
"output_value": 78989,
"sequence": 4294967295,
"addresses": [
"1AGygbyEFYduWkkmZbbvirgS9kuBBMLJCP"
],
"script_type": "pay-to-pubkey-hash",
"age": 547832
}
],
"outputs": [
{
"value": 78768,
"script": "a914fdca5619962026e5d80a84d3e25f5cea931ce38587",
"spent_by": "861d5c021965e253f62ff83fd4d8571f13ab1f444d41ec0ed5e4b1cb6acce255",
"addresses": [
"3QpwGejV9Dyai72HDhXZVkYBWioyuLVjti"
],
"script_type": "pay-to-script-hash"
}
]
}
Key details:
- block_hash:
000000000000000000204cb0026f5c7dd1ba847144528c79589493ff005351c3
- block_height: 547916
- confirmed: 2018-10-30T03:28:42Z
- received: 2018-10-29T14:51:09.984Z
- txid:
dfd0304f6bd018d8d97e63e387eeab0696b61d9632d50d28f1618daf434a7ba3
Using the times from blockcypher.com:
- Broadcast time: 2018-10-29 14:51
- Confirmation time: 2018-10-30 03:28
Time taken ~= 12.5 hours
Log on to LocalBitcoins.
Go to Wallet / Transactions.
- Latest received transaction:
-- Datetime: 10/30/2018 03:46
-- Received BTC: 0.00063768
-- Description: Deposit to 3QpwGe......
-- Deposit Fee BTC: 0.00015000
The test address has now been validated. I have moved some bitcoin into it and then successfully retrieved it.
I can now move a large amount of bitcoin into this address and be certain that I can retrieve it. Even if I later discover an error in the code stack used to create and sign a transaction, I know that eventually this error could be fixed and a valid transaction could be created. I would only risk a temporary lack of access to the bitcoin, not its permanent loss.
Good.
That's the end of this project.