Skip to content

Instantly share code, notes, and snippets.

@ObjSal
Last active June 7, 2019 14:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ObjSal/fa8a340b3e8db4a73b9b6cbf88cf9b94 to your computer and use it in GitHub Desktop.
Save ObjSal/fa8a340b3e8db4a73b9b6cbf88cf9b94 to your computer and use it in GitHub Desktop.
Validate PKH (or SHA256/RIPEMD160 hash) with base58check.py to detect typing errors (code created from the theory of Grokking Bitcoin)
#!/usr/bin/env python3
#
# Copyright ByteApps 2019
# Author: Salvador Guerrero (salvador.icc@gmail.com)
#
# Tested by encoding and decoding the input with the following example:
# $ echo 5f2613791b36f667fdb8e95608b55e3df4c5f9eb | \
# ./base58check.py -encode | \
# ./base58check.py -decode
# will output:
# 5f2613791b36f667fdb8e95608b55e3df4c5f9eb
import sys
import hashlib
import binascii
from baseconvert import base58encode
from baseconvert import base58decode
# Commands
CMD_ENCODE = "-encode"
CMD_DECODE = "-decode"
# Constants
VERSION = "00"
def sha256double(line):
# Ref: 1 https://twitter.com/kallerosenbaum/status/1136906952271962112
# Ref: 2 https://twitter.com/kallerosenbaum/status/1136907162498818048
line = binascii.a2b_hex(line.encode('ascii'))
hash1 = hashlib.sha256(line)
hash2 = hashlib.sha256(hash1.digest())
return hash2.hexdigest()
def encode(line):
checksum = sha256double(VERSION + line)
checksumed_line = line + checksum[:8]
b58 = base58encode(checksumed_line)
# Add 1 at the beginning
print("1" + b58)
def decode(line):
# Remove the prefix "1"
line = line[1:]
b16 = base58decode(line)
# Get the inline checksum
inline_checksum = b16[len(b16)-8:]
# Get the inline hash
inline_hash = b16[:len(b16)-8]
# Calculate the checksum for the versioned hash
checksum = sha256double(VERSION + inline_hash)
# Compare the inline checksum with the generated one
# if the match it means that it's a valid decode, then return the hash
if inline_checksum != checksum[:8]:
return 1
print(inline_hash)
def main():
if len(sys.argv) < 2:
print("\nMissing Argument!\n")
print("Available Options:\n\t-encode\n\t--decode")
print("Example:")
print("\techo \"abc\" | ./base58check.py -encode or -decode")
exit(1)
cmd = str(sys.argv[1])
line = sys.stdin.readline().strip()
if cmd == CMD_ENCODE:
encode(line)
elif cmd == CMD_DECODE:
decode(line)
if __name__ == "__main__":
# stuff only to run when not called via 'import' here
main()
#!/usr/bin/env python3
#
# Copyright ByteApps 2019
# Author: Salvador Guerrero (salvador.icc@gmail.com)
#
# Tested with sample from Grokking Bitcoin sample in page 76
# $ echo 5f2613791b36f667fdb8e95608b55e3df4c5f9eb12181e60 | \
# ./baseconvert.py -16to10 | \
# ./baseconvert.py -10to58
# will output:
# 9g6oo8foQF5jfqK9gH2bLkFNwgCenRBPD
import sys
import math
CMD_BASE16_TO_10 = "-16to10"
CMD_BASE10_TO_16 = "-10to16"
CMD_BASE10_TO_58 = "-10to58"
CMD_BASE58_TO_10 = "-58to10"
base16 = "0123456789abcdef"
base58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
def decode(base, map, line):
leng = len(line.lower())
i = leng - 1
total = 0
while i >= 0:
char = line[i]
# b10 is the base10 number for the given base number,
# it is also the position of the base char
b10 = map.find(char)
total += b10 * pow(base, leng - 1 - i)
i -= 1
return total
def encode(base, map, line):
n = int(line)
total = ""
while n > 0:
remainder = n % base
total = map[remainder] + total
# Big number division with //
n = int(n // base)
return total
def convert16to10(line):
return decode(16, base16, line)
def convert10to16(line):
return encode(16, base16, line)
def convert10to58(line):
return encode(58, base58, line)
def convert58to10(line):
return decode(58, base58, line)
def base58encode(line):
return convert10to58(convert16to10(line))
def base58decode(line):
return convert10to16(convert58to10(line))
# terminal functions
def _convert16to10(line):
print(decode(16, base16, line))
def _convert10to16(line):
print(encode(16, base16, line))
def _convert10to58(line):
print(encode(58, base58, line))
def _convert58to10(line):
print(decode(58, base58, line))
def main():
if len(sys.argv) < 2:
print("\nMissing Argument!\n")
print("Available Options:\n\t-16to10\n\t-10to58\n\t-10to16\n\t-58to10\n\t-reverse")
print("Example:")
print("\techo \"abc\" | ./base.py -16to10 or -10to58 or -10to16 or -58to10")
exit(1)
cmd = str(sys.argv[1])
line = sys.stdin.readline().strip()
if cmd == CMD_BASE16_TO_10:
_convert16to10(line)
elif cmd == CMD_BASE10_TO_58:
_convert10to58(line)
elif cmd == CMD_BASE10_TO_16:
_convert10to16(line)
elif cmd == CMD_BASE58_TO_10:
_convert58to10(line)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment