cleaning code

This commit is contained in:
4tochka 2019-03-15 01:36:25 +04:00
parent d6559acd18
commit 2c1457a135
16 changed files with 337 additions and 283 deletions

View File

@ -1,6 +1,9 @@
from .functions import * from pybtc.constants import *
from .opcodes import * from pybtc.opcodes import *
from .consensus import * from pybtc.consensus import *
from pybtc.functions import *
from .transaction import * from .transaction import *
from .block import * from .block import *
from .address import * from .address import *

View File

@ -1,5 +1,15 @@
from .functions import * from pybtc.constants import *
from pybtc.opcodes import *
from pybtc.functions.tools import bytes_from_hex, int_to_var_int
from pybtc.functions.script import op_push_data, decode_script
from pybtc.functions.hash import hash160, sha256
from pybtc.functions.address import hash_to_address, public_key_to_p2sh_p2wpkh_script
from pybtc.functions.key import (create_private_key,
private_key_to_wif,
is_wif_valid,
wif_to_private_key,
is_public_key_valid,
private_to_public_key)
class PrivateKey(): class PrivateKey():
""" """
@ -31,7 +41,7 @@ class PrivateKey():
else: else:
if isinstance(key, str): if isinstance(key, str):
try: try:
key = bytes.fromhex(key) key = bytes_from_hex(key)
except: except:
pass pass
if isinstance(key, bytes): if isinstance(key, bytes):
@ -90,7 +100,7 @@ class PublicKey():
def __init__(self, key, compressed=True, testnet=False): def __init__(self, key, compressed=True, testnet=False):
if isinstance(key, str): if isinstance(key, str):
try: try:
key = bytes.fromhex(key) key = bytes_from_hex(key)
except: except:
if is_wif_valid(key): if is_wif_valid(key):
key = PrivateKey(key) key = PrivateKey(key)

View File

@ -1,6 +1,10 @@
from .transaction import Transaction from struct import unpack, pack
from struct import unpack from io import BytesIO
from .functions import * from pybtc.functions.block import bits_to_target, target_to_difficulty
from pybtc.functions.hash import double_sha256
from pybtc.functions.tools import var_int_to_int, read_var_int, var_int_len, rh2s
from pybtc.transaction import Transaction
class Block(dict): class Block(dict):
def __init__(self, raw_block=None, format="decoded", version=536870912, testnet=False): def __init__(self, raw_block=None, format="decoded", version=536870912, testnet=False):
@ -11,7 +15,7 @@ class Block(dict):
self["header"] = None self["header"] = None
self["hash"] = None self["hash"] = None
self["version"] = version self["version"] = version
self["versionHex"] = struct.pack(">L", version).hex() self["versionHex"] = pack(">L", version).hex()
self["previousBlockHash"] = None self["previousBlockHash"] = None
self["merkleRoot"] = None self["merkleRoot"] = None
self["tx"] = dict() self["tx"] = dict()
@ -32,7 +36,7 @@ class Block(dict):
s = self.get_stream(raw_block) s = self.get_stream(raw_block)
self["format"] = "raw" self["format"] = "raw"
self["version"] = unpack("<L", s.read(4))[0] self["version"] = unpack("<L", s.read(4))[0]
self["versionHex"] = struct.pack(">L", self["version"]).hex() self["versionHex"] = pack(">L", self["version"]).hex()
self["previousBlockHash"] = s.read(32) self["previousBlockHash"] = s.read(32)
self["merkleRoot"] = s.read(32) self["merkleRoot"] = s.read(32)
self["time"] = unpack("<L", s.read(4))[0] self["time"] = unpack("<L", s.read(4))[0]
@ -82,11 +86,11 @@ class Block(dict):
@staticmethod @staticmethod
def get_stream(stream): def get_stream(stream):
if type(stream) != io.BytesIO: if type(stream) != BytesIO:
if type(stream) == str: if type(stream) == str:
stream = bytes.fromhex(stream) stream = bytes.fromhex(stream)
if type(stream) == bytes: if type(stream) == bytes:
stream = io.BytesIO(stream) stream = BytesIO(stream)
else: else:
raise TypeError raise TypeError
return stream return stream

View File

@ -1,4 +1,4 @@
from secp256k1 import lib as secp256k1 from secp256k1 import lib
import random import random
import os import os
@ -50,23 +50,23 @@ MAINNET_SEGWIT_ADDRESS_PREFIX = 'bc'
TESTNET_SEGWIT_ADDRESS_PREFIX = 'tb' TESTNET_SEGWIT_ADDRESS_PREFIX = 'tb'
EC_COMPRESSED = secp256k1.SECP256K1_EC_COMPRESSED EC_COMPRESSED = lib.SECP256K1_EC_COMPRESSED
EC_UNCOMPRESSED = secp256k1.SECP256K1_EC_UNCOMPRESSED EC_UNCOMPRESSED = lib.SECP256K1_EC_UNCOMPRESSED
FLAG_SIGN = secp256k1.SECP256K1_CONTEXT_SIGN FLAG_SIGN = lib.SECP256K1_CONTEXT_SIGN
FLAG_VERIFY = secp256k1.SECP256K1_CONTEXT_VERIFY FLAG_VERIFY = lib.SECP256K1_CONTEXT_VERIFY
ALL_FLAGS = FLAG_SIGN | FLAG_VERIFY ALL_FLAGS = FLAG_SIGN | FLAG_VERIFY
NO_FLAGS = secp256k1.SECP256K1_CONTEXT_NONE NO_FLAGS = lib.SECP256K1_CONTEXT_NONE
HAS_RECOVERABLE = hasattr(secp256k1, 'secp256k1_ecdsa_sign_recoverable') HAS_RECOVERABLE = hasattr(lib, 'secp256k1_ecdsa_sign_recoverable')
HAS_SCHNORR = hasattr(secp256k1, 'secp256k1_schnorr_sign') HAS_SCHNORR = hasattr(lib, 'secp256k1_schnorr_sign')
HAS_ECDH = hasattr(secp256k1, 'secp256k1_ecdh') HAS_ECDH = hasattr(lib, 'secp256k1_ecdh')
ECDSA_CONTEXT_SIGN = secp256k1.secp256k1_context_create(FLAG_SIGN) ECDSA_CONTEXT_SIGN = lib.secp256k1_context_create(FLAG_SIGN)
ECDSA_CONTEXT_VERIFY = secp256k1.secp256k1_context_create(FLAG_VERIFY) ECDSA_CONTEXT_VERIFY = lib.secp256k1_context_create(FLAG_VERIFY)
ECDSA_CONTEXT_ALL = secp256k1.secp256k1_context_create(ALL_FLAGS) ECDSA_CONTEXT_ALL = lib.secp256k1_context_create(ALL_FLAGS)
secp256k1.secp256k1_context_randomize(ECDSA_CONTEXT_SIGN, lib.secp256k1_context_randomize(ECDSA_CONTEXT_SIGN,
random.SystemRandom().randint(0, ECDSA_SEC256K1_ORDER).to_bytes(32, byteorder="big")) random.SystemRandom().randint(0, ECDSA_SEC256K1_ORDER).to_bytes(32, byteorder="big"))
SCRIPT_TYPES = {"P2PKH": 0, SCRIPT_TYPES = {"P2PKH": 0,
"P2SH": 1, "P2SH": 1,

View File

@ -1,12 +1,17 @@
import os
import sys
parentPath = os.path.abspath("../..")
if parentPath not in sys.path:
sys.path.insert(0, parentPath)
from pybtc.opcodes import * from pybtc.opcodes import *
from .key import * from pybtc.constants import *
from .hash import *
from pybtc.functions.tools import bytes_from_hex
from pybtc.functions.hash import double_sha256, hash160
from pybtc.functions.encode import (encode_base58,
rebase_8_to_5,
bech32_polymod,
rebase_5_to_32,
decode_base58,
rebase_5_to_8,
rebase_32_to_5,
base32charset,
base32charset_upcase)
def hash_to_address(address_hash, testnet=False, script_hash=False, witness_version=0): def hash_to_address(address_hash, testnet=False, script_hash=False, witness_version=0):
@ -23,7 +28,7 @@ def hash_to_address(address_hash, testnet=False, script_hash=False, witness_vers
:return: address in base58 or bech32 format. :return: address in base58 or bech32 format.
""" """
if isinstance(address_hash, str): if isinstance(address_hash, str):
address_hash = bytes.fromhex(address_hash) address_hash = bytes_from_hex(address_hash)
if not isinstance(address_hash, bytes): if not isinstance(address_hash, bytes):
raise TypeError("address hash must be HEX encoded string or bytes") raise TypeError("address hash must be HEX encoded string or bytes")

View File

@ -1,5 +1,11 @@
import struct from struct import pack
from .key import * from secp256k1 import ffi, lib
from pybtc.functions.key import private_to_public_key, private_key_to_wif
from pybtc.functions.hash import hmac_sha512, double_sha256, hash160
from pybtc.functions.encode import (encode_base58,
decode_base58_with_checksum,
encode_base58_with_checksum)
from pybtc.constants import *
def create_master_xprivate_key(seed, testnet=False, base58=True, hex=False): def create_master_xprivate_key(seed, testnet=False, base58=True, hex=False):
@ -110,7 +116,7 @@ def derive_child_xprivate_key(xprivate_key, i):
raise ValueError("path depth should be <= 255") raise ValueError("path depth should be <= 255")
pub = private_to_public_key(k[1:], hex=False) pub = private_to_public_key(k[1:], hex=False)
fingerprint = hash160(pub)[:4] fingerprint = hash160(pub)[:4]
s = hmac_sha512(c, b"%s%s" % (k if i >= HARDENED_KEY else pub, struct.pack(">L", i))) s = hmac_sha512(c, b"%s%s" % (k if i >= HARDENED_KEY else pub, pack(">L", i)))
p_int = int.from_bytes(s[:32],byteorder='big') p_int = int.from_bytes(s[:32],byteorder='big')
if p_int >= ECDSA_SEC256K1_ORDER: if p_int >= ECDSA_SEC256K1_ORDER:
return None return None
@ -121,7 +127,7 @@ def derive_child_xprivate_key(xprivate_key, i):
return b"".join([xprivate_key[:4], return b"".join([xprivate_key[:4],
bytes([depth]), bytes([depth]),
fingerprint, fingerprint,
struct.pack(">L", i), pack(">L", i),
s[32:], s[32:],
b'\x00', b'\x00',
key]) key])
@ -136,24 +142,24 @@ def derive_child_xpublic_key(xpublic_key, i):
raise ValueError("path depth should be <= 255") raise ValueError("path depth should be <= 255")
if i >= HARDENED_KEY: if i >= HARDENED_KEY:
raise ValueError("derivation from extended public key impossible") raise ValueError("derivation from extended public key impossible")
s = hmac_sha512(c, k + struct.pack(">L", i)) s = hmac_sha512(c, k + pack(">L", i))
if int.from_bytes(s[:32], byteorder='big') >= ECDSA_SEC256K1_ORDER: if int.from_bytes(s[:32], byteorder='big') >= ECDSA_SEC256K1_ORDER:
return None return None
pubkey_ptr = ffi.new('secp256k1_pubkey *') pubkey_ptr = ffi.new('secp256k1_pubkey *')
if not secp256k1.secp256k1_ec_pubkey_parse(ECDSA_CONTEXT_VERIFY, pubkey_ptr, k, len(k)): if not lib.secp256k1_ec_pubkey_parse(ECDSA_CONTEXT_VERIFY, pubkey_ptr, k, len(k)):
raise RuntimeError("secp256k1 parse public key operation failed") raise RuntimeError("secp256k1 parse public key operation failed")
if not secp256k1.secp256k1_ec_pubkey_tweak_add(ECDSA_CONTEXT_ALL, pubkey_ptr, s[:32]): if not lib.secp256k1_ec_pubkey_tweak_add(ECDSA_CONTEXT_ALL, pubkey_ptr, s[:32]):
raise RuntimeError("secp256k1 parse tweak addition operation failed") raise RuntimeError("secp256k1 parse tweak addition operation failed")
pubkey = ffi.new('char [%d]' % 33) pubkey = ffi.new('char [%d]' % 33)
outlen = ffi.new('size_t *', 33) outlen = ffi.new('size_t *', 33)
if not secp256k1.secp256k1_ec_pubkey_serialize(ECDSA_CONTEXT_VERIFY, pubkey, outlen, pubkey_ptr, EC_COMPRESSED): if not lib.secp256k1_ec_pubkey_serialize(ECDSA_CONTEXT_VERIFY, pubkey, outlen, pubkey_ptr, EC_COMPRESSED):
raise RuntimeError("secp256k1 serialize public key operation failed") raise RuntimeError("secp256k1 serialize public key operation failed")
pk = bytes(ffi.buffer(pubkey, 33)) pk = bytes(ffi.buffer(pubkey, 33))
print(len(pk)) print(len(pk))
return b"".join([xpublic_key[:4], return b"".join([xpublic_key[:4],
bytes([depth]), bytes([depth]),
fingerprint, fingerprint,
struct.pack(">L", i), pack(">L", i),
s[32:], s[32:],
pk]) pk])

View File

@ -1,14 +1,8 @@
import os
import sys
import time
parentPath = os.path.abspath("../..")
if parentPath not in sys.path:
sys.path.insert(0, parentPath)
from pybtc.constants import * from pybtc.constants import *
from .hash import * import time
from hashlib import pbkdf2_hmac import hashlib
from pybtc.functions.hash import sha256
from pybtc.functions.tools import int_from_bytes
def generate_entropy(strength=256, hex=True): def generate_entropy(strength=256, hex=True):
""" """
@ -27,7 +21,7 @@ def generate_entropy(strength=256, hex=True):
while i: while i:
h = hashlib.sha256(h).digest() h = hashlib.sha256(h).digest()
i -= 1 i -= 1
if not i and int.from_bytes(h, byteorder="big") > ECDSA_SEC256K1_ORDER: if not i and int_from_bytes(h, byteorder="big") > ECDSA_SEC256K1_ORDER:
i += 1 i += 1
return h[:int(strength/8)] if not hex else h[:int(strength/8)].hex() return h[:int(strength/8)] if not hex else h[:int(strength/8)].hex()
@ -139,5 +133,5 @@ def mnemonic_to_seed(mnemonic, passphrase="", hex=True):
if not isinstance(passphrase, str): if not isinstance(passphrase, str):
raise TypeError("mnemonic should be string") raise TypeError("mnemonic should be string")
seed = pbkdf2_hmac('sha512', mnemonic.encode(), ("mnemonic"+passphrase).encode(), 2048) seed = hashlib.pbkdf2_hmac('sha512', mnemonic.encode(), ("mnemonic"+passphrase).encode(), 2048)
return seed if not hex else seed.hex() return seed if not hex else seed.hex()

View File

@ -1,3 +1,5 @@
from pybtc.functions.tools import s2rh, bytes_from_hex, int_from_bytes
from pybtc.functions.hash import double_sha256
def merkle_root(tx_hash_list, hex=True): def merkle_root(tx_hash_list, hex=True):
""" """
@ -12,13 +14,14 @@ def merkle_root(tx_hash_list, hex=True):
return tx_hash_list[0] return tx_hash_list[0]
while True: while True:
new_hash_list = list() new_hash_list = list()
append = new_hash_list.append
while tx_hash_list: while tx_hash_list:
h1 = tx_hash_list.pop(0) h1 = tx_hash_list.pop(0)
try: try:
h2 = tx_hash_list.pop(0) h2 = tx_hash_list.pop(0)
except: except:
h2 = h1 h2 = h1
new_hash_list.append(double_sha256(h1 + h2)) append(double_sha256(h1 + h2))
if len(new_hash_list) > 1: if len(new_hash_list) > 1:
tx_hash_list = new_hash_list tx_hash_list = new_hash_list
else: else:
@ -38,21 +41,23 @@ def merkle_branches(tx_hash_list, hex=True):
if len(tx_hash_list) == 1: if len(tx_hash_list) == 1:
return [] return []
tx_hash_list.pop(0) tx_hash_list.pop(0)
branches_append = branches.append
while True: while True:
branches.append(tx_hash_list.pop(0)) branches_append(tx_hash_list.pop(0))
new_hash_list = list() new_hash_list = list()
new_hash_list_append = new_hash_list.append
while tx_hash_list: while tx_hash_list:
h1 = tx_hash_list.pop(0) h1 = tx_hash_list.pop(0)
try: try:
h2 = tx_hash_list.pop(0) h2 = tx_hash_list.pop(0)
except: except:
h2 = h1 h2 = h1
new_hash_list.append(double_sha256(h1 + h2)) new_hash_list_append(double_sha256(h1 + h2))
if len(new_hash_list) > 1: if len(new_hash_list) > 1:
tx_hash_list = new_hash_list tx_hash_list = new_hash_list
else: else:
if new_hash_list: if new_hash_list:
branches.append(new_hash_list.pop(0)) branches_append(new_hash_list.pop(0))
return branches if not hex else [h.hex() for h in branches] return branches if not hex else [h.hex() for h in branches]
@ -65,10 +70,10 @@ def merkleroot_from_branches(merkle_branches, coinbase_hash, hex=True):
:param hex: (optional) If set to True return result in HEX format, by default is True. :param hex: (optional) If set to True return result in HEX format, by default is True.
:return: merkle root in bytes or HEX encoded string corresponding hex flag. :return: merkle root in bytes or HEX encoded string corresponding hex flag.
""" """
merkle_root = coinbase_hash if not isinstance(coinbase_hash, str) else bytes.fromhex(coinbase_hash) merkle_root = coinbase_hash if not isinstance(coinbase_hash, str) else bytes_from_hex(coinbase_hash)
for h in merkle_branches: for h in merkle_branches:
if type(h) == str: if type(h) == str:
h = bytes.fromhex(h) h = bytes_from_hex(h)
merkle_root = double_sha256(merkle_root + h) merkle_root = double_sha256(merkle_root + h)
return merkle_root if not hex else merkle_root.hex() return merkle_root if not hex else merkle_root.hex()
@ -84,9 +89,9 @@ def bits_to_target(bits):
:return: integer. :return: integer.
""" """
if type(bits) == str: if type(bits) == str:
bits = bytes.fromhex(bits) bits = bytes_from_hex(bits)
if type(bits) == bytes: if type(bits) == bytes:
return int.from_bytes(bits[1:], 'big') * (2 ** (8 * (bits[0] - 3))) return int_from_bytes(bits[1:], 'big') * (2 ** (8 * (bits[0] - 3)))
else: else:
shift = bits >> 24 shift = bits >> 24
target = (bits & 0xffffff) * (1 << (8 * (shift - 3))) target = (bits & 0xffffff) * (1 << (8 * (shift - 3)))

View File

@ -1,4 +1,5 @@
from .hash import double_sha256 from pybtc.functions.hash import double_sha256
from pybtc.functions.tools import bytes_from_hex
b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
base32charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" base32charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
@ -19,6 +20,7 @@ def rebasebits(data, frombits, tobits, pad=True):
acc = 0 acc = 0
bits = 0 bits = 0
ret = bytearray() ret = bytearray()
append = ret.append
maxv = (1 << tobits) - 1 maxv = (1 << tobits) - 1
max_acc = (1 << (frombits + tobits - 1)) - 1 max_acc = (1 << (frombits + tobits - 1)) - 1
for value in data: for value in data:
@ -28,10 +30,10 @@ def rebasebits(data, frombits, tobits, pad=True):
bits += frombits bits += frombits
while bits >= tobits: while bits >= tobits:
bits -= tobits bits -= tobits
ret.append((acc >> bits) & maxv) append((acc >> bits) & maxv)
if pad: if pad:
if bits: if bits:
ret.append((acc << (tobits - bits)) & maxv) append((acc << (tobits - bits)) & maxv)
elif bits >= frombits or ((acc << (tobits - bits)) & maxv): elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
raise ValueError("invalid padding") raise ValueError("invalid padding")
return ret return ret
@ -46,12 +48,12 @@ def rebase_8_to_5(data, pad = True):
def rebase_32_to_5(data): def rebase_32_to_5(data):
if type(data) == bytes: if isinstance(data, bytes):
data = data.decode() data = data.decode()
b = bytearray() b = bytearray()
append = b.append
try: try:
for i in data: [append(int_base32_map[i]) for i in data]
b.append(int_base32_map[i])
except: except:
raise Exception("Non base32 characters") raise Exception("Non base32 characters")
return b return b
@ -59,8 +61,8 @@ def rebase_32_to_5(data):
def rebase_5_to_32(data, bytes = True): def rebase_5_to_32(data, bytes = True):
r = bytearray() r = bytearray()
for i in data: append = r.append
r.append(base32_int_map[i]) [append(base32_int_map[i]) for i in data]
return r.decode() if not bytes else r return r.decode() if not bytes else r
@ -80,13 +82,14 @@ def encode_base58(b):
"""Encode bytes to a base58-encoded string""" """Encode bytes to a base58-encoded string"""
# Convert big-endian bytes to integer # Convert big-endian bytes to integer
n= int('0x0' + b.hex(), 16) n= int('0x0%s' % b.hex(), 16)
# Divide that integer into bas58 # Divide that integer into bas58
res = [] res = []
append = res.append
while n > 0: while n > 0:
n, r = divmod(n, 58) n, r = divmod(n, 58)
res.append(b58_digits[r]) append(b58_digits[r])
res = ''.join(res[::-1]) res = ''.join(res[::-1])
# Encode leading zeros as base58 zeros # Encode leading zeros as base58 zeros
czero = 0 czero = 0
@ -114,8 +117,8 @@ def decode_base58(s):
# Convert the integer to bytes # Convert the integer to bytes
h = '%x' % n h = '%x' % n
if len(h) % 2: if len(h) % 2:
h = '0' + h h = '0%s' % h
res = bytes.fromhex(h) res = bytes_from_hex(h)
# Add padding back. # Add padding back.
pad = 0 pad = 0
for c in s[:-1]: for c in s[:-1]:
@ -123,7 +126,7 @@ def decode_base58(s):
pad += 1 pad += 1
else: else:
break break
return b'\x00' * pad + res return b''.join((b'\x00' * pad, res))
def encode_base58_with_checksum(b): def encode_base58_with_checksum(b):
@ -132,5 +135,6 @@ def encode_base58_with_checksum(b):
def decode_base58_with_checksum(s): def decode_base58_with_checksum(s):
b = decode_base58(s) b = decode_base58(s)
assert double_sha256(b[:-4])[:4] == b[-4:] if double_sha256(b[:-4])[:4] != b[-4:]:
raise Exception("invalid checksum")
return b[:-4] return b[:-4]

View File

@ -1,43 +1,39 @@
import hashlib from hashlib import new as hashlib_new
from hashlib import sha256 as hashlib_sha256
from hashlib import sha512 as hashlib_sha512
import hmac import hmac
bytes_from_hex = bytes.fromhex
def sha256(h, hex=False): def sha256(h, hex=False):
if type(h) == str: if isinstance(h, str):
h = bytes.fromhex(h) h = bytes_from_hex(h)
if hex: return hashlib_sha256(h).hexdigest() if hex else hashlib_sha256(h).digest()
return hashlib.sha256(h).hexdigest()
return hashlib.sha256(h).digest()
def double_sha256(h, hex=False): def double_sha256(h, hex=False):
if type(h) == str: if isinstance(h,str):
h = bytes.fromhex(h) h = bytes_from_hex(h)
if hex: return sha256(sha256(h), True) if hex else sha256(sha256(h))
return sha256(sha256(h), 1)
return sha256(sha256(h))
def hmac_sha512(key, data, hex=False): def hmac_sha512(key, data, hex=False):
if hex: if hex:
return hmac.new(key, data, hashlib.sha512).hexdigest() return hmac.new(key, data, hashlib_sha512).hexdigest()
return hmac.new(key, data, hashlib.sha512).digest() return hmac.new(key, data, hashlib_sha512).digest()
def ripemd160(h, hex=False): def ripemd160(h, hex=False):
if type(h) == str: if isinstance(h, str):
h = bytes.fromhex(h) h = bytes_from_hex(h)
a = hashlib.new('ripemd160') a = hashlib_new('ripemd160')
a.update(h) a.update(h)
if hex: return a.hexdigest() if hex else a.digest()
return a.hexdigest()
return a.digest()
def hash160(h, hex=False): def hash160(h, hex=False):
if type(h) == str: if isinstance(h, str):
bytes.fromhex(h) bytes_from_hex(h)
if hex: return ripemd160(sha256(h), True) if hex else ripemd160(sha256(h))
return ripemd160(sha256(h), 1)
return ripemd160(sha256(h))

View File

@ -1,15 +1,14 @@
import os from secp256k1 import ffi, lib
import sys secp256k1_ec_pubkey_create = lib.secp256k1_ec_pubkey_create
from secp256k1 import ffi secp256k1_ec_pubkey_serialize = lib.secp256k1_ec_pubkey_serialize
parentPath = os.path.abspath("../..")
if parentPath not in sys.path:
sys.path.insert(0, parentPath)
from pybtc.constants import * from pybtc.constants import *
from .encode import * from pybtc.functions.encode import encode_base58, decode_base58
from .hash import * from pybtc.functions.hash import double_sha256
from .bip39_mnemonic import generate_entropy from .bip39_mnemonic import generate_entropy
bytes_from_hex = bytes.fromhex
def create_private_key(compressed=True, testnet=False, wif=True, hex=False): def create_private_key(compressed=True, testnet=False, wif=True, hex=False):
""" """
@ -44,7 +43,7 @@ def private_key_to_wif(h, compressed=True, testnet=False):
# uncompressed: 0x80 + [32-byte secret] + [4 bytes of Hash() of previous 33 bytes], base58 encoded. # 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. # compressed: 0x80 + [32-byte secret] + 0x01 + [4 bytes of Hash() previous 34 bytes], base58 encoded.
if isinstance(h, str): if isinstance(h, str):
h = bytes.fromhex(h) h = bytes_from_hex(h)
if len(h) != 32 and isinstance(h, bytes): if len(h) != 32 and isinstance(h, bytes):
raise TypeError("private key must be a 32 bytes or hex encoded string") raise TypeError("private key must be a 32 bytes or hex encoded string")
if testnet: if testnet:
@ -67,9 +66,7 @@ def wif_to_private_key(h, hex=True):
if not is_wif_valid(h): if not is_wif_valid(h):
raise TypeError("invalid wif key") raise TypeError("invalid wif key")
h = decode_base58(h) h = decode_base58(h)
if hex: return h[1:33].hex() if hex else h[1:33]
return h[1:33].hex()
return h[1:33]
def is_wif_valid(wif): def is_wif_valid(wif):
@ -115,23 +112,23 @@ def private_to_public_key(private_key, compressed=True, hex=True):
private_key = bytes(private_key) private_key = bytes(private_key)
elif isinstance(private_key, str): elif isinstance(private_key, str):
if not is_wif_valid(private_key): if not is_wif_valid(private_key):
private_key = bytes.fromhex(private_key) private_key = bytes_from_hex(private_key)
else: else:
if private_key[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX, if private_key[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX,
TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX): TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX):
compressed = False compressed = False
private_key = wif_to_private_key(private_key, hex=0) private_key = wif_to_private_key(private_key, hex=False)
else: else:
raise TypeError("private key must be a bytes or WIF or hex encoded string") raise TypeError("private key must be a bytes or WIF or hex encoded string")
pubkey_ptr = ffi.new('secp256k1_pubkey *') pubkey_ptr = ffi.new('secp256k1_pubkey *')
r = secp256k1.secp256k1_ec_pubkey_create(ECDSA_CONTEXT_ALL, pubkey_ptr, private_key) r = secp256k1_ec_pubkey_create(ECDSA_CONTEXT_ALL, pubkey_ptr, private_key)
if not r: if not r:
raise RuntimeError("secp256k1 error") raise RuntimeError("secp256k1 error")
len_key = 33 if compressed else 65 len_key = 33 if compressed else 65
pubkey = ffi.new('char [%d]' % len_key) pubkey = ffi.new('char [%d]' % len_key)
outlen = ffi.new('size_t *', len_key) outlen = ffi.new('size_t *', len_key)
compflag = EC_COMPRESSED if compressed else EC_UNCOMPRESSED compflag = EC_COMPRESSED if compressed else EC_UNCOMPRESSED
r = secp256k1.secp256k1_ec_pubkey_serialize(ECDSA_CONTEXT_VERIFY, pubkey, outlen, pubkey_ptr, compflag) r = secp256k1_ec_pubkey_serialize(ECDSA_CONTEXT_VERIFY, pubkey, outlen, pubkey_ptr, compflag)
pub = bytes(ffi.buffer(pubkey, len_key)) pub = bytes(ffi.buffer(pubkey, len_key))
if not r: if not r:
raise RuntimeError("secp256k1 error") raise RuntimeError("secp256k1 error")
@ -146,7 +143,7 @@ def is_public_key_valid(key):
:return: boolean. :return: boolean.
""" """
if isinstance(key, str): if isinstance(key, str):
key = bytes.fromhex(key) key = bytes_from_hex(key)
if len(key) < 33: if len(key) < 33:
return False return False
elif key[0] == 0x04 and len(key) != 65: elif key[0] == 0x04 and len(key) != 65:

View File

@ -1,19 +1,28 @@
import os from struct import unpack
import sys
from secp256k1 import ffi from secp256k1 import ffi, lib
parentPath = os.path.abspath("../..") secp256k1_ecdsa_signature_parse_der = lib.secp256k1_ecdsa_signature_parse_der
if parentPath not in sys.path: secp256k1_ec_pubkey_parse = lib.secp256k1_ec_pubkey_parse
sys.path.insert(0, parentPath) secp256k1_ecdsa_verify = lib.secp256k1_ecdsa_verify
secp256k1_ecdsa_sign = lib.secp256k1_ecdsa_sign
secp256k1_ecdsa_signature_serialize_der = lib.secp256k1_ecdsa_signature_serialize_der
secp256k1_ecdsa_signature_serialize_compact = lib.secp256k1_ecdsa_signature_serialize_compact
secp256k1_ecdsa_recoverable_signature_parse_compact = lib.secp256k1_ecdsa_recoverable_signature_parse_compact
secp256k1_ecdsa_recover = lib.secp256k1_ecdsa_recover
secp256k1_ec_pubkey_serialize = lib.secp256k1_ec_pubkey_serialize
from pybtc.opcodes import * from pybtc.opcodes import *
from pybtc.constants import * from pybtc.constants import *
from .tools import *
from .hash import * from pybtc.functions.tools import bytes_from_hex, int_to_bytes, get_stream
from .address import hash_to_address from pybtc.functions.hash import hash160, sha256
from pybtc.functions.address import hash_to_address
from pybtc.functions.key import is_wif_valid, wif_to_private_key
def public_key_to_pubkey_script(key, hex=True): def public_key_to_pubkey_script(key, hex=True):
if isinstance(key, str): if isinstance(key, str):
key = bytes.fromhex(key) key = bytes_from_hex(key)
s = b"%s%s%s" % (bytes([len(key)]), key, OP_CHECKSIG) s = b"%s%s%s" % (bytes([len(key)]), key, OP_CHECKSIG)
return s.hex() if hex else s return s.hex() if hex else s
@ -37,7 +46,7 @@ def parse_script(script, segwit=True):
return {"nType": 7, "type": "NON_STANDARD", "reqSigs": 0, "script": b""} return {"nType": 7, "type": "NON_STANDARD", "reqSigs": 0, "script": b""}
if isinstance(script, str): if isinstance(script, str):
try: try:
script = bytes.fromhex(script) script = bytes_from_hex(script)
except: except:
pass pass
assert isinstance(script, bytes) assert isinstance(script, bytes)
@ -109,12 +118,12 @@ def parse_script(script, segwit=True):
break break
elif script[s] == OPCODE["OP_PUSHDATA2"]: elif script[s] == OPCODE["OP_PUSHDATA2"]:
try: try:
s += 2 + struct.unpack('<H', script[s: s + 2])[0] s += 2 + unpack('<H', script[s: s + 2])[0]
except: except:
break break
elif script[s] == OPCODE["OP_PUSHDATA4"]: elif script[s] == OPCODE["OP_PUSHDATA4"]:
try: try:
s += 4 + struct.unpack('<L', script[s: s + 4])[0] s += 4 + unpack('<L', script[s: s + 4])[0]
except: except:
break break
else: else:
@ -165,7 +174,7 @@ def decode_script(script, asm=False):
if isinstance(script, str): if isinstance(script, str):
try: try:
script = bytes.fromhex(script) script = bytes_from_hex(script)
except: except:
pass pass
if not isinstance(script, bytes): if not isinstance(script, bytes):
@ -193,7 +202,7 @@ def decode_script(script, asm=False):
s += 1 + script[s + 1] + 1 s += 1 + script[s + 1] + 1
elif script[s] == OPCODE["OP_PUSHDATA2"]: elif script[s] == OPCODE["OP_PUSHDATA2"]:
ld = struct.unpack('<H', script[s + 1: s + 3])[0] ld = unpack('<H', script[s + 1: s + 3])[0]
if asm: if asm:
append(script[s + 1:s + 1 + ld].hex()) append(script[s + 1:s + 1 + ld].hex())
else: else:
@ -201,7 +210,7 @@ def decode_script(script, asm=False):
append('[%s]' % ld) append('[%s]' % ld)
s += 2 + 1 + ld s += 2 + 1 + ld
elif script[s] == OPCODE["OP_PUSHDATA4"]: elif script[s] == OPCODE["OP_PUSHDATA4"]:
ld = struct.unpack('<L', script[s + 1: s + 5])[0] ld = unpack('<L', script[s + 1: s + 5])[0]
if asm: if asm:
append(script[s + 1:s + 1 + ld].hex()) append(script[s + 1:s + 1 + ld].hex())
else: else:
@ -216,7 +225,7 @@ def decode_script(script, asm=False):
def delete_from_script(script, sub_script): def delete_from_script(script, sub_script):
""" """
Decode OPCODE or subscript from script. Decode OP_CODE or subscript from script.
:param script: target script in bytes or HEX encoded string. :param script: target script in bytes or HEX encoded string.
:param sub_script: sub_script which is necessary to remove from target script in bytes or HEX encoded string. :param sub_script: sub_script which is necessary to remove from target script in bytes or HEX encoded string.
@ -227,13 +236,13 @@ def delete_from_script(script, sub_script):
s_hex = False s_hex = False
if isinstance(script, str): if isinstance(script, str):
try: try:
script = bytes.fromhex(script) script = bytes_from_hex(script)
s_hex = True s_hex = True
except: except:
pass pass
if isinstance(sub_script, str): if isinstance(sub_script, str):
try: try:
sub_script = bytes.fromhex(sub_script) sub_script = bytes_from_hex(sub_script)
except: except:
pass pass
@ -247,40 +256,42 @@ def delete_from_script(script, sub_script):
s = 0 s = 0
k = 0 k = 0
stack = [] stack = []
stack_append = stack.append
result = [] result = []
result_append = result.append
while l - s > 0: while l - s > 0:
if script[s] < 0x4c and script[s]: if script[s] < 0x4c and script[s]:
stack.append(script[s] + 1) stack_append(script[s] + 1)
s += script[s] + 1 s += script[s] + 1
elif script[s] == OPCODE["OP_PUSHDATA1"]: elif script[s] == OPCODE["OP_PUSHDATA1"]:
stack.append(1 + script[s + 1]) stack_append(1 + script[s + 1])
s += 1 + script[s + 1] s += 1 + script[s + 1]
elif script[s] == OPCODE["OP_PUSHDATA2"]: elif script[s] == OPCODE["OP_PUSHDATA2"]:
stack.append(2 + struct.unpack('<H', script[s: s + 2])) stack_append(2 + unpack('<H', script[s: s + 2])[0])
s += 2 + struct.unpack('<H', script[s: s + 2]) s += 2 + unpack('<H', script[s: s + 2])[0]
elif script[s] == OPCODE["OP_PUSHDATA4"]: elif script[s] == OPCODE["OP_PUSHDATA4"]:
stack.append(4 + struct.unpack('<L', script[s: s + 4])) stack_append(4 + unpack('<L', script[s: s + 4])[0])
s += 4 + struct.unpack('<L', script[s: s + 4]) s += 4 + unpack('<L', script[s: s + 4])[0]
else: else:
stack.append(1) stack_append(1)
s += 1 s += 1
if s - k >= ls: if s - k >= ls:
if script[k:s][:ls] == sub_script: if script[k:s][:ls] == sub_script:
if s - k > ls: if s - k > ls:
result.append(script[k + ls:s]) result_append(script[k + ls:s])
t = 0 t = 0
while t != s - k: while t != s - k:
t += stack.pop(0) t += stack.pop(0)
k = s k = s
else: else:
t = stack.pop(0) t = stack.pop(0)
result.append(script[k:k + t]) result_append(script[k:k + t])
k += t k += t
if script[k:s][:ls] == sub_script: if script[k:s][:ls] == sub_script:
if s - k > ls: if s - k > ls:
result.append(script[k + ls:s]) result_append(script[k + ls:s])
else: else:
result.append(script[k:k + ls]) result_append(script[k:k + ls])
return b''.join(result) if not s_hex else b''.join(result).hex() return b''.join(result) if not s_hex else b''.join(result).hex()
@ -296,7 +307,7 @@ def script_to_hash(script, witness=False, hex=True):
:return: script in bytes or HEX encoded string corresponding to the format of target script. :return: script in bytes or HEX encoded string corresponding to the format of target script.
""" """
if isinstance(script, str): if isinstance(script, str):
s = bytes.fromhex(script) s = bytes_from_hex(script)
if witness: if witness:
return sha256(script, hex) return sha256(script, hex)
else: else:
@ -327,17 +338,18 @@ def get_multisig_public_keys(script):
def read_opcode(stream): def read_opcode(stream):
b = stream.read(1) read = stream.read
b = read(1)
if not b: if not b:
return None, None return None, None
if b[0] <= 0x4b: if b[0] <= 0x4b:
return b, stream.read(b[0]) return b, read(b[0])
elif b[0] == OP_PUSHDATA1: elif b[0] == OP_PUSHDATA1:
return b, stream.read(stream.read(1)[0]) return b, read(read(1)[0])
elif b[0] == OP_PUSHDATA2: elif b[0] == OP_PUSHDATA2:
return b, stream.read(struct.unpack("<H", stream.read(2)[0])) return b, read(unpack("<H", read(2)[0]))
elif b[0] == OP_PUSHDATA4: elif b[0] == OP_PUSHDATA4:
return b, stream.read(struct.unpack("<L", stream.read(4)[0])) return b, read(unpack("<L", read(4)[0]))
else: else:
return b, None return b, None
@ -375,11 +387,11 @@ def verify_signature(sig, pub_key, msg):
raw_sig = ffi.new('secp256k1_ecdsa_signature *') raw_sig = ffi.new('secp256k1_ecdsa_signature *')
raw_pubkey = ffi.new('secp256k1_pubkey *') raw_pubkey = ffi.new('secp256k1_pubkey *')
if not secp256k1.secp256k1_ecdsa_signature_parse_der(ECDSA_CONTEXT_VERIFY, raw_sig, sig, len(sig)): if not secp256k1_ecdsa_signature_parse_der(ECDSA_CONTEXT_VERIFY, raw_sig, sig, len(sig)):
raise TypeError("signature must be DER encoded") raise TypeError("signature must be DER encoded")
if not secp256k1.secp256k1_ec_pubkey_parse(ECDSA_CONTEXT_VERIFY, raw_pubkey, pub_key, len(pub_key)): if not secp256k1_ec_pubkey_parse(ECDSA_CONTEXT_VERIFY, raw_pubkey, pub_key, len(pub_key)):
raise TypeError("public key format error") raise TypeError("public key format error")
result = secp256k1.secp256k1_ecdsa_verify(ECDSA_CONTEXT_VERIFY, raw_sig, msg, raw_pubkey) result = secp256k1_ecdsa_verify(ECDSA_CONTEXT_VERIFY, raw_sig, msg, raw_pubkey)
return True if result else False return True if result else False
@ -396,7 +408,7 @@ def sign_message(msg, private_key, hex=True):
msg = bytes(msg) msg = bytes(msg)
if isinstance(msg, str): if isinstance(msg, str):
try: try:
msg = bytes.fromhex(msg) msg = bytes_from_hex(msg)
except: except:
pass pass
if not isinstance(msg, bytes): if not isinstance(msg, bytes):
@ -406,7 +418,7 @@ def sign_message(msg, private_key, hex=True):
private_key = bytes(private_key) private_key = bytes(private_key)
if isinstance(private_key, str): if isinstance(private_key, str):
try: try:
private_key = bytes.fromhex(private_key) private_key = bytes_from_hex(private_key)
except: except:
if is_wif_valid(private_key): if is_wif_valid(private_key):
private_key = wif_to_private_key(private_key, hex=False) private_key = wif_to_private_key(private_key, hex=False)
@ -414,49 +426,46 @@ def sign_message(msg, private_key, hex=True):
raise TypeError("private key must be a bytes, hex encoded string or in WIF format") raise TypeError("private key must be a bytes, hex encoded string or in WIF format")
raw_sig = ffi.new('secp256k1_ecdsa_signature *') raw_sig = ffi.new('secp256k1_ecdsa_signature *')
signed = secp256k1.secp256k1_ecdsa_sign(ECDSA_CONTEXT_SIGN, raw_sig, msg, signed = secp256k1_ecdsa_sign(ECDSA_CONTEXT_SIGN, raw_sig, msg,
private_key, ffi.NULL, ffi.NULL) private_key, ffi.NULL, ffi.NULL)
if not signed: if not signed:
raise RuntimeError("secp256k1 error") raise RuntimeError("secp256k1 error")
len_sig = 74 len_sig = 74
output = ffi.new('unsigned char[%d]' % len_sig) output = ffi.new('unsigned char[%d]' % len_sig)
outputlen = ffi.new('size_t *', len_sig) outputlen = ffi.new('size_t *', len_sig)
res = secp256k1.secp256k1_ecdsa_signature_serialize_der(ECDSA_CONTEXT_SIGN, res = secp256k1_ecdsa_signature_serialize_der(ECDSA_CONTEXT_SIGN,
output, outputlen, raw_sig) output, outputlen, raw_sig)
if not res: if not res:
raise RuntimeError("secp256k1 error") raise RuntimeError("secp256k1 error")
signature = bytes(ffi.buffer(output, outputlen[0])) signature = bytes(ffi.buffer(output, outputlen[0]))
raw_sig = ffi.new('secp256k1_ecdsa_signature *')
return signature.hex() if hex else signature return signature.hex() if hex else signature
def public_key_recovery(signature, messsage, rec_id, compressed=True, hex=True): def public_key_recovery(signature, messsage, rec_id, compressed=True, hex=True):
if isinstance(signature, str): if isinstance(signature, str):
signature = bytes.fromhex(signature) signature = bytes_from_hex(signature)
if isinstance(messsage, str): if isinstance(messsage, str):
messsage = bytes.fromhex(messsage) messsage = bytes_from_hex(messsage)
raw_sig = ffi.new('secp256k1_ecdsa_signature *') raw_sig = ffi.new('secp256k1_ecdsa_signature *')
r = secp256k1.secp256k1_ecdsa_signature_parse_der(ECDSA_CONTEXT_SIGN, r = secp256k1_ecdsa_signature_parse_der(ECDSA_CONTEXT_SIGN, raw_sig,
raw_sig, signature, len(signature))
signature,
len(signature))
if not r: if not r:
raise RuntimeError("secp256k1 error") raise RuntimeError("secp256k1 error")
compact_sig = ffi.new('unsigned char[%d]' % 64) compact_sig = ffi.new('unsigned char[%d]' % 64)
r = secp256k1.secp256k1_ecdsa_signature_serialize_compact(ECDSA_CONTEXT_VERIFY, r = secp256k1_ecdsa_signature_serialize_compact(ECDSA_CONTEXT_VERIFY,
compact_sig, compact_sig,
raw_sig) raw_sig)
if not r: if not r:
raise RuntimeError("secp256k1 error") raise RuntimeError("secp256k1 error")
recover_sig = ffi.new('secp256k1_ecdsa_recoverable_signature *') recover_sig = ffi.new('secp256k1_ecdsa_recoverable_signature *')
t = secp256k1.secp256k1_ecdsa_recoverable_signature_parse_compact( t = secp256k1_ecdsa_recoverable_signature_parse_compact(
ECDSA_CONTEXT_ALL, recover_sig, compact_sig, rec_id) ECDSA_CONTEXT_ALL, recover_sig, compact_sig, rec_id)
if not r: if not r:
raise RuntimeError("secp256k1 error") raise RuntimeError("secp256k1 error")
pubkey_ptr = ffi.new('secp256k1_pubkey *') pubkey_ptr = ffi.new('secp256k1_pubkey *')
t = secp256k1.secp256k1_ecdsa_recover( t = secp256k1_ecdsa_recover(
ECDSA_CONTEXT_ALL, pubkey_ptr, recover_sig, messsage) ECDSA_CONTEXT_ALL, pubkey_ptr, recover_sig, messsage)
len_key = 33 if compressed else 65 len_key = 33 if compressed else 65
pubkey = ffi.new('char [%d]' % len_key) pubkey = ffi.new('char [%d]' % len_key)
@ -464,7 +473,7 @@ def public_key_recovery(signature, messsage, rec_id, compressed=True, hex=True):
compflag = EC_COMPRESSED if compressed else EC_UNCOMPRESSED compflag = EC_COMPRESSED if compressed else EC_UNCOMPRESSED
if bytes(ffi.buffer(pubkey_ptr.data, 64)) == b"\x00" * 64: if bytes(ffi.buffer(pubkey_ptr.data, 64)) == b"\x00" * 64:
return None return None
r = secp256k1.secp256k1_ec_pubkey_serialize(ECDSA_CONTEXT_VERIFY, pubkey, outlen, pubkey_ptr, compflag) r = secp256k1_ec_pubkey_serialize(ECDSA_CONTEXT_VERIFY, pubkey, outlen, pubkey_ptr, compflag)
if not r: if not r:
raise RuntimeError("secp256k1 error") raise RuntimeError("secp256k1 error")
pub = bytes(ffi.buffer(pubkey, len_key)) pub = bytes(ffi.buffer(pubkey, len_key))

View File

@ -1,6 +1,11 @@
import math from math import ceil
import io from io import BytesIO
import struct from struct import pack, unpack
bytes_from_hex = bytes.fromhex
int_from_bytes = int.from_bytes
def rh2s(raw_hash): def rh2s(raw_hash):
@ -20,11 +25,11 @@ def s2rh(hash_string):
:param raw_hash: transaction hash in bytes string. :param raw_hash: transaction hash in bytes string.
:return: bytes string. :return: bytes string.
""" """
return bytes.fromhex(hash_string)[::-1] return bytes_from_hex(hash_string)[::-1]
def s2rh_step4(hash_string): def s2rh_step4(hash_string):
h = bytes.fromhex(hash_string) h = bytes_from_hex(hash_string)
return reverse_hash(h) return reverse_hash(h)
@ -35,7 +40,7 @@ def reverse_hash(raw_hash):
:param raw_hash: bytes string. :param raw_hash: bytes string.
:return: bytes string. :return: bytes string.
""" """
return struct.pack('>IIIIIIII', *struct.unpack('>IIIIIIII', raw_hash)[::-1])[::-1] return pack('>IIIIIIII', * unpack('>IIIIIIII', raw_hash)[::-1])[::-1]
def bytes_needed(n): def bytes_needed(n):
@ -45,9 +50,7 @@ def bytes_needed(n):
:param n: integer. :param n: integer.
:return: integer. :return: integer.
""" """
if n == 0: return ceil(n.bit_length()/8) if n != 0 else 1
return 1
return math.ceil(n.bit_length()/8)
def int_to_bytes(i, byteorder='big'): def int_to_bytes(i, byteorder='big'):
@ -69,7 +72,7 @@ def bytes_to_int(i, byteorder='big'):
:param byteorder: (optional) byte order 'big' or 'little', by default 'big'. :param byteorder: (optional) byte order 'big' or 'little', by default 'big'.
:return: integer. :return: integer.
""" """
return int.from_bytes(i, byteorder=byteorder, signed=False) return int_from_bytes(i, byteorder=byteorder, signed=False)
# variable integer # variable integer
@ -82,12 +85,12 @@ def int_to_var_int(i):
:return: bytes. :return: bytes.
""" """
if i < 0xfd: if i < 0xfd:
return struct.pack('<B', i) return pack('<B', i)
if i <= 0xffff: if i <= 0xffff:
return b'\xfd%s' % struct.pack('<H', i) return b'\xfd%s' % pack('<H', i)
if i <= 0xffffffff: if i <= 0xffffffff:
return b'\xfe%s' % struct.pack('<L', i) return b'\xfe%s' % pack('<L', i)
return b'\xff%s' % struct.pack('<Q', i) return b'\xff%s' % pack('<Q', i)
def var_int_to_int(data): def var_int_to_int(data):
@ -98,11 +101,11 @@ def var_int_to_int(data):
:return: integer. :return: integer.
""" """
if data[0] == 0xfd: if data[0] == 0xfd:
return struct.unpack('<H', data[1:3])[0] return unpack('<H', data[1:3])[0]
elif data[0] == 0xfe: elif data[0] == 0xfe:
return struct.unpack('<L', data[1:5])[0] return unpack('<L', data[1:5])[0]
elif data[0] == 0xff: elif data[0] == 0xff:
return struct.unpack('<Q', data[1:9])[0] return unpack('<Q', data[1:9])[0]
return data[0] return data[0]
@ -145,8 +148,9 @@ def read_var_int(stream):
:param stream: io.BytesIO stream. :param stream: io.BytesIO stream.
:return: bytes. :return: bytes.
""" """
l = stream.read(1) read = stream.read
return b"".join((l, stream.read(get_var_int_len(l) - 1))) l = read(1)
return b"".join((l, read(get_var_int_len(l) - 1)))
def read_var_list(stream, data_type): def read_var_list(stream, data_type):
@ -157,8 +161,9 @@ def read_var_list(stream, data_type):
:param data_type: list data type. :param data_type: list data type.
:return: list of data_type. :return: list of data_type.
""" """
deserialize = data_type.deserialize
count = var_int_to_int(read_var_int(stream)) count = var_int_to_int(read_var_int(stream))
return [data_type.deserialize(stream) for i in range(count)] return [deserialize(stream) for i in range(count)]
# compressed integer # compressed integer
@ -179,8 +184,8 @@ def int_to_c_int(n, base_bytes=1):
if l <= min_bits + 1: if l <= min_bits + 1:
return n.to_bytes(base_bytes, byteorder="big") return n.to_bytes(base_bytes, byteorder="big")
prefix = 0 prefix = 0
payload_bytes = math.ceil((l)/8) - base_bytes payload_bytes = ceil((l)/8) - base_bytes
extra_bytes = int(math.ceil((l+payload_bytes)/8) - base_bytes) extra_bytes = int(ceil((l+payload_bytes)/8) - base_bytes)
for i in range(extra_bytes): for i in range(extra_bytes):
prefix += 2 ** i prefix += 2 ** i
if l < base_bytes * 8: if l < base_bytes * 8:
@ -189,7 +194,7 @@ def int_to_c_int(n, base_bytes=1):
if prefix.bit_length() % 8: if prefix.bit_length() % 8:
prefix = prefix << 8 - prefix.bit_length() % 8 prefix = prefix << 8 - prefix.bit_length() % 8
n ^= prefix n ^= prefix
return n.to_bytes(math.ceil(n.bit_length()/8), byteorder="big") return n.to_bytes(ceil(n.bit_length()/8), byteorder="big")
def c_int_to_int(b, base_bytes=1): def c_int_to_int(b, base_bytes=1):
@ -212,7 +217,7 @@ def c_int_to_int(b, base_bytes=1):
byte_length += 1 byte_length += 1
v = v << 1 v = v << 1
break break
n = int.from_bytes(b[:byte_length+base_bytes], byteorder="big") n = int_from_bytes(b[:byte_length+base_bytes], byteorder="big")
if byte_length: if byte_length:
return n & ((1 << (byte_length+base_bytes) * 8 - byte_length) - 1) return n & ((1 << (byte_length+base_bytes) * 8 - byte_length) - 1)
return n return n
@ -232,8 +237,8 @@ def c_int_len(n, base_bytes=1):
min_bits = base_bytes * 8 - 1 min_bits = base_bytes * 8 - 1
if l <= min_bits + 1: if l <= min_bits + 1:
return 1 return 1
payload_bytes = math.ceil((l)/8) - base_bytes payload_bytes = ceil((l)/8) - base_bytes
return int(math.ceil((l+payload_bytes)/8)) return int(ceil((l+payload_bytes)/8))
# generic big endian MPI format # generic big endian MPI format
@ -268,7 +273,7 @@ def bn2mpi(v):
if v < 0: if v < 0:
neg = True neg = True
v = -v v = -v
s = struct.pack(b">I", bn_bytes(v, have_ext)) s = pack(b">I", bn_bytes(v, have_ext))
ext = bytearray() ext = bytearray()
if have_ext: if have_ext:
ext.append(0) ext.append(0)
@ -285,7 +290,7 @@ def mpi2bn(s):
if len(s) < 4: if len(s) < 4:
return None return None
s_size = bytes(s[:4]) s_size = bytes(s[:4])
v_len = struct.unpack(b">I", s_size)[0] v_len = unpack(b">I", s_size)[0]
if len(s) != (v_len + 4): if len(s) != (v_len + 4):
return None return None
if v_len == 0: if v_len == 0:
@ -308,9 +313,7 @@ def mpi2bn(s):
def mpi2vch(s): def mpi2vch(s):
r = s[4:] # strip size r = s[4:] # strip size
# if r:
r = r[::-1] # reverse string, converting BE->LE r = r[::-1] # reverse string, converting BE->LE
# else: r=b'\x00'
return r return r
@ -319,7 +322,7 @@ def bn2vch(v):
def vch2mpi(s): def vch2mpi(s):
r = struct.pack(b">I", len(s)) # size r = pack(b">I", len(s)) # size
r += s[::-1] # reverse string, converting LE->BE r += s[::-1] # reverse string, converting LE->BE
return r return r
@ -335,11 +338,11 @@ def b2i(b): return vch2bn(b)
def get_stream(stream): def get_stream(stream):
if type(stream) != io.BytesIO: if not isinstance(stream, BytesIO):
if type(stream) == str: if isinstance(stream, str):
stream = bytes.fromhex(stream) stream = bytes_from_hex(stream)
if type(stream) == bytes: if isinstance(stream, bytes):
stream = io.BytesIO(stream) stream = BytesIO(stream)
else: else:
raise TypeError raise TypeError
return stream return stream

View File

@ -1,14 +1,14 @@
from .hash_functions import * from .hash_functions import *
from .integer import * from .integer import *
from .address_functions import * from .address_functions import *
from .address_class import * from .script_functions import *
from .ecdsa import * from .ecdsa import *
from .mnemonic import *
from .sighash import *
from .address_class import *
from .transaction_deserialize import * from .transaction_deserialize import *
from .transaction_constructor import * from .transaction_constructor import *
from .sighash import *
from .block import * from .block import *
from .mnemonic import *
from .script_functions import *
# from .script_deserialize import * # from .script_deserialize import *
# from .create_transaction import * # from .create_transaction import *

View File

@ -597,7 +597,7 @@ class BlockDeserializeTests(unittest.TestCase):
block_c = "2000000000000000000029a1a0390376afd72db120d698c2d8ebc41c545c7d4bc2b9033c303dd3d09d455da1c587406820a4dd77c5aa3fcc546dcceb1becc639829c4fe6535e815a1a372b405cc0829701010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2303f88e130068747470733a5c6170692e6269746170732e636f6d20f251bec0cb000000ffffffff02c817a804000000001976a914d4e49947b9f545218cd20389244c249d3520545d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000" block_c = "2000000000000000000029a1a0390376afd72db120d698c2d8ebc41c545c7d4bc2b9033c303dd3d09d455da1c587406820a4dd77c5aa3fcc546dcceb1becc639829c4fe6535e815a1a372b405cc0829701010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2303f88e130068747470733a5c6170692e6269746170732e636f6d20f251bec0cb000000ffffffff02c817a804000000001976a914d4e49947b9f545218cd20389244c249d3520545d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000"
block_e = "2000000000000000000029a1a0390376afd72db120d698c2d8ebc41c545c7d4bc2b9033c303dd3d09d455da1c587406820a4dd77c5aa3fcc546dcceb1becc639829c4fe65a815e531a372b405cc0829701010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2303f88e130068747470733a5c6170692e6269746170732e636f6d20f251bec0cb000000ffffffff02c817a804000000001976a914d4e49947b9f545218cd20389244c249d3520545d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000" block_e = "2000000000000000000029a1a0390376afd72db120d698c2d8ebc41c545c7d4bc2b9033c303dd3d09d455da1c587406820a4dd77c5aa3fcc546dcceb1becc639829c4fe65a815e531a372b405cc0829701010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2303f88e130068747470733a5c6170692e6269746170732e636f6d20f251bec0cb000000ffffffff02c817a804000000001976a914d4e49947b9f545218cd20389244c249d3520545d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000"
l = Block(block_a, format="decoded") l = Block(block_a, format="decoded")
f = open('./test/raw_block.txt') f = open('./pybtc/test/raw_block.txt')
fc = f.readline() fc = f.readline()
qt = time.time() qt = time.time()
bt = Block(fc[:-1], format="decoded") bt = Block(fc[:-1], format="decoded")
@ -606,11 +606,11 @@ class BlockDeserializeTests(unittest.TestCase):
import cProfile import cProfile
cProfile.run("import pybtc;" cProfile.run("import pybtc;"
"f = open('./test/raw_block.txt');" "f = open('./pybtc/test/raw_block.txt');"
"fc = f.readline();" "fc = f.readline();"
"pybtc.Block(fc[:-1], format='decoded')") "pybtc.Block(fc[:-1], format='decoded')")
cProfile.run("import pybtc;" cProfile.run("import pybtc;"
"f = open('./test/raw_block.txt');" "f = open('./pybtc/test/raw_block.txt');"
"fc = f.readline();" "fc = f.readline();"
"pybtc.Block(fc[:-1], format='raw')") "pybtc.Block(fc[:-1], format='raw')")
# print(">>>",block.bits) # print(">>>",block.bits)

View File

@ -1,8 +1,23 @@
from struct import unpack
import json import json
from .functions import * from struct import unpack, pack
from .address import PrivateKey, Address, PublicKey, ScriptAddress from math import ceil
from io import BytesIO
from pybtc.constants import *
from pybtc.opcodes import *
from pybtc.functions.tools import (int_to_var_int,
read_var_int,
var_int_to_int,
rh2s,
s2rh,
bytes_from_hex,
get_stream)
from pybtc.functions.script import op_push_data, decode_script, parse_script, sign_message
from pybtc.functions.script import get_multisig_public_keys, read_opcode, is_valid_signature_encoding
from pybtc.functions.script import public_key_recovery, delete_from_script
from pybtc.functions.hash import hash160, sha256, double_sha256
from pybtc.functions.address import hash_to_address, address_net_type, address_to_script
from pybtc.address import PrivateKey, Address, ScriptAddress, PublicKey
class Transaction(dict): class Transaction(dict):
@ -53,31 +68,34 @@ class Transaction(dict):
sw = sw_len = 0 sw = sw_len = 0
stream = self.get_stream(raw_tx) stream = self.get_stream(raw_tx)
start = stream.tell() start = stream.tell()
read = stream.read
tell = stream.tell
seek = stream.seek
# start deserialization # start deserialization
self["version"] = unpack('<L', stream.read(4))[0] self["version"] = unpack('<L', read(4))[0]
n = read_var_int(stream) n = read_var_int(stream)
if n == b'\x00': if n == b'\x00':
# segwit format # segwit format
sw = 1 sw = 1
self["flag"] = stream.read(1) self["flag"] = read(1)
n = read_var_int(stream) n = read_var_int(stream)
# inputs # inputs
ic = var_int_to_int(n) ic = var_int_to_int(n)
for k in range(ic): for k in range(ic):
self["vIn"][k] = dict() self["vIn"][k] = dict()
self["vIn"][k]["txId"] = stream.read(32) self["vIn"][k]["txId"] = read(32)
self["vIn"][k]["vOut"] = unpack('<L', stream.read(4))[0] self["vIn"][k]["vOut"] = unpack('<L', read(4))[0]
self["vIn"][k]["scriptSig"] = stream.read(var_int_to_int(read_var_int(stream))) self["vIn"][k]["scriptSig"] = read(var_int_to_int(read_var_int(stream)))
self["vIn"][k]["sequence"] = unpack('<L', stream.read(4))[0] self["vIn"][k]["sequence"] = unpack('<L', read(4))[0]
# outputs # outputs
for k in range(var_int_to_int(read_var_int(stream))): for k in range(var_int_to_int(read_var_int(stream))):
self["vOut"][k] = dict() self["vOut"][k] = dict()
self["vOut"][k]["value"] = unpack('<Q', stream.read(8))[0] self["vOut"][k]["value"] = unpack('<Q', read(8))[0]
self["amount"] += self["vOut"][k]["value"] self["amount"] += self["vOut"][k]["value"]
self["vOut"][k]["scriptPubKey"] = stream.read(var_int_to_int(read_var_int(stream))) self["vOut"][k]["scriptPubKey"] = read(var_int_to_int(read_var_int(stream)))
s = parse_script(self["vOut"][k]["scriptPubKey"]) s = parse_script(self["vOut"][k]["scriptPubKey"])
self["vOut"][k]["nType"] = s["nType"] self["vOut"][k]["nType"] = s["nType"]
self["vOut"][k]["type"] = s["type"] self["vOut"][k]["type"] = s["type"]
@ -90,22 +108,22 @@ class Transaction(dict):
# witness # witness
if sw: if sw:
sw = stream.tell() - start sw = tell() - start
for k in range(ic): for k in range(ic):
self["vIn"][k]["txInWitness"] = [stream.read(var_int_to_int(read_var_int(stream))) \ self["vIn"][k]["txInWitness"] = [read(var_int_to_int(read_var_int(stream))) \
for c in range(var_int_to_int(read_var_int(stream)))] for c in range(var_int_to_int(read_var_int(stream)))]
sw_len = (stream.tell() - start) - sw + 2 sw_len = (stream.tell() - start) - sw + 2
self["lockTime"] = unpack('<L', stream.read(4))[0] self["lockTime"] = unpack('<L', read(4))[0]
end = stream.tell() end = tell()
stream.seek(start) seek(start)
b = stream.read(end - start) b = read(end - start)
self["rawTx"] = b self["rawTx"] = b
self["size"] = end - start self["size"] = end - start
self["bSize"] = end - start - sw_len self["bSize"] = end - start - sw_len
self["weight"] = self["bSize"] * 3 + self["size"] self["weight"] = self["bSize"] * 3 + self["size"]
self["vSize"] = math.ceil(self["weight"] / 4) self["vSize"] = ceil(self["weight"] / 4)
if ic == 1 and \ if ic == 1 and \
self["vIn"][0]["txId"] == b'\x00' * 32 and \ self["vIn"][0]["txId"] == b'\x00' * 32 and \
self["vIn"][0]["vOut"] == 0xffffffff: self["vIn"][0]["vOut"] == 0xffffffff:
@ -151,10 +169,11 @@ class Transaction(dict):
self["vIn"][i]["scriptSig"] = self["vIn"][i]["scriptSig"].hex() self["vIn"][i]["scriptSig"] = self["vIn"][i]["scriptSig"].hex()
try: try:
t = list() t = list()
append = t.append
for w in self["vIn"][i]["txInWitness"]: for w in self["vIn"][i]["txInWitness"]:
if type(w) == bytes: if type(w) == bytes:
w = w.hex() w = w.hex()
t.append(w) append(w)
self["vIn"][i]["txInWitness"] = t self["vIn"][i]["txInWitness"] = t
except: except:
@ -221,25 +240,26 @@ class Transaction(dict):
if type(self["hash"]) == str: if type(self["hash"]) == str:
self["hash"] = s2rh(self["hash"]) self["hash"] = s2rh(self["hash"])
if type(self["rawTx"]) == str: if type(self["rawTx"]) == str:
self["rawTx"] = bytes.fromhex(self["rawTx"]) self["rawTx"] = bytes_from_hex(self["rawTx"])
for i in self["vIn"]: for i in self["vIn"]:
if type(self["vIn"][i]["txId"]) == str: if type(self["vIn"][i]["txId"]) == str:
self["vIn"][i]["txId"] = s2rh(self["vIn"][i]["txId"]) self["vIn"][i]["txId"] = s2rh(self["vIn"][i]["txId"])
if type(self["vIn"][i]["scriptSig"]) == str: if type(self["vIn"][i]["scriptSig"]) == str:
self["vIn"][i]["scriptSig"] = bytes.fromhex(self["vIn"][i]["scriptSig"]) self["vIn"][i]["scriptSig"] = bytes_from_hex(self["vIn"][i]["scriptSig"])
try: try:
t = list() t = list()
append = t.append
for w in self["vIn"][i]["txInWitness"]: for w in self["vIn"][i]["txInWitness"]:
if type(w) == str: if type(w) == str:
w = bytes.fromhex(w) w = bytes_from_hex(w)
t.append(w) append(w)
self["vIn"][i]["txInWitness"] = t self["vIn"][i]["txInWitness"] = t
except: except:
pass pass
try: try:
if type(self["vIn"][i]["addressHash"]) == str: if type(self["vIn"][i]["addressHash"]) == str:
self["vIn"][i]["addressHash"] = bytes.fromhex(self["vIn"][i]["addressHash"]) self["vIn"][i]["addressHash"] = bytes_from_hex(self["vIn"][i]["addressHash"])
if "address" in self["vIn"][i]: if "address" in self["vIn"][i]:
del self["vIn"][i]["address"] del self["vIn"][i]["address"]
except: except:
@ -253,7 +273,7 @@ class Transaction(dict):
if "scriptPubKeyAsm" in self["vIn"][i]: if "scriptPubKeyAsm" in self["vIn"][i]:
del self["vIn"][i]["scriptPubKeyAsm"] del self["vIn"][i]["scriptPubKeyAsm"]
if "scriptPubKey" in self["vIn"][i]: if "scriptPubKey" in self["vIn"][i]:
self["vIn"][i]["scriptPubKey"] = bytes.fromhex(self["vIn"][i]["scriptPubKey"]) self["vIn"][i]["scriptPubKey"] = bytes_from_hex(self["vIn"][i]["scriptPubKey"])
if "redeemScriptOpcodes" in self["vIn"][i]: if "redeemScriptOpcodes" in self["vIn"][i]:
del self["vIn"][i]["redeemScriptOpcodes"] del self["vIn"][i]["redeemScriptOpcodes"]
if "redeemScriptAsm" in self["vIn"][i]: if "redeemScriptAsm" in self["vIn"][i]:
@ -263,10 +283,10 @@ class Transaction(dict):
for i in self["vOut"]: for i in self["vOut"]:
if type(self["vOut"][i]["scriptPubKey"]) == str: if type(self["vOut"][i]["scriptPubKey"]) == str:
self["vOut"][i]["scriptPubKey"] = bytes.fromhex(self["vOut"][i]["scriptPubKey"]) self["vOut"][i]["scriptPubKey"] = bytes_from_hex(self["vOut"][i]["scriptPubKey"])
try: try:
if type(self["vOut"][i]["addressHash"]) == str: if type(self["vOut"][i]["addressHash"]) == str:
self["vOut"][i]["addressHash"] = bytes.fromhex(self["vOut"][i]["addressHash"]) self["vOut"][i]["addressHash"] = bytes_from_hex(self["vOut"][i]["addressHash"])
if "address" in self["vOut"][i]: if "address" in self["vOut"][i]:
del self["vOut"][i]["address"] del self["vOut"][i]["address"]
except: except:
@ -278,17 +298,17 @@ class Transaction(dict):
if "data" in self: if "data" in self:
if type(self["data"]) == str: if type(self["data"]) == str:
self["data"] = bytes.fromhex(self["data"]) self["data"] = bytes_from_hex(self["data"])
self["format"] = "raw" self["format"] = "raw"
return self return self
@staticmethod @staticmethod
def get_stream(stream): def get_stream(stream):
if type(stream) != io.BytesIO: if type(stream) != BytesIO:
if type(stream) == str: if type(stream) == str:
stream = bytes.fromhex(stream) stream = bytes_from_hex(stream)
if type(stream) == bytes: if type(stream) == bytes:
stream = io.BytesIO(stream) stream = BytesIO(stream)
else: else:
raise TypeError raise TypeError
return stream return stream
@ -304,7 +324,7 @@ class Transaction(dict):
""" """
chunks = [] chunks = []
append = chunks.append append = chunks.append
append(struct.pack('<L', self["version"])) append(pack('<L', self["version"]))
if segwit and self["segwit"]: if segwit and self["segwit"]:
append(b"\x00\x01") append(b"\x00\x01")
append(int_to_var_int(len(self["vIn"]))) append(int_to_var_int(len(self["vIn"])))
@ -313,23 +333,23 @@ class Transaction(dict):
append(self["vIn"][i]['txId']) append(self["vIn"][i]['txId'])
else: else:
append(s2rh(self["vIn"][i]['txId'])) append(s2rh(self["vIn"][i]['txId']))
append(struct.pack('<L', self["vIn"][i]['vOut'])) append(pack('<L', self["vIn"][i]['vOut']))
if isinstance(self["vIn"][i]['scriptSig'], bytes): if isinstance(self["vIn"][i]['scriptSig'], bytes):
append(int_to_var_int(len(self["vIn"][i]['scriptSig']))) append(int_to_var_int(len(self["vIn"][i]['scriptSig'])))
append(self["vIn"][i]['scriptSig']) append(self["vIn"][i]['scriptSig'])
else: else:
append(int_to_var_int(int(len(self["vIn"][i]['scriptSig']) / 2))) append(int_to_var_int(int(len(self["vIn"][i]['scriptSig']) / 2)))
append(bytes.fromhex(self["vIn"][i]['scriptSig'])) append(bytes_from_hex(self["vIn"][i]['scriptSig']))
append(struct.pack('<L', self["vIn"][i]['sequence'])) append(pack('<L', self["vIn"][i]['sequence']))
append(int_to_var_int(len(self["vOut"]))) append(int_to_var_int(len(self["vOut"])))
for i in self["vOut"]: for i in self["vOut"]:
append(struct.pack('<Q', self["vOut"][i]['value'])) append(pack('<Q', self["vOut"][i]['value']))
if isinstance(self["vOut"][i]['scriptPubKey'], bytes): if isinstance(self["vOut"][i]['scriptPubKey'], bytes):
append(int_to_var_int(len(self["vOut"][i]['scriptPubKey']))) append(int_to_var_int(len(self["vOut"][i]['scriptPubKey'])))
append(self["vOut"][i]['scriptPubKey']) append(self["vOut"][i]['scriptPubKey'])
else: else:
append(int_to_var_int(int(len(self["vOut"][i]['scriptPubKey']) / 2))) append(int_to_var_int(int(len(self["vOut"][i]['scriptPubKey']) / 2)))
append(bytes.fromhex(self["vOut"][i]['scriptPubKey'])) append(bytes_from_hex(self["vOut"][i]['scriptPubKey']))
if segwit and self["segwit"]: if segwit and self["segwit"]:
for i in self["vIn"]: for i in self["vIn"]:
append(int_to_var_int(len(self["vIn"][i]['txInWitness']))) append(int_to_var_int(len(self["vIn"][i]['txInWitness'])))
@ -339,8 +359,8 @@ class Transaction(dict):
append(w) append(w)
else: else:
append(int_to_var_int(int(len(w) / 2))) append(int_to_var_int(int(len(w) / 2)))
append(bytes.fromhex(w)) append(bytes_from_hex(w))
append(struct.pack('<L', self['lockTime'])) append(pack('<L', self['lockTime']))
tx = b''.join(chunks) tx = b''.join(chunks)
return tx if not hex else tx.hex() return tx if not hex else tx.hex()
@ -371,7 +391,7 @@ class Transaction(dict):
raise TypeError("tx_id invalid") raise TypeError("tx_id invalid")
if isinstance(script_sig, str): if isinstance(script_sig, str):
script_sig = bytes.fromhex(script_sig) script_sig = bytes_from_hex(script_sig)
if not isinstance(script_sig, bytes) or (len(script_sig) > 520 and input_verify): if not isinstance(script_sig, bytes) or (len(script_sig) > 520 and input_verify):
raise TypeError("script_sig invalid") raise TypeError("script_sig invalid")
@ -394,9 +414,9 @@ class Transaction(dict):
witness = [] witness = []
for w in tx_in_witness: for w in tx_in_witness:
if isinstance(w, str): if isinstance(w, str):
witness.append(bytes.fromhex(w) if self["format"] == "raw" else w) witness.append(bytes_from_hex(w) if self["format"] == "raw" else w)
else: else:
witness.append(w if self["format"] == "raw" else bytes.fromhex(w)) witness.append(w if self["format"] == "raw" else bytes_from_hex(w))
l += 1 + len(w) l += 1 + len(w)
if len(w) >= 0x4c: if len(w) >= 0x4c:
l += 1 l += 1
@ -415,13 +435,13 @@ class Transaction(dict):
# script_pub_key # script_pub_key
if script_pub_key: if script_pub_key:
if isinstance(script_pub_key, str): if isinstance(script_pub_key, str):
script_pub_key = bytes.fromhex(script_pub_key) script_pub_key = bytes_from_hex(script_pub_key)
if not isinstance(script_pub_key, bytes): if not isinstance(script_pub_key, bytes):
raise TypeError("script_pub_key tx invalid") raise TypeError("script_pub_key tx invalid")
if redeem_script: if redeem_script:
if isinstance(redeem_script, str): if isinstance(redeem_script, str):
redeem_script = bytes.fromhex(redeem_script) redeem_script = bytes_from_hex(redeem_script)
if not isinstance(redeem_script, bytes): if not isinstance(redeem_script, bytes):
raise TypeError("redeem_script tx invalid") raise TypeError("redeem_script tx invalid")
@ -485,7 +505,7 @@ class Transaction(dict):
raise Exception("unable to add output, amount value error") raise Exception("unable to add output, amount value error")
if script_pub_key: if script_pub_key:
if isinstance(script_pub_key, str): if isinstance(script_pub_key, str):
script_pub_key = bytes.fromhex(script_pub_key) script_pub_key = bytes_from_hex(script_pub_key)
if not isinstance(script_pub_key, bytes): if not isinstance(script_pub_key, bytes):
raise TypeError("unable to add output, script_pub_key type error") raise TypeError("unable to add output, script_pub_key type error")
else: else:
@ -920,7 +940,7 @@ class Transaction(dict):
script_code = delete_from_script(script_code, BYTE_OPCODE["OP_CODESEPARATOR"]) script_code = delete_from_script(script_code, BYTE_OPCODE["OP_CODESEPARATOR"])
pm = bytearray() pm = bytearray()
pm += b"%s%s" % (struct.pack('<L', self["version"]), pm += b"%s%s" % (pack('<L', self["version"]),
b'\x01' if sighash_type & SIGHASH_ANYONECANPAY else int_to_var_int(len(self["vIn"]))) b'\x01' if sighash_type & SIGHASH_ANYONECANPAY else int_to_var_int(len(self["vIn"])))
for i in self["vIn"]: for i in self["vIn"]:
# skip all other inputs for SIGHASH_ANYONECANPAY case # skip all other inputs for SIGHASH_ANYONECANPAY case
@ -934,15 +954,13 @@ class Transaction(dict):
tx_id = s2rh(tx_id) tx_id = s2rh(tx_id)
if n == i: if n == i:
pm += b"%s%s%s%s%s" % (tx_id, pm += b"%s%s%s%s%s" % (tx_id, pack('<L', self["vIn"][i]["vOut"]),
struct.pack('<L', self["vIn"][i]["vOut"]),
int_to_var_int(len(script_code)), int_to_var_int(len(script_code)),
script_code, script_code, pack('<L', sequence))
struct.pack('<L', sequence))
else: else:
pm += b'%s%s\x00%s' % (tx_id, pm += b'%s%s\x00%s' % (tx_id,
struct.pack('<L', self["vIn"][i]["vOut"]), pack('<L', self["vIn"][i]["vOut"]),
struct.pack('<L', sequence)) pack('<L', sequence))
if (sighash_type & 31) == SIGHASH_NONE: if (sighash_type & 31) == SIGHASH_NONE:
pm += b'\x00' pm += b'\x00'
else: else:
@ -955,7 +973,7 @@ class Transaction(dict):
for i in self["vOut"]: for i in self["vOut"]:
script_pub_key = self["vOut"][i]["scriptPubKey"] script_pub_key = self["vOut"][i]["scriptPubKey"]
if isinstance(self["vOut"][i]["scriptPubKey"], str): if isinstance(self["vOut"][i]["scriptPubKey"], str):
script_pub_key = bytes.fromhex(script_pub_key) script_pub_key = bytes_from_hex(script_pub_key)
if i > n and (sighash_type & 31) == SIGHASH_SINGLE: if i > n and (sighash_type & 31) == SIGHASH_SINGLE:
continue continue
if (sighash_type & 31) == SIGHASH_SINGLE and (n != i): if (sighash_type & 31) == SIGHASH_SINGLE and (n != i):
@ -964,7 +982,7 @@ class Transaction(dict):
pm += b"%s%s%s" % (self["vOut"][i]["value"].to_bytes(8, 'little'), pm += b"%s%s%s" % (self["vOut"][i]["value"].to_bytes(8, 'little'),
int_to_var_int(len(script_pub_key)), int_to_var_int(len(script_pub_key)),
script_pub_key) script_pub_key)
pm += b"%s%s" % (self["lockTime"].to_bytes(4, 'little'), struct.pack(b"<i", sighash_type)) pm += b"%s%s" % (self["lockTime"].to_bytes(4, 'little'), pack(b"<i", sighash_type))
if not preimage: if not preimage:
pm = double_sha256(pm) pm = double_sha256(pm)
return pm if self["format"] == "raw" else rh2s(pm) return pm if self["format"] == "raw" else rh2s(pm)
@ -990,7 +1008,7 @@ class Transaction(dict):
# remove opcode separators # remove opcode separators
pm = bytearray() pm = bytearray()
# 1. nVersion of the transaction (4-byte little endian) # 1. nVersion of the transaction (4-byte little endian)
pm += struct.pack('<L', self["version"]) pm += pack('<L', self["version"])
# 2. hashPrevouts (32-byte hash) # 2. hashPrevouts (32-byte hash)
# 3. hashSequence (32-byte hash) # 3. hashSequence (32-byte hash)
# 4. outpoint (32-byte hash + 4-byte little endian) # 4. outpoint (32-byte hash + 4-byte little endian)
@ -1004,12 +1022,12 @@ class Transaction(dict):
if type(tx_id) == str: if type(tx_id) == str:
tx_id = s2rh(tx_id) tx_id = s2rh(tx_id)
if not (sighash_type & SIGHASH_ANYONECANPAY): if not (sighash_type & SIGHASH_ANYONECANPAY):
hp += b"%s%s" % (tx_id, struct.pack('<L', self["vIn"][i]["vOut"])) hp += b"%s%s" % (tx_id, pack('<L', self["vIn"][i]["vOut"]))
if (sighash_type & 31) != SIGHASH_SINGLE and (sighash_type & 31) != SIGHASH_NONE: if (sighash_type & 31) != SIGHASH_SINGLE and (sighash_type & 31) != SIGHASH_NONE:
hs += struct.pack('<L', self["vIn"][i]["sequence"]) hs += pack('<L', self["vIn"][i]["sequence"])
if i == n: if i == n:
outpoint = b"%s%s" % (tx_id, struct.pack('<L', self["vIn"][i]["vOut"])) outpoint = b"%s%s" % (tx_id, pack('<L', self["vIn"][i]["vOut"]))
n_sequence = struct.pack('<L', self["vIn"][i]["sequence"]) n_sequence = pack('<L', self["vIn"][i]["sequence"])
hash_prevouts = double_sha256(hp) if hp else b'\x00' * 32 hash_prevouts = double_sha256(hp) if hp else b'\x00' * 32
hash_sequence = double_sha256(hs) if hs else b'\x00' * 32 hash_sequence = double_sha256(hs) if hs else b'\x00' * 32
value = amount.to_bytes(8, 'little') value = amount.to_bytes(8, 'little')
@ -1018,7 +1036,7 @@ class Transaction(dict):
for o in self["vOut"]: for o in self["vOut"]:
script_pub_key = self["vOut"][o]["scriptPubKey"] script_pub_key = self["vOut"][o]["scriptPubKey"]
if type(self["vOut"][o]["scriptPubKey"]) == str: if type(self["vOut"][o]["scriptPubKey"]) == str:
script_pub_key = bytes.fromhex(script_pub_key) script_pub_key = bytes_from_hex(script_pub_key)
if (sighash_type & 31) != SIGHASH_SINGLE and (sighash_type & 31) != SIGHASH_NONE: if (sighash_type & 31) != SIGHASH_SINGLE and (sighash_type & 31) != SIGHASH_NONE:
ho += b"%s%s%s" % (self["vOut"][o]["value"].to_bytes(8, 'little'), ho += b"%s%s%s" % (self["vOut"][o]["value"].to_bytes(8, 'little'),
int_to_var_int(len(script_pub_key)), int_to_var_int(len(script_pub_key)),
@ -1031,8 +1049,8 @@ class Transaction(dict):
hash_outputs = double_sha256(ho) if ho else b'\x00' * 32 hash_outputs = double_sha256(ho) if ho else b'\x00' * 32
pm += b"%s%s%s%s%s%s%s%s%s" % (hash_prevouts, hash_sequence, outpoint, pm += b"%s%s%s%s%s%s%s%s%s" % (hash_prevouts, hash_sequence, outpoint,
script_code, value, n_sequence, hash_outputs, script_code, value, n_sequence, hash_outputs,
struct.pack('<L', self["lockTime"]), pack('<L', self["lockTime"]),
struct.pack('<L', sighash_type)) pack('<L', sighash_type))
if not preimage: if not preimage:
pm = double_sha256(pm) pm = double_sha256(pm)
return pm if self["format"] == "raw" else pm.hex() return pm if self["format"] == "raw" else pm.hex()
@ -1055,7 +1073,7 @@ class Transaction(dict):
self["size"] = len(self["rawTx"]) self["size"] = len(self["rawTx"])
self["bSize"] = len(no_segwit_view) self["bSize"] = len(no_segwit_view)
self["weight"] = self["bSize"] * 3 + self["size"] self["weight"] = self["bSize"] * 3 + self["size"]
self["vSize"] = math.ceil(self["weight"] / 4) self["vSize"] = ceil(self["weight"] / 4)
if self["format"] != "raw": if self["format"] != "raw":
self["txId"] = rh2s(self["txId"]) self["txId"] = rh2s(self["txId"])