From 6b3bf768c7fc76658f74d8493451b61b83063d2b Mon Sep 17 00:00:00 2001 From: "ilker.icyuz" Date: Wed, 17 Aug 2022 13:38:22 +0200 Subject: [PATCH 1/4] changes from 0.2.81 --- btcpy/lib/opcodes.py | 232 +++++++++++++++++++++---------------------- btcpy/structs/hd.py | 88 ++++++++-------- btcpy/structs/sig.py | 81 ++++++++------- setup.py | 2 +- 4 files changed, 201 insertions(+), 202 deletions(-) diff --git a/btcpy/lib/opcodes.py b/btcpy/lib/opcodes.py index ac420dc..df44d21 100644 --- a/btcpy/lib/opcodes.py +++ b/btcpy/lib/opcodes.py @@ -1,122 +1,122 @@ class OpCodeConverter(object): opcodes = [('OP_0', 0), - ('OP_PUSHDATA1', 76), - ('OP_PUSHDATA2', 77), - ('OP_PUSHDATA4', 78), - ('OP_1NEGATE', 79), - ('OP_RESERVED', 80), - ('OP_1', 81), - ('OP_2', 82), - ('OP_3', 83), - ('OP_4', 84), - ('OP_5', 85), - ('OP_6', 86), - ('OP_7', 87), - ('OP_8', 88), - ('OP_9', 89), - ('OP_10', 90), - ('OP_11', 91), - ('OP_12', 92), - ('OP_13', 93), - ('OP_14', 94), - ('OP_15', 95), - ('OP_16', 96), - ('OP_NOP', 97), - ('OP_VER', 98), - ('OP_IF', 99), - ('OP_NOTIF', 100), - ('OP_VERIF', 101), - ('OP_VERNOTIF', 102), - ('OP_ELSE', 103), - ('OP_ENDIF', 104), - ('OP_VERIFY', 105), - ('OP_RETURN', 106), - ('OP_TOALTSTACK', 107), - ('OP_FROMALTSTACK', 108), - ('OP_2DROP', 109), - ('OP_2DUP', 110), - ('OP_3DUP', 111), - ('OP_2OVER', 112), - ('OP_2ROT', 113), - ('OP_2SWAP', 114), - ('OP_IFDUP', 115), - ('OP_DEPTH', 116), - ('OP_DROP', 117), - ('OP_DUP', 118), - ('OP_NIP', 119), - ('OP_OVER', 120), - ('OP_PICK', 121), - ('OP_ROLL', 122), - ('OP_ROT', 123), - ('OP_SWAP', 124), - ('OP_TUCK', 125), - ('OP_CAT', 126), - ('OP_SUBSTR', 127), - ('OP_LEFT', 128), - ('OP_RIGHT', 129), - ('OP_SIZE', 130), - ('OP_INVERT', 131), - ('OP_AND', 132), - ('OP_OR', 133), - ('OP_XOR', 134), - ('OP_EQUAL', 135), - ('OP_EQUALVERIFY', 136), - ('OP_RESERVED1', 137), - ('OP_RESERVED2', 138), - ('OP_1ADD', 139), - ('OP_1SUB', 140), - ('OP_2MUL', 141), - ('OP_2DIV', 142), - ('OP_NEGATE', 143), - ('OP_ABS', 144), - ('OP_NOT', 145), - ('OP_0NOTEQUAL', 146), - ('OP_ADD', 147), - ('OP_SUB', 148), - ('OP_MUL', 149), - ('OP_DIV', 150), - ('OP_MOD', 151), - ('OP_LSHIFT', 152), - ('OP_RSHIFT', 153), - ('OP_BOOLAND', 154), - ('OP_BOOLOR', 155), - ('OP_NUMEQUAL', 156), - ('OP_NUMEQUALVERIFY', 157), - ('OP_NUMNOTEQUAL', 158), - ('OP_LESSTHAN', 159), - ('OP_GREATERTHAN', 160), - ('OP_LESSTHANOREQUAL', 161), - ('OP_GREATERTHANOREQUAL', 162), - ('OP_MIN', 163), - ('OP_MAX', 164), - ('OP_WITHIN', 165), - ('OP_RIPEMD160', 166), - ('OP_SHA1', 167), - ('OP_SHA256', 168), - ('OP_HASH160', 169), - ('OP_HASH256', 170), - ('OP_CODESEPARATOR', 171), - ('OP_CHECKSIG', 172), - ('OP_CHECKSIGVERIFY', 173), - ('OP_CHECKMULTISIG', 174), - ('OP_CHECKMULTISIGVERIFY', 175), - ('OP_NOP1', 176), - ('OP_NOP2', 177), - ('OP_CHECKLOCKTIMEVERIFY', 177), - ('OP_NOP3', 178), - ('OP_CHECKSEQUENCEVERIFY', 178), - ('OP_NOP4', 179), - ('OP_NOP5', 180), - ('OP_NOP6', 181), - ('OP_NOP7', 182), - ('OP_NOP8', 183), - ('OP_NOP9', 184), - ('OP_NOP10', 185), - ('OP_NULLDATA', 252), - ('OP_PUBKEYHASH', 253), - ('OP_PUBKEY', 254), - ('OP_INVALIDOPCODE', 255), ] + ('OP_PUSHDATA1', 76), + ('OP_PUSHDATA2', 77), + ('OP_PUSHDATA4', 78), + ('OP_1NEGATE', 79), + ('OP_RESERVED', 80), + ('OP_1', 81), + ('OP_2', 82), + ('OP_3', 83), + ('OP_4', 84), + ('OP_5', 85), + ('OP_6', 86), + ('OP_7', 87), + ('OP_8', 88), + ('OP_9', 89), + ('OP_10', 90), + ('OP_11', 91), + ('OP_12', 92), + ('OP_13', 93), + ('OP_14', 94), + ('OP_15', 95), + ('OP_16', 96), + ('OP_NOP', 97), + ('OP_VER', 98), + ('OP_IF', 99), + ('OP_NOTIF', 100), + ('OP_VERIF', 101), + ('OP_VERNOTIF', 102), + ('OP_ELSE', 103), + ('OP_ENDIF', 104), + ('OP_VERIFY', 105), + ('OP_RETURN', 106), + ('OP_TOALTSTACK', 107), + ('OP_FROMALTSTACK', 108), + ('OP_2DROP', 109), + ('OP_2DUP', 110), + ('OP_3DUP', 111), + ('OP_2OVER', 112), + ('OP_2ROT', 113), + ('OP_2SWAP', 114), + ('OP_IFDUP', 115), + ('OP_DEPTH', 116), + ('OP_DROP', 117), + ('OP_DUP', 118), + ('OP_NIP', 119), + ('OP_OVER', 120), + ('OP_PICK', 121), + ('OP_ROLL', 122), + ('OP_ROT', 123), + ('OP_SWAP', 124), + ('OP_TUCK', 125), + ('OP_CAT', 126), + ('OP_SUBSTR', 127), + ('OP_LEFT', 128), + ('OP_RIGHT', 129), + ('OP_SIZE', 130), + ('OP_INVERT', 131), + ('OP_AND', 132), + ('OP_OR', 133), + ('OP_XOR', 134), + ('OP_EQUAL', 135), + ('OP_EQUALVERIFY', 136), + ('OP_RESERVED1', 137), + ('OP_RESERVED2', 138), + ('OP_1ADD', 139), + ('OP_1SUB', 140), + ('OP_2MUL', 141), + ('OP_2DIV', 142), + ('OP_NEGATE', 143), + ('OP_ABS', 144), + ('OP_NOT', 145), + ('OP_0NOTEQUAL', 146), + ('OP_ADD', 147), + ('OP_SUB', 148), + ('OP_MUL', 149), + ('OP_DIV', 150), + ('OP_MOD', 151), + ('OP_LSHIFT', 152), + ('OP_RSHIFT', 153), + ('OP_BOOLAND', 154), + ('OP_BOOLOR', 155), + ('OP_NUMEQUAL', 156), + ('OP_NUMEQUALVERIFY', 157), + ('OP_NUMNOTEQUAL', 158), + ('OP_LESSTHAN', 159), + ('OP_GREATERTHAN', 160), + ('OP_LESSTHANOREQUAL', 161), + ('OP_GREATERTHANOREQUAL', 162), + ('OP_MIN', 163), + ('OP_MAX', 164), + ('OP_WITHIN', 165), + ('OP_RIPEMD160', 166), + ('OP_SHA1', 167), + ('OP_SHA256', 168), + ('OP_HASH160', 169), + ('OP_HASH256', 170), + ('OP_CODESEPARATOR', 171), + ('OP_CHECKSIG', 172), + ('OP_CHECKSIGVERIFY', 173), + ('OP_CHECKMULTISIG', 174), + ('OP_CHECKMULTISIGVERIFY', 175), + ('OP_NOP1', 176), + ('OP_NOP2', 177), + ('OP_CHECKLOCKTIMEVERIFY', 177), + ('OP_NOP3', 178), + ('OP_CHECKSEQUENCEVERIFY', 178), + ('OP_NOP4', 179), + ('OP_NOP5', 180), + ('OP_NOP6', 181), + ('OP_NOP7', 182), + ('OP_NOP8', 183), + ('OP_NOP9', 184), + ('OP_NOP10', 185), + ('OP_NULLDATA', 252), + ('OP_PUBKEYHASH', 253), + ('OP_PUBKEY', 254), + ('OP_INVALIDOPCODE', 255), ] _opcode_to_int_dict = {op: num for op, num in opcodes} _int_to_opcode_dict = dict({num: op for op, num in opcodes}) diff --git a/btcpy/structs/hd.py b/btcpy/structs/hd.py index b6c2572..07abb32 100644 --- a/btcpy/structs/hd.py +++ b/btcpy/structs/hd.py @@ -26,61 +26,60 @@ class ExtendedKey(HexSerializable, metaclass=ABCMeta): - - master_parent_fingerprint = bytearray([0]*4) + master_parent_fingerprint = bytearray([0] * 4) first_hardened_index = 1 << 31 curve_order = SECP256k1.order - + @classmethod def master(cls, key, chaincode): return cls(key, chaincode, 0, cls.master_parent_fingerprint, 0, hardened=True) - + @classmethod def decode(cls, string, check_network=True): if string[0] not in NETWORKS[net_name()].key_prefixes: raise ValueError('Encoded key not recognised: {}'.format(string)) network = NETWORKS[net_name()].key_prefixes[string[0]] - + if check_network and network != net_name(): raise ValueError('Trying to decode {} key in {} environment'.format(network, net_name())) - + decoded = b58decode_check(string) parser = Parser(bytearray(decoded)) parser >> 4 depth = int.from_bytes(parser >> 1, 'big') fingerprint = parser >> 4 index = int.from_bytes(parser >> 4, 'big') - + if index >= cls.first_hardened_index: index -= cls.first_hardened_index hardened = True else: hardened = False - + chaincode = parser >> 32 keydata = parser >> 33 - + if string[1:4] == 'prv': subclass = ExtendedPrivateKey elif string[1:4] == 'pub': subclass = ExtendedPublicKey else: raise ValueError('Encoded key not recognised: {}'.format(string)) - + key = subclass.decode_key(keydata) - + return subclass(key, chaincode, depth, fingerprint, index, hardened) - + @staticmethod @abstractmethod def decode_key(keydata): raise NotImplemented - + @staticmethod @abstractmethod def get_version(mainnet=None): raise NotImplemented - + def __init__(self, key, chaincode, depth, pfing, index, hardened=False): if not 0 <= depth <= 255: raise ValueError('Depth must be between 0 and 255') @@ -90,7 +89,7 @@ def __init__(self, key, chaincode, depth, pfing, index, hardened=False): self.parent_fingerprint = pfing self.index = index self.hardened = hardened - + def derive(self, path): """ :param path: a path like "m/44'/0'/1'/0/10" if deriving from a master key, @@ -99,13 +98,13 @@ def derive(self, path): the derived ExtendedPrivateKey if deriving from an ExtendedPrivateKey """ steps = path.split('/') - + if steps[0] not in {'m', '.'}: raise ValueError('Invalid derivation path: {}'.format(path)) - + if steps[0] == 'm' and not self.is_master(): raise ValueError('Trying to derive absolute path from non-master key') - + current = self for step in steps[1:]: hardened = False @@ -115,13 +114,13 @@ def derive(self, path): index = int(step) current = current.get_child(index, hardened) # print(current) - + return current @abstractmethod def get_child(self, index, hardened=False): raise NotImplemented - + @abstractmethod def _serialized_public(self): raise NotImplemented @@ -129,7 +128,7 @@ def _serialized_public(self): @abstractmethod def _serialize_key(self): raise NotImplemented - + def get_hash(self, index, hardened=False): cls = self.__class__ if hardened: @@ -141,15 +140,15 @@ def get_hash(self, index, hardened=False): if left > cls.curve_order: raise ValueError('Left side of hmac generated number bigger than SECP256k1 curve order') return left, right - + def is_master(self): return all([self.depth == 0, self.parent_fingerprint == ExtendedKey.master_parent_fingerprint, self.index == 0]) - + def encode(self, mainnet=None): return b58encode_check(bytes(self.serialize(mainnet))) - + def serialize(self, mainnet=None): cls = self.__class__ result = Stream() @@ -163,7 +162,7 @@ def serialize(self, mainnet=None): result << self.chaincode result << self._serialize_key() return result.serialize() - + def __str__(self): return 'version: {}\ndepth: {}\nparent fp: {}\n' \ 'index: {}\nchaincode: {}\nkey: {}\nhardened: {}'.format(self.__class__.get_version(), @@ -173,7 +172,7 @@ def __str__(self): self.chaincode, self.key, self.hardened) - + def __eq__(self, other): return all([self.key == other.key, self.chaincode == other.chaincode, @@ -181,8 +180,8 @@ def __eq__(self, other): self.parent_fingerprint == other.parent_fingerprint, self.index == other.index, self.hardened == other.hardened]) - - + + class ExtendedPrivateKey(ExtendedKey): version_strings = None @@ -196,11 +195,11 @@ def get_version(mainnet=None): if mainnet is None: network = net_name() return bytearray(NETWORKS[net_name()].private_key_version_strings[network]) - + @staticmethod def decode_key(keydata): return PrivateKey(keydata[1:]) - + def __init__(self, key, chaincode, depth, pfing, index, hardened=False): if not isinstance(key, PrivateKey): raise TypeError('ExtendedPrivateKey expects a PrivateKey') @@ -208,7 +207,7 @@ def __init__(self, key, chaincode, depth, pfing, index, hardened=False): def __int__(self): return int.from_bytes(self.key.key, 'big') - + def get_child(self, index, hardened=False): left, right = self.get_hash(index, hardened) k = (int(self) + left) % self.__class__.curve_order @@ -220,16 +219,16 @@ def get_child(self, index, hardened=False): self.get_fingerprint(), index, hardened) - + def get_fingerprint(self): return self.pub().get_fingerprint() - + def _serialize_key(self): return bytearray([0]) + self.key.serialize() - + def _serialized_public(self): return self.pub()._serialize_key() - + def pub(self): return ExtendedPublicKey(self.key.pub(), self.chaincode, @@ -237,7 +236,7 @@ def pub(self): self.parent_fingerprint, self.index, self.hardened) - + class ExtendedPublicKey(ExtendedKey): version_strings = None @@ -252,11 +251,11 @@ def get_version(mainnet=None): if mainnet is None: network = net_name() return bytearray(NETWORKS[net_name()].public_key_version_strings[network]) - + @staticmethod def decode_key(keydata): return PublicKey(keydata) - + def __init__(self, key, chaincode, depth, pfing, index, hardened=False): if not isinstance(key, PublicKey): raise TypeError('ExtendedPublicKey expects a PublicKey') @@ -264,7 +263,7 @@ def __init__(self, key, chaincode, depth, pfing, index, hardened=False): def __int__(self): return int.from_bytes(self.key.key, 'big') - + def get_fingerprint(self): return self.key.hash()[:4] @@ -274,18 +273,19 @@ def get_child(self, index, hardened=False): + VerifyingKey.from_string(self.key.uncompressed[1:], curve=SECP256k1).pubkey.point) if point == INFINITY: raise ValueError('Computed point equals INFINITY') - return ExtendedPublicKey(PublicKey.from_point(point), right, self.depth+1, self.get_fingerprint(), index, False) - + return ExtendedPublicKey(PublicKey.from_point(point), right, self.depth + 1, self.get_fingerprint(), index, + False) + def get_hash(self, index, hardened=False): if hardened: raise ValueError('Trying to generate hardened child from public key') return super().get_hash(index, hardened) - + def _serialized_public(self): return self._serialize_key() - + def _serialize_key(self): return self.key.compressed - + def __lt__(self, other): return self.key < other.key diff --git a/btcpy/structs/sig.py b/btcpy/structs/sig.py index 7ce0978..a963098 100644 --- a/btcpy/structs/sig.py +++ b/btcpy/structs/sig.py @@ -25,7 +25,6 @@ class Branch(Enum): class Sighash(Immutable, HexSerializable): - types = {'ALL': 0x01, 'NONE': 0x02, 'SINGLE': 0x03} @@ -53,7 +52,7 @@ def serialize(self): class Solver(metaclass=ABCMeta): - + @abstractmethod def solve(self, digest): raise NotImplemented @@ -61,39 +60,39 @@ def solve(self, digest): @abstractmethod def get_sighashes(self): raise NotImplemented - + @abstractmethod def solves_segwit(self): raise NotImplemented - + def has_prev_script(self): return False - - + + class SingleSigSolver(Solver, metaclass=ABCMeta): def __init__(self, sighash=Sighash('ALL')): self.sighash = sighash - + def get_sighashes(self): return [self.sighash] def solves_segwit(self): return False - + class SegWitSolver(Solver, metaclass=ABCMeta): - + def solves_segwit(self): return True - + class P2pkhSolver(SingleSigSolver): - + def __init__(self, privk, sighash=Sighash('ALL')): super().__init__(sighash) self.privk = privk - + def solve(self, digest): from .transaction import Witness pubkey = self.privk.pub() @@ -101,21 +100,21 @@ def solve(self, digest): return (ScriptSig.from_stack_data([StackData.from_bytes(sig + self.sighash.as_byte()), StackData.from_bytes(pubkey.compressed)]), Witness([])) - - + + class P2wpkhV0Solver(SegWitSolver, P2pkhSolver): - + def solve(self, digest): script_sig, witness = super().solve(digest) return witness.to_script_sig(), script_sig.to_witness() class P2pkSolver(SingleSigSolver): - + def __init__(self, privk, sighash=Sighash('ALL')): super().__init__(sighash) self.privk = privk - + def solve(self, digest): from .transaction import Witness sig = self.privk.sign(digest) @@ -124,35 +123,35 @@ def solve(self, digest): class P2shSolver(Solver): - + def __init__(self, redeem_script, redeem_script_solver): self.redeem_script = redeem_script self.redeem_script_solver = redeem_script_solver - + def solve(self, *digests): script_sig, witness = self.redeem_script_solver.solve(*digests) script_sig_data = script_sig.get_data() script_sig_data.append(self.redeem_script.to_stack_data()) return ScriptSig.from_stack_data(script_sig_data), witness - + def get_sighashes(self): return self.redeem_script_solver.get_sighashes() def solves_segwit(self): return self.redeem_script_solver.solves_segwit() - + def get_prev_script(self): if self.redeem_script_solver.has_prev_script(): return self.redeem_script_solver.get_prev_script() else: return self.redeem_script - + def has_prev_script(self): return True class P2wshV0Solver(SegWitSolver): - + def __init__(self, witness_script, witness_script_solver): self.witness_script = witness_script self.witness_script_solver = witness_script_solver @@ -164,19 +163,19 @@ def solve(self, *digests): (script_sig.to_witness() + witness + Witness([self.get_prev_script().to_stack_data()]))) - + def get_sighashes(self): return self.witness_script_solver.get_sighashes() - + def get_prev_script(self): return self.witness_script - + def has_prev_script(self): return True class MultisigSolver(Solver): - + def __init__(self, *privkeys, sighashes=None): if sighashes is None: sighashes = [Sighash('ALL') for _ in privkeys] @@ -184,7 +183,7 @@ def __init__(self, *privkeys, sighashes=None): raise ValueError('{} privkeys provided and {} sighashes'.format(len(privkeys), len(sighashes))) self.privkeys = privkeys self.sighashes = sighashes - + def solve(self, *digests): from .transaction import Witness if len(digests) != len(self.privkeys): @@ -194,26 +193,26 @@ def solve(self, *digests): sig = priv.sign(digest) script_sig_data.append(StackData.from_bytes(sig + sighash.as_byte())) return ScriptSig.from_stack_data(script_sig_data), Witness([]) - + def get_sighashes(self): return self.sighashes def solves_segwit(self): return False - + class IfElseSolver(Solver): - + def __init__(self, branch, inner_solver): self.branch = branch self.inner_solver = inner_solver - + def solve(self, *digests): script_sig, witness = self.inner_solver.solve(*digests) script_sig_data = script_sig.get_data() script_sig_data.append(StackData.from_int(self.branch.value)) return ScriptSig.from_stack_data(script_sig_data), witness - + def get_sighashes(self): return self.inner_solver.get_sighashes() @@ -222,32 +221,32 @@ def solves_segwit(self): class TimelockSolver(Solver): - + def __init__(self, inner_solver): self.inner_solver = inner_solver - + def solve(self, *digests): return self.inner_solver.solve(*digests) - + def get_sighashes(self): return self.inner_solver.get_sighashes() def solves_segwit(self): return self.inner_solver.solves_segwit() - - + + class HashlockSolver(Solver): - + def __init__(self, preimage, inner_solver): self.preimage = preimage self.inner_solver = inner_solver - + def solve(self, *digests): script_sig, witness = self.inner_solver.solve(*digests) script_sig_data = script_sig.get_data() script_sig_data.append(StackData.from_bytes(self.preimage)) return ScriptSig.from_stack_data(script_sig_data), witness - + def get_sighashes(self): return self.inner_solver.get_sighashes() diff --git a/setup.py b/setup.py index 15154b9..632e1af 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ from setuptools import find_packages setup(name='chainside-btcpy-multi', - version='0.2.80', + version='0.2.81', packages=find_packages(), install_requires=['ecdsa>=0.15.0,<0.16.0'], extras_require={'develop': ['python-bitcoinlib==0.7.0']}, From beede63ec5589e973e5053d03745885f3db776bf Mon Sep 17 00:00:00 2001 From: "ilker.icyuz" Date: Wed, 17 Aug 2022 13:40:32 +0200 Subject: [PATCH 2/4] fix missing ripemd160 in openssl by creating a new one in this repo --- btcpy/structs/crypto.py | 6 +- btcpy/structs/script.py | 6 +- btcpy/utils/py_ripemd160.py | 107 ++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 6 deletions(-) create mode 100644 btcpy/utils/py_ripemd160.py diff --git a/btcpy/structs/crypto.py b/btcpy/structs/crypto.py index 4ecc089..a43a0c4 100644 --- a/btcpy/structs/crypto.py +++ b/btcpy/structs/crypto.py @@ -16,6 +16,7 @@ from functools import partial from abc import ABCMeta +from btcpy.utils.py_ripemd160 import ripemd160 from ..lib.base58 import b58decode_check, b58encode_check from ..lib.types import HexSerializable from .address import Address, SegWitAddress @@ -197,9 +198,8 @@ def hash(self): import hashlib original = self.uncompressed if self.type == 'uncompressed' else self.compressed sha = hashlib.sha256(original).digest() - ripe = hashlib.new('ripemd160') - ripe.update(sha) - return bytearray(ripe.digest()) + ripe = ripemd160(sha) + return bytearray(ripe) def serialize(self): return self.uncompressed if self.type == 'uncompressed' else self.compressed diff --git a/btcpy/structs/script.py b/btcpy/structs/script.py index 48d639d..8e7971d 100644 --- a/btcpy/structs/script.py +++ b/btcpy/structs/script.py @@ -15,6 +15,7 @@ from binascii import hexlify, unhexlify from abc import ABCMeta, abstractmethod +from btcpy.utils.py_ripemd160 import ripemd160 from ..lib.types import HexSerializable, Immutable, cached from ..lib.parsing import ScriptParser, Parser, Stream, UnexpectedOperationFound from ..lib.opcodes import OpCodeConverter @@ -418,9 +419,8 @@ def type(self): @cached def p2sh_hash(self): - ripemd160 = hashlib.new('ripemd160') - ripemd160.update(hashlib.sha256(self.body).digest()) - return bytearray(ripemd160.digest()) + ripe = ripemd160(hashlib.sha256(self.body).digest()) + return bytearray(ripe) @cached def p2wsh_hash(self): diff --git a/btcpy/utils/py_ripemd160.py b/btcpy/utils/py_ripemd160.py new file mode 100644 index 0000000..2a3b22c --- /dev/null +++ b/btcpy/utils/py_ripemd160.py @@ -0,0 +1,107 @@ +# Copyright (c) 2021 Pieter Wuille +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Pure Python RIPEMD160 implementation.""" + +# Message schedule indexes for the left path. +ML = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, + 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, + 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, + 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13 +] + +# Message schedule indexes for the right path. +MR = [ + 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, + 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, + 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, + 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, + 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11 +] + +# Rotation counts for the left path. +RL = [ + 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, + 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, + 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, + 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, + 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 +] + +# Rotation counts for the right path. +RR = [ + 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, + 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, + 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, + 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, + 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 +] + +# K constants for the left path. +KL = [0, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e] + +# K constants for the right path. +KR = [0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0] + + +def fi(x, y, z, i): + """The f1, f2, f3, f4, and f5 functions from the specification.""" + if i == 0: + return x ^ y ^ z + elif i == 1: + return (x & y) | (~x & z) + elif i == 2: + return (x | ~y) ^ z + elif i == 3: + return (x & z) | (y & ~z) + elif i == 4: + return x ^ (y | ~z) + else: + assert False + + +def rol(x, i): + """Rotate the bottom 32 bits of x left by i bits.""" + return ((x << i) | ((x & 0xffffffff) >> (32 - i))) & 0xffffffff + + +def compress(h0, h1, h2, h3, h4, block): + """Compress state (h0, h1, h2, h3, h4) with block.""" + # Left path variables. + al, bl, cl, dl, el = h0, h1, h2, h3, h4 + # Right path variables. + ar, br, cr, dr, er = h0, h1, h2, h3, h4 + # Message variables. + x = [int.from_bytes(block[4*i:4*(i+1)], 'little') for i in range(16)] + + # Iterate over the 80 rounds of the compression. + for j in range(80): + rnd = j >> 4 + # Perform left side of the transformation. + al = rol(al + fi(bl, cl, dl, rnd) + x[ML[j]] + KL[rnd], RL[j]) + el + al, bl, cl, dl, el = el, al, bl, rol(cl, 10), dl + # Perform right side of the transformation. + ar = rol(ar + fi(br, cr, dr, 4 - rnd) + x[MR[j]] + KR[rnd], RR[j]) + er + ar, br, cr, dr, er = er, ar, br, rol(cr, 10), dr + + # Compose old state, left transform, and right transform into new state. + return h1 + cl + dr, h2 + dl + er, h3 + el + ar, h4 + al + br, h0 + bl + cr + + +def ripemd160(data): + """Compute the RIPEMD-160 hash of data.""" + # Initialize state. + state = (0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0) + # Process full 64-byte blocks in the input. + for b in range(len(data) >> 6): + state = compress(*state, data[64*b:64*(b+1)]) + # Construct final blocks (with padding and size). + pad = b"\x80" + b"\x00" * ((119 - len(data)) & 63) + fin = data[len(data) & ~63:] + pad + (8 * len(data)).to_bytes(8, 'little') + # Process final blocks. + for b in range(len(fin) >> 6): + state = compress(*state, fin[64*b:64*(b+1)]) + # Produce output. + return b"".join((h & 0xffffffff).to_bytes(4, 'little') for h in state) \ No newline at end of file From b1be92c5f84211a8f3fc3218e0f5a15529c8e5e7 Mon Sep 17 00:00:00 2001 From: "ilker.icyuz" Date: Wed, 17 Aug 2022 17:16:01 +0200 Subject: [PATCH 3/4] fix curve order returning in mpz type by casting to int --- btcpy/structs/hd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/btcpy/structs/hd.py b/btcpy/structs/hd.py index 07abb32..c9abac4 100644 --- a/btcpy/structs/hd.py +++ b/btcpy/structs/hd.py @@ -210,7 +210,7 @@ def __int__(self): def get_child(self, index, hardened=False): left, right = self.get_hash(index, hardened) - k = (int(self) + left) % self.__class__.curve_order + k = int((int(self) + left) % self.__class__.curve_order) if k == 0: raise ValueError('Got 0 as k') return ExtendedPrivateKey(PrivateKey(k.to_bytes(32, 'big')), From 73ce5a97804a589e590a630031e41553715695d9 Mon Sep 17 00:00:00 2001 From: "ilker.icyuz" Date: Wed, 17 Aug 2022 17:30:09 +0200 Subject: [PATCH 4/4] add __init__.py to utils --- btcpy/utils/__init__.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 btcpy/utils/__init__.py diff --git a/btcpy/utils/__init__.py b/btcpy/utils/__init__.py new file mode 100644 index 0000000..9007c83 --- /dev/null +++ b/btcpy/utils/__init__.py @@ -0,0 +1,11 @@ +# Copyright (C) 2017 chainside srl +# +# This file is part of the btcpy package. +# +# It is subject to the license terms in the LICENSE.md file found in the top-level +# directory of this distribution. +# +# No part of btcpy, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE.md file. +