Last active
June 7, 2019 14:35
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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