Goal
Display hex bytes in a form that allows a user to easily read them on the screen of one computer and type them into another computer.
Contents
- Goal
- Contents
- Summary
- Downloadable Assets
- Recipe For Manually Copying A Bitcoin Transaction Generated Offline To An Online Computer
- Examples
- Further Work
- Project Log
Summary
I developed a script that prints a hex byte string, inserting spaces and newlines to make it more readable.
For details about how to use this script, please see the section Recipe For Manually Copying A Bitcoin Transaction Generated Offline To An Online Computer.
Downloadable Assets
Asset: A script that prints a hex byte string, inserting spaces and newlines to make it more readable.
display_hex_bytes.py
Recipe For Manually Copying A Bitcoin Transaction Generated Offline To An Online Computer
Initial conditions:
- You must have Python 2.7.x installed on your offline computer. The code asset was developed and 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.
1) Obtain the script display_hex_bytes.py from the Downloadable Assets section of this article.
2) Copy display_hex_bytes.py onto the offline computer. How to do this is not specified in this recipe.
3) Generate the Bitcoin transaction offline. How to do this is not specified in this recipe.
4) Open the script display_hex_bytes.py in a text editor. Scroll to lines 12-15, which should be the section of text that lies between
##### START CONTROLS
and
##### END CONTROLS
5) Set the variable
hex_bytes
to be the hex byte string form of the signed transaction. This string must consist only of hex characters (the ten digit characters "0123456789" and the lower-case alphabetical characters "abcdef"). 6) Set the variable
group_size
to be your desired number of characters per group. Groups are divided by spacing. 7) Set the variable
groups_per_line
to be your desired number of character groups to be printed on each line.8) Set the variable
spaces_between_groups
to be your desired number of space characters in the spacing between each group. 9) Open a terminal and change directory to the directory containing display_hex_bytes.py.
10) Run the following command:
python display_hex_bytes.py
11) The transaction should now be displayed on the screen of the offline computer in a readable format. Manually type the transaction into a text file on an online computer. The rest of this recipe will assume that this text file was named "tx_copied.txt".
12) Transform the transaction back into its original form by running the following sequence of bash commands:
- Remove spaces and newlines from the file and store the result in a new file:
cat tx_copied.txt | tr -d ' \n' > tx.txt
- Add an empty line (i.e. a newline) to the end of the file:
echo '' >> tx.txt
- Print the contents of the resulting file:
cat tx.txt
Please see the Examples section of this article to see a sequence of example results produced by this recipe.
Examples
The original signed transaction:
01000000018bc44cf2d3f7f30b774ae6197d999b2f9a5c15032bf0f77b3b76f84bef167abf000000008a4730440220419ecae5ca010db8a2c7ec04cdebaa58a921b0102034db08d6456087c793494e022048cf345bf504905a23021b3dbd95d7a53d8786678505a3c5cd72e03ea259df230141049d8a4c3947fb596c2664218f279be9ba2c0421ca006888243877cdb0c0146202a2a2af299928cc634d58976db32023facf269c1fb7069d9af25c54c07e85eb8effffffff01b03301000000000017a914fdca5619962026e5d80a84d3e25f5cea931ce385870000000001000000
Settings in display_hex_bytes.py:
display_hex_bytes.py
### START CONTROLS | |
hex_bytes = "01000000018bc44cf2d3f7f30b774ae6197d999b2f9a5c15032bf0f77b3b76f84bef167abf000000008a4730440220419ecae5ca010db8a2c7ec04cdebaa58a921b0102034db08d6456087c793494e022048cf345bf504905a23021b3dbd95d7a53d8786678505a3c5cd72e03ea259df230141049d8a4c3947fb596c2664218f279be9ba2c0421ca006888243877cdb0c0146202a2a2af299928cc634d58976db32023facf269c1fb7069d9af25c54c07e85eb8effffffff01b03301000000000017a914fdca5619962026e5d80a84d3e25f5cea931ce385870000000001000000" | |
group_size = 4 # number of characters in a group | |
groups_per_line = 4 | |
spaces_between_groups = 2 | |
### END CONTROLS |
Running the script:
aineko:work stjohnpiano$ python display_hex_bytes.py
0100 0000 018b c44c
f2d3 f7f3 0b77 4ae6
197d 999b 2f9a 5c15
032b f0f7 7b3b 76f8
4bef 167a bf00 0000
008a 4730 4402 2041
9eca e5ca 010d b8a2
c7ec 04cd ebaa 58a9
21b0 1020 34db 08d6
4560 87c7 9349 4e02
2048 cf34 5bf5 0490
5a23 021b 3dbd 95d7
a53d 8786 6785 05a3
c5cd 72e0 3ea2 59df
2301 4104 9d8a 4c39
47fb 596c 2664 218f
279b e9ba 2c04 21ca
0068 8824 3877 cdb0
c014 6202 a2a2 af29
9928 cc63 4d58 976d
b320 23fa cf26 9c1f
b706 9d9a f25c 54c0
7e85 eb8e ffff ffff
01b0 3301 0000 0000
0017 a914 fdca 5619
9620 26e5 d80a 84d3
e25f 5cea 931c e385
8700 0000 0001 0000
00
[Here, assume that the transaction shown has been manually typed into a file called "tx_copied.txt".]
Transforming the transaction back into its original form by running the specified sequence of bash commands:
[Remove spaces and newlines from the file and store the result in a new file.]
[Add an empty line (i.e. a newline) to the end of the file.]
[Print the contents of the resulting file.]
01000000018bc44cf2d3f7f30b774ae6197d999b2f9a5c15032bf0f77b3b76f84bef167abf000000008a4730440220419ecae5ca010db8a2c7ec04cdebaa58a921b0102034db08d6456087c793494e022048cf345bf504905a23021b3dbd95d7a53d8786678505a3c5cd72e03ea259df230141049d8a4c3947fb596c2664218f279be9ba2c0421ca006888243877cdb0c0146202a2a2af299928cc634d58976db32023facf269c1fb7069d9af25c54c07e85eb8effffffff01b03301000000000017a914fdca5619962026e5d80a84d3e25f5cea931ce385870000000001000000
aineko:work stjohnpiano$ cat tx_copied.txt | tr -d ' \n' > tx.txt
[Add an empty line (i.e. a newline) to the end of the file.]
aineko:work stjohnpiano$ echo '' >> tx.txt
[Print the contents of the resulting file.]
aineko:work stjohnpiano$ cat tx.txt
01000000018bc44cf2d3f7f30b774ae6197d999b2f9a5c15032bf0f77b3b76f84bef167abf000000008a4730440220419ecae5ca010db8a2c7ec04cdebaa58a921b0102034db08d6456087c793494e022048cf345bf504905a23021b3dbd95d7a53d8786678505a3c5cd72e03ea259df230141049d8a4c3947fb596c2664218f279be9ba2c0421ca006888243877cdb0c0146202a2a2af299928cc634d58976db32023facf269c1fb7069d9af25c54c07e85eb8effffffff01b03301000000000017a914fdca5619962026e5d80a84d3e25f5cea931ce385870000000001000000
Further Work
Add line numbers to the beginning of each line, with a separating character.
Example:
0000: 0100 0000 018b c44c
0001: f2d3 f7f3 0b77 4ae6
0002: 197d 999b 2f9a 5c15
0001: f2d3 f7f3 0b77 4ae6
0002: 197d 999b 2f9a 5c15
Add a checksum to the end of each line, and a final checksum for the entire transaction in an extra final line. Then, if a human transcriber were to make a transcription error, a script could mechanically locate the line in which the error existed.
Project Log
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
During the project Using a transaction to validate a Bitcoin address, I created the following signed Bitcoin transaction:
01000000018bc44cf2d3f7f30b774ae6197d999b2f9a5c15032bf0f77b3b76f84bef167abf000000008a4730440220419ecae5ca010db8a2c7ec04cdebaa58a921b0102034db08d6456087c793494e022048cf345bf504905a23021b3dbd95d7a53d8786678505a3c5cd72e03ea259df230141049d8a4c3947fb596c2664218f279be9ba2c0421ca006888243877cdb0c0146202a2a2af299928cc634d58976db32023facf269c1fb7069d9af25c54c07e85eb8effffffff01b03301000000000017a914fdca5619962026e5d80a84d3e25f5cea931ce385870000000001000000
If I had created this transaction on an offline computer, I would want to be able to transfer it to an online computer (for broadcasting) without using a memory stick.
One way to do this is to display the signed transaction string on the screen of the offline computer and manually type it into a text file on the online computer.
The whole transaction in one string is a bit hard to copy. It's easy to lose your place in the string as you go.
It would be easier if the transaction were displayed in small strings of a specific size.
So, I'll develop a script that takes a hex byte string as an input, formats it in a specified way, and prints the result to the screen.
[development occurs here]
display_hex_bytes.py
### DESCRIPTION | |
# - This script: | |
# -- divides a hex byte string into groups of characters. | |
# -- prints these groups to the screen, including a particular number of groups on each line. | |
def main(): | |
### START CONTROLS | |
hex_bytes = "01000000018bc44cf2d3f7f30b774ae6197d999b2f9a5c15032bf0f77b3b76f84bef167abf000000008a4730440220419ecae5ca010db8a2c7ec04cdebaa58a921b0102034db08d6456087c793494e022048cf345bf504905a23021b3dbd95d7a53d8786678505a3c5cd72e03ea259df230141049d8a4c3947fb596c2664218f279be9ba2c0421ca006888243877cdb0c0146202a2a2af299928cc634d58976db32023facf269c1fb7069d9af25c54c07e85eb8effffffff01b03301000000000017a914fdca5619962026e5d80a84d3e25f5cea931ce385870000000001000000" | |
group_size = 4 # number of characters in a group | |
groups_per_line = 4 | |
spaces_between_groups = 2 | |
### END CONTROLS | |
### START PROCESSING | |
validate_hex(hex_bytes) | |
n = len(hex_bytes) # total number of characters | |
spacing = " " * spaces_between_groups | |
chars_per_line = group_size * groups_per_line | |
for i in range(0, n, chars_per_line): | |
section = hex_bytes[i:i+chars_per_line] | |
m = len(section) # number of characters in a line. | |
# note: the last line may not contain exactly chars_per_line characters. | |
line = "" | |
for j in range(0, m, group_size): | |
p = i + j # p = position | |
group = hex_bytes[p:p+group_size] | |
line += "%s%s" % (group, spacing) | |
print line | |
### END PROCESSING | |
def validate_hex(input): | |
validate_string(input) | |
if not is_hex(input): | |
message = "Input must consist only of hex characters. Input: %s" % input | |
stop(message) | |
# 2 hex characters represents 1 byte, so input must have an even number of characters. | |
if len(input) % 2 == 0: | |
return True | |
else: | |
message = "Input must have an even number of hex characters. Input: %s" % input | |
stop(message) | |
message = "Unknown problem in function validate_hex()." | |
stop(message) | |
def is_hex(input): | |
hex_characters = "0123456789abcdef" | |
for c in input: # c = character | |
if c not in hex_characters: | |
return False | |
return True | |
def validate_string(input): | |
if isinstance(input, str): | |
return True | |
message = "Input is not a string. Input type is: %s" % type(input) | |
stop(message) | |
def stop(message): | |
# raise an Exception to get a traceback. | |
raise Exception("\n\nERROR: %s\n" % message) | |
if __name__ == '__main__': main() |
Let's run it.
aineko:work stjohnpiano$ python display_hex_bytes.py
0100 0000 018b c44c
f2d3 f7f3 0b77 4ae6
197d 999b 2f9a 5c15
032b f0f7 7b3b 76f8
4bef 167a bf00 0000
008a 4730 4402 2041
9eca e5ca 010d b8a2
c7ec 04cd ebaa 58a9
21b0 1020 34db 08d6
4560 87c7 9349 4e02
2048 cf34 5bf5 0490
5a23 021b 3dbd 95d7
a53d 8786 6785 05a3
c5cd 72e0 3ea2 59df
2301 4104 9d8a 4c39
47fb 596c 2664 218f
279b e9ba 2c04 21ca
0068 8824 3877 cdb0
c014 6202 a2a2 af29
9928 cc63 4d58 976d
b320 23fa cf26 9c1f
b706 9d9a f25c 54c0
7e85 eb8e ffff ffff
01b0 3301 0000 0000
0017 a914 fdca 5619
9620 26e5 d80a 84d3
e25f 5cea 931c e385
8700 0000 0001 0000
00
Excellent.
Now, let's imagine I've generated this transaction on an offline computer, and I've typed it into a text file on an online computer.
I would next wish to remove spaces and newlines, so that the reconstituted hex string would be ready for broadcast.
Copy the result above into a text file.
tx_copied.txt
0100 0000 018b c44c
f2d3 f7f3 0b77 4ae6
197d 999b 2f9a 5c15
032b f0f7 7b3b 76f8
4bef 167a bf00 0000
008a 4730 4402 2041
9eca e5ca 010d b8a2
c7ec 04cd ebaa 58a9
21b0 1020 34db 08d6
4560 87c7 9349 4e02
2048 cf34 5bf5 0490
5a23 021b 3dbd 95d7
a53d 8786 6785 05a3
c5cd 72e0 3ea2 59df
2301 4104 9d8a 4c39
47fb 596c 2664 218f
279b e9ba 2c04 21ca
0068 8824 3877 cdb0
c014 6202 a2a2 af29
9928 cc63 4d58 976d
b320 23fa cf26 9c1f
b706 9d9a f25c 54c0
7e85 eb8e ffff ffff
01b0 3301 0000 0000
0017 a914 fdca 5619
9620 26e5 d80a 84d3
e25f 5cea 931c e385
8700 0000 0001 0000
00
Remove spaces and newlines from the file and store the result in a new file.
aineko:work stjohnpiano$ cat tx_copied.txt | tr -d ' \n' > tx.txt
[add an empty line (i.e. a newline) to the end of the file]
aineko:work stjohnpiano$ echo -e '' >> tx.txt
aineko:work stjohnpiano$ cat tx.txt
01000000018bc44cf2d3f7f30b774ae6197d999b2f9a5c15032bf0f77b3b76f84bef167abf000000008a4730440220419ecae5ca010db8a2c7ec04cdebaa58a921b0102034db08d6456087c793494e022048cf345bf504905a23021b3dbd95d7a53d8786678505a3c5cd72e03ea259df230141049d8a4c3947fb596c2664218f279be9ba2c0421ca006888243877cdb0c0146202a2a2af299928cc634d58976db32023facf269c1fb7069d9af25c54c07e85eb8effffffff01b03301000000000017a914fdca5619962026e5d80a84d3e25f5cea931ce385870000000001000000
Reconstituted transaction:
01000000018bc44cf2d3f7f30b774ae6197d999b2f9a5c15032bf0f77b3b76f84bef167abf000000008a4730440220419ecae5ca010db8a2c7ec04cdebaa58a921b0102034db08d6456087c793494e022048cf345bf504905a23021b3dbd95d7a53d8786678505a3c5cd72e03ea259df230141049d8a4c3947fb596c2664218f279be9ba2c0421ca006888243877cdb0c0146202a2a2af299928cc634d58976db32023facf269c1fb7069d9af25c54c07e85eb8effffffff01b03301000000000017a914fdca5619962026e5d80a84d3e25f5cea931ce385870000000001000000
So, the transaction could now be broadcast.
Let's double-check the process by confirming that the transaction hasn't been changed during its division and reconstitution.
[original tx]
[tx produced by the process in this project]
equal
aineko:work stjohnpiano$ s1="01000000018bc44cf2d3f7f30b774ae6197d999b2f9a5c15032bf0f77b3b76f84bef167abf000000008a4730440220419ecae5ca010db8a2c7ec04cdebaa58a921b0102034db08d6456087c793494e022048cf345bf504905a23021b3dbd95d7a53d8786678505a3c5cd72e03ea259df230141049d8a4c3947fb596c2664218f279be9ba2c0421ca006888243877cdb0c0146202a2a2af299928cc634d58976db32023facf269c1fb7069d9af25c54c07e85eb8effffffff01b03301000000000017a914fdca5619962026e5d80a84d3e25f5cea931ce385870000000001000000"
[tx produced by the process in this project]
aineko:work stjohnpiano$ s2="01000000018bc44cf2d3f7f30b774ae6197d999b2f9a5c15032bf0f77b3b76f84bef167abf000000008a4730440220419ecae5ca010db8a2c7ec04cdebaa58a921b0102034db08d6456087c793494e022048cf345bf504905a23021b3dbd95d7a53d8786678505a3c5cd72e03ea259df230141049d8a4c3947fb596c2664218f279be9ba2c0421ca006888243877cdb0c0146202a2a2af299928cc634d58976db32023facf269c1fb7069d9af25c54c07e85eb8effffffff01b03301000000000017a914fdca5619962026e5d80a84d3e25f5cea931ce385870000000001000000"
aineko:work stjohnpiano$ [[ "$s1" = "$s2" ]] && echo equal || echo not-equal
equal
Excellent. The transaction was not changed by the process.