create coinbase transaction

This commit is contained in:
4tochka 2018-03-27 00:23:26 +04:00
parent 3edac78c3e
commit 9cbec80963
13 changed files with 603 additions and 188 deletions

1
doc/addresses.md Normal file
View File

@ -0,0 +1 @@
![Address map](img/address_map.svg)

View File

@ -83,6 +83,7 @@ class Script():
self.data = b''
self.type = "NON_STANDARD"
self.ntype = 7
self.witness_version = None
self.op_sig_count = 0
if coinbase:
self.pattern = b"<coinbase>"
@ -148,11 +149,13 @@ class Script():
self.type = "P2WPKH"
self.op_sig_count = 1
self.ntype = 5
self.address.append(b"\x00"+self.script[1].data)
self.witness_version = 0
self.address.append(self.script[1].data)
elif self.pattern == "OP_0 <32>":
self.type = "P2WSH"
self.ntype = 6
self.address.append(b"\x00"+self.script[1].data)
self.witness_version = 0
self.address.append(self.script[1].data)
@ -317,6 +320,13 @@ class Transaction():
self.tx_in_count += 1
self.recalculate_txid()
def add_output(self, amount, script):
if type(script)==str:
script = unhexlify(script)
self.tx_out.append(Output(amount,script))
self.tx_out_count += 1
self.recalculate_txid()
def add_P2SH_output(self, amount, p2sh_address):
if type(p2sh_address)==str:
p2sh_address = decode_base58(p2sh_address)[1:-4]

76
pybtc/constants.py Normal file
View File

@ -0,0 +1,76 @@
from secp256k1 import lib as secp256k1
from secp256k1 import ffi
import random
SIGHASH_ALL = 0x00000001
SIGHASH_NONE = 0x00000002
SIGHASH_SINGLE = 0x00000003
SIGHASH_ANYONECANPAY = 0x00000080
MAX_INT_PRIVATE_KEY = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
MAINNET_ADDRESS_BYTE_PREFIX = b'\x00'
TESTNET_ADDRESS_BYTE_PREFIX = b'\x6f'
MAINNET_SCRIPT_ADDRESS_BYTE_PREFIX = b'\x05'
TESTNET_SCRIPT_ADDRESS_BYTE_PREFIX = b'\xc4'
MAINNET_SEGWIT_ADDRESS_BYTE_PREFIX = b'\x03\x03\x00\x02\x03'
TESTNET_SEGWIT_ADDRESS_BYTE_PREFIX = b'\x03\x03\x00\x14\x02'
MAINNET_ADDRESS_PREFIX = '1'
TESTNET_ADDRESS_PREFIX = 'm'
TESTNET_ADDRESS_PREFIX_2 = 'n'
MAINNET_SCRIPT_ADDRESS_PREFIX = '3'
TESTNET_SCRIPT_ADDRESS_PREFIX = '2'
MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX = '5'
MAINNET_PRIVATE_KEY_COMPRESSED_PREFIX = 'K'
MAINNET_PRIVATE_KEY_COMPRESSED_PREFIX_2 = 'L'
TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX = '9'
TESTNET_PRIVATE_KEY_COMPRESSED_PREFIX = 'c'
ADDRESS_PREFIX_LIST = (MAINNET_ADDRESS_PREFIX,
TESTNET_ADDRESS_PREFIX,
TESTNET_ADDRESS_PREFIX_2,
MAINNET_SCRIPT_ADDRESS_PREFIX,
TESTNET_SCRIPT_ADDRESS_PREFIX)
PRIVATE_KEY_PREFIX_LIST = (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX,
MAINNET_PRIVATE_KEY_COMPRESSED_PREFIX,
MAINNET_PRIVATE_KEY_COMPRESSED_PREFIX_2,
TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX,
TESTNET_PRIVATE_KEY_COMPRESSED_PREFIX)
MAINNET_PRIVATE_KEY_BYTE_PREFIX = b'\x80'
TESTNET_PRIVATE_KEY_BYTE_PREFIX = b'\xef'
MAINNET_SEGWIT_ADDRESS_PREFIX = 'bc'
TESTNET_SEGWIT_ADDRESS_PREFIX = 'tb'
EC_COMPRESSED = secp256k1.SECP256K1_EC_COMPRESSED
EC_UNCOMPRESSED = secp256k1.SECP256K1_EC_UNCOMPRESSED
FLAG_SIGN = secp256k1.SECP256K1_CONTEXT_SIGN
FLAG_VERIFY = secp256k1.SECP256K1_CONTEXT_VERIFY
ALL_FLAGS = FLAG_SIGN | FLAG_VERIFY
NO_FLAGS = secp256k1.SECP256K1_CONTEXT_NONE
HAS_RECOVERABLE = hasattr(secp256k1, 'secp256k1_ecdsa_sign_recoverable')
HAS_SCHNORR = hasattr(secp256k1, 'secp256k1_schnorr_sign')
HAS_ECDH = hasattr(secp256k1, 'secp256k1_ecdh')
ECDSA_CONTEXT_SIGN = secp256k1.secp256k1_context_create(FLAG_SIGN)
ECDSA_CONTEXT_VERIFY = secp256k1.secp256k1_context_create(FLAG_VERIFY)
ECDSA_CONTEXT_ALL = secp256k1.secp256k1_context_create(ALL_FLAGS)
secp256k1.secp256k1_context_randomize(ECDSA_CONTEXT_SIGN,
random.SystemRandom().randint(0,MAX_INT_PRIVATE_KEY).to_bytes(32,byteorder="big"))
SCRIPT_TYPES = { "P2PKH": 0,
"P2SH" : 1,
"PUBKEY": 2,
"NULL_DATA": 3,
"MULTISIG": 4,
"P2WPKH": 5,
"P2WSH": 6,
"NON_STANDART": 7
}

119
pybtc/encode.py Normal file
View File

@ -0,0 +1,119 @@
from binascii import hexlify, unhexlify
b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
base32charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
base32charset_upcase = "QPZRY9X8GF2TVDW0S3JN54KHCE6MUA7L"
int_base32_map = dict()
base32_int_map = dict()
for n, i in enumerate(base32charset):
int_base32_map[i] = n
base32_int_map[n] = ord(i)
for n, i in enumerate(base32charset_upcase):
int_base32_map[i] = n
def rebasebits(data, frombits, tobits, pad=True):
"""General power-of-2 base conversion."""
acc = 0
bits = 0
ret = bytearray()
maxv = (1 << tobits) - 1
max_acc = (1 << (frombits + tobits - 1)) - 1
for value in data:
if value < 0 or (value >> frombits):
raise Exception("invalid bytes")
acc = ((acc << frombits) | value) & max_acc
bits += frombits
while bits >= tobits:
bits -= tobits
ret.append((acc >> bits) & maxv)
if pad:
if bits:
ret.append((acc << (tobits - bits)) & maxv)
elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
raise Exception("invalid padding")
return ret
def rebase_5_to_8(data, pad = True):
return rebasebits(data, 5, 8, pad)
def rebase_8_to_5(data, pad = True):
return rebasebits(data, 8, 5, pad)
def rebase_32_to_5(data):
if type(data) == bytes:
data = data.decode()
b = bytearray()
try:
for i in data:
b.append(int_base32_map[i])
except:
raise Exception("Non base32 characters")
return b
def rebase_5_to_32(data, bytes = True):
r = bytearray()
for i in data:
r.append(base32_int_map[i])
return r.decode() if not bytes else r
def bech32_polymod(values):
"""Internal function that computes the Bech32 checksum."""
generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
chk = 1
for value in values:
top = chk >> 25
chk = (chk & 0x1ffffff) << 5 ^ value
for i in range(5):
chk ^= generator[i] if ((top >> i) & 1) else 0
return chk ^ 1
def encode_base58(b):
"""Encode bytes to a base58-encoded string"""
# Convert big-endian bytes to integer
n = int('0x0' + hexlify(b).decode('utf8'), 16)
# Divide that integer into bas58
res = []
while n > 0:
n, r = divmod(n, 58)
res.append(b58_digits[r])
res = ''.join(res[::-1])
# Encode leading zeros as base58 zeros
czero = 0
pad = 0
for c in b:
if c == czero:
pad += 1
else:
break
return b58_digits[0] * pad + res
def decode_base58(s):
"""Decode a base58-encoding string, returning bytes"""
if not s:
return b''
# Convert the string to an integer
n = 0
for c in s:
n *= 58
if c not in b58_digits:
raise Exception('Character %r is not a valid base58 character' % c)
digit = b58_digits.index(c)
n += digit
# Convert the integer to bytes
h = '%x' % n
if len(h) % 2:
h = '0' + h
res = unhexlify(h.encode('utf8'))
# Add padding back.
pad = 0
for c in s[:-1]:
if c == b58_digits[0]:
pad += 1
else:
break
return b'\x00' * pad + res

43
pybtc/hash.py Normal file
View File

@ -0,0 +1,43 @@
import hashlib
import hmac
from binascii import unhexlify
def sha256(h, hex = False):
if type(h) == str:
h = unhexlify(h)
if hex:
return hashlib.sha256(h).hexdigest()
return hashlib.sha256(h).digest()
def double_sha256(h, hex = False):
if type(h) == str:
h = unhexlify(h)
if hex:
return sha256(sha256(h), 1)
return sha256(sha256(h))
def hmac_sha512(key, data, hex = False):
if hex:
return hmac.new(key, data, hashlib.sha512).hexdigest()
return hmac.new(key, data, hashlib.sha512).digest()
def ripemd160(h, hex = False):
if type(h) == str:
h = unhexlify(h)
a = hashlib.new('ripemd160')
a.update(h)
if hex:
return a.hexdigest()
return a.digest()
def hash160(h, hex = False):
if type(h) == str:
h = unhexlify(h)
if hex:
return ripemd160(sha256(h), 1)
return ripemd160(sha256(h))
#

View File

@ -1,122 +1,17 @@
import hashlib
from binascii import hexlify, unhexlify
import time
import random
import struct
import hmac
from secp256k1 import lib as secp256k1
from secp256k1 import ffi
from .constants import *
from .opcodes import *
SIGHASH_ALL = 0x00000001
SIGHASH_NONE = 0x00000002
SIGHASH_SINGLE = 0x00000003
SIGHASH_ANYONECANPAY = 0x00000080
MAX_INT_PRIVATE_KEY = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
EC_COMPRESSED = secp256k1.SECP256K1_EC_COMPRESSED
EC_UNCOMPRESSED = secp256k1.SECP256K1_EC_UNCOMPRESSED
FLAG_SIGN = secp256k1.SECP256K1_CONTEXT_SIGN
FLAG_VERIFY = secp256k1.SECP256K1_CONTEXT_VERIFY
ALL_FLAGS = FLAG_SIGN | FLAG_VERIFY
NO_FLAGS = secp256k1.SECP256K1_CONTEXT_NONE
HAS_RECOVERABLE = hasattr(secp256k1, 'secp256k1_ecdsa_sign_recoverable')
HAS_SCHNORR = hasattr(secp256k1, 'secp256k1_schnorr_sign')
HAS_ECDH = hasattr(secp256k1, 'secp256k1_ecdh')
ECDSA_CONTEXT_SIGN = secp256k1.secp256k1_context_create(FLAG_SIGN)
ECDSA_CONTEXT_VERIFY = secp256k1.secp256k1_context_create(FLAG_VERIFY)
ECDSA_CONTEXT_ALL = secp256k1.secp256k1_context_create(ALL_FLAGS)
secp256k1.secp256k1_context_randomize(ECDSA_CONTEXT_SIGN,
random.SystemRandom().randint(0,MAX_INT_PRIVATE_KEY).to_bytes(32,byteorder="big"))
SCRIPT_TYPES = { "P2PKH": 0,
"P2SH" : 1,
"PUBKEY": 2,
"NULL_DATA": 3,
"MULTISIG": 4,
"NON_STANDART": 5,
"SP2PKH": 6
}
b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
#
# Encoding functions
#
def encode_base58(b):
"""Encode bytes to a base58-encoded string"""
# Convert big-endian bytes to integer
n = int('0x0' + hexlify(b).decode('utf8'), 16)
# Divide that integer into bas58
res = []
while n > 0:
n, r = divmod(n, 58)
res.append(b58_digits[r])
res = ''.join(res[::-1])
# Encode leading zeros as base58 zeros
czero = 0
pad = 0
for c in b:
if c == czero:
pad += 1
else:
break
return b58_digits[0] * pad + res
def decode_base58(s):
"""Decode a base58-encoding string, returning bytes"""
if not s:
return b''
# Convert the string to an integer
n = 0
for c in s:
n *= 58
if c not in b58_digits:
raise Exception('Character %r is not a valid base58 character' % c)
digit = b58_digits.index(c)
n += digit
# Convert the integer to bytes
h = '%x' % n
if len(h) % 2:
h = '0' + h
res = unhexlify(h.encode('utf8'))
# Add padding back.
pad = 0
for c in s[:-1]:
if c == b58_digits[0]:
pad += 1
else:
break
return b'\x00' * pad + res
#
# Hash functions
#
def sha256(bytes):
return hashlib.sha256(bytes).digest()
def double_sha256(bytes):
return sha256(sha256(bytes))
def hmac_sha512(key, data):
return hmac.new(key, data, hashlib.sha512).digest()
def ripemd160(bytes):
h = hashlib.new('ripemd160')
h.update(bytes)
return h.digest()
def hash160(bytes):
return ripemd160(sha256(bytes))
from .hash import *
from .encode import *
#
# Bitcoin keys/ addresses
#
def create_priv():
def create_priv(hex=False):
"""
:return: 32 bytes private key
"""
@ -131,6 +26,8 @@ def create_priv():
else:
if int.from_bytes(h,byteorder="big")<MAX_INT_PRIVATE_KEY:
break
if hex:
return hexlify(h).decode()
return h
def priv_from_int(k):
@ -140,36 +37,56 @@ def priv_from_int(k):
def priv2WIF(h, compressed = False, testnet = False):
#uncompressed: 0x80 + [32-byte secret] + [4 bytes of Hash() of previous 33 bytes], base58 encoded
#compressed: 0x80 + [32-byte secret] + 0x01 + [4 bytes of Hash() previous 34 bytes], base58 encoded
prefix = b'\x80'
if type(h) == str:
h = unhexlify(h)
if testnet:
prefix = b'\xef'
h = prefix + h
h = TESTNET_PRIVATE_KEY_BYTE_PREFIX + h
else:
h = MAINNET_PRIVATE_KEY_BYTE_PREFIX + h
if compressed: h += b'\x01'
h += hashlib.sha256(hashlib.sha256(h).digest()).digest()[:4]
h += double_sha256(h)[:4]
return encode_base58(h)
def WIF2priv(h):
def WIF2priv(h, hex = False, verify = 1):
if verify:
assert is_WIF_valid(h)
h = decode_base58(h)
if hex:
return hexlify(h[1:33]).decode()
return h[1:33]
def is_WIF_valid(wif):
if wif[0] not in ['5', 'K', 'L', '9', 'c']:
if wif[0] not in PRIVATE_KEY_PREFIX_LIST:
return False
try:
h = decode_base58(wif)
except:
return False
h = decode_base58(wif)
if len(h) != 37: return False
checksum = h[-4:]
if hashlib.sha256(hashlib.sha256(h[:-4]).digest()).digest()[:4] != checksum: return False
if wif[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX,
TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX):
if len(h) != 37:
return False
elif len(h) != 38:
return False
if double_sha256(h[:-4])[:4] != checksum:
return False
return True
def priv2pub(private_key, compressed = True, hex = False):
if type(private_key)!= bytes:
if type(private_key) == bytearray:
private_key = bytes(private_key)
elif type(private_key) == str:
private_key = unhexlify(private_key)
if not is_WIF_valid(private_key):
private_key = unhexlify(private_key)
else:
if private_key[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX,
TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX):
compressed = False
private_key = WIF2priv(private_key, verify=0)
else:
raise TypeError("private key must be a bytes or hex encoded string")
raise TypeError("private key must be a bytes or WIF or hex encoded string")
pubkey_ptr = ffi.new('secp256k1_pubkey *')
r = secp256k1.secp256k1_ec_pubkey_create(ECDSA_CONTEXT_ALL, pubkey_ptr, private_key)
assert r == 1
@ -185,74 +102,195 @@ def priv2pub(private_key, compressed = True, hex = False):
def is_valid_pub(key):
if len(key) < 33:
return False
if key[0] == 0x04 and len(key) != 65:
return False
elif key[0] == 0x02 or key[0] == 0x03:
if len(key) != 33:
return False
# else: return False
return True
#
# Bitcoin addresses
#
def hash1602address(hash160, testnet = False, p2sh = False):
if type(hash160) == str:
hash160 = unhexlify(hash160)
if not p2sh:
prefix = b'\x6f' if testnet else b'\x00'
def hash2address(address_hash, testnet = False,
script_hash = False, witness_version = 0):
if type(address_hash) == str:
address_hash = unhexlify(address_hash)
if not script_hash:
if witness_version is None:
assert len(address_hash) == 20
if testnet:
prefix = TESTNET_ADDRESS_BYTE_PREFIX
else:
prefix = MAINNET_ADDRESS_BYTE_PREFIX
address_hash = prefix + address_hash
address_hash += double_sha256(address_hash)[:4]
return encode_base58(address_hash)
else:
assert len(address_hash) in (20,32)
if witness_version is None:
if testnet:
prefix = TESTNET_SCRIPT_ADDRESS_BYTE_PREFIX
else:
prefix = MAINNET_SCRIPT_ADDRESS_BYTE_PREFIX
address_hash = prefix + address_hash
address_hash += double_sha256(address_hash)[:4]
return encode_base58(address_hash)
if testnet:
prefix = TESTNET_SEGWIT_ADDRESS_BYTE_PREFIX
hrp = TESTNET_SEGWIT_ADDRESS_PREFIX
else:
prefix = b'\xc4' if testnet else b'\x05'
hash160 = prefix + hash160
hash160 += double_sha256(hash160)[:4]
return encode_base58(hash160)
prefix = MAINNET_SEGWIT_ADDRESS_BYTE_PREFIX
hrp = MAINNET_SEGWIT_ADDRESS_PREFIX
address_hash = witness_version.to_bytes(1, "big") + rebase_8_to_5( address_hash)
checksum = bech32_polymod(prefix + address_hash + b"\x00" * 6)
checksum = rebase_8_to_5(checksum.to_bytes(5, "big"))[2:]
return "%s1%s" % (hrp, rebase_5_to_32(address_hash + checksum).decode())
def address2hash160(address):
return decode_base58(address)[1:-4]
def address_type(address):
if address[0] in ('2', '3'):
return 'P2SH'
if address[0] in ('1', 'm', 'n'):
return 'P2PKH'
return 'UNKNOWN'
def address2hash(address, hex = False):
if address[0] in ADDRESS_PREFIX_LIST:
h = decode_base58(address)[1:-4]
elif address[:2] in (MAINNET_SEGWIT_ADDRESS_PREFIX,
TESTNET_SEGWIT_ADDRESS_PREFIX):
address = address.split("1")[1]
h = rebase_5_to_8(rebase_32_to_5(address)[1:-6], False)
else:
return None
if hex:
return h.hex()
else:
return h
def address_type(address, num = False):
if address[0] in (TESTNET_SCRIPT_ADDRESS_PREFIX,
MAINNET_SCRIPT_ADDRESS_PREFIX):
t = 'P2SH'
elif address[0] in (MAINNET_ADDRESS_PREFIX,
TESTNET_ADDRESS_PREFIX,
TESTNET_ADDRESS_PREFIX_2):
t = 'P2PKH'
elif address[:2] in (MAINNET_SEGWIT_ADDRESS_PREFIX,
TESTNET_SEGWIT_ADDRESS_PREFIX):
if len(address) == 42:
t = 'P2WPKH'
elif len(address) == 62:
t = 'P2WSH'
else:
return SCRIPT_TYPES['NON_STANDARD'] if num else 'UNKNOWN'
else:
return SCRIPT_TYPES['NON_STANDARD'] if num else 'UNKNOWN'
return SCRIPT_TYPES[t] if num else t
def script2hash(s, witness = False, hex = False):
if type(s) == str:
s = unhexlify(s)
if witness:
return sha256(s, hex)
else:
return hash160(s, hex)
def address2script(address):
if address[0] in ('2', '3'):
return OPCODE["OP_HASH160"] + b'\x14' + address2hash160(address) + OPCODE["OP_EQUAL"]
if address[0] in ('1', 'm', 'n'):
if address[0] in (TESTNET_SCRIPT_ADDRESS_PREFIX,
MAINNET_SCRIPT_ADDRESS_PREFIX):
return OPCODE["OP_HASH160"] + b'\x14' + address2hash(address) + OPCODE["OP_EQUAL"]
if address[0] in (MAINNET_ADDRESS_PREFIX,
TESTNET_ADDRESS_PREFIX,
TESTNET_ADDRESS_PREFIX_2):
return OPCODE["OP_DUP"] + OPCODE["OP_HASH160"] + b'\x14' + \
address2hash160(address) + OPCODE["OP_EQUALVERIFY"] + OPCODE["OP_CHECKSIG"]
address2hash(address) + OPCODE["OP_EQUALVERIFY"] + OPCODE["OP_CHECKSIG"]
if address[0] in (TESTNET_SEGWIT_ADDRESS_PREFIX,
MAINNET_SEGWIT_ADDRESS_PREFIX):
h = address2hash(address)
return OPCODE["OP_0"] + bytes([len(h)]) + h
raise Exception("Unknown address")
def pub2address(pubkey, testnet = False, p2sh = False):
h = hash160(pubkey)
return hash1602address(h, testnet = testnet, p2sh = p2sh)
def pub2segwit(pubkey, testnet = False):
return hash1602address(pub2hash160segwit(pubkey),
testnet=testnet,
p2sh=True)
def pub2hash160segwit(pubkey):
return hash160(b'\x00\x14' + hash160(pubkey))
def is_address_valid(addr, testnet = False):
if testnet:
if addr[0] not in ('m', 'n', '2'):
return False
def pub2address(pubkey, testnet = False,
inside_p2sh = False,
witness_version = 0):
if type(pubkey) == str:
pubkey = unhexlify(pubkey)
if inside_p2sh:
assert len(pubkey) == 33
h = hash160(b'\x00\x14' + hash160(pubkey))
else:
if addr[0] not in ('1','3'):
if witness_version is not None:
assert len(pubkey) == 33
h = hash160(pubkey)
return hash2address(h, testnet = testnet,
script_hash = inside_p2sh,
witness_version = witness_version)
# def pub2P2SH_P2WPKH_hash(pubkey):
# return hash160(b'\x00\x14' + hash160(pubkey))
#
# def pub2P2SH_P2WPKH_address(pubkey, testnet = False):
# return hash2address(pub2P2SH_P2WPKH_hash(pubkey),
# testnet=testnet,
# script_hash=True,
# witness_version=None)
def is_address_valid(address, testnet = False):
if not address or type(address) != str:
return False
if address[0] in (MAINNET_ADDRESS_PREFIX,
MAINNET_SCRIPT_ADDRESS_PREFIX,
TESTNET_ADDRESS_PREFIX,
TESTNET_ADDRESS_PREFIX_2,
TESTNET_SCRIPT_ADDRESS_PREFIX):
if testnet:
if address[0] not in (TESTNET_ADDRESS_PREFIX,
TESTNET_ADDRESS_PREFIX_2,
TESTNET_SCRIPT_ADDRESS_PREFIX):
return False
else:
if address[0] not in (MAINNET_ADDRESS_PREFIX,
MAINNET_SCRIPT_ADDRESS_PREFIX):
return False
h = decode_base58(address)
if len(h) != 25: return False
checksum = h[-4:]
if double_sha256(h[:-4])[:4] != checksum:
return False
h = decode_base58(addr)
if len(h) != 25: return False
checksum = h[-4:]
if hashlib.sha256(hashlib.sha256(h[:-4]).digest()).digest()[:4] != checksum: return False
return True
return True
elif address[:2].lower() in (TESTNET_SEGWIT_ADDRESS_PREFIX,
MAINNET_SEGWIT_ADDRESS_PREFIX):
if len(address) not in (42, 62):
return False
prefix, payload = address.split('1')
upp = True if prefix[0].isupper() else False
for i in payload[1:]:
if upp:
if not i.isupper() or i not in base32charset_upcase:
return False
else:
if i.isupper() or i not in base32charset:
return False
payload = payload.lower()
prefix = prefix.lower()
if testnet:
if prefix != TESTNET_SEGWIT_ADDRESS_PREFIX:
return False
stripped_prefix = TESTNET_SEGWIT_ADDRESS_BYTE_PREFIX
else:
if prefix != MAINNET_SEGWIT_ADDRESS_PREFIX:
return False
stripped_prefix = MAINNET_SEGWIT_ADDRESS_BYTE_PREFIX
d = rebase_32_to_5(payload)
address_hash = d[:-6]
checksum = d[-6:]
checksum2 = bech32_polymod(stripped_prefix + address_hash + b"\x00" * 6)
checksum2 = rebase_8_to_5(checksum2.to_bytes(5, "big"))[2:]
if checksum != checksum2:
return False
return True
#

View File

@ -1,7 +1,7 @@
# from .script_deserialize import *
# from .hash_functions import *
# from .address_functions import *
# from .transaction_deserialize import *
# from .sighash import *
# from .ecdsa import *
from .block import *
from .script_deserialize import *
from .hash_functions import *
from .address_functions import *
from .transaction_deserialize import *
from .sighash import *
from .ecdsa import *
# from .block import *

View File

@ -1,5 +1,6 @@
import unittest
from pybtc import tools
from pybtc import OPCODE
from binascii import unhexlify
@ -8,7 +9,121 @@ class AddressFunctionsTests(unittest.TestCase):
def setUpClass(cls):
print("\nTesting address functions:\n")
def test_pub2segwit(self):
print("pub2segwit")
self.assertEqual(tools.pub2segwit(unhexlify("03db633162d49193d1178a5bbb90bde2f3c196ba0296f010b12a2320a7c6568582")),
"3PjV3gFppqmDEHjLvqDWv3Y4riLMQg7X1y")
def test_priv2WIF(self):
p = "ceda1ae4286015d45ec5147fe3f63e9377ccd6d4e98bcf0847df9937da1944a4"
pcm = "L49obCXV7fGz2YRzLCSJgeZBYmGeBbKPT7xiehUeYX2S4URkPFZX"
pum = "5KPPLXhtga99qqMceRo4Z6LXV3Kx6a9hRx3ez2U7EwP5KZfy2Wf"
put = "93A1vGXSGoDHotruGmgyRgtV8hgfFjgtmtuc4epcag886W9d44L"
pct = "cUWo47XLYiyFByuFicFS3y4FAza3r3R5XA7Bm7wA3dgSKDYox7h6"
self.assertEqual(tools.priv2WIF(p, compressed=1, testnet=0),pcm)
self.assertEqual(tools.priv2WIF(p, compressed=0, testnet=0),pum)
self.assertEqual(tools.priv2WIF(p, compressed=1, testnet=1),pct)
self.assertEqual(tools.priv2WIF(p, compressed=0, testnet=1),put)
def test_is_WIF_valid(self):
self.assertEqual(tools.is_WIF_valid("L49obCXV7fGz2YRzLCSJgeZBYmGeBbKPT7xiehUeYX2S4URkPFZX"),1)
self.assertEqual(tools.is_WIF_valid("5KPPLXhtga99qqMceRo4Z6LXV3Kx6a9hRx3ez2U7EwP5KZfy2Wf"),1)
self.assertEqual(tools.is_WIF_valid("5KPPLXhtga99qqMcWRo4Z6LXV3Kx6a9hRx3ez2U7EwP5KZfy2Wf"),0)
self.assertEqual(tools.is_WIF_valid("93A1vGXSGoDHotruGmgyRgtV8hgfFjgtmtuc4epcag886W9d44L"),1)
self.assertEqual(tools.is_WIF_valid("cUWo47XLYiyFByuFicFS3y4FAza3r3R5XA7Bm7wA3dgSKDYox7h6"),1)
self.assertEqual(tools.is_WIF_valid("cUWo47XLYiyByuFicFS3y4FAza3r3R5XA7Bm7wA3dgSKDYox7h6"),0)
def test_WIF2priv(self):
p = "ceda1ae4286015d45ec5147fe3f63e9377ccd6d4e98bcf0847df9937da1944a4"
self.assertEqual(tools.WIF2priv("L49obCXV7fGz2YRzLCSJgeZBYmGeBbKPT7xiehUeYX2S4URkPFZX",
hex=1),p)
self.assertEqual(tools.WIF2priv("L49obCXV7fGz2YRzLCSJgeZBYmGeBbKPT7xiehUeYX2S4URkPFZX",
hex=0),unhexlify(p))
self.assertEqual(tools.WIF2priv("5KPPLXhtga99qqMceRo4Z6LXV3Kx6a9hRx3ez2U7EwP5KZfy2Wf",
hex=1),p)
self.assertEqual(tools.WIF2priv("93A1vGXSGoDHotruGmgyRgtV8hgfFjgtmtuc4epcag886W9d44L",
hex=1),p)
self.assertEqual(tools.WIF2priv("cUWo47XLYiyFByuFicFS3y4FAza3r3R5XA7Bm7wA3dgSKDYox7h6",
hex=1),p)
def test_priv2pub(self):
p = "ceda1ae4286015d45ec5147fe3f63e9377ccd6d4e98bcf0847df9937da1944a4"
pu = "04b635dbdc16dbdf4bb9cf5b55e7d03e514fb04dcef34208155c7d3ec88e9045f4c8cbe28702911260f2a1da099a338bed4ee98f66bb8dba8031a76ab537ff6663"
pk = "03b635dbdc16dbdf4bb9cf5b55e7d03e514fb04dcef34208155c7d3ec88e9045f4"
self.assertEqual(tools.priv2pub(p, hex=1),pk)
self.assertEqual(tools.priv2pub(p, hex=0),unhexlify(pk))
self.assertEqual(tools.priv2pub(p, compressed=0, hex=1),pu)
self.assertEqual(tools.priv2pub("L49obCXV7fGz2YRzLCSJgeZBYmGeBbKPT7xiehUeYX2S4URkPFZX", hex=1),pk)
self.assertEqual(tools.priv2pub("5KPPLXhtga99qqMceRo4Z6LXV3Kx6a9hRx3ez2U7EwP5KZfy2Wf", hex=1),pu)
self.assertEqual(tools.priv2pub("93A1vGXSGoDHotruGmgyRgtV8hgfFjgtmtuc4epcag886W9d44L", hex=1),pu)
self.assertEqual(tools.priv2pub("cUWo47XLYiyFByuFicFS3y4FAza3r3R5XA7Bm7wA3dgSKDYox7h6", hex=1),pk)
def test_hash2address(self):
pc = "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
h = tools.hash160(pc)
s = bytes([len(unhexlify(pc))])+unhexlify(pc) + OPCODE["OP_CHECKSIG"]
self.assertEqual(tools.hash2address(h), "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4")
self.assertEqual(tools.hash2address(h, testnet=1), "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx")
h = tools.script2hash(s, 1, 1)
self.assertEqual(tools.hash2address(h), "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3")
self.assertEqual(tools.hash2address(h, testnet=1), "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7")
pk = "03b635dbdc16dbdf4bb9cf5b55e7d03e514fb04dcef34208155c7d3ec88e9045f4"
h = tools.hash160(pk)
self.assertEqual(tools.hash2address(h, witness_version=None), "1Fs2Xqrk4P2XADaJeZWykaGXJ4HEb6RyT1")
self.assertEqual(tools.hash2address(h, witness_version=None, testnet=1), "mvNyptwisQTmwL3vN8VMaVUrA3swVCX83c")
# p2wpkh inside p2sh
p = "L32a8Mo1LgvjrVDbzcc3NkuUfkpoLsf2Y2oEWkV4t1KpQdFzuyff"
pk = tools.priv2pub(p)
script = b'\x00\x14' + tools.hash160(pk)
script_hash = tools.hash160(script)
self.assertEqual(tools.hash2address(script_hash, script_hash=1, witness_version=None), "33am12q3Bncnn3BfvLYHczyv23Sq2Wbwjw")
self.assertEqual(tools.hash2address(script_hash, script_hash=1, witness_version=None, testnet=1), "2Mu8y4mm4oF88yppDbUAAEwyBEPezrx7CLh")
def test_address2hash(self):
h = "751e76e8199196d454941c45d1b3a323f1433bd6"
self.assertEqual(tools.address2hash("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", 1), h)
self.assertEqual(tools.address2hash("tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx", 1), h)
h = "1863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262"
self.assertEqual(tools.address2hash("bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3", 1), h)
h = "a307d67484911deee457779b17505cedd20e1fe9"
self.assertEqual(tools.address2hash("1Fs2Xqrk4P2XADaJeZWykaGXJ4HEb6RyT1", 1), h)
self.assertEqual(tools.address2hash("mvNyptwisQTmwL3vN8VMaVUrA3swVCX83c", 1), h)
h = "14c14c8d26acbea970757b78e6429ad05a6ac6bb"
self.assertEqual(tools.address2hash("33am12q3Bncnn3BfvLYHczyv23Sq2Wbwjw", 1), h)
self.assertEqual(tools.address2hash("2Mu8y4mm4oF88yppDbUAAEwyBEPezrx7CLh", 1), h)
def test_address_type(self):
self.assertEqual(tools.address_type("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"), 'P2WPKH')
self.assertEqual(tools.address_type("tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx"), 'P2WPKH')
self.assertEqual(tools.address_type("bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3"), 'P2WSH')
self.assertEqual(tools.address_type("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7"), 'P2WSH')
self.assertEqual(tools.address_type("1Fs2Xqrk4P2XADaJeZWykaGXJ4HEb6RyT1"), 'P2PKH')
self.assertEqual(tools.address_type("mvNyptwisQTmwL3vN8VMaVUrA3swVCX83c"), 'P2PKH')
self.assertEqual(tools.address_type("33am12q3Bncnn3BfvLYHczyv23Sq2Wbwjw"), 'P2SH')
self.assertEqual(tools.address_type("2Mu8y4mm4oF88yppDbUAAEwyBEPezrx7CLh"), 'P2SH')
def test_pub2address(self):
pc = "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
self.assertEqual(tools.pub2address(pc), "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4")
self.assertEqual(tools.pub2address(pc, testnet=1), "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx")
pc = "03b635dbdc16dbdf4bb9cf5b55e7d03e514fb04dcef34208155c7d3ec88e9045f4"
self.assertEqual(tools.pub2address(pc, witness_version=None, testnet=0), "1Fs2Xqrk4P2XADaJeZWykaGXJ4HEb6RyT1")
self.assertEqual(tools.pub2address(pc, witness_version=None, testnet=1), "mvNyptwisQTmwL3vN8VMaVUrA3swVCX83c")
p = "L32a8Mo1LgvjrVDbzcc3NkuUfkpoLsf2Y2oEWkV4t1KpQdFzuyff"
pk = tools.priv2pub(p)
self.assertEqual(tools.pub2address(pk, inside_p2sh=1,witness_version=None), "33am12q3Bncnn3BfvLYHczyv23Sq2Wbwjw")
def test_is_address_valid(self):
self.assertEqual(tools.is_address_valid("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"), 1)
self.assertEqual(tools.is_address_valid("tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx", 1), 1)
self.assertEqual(tools.is_address_valid("tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx"), 0)
self.assertEqual(tools.is_address_valid("bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3"), 1)
self.assertEqual(tools.is_address_valid("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", 1), 1)
self.assertEqual(tools.is_address_valid("1Fs2Xqrk4P2XADaJeZWykaGXJ4HEb6RyT1"), 1)
self.assertEqual(tools.is_address_valid("mvNyptwisQTmwL3vN8VMaVUrA3swVCX83c", 1), 1)
self.assertEqual(tools.is_address_valid("33am12q3Bncnn3BfvLYHczyv23Sq2Wbwjw"), 1)
self.assertEqual(tools.is_address_valid("2Mu8y4mm4oF88yppDbUAAEwyBEPezrx7CLh",1), 1)
self.assertEqual(tools.is_address_valid("2Mu8y4mm4oF89yppDbUAAEwyBEPezrx7CLh",1), 0)
self.assertEqual(tools.is_address_valid("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", 1), 0)
self.assertEqual(tools.is_address_valid("tb1qw508d6qejxtdg4W5r3zarvary0c5xw7kxpjzsx",1), 0)
self.assertEqual(tools.is_address_valid("bc1qrp33g0q5c5txsp8arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3"), 0)
self.assertEqual(tools.is_address_valid("tb1qrp23g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7",1), 0)
self.assertEqual(tools.is_address_valid("1Fs2Xqrk4P2XADaJeZWykaGXJ2HEb6RyT1"), 0)
self.assertEqual(tools.is_address_valid("mvNyptwisQTkwL3vN8VMaVUrA3swVCX83c", 1), 0)
self.assertEqual(tools.is_address_valid("33am12q3Bncmn3BfvLYHczyv23Sq2Wbwjw"), 0)
self.assertEqual(tools.is_address_valid("2Mu8y4mm4oF78yppDbUAAEwyBEPezrx7CLh", 1), 0)

View File

@ -587,8 +587,10 @@ class BlockDeserializeTests(unittest.TestCase):
def setUpClass(cls):
print("\nTesting Block class deserialization:\n")
def test_block_deserialize(self):
block_c = "2000000000000000000001b7b88a5bad46d2e6707a89d7dc850441c81876cc9b7cfa6ce4c7a3304d8f487f52b6268d942f72d908a10a027ea01c3c04c93aff718bb8783e8053815a1a372b40915ae40604010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2303c38e130068747470733a5c6170692e6269746170732e636f6d204a5948fc8a000000ffffffff02c7a2a904000000001976a914d4e49947b9f545218cd20389244c249d3520545d88ac0000000000000000266a24aa21a9ed7a2c14ed22283b657a9181f8981dc05361ca46f0b2a5c71d983326e5a73578100120000000000000000000000000000000000000000000000000000000000000000000000000010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2303c38e130068747470733a5c6170692e6269746170732e636f6d2004b7841500000000ffffffff02c7a2a904000000001976a914d4e49947b9f545218cd20389244c249d3520545d88ac0000000000000000266a24aa21a9edcdbbce5ecb7d4878ac6828fe9284eb5616b26941d395524a95af8ea9b7b62df301200000000000000000000000000000000000000000000000000000000000000000000000000100000001f70f321af3d0cd51241b3755129298e2b389056084e7f9e716677feb7bfc823c010000006a47304402204970ddbb984e260419d78ac265af76ed427aa7789a38661e65d08535db408f1d022048f11c920753c8409b83cc5adfbfa36c19a176b502d5ea59b693dd45661db5ac012103b403539e4175cde64cc99b71d0d9cfc701c193bed1d6df17ae37b92e8506f141ffffffff0200000000000000001e6a1c504f455400000002b2dabb0c40f888bcedc5006bdf1cf3ed7bd26b0b239ede490a0000001976a9140fcc88829c46f85443edcce2150a8edbf57b48be88ac0000000001000000015bd2dcb6ec8cb4c82c2d6c59dc6390d94693d807d91f8749a5638fd925e88cd200000000fdfe000048304502210081c18398e662da86f8a8106ab78bdbd5732e456c872cc112f646070cc02f375202200ad1dd76bdf427222ce3edc73262128ecf20b0d57b328450e5fdaa7420fefc9a01483045022100ec41d289edc6bbec37560cf9f47d294c47d92a811cb67400a17e6bcd82594328022061031dd011fac9bc90bcde08eadca8143725854c502ab559270caa84c298d73b014c69522102f4dade8a5d54a904e3c7e639f397f049ab597b0859c6b486c5721344f4488f4921034b669c9a2f24d3d3d3d72358ae3f75bb257bfd3e166b404b4ff4ba8d6d0b52a821030fbf0e349acc83649bf187ba78c7764845f16cfe95c7c0287b6fac123815755753aeffffffff02a08601000000000017a914ac160ac1388194d6fe0e8e620d905e90bb3031fd87de6f850b0000000017a914d24feab4ad5431661fe18ffd158295158fe046218700000000"
block = blockchain.Block.deserialize(block_c)
block_c = "2000000000000000000029a1a0390376afd72db120d698c2d8ebc41c545c7d4bc2b9033c303dd3d09d455da1c587406820a4dd77c5aa3fcc546dcceb1becc639829c4fe6535e815a1a372b405cc0829701010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2303f88e130068747470733a5c6170692e6269746170732e636f6d20f251bec0cb000000ffffffff02c817a804000000001976a914d4e49947b9f545218cd20389244c249d3520545d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000"
block_e = "2000000000000000000029a1a0390376afd72db120d698c2d8ebc41c545c7d4bc2b9033c303dd3d09d455da1c587406820a4dd77c5aa3fcc546dcceb1becc639829c4fe65a815e531a372b405cc0829701010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2303f88e130068747470733a5c6170692e6269746170732e636f6d20f251bec0cb000000ffffffff02c817a804000000001976a914d4e49947b9f545218cd20389244c249d3520545d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000"
block = blockchain.Block.deserialize(block_e)
print(">>>",block.bits)
print(">>>",block.hash)

View File

@ -1,6 +1,6 @@
import unittest
from pybtc import tools
from binascii import unhexlify
from binascii import unhexlify, hexlify
@ -13,13 +13,24 @@ class HashFunctionsTests(unittest.TestCase):
print("Double SHA256")
self.assertEqual(tools.double_sha256(b"test double sha256"),
unhexlify("1ab3067efb509c48bda198f48c473f034202537c28b7b4c3b2ab2c4bf4a95c8d"))
self.assertEqual(tools.double_sha256(hexlify(b"test double sha256").decode()),
unhexlify("1ab3067efb509c48bda198f48c473f034202537c28b7b4c3b2ab2c4bf4a95c8d"))
self.assertEqual(tools.double_sha256(hexlify(b"test double sha256").decode(), 1),
"1ab3067efb509c48bda198f48c473f034202537c28b7b4c3b2ab2c4bf4a95c8d")
def test_ripemd160(self):
print("RIPEMD160")
self.assertEqual(tools.ripemd160(b"test ripemd160"),
unhexlify("45b17861a7defaac439f740d890f3dac4813cc37"))
self.assertEqual(tools.ripemd160(hexlify(b"test ripemd160").decode()),
unhexlify("45b17861a7defaac439f740d890f3dac4813cc37"))
self.assertEqual(tools.ripemd160(hexlify(b"test ripemd160").decode(), 1),
"45b17861a7defaac439f740d890f3dac4813cc37")
def test_hash160(self):
print("HASH160")
self.assertEqual(tools.ripemd160(b"test hash160"),
unhexlify("46a80bd289028559818a222eea64552d7a6a966f"))
unhexlify("46a80bd289028559818a222eea64552d7a6a966f"))
self.assertEqual(tools.ripemd160(hexlify(b"test hash160").decode()),
unhexlify("46a80bd289028559818a222eea64552d7a6a966f"))
self.assertEqual(tools.ripemd160(hexlify(b"test hash160").decode(), 1),
"46a80bd289028559818a222eea64552d7a6a966f")

View File

@ -1,7 +1,7 @@
import unittest
from pybtc import blockchain
from binascii import unhexlify
from pybtc import address2hash160
from pybtc import address2hash as address2hash160
class ScriptDeserializeTests(unittest.TestCase):

View File

@ -1,7 +1,7 @@
import unittest
from pybtc import *
from binascii import unhexlify
from pybtc import address2hash160
from pybtc import address2hash as address2hash160
class SighashTests(unittest.TestCase):
@classmethod

View File

@ -1,7 +1,7 @@
import unittest
from pybtc import blockchain
from binascii import unhexlify
from pybtc import address2hash160
from pybtc import address2hash as address2hash160
class TransactionDeserializeTests(unittest.TestCase):