diff --git a/electrum/address_synchronizer.py b/electrum/address_synchronizer.py index 437da067b..413409b84 100644 --- a/electrum/address_synchronizer.py +++ b/electrum/address_synchronizer.py @@ -341,7 +341,7 @@ class AddressSynchronizer(Logger, EventListener): for n, txo in enumerate(tx.outputs()): v = txo.value ser = tx_hash + ':%d'%n - scripthash = bitcoin.script_to_scripthash(txo.scriptpubkey.hex()) + scripthash = bitcoin.script_to_scripthash(txo.scriptpubkey) self.db.add_prevout_by_scripthash(scripthash, prevout=TxOutpoint.from_str(ser), value=v) addr = txo.address if addr and self.is_mine(addr): @@ -407,7 +407,7 @@ class AddressSynchronizer(Logger, EventListener): self.unconfirmed_tx.pop(tx_hash, None) if tx: for idx, txo in enumerate(tx.outputs()): - scripthash = bitcoin.script_to_scripthash(txo.scriptpubkey.hex()) + scripthash = bitcoin.script_to_scripthash(txo.scriptpubkey) prevout = TxOutpoint(bfh(tx_hash), idx) self.db.remove_prevout_by_scripthash(scripthash, prevout=prevout, value=txo.value) util.trigger_callback('adb_removed_tx', self, tx_hash, tx) diff --git a/electrum/bip32.py b/electrum/bip32.py index 85cc27fdb..834b64155 100644 --- a/electrum/bip32.py +++ b/electrum/bip32.py @@ -11,7 +11,7 @@ from .util import bfh, BitcoinException from . import constants from . import ecc from .crypto import hash_160, hmac_oneshot -from .bitcoin import rev_hex, int_to_hex, EncodeBase58Check, DecodeBase58Check +from .bitcoin import EncodeBase58Check, DecodeBase58Check from .logging import get_logger @@ -49,7 +49,7 @@ def CKD_priv(parent_privkey: bytes, parent_chaincode: bytes, child_index: int) - is_hardened_child = bool(child_index & BIP32_PRIME) return _CKD_priv(parent_privkey=parent_privkey, parent_chaincode=parent_chaincode, - child_index=bfh(rev_hex(int_to_hex(child_index, 4))), + child_index=int.to_bytes(child_index, length=4, byteorder="big", signed=False), is_hardened_child=is_hardened_child) @@ -85,7 +85,7 @@ def CKD_pub(parent_pubkey: bytes, parent_chaincode: bytes, child_index: int) -> if child_index & BIP32_PRIME: raise Exception('not possible to derive hardened child from parent pubkey') return _CKD_pub(parent_pubkey=parent_pubkey, parent_chaincode=parent_chaincode, - child_index=bfh(rev_hex(int_to_hex(child_index, 4)))) + child_index=int.to_bytes(child_index, length=4, byteorder="big", signed=False)) # helper function, callable with arbitrary 'child_index' byte-string. diff --git a/electrum/bitcoin.py b/electrum/bitcoin.py index 43ada289a..9e5a57ad4 100644 --- a/electrum/bitcoin.py +++ b/electrum/bitcoin.py @@ -197,34 +197,14 @@ class opcodes(IntEnum): return bytes([self]).hex() -def rev_hex(s: str) -> str: - return bfh(s)[::-1].hex() - - -def int_to_hex(i: int, length: int=1) -> str: - """Converts int to little-endian hex string. - `length` is the number of bytes available - """ - if not isinstance(i, int): - raise TypeError('{} instead of int'.format(i)) - range_size = pow(256, length) - if i < -(range_size//2) or i >= range_size: - raise OverflowError('cannot convert int {} to hex ({} bytes)'.format(i, length)) - if i < 0: - # two's complement - i = range_size + i - s = hex(i)[2:].rstrip('L') - s = "0"*(2*length - len(s)) + s - return rev_hex(s) - -def script_num_to_hex(i: int) -> str: +def script_num_to_bytes(i: int) -> bytes: """See CScriptNum in Bitcoin Core. - Encodes an integer as hex, to be used in script. + Encodes an integer as bytes, to be used in script. ported from https://github.com/bitcoin/bitcoin/blob/8cbc5c4be4be22aca228074f087a374a7ec38be8/src/script/script.h#L326 """ if i == 0: - return '' + return b"" result = bytearray() neg = i < 0 @@ -238,104 +218,102 @@ def script_num_to_hex(i: int) -> str: elif neg: result[-1] |= 0x80 - return result.hex() + return bytes(result) -def var_int(i: int) -> str: +def var_int(i: int) -> bytes: # https://en.bitcoin.it/wiki/Protocol_specification#Variable_length_integer # https://github.com/bitcoin/bitcoin/blob/efe1ee0d8d7f82150789f1f6840f139289628a2b/src/serialize.h#L247 # "CompactSize" assert i >= 0, i - if i<0xfd: - return int_to_hex(i) - elif i<=0xffff: - return "fd"+int_to_hex(i,2) - elif i<=0xffffffff: - return "fe"+int_to_hex(i,4) + if i < 0xfd: + return int.to_bytes(i, length=1, byteorder="little", signed=False) + elif i <= 0xffff: + return b"\xfd" + int.to_bytes(i, length=2, byteorder="little", signed=False) + elif i <= 0xffffffff: + return b"\xfe" + int.to_bytes(i, length=4, byteorder="little", signed=False) else: - return "ff"+int_to_hex(i,8) + return b"\xff" + int.to_bytes(i, length=8, byteorder="little", signed=False) -def witness_push(item: str) -> str: - """Returns data in the form it should be present in the witness. - hex -> hex - """ - return var_int(len(item) // 2) + item +def witness_push(item: bytes) -> bytes: + """Returns data in the form it should be present in the witness.""" + return var_int(len(item)) + item -def _op_push(i: int) -> str: +def _op_push(i: int) -> bytes: if i < opcodes.OP_PUSHDATA1: - return int_to_hex(i) + return int.to_bytes(i, length=1, byteorder="little", signed=False) elif i <= 0xff: - return opcodes.OP_PUSHDATA1.hex() + int_to_hex(i, 1) + return bytes([opcodes.OP_PUSHDATA1]) + int.to_bytes(i, length=1, byteorder="little", signed=False) elif i <= 0xffff: - return opcodes.OP_PUSHDATA2.hex() + int_to_hex(i, 2) + return bytes([opcodes.OP_PUSHDATA2]) + int.to_bytes(i, length=2, byteorder="little", signed=False) else: - return opcodes.OP_PUSHDATA4.hex() + int_to_hex(i, 4) + return bytes([opcodes.OP_PUSHDATA4]) + int.to_bytes(i, length=4, byteorder="little", signed=False) -def push_script(data: str) -> str: +def push_script(data: bytes) -> bytes: """Returns pushed data to the script, automatically choosing canonical opcodes depending on the length of the data. - hex -> hex ported from https://github.com/btcsuite/btcd/blob/fdc2bc867bda6b351191b5872d2da8270df00d13/txscript/scriptbuilder.go#L128 """ - data = bfh(data) data_len = len(data) # "small integer" opcodes if data_len == 0 or data_len == 1 and data[0] == 0: - return opcodes.OP_0.hex() + return bytes([opcodes.OP_0]) elif data_len == 1 and data[0] <= 16: - return bytes([opcodes.OP_1 - 1 + data[0]]).hex() + return bytes([opcodes.OP_1 - 1 + data[0]]) elif data_len == 1 and data[0] == 0x81: - return opcodes.OP_1NEGATE.hex() + return bytes([opcodes.OP_1NEGATE]) - return _op_push(data_len) + data.hex() + return _op_push(data_len) + data -def make_op_return(x:bytes) -> bytes: - return bytes([opcodes.OP_RETURN]) + bytes.fromhex(push_script(x.hex())) +def make_op_return(x: bytes) -> bytes: + return bytes([opcodes.OP_RETURN]) + push_script(x) def add_number_to_script(i: int) -> bytes: - return bfh(push_script(script_num_to_hex(i))) + return push_script(script_num_to_bytes(i)) -def construct_witness(items: Sequence[Union[str, int, bytes]]) -> str: +def construct_witness(items: Sequence[Union[str, int, bytes]]) -> bytes: """Constructs a witness from the given stack items.""" - witness = var_int(len(items)) + witness = bytearray() + witness += var_int(len(items)) for item in items: if type(item) is int: - item = script_num_to_hex(item) + item = script_num_to_bytes(item) elif isinstance(item, (bytes, bytearray)): - item = item.hex() + pass # use as-is else: - assert is_hex_str(item) + assert is_hex_str(item), repr(item) + item = bfh(item) witness += witness_push(item) - return witness + return bytes(witness) -def construct_script(items: Sequence[Union[str, int, bytes, opcodes]], values=None) -> str: +def construct_script(items: Sequence[Union[str, int, bytes, opcodes]], values=None) -> bytes: """Constructs bitcoin script from given items.""" - script = '' + script = bytearray() values = values or {} for i, item in enumerate(items): if i in values: item = values[i] if isinstance(item, opcodes): - script += item.hex() + script += bytes([item]) elif type(item) is int: - script += add_number_to_script(item).hex() + script += add_number_to_script(item) elif isinstance(item, (bytes, bytearray)): - script += push_script(item.hex()) + script += push_script(item) elif isinstance(item, str): assert is_hex_str(item) - script += push_script(item) + script += push_script(bfh(item)) else: raise Exception(f'unexpected item for script: {item!r}') - return script + return bytes(script) def relayfee(network: 'Network' = None) -> int: @@ -412,15 +390,11 @@ def hash_to_segwit_addr(h: bytes, witver: int, *, net=None) -> str: def public_key_to_p2wpkh(public_key: bytes, *, net=None) -> str: return hash_to_segwit_addr(hash_160(public_key), witver=0, net=net) -def script_to_p2wsh(script: str, *, net=None) -> str: - return hash_to_segwit_addr(sha256(bfh(script)), witver=0, net=net) - -def p2wpkh_nested_script(pubkey: str) -> str: - pkh = hash_160(bfh(pubkey)) - return construct_script([0, pkh]) +def script_to_p2wsh(script: bytes, *, net=None) -> str: + return hash_to_segwit_addr(sha256(script), witver=0, net=net) -def p2wsh_nested_script(witness_script: str) -> str: - wsh = sha256(bfh(witness_script)) +def p2wsh_nested_script(witness_script: bytes) -> bytes: + wsh = sha256(witness_script) return construct_script([0, wsh]) def pubkey_to_address(txin_type: str, pubkey: str, *, net=None) -> str: @@ -430,27 +404,28 @@ def pubkey_to_address(txin_type: str, pubkey: str, *, net=None) -> str: # TODO this method is confusingly named -def redeem_script_to_address(txin_type: str, scriptcode: str, *, net=None) -> str: +def redeem_script_to_address(txin_type: str, scriptcode: bytes, *, net=None) -> str: + assert isinstance(scriptcode, bytes) if txin_type == 'p2sh': # given scriptcode is a redeem_script - return hash160_to_p2sh(hash_160(bfh(scriptcode)), net=net) + return hash160_to_p2sh(hash_160(scriptcode), net=net) elif txin_type == 'p2wsh': # given scriptcode is a witness_script return script_to_p2wsh(scriptcode, net=net) elif txin_type == 'p2wsh-p2sh': # given scriptcode is a witness_script redeem_script = p2wsh_nested_script(scriptcode) - return hash160_to_p2sh(hash_160(bfh(redeem_script)), net=net) + return hash160_to_p2sh(hash_160(redeem_script), net=net) else: raise NotImplementedError(txin_type) -def script_to_address(script: str, *, net=None) -> Optional[str]: +def script_to_address(script: bytes, *, net=None) -> Optional[str]: from .transaction import get_address_from_output_script - return get_address_from_output_script(bfh(script), net=net) + return get_address_from_output_script(script, net=net) -def address_to_script(addr: str, *, net=None) -> str: +def address_to_script(addr: str, *, net=None) -> bytes: if net is None: net = constants.net if not is_address(addr, net=net): raise BitcoinException(f"invalid bitcoin address: {addr}") @@ -461,7 +436,7 @@ def address_to_script(addr: str, *, net=None) -> str: return construct_script([witver, bytes(witprog)]) addrtype, hash_160_ = b58_address_to_hash160(addr) if addrtype == net.ADDRTYPE_P2PKH: - script = pubkeyhash_to_p2pkh_script(hash_160_.hex()) + script = pubkeyhash_to_p2pkh_script(hash_160_) elif addrtype == net.ADDRTYPE_P2SH: script = construct_script([opcodes.OP_HASH160, hash_160_, opcodes.OP_EQUAL]) else: @@ -514,14 +489,12 @@ def address_to_scripthash(addr: str, *, net=None) -> str: return script_to_scripthash(script) -def script_to_scripthash(script: str) -> str: - h = sha256(bfh(script))[0:32] +def script_to_scripthash(script: bytes) -> str: + h = sha256(script) return h[::-1].hex() -def public_key_to_p2pk_script(pubkey: str) -> str: - return construct_script([pubkey, opcodes.OP_CHECKSIG]) -def pubkeyhash_to_p2pkh_script(pubkey_hash160: str) -> str: +def pubkeyhash_to_p2pkh_script(pubkey_hash160: bytes) -> bytes: return construct_script([ opcodes.OP_DUP, opcodes.OP_HASH160, @@ -756,7 +729,7 @@ def minikey_to_private_key(text: str) -> bytes: def _get_dummy_address(purpose: str) -> str: - return redeem_script_to_address('p2wsh', sha256(bytes(purpose, "utf8")).hex()) + return redeem_script_to_address('p2wsh', sha256(bytes(purpose, "utf8"))) _dummy_addr_funcs = set() class DummyAddress: diff --git a/electrum/blockchain.py b/electrum/blockchain.py index f7af34da1..65b83f554 100644 --- a/electrum/blockchain.py +++ b/electrum/blockchain.py @@ -26,7 +26,7 @@ import time from typing import Optional, Dict, Mapping, Sequence, TYPE_CHECKING from . import util -from .bitcoin import hash_encode, int_to_hex, rev_hex +from .bitcoin import hash_encode from .crypto import sha256d from . import constants from .util import bfh, with_lock @@ -49,13 +49,14 @@ class MissingHeader(Exception): class InvalidHeader(Exception): pass -def serialize_header(header_dict: dict) -> str: - s = int_to_hex(header_dict['version'], 4) \ - + rev_hex(header_dict['prev_block_hash']) \ - + rev_hex(header_dict['merkle_root']) \ - + int_to_hex(int(header_dict['timestamp']), 4) \ - + int_to_hex(int(header_dict['bits']), 4) \ - + int_to_hex(int(header_dict['nonce']), 4) +def serialize_header(header_dict: dict) -> bytes: + s = ( + int.to_bytes(header_dict['version'], length=4, byteorder="little", signed=False) + + bfh(header_dict['prev_block_hash'])[::-1] + + bfh(header_dict['merkle_root'])[::-1] + + int.to_bytes(int(header_dict['timestamp']), length=4, byteorder="little", signed=False) + + int.to_bytes(int(header_dict['bits']), length=4, byteorder="little", signed=False) + + int.to_bytes(int(header_dict['nonce']), length=4, byteorder="little", signed=False)) return s def deserialize_header(s: bytes, height: int) -> dict: @@ -63,14 +64,13 @@ def deserialize_header(s: bytes, height: int) -> dict: raise InvalidHeader('Invalid header: {}'.format(s)) if len(s) != HEADER_SIZE: raise InvalidHeader('Invalid header length: {}'.format(len(s))) - hex_to_int = lambda s: int.from_bytes(s, byteorder='little') h = {} - h['version'] = hex_to_int(s[0:4]) + h['version'] = int.from_bytes(s[0:4], byteorder='little') h['prev_block_hash'] = hash_encode(s[4:36]) h['merkle_root'] = hash_encode(s[36:68]) - h['timestamp'] = hex_to_int(s[68:72]) - h['bits'] = hex_to_int(s[72:76]) - h['nonce'] = hex_to_int(s[76:80]) + h['timestamp'] = int.from_bytes(s[68:72], byteorder='little') + h['bits'] = int.from_bytes(s[72:76], byteorder='little') + h['nonce'] = int.from_bytes(s[76:80], byteorder='little') h['block_height'] = height return h @@ -82,8 +82,9 @@ def hash_header(header: dict) -> str: return hash_raw_header(serialize_header(header)) -def hash_raw_header(header: str) -> str: - return hash_encode(sha256d(bfh(header))) +def hash_raw_header(header: bytes) -> str: + assert isinstance(header, bytes) + return hash_encode(sha256d(header)) pow_hash_header = hash_header @@ -413,7 +414,7 @@ class Blockchain(Logger): # swap parameters self.parent, parent.parent = parent.parent, self # type: Optional[Blockchain], Optional[Blockchain] self.forkpoint, parent.forkpoint = parent.forkpoint, self.forkpoint - self._forkpoint_hash, parent._forkpoint_hash = parent._forkpoint_hash, hash_raw_header(parent_data[:HEADER_SIZE].hex()) + self._forkpoint_hash, parent._forkpoint_hash = parent._forkpoint_hash, hash_raw_header(parent_data[:HEADER_SIZE]) self._prev_hash, parent._prev_hash = parent._prev_hash, self._prev_hash # parent's new name os.replace(child_old_name, parent.path()) @@ -454,7 +455,7 @@ class Blockchain(Logger): @with_lock def save_header(self, header: dict) -> None: delta = header.get('block_height') - self.forkpoint - data = bfh(serialize_header(header)) + data = serialize_header(header) # headers are only _appended_ to the end: assert delta == self.size(), (delta, self.size()) assert len(data) == HEADER_SIZE diff --git a/electrum/commands.py b/electrum/commands.py index 14b655dc7..890f347c7 100644 --- a/electrum/commands.py +++ b/electrum/commands.py @@ -422,9 +422,9 @@ class Commands: sec = txin_dict.get('privkey') if sec: txin_type, privkey, compressed = bitcoin.deserialize_privkey(sec) - pubkey = ecc.ECPrivkey(privkey).get_public_key_hex(compressed=compressed) - keypairs[pubkey] = privkey, compressed - desc = descriptor.get_singlesig_descriptor_from_legacy_leaf(pubkey=pubkey, script_type=txin_type) + pubkey = ecc.ECPrivkey(privkey).get_public_key_bytes(compressed=compressed) + keypairs[pubkey] = privkey + desc = descriptor.get_singlesig_descriptor_from_legacy_leaf(pubkey=pubkey.hex(), script_type=txin_type) txin.script_descriptor = desc inputs.append(txin) @@ -465,7 +465,7 @@ class Commands: if address in txins_dict.keys(): for txin in txins_dict[address]: txin.script_descriptor = desc - tx.sign({pubkey.hex(): (priv2, compressed)}) + tx.sign({pubkey: priv2}) return tx.serialize() @@ -494,8 +494,8 @@ class Commands: """Create multisig address""" assert isinstance(pubkeys, list), (type(num), type(pubkeys)) redeem_script = multisig_script(pubkeys, num) - address = bitcoin.hash160_to_p2sh(hash_160(bfh(redeem_script))) - return {'address':address, 'redeemScript':redeem_script} + address = bitcoin.hash160_to_p2sh(hash_160(redeem_script)) + return {'address': address, 'redeemScript': redeem_script.hex()} @command('w') async def freeze(self, address: str, wallet: Abstract_Wallet = None): diff --git a/electrum/constants.py b/electrum/constants.py index 9e379755d..445bd8490 100644 --- a/electrum/constants.py +++ b/electrum/constants.py @@ -74,7 +74,7 @@ class AbstractNet: @classmethod def rev_genesis_bytes(cls) -> bytes: - return bytes.fromhex(bitcoin.rev_hex(cls.GENESIS)) + return bytes.fromhex(cls.GENESIS)[::-1] class BitcoinMainnet(AbstractNet): diff --git a/electrum/descriptor.py b/electrum/descriptor.py index 3bce2da20..e2904f3f2 100644 --- a/electrum/descriptor.py +++ b/electrum/descriptor.py @@ -75,7 +75,7 @@ class ExpandedScripts: self._scriptcode_for_sighash = value def address(self, *, net=None) -> Optional[str]: - return bitcoin.script_to_address(self.output_script.hex(), net=net) + return bitcoin.script_to_address(self.output_script, net=net) class ScriptSolutionInner(NamedTuple): @@ -379,9 +379,9 @@ class Descriptor(object): witness = None script_sig = None if self.is_segwit(): - witness = bfh(construct_witness(sol.witness_items)) + witness = construct_witness(sol.witness_items) else: - script_sig = bfh(construct_script(sol.witness_items)) + script_sig = construct_script(sol.witness_items) return ScriptSolutionTop( witness=witness, script_sig=script_sig, @@ -473,7 +473,7 @@ class PKDescriptor(Descriptor): def expand(self, *, pos: Optional[int] = None) -> "ExpandedScripts": pubkey = self.pubkeys[0].get_pubkey_bytes(pos=pos) script = construct_script([pubkey, opcodes.OP_CHECKSIG]) - return ExpandedScripts(output_script=bytes.fromhex(script)) + return ExpandedScripts(output_script=script) def _satisfy_inner(self, *, sigdata=None, allow_dummy=False) -> ScriptSolutionInner: if sigdata is None: sigdata = {} @@ -513,9 +513,9 @@ class PKHDescriptor(Descriptor): def expand(self, *, pos: Optional[int] = None) -> "ExpandedScripts": pubkey = self.pubkeys[0].get_pubkey_bytes(pos=pos) - pkh = hash_160(pubkey).hex() + pkh = hash_160(pubkey) script = bitcoin.pubkeyhash_to_p2pkh_script(pkh) - return ExpandedScripts(output_script=bytes.fromhex(script)) + return ExpandedScripts(output_script=script) def _satisfy_inner(self, *, sigdata=None, allow_dummy=False) -> ScriptSolutionInner: if sigdata is None: sigdata = {} @@ -556,10 +556,10 @@ class WPKHDescriptor(Descriptor): def expand(self, *, pos: Optional[int] = None) -> "ExpandedScripts": pkh = hash_160(self.pubkeys[0].get_pubkey_bytes(pos=pos)) output_script = construct_script([0, pkh]) - scriptcode = bitcoin.pubkeyhash_to_p2pkh_script(pkh.hex()) + scriptcode = bitcoin.pubkeyhash_to_p2pkh_script(pkh) return ExpandedScripts( - output_script=bytes.fromhex(output_script), - scriptcode_for_sighash=bytes.fromhex(scriptcode), + output_script=output_script, + scriptcode_for_sighash=scriptcode, ) def _satisfy_inner(self, *, sigdata=None, allow_dummy=False) -> ScriptSolutionInner: @@ -625,7 +625,7 @@ class MultisigDescriptor(Descriptor): der_pks = [p.get_pubkey_bytes(pos=pos) for p in self.pubkeys] if self.is_sorted: der_pks.sort() - script = bfh(construct_script([self.thresh, *der_pks, len(der_pks), opcodes.OP_CHECKMULTISIG])) + script = construct_script([self.thresh, *der_pks, len(der_pks), opcodes.OP_CHECKMULTISIG]) return ExpandedScripts(output_script=script) def _satisfy_inner(self, *, sigdata=None, allow_dummy=False) -> ScriptSolutionInner: @@ -678,7 +678,7 @@ class SHDescriptor(Descriptor): sub_scripts = self.subdescriptors[0].expand(pos=pos) redeem_script = sub_scripts.output_script witness_script = sub_scripts.witness_script - script = bfh(construct_script([opcodes.OP_HASH160, hash_160(redeem_script), opcodes.OP_EQUAL])) + script = construct_script([opcodes.OP_HASH160, hash_160(redeem_script), opcodes.OP_EQUAL]) return ExpandedScripts( output_script=script, redeem_script=redeem_script, @@ -697,10 +697,10 @@ class SHDescriptor(Descriptor): witness = None if isinstance(subdesc, (WSHDescriptor, WPKHDescriptor)): # witness_v0 nested in p2sh witness = subdesc.satisfy(sigdata=sigdata, allow_dummy=allow_dummy).witness - script_sig = bfh(construct_script([redeem_script])) + script_sig = construct_script([redeem_script]) else: # legacy p2sh subsol = subdesc._satisfy_inner(sigdata=sigdata, allow_dummy=allow_dummy) - script_sig = bfh(construct_script([*subsol.witness_items, redeem_script])) + script_sig = construct_script([*subsol.witness_items, redeem_script]) return ScriptSolutionTop( witness=witness, script_sig=script_sig, @@ -724,7 +724,7 @@ class WSHDescriptor(Descriptor): assert len(self.subdescriptors) == 1 sub_scripts = self.subdescriptors[0].expand(pos=pos) witness_script = sub_scripts.output_script - output_script = bfh(construct_script([0, sha256(witness_script)])) + output_script = construct_script([0, sha256(witness_script)]) return ExpandedScripts( output_script=output_script, witness_script=witness_script, @@ -740,7 +740,7 @@ class WSHDescriptor(Descriptor): witness_script = self.expand().witness_script witness = construct_witness([*subsol.witness_items, witness_script]) return ScriptSolutionTop( - witness=bytes.fromhex(witness), + witness=witness, ) def is_segwit(self) -> bool: diff --git a/electrum/ecc.py b/electrum/ecc.py index b28759f76..99d2b4e2f 100644 --- a/electrum/ecc.py +++ b/electrum/ecc.py @@ -415,7 +415,7 @@ POINT_AT_INFINITY = ECPubkey(None) def usermessage_magic(message: bytes) -> bytes: from .bitcoin import var_int - length = bfh(var_int(len(message))) + length = var_int(len(message)) return b"\x18Bitcoin Signed Message:\n" + length + message diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py index c755d896e..2cb35999c 100644 --- a/electrum/gui/qt/main_window.py +++ b/electrum/gui/qt/main_window.py @@ -34,7 +34,7 @@ import base64 from functools import partial import queue import asyncio -from typing import Optional, TYPE_CHECKING, Sequence, List, Union, Dict, Set +from typing import Optional, TYPE_CHECKING, Sequence, List, Union, Dict, Set, Mapping import concurrent.futures from PyQt5.QtGui import QPixmap, QKeySequence, QIcon, QCursor, QFont, QFontMetrics @@ -1106,7 +1106,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener): self, tx: Transaction, *, - external_keypairs=None, + external_keypairs: Mapping[bytes, bytes] = None, payment_identifier: PaymentIdentifier = None, ): show_transaction(tx, parent=self, external_keypairs=external_keypairs, payment_identifier=payment_identifier) @@ -1269,10 +1269,24 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener): self.send_tab.broadcast_transaction(tx, payment_identifier=payment_identifier) @protected - def sign_tx(self, tx, *, callback, external_keypairs, password): + def sign_tx( + self, + tx: PartialTransaction, + *, + callback, + external_keypairs: Optional[Mapping[bytes, bytes]], + password, + ): self.sign_tx_with_password(tx, callback=callback, password=password, external_keypairs=external_keypairs) - def sign_tx_with_password(self, tx: PartialTransaction, *, callback, password, external_keypairs=None): + def sign_tx_with_password( + self, + tx: PartialTransaction, + *, + callback, + password, + external_keypairs: Mapping[bytes, bytes] = None, + ): '''Sign the transaction in a separate thread. When done, calls the callback with a success code of True or False. ''' diff --git a/electrum/gui/qt/send_tab.py b/electrum/gui/qt/send_tab.py index 8566b6a79..e14ae70e2 100644 --- a/electrum/gui/qt/send_tab.py +++ b/electrum/gui/qt/send_tab.py @@ -3,7 +3,7 @@ # file LICENCE or http://www.opensource.org/licenses/mit-license.php from decimal import Decimal -from typing import Optional, TYPE_CHECKING, Sequence, List, Callable, Union +from typing import Optional, TYPE_CHECKING, Sequence, List, Callable, Union, Mapping from PyQt5.QtCore import pyqtSignal, QPoint, QSize, Qt from PyQt5.QtWidgets import (QLabel, QVBoxLayout, QGridLayout, QHBoxLayout, QWidget, QToolTip, QPushButton, QApplication) @@ -295,7 +295,7 @@ class SendTab(QWidget, MessageBoxMixin, Logger): outputs: List[PartialTxOutput], *, nonlocal_only=False, - external_keypairs=None, + external_keypairs: Mapping[bytes, bytes] = None, get_coins: Callable[..., Sequence[PartialTxInput]] = None, invoice: Optional[Invoice] = None ) -> None: diff --git a/electrum/gui/qt/transaction_dialog.py b/electrum/gui/qt/transaction_dialog.py index 8f1835751..eb3b80694 100644 --- a/electrum/gui/qt/transaction_dialog.py +++ b/electrum/gui/qt/transaction_dialog.py @@ -30,7 +30,7 @@ import copy import datetime import traceback import time -from typing import TYPE_CHECKING, Callable, Optional, List, Union, Tuple +from typing import TYPE_CHECKING, Callable, Optional, List, Union, Tuple, Mapping from functools import partial from decimal import Decimal @@ -410,7 +410,7 @@ def show_transaction( *, parent: 'ElectrumWindow', prompt_if_unsaved: bool = False, - external_keypairs=None, + external_keypairs: Mapping[bytes, bytes] = None, payment_identifier: 'PaymentIdentifier' = None, ): try: @@ -438,7 +438,7 @@ class TxDialog(QDialog, MessageBoxMixin): *, parent: 'ElectrumWindow', prompt_if_unsaved: bool, - external_keypairs=None, + external_keypairs: Mapping[bytes, bytes] = None, payment_identifier: 'PaymentIdentifier' = None, ): '''Transactions in the wallet will show their description. diff --git a/electrum/gui/text.py b/electrum/gui/text.py index c5280f7a4..e34dbca93 100644 --- a/electrum/gui/text.py +++ b/electrum/gui/text.py @@ -624,7 +624,7 @@ class ElectrumGui(BaseElectrumGui, EventListener): return elif is_address(self.str_recipient): amount_sat = self.parse_amount(self.str_amount) - scriptpubkey = bytes.fromhex(address_to_script(self.str_recipient)) + scriptpubkey = address_to_script(self.str_recipient) outputs = [PartialTxOutput(scriptpubkey=scriptpubkey, value=amount_sat)] invoice = self.wallet.create_invoice( outputs=outputs, diff --git a/electrum/keystore.py b/electrum/keystore.py index f71ba1926..4c5c5b804 100644 --- a/electrum/keystore.py +++ b/electrum/keystore.py @@ -107,13 +107,13 @@ class KeyStore(Logger, ABC): """Returns whether the keystore can be encrypted with a password.""" pass - def _get_tx_derivations(self, tx: 'PartialTransaction') -> Dict[str, Union[Sequence[int], str]]: + def _get_tx_derivations(self, tx: 'PartialTransaction') -> Dict[bytes, Union[Sequence[int], str]]: keypairs = {} for txin in tx.inputs(): keypairs.update(self._get_txin_derivations(txin)) return keypairs - def _get_txin_derivations(self, txin: 'PartialTxInput') -> Dict[str, Union[Sequence[int], str]]: + def _get_txin_derivations(self, txin: 'PartialTxInput') -> Dict[bytes, Union[Sequence[int], str]]: if txin.is_complete(): return {} keypairs = {} @@ -124,7 +124,7 @@ class KeyStore(Logger, ABC): derivation = self.get_pubkey_derivation(pubkey, txin) if not derivation: continue - keypairs[pubkey.hex()] = derivation + keypairs[pubkey] = derivation return keypairs def can_sign(self, tx: 'Transaction', *, ignore_watching_only=False) -> bool: @@ -237,9 +237,11 @@ class Software_KeyStore(KeyStore): # Raise if password is not correct. self.check_password(password) # Add private keys - keypairs = self._get_tx_derivations(tx) - for k, v in keypairs.items(): - keypairs[k] = self.get_private_key(v, password) + keypairs = {} + pubkey_to_deriv_map = self._get_tx_derivations(tx) + for pubkey, deriv in pubkey_to_deriv_map.items(): + privkey, is_compressed = self.get_private_key(deriv, password) + keypairs[pubkey] = privkey # Sign if keypairs: tx.sign(keypairs) diff --git a/electrum/lnchannel.py b/electrum/lnchannel.py index f135dbe4d..18c5156ca 100644 --- a/electrum/lnchannel.py +++ b/electrum/lnchannel.py @@ -1099,7 +1099,7 @@ class Channel(AbstractChannel): commit=pending_remote_commitment, ctx_output_idx=ctx_output_idx, htlc=htlc) - sig = bfh(htlc_tx.sign_txin(0, their_remote_htlc_privkey)) + sig = htlc_tx.sign_txin(0, their_remote_htlc_privkey) htlc_sig = ecc.ecdsa_sig64_from_der_sig(sig[:-1]) htlcsigs.append((ctx_output_idx, htlc_sig)) htlcsigs.sort() @@ -1121,13 +1121,13 @@ class Channel(AbstractChannel): assert len(htlc_sigs) == 0 or type(htlc_sigs[0]) is bytes pending_local_commitment = self.get_next_commitment(LOCAL) - preimage_hex = pending_local_commitment.serialize_preimage(0) - pre_hash = sha256d(bfh(preimage_hex)) - if not ECPubkey(self.config[REMOTE].multisig_key.pubkey).ecdsa_verify(sig, pre_hash): + pre_hash = pending_local_commitment.serialize_preimage(0) + msg_hash = sha256d(pre_hash) + if not ECPubkey(self.config[REMOTE].multisig_key.pubkey).ecdsa_verify(sig, msg_hash): raise LNProtocolWarning( f'failed verifying signature for our updated commitment transaction. ' f'sig={sig.hex()}. ' - f'pre_hash={pre_hash.hex()}. ' + f'msg_hash={msg_hash.hex()}. ' f'pubkey={self.config[REMOTE].multisig_key.pubkey}. ' f'ctx={pending_local_commitment.serialize()} ' ) @@ -1167,16 +1167,16 @@ class Channel(AbstractChannel): commit=ctx, ctx_output_idx=ctx_output_idx, htlc=htlc) - preimage_hex = htlc_tx.serialize_preimage(0) - pre_hash = sha256d(bfh(preimage_hex)) + pre_hash = htlc_tx.serialize_preimage(0) + msg_hash = sha256d(pre_hash) remote_htlc_pubkey = derive_pubkey(self.config[REMOTE].htlc_basepoint.pubkey, pcp) - if not ECPubkey(remote_htlc_pubkey).ecdsa_verify(htlc_sig, pre_hash): + if not ECPubkey(remote_htlc_pubkey).ecdsa_verify(htlc_sig, msg_hash): raise LNProtocolWarning( f'failed verifying HTLC signatures: {htlc=}, {htlc_direction=}. ' f'htlc_tx={htlc_tx.serialize()}. ' f'htlc_sig={htlc_sig.hex()}. ' f'remote_htlc_pubkey={remote_htlc_pubkey.hex()}. ' - f'pre_hash={pre_hash.hex()}. ' + f'msg_hash={msg_hash.hex()}. ' f'ctx={ctx.serialize()}. ' f'ctx_output_idx={ctx_output_idx}. ' f'ctn={ctn}. ' @@ -1587,8 +1587,8 @@ class Channel(AbstractChannel): }, local_amount_msat=self.balance(LOCAL), remote_amount_msat=self.balance(REMOTE) if not drop_remote else 0, - local_script=local_script.hex(), - remote_script=remote_script.hex(), + local_script=local_script, + remote_script=remote_script, htlcs=[], dust_limit_sat=self.config[LOCAL].dust_limit_sat) @@ -1599,14 +1599,14 @@ class Channel(AbstractChannel): funding_sat=self.constraints.capacity, outputs=outputs) - der_sig = bfh(closing_tx.sign_txin(0, self.config[LOCAL].multisig_key.privkey)) + der_sig = closing_tx.sign_txin(0, self.config[LOCAL].multisig_key.privkey) sig = ecc.ecdsa_sig64_from_der_sig(der_sig[:-1]) return sig, closing_tx def signature_fits(self, tx: PartialTransaction) -> bool: remote_sig = self.config[LOCAL].current_commitment_signature - preimage_hex = tx.serialize_preimage(0) - msg_hash = sha256d(bfh(preimage_hex)) + pre_hash = tx.serialize_preimage(0) + msg_hash = sha256d(pre_hash) assert remote_sig res = ECPubkey(self.config[REMOTE].multisig_key.pubkey).ecdsa_verify(remote_sig, msg_hash) return res @@ -1614,12 +1614,12 @@ class Channel(AbstractChannel): def force_close_tx(self) -> PartialTransaction: tx = self.get_latest_commitment(LOCAL) assert self.signature_fits(tx) - tx.sign({self.config[LOCAL].multisig_key.pubkey.hex(): (self.config[LOCAL].multisig_key.privkey, True)}) + tx.sign({self.config[LOCAL].multisig_key.pubkey: self.config[LOCAL].multisig_key.privkey}) remote_sig = self.config[LOCAL].current_commitment_signature remote_sig = ecc.ecdsa_der_sig_from_ecdsa_sig64(remote_sig) + Sighash.to_sigbytes(Sighash.ALL) tx.add_signature_to_txin(txin_idx=0, - signing_pubkey=self.config[REMOTE].multisig_key.pubkey.hex(), - sig=remote_sig.hex()) + signing_pubkey=self.config[REMOTE].multisig_key.pubkey, + sig=remote_sig) assert tx.is_complete() return tx diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py index 6f9e2c0ad..23819da5f 100644 --- a/electrum/lnpeer.py +++ b/electrum/lnpeer.py @@ -2368,7 +2368,7 @@ class Peer(Logger): if chan.config[LOCAL].upfront_shutdown_script: scriptpubkey = chan.config[LOCAL].upfront_shutdown_script else: - scriptpubkey = bfh(bitcoin.address_to_script(chan.sweep_address)) + scriptpubkey = bitcoin.address_to_script(chan.sweep_address) assert scriptpubkey # wait until no more pending updates (bolt2) chan.set_can_send_ctx_updates(False) @@ -2421,7 +2421,7 @@ class Peer(Logger): if chan.config[LOCAL].upfront_shutdown_script: our_scriptpubkey = chan.config[LOCAL].upfront_shutdown_script else: - our_scriptpubkey = bfh(bitcoin.address_to_script(chan.sweep_address)) + our_scriptpubkey = bitcoin.address_to_script(chan.sweep_address) assert our_scriptpubkey # estimate fee of closing tx dummy_sig, dummy_tx = chan.make_closing_tx(our_scriptpubkey, their_scriptpubkey, fee_sat=0) @@ -2448,9 +2448,9 @@ class Peer(Logger): def verify_signature(tx: 'PartialTransaction', sig) -> bool: their_pubkey = chan.config[REMOTE].multisig_key.pubkey - preimage_hex = tx.serialize_preimage(0) - pre_hash = sha256d(bfh(preimage_hex)) - return ECPubkey(their_pubkey).ecdsa_verify(sig, pre_hash) + pre_hash = tx.serialize_preimage(0) + msg_hash = sha256d(pre_hash) + return ECPubkey(their_pubkey).ecdsa_verify(sig, msg_hash) async def receive_closing_signed(): nonlocal our_sig, closing_tx @@ -2476,7 +2476,7 @@ class Peer(Logger): raise Exception('failed to verify their signature') # at this point we know how the closing tx looks like # check that their output is above their scriptpubkey's network dust limit - to_remote_set = closing_tx.get_output_idxs_from_scriptpubkey(their_scriptpubkey.hex()) + to_remote_set = closing_tx.get_output_idxs_from_scriptpubkey(their_scriptpubkey) if not drop_remote and to_remote_set: to_remote_idx = to_remote_set.pop() to_remote_amount = closing_tx.outputs()[to_remote_idx].value @@ -2570,12 +2570,12 @@ class Peer(Logger): # add signatures closing_tx.add_signature_to_txin( txin_idx=0, - signing_pubkey=chan.config[LOCAL].multisig_key.pubkey.hex(), - sig=(ecdsa_der_sig_from_ecdsa_sig64(our_sig) + Sighash.to_sigbytes(Sighash.ALL)).hex()) + signing_pubkey=chan.config[LOCAL].multisig_key.pubkey, + sig=ecdsa_der_sig_from_ecdsa_sig64(our_sig) + Sighash.to_sigbytes(Sighash.ALL)) closing_tx.add_signature_to_txin( txin_idx=0, - signing_pubkey=chan.config[REMOTE].multisig_key.pubkey.hex(), - sig=(ecdsa_der_sig_from_ecdsa_sig64(their_sig) + Sighash.to_sigbytes(Sighash.ALL)).hex()) + signing_pubkey=chan.config[REMOTE].multisig_key.pubkey, + sig=ecdsa_der_sig_from_ecdsa_sig64(their_sig) + Sighash.to_sigbytes(Sighash.ALL)) # save local transaction and set state try: self.lnworker.wallet.adb.add_transaction(closing_tx) diff --git a/electrum/lnsweep.py b/electrum/lnsweep.py index fe5f78fb7..d80707004 100644 --- a/electrum/lnsweep.py +++ b/electrum/lnsweep.py @@ -55,7 +55,7 @@ def create_sweeptxs_for_watchtower(chan: 'Channel', ctx: Transaction, per_commit # to_local revocation_pubkey = ecc.ECPrivkey(other_revocation_privkey).get_public_key_bytes(compressed=True) witness_script = make_commitment_output_to_local_witness_script( - revocation_pubkey, to_self_delay, this_delayed_pubkey).hex() + revocation_pubkey, to_self_delay, this_delayed_pubkey) to_local_address = redeem_script_to_address('p2wsh', witness_script) output_idxs = ctx.get_output_idxs_from_address(to_local_address) if output_idxs: @@ -64,7 +64,7 @@ def create_sweeptxs_for_watchtower(chan: 'Channel', ctx: Transaction, per_commit sweep_address=sweep_address, ctx=ctx, output_idx=output_idx, - witness_script=bfh(witness_script), + witness_script=witness_script, privkey=other_revocation_privkey, is_revocation=True, config=chan.lnworker.config) @@ -122,7 +122,7 @@ def create_sweeptx_for_their_revoked_ctx( # to_local revocation_pubkey = ecc.ECPrivkey(other_revocation_privkey).get_public_key_bytes(compressed=True) witness_script = make_commitment_output_to_local_witness_script( - revocation_pubkey, to_self_delay, this_delayed_pubkey).hex() + revocation_pubkey, to_self_delay, this_delayed_pubkey) to_local_address = redeem_script_to_address('p2wsh', witness_script) output_idxs = ctx.get_output_idxs_from_address(to_local_address) if output_idxs: @@ -131,7 +131,7 @@ def create_sweeptx_for_their_revoked_ctx( sweep_address=sweep_address, ctx=ctx, output_idx=output_idx, - witness_script=bfh(witness_script), + witness_script=witness_script, privkey=other_revocation_privkey, is_revocation=True, config=chan.lnworker.config) @@ -162,7 +162,7 @@ def create_sweeptx_for_their_revoked_htlc( # same witness script as to_local revocation_pubkey = ecc.ECPrivkey(other_revocation_privkey).get_public_key_bytes(compressed=True) witness_script = make_commitment_output_to_local_witness_script( - revocation_pubkey, to_self_delay, this_delayed_pubkey).hex() + revocation_pubkey, to_self_delay, this_delayed_pubkey) htlc_address = redeem_script_to_address('p2wsh', witness_script) # check that htlc_tx is a htlc if htlc_tx.outputs()[0].address != htlc_address: @@ -170,7 +170,7 @@ def create_sweeptx_for_their_revoked_htlc( gen_tx = lambda: create_sweeptx_that_spends_htlctx_that_spends_htlc_in_ctx( sweep_address=sweep_address, htlc_tx=htlc_tx, - htlctx_witness_script=bfh(witness_script), + htlctx_witness_script=witness_script, privkey=other_revocation_privkey, is_revocation=True, config=chan.lnworker.config) @@ -204,7 +204,7 @@ def create_sweeptxs_for_our_ctx( per_commitment_point=our_pcp).to_bytes(32, 'big') our_localdelayed_pubkey = our_localdelayed_privkey.get_public_key_bytes(compressed=True) to_local_witness_script = make_commitment_output_to_local_witness_script( - their_revocation_pubkey, to_self_delay, our_localdelayed_pubkey).hex() + their_revocation_pubkey, to_self_delay, our_localdelayed_pubkey) to_local_address = redeem_script_to_address('p2wsh', to_local_witness_script) to_remote_address = None # test if this is our_ctx @@ -231,7 +231,7 @@ def create_sweeptxs_for_our_ctx( sweep_address=sweep_address, ctx=ctx, output_idx=output_idx, - witness_script=bfh(to_local_witness_script), + witness_script=to_local_witness_script, privkey=our_localdelayed_privkey.get_secret_bytes(), is_revocation=False, to_self_delay=to_self_delay, @@ -358,7 +358,7 @@ def create_sweeptxs_for_their_ctx( our_revocation_pubkey = derive_blinded_pubkey(our_conf.revocation_basepoint.pubkey, their_pcp) their_delayed_pubkey = derive_pubkey(their_conf.delayed_basepoint.pubkey, their_pcp) witness_script = make_commitment_output_to_local_witness_script( - our_revocation_pubkey, our_conf.to_self_delay, their_delayed_pubkey).hex() + our_revocation_pubkey, our_conf.to_self_delay, their_delayed_pubkey) to_local_address = redeem_script_to_address('p2wsh', witness_script) to_remote_address = None # test if this is their ctx @@ -469,9 +469,9 @@ def create_htlctx_that_spends_from_our_ctx( ctx_output_idx=ctx_output_idx, name=f'our_ctx_{ctx_output_idx}_htlc_tx_{htlc.payment_hash.hex()}') remote_htlc_sig = chan.get_remote_htlc_sig_for_htlc(htlc_relative_idx=htlc_relative_idx) - local_htlc_sig = bfh(htlc_tx.sign_txin(0, local_htlc_privkey)) + local_htlc_sig = htlc_tx.sign_txin(0, local_htlc_privkey) txin = htlc_tx.inputs()[0] - witness_program = bfh(Transaction.get_preimage_script(txin)) + witness_program = Transaction.get_preimage_script(txin) txin.witness = make_htlc_tx_witness(remote_htlc_sig, local_htlc_sig, preimage, witness_program) return witness_script, htlc_tx @@ -496,13 +496,13 @@ def create_sweeptx_their_ctx_htlc( if outvalue <= dust_threshold(): return None sweep_outputs = [PartialTxOutput.from_address_and_value(sweep_address, outvalue)] tx = PartialTransaction.from_io(sweep_inputs, sweep_outputs, version=2, locktime=cltv_abs) - sig = bfh(tx.sign_txin(0, privkey)) + sig = tx.sign_txin(0, privkey) if not is_revocation: witness = construct_witness([sig, preimage, witness_script]) else: revocation_pubkey = privkey_to_pubkey(privkey) witness = construct_witness([sig, revocation_pubkey, witness_script]) - tx.inputs()[0].witness = bfh(witness) + tx.inputs()[0].witness = witness assert tx.is_complete() return tx @@ -511,12 +511,12 @@ def create_sweeptx_their_ctx_to_remote( sweep_address: str, ctx: Transaction, output_idx: int, our_payment_privkey: ecc.ECPrivkey, config: SimpleConfig) -> Optional[PartialTransaction]: - our_payment_pubkey = our_payment_privkey.get_public_key_hex(compressed=True) + our_payment_pubkey = our_payment_privkey.get_public_key_bytes(compressed=True) val = ctx.outputs()[output_idx].value prevout = TxOutpoint(txid=bfh(ctx.txid()), out_idx=output_idx) txin = PartialTxInput(prevout=prevout) txin._trusted_value_sats = val - desc = descriptor.get_singlesig_descriptor_from_legacy_leaf(pubkey=our_payment_pubkey, script_type='p2wpkh') + desc = descriptor.get_singlesig_descriptor_from_legacy_leaf(pubkey=our_payment_pubkey.hex(), script_type='p2wpkh') txin.script_descriptor = desc sweep_inputs = [txin] tx_size_bytes = 110 # approx size of p2wpkh->p2wpkh @@ -526,7 +526,7 @@ def create_sweeptx_their_ctx_to_remote( sweep_outputs = [PartialTxOutput.from_address_and_value(sweep_address, outvalue)] sweep_tx = PartialTransaction.from_io(sweep_inputs, sweep_outputs) sweep_tx.set_rbf(True) - sweep_tx.sign({our_payment_pubkey: (our_payment_privkey.get_secret_bytes(), True)}) + sweep_tx.sign({our_payment_pubkey: our_payment_privkey.get_secret_bytes()}) if not sweep_tx.is_complete(): raise Exception('channel close sweep tx is not complete') return sweep_tx @@ -561,7 +561,7 @@ def create_sweeptx_ctx_to_local( sweep_tx = PartialTransaction.from_io(sweep_inputs, sweep_outputs, version=2) sig = sweep_tx.sign_txin(0, privkey) witness = construct_witness([sig, int(is_revocation), witness_script]) - sweep_tx.inputs()[0].witness = bfh(witness) + sweep_tx.inputs()[0].witness = witness return sweep_tx diff --git a/electrum/lnutil.py b/electrum/lnutil.py index bbcd9e5aa..102548c58 100644 --- a/electrum/lnutil.py +++ b/electrum/lnutil.py @@ -24,7 +24,7 @@ from .transaction import (Transaction, PartialTransaction, PartialTxInput, TxOut from .ecc import CURVE_ORDER, ecdsa_sig64_from_der_sig, ECPubkey, string_to_number from . import ecc, bitcoin, crypto, transaction from . import descriptor -from .bitcoin import (push_script, redeem_script_to_address, address_to_script, +from .bitcoin import (redeem_script_to_address, address_to_script, construct_witness, construct_script) from . import segwit_addr from .i18n import _ @@ -591,7 +591,9 @@ def derive_blinded_privkey(basepoint_secret: bytes, per_commitment_secret: bytes return int.to_bytes(sum, length=32, byteorder='big', signed=False) -def make_htlc_tx_output(amount_msat, local_feerate, revocationpubkey, local_delayedpubkey, success, to_self_delay): +def make_htlc_tx_output( + amount_msat, local_feerate, revocationpubkey, local_delayedpubkey, success, to_self_delay, +) -> Tuple[bytes, PartialTxOutput]: assert type(amount_msat) is int assert type(local_feerate) is int script = make_commitment_output_to_local_witness_script( @@ -600,7 +602,7 @@ def make_htlc_tx_output(amount_msat, local_feerate, revocationpubkey, local_dela delayed_pubkey=local_delayedpubkey, ) - p2wsh = bitcoin.redeem_script_to_address('p2wsh', script.hex()) + p2wsh = bitcoin.redeem_script_to_address('p2wsh', script) weight = HTLC_SUCCESS_WEIGHT if success else HTLC_TIMEOUT_WEIGHT fee = local_feerate * weight fee = fee // 1000 * 1000 @@ -615,7 +617,7 @@ def make_htlc_tx_witness(remotehtlcsig: bytes, localhtlcsig: bytes, assert type(localhtlcsig) is bytes assert type(payment_preimage) is bytes assert type(witness_script) is bytes - return bfh(construct_witness([0, remotehtlcsig, localhtlcsig, payment_preimage, witness_script])) + return construct_witness([0, remotehtlcsig, localhtlcsig, payment_preimage, witness_script]) def make_htlc_tx_inputs(htlc_output_txid: str, htlc_output_index: int, amount_msat: int, witness_script: str) -> List[PartialTxInput]: @@ -648,7 +650,7 @@ def make_offered_htlc( assert type(remote_htlcpubkey) is bytes assert type(local_htlcpubkey) is bytes assert type(payment_hash) is bytes - script = bfh(construct_script([ + script = construct_script([ opcodes.OP_DUP, opcodes.OP_HASH160, bitcoin.hash_160(revocation_pubkey), @@ -675,7 +677,7 @@ def make_offered_htlc( opcodes.OP_CHECKSIG, opcodes.OP_ENDIF, opcodes.OP_ENDIF, - ])) + ]) return script def make_received_htlc( @@ -690,7 +692,7 @@ def make_received_htlc( assert type(i) is bytes assert type(cltv_abs) is int - script = bfh(construct_script([ + script = construct_script([ opcodes.OP_DUP, opcodes.OP_HASH160, bitcoin.hash_160(revocation_pubkey), @@ -720,7 +722,7 @@ def make_received_htlc( opcodes.OP_CHECKSIG, opcodes.OP_ENDIF, opcodes.OP_ENDIF, - ])) + ]) return script WITNESS_TEMPLATE_OFFERED_HTLC = [ @@ -830,7 +832,7 @@ def possible_output_idxs_of_htlc_in_ctx(*, chan: 'Channel', pcp: bytes, subject: local_htlc_pubkey=htlc_pubkey, payment_hash=payment_hash, cltv_abs=cltv_abs) - htlc_address = redeem_script_to_address('p2wsh', preimage_script.hex()) + htlc_address = redeem_script_to_address('p2wsh', preimage_script) candidates = ctx.get_output_idxs_from_address(htlc_address) return {output_idx for output_idx in candidates if ctx.outputs()[output_idx].value == htlc.amount_msat // 1000} @@ -938,20 +940,20 @@ LOCAL = HTLCOwner.LOCAL REMOTE = HTLCOwner.REMOTE def make_commitment_outputs(*, fees_per_participant: Mapping[HTLCOwner, int], local_amount_msat: int, remote_amount_msat: int, - local_script: str, remote_script: str, htlcs: List[ScriptHtlc], dust_limit_sat: int) -> Tuple[List[PartialTxOutput], List[PartialTxOutput]]: + local_script: bytes, remote_script: bytes, htlcs: List[ScriptHtlc], dust_limit_sat: int) -> Tuple[List[PartialTxOutput], List[PartialTxOutput]]: # BOLT-03: "Base commitment transaction fees are extracted from the funder's amount; # if that amount is insufficient, the entire amount of the funder's output is used." # -> if funder cannot afford feerate, their output might go negative, so take max(0, x) here: to_local_amt = max(0, local_amount_msat - fees_per_participant[LOCAL]) - to_local = PartialTxOutput(scriptpubkey=bfh(local_script), value=to_local_amt // 1000) + to_local = PartialTxOutput(scriptpubkey=local_script, value=to_local_amt // 1000) to_remote_amt = max(0, remote_amount_msat - fees_per_participant[REMOTE]) - to_remote = PartialTxOutput(scriptpubkey=bfh(remote_script), value=to_remote_amt // 1000) + to_remote = PartialTxOutput(scriptpubkey=remote_script, value=to_remote_amt // 1000) non_htlc_outputs = [to_local, to_remote] htlc_outputs = [] for script, htlc in htlcs: - addr = bitcoin.redeem_script_to_address('p2wsh', script.hex()) - htlc_outputs.append(PartialTxOutput(scriptpubkey=bfh(address_to_script(addr)), + addr = bitcoin.redeem_script_to_address('p2wsh', script) + htlc_outputs.append(PartialTxOutput(scriptpubkey=address_to_script(addr), value=htlc.amount_msat // 1000)) # trim outputs @@ -1060,7 +1062,7 @@ def make_commitment_output_to_local_witness_script( assert type(revocation_pubkey) is bytes assert type(to_self_delay) is int assert type(delayed_pubkey) is bytes - script = bfh(construct_script([ + script = construct_script([ opcodes.OP_IF, revocation_pubkey, opcodes.OP_ELSE, @@ -1070,27 +1072,27 @@ def make_commitment_output_to_local_witness_script( delayed_pubkey, opcodes.OP_ENDIF, opcodes.OP_CHECKSIG, - ])) + ]) return script def make_commitment_output_to_local_address( revocation_pubkey: bytes, to_self_delay: int, delayed_pubkey: bytes) -> str: local_script = make_commitment_output_to_local_witness_script(revocation_pubkey, to_self_delay, delayed_pubkey) - return bitcoin.redeem_script_to_address('p2wsh', local_script.hex()) + return bitcoin.redeem_script_to_address('p2wsh', local_script) def make_commitment_output_to_remote_address(remote_payment_pubkey: bytes) -> str: return bitcoin.pubkey_to_address('p2wpkh', remote_payment_pubkey.hex()) def sign_and_get_sig_string(tx: PartialTransaction, local_config, remote_config): - tx.sign({local_config.multisig_key.pubkey.hex(): (local_config.multisig_key.privkey, True)}) + tx.sign({local_config.multisig_key.pubkey: local_config.multisig_key.privkey}) sig = tx.inputs()[0].part_sigs[local_config.multisig_key.pubkey] sig_64 = ecdsa_sig64_from_der_sig(sig[:-1]) return sig_64 -def funding_output_script(local_config, remote_config) -> str: +def funding_output_script(local_config: 'LocalConfig', remote_config: 'RemoteConfig') -> bytes: return funding_output_script_from_keys(local_config.multisig_key.pubkey, remote_config.multisig_key.pubkey) -def funding_output_script_from_keys(pubkey1: bytes, pubkey2: bytes) -> str: +def funding_output_script_from_keys(pubkey1: bytes, pubkey2: bytes) -> bytes: pubkeys = sorted([pubkey1.hex(), pubkey2.hex()]) return transaction.multisig_script(pubkeys, 2) diff --git a/electrum/lnworker.py b/electrum/lnworker.py index 62d930bb6..7fb40590d 100644 --- a/electrum/lnworker.py +++ b/electrum/lnworker.py @@ -58,7 +58,7 @@ from .lnrater import LNRater from . import lnutil from .lnutil import funding_output_script from .lnutil import serialize_htlc_key, deserialize_htlc_key -from .bitcoin import redeem_script_to_address, DummyAddress +from .bitcoin import DummyAddress from .lnutil import (Outpoint, LNPeerAddr, get_compressed_pubkey_from_bech32, extract_nodeid, PaymentFailure, split_host_port, ConnStringFormatError, diff --git a/electrum/payment_identifier.py b/electrum/payment_identifier.py index 37aafa73f..1161dfb63 100644 --- a/electrum/payment_identifier.py +++ b/electrum/payment_identifier.py @@ -320,7 +320,7 @@ class PaymentIdentifier(Logger): 'security check, DNSSEC, and thus may not be correct.').format(key) try: assert bitcoin.is_address(address) - scriptpubkey = bytes.fromhex(bitcoin.address_to_script(address)) + scriptpubkey = bitcoin.address_to_script(address) self._type = PaymentIdentifierType.OPENALIAS self.spk = scriptpubkey self.set_state(PaymentIdentifierState.AVAILABLE) @@ -518,20 +518,20 @@ class PaymentIdentifier(Logger): def parse_output(self, x: str) -> Tuple[Optional[bytes], bool]: try: address = self.parse_address(x) - return bytes.fromhex(bitcoin.address_to_script(address)), True + return bitcoin.address_to_script(address), True except Exception as e: pass try: m = re.match('^' + RE_SCRIPT_FN + '$', x) script = self.parse_script(str(m.group(1))) - return bytes.fromhex(script), False + return script, False except Exception as e: pass return None, False - def parse_script(self, x: str) -> str: - script = '' + def parse_script(self, x: str) -> bytes: + script = bytearray() for word in x.split(): if word[0:3] == 'OP_': opcode_int = opcodes[word] @@ -539,7 +539,7 @@ class PaymentIdentifier(Logger): else: bytes.fromhex(word) # to test it is hex data script += construct_script([word]) - return script + return bytes(script) def parse_amount(self, x: str) -> Union[str, int]: x = x.strip() diff --git a/electrum/paymentrequest.py b/electrum/paymentrequest.py index 33f64aad6..ba2f4a58b 100644 --- a/electrum/paymentrequest.py +++ b/electrum/paymentrequest.py @@ -291,7 +291,7 @@ class PaymentRequest: paymnt.merchant_data = pay_det.merchant_data paymnt.transactions.append(bfh(raw_tx)) ref_out = paymnt.refund_to.add() - ref_out.script = util.bfh(address_to_script(refund_addr)) + ref_out.script = address_to_script(refund_addr) paymnt.memo = "Paid using Electrum" pm = paymnt.SerializeToString() payurl = urllib.parse.urlparse(pay_det.payment_url) @@ -334,7 +334,7 @@ def make_unsigned_request(req: 'Invoice'): if amount is None: amount = 0 memo = req.message - script = bfh(address_to_script(addr)) + script = address_to_script(addr) outputs = [(script, amount)] pd = pb2.PaymentDetails() if constants.net.TESTNET: diff --git a/electrum/plugins/bitbox02/bitbox02.py b/electrum/plugins/bitbox02/bitbox02.py index 5e8ecee32..5c1f33748 100644 --- a/electrum/plugins/bitbox02/bitbox02.py +++ b/electrum/plugins/bitbox02/bitbox02.py @@ -536,8 +536,8 @@ class BitBox02Client(HardwareClientBase): # Fill signatures if len(sigs) != len(tx.inputs()): raise Exception("Incorrect number of inputs signed.") # Should never occur - sighash = Sighash.to_sigbytes(Sighash.ALL).hex() - signatures = [ecc.ecdsa_der_sig_from_ecdsa_sig64(x[1]).hex() + sighash for x in sigs] + sighash = Sighash.to_sigbytes(Sighash.ALL) + signatures = [ecc.ecdsa_der_sig_from_ecdsa_sig64(x[1]) + sighash for x in sigs] tx.update_signatures(signatures) def sign_message(self, keypath: str, message: bytes, script_type: str) -> bytes: diff --git a/electrum/plugins/coldcard/coldcard.py b/electrum/plugins/coldcard/coldcard.py index 00793ae70..c9f55308b 100644 --- a/electrum/plugins/coldcard/coldcard.py +++ b/electrum/plugins/coldcard/coldcard.py @@ -600,7 +600,7 @@ class ColdcardPlugin(HW_PluginBase): xfp_int = xfp_int_from_xfp_bytes(fp_bytes) xfp_paths.append([xfp_int] + list(der_full)) - script = bfh(wallet.pubkeys_to_scriptcode(pubkey_hexes)) + script = wallet.pubkeys_to_scriptcode(pubkey_hexes) keystore.show_p2sh_address(wallet.m, script, xfp_paths, txin_type) diff --git a/electrum/plugins/digitalbitbox/digitalbitbox.py b/electrum/plugins/digitalbitbox/digitalbitbox.py index 8faccfc3b..8cb454176 100644 --- a/electrum/plugins/digitalbitbox/digitalbitbox.py +++ b/electrum/plugins/digitalbitbox/digitalbitbox.py @@ -547,7 +547,7 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore): if not inputPath: self.give_error("No matching pubkey for sign_transaction") # should never happen inputPath = convert_bip32_intpath_to_strpath(inputPath) - inputHash = sha256d(bfh(tx.serialize_preimage(i))) + inputHash = sha256d(tx.serialize_preimage(i)) hasharray_i = {'hash': to_hexstr(inputHash), 'keypath': inputPath} hasharray.append(hasharray_i) inputhasharray.append(inputHash) @@ -659,8 +659,8 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore): sig_r = int(signed['sig'][:64], 16) sig_s = int(signed['sig'][64:], 16) sig = ecc.ecdsa_der_sig_from_r_and_s(sig_r, sig_s) - sig = to_hexstr(sig) + Sighash.to_sigbytes(Sighash.ALL).hex() - tx.add_signature_to_txin(txin_idx=i, signing_pubkey=pubkey_bytes.hex(), sig=sig) + sig = sig + Sighash.to_sigbytes(Sighash.ALL) + tx.add_signature_to_txin(txin_idx=i, signing_pubkey=pubkey_bytes, sig=sig) except UserCancelled: raise except BaseException as e: diff --git a/electrum/plugins/jade/jade.py b/electrum/plugins/jade/jade.py index 3d7d04220..8a36b69ed 100644 --- a/electrum/plugins/jade/jade.py +++ b/electrum/plugins/jade/jade.py @@ -268,7 +268,6 @@ class Jade_KeyStore(Hardware_KeyStore): pubkey, path = self.find_my_pubkey_in_txinout(txin) witness_input = txin.is_segwit() redeem_script = Transaction.get_preimage_script(txin) - redeem_script = bytes.fromhex(redeem_script) if redeem_script is not None else None input_tx = txin.utxo input_tx = bytes.fromhex(input_tx.serialize()) if input_tx is not None else None @@ -314,9 +313,11 @@ class Jade_KeyStore(Hardware_KeyStore): for index, (txin, signature) in enumerate(zip(tx.inputs(), signatures)): pubkey, path = self.find_my_pubkey_in_txinout(txin) if pubkey is not None and signature is not None: - tx.add_signature_to_txin(txin_idx=index, - signing_pubkey=pubkey.hex(), - sig=signature.hex()) + tx.add_signature_to_txin( + txin_idx=index, + signing_pubkey=pubkey, + sig=signature, + ) finally: self.handler.finished() diff --git a/electrum/plugins/keepkey/keepkey.py b/electrum/plugins/keepkey/keepkey.py index db288693b..a5f577a53 100644 --- a/electrum/plugins/keepkey/keepkey.py +++ b/electrum/plugins/keepkey/keepkey.py @@ -266,8 +266,8 @@ class KeepKeyPlugin(HW_PluginBase): outputs = self.tx_outputs(tx, keystore=keystore) signatures = client.sign_tx(self.get_coin_name(), inputs, outputs, lock_time=tx.locktime, version=tx.version)[0] - sighash = Sighash.to_sigbytes(Sighash.ALL).hex() - signatures = [(x.hex() + sighash) for x in signatures] + sighash = Sighash.to_sigbytes(Sighash.ALL) + signatures = [(sig + sighash) for sig in signatures] tx.update_signatures(signatures) @runs_in_hwd_thread diff --git a/electrum/plugins/ledger/ledger.py b/electrum/plugins/ledger/ledger.py index 8d62650c9..c6ecd8ba4 100644 --- a/electrum/plugins/ledger/ledger.py +++ b/electrum/plugins/ledger/ledger.py @@ -9,7 +9,7 @@ from typing import Dict, List, Optional, Sequence, Tuple, TYPE_CHECKING from electrum import bip32, constants, ecc from electrum import descriptor from electrum.bip32 import BIP32Node, convert_bip32_intpath_to_strpath, normalize_bip32_derivation -from electrum.bitcoin import EncodeBase58Check, int_to_hex, is_b58_address, is_segwit_script_type, var_int +from electrum.bitcoin import EncodeBase58Check, is_b58_address, is_segwit_script_type, var_int from electrum.crypto import hash_160 from electrum.i18n import _ from electrum.keystore import Hardware_KeyStore @@ -584,7 +584,7 @@ class Ledger_Client_Legacy(Ledger_Client): self.give_error("No matching pubkey for sign_transaction") # should never happen full_path = convert_bip32_intpath_to_strpath(full_path)[2:] - redeemScript = Transaction.get_preimage_script(txin) + redeemScript = Transaction.get_preimage_script(txin).hex() txin_prev_tx = txin.utxo if txin_prev_tx is None and not txin.is_segwit(): raise UserFacingException(_('Missing previous tx for legacy input.')) @@ -604,13 +604,14 @@ class Ledger_Client_Legacy(Ledger_Client): if not is_txin_legacy_multisig(txin): self.give_error("P2SH / regular input mixed in same transaction not supported") # should never happen - txOutput = var_int(len(tx.outputs())) + txOutput = bytearray() + txOutput += var_int(len(tx.outputs())) for o in tx.outputs(): - txOutput += int_to_hex(o.value, 8) - script = o.scriptpubkey.hex() - txOutput += var_int(len(script) // 2) + txOutput += int.to_bytes(o.value, length=8, byteorder="little", signed=False) + script = o.scriptpubkey + txOutput += var_int(len(script)) txOutput += script - txOutput = bfh(txOutput) + txOutput = bytes(txOutput) if not self.supports_multi_output(): if len(tx.outputs()) > 2: @@ -649,11 +650,11 @@ class Ledger_Client_Legacy(Ledger_Client): # Get trusted inputs from the original transactions for input_idx, utxo in enumerate(inputs): self.handler.show_message(_("Preparing transaction inputs...") + f" (phase1, {input_idx}/{len(inputs)})") - sequence = int_to_hex(utxo[5], 4) + sequence = int.to_bytes(utxo[5], length=4, byteorder="little", signed=False) if segwitTransaction and not self.supports_segwit_trustedInputs(): tmp = bfh(utxo[3])[::-1] - tmp += bfh(int_to_hex(utxo[1], 4)) - tmp += bfh(int_to_hex(utxo[6], 8)) # txin['value'] + tmp += int.to_bytes(utxo[1], length=4, byteorder="little", signed=False) + tmp += int.to_bytes(utxo[6], length=8, byteorder="little", signed=False) # txin['value'] chipInputs.append({'value': tmp, 'witness': True, 'sequence': sequence}) redeemScripts.append(bfh(utxo[2])) elif (not p2shTransaction) or self.supports_multi_output(): @@ -669,7 +670,7 @@ class Ledger_Client_Legacy(Ledger_Client): redeemScripts.append(txtmp.outputs[utxo[1]].script) else: tmp = bfh(utxo[3])[::-1] - tmp += bfh(int_to_hex(utxo[1], 4)) + tmp += int.to_bytes(utxo[1], length=4, byteorder="little", signed=False) chipInputs.append({'value': tmp, 'sequence': sequence}) redeemScripts.append(bfh(utxo[2])) @@ -703,8 +704,8 @@ class Ledger_Client_Legacy(Ledger_Client): inputSignature[0] = 0x30 # force for 1.4.9+ my_pubkey = inputs[inputIndex][4] tx.add_signature_to_txin(txin_idx=inputIndex, - signing_pubkey=my_pubkey.hex(), - sig=inputSignature.hex()) + signing_pubkey=my_pubkey, + sig=inputSignature) inputIndex = inputIndex + 1 else: while inputIndex < len(inputs): @@ -728,8 +729,8 @@ class Ledger_Client_Legacy(Ledger_Client): inputSignature[0] = 0x30 # force for 1.4.9+ my_pubkey = inputs[inputIndex][4] tx.add_signature_to_txin(txin_idx=inputIndex, - signing_pubkey=my_pubkey.hex(), - sig=inputSignature.hex()) + signing_pubkey=my_pubkey, + sig=inputSignature) inputIndex = inputIndex + 1 firstTransaction = False except UserWarning: @@ -1247,7 +1248,7 @@ class Ledger_Client_New(Ledger_Client): input_sigs = self.client.sign_psbt(psbt, wallet, wallet_hmac) for idx, part_sig in input_sigs: tx.add_signature_to_txin( - txin_idx=idx, signing_pubkey=part_sig.pubkey.hex(), sig=part_sig.signature.hex()) + txin_idx=idx, signing_pubkey=part_sig.pubkey, sig=part_sig.signature) except DenyError: pass # cancelled by user except BaseException as e: diff --git a/electrum/plugins/safe_t/safe_t.py b/electrum/plugins/safe_t/safe_t.py index a7981b317..baeba7984 100644 --- a/electrum/plugins/safe_t/safe_t.py +++ b/electrum/plugins/safe_t/safe_t.py @@ -236,8 +236,8 @@ class SafeTPlugin(HW_PluginBase): outputs = self.tx_outputs(tx, keystore=keystore) signatures = client.sign_tx(self.get_coin_name(), inputs, outputs, lock_time=tx.locktime, version=tx.version)[0] - sighash = Sighash.to_sigbytes(Sighash.ALL).hex() - signatures = [(x.hex() + sighash) for x in signatures] + sighash = Sighash.to_sigbytes(Sighash.ALL) + signatures = [(sig + sighash) for sig in signatures] tx.update_signatures(signatures) @runs_in_hwd_thread diff --git a/electrum/plugins/trezor/trezor.py b/electrum/plugins/trezor/trezor.py index 0405c0d1e..da6a57170 100644 --- a/electrum/plugins/trezor/trezor.py +++ b/electrum/plugins/trezor/trezor.py @@ -346,8 +346,8 @@ class TrezorPlugin(HW_PluginBase): amount_unit=self.get_trezor_amount_unit(), serialize=False, prev_txes=prev_tx) - sighash = Sighash.to_sigbytes(Sighash.ALL).hex() - signatures = [((x.hex() + sighash) if x else None) for x in signatures] + sighash = Sighash.to_sigbytes(Sighash.ALL) + signatures = [((sig + sighash) if sig else None) for sig in signatures] tx.update_signatures(signatures) @runs_in_hwd_thread diff --git a/electrum/scripts/peers.py b/electrum/scripts/peers.py index 64c25e0cd..0a65edf2d 100755 --- a/electrum/scripts/peers.py +++ b/electrum/scripts/peers.py @@ -21,7 +21,7 @@ async def f(): results = await network.send_multiple_requests(peers, 'blockchain.headers.subscribe', []) for server, header in sorted(results.items(), key=lambda x: x[1].get('height')): height = header.get('height') - blockhash = hash_raw_header(header.get('hex')) + blockhash = hash_raw_header(bytes.fromhex(header.get('hex'))) print(server, height, blockhash) finally: stopping_fut.set_result(1) diff --git a/electrum/submarine_swaps.py b/electrum/submarine_swaps.py index 56e232d6f..f14c2d31d 100644 --- a/electrum/submarine_swaps.py +++ b/electrum/submarine_swaps.py @@ -12,8 +12,8 @@ import aiohttp from . import lnutil from .crypto import sha256, hash_160 from .ecc import ECPrivkey -from .bitcoin import (script_to_p2wsh, opcodes, p2wsh_nested_script, push_script, - is_segwit_address, construct_witness) +from .bitcoin import (script_to_p2wsh, opcodes, + construct_witness) from .transaction import PartialTxInput, PartialTxOutput, PartialTransaction, Transaction, TxInput, TxOutpoint from .transaction import script_GetOp, match_script_against_template, OPPushDataGeneric, OPPushDataPubkey from .util import log_exceptions, BelowDustLimit, OldTaskGroup @@ -87,18 +87,17 @@ WITNESS_TEMPLATE_REVERSE_SWAP = [ def check_reverse_redeem_script( *, - redeem_script: str, + redeem_script: bytes, lockup_address: str, payment_hash: bytes, locktime: int, refund_pubkey: bytes = None, claim_pubkey: bytes = None, ) -> None: - redeem_script = bytes.fromhex(redeem_script) parsed_script = [x for x in script_GetOp(redeem_script)] if not match_script_against_template(redeem_script, WITNESS_TEMPLATE_REVERSE_SWAP): raise Exception("rswap check failed: scriptcode does not match template") - if script_to_p2wsh(redeem_script.hex()) != lockup_address: + if script_to_p2wsh(redeem_script) != lockup_address: raise Exception("rswap check failed: inconsistent scriptcode and address") if ripemd(payment_hash) != parsed_script[5][1]: raise Exception("rswap check failed: our preimage not in script") @@ -438,7 +437,7 @@ class SwapManager(Logger): def add_normal_swap( self, *, - redeem_script: str, + redeem_script: bytes, locktime: int, # onchain onchain_amount_sat: int, lightning_amount_sat: int, @@ -487,7 +486,7 @@ class SwapManager(Logger): lockup_address = script_to_p2wsh(redeem_script) receive_address = self.wallet.get_receiving_address() swap = SwapData( - redeem_script = bytes.fromhex(redeem_script), + redeem_script=redeem_script, locktime = locktime, privkey = our_privkey, preimage = None, @@ -533,7 +532,7 @@ class SwapManager(Logger): def add_reverse_swap( self, *, - redeem_script: str, + redeem_script: bytes, locktime: int, # onchain privkey: bytes, lightning_amount_sat: int, @@ -545,7 +544,7 @@ class SwapManager(Logger): lockup_address = script_to_p2wsh(redeem_script) receive_address = self.wallet.get_receiving_address() swap = SwapData( - redeem_script = bytes.fromhex(redeem_script), + redeem_script = redeem_script, locktime = locktime, privkey = privkey, preimage = preimage, @@ -645,7 +644,7 @@ class SwapManager(Logger): onchain_amount = data["expectedAmount"] locktime = data["timeoutBlockHeight"] lockup_address = data["address"] - redeem_script = data["redeemScript"] + redeem_script = bytes.fromhex(data["redeemScript"]) # verify redeem_script is built with our pubkey and preimage check_reverse_redeem_script( redeem_script=redeem_script, @@ -805,7 +804,7 @@ class SwapManager(Logger): invoice = data['invoice'] fee_invoice = data.get('minerFeeInvoice') lockup_address = data['lockupAddress'] - redeem_script = data['redeemScript'] + redeem_script = bytes.fromhex(data['redeemScript']) locktime = data['timeoutBlockHeight'] onchain_amount = data["onchainAmount"] response_id = data['id'] @@ -1052,7 +1051,7 @@ class SwapManager(Logger): txin.witness_script = witness_script sig_dummy = b'\x00' * 71 # DER-encoded ECDSA sig, with low S and low R witness = [sig_dummy, preimage, witness_script] - txin.witness_sizehint = len(bytes.fromhex(construct_witness(witness))) + txin.witness_sizehint = len(construct_witness(witness)) @classmethod def sign_tx(cls, tx: PartialTransaction, swap: SwapData) -> None: @@ -1063,9 +1062,9 @@ class SwapManager(Logger): assert txin.prevout.txid.hex() == swap.funding_txid txin.script_sig = b'' txin.witness_script = witness_script - sig = bytes.fromhex(tx.sign_txin(0, swap.privkey)) + sig = tx.sign_txin(0, swap.privkey) witness = [sig, preimage, witness_script] - txin.witness = bytes.fromhex(construct_witness(witness)) + txin.witness = construct_witness(witness) @classmethod def _create_and_sign_claim_tx( diff --git a/electrum/transaction.py b/electrum/transaction.py index fe50a3750..5ae075f6b 100644 --- a/electrum/transaction.py +++ b/electrum/transaction.py @@ -33,7 +33,7 @@ import sys import io import base64 from typing import (Sequence, Union, NamedTuple, Tuple, Optional, Iterable, - Callable, List, Dict, Set, TYPE_CHECKING) + Callable, List, Dict, Set, TYPE_CHECKING, Mapping) from collections import defaultdict from enum import IntEnum import itertools @@ -47,8 +47,7 @@ from .util import profiler, to_bytes, bfh, chunks, is_hex_str, parse_max_spend from .bitcoin import (TYPE_ADDRESS, TYPE_SCRIPT, hash_160, hash160_to_p2sh, hash160_to_p2pkh, hash_to_segwit_addr, var_int, TOTAL_COIN_SUPPLY_LIMIT_IN_BTC, COIN, - int_to_hex, push_script, b58_address_to_hash160, - opcodes, add_number_to_script, base_decode, + opcodes, base_decode, base_encode, construct_witness, construct_script) from .crypto import sha256d from .logging import get_logger @@ -138,13 +137,13 @@ class TxOutput: @classmethod def from_address_and_value(cls, address: str, value: Union[int, str]) -> Union['TxOutput', 'PartialTxOutput']: - return cls(scriptpubkey=bfh(bitcoin.address_to_script(address)), + return cls(scriptpubkey=bitcoin.address_to_script(address), value=value) def serialize_to_network(self) -> bytes: buf = int.to_bytes(self.value, 8, byteorder="little", signed=False) script = self.scriptpubkey - buf += bfh(var_int(len(script.hex()) // 2)) + buf += var_int(len(script)) buf += script return buf @@ -212,9 +211,9 @@ class TxOutput: class BIP143SharedTxDigestFields(NamedTuple): - hashPrevouts: str - hashSequence: str - hashOutputs: str + hashPrevouts: bytes + hashSequence: bytes + hashOutputs: bytes class TxOutpoint(NamedTuple): @@ -241,7 +240,7 @@ class TxOutpoint(NamedTuple): return [self.txid.hex(), self.out_idx] def serialize_to_network(self) -> bytes: - return self.txid[::-1] + bfh(int_to_hex(self.out_idx, 4)) + return self.txid[::-1] + int.to_bytes(self.out_idx, length=4, byteorder="little", signed=False) def is_coinbase(self) -> bool: return self.txid == bytes(32) @@ -356,9 +355,9 @@ class TxInput: # Prev hash and index s = self.prevout.serialize_to_network() # Script length, script, sequence - s += bytes.fromhex(var_int(len(script_sig))) + s += var_int(len(script_sig)) s += script_sig - s += bytes.fromhex(int_to_hex(self.nsequence, 4)) + s += int.to_bytes(self.nsequence, length=4, byteorder="little", signed=False) return s def witness_elements(self) -> Sequence[bytes]: @@ -714,7 +713,7 @@ def parse_input(vds: BCDataStream) -> TxInput: def parse_witness(vds: BCDataStream, txin: TxInput) -> None: n = vds.read_compact_size() witness_elements = list(vds.read_bytes(vds.read_compact_size()) for i in range(n)) - txin.witness = bfh(construct_witness(witness_elements)) + txin.witness = construct_witness(witness_elements) def parse_output(vds: BCDataStream) -> TxOutput: @@ -729,7 +728,7 @@ def parse_output(vds: BCDataStream) -> TxOutput: # pay & redeem scripts -def multisig_script(public_keys: Sequence[str], m: int) -> str: +def multisig_script(public_keys: Sequence[str], m: int) -> bytes: n = len(public_keys) assert 1 <= m <= n <= 15, f'm {m}, n {n}' return construct_script([m, *public_keys, n, opcodes.OP_CHECKMULTISIG]) @@ -833,18 +832,18 @@ class Transaction: raise SerializationError('extra junk at the end') @classmethod - def serialize_witness(cls, txin: TxInput, *, estimate_size=False) -> str: + def serialize_witness(cls, txin: TxInput, *, estimate_size=False) -> bytes: if txin.witness is not None: - return txin.witness.hex() + return txin.witness if txin.is_coinbase_input(): - return '' + return b"" assert isinstance(txin, PartialTxInput) if not txin.is_segwit(): return construct_witness([]) if estimate_size and txin.witness_sizehint is not None: - return '00' * txin.witness_sizehint + return bytes(txin.witness_sizehint) dummy_desc = None if estimate_size: @@ -852,22 +851,22 @@ class Transaction: if desc := (txin.script_descriptor or dummy_desc): sol = desc.satisfy(allow_dummy=estimate_size, sigdata=txin.part_sigs) if sol.witness is not None: - return sol.witness.hex() + return sol.witness return construct_witness([]) raise UnknownTxinType("cannot construct witness") @classmethod - def input_script(self, txin: TxInput, *, estimate_size=False) -> str: + def input_script(self, txin: TxInput, *, estimate_size=False) -> bytes: if txin.script_sig is not None: - return txin.script_sig.hex() + return txin.script_sig if txin.is_coinbase_input(): - return '' + return b"" assert isinstance(txin, PartialTxInput) if txin.is_p2sh_segwit() and txin.redeem_script: return construct_script([txin.redeem_script]) if txin.is_native_segwit(): - return '' + return b"" dummy_desc = None if estimate_size: @@ -876,37 +875,39 @@ class Transaction: if desc.is_segwit(): if redeem_script := desc.expand().redeem_script: return construct_script([redeem_script]) - return "" + return b"" sol = desc.satisfy(allow_dummy=estimate_size, sigdata=txin.part_sigs) if sol.script_sig is not None: - return sol.script_sig.hex() - return "" + return sol.script_sig + return b"" raise UnknownTxinType("cannot construct scriptSig") @classmethod - def get_preimage_script(cls, txin: 'PartialTxInput') -> str: + def get_preimage_script(cls, txin: 'PartialTxInput') -> bytes: if txin.witness_script: if opcodes.OP_CODESEPARATOR in [x[0] for x in script_GetOp(txin.witness_script)]: raise Exception('OP_CODESEPARATOR black magic is not supported') - return txin.witness_script.hex() + return txin.witness_script if not txin.is_segwit() and txin.redeem_script: if opcodes.OP_CODESEPARATOR in [x[0] for x in script_GetOp(txin.redeem_script)]: raise Exception('OP_CODESEPARATOR black magic is not supported') - return txin.redeem_script.hex() + return txin.redeem_script if desc := txin.script_descriptor: sc = desc.expand() if script := sc.scriptcode_for_sighash: - return script.hex() + return script raise Exception(f"don't know scriptcode for descriptor: {desc.to_string()}") raise UnknownTxinType(f'cannot construct preimage_script') def _calc_bip143_shared_txdigest_fields(self) -> BIP143SharedTxDigestFields: inputs = self.inputs() outputs = self.outputs() - hashPrevouts = sha256d(b''.join(txin.prevout.serialize_to_network() for txin in inputs)).hex() - hashSequence = sha256d(bfh(''.join(int_to_hex(txin.nsequence, 4) for txin in inputs))).hex() - hashOutputs = sha256d(bfh(''.join(o.serialize_to_network().hex() for o in outputs))).hex() + hashPrevouts = sha256d(b''.join(txin.prevout.serialize_to_network() for txin in inputs)) + hashSequence = sha256d(b''.join( + int.to_bytes(txin.nsequence, length=4, byteorder="little", signed=False) + for txin in inputs)) + hashOutputs = sha256d(b''.join(o.serialize_to_network() for o in outputs)) return BIP143SharedTxDigestFields(hashPrevouts=hashPrevouts, hashSequence=hashSequence, hashOutputs=hashOutputs) @@ -934,19 +935,20 @@ class Transaction: note: (not include_sigs) implies force_legacy """ self.deserialize() - nVersion = int_to_hex(self.version, 4) - nLocktime = int_to_hex(self.locktime, 4) + nVersion = int.to_bytes(self.version, length=4, byteorder="little", signed=True).hex() + nLocktime = int.to_bytes(self.locktime, length=4, byteorder="little", signed=False).hex() inputs = self.inputs() outputs = self.outputs() def create_script_sig(txin: TxInput) -> bytes: if include_sigs: script_sig = self.input_script(txin, estimate_size=estimate_size) - return bytes.fromhex(script_sig) + return script_sig return b"" - txins = var_int(len(inputs)) + ''.join(txin.serialize_to_network(script_sig=create_script_sig(txin)).hex() - for txin in inputs) - txouts = var_int(len(outputs)) + ''.join(o.serialize_to_network().hex() for o in outputs) + txins = var_int(len(inputs)).hex() + ''.join( + txin.serialize_to_network(script_sig=create_script_sig(txin)).hex() + for txin in inputs) + txouts = var_int(len(outputs)).hex() + ''.join(o.serialize_to_network().hex() for o in outputs) use_segwit_ser_for_estimate_size = estimate_size and self.is_segwit(guess_for_address=True) use_segwit_ser_for_actual_use = not estimate_size and self.is_segwit() @@ -954,7 +956,7 @@ class Transaction: if include_sigs and not force_legacy and use_segwit_ser: marker = '00' flag = '01' - witness = ''.join(self.serialize_witness(x, estimate_size=estimate_size) for x in inputs) + witness = ''.join(self.serialize_witness(x, estimate_size=estimate_size).hex() for x in inputs) return nVersion + marker + flag + txins + txouts + witness + nLocktime else: return nVersion + txins + txouts + nLocktime @@ -1073,7 +1075,7 @@ class Transaction: """Whether the tx explicitly signals BIP-0125 replace-by-fee.""" return any([txin.nsequence < 0xffffffff - 1 for txin in self.inputs()]) - def estimated_size(self): + def estimated_size(self) -> int: """Return an estimated virtual tx size in vbytes. BIP-0141 defines 'Virtual transaction size' to be weight/4 rounded up. This definition is only for humans, and has little meaning otherwise. @@ -1084,13 +1086,13 @@ class Transaction: return self.virtual_size_from_weight(weight) @classmethod - def estimated_input_weight(cls, txin: TxInput, is_segwit_tx: bool): + def estimated_input_weight(cls, txin: TxInput, is_segwit_tx: bool) -> int: '''Return an estimate of serialized input weight in weight units.''' script_sig = cls.input_script(txin, estimate_size=True) - input_size = len(txin.serialize_to_network(script_sig=bytes.fromhex(script_sig))) + input_size = len(txin.serialize_to_network(script_sig=script_sig)) if txin.is_segwit(guess_for_address=True): - witness_size = len(cls.serialize_witness(txin, estimate_size=True)) // 2 + witness_size = len(cls.serialize_witness(txin, estimate_size=True)) else: witness_size = 1 if is_segwit_tx else 0 @@ -1103,15 +1105,15 @@ class Transaction: return cls.estimated_output_size_for_script(script) @classmethod - def estimated_output_size_for_script(cls, script: str) -> int: + def estimated_output_size_for_script(cls, script: bytes) -> int: """Return an estimate of serialized output size in bytes.""" # 8 byte value + varint script len + script - script_len = len(script) // 2 - var_int_len = len(var_int(script_len)) // 2 + script_len = len(script) + var_int_len = len(var_int(script_len)) return 8 + var_int_len + script_len @classmethod - def virtual_size_from_weight(cls, weight): + def virtual_size_from_weight(cls, weight: int) -> int: return weight // 4 + (weight % 4 > 0) @classmethod @@ -1132,8 +1134,8 @@ class Transaction: if not self.is_segwit(guess_for_address=estimate): return 0 inputs = self.inputs() - witness = ''.join(self.serialize_witness(x, estimate_size=estimate) for x in inputs) - witness_size = len(witness) // 2 + 2 # include marker and flag + witness = b"".join(self.serialize_witness(x, estimate_size=estimate) for x in inputs) + witness_size = len(witness) + 2 # include marker and flag return witness_size def estimated_base_size(self): @@ -1149,17 +1151,16 @@ class Transaction: def is_complete(self) -> bool: return True - def get_output_idxs_from_scriptpubkey(self, script: str) -> Set[int]: + def get_output_idxs_from_scriptpubkey(self, script: bytes) -> Set[int]: """Returns the set indices of outputs with given script.""" - assert isinstance(script, str) # hex + assert isinstance(script, bytes) # build cache if there isn't one yet # note: can become stale and return incorrect data # if the tx is modified later; that's out of scope. if not hasattr(self, '_script_to_output_idx'): d = defaultdict(set) for output_idx, o in enumerate(self.outputs()): - o_script = o.scriptpubkey.hex() - assert isinstance(o_script, str) + o_script = o.scriptpubkey d[o_script].add(output_idx) self._script_to_output_idx = d return set(self._script_to_output_idx[script]) # copy @@ -1347,9 +1348,9 @@ class PSBTSection: def create_psbt_writer(cls, fd): def wr(key_type: int, val: bytes, key: bytes = b''): full_key = cls.get_fullkey_from_keytype_and_key(key_type, key) - fd.write(bytes.fromhex(var_int(len(full_key)))) # key_size + fd.write(var_int(len(full_key))) # key_size fd.write(full_key) # key - fd.write(bytes.fromhex(var_int(len(val)))) # val_size + fd.write(var_int(len(val))) # val_size fd.write(val) # val return wr @@ -1363,7 +1364,7 @@ class PSBTSection: @classmethod def get_fullkey_from_keytype_and_key(cls, key_type: int, key: bytes) -> bytes: - key_type_bytes = bytes.fromhex(var_int(key_type)) + key_type_bytes = var_int(key_type) return key_type_bytes + key def _serialize_psbt_section(self, fd): @@ -1492,11 +1493,11 @@ class PartialTxInput(TxInput, PSBTSection): f"If a redeemScript is provided, the scriptPubKey must be for that redeemScript") if self.witness_script: if self.redeem_script: - if self.redeem_script != bfh(bitcoin.p2wsh_nested_script(self.witness_script.hex())): + if self.redeem_script != bitcoin.p2wsh_nested_script(self.witness_script): raise PSBTInputConsistencyFailure(f"PSBT input validation: " f"If a witnessScript is provided, the redeemScript must be for that witnessScript") elif self.address: - if self.address != bitcoin.script_to_p2wsh(self.witness_script.hex()): + if self.address != bitcoin.script_to_p2wsh(self.witness_script): raise PSBTInputConsistencyFailure(f"PSBT input validation: " f"If a witnessScript is provided, the scriptPubKey must be for that witnessScript") @@ -1617,7 +1618,7 @@ class PartialTxInput(TxInput, PSBTSection): if (spk := super().scriptpubkey) is not None: return spk if self._trusted_address is not None: - return bfh(bitcoin.address_to_script(self._trusted_address)) + return bitcoin.address_to_script(self._trusted_address) if self.witness_utxo: return self.witness_utxo.scriptpubkey return None @@ -1657,8 +1658,8 @@ class PartialTxInput(TxInput, PSBTSection): clear_fields_when_finalized() return # already finalized if self.is_complete(): - self.script_sig = bfh(Transaction.input_script(self)) - self.witness = bfh(Transaction.serialize_witness(self)) + self.script_sig = Transaction.input_script(self) + self.witness = Transaction.serialize_witness(self) clear_fields_when_finalized() def combine_with_other_txin(self, other_txin: 'TxInput') -> None: @@ -2086,16 +2087,16 @@ class PartialTransaction(Transaction): self.invalidate_ser_cache() def serialize_preimage(self, txin_index: int, *, - bip143_shared_txdigest_fields: BIP143SharedTxDigestFields = None) -> str: - nVersion = int_to_hex(self.version, 4) - nLocktime = int_to_hex(self.locktime, 4) + bip143_shared_txdigest_fields: BIP143SharedTxDigestFields = None) -> bytes: + nVersion = int.to_bytes(self.version, length=4, byteorder="little", signed=True) + nLocktime = int.to_bytes(self.locktime, length=4, byteorder="little", signed=False) inputs = self.inputs() outputs = self.outputs() txin = inputs[txin_index] sighash = txin.sighash if txin.sighash is not None else Sighash.ALL if not Sighash.is_valid(sighash): raise Exception(f"SIGHASH_FLAG ({sighash}) not supported!") - nHashType = int_to_hex(sighash, 4) + nHashType = int.to_bytes(sighash, length=4, byteorder="little", signed=False) preimage_script = self.get_preimage_script(txin) if txin.is_segwit(): if bip143_shared_txdigest_fields is None: @@ -2103,58 +2104,64 @@ class PartialTransaction(Transaction): if not (sighash & Sighash.ANYONECANPAY): hashPrevouts = bip143_shared_txdigest_fields.hashPrevouts else: - hashPrevouts = '00' * 32 + hashPrevouts = bytes(32) if not (sighash & Sighash.ANYONECANPAY) and (sighash & 0x1f) != Sighash.SINGLE and (sighash & 0x1f) != Sighash.NONE: hashSequence = bip143_shared_txdigest_fields.hashSequence else: - hashSequence = '00' * 32 + hashSequence = bytes(32) if (sighash & 0x1f) != Sighash.SINGLE and (sighash & 0x1f) != Sighash.NONE: hashOutputs = bip143_shared_txdigest_fields.hashOutputs elif (sighash & 0x1f) == Sighash.SINGLE and txin_index < len(outputs): - hashOutputs = sha256d(outputs[txin_index].serialize_to_network()).hex() + hashOutputs = sha256d(outputs[txin_index].serialize_to_network()) else: - hashOutputs = '00' * 32 - outpoint = txin.prevout.serialize_to_network().hex() - scriptCode = var_int(len(preimage_script) // 2) + preimage_script - amount = int_to_hex(txin.value_sats(), 8) - nSequence = int_to_hex(txin.nsequence, 4) + hashOutputs = bytes(32) + outpoint = txin.prevout.serialize_to_network() + scriptCode = var_int(len(preimage_script)) + preimage_script + amount = int.to_bytes(txin.value_sats(), length=8, byteorder="little", signed=False) + nSequence = int.to_bytes(txin.nsequence, length=4, byteorder="little", signed=False) preimage = nVersion + hashPrevouts + hashSequence + outpoint + scriptCode + amount + nSequence + hashOutputs + nLocktime + nHashType else: if sighash != Sighash.ALL: raise Exception(f"SIGHASH_FLAG ({sighash}) not supported! (for legacy sighash)") - txins = var_int(len(inputs)) + ''.join(txin.serialize_to_network(script_sig=bfh(preimage_script) if txin_index==k else b"").hex() - for k, txin in enumerate(inputs)) - txouts = var_int(len(outputs)) + ''.join(o.serialize_to_network().hex() for o in outputs) + txins = var_int(len(inputs)) + b"".join( + txin.serialize_to_network(script_sig=preimage_script if txin_index==k else b"") + for k, txin in enumerate(inputs)) + txouts = var_int(len(outputs)) + b"".join(o.serialize_to_network() for o in outputs) preimage = nVersion + txins + txouts + nLocktime + nHashType return preimage - def sign(self, keypairs) -> None: - # keypairs: pubkey_hex -> (secret_bytes, is_compressed) + def sign(self, keypairs: Mapping[bytes, bytes]) -> None: + # keypairs: pubkey_bytes -> secret_bytes bip143_shared_txdigest_fields = self._calc_bip143_shared_txdigest_fields() for i, txin in enumerate(self.inputs()): - pubkeys = [pk.hex() for pk in txin.pubkeys] - for pubkey in pubkeys: + for pubkey in txin.pubkeys: if txin.is_complete(): break if pubkey not in keypairs: continue _logger.info(f"adding signature for {pubkey}. spending utxo {txin.prevout.to_str()}") - sec, compressed = keypairs[pubkey] + sec = keypairs[pubkey] sig = self.sign_txin(i, sec, bip143_shared_txdigest_fields=bip143_shared_txdigest_fields) self.add_signature_to_txin(txin_idx=i, signing_pubkey=pubkey, sig=sig) - _logger.debug(f"is_complete {self.is_complete()}") + _logger.debug(f"tx.sign() finished. is_complete={self.is_complete()}") self.invalidate_ser_cache() - def sign_txin(self, txin_index, privkey_bytes, *, bip143_shared_txdigest_fields=None) -> str: + def sign_txin( + self, + txin_index: int, + privkey_bytes: bytes, + *, + bip143_shared_txdigest_fields=None, + ) -> bytes: txin = self.inputs()[txin_index] txin.validate_data(for_signing=True) sighash = txin.sighash if txin.sighash is not None else Sighash.ALL - pre_hash = sha256d(bfh(self.serialize_preimage(txin_index, - bip143_shared_txdigest_fields=bip143_shared_txdigest_fields))) + pre_hash = self.serialize_preimage(txin_index, bip143_shared_txdigest_fields=bip143_shared_txdigest_fields) + msg_hash = sha256d(pre_hash) privkey = ecc.ECPrivkey(privkey_bytes) - sig = privkey.ecdsa_sign(pre_hash, sigencode=ecc.ecdsa_der_sig_from_r_and_s) - sig = sig.hex() + Sighash.to_sigbytes(sighash).hex() + sig = privkey.ecdsa_sign(msg_hash, sigencode=ecc.ecdsa_der_sig_from_r_and_s) + sig = sig + Sighash.to_sigbytes(sighash) return sig def is_complete(self) -> bool: @@ -2189,7 +2196,7 @@ class PartialTransaction(Transaction): raw_bytes = self.serialize_as_bytes() return base64.b64encode(raw_bytes).decode('ascii') - def update_signatures(self, signatures: Sequence[Union[str, None]]): + def update_signatures(self, signatures: Sequence[Union[bytes, None]]) -> None: """Add new signatures to a transaction `signatures` is expected to be a list of sigs with signatures[i] @@ -2201,33 +2208,32 @@ class PartialTransaction(Transaction): if len(self.inputs()) != len(signatures): raise Exception('expected {} signatures; got {}'.format(len(self.inputs()), len(signatures))) for i, txin in enumerate(self.inputs()): - pubkeys = [pk.hex() for pk in txin.pubkeys] sig = signatures[i] if sig is None: continue - if bfh(sig) in list(txin.part_sigs.values()): + if sig in list(txin.part_sigs.values()): continue - pre_hash = sha256d(bfh(self.serialize_preimage(i))) - sig_string = ecc.ecdsa_sig64_from_der_sig(bfh(sig[:-2])) + msg_hash = sha256d(self.serialize_preimage(i)) + sig64 = ecc.ecdsa_sig64_from_der_sig(sig[:-1]) for recid in range(4): try: - public_key = ecc.ECPubkey.from_ecdsa_sig64(sig_string, recid, pre_hash) + public_key = ecc.ECPubkey.from_ecdsa_sig64(sig64, recid, msg_hash) except ecc.InvalidECPointException: # the point might not be on the curve for some recid values continue - pubkey_hex = public_key.get_public_key_hex(compressed=True) - if pubkey_hex in pubkeys: - if not public_key.ecdsa_verify(sig_string, pre_hash): + pubkey_bytes = public_key.get_public_key_bytes(compressed=True) + if pubkey_bytes in txin.pubkeys: + if not public_key.ecdsa_verify(sig64, msg_hash): continue - _logger.info(f"adding sig: txin_idx={i}, signing_pubkey={pubkey_hex}, sig={sig}") - self.add_signature_to_txin(txin_idx=i, signing_pubkey=pubkey_hex, sig=sig) + _logger.info(f"adding sig: txin_idx={i}, signing_pubkey={pubkey_bytes.hex()}, sig={sig.hex()}") + self.add_signature_to_txin(txin_idx=i, signing_pubkey=pubkey_bytes, sig=sig) break # redo raw self.invalidate_ser_cache() - def add_signature_to_txin(self, *, txin_idx: int, signing_pubkey: str, sig: str): + def add_signature_to_txin(self, *, txin_idx: int, signing_pubkey: bytes, sig: bytes) -> None: txin = self._inputs[txin_idx] - txin.part_sigs[bfh(signing_pubkey)] = bfh(sig) + txin.part_sigs[signing_pubkey] = sig # force re-serialization txin.script_sig = None txin.witness = None diff --git a/electrum/wallet.py b/electrum/wallet.py index b1fa943f0..101d431d5 100644 --- a/electrum/wallet.py +++ b/electrum/wallet.py @@ -40,7 +40,7 @@ from functools import partial from collections import defaultdict from numbers import Number from decimal import Decimal -from typing import TYPE_CHECKING, List, Optional, Tuple, Union, NamedTuple, Sequence, Dict, Any, Set, Iterable +from typing import TYPE_CHECKING, List, Optional, Tuple, Union, NamedTuple, Sequence, Dict, Any, Set, Iterable, Mapping from abc import ABC, abstractmethod import itertools import threading @@ -111,14 +111,14 @@ async def _append_utxos_to_inputs( script_descriptor: 'descriptor.Descriptor', imax: int, ) -> None: - script = script_descriptor.expand().output_script.hex() + script = script_descriptor.expand().output_script scripthash = bitcoin.script_to_scripthash(script) async def append_single_utxo(item): prev_tx_raw = await network.get_transaction(item['tx_hash']) prev_tx = Transaction(prev_tx_raw) prev_txout = prev_tx.outputs()[item['tx_pos']] - if scripthash != bitcoin.script_to_scripthash(prev_txout.scriptpubkey.hex()): + if scripthash != bitcoin.script_to_scripthash(prev_txout.scriptpubkey): raise Exception('scripthash mismatch when sweeping') prevout_str = item['tx_hash'] + ':%d' % item['tx_pos'] prevout = TxOutpoint.from_str(prevout_str) @@ -136,20 +136,22 @@ async def _append_utxos_to_inputs( await group.spawn(append_single_utxo(item)) -async def sweep_preparations(privkeys, network: 'Network', imax=100): +async def sweep_preparations( + privkeys: Iterable[str], network: 'Network', imax=100, +) -> Tuple[Sequence[PartialTxInput], Mapping[bytes, bytes]]: - async def find_utxos_for_privkey(txin_type, privkey, compressed): - pubkey = ecc.ECPrivkey(privkey).get_public_key_hex(compressed=compressed) - desc = descriptor.get_singlesig_descriptor_from_legacy_leaf(pubkey=pubkey, script_type=txin_type) + async def find_utxos_for_privkey(txin_type: str, privkey: bytes, compressed: bool): + pubkey = ecc.ECPrivkey(privkey).get_public_key_bytes(compressed=compressed) + desc = descriptor.get_singlesig_descriptor_from_legacy_leaf(pubkey=pubkey.hex(), script_type=txin_type) await _append_utxos_to_inputs( inputs=inputs, network=network, script_descriptor=desc, imax=imax) - keypairs[pubkey] = privkey, compressed + keypairs[pubkey] = privkey inputs = [] # type: List[PartialTxInput] - keypairs = {} + keypairs = {} # type: Dict[bytes, bytes] async with OldTaskGroup() as group: for sec in privkeys: txin_type, privkey, compressed = bitcoin.deserialize_privkey(sec) @@ -169,7 +171,7 @@ async def sweep_preparations(privkeys, network: 'Network', imax=100): async def sweep( - privkeys, + privkeys: Iterable[str], *, network: 'Network', config: 'SimpleConfig', @@ -182,7 +184,7 @@ async def sweep( inputs, keypairs = await sweep_preparations(privkeys, network, imax) total = sum(txin.value_sats() for txin in inputs) if fee is None: - outputs = [PartialTxOutput(scriptpubkey=bfh(bitcoin.address_to_script(to_address)), + outputs = [PartialTxOutput(scriptpubkey=bitcoin.address_to_script(to_address), value=total)] tx = PartialTransaction.from_io(inputs, outputs) fee = config.estimate_fee(tx.estimated_size()) @@ -191,7 +193,7 @@ async def sweep( if total - fee < dust_threshold(network): raise Exception(_('Not enough funds on address.') + '\nTotal: %d satoshis\nFee: %d\nDust Threshold: %d'%(total, fee, dust_threshold(network))) - outputs = [PartialTxOutput(scriptpubkey=bfh(bitcoin.address_to_script(to_address)), + outputs = [PartialTxOutput(scriptpubkey=bitcoin.address_to_script(to_address), value=total - fee)] if locktime is None: locktime = get_locktime_for_new_transaction(network) @@ -1297,7 +1299,7 @@ class Abstract_Wallet(ABC, Logger, EventListener): conf_needed = None # type: Optional[int] with self.lock, self.transaction_lock: for invoice_scriptpubkey, invoice_amt in invoice_amounts.items(): - scripthash = bitcoin.script_to_scripthash(invoice_scriptpubkey.hex()) + scripthash = bitcoin.script_to_scripthash(invoice_scriptpubkey) prevouts_and_values = self.db.get_prevouts_by_scripthash(scripthash) confs_and_values = [] for prevout, v in prevouts_and_values: @@ -2266,12 +2268,12 @@ class Abstract_Wallet(ABC, Logger, EventListener): delta_total = target_fee - cur_fee if delta_total <= 0: break - out_size_total = sum(Transaction.estimated_output_size_for_script(out.scriptpubkey.hex()) + out_size_total = sum(Transaction.estimated_output_size_for_script(out.scriptpubkey) for (idx, out) in s if idx not in del_out_idxs) if out_size_total == 0: # no outputs left to decrease raise CannotBumpFee(_('Could not find suitable outputs')) for idx, out in s: - out_size = Transaction.estimated_output_size_for_script(out.scriptpubkey.hex()) + out_size = Transaction.estimated_output_size_for_script(out.scriptpubkey) delta = int(math.ceil(delta_total * out_size / out_size_total)) if out.value - delta >= self.dust_threshold(): new_output_value = out.value - delta @@ -3804,7 +3806,7 @@ class Multisig_Wallet(Deterministic_Wallet): redeem_script = self.pubkeys_to_scriptcode(pubkeys) return bitcoin.redeem_script_to_address(self.txin_type, redeem_script) - def pubkeys_to_scriptcode(self, pubkeys: Sequence[str]) -> str: + def pubkeys_to_scriptcode(self, pubkeys: Sequence[str]) -> bytes: return transaction.multisig_script(sorted(pubkeys), self.m) def derive_pubkeys(self, c, i): diff --git a/electrum/wallet_db.py b/electrum/wallet_db.py index 82ac750e6..fc3004d75 100644 --- a/electrum/wallet_db.py +++ b/electrum/wallet_db.py @@ -522,7 +522,7 @@ class WalletDBUpgrader(Logger): tx = Transaction(raw_tx) for idx, txout in enumerate(tx.outputs()): outpoint = f"{txid}:{idx}" - scripthash = script_to_scripthash(txout.scriptpubkey.hex()) + scripthash = script_to_scripthash(txout.scriptpubkey) prevouts_by_scripthash[scripthash].append((outpoint, txout.value)) self.put('prevouts_by_scripthash', prevouts_by_scripthash) @@ -1126,7 +1126,7 @@ class WalletDBUpgrader(Logger): tx = Transaction(raw_tx) for idx, txout in enumerate(tx.outputs()): outpoint = f"{txid}:{idx}" - scripthash = script_to_scripthash(txout.scriptpubkey.hex()) + scripthash = script_to_scripthash(txout.scriptpubkey) if scripthash not in prevouts_by_scripthash: prevouts_by_scripthash[scripthash] = {} prevouts_by_scripthash[scripthash][outpoint] = txout.value diff --git a/tests/test_bitcoin.py b/tests/test_bitcoin.py index 88de7c484..be0b05330 100644 --- a/tests/test_bitcoin.py +++ b/tests/test_bitcoin.py @@ -8,7 +8,7 @@ from electrum.bitcoin import (public_key_to_p2pkh, address_from_private_key, deserialize_privkey, serialize_privkey, is_segwit_address, is_b58_address, address_to_scripthash, is_minikey, is_compressed_privkey, EncodeBase58Check, DecodeBase58Check, - script_num_to_hex, push_script, add_number_to_script, int_to_hex, + script_num_to_bytes, push_script, add_number_to_script, opcodes, base_encode, base_decode, BitcoinException) from electrum import bip32 from electrum import segwit_addr @@ -423,83 +423,63 @@ class Test_bitcoin(ElectrumTestCase): self.assertEqual(b'\x95MZI\xfdp\xd9\xb8\xbc\xdb5\xd2R&x)\x95\x7f~\xf7\xfalt\xf8\x84\x19\xbd\xc5\xe8"\t\xf4', sha256d(u"test")) - def test_int_to_hex(self): - self.assertEqual('00', int_to_hex(0, 1)) - self.assertEqual('ff', int_to_hex(-1, 1)) - self.assertEqual('00000000', int_to_hex(0, 4)) - self.assertEqual('01000000', int_to_hex(1, 4)) - self.assertEqual('7f', int_to_hex(127, 1)) - self.assertEqual('7f00', int_to_hex(127, 2)) - self.assertEqual('80', int_to_hex(128, 1)) - self.assertEqual('80', int_to_hex(-128, 1)) - self.assertEqual('8000', int_to_hex(128, 2)) - self.assertEqual('ff', int_to_hex(255, 1)) - self.assertEqual('ff7f', int_to_hex(32767, 2)) - self.assertEqual('0080', int_to_hex(-32768, 2)) - self.assertEqual('ffff', int_to_hex(65535, 2)) - with self.assertRaises(OverflowError): int_to_hex(256, 1) - with self.assertRaises(OverflowError): int_to_hex(-129, 1) - with self.assertRaises(OverflowError): int_to_hex(-257, 1) - with self.assertRaises(OverflowError): int_to_hex(65536, 2) - with self.assertRaises(OverflowError): int_to_hex(-32769, 2) - def test_var_int(self): for i in range(0xfd): - self.assertEqual(var_int(i), "{:02x}".format(i)) - - self.assertEqual(var_int(0xfd), "fdfd00") - self.assertEqual(var_int(0xfe), "fdfe00") - self.assertEqual(var_int(0xff), "fdff00") - self.assertEqual(var_int(0x1234), "fd3412") - self.assertEqual(var_int(0xffff), "fdffff") - self.assertEqual(var_int(0x10000), "fe00000100") - self.assertEqual(var_int(0x12345678), "fe78563412") - self.assertEqual(var_int(0xffffffff), "feffffffff") - self.assertEqual(var_int(0x100000000), "ff0000000001000000") - self.assertEqual(var_int(0x0123456789abcdef), "ffefcdab8967452301") + self.assertEqual(var_int(i), bfh("{:02x}".format(i))) + + self.assertEqual(var_int(0xfd), bfh("fdfd00")) + self.assertEqual(var_int(0xfe), bfh("fdfe00")) + self.assertEqual(var_int(0xff), bfh("fdff00")) + self.assertEqual(var_int(0x1234), bfh("fd3412")) + self.assertEqual(var_int(0xffff), bfh("fdffff")) + self.assertEqual(var_int(0x10000), bfh("fe00000100")) + self.assertEqual(var_int(0x12345678), bfh("fe78563412")) + self.assertEqual(var_int(0xffffffff), bfh("feffffffff")) + self.assertEqual(var_int(0x100000000), bfh("ff0000000001000000")) + self.assertEqual(var_int(0x0123456789abcdef), bfh("ffefcdab8967452301")) def test_op_push(self): - self.assertEqual(_op_push(0x00), '00') - self.assertEqual(_op_push(0x12), '12') - self.assertEqual(_op_push(0x4b), '4b') - self.assertEqual(_op_push(0x4c), '4c4c') - self.assertEqual(_op_push(0xfe), '4cfe') - self.assertEqual(_op_push(0xff), '4cff') - self.assertEqual(_op_push(0x100), '4d0001') - self.assertEqual(_op_push(0x1234), '4d3412') - self.assertEqual(_op_push(0xfffe), '4dfeff') - self.assertEqual(_op_push(0xffff), '4dffff') - self.assertEqual(_op_push(0x10000), '4e00000100') - self.assertEqual(_op_push(0x12345678), '4e78563412') + self.assertEqual(_op_push(0x00), bfh('00')) + self.assertEqual(_op_push(0x12), bfh('12')) + self.assertEqual(_op_push(0x4b), bfh('4b')) + self.assertEqual(_op_push(0x4c), bfh('4c4c')) + self.assertEqual(_op_push(0xfe), bfh('4cfe')) + self.assertEqual(_op_push(0xff), bfh('4cff')) + self.assertEqual(_op_push(0x100), bfh('4d0001')) + self.assertEqual(_op_push(0x1234), bfh('4d3412')) + self.assertEqual(_op_push(0xfffe), bfh('4dfeff')) + self.assertEqual(_op_push(0xffff), bfh('4dffff')) + self.assertEqual(_op_push(0x10000), bfh('4e00000100')) + self.assertEqual(_op_push(0x12345678), bfh('4e78563412')) def test_script_num_to_hex(self): # test vectors from https://github.com/btcsuite/btcd/blob/fdc2bc867bda6b351191b5872d2da8270df00d13/txscript/scriptnum.go#L77 - self.assertEqual(script_num_to_hex(127), '7f') - self.assertEqual(script_num_to_hex(-127), 'ff') - self.assertEqual(script_num_to_hex(128), '8000') - self.assertEqual(script_num_to_hex(-128), '8080') - self.assertEqual(script_num_to_hex(129), '8100') - self.assertEqual(script_num_to_hex(-129), '8180') - self.assertEqual(script_num_to_hex(256), '0001') - self.assertEqual(script_num_to_hex(-256), '0081') - self.assertEqual(script_num_to_hex(32767), 'ff7f') - self.assertEqual(script_num_to_hex(-32767), 'ffff') - self.assertEqual(script_num_to_hex(32768), '008000') - self.assertEqual(script_num_to_hex(-32768), '008080') + self.assertEqual(script_num_to_bytes(127), bfh('7f')) + self.assertEqual(script_num_to_bytes(-127), bfh('ff')) + self.assertEqual(script_num_to_bytes(128), bfh('8000')) + self.assertEqual(script_num_to_bytes(-128), bfh('8080')) + self.assertEqual(script_num_to_bytes(129), bfh('8100')) + self.assertEqual(script_num_to_bytes(-129), bfh('8180')) + self.assertEqual(script_num_to_bytes(256), bfh('0001')) + self.assertEqual(script_num_to_bytes(-256), bfh('0081')) + self.assertEqual(script_num_to_bytes(32767), bfh('ff7f')) + self.assertEqual(script_num_to_bytes(-32767), bfh('ffff')) + self.assertEqual(script_num_to_bytes(32768), bfh('008000')) + self.assertEqual(script_num_to_bytes(-32768), bfh('008080')) def test_push_script(self): # https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#push-operators - self.assertEqual(push_script(''), bytes([opcodes.OP_0]).hex()) - self.assertEqual(push_script('07'), bytes([opcodes.OP_7]).hex()) - self.assertEqual(push_script('10'), bytes([opcodes.OP_16]).hex()) - self.assertEqual(push_script('81'), bytes([opcodes.OP_1NEGATE]).hex()) - self.assertEqual(push_script('11'), '0111') - self.assertEqual(push_script(75 * '42'), '4b' + 75 * '42') - self.assertEqual(push_script(76 * '42'), (bytes([opcodes.OP_PUSHDATA1]) + bfh('4c' + 76 * '42')).hex()) - self.assertEqual(push_script(100 * '42'), (bytes([opcodes.OP_PUSHDATA1]) + bfh('64' + 100 * '42')).hex()) - self.assertEqual(push_script(255 * '42'), (bytes([opcodes.OP_PUSHDATA1]) + bfh('ff' + 255 * '42')).hex()) - self.assertEqual(push_script(256 * '42'), (bytes([opcodes.OP_PUSHDATA2]) + bfh('0001' + 256 * '42')).hex()) - self.assertEqual(push_script(520 * '42'), (bytes([opcodes.OP_PUSHDATA2]) + bfh('0802' + 520 * '42')).hex()) + self.assertEqual(push_script(b""), bytes([opcodes.OP_0])) + self.assertEqual(push_script(b'\x07'), bytes([opcodes.OP_7])) + self.assertEqual(push_script(b'\x10'), bytes([opcodes.OP_16])) + self.assertEqual(push_script(b'\x81'), bytes([opcodes.OP_1NEGATE])) + self.assertEqual(push_script(b'\x11'), bfh('0111')) + self.assertEqual(push_script(75 * b'\x42'), bfh('4b' + 75 * '42')) + self.assertEqual(push_script(76 * b'\x42'), bytes([opcodes.OP_PUSHDATA1]) + bfh('4c' + 76 * '42')) + self.assertEqual(push_script(100 * b'\x42'), bytes([opcodes.OP_PUSHDATA1]) + bfh('64' + 100 * '42')) + self.assertEqual(push_script(255 * b'\x42'), bytes([opcodes.OP_PUSHDATA1]) + bfh('ff' + 255 * '42')) + self.assertEqual(push_script(256 * b'\x42'), bytes([opcodes.OP_PUSHDATA2]) + bfh('0001' + 256 * '42')) + self.assertEqual(push_script(520 * b'\x42'), bytes([opcodes.OP_PUSHDATA2]) + bfh('0802' + 520 * '42')) def test_add_number_to_script(self): # https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#numbers @@ -528,17 +508,17 @@ class Test_bitcoin(ElectrumTestCase): # bech32/bech32m native segwit # test vectors from BIP-0173 # note: the ones that are commented out have been invalidated by BIP-0350 - self.assertEqual(address_to_script('BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4'), '0014751e76e8199196d454941c45d1b3a323f1433bd6') + self.assertEqual(address_to_script('BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4').hex(), '0014751e76e8199196d454941c45d1b3a323f1433bd6') # self.assertEqual(address_to_script('bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx'), '5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6') # self.assertEqual(address_to_script('BC1SW50QA3JX3S'), '6002751e') # self.assertEqual(address_to_script('bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj'), '5210751e76e8199196d454941c45d1b3a323') # bech32/bech32m native segwit # test vectors from BIP-0350 - self.assertEqual(address_to_script('bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y'), '5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6') - self.assertEqual(address_to_script('BC1SW50QGDZ25J'), '6002751e') - self.assertEqual(address_to_script('bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs'), '5210751e76e8199196d454941c45d1b3a323') - self.assertEqual(address_to_script('bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0'), '512079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798') + self.assertEqual(address_to_script('bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y').hex(), '5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6') + self.assertEqual(address_to_script('BC1SW50QGDZ25J').hex(), '6002751e') + self.assertEqual(address_to_script('bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs').hex(), '5210751e76e8199196d454941c45d1b3a323') + self.assertEqual(address_to_script('bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0').hex(), '512079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798') # invalid addresses (from BIP-0173) self.assertFalse(is_address('tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty')) @@ -570,12 +550,12 @@ class Test_bitcoin(ElectrumTestCase): self.assertFalse(is_address('bc1gmk9yu')) # base58 P2PKH - self.assertEqual(address_to_script('14gcRovpkCoGkCNBivQBvw7eso7eiNAbxG'), '76a91428662c67561b95c79d2257d2a93d9d151c977e9188ac') - self.assertEqual(address_to_script('1BEqfzh4Y3zzLosfGhw1AsqbEKVW6e1qHv'), '76a914704f4b81cadb7bf7e68c08cd3657220f680f863c88ac') + self.assertEqual(address_to_script('14gcRovpkCoGkCNBivQBvw7eso7eiNAbxG').hex(), '76a91428662c67561b95c79d2257d2a93d9d151c977e9188ac') + self.assertEqual(address_to_script('1BEqfzh4Y3zzLosfGhw1AsqbEKVW6e1qHv').hex(), '76a914704f4b81cadb7bf7e68c08cd3657220f680f863c88ac') # base58 P2SH - self.assertEqual(address_to_script('35ZqQJcBQMZ1rsv8aSuJ2wkC7ohUCQMJbT'), 'a9142a84cf00d47f699ee7bbc1dea5ec1bdecb4ac15487') - self.assertEqual(address_to_script('3PyjzJ3im7f7bcV724GR57edKDqoZvH7Ji'), 'a914f47c8954e421031ad04ecd8e7752c9479206b9d387') + self.assertEqual(address_to_script('35ZqQJcBQMZ1rsv8aSuJ2wkC7ohUCQMJbT').hex(), 'a9142a84cf00d47f699ee7bbc1dea5ec1bdecb4ac15487') + self.assertEqual(address_to_script('3PyjzJ3im7f7bcV724GR57edKDqoZvH7Ji').hex(), 'a914f47c8954e421031ad04ecd8e7752c9479206b9d387') def test_address_to_payload(self): # bech32 P2WPKH @@ -698,14 +678,14 @@ class Test_bitcoin_testnet(ElectrumTestCase): def test_address_to_script(self): # bech32/bech32m native segwit # test vectors from BIP-0173 - self.assertEqual(address_to_script('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7'), '00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262') - self.assertEqual(address_to_script('tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy'), '0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433') + self.assertEqual(address_to_script('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7').hex(), '00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262') + self.assertEqual(address_to_script('tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy').hex(), '0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433') # bech32/bech32m native segwit # test vectors from BIP-0350 - self.assertEqual(address_to_script('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7'), '00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262') - self.assertEqual(address_to_script('tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy'), '0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433') - self.assertEqual(address_to_script('tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c'), '5120000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433') + self.assertEqual(address_to_script('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7').hex(), '00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262') + self.assertEqual(address_to_script('tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy').hex(), '0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433') + self.assertEqual(address_to_script('tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c').hex(), '5120000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433') # invalid addresses (from BIP-0173) self.assertFalse(is_address('tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty')) @@ -737,12 +717,12 @@ class Test_bitcoin_testnet(ElectrumTestCase): self.assertFalse(is_address('bc1gmk9yu')) # base58 P2PKH - self.assertEqual(address_to_script('mutXcGt1CJdkRvXuN2xoz2quAAQYQ59bRX'), '76a9149da64e300c5e4eb4aaffc9c2fd465348d5618ad488ac') - self.assertEqual(address_to_script('miqtaRTkU3U8rzwKbEHx3g8FSz8GJtPS3K'), '76a914247d2d5b6334bdfa2038e85b20fc15264f8e5d2788ac') + self.assertEqual(address_to_script('mutXcGt1CJdkRvXuN2xoz2quAAQYQ59bRX').hex(), '76a9149da64e300c5e4eb4aaffc9c2fd465348d5618ad488ac') + self.assertEqual(address_to_script('miqtaRTkU3U8rzwKbEHx3g8FSz8GJtPS3K').hex(), '76a914247d2d5b6334bdfa2038e85b20fc15264f8e5d2788ac') # base58 P2SH - self.assertEqual(address_to_script('2N3LSvr3hv5EVdfcrxg2Yzecf3SRvqyBE4p'), 'a9146eae23d8c4a941316017946fc761a7a6c85561fb87') - self.assertEqual(address_to_script('2NE4ZdmxFmUgwu5wtfoN2gVniyMgRDYq1kk'), 'a914e4567743d378957cd2ee7072da74b1203c1a7a0b87') + self.assertEqual(address_to_script('2N3LSvr3hv5EVdfcrxg2Yzecf3SRvqyBE4p').hex(), 'a9146eae23d8c4a941316017946fc761a7a6c85561fb87') + self.assertEqual(address_to_script('2NE4ZdmxFmUgwu5wtfoN2gVniyMgRDYq1kk').hex(), 'a914e4567743d378957cd2ee7072da74b1203c1a7a0b87') class Test_xprv_xpub(ElectrumTestCase): diff --git a/tests/test_lnpeer.py b/tests/test_lnpeer.py index 096b02781..1eae2580d 100644 --- a/tests/test_lnpeer.py +++ b/tests/test_lnpeer.py @@ -1208,7 +1208,7 @@ class TestPeerDirect(TestPeer): # shutdown script bob_uss_pub = lnutil.privkey_to_pubkey(os.urandom(32)) bob_uss_addr = bitcoin.pubkey_to_address('p2wpkh', bob_uss_pub.hex()) - bob_uss = bfh(bitcoin.address_to_script(bob_uss_addr)) + bob_uss = bitcoin.address_to_script(bob_uss_addr) # bob commits to close to bob_uss alice_channel.config[HTLCOwner.REMOTE].upfront_shutdown_script = bob_uss diff --git a/tests/test_lnutil.py b/tests/test_lnutil.py index f17f29d3a..3689253d7 100644 --- a/tests/test_lnutil.py +++ b/tests/test_lnutil.py @@ -508,7 +508,7 @@ class TestLNUtil(ElectrumTestCase): htlc_payment_preimage[4] = b"\x04" * 32 htlc[4] = make_received_htlc(**htlc_pubkeys, payment_hash=bitcoin.sha256(htlc_payment_preimage[4]), cltv_abs=htlc_cltv_timeout[4]) - remote_signature = "304402204fd4928835db1ccdfc40f5c78ce9bd65249b16348df81f0c44328dcdefc97d630220194d3869c38bc732dd87d13d2958015e2fc16829e74cd4377f84d215c0b70606" + remote_signature = bfh("304402204fd4928835db1ccdfc40f5c78ce9bd65249b16348df81f0c44328dcdefc97d630220194d3869c38bc732dd87d13d2958015e2fc16829e74cd4377f84d215c0b70606") output_commit_tx = "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8007e80300000000000022002052bfef0479d7b293c27e0f1eb294bea154c63a3294ef092c19af51409bce0e2ad007000000000000220020403d394747cae42e98ff01734ad5c08f82ba123d3d9a620abda88989651e2ab5d007000000000000220020748eba944fedc8827f6b06bc44678f93c0f9e6078b35c6331ed31e75f8ce0c2db80b000000000000220020c20b5d1f8584fd90443e7b7b720136174fa4b9333c261d04dbbd012635c0f419a00f0000000000002200208c48d15160397c9731df9bc3b236656efb6665fbfe92b4a6878e88a499f741c4c0c62d0000000000160014ccf1af2f2aabee14bb40fa3851ab2301de843110e0a06a00000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e04004730440220275b0c325a5e9355650dc30c0eccfbc7efb23987c24b556b9dfdd40effca18d202206caceb2c067836c51f296740c7ae807ffcbfbf1dd3a0d56b6de9a5b247985f060147304402204fd4928835db1ccdfc40f5c78ce9bd65249b16348df81f0c44328dcdefc97d630220194d3869c38bc732dd87d13d2958015e2fc16829e74cd4377f84d215c0b7060601475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220" htlc_obj = {} @@ -594,7 +594,7 @@ class TestLNUtil(ElectrumTestCase): our_htlc_tx_witness = make_htlc_tx_witness( remotehtlcsig=bfh(remote_htlc_sig) + b"\x01", # 0x01 is SIGHASH_ALL - localhtlcsig=bfh(local_sig), + localhtlcsig=local_sig, payment_preimage=htlc_payment_preimage if success else b'', # will put 00 on witness if timeout witness_script=htlc) our_htlc_tx._inputs[0].witness = our_htlc_tx_witness @@ -604,7 +604,7 @@ class TestLNUtil(ElectrumTestCase): to_local_msat= 6988000000 to_remote_msat= 3000000000 local_feerate_per_kw= 9651181 - remote_signature = "3044022064901950be922e62cbe3f2ab93de2b99f37cff9fc473e73e394b27f88ef0731d02206d1dfa227527b4df44a07599289e207d6fd9cca60c0365682dcd3deaf739567e" + remote_signature = bfh("3044022064901950be922e62cbe3f2ab93de2b99f37cff9fc473e73e394b27f88ef0731d02206d1dfa227527b4df44a07599289e207d6fd9cca60c0365682dcd3deaf739567e") output_commit_tx= "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8001c0c62d0000000000160014ccf1af2f2aabee14bb40fa3851ab2301de8431100400473044022031a82b51bd014915fe68928d1abf4b9885353fb896cac10c3fdd88d7f9c7f2e00220716bda819641d2c63e65d3549b6120112e1aeaf1742eed94a471488e79e206b101473044022064901950be922e62cbe3f2ab93de2b99f37cff9fc473e73e394b27f88ef0731d02206d1dfa227527b4df44a07599289e207d6fd9cca60c0365682dcd3deaf739567e01475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220" our_commit_tx = make_commitment( @@ -633,7 +633,7 @@ class TestLNUtil(ElectrumTestCase): to_local_msat= 6988000000 to_remote_msat= 3000000000 local_feerate_per_kw= 9651936 - remote_signature = "3044022064901950be922e62cbe3f2ab93de2b99f37cff9fc473e73e394b27f88ef0731d02206d1dfa227527b4df44a07599289e207d6fd9cca60c0365682dcd3deaf739567e" + remote_signature = bfh("3044022064901950be922e62cbe3f2ab93de2b99f37cff9fc473e73e394b27f88ef0731d02206d1dfa227527b4df44a07599289e207d6fd9cca60c0365682dcd3deaf739567e") output_commit_tx= "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8001c0c62d0000000000160014ccf1af2f2aabee14bb40fa3851ab2301de8431100400473044022031a82b51bd014915fe68928d1abf4b9885353fb896cac10c3fdd88d7f9c7f2e00220716bda819641d2c63e65d3549b6120112e1aeaf1742eed94a471488e79e206b101473044022064901950be922e62cbe3f2ab93de2b99f37cff9fc473e73e394b27f88ef0731d02206d1dfa227527b4df44a07599289e207d6fd9cca60c0365682dcd3deaf739567e01475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220" our_commit_tx = make_commitment( @@ -701,7 +701,7 @@ class TestLNUtil(ElectrumTestCase): # actual commitment transaction fee = 10860 # to_local amount 6989140 wscript 63210212a140cd0c6539d07cd08dfe09984dec3251ea808b892efeac3ede9402bf2b1967029000b2752103fd5960528dc152014952efdb702a88f71e3c1653b2314431701ec77e57fde83c68ac # to_remote amount 3000000 P2WPKH(0394854aa6eab5b2a8122cc726e9dded053a2184d88256816826d6231c068d4a5b) - remote_signature = "3045022100f51d2e566a70ba740fc5d8c0f07b9b93d2ed741c3c0860c613173de7d39e7968022041376d520e9c0e1ad52248ddf4b22e12be8763007df977253ef45a4ca3bdb7c0" + remote_signature = bfh("3045022100f51d2e566a70ba740fc5d8c0f07b9b93d2ed741c3c0860c613173de7d39e7968022041376d520e9c0e1ad52248ddf4b22e12be8763007df977253ef45a4ca3bdb7c0") # local_signature = 3044022051b75c73198c6deee1a875871c3961832909acd297c6b908d59e3319e5185a46022055c419379c5051a78d00dbbce11b5b664a0c22815fbcc6fcef6b1937c3836939 our_commit_tx = make_commitment( ctn=commitment_number, @@ -725,17 +725,17 @@ class TestLNUtil(ElectrumTestCase): ref_commit_tx_str = '02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8002c0c62d0000000000160014ccf1af2f2aabee14bb40fa3851ab2301de84311054a56a00000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e0400473044022051b75c73198c6deee1a875871c3961832909acd297c6b908d59e3319e5185a46022055c419379c5051a78d00dbbce11b5b664a0c22815fbcc6fcef6b1937c383693901483045022100f51d2e566a70ba740fc5d8c0f07b9b93d2ed741c3c0860c613173de7d39e7968022041376d520e9c0e1ad52248ddf4b22e12be8763007df977253ef45a4ca3bdb7c001475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220' self.assertEqual(str(our_commit_tx), ref_commit_tx_str) - def sign_and_insert_remote_sig(self, tx: PartialTransaction, remote_pubkey, remote_signature, pubkey, privkey): + def sign_and_insert_remote_sig(self, tx: PartialTransaction, remote_pubkey: bytes, remote_signature: bytes, pubkey: bytes, privkey: bytes): assert type(remote_pubkey) is bytes assert len(remote_pubkey) == 33 - assert type(remote_signature) is str + assert type(remote_signature) is bytes assert type(pubkey) is bytes assert type(privkey) is bytes assert len(pubkey) == 33 assert len(privkey) == 33 - tx.sign({pubkey.hex(): (privkey[:-1], True)}) - sighash = Sighash.to_sigbytes(Sighash.ALL).hex() - tx.add_signature_to_txin(txin_idx=0, signing_pubkey=remote_pubkey.hex(), sig=remote_signature + sighash) + tx.sign({pubkey: privkey[:-1]}) + sighash = Sighash.to_sigbytes(Sighash.ALL) + tx.add_signature_to_txin(txin_idx=0, signing_pubkey=remote_pubkey, sig=remote_signature + sighash) def test_get_compressed_pubkey_from_bech32(self): self.assertEqual(b'\x03\x84\xef\x87\xd9d\xa2\xaaa7=\xff\xb8\xfe=t8[}>;\n\x13\xa8e\x8eo:\xf5Mi\xb5H', diff --git a/tests/test_transaction.py b/tests/test_transaction.py index 50fbafeda..060d06637 100644 --- a/tests/test_transaction.py +++ b/tests/test_transaction.py @@ -18,7 +18,7 @@ signed_blob = '01000000012a5c9a94fcde98f5581cd00162c60a13936ceb75389ea65bf38633b v2_blob = "0200000001191601a44a81e061502b7bfbc6eaa1cef6d1e6af5308ef96c9342f71dbf4b9b5000000006b483045022100a6d44d0a651790a477e75334adfb8aae94d6612d01187b2c02526e340a7fd6c8022028bdf7a64a54906b13b145cd5dab21a26bd4b85d6044e9b97bceab5be44c2a9201210253e8e0254b0c95776786e40984c1aa32a7d03efa6bdacdea5f421b774917d346feffffff026b20fa04000000001976a914024db2e87dd7cfd0e5f266c5f212e21a31d805a588aca0860100000000001976a91421919b94ae5cefcdf0271191459157cdb41c4cbf88aca6240700" signed_segwit_blob = "01000000000101b66d722484f2db63e827ebf41d02684fed0c6550e85015a6c9d41ef216a8a6f00000000000fdffffff0280c3c90100000000160014b65ce60857f7e7892b983851c2a8e3526d09e4ab64bac30400000000160014c478ebbc0ab2097706a98e10db7cf101839931c4024730440220789c7d47f876638c58d98733c30ae9821c8fa82b470285dcdf6db5994210bf9f02204163418bbc44af701212ad42d884cc613f3d3d831d2d0cc886f767cca6e0235e012103083a6dc250816d771faa60737bfe78b23ad619f6b458e0a1f1688e3a0605e79c00000000" -signed_blob_signatures = ['3046022100a82bbc57a0136751e5433f41cf000b3f1a99c6744775e76ec764fb78c54ee100022100f9e80b7de89de861dc6fb0c1429d5da72c2b6b2ee2406bc9bfb1beedd729d98501',] +signed_blob_signatures = [bfh('3046022100a82bbc57a0136751e5433f41cf000b3f1a99c6744775e76ec764fb78c54ee100022100f9e80b7de89de861dc6fb0c1429d5da72c2b6b2ee2406bc9bfb1beedd729d98501'),] class TestBCDataStream(ElectrumTestCase): @@ -81,11 +81,11 @@ class TestBCDataStream(ElectrumTestCase): class TestTransaction(ElectrumTestCase): def test_match_against_script_template(self): - script = bfh(construct_script([opcodes.OP_5, bytes(29)])) + script = construct_script([opcodes.OP_5, bytes(29)]) self.assertTrue(match_script_against_template(script, SCRIPTPUBKEY_TEMPLATE_ANYSEGWIT)) - script = bfh(construct_script([opcodes.OP_NOP, bytes(30)])) + script = construct_script([opcodes.OP_NOP, bytes(30)]) self.assertFalse(match_script_against_template(script, SCRIPTPUBKEY_TEMPLATE_ANYSEGWIT)) - script = bfh(construct_script([opcodes.OP_0, bytes(50)])) + script = construct_script([opcodes.OP_0, bytes(50)]) self.assertFalse(match_script_against_template(script, SCRIPTPUBKEY_TEMPLATE_ANYSEGWIT)) def test_tx_update_signatures(self): @@ -875,9 +875,9 @@ class TestTransactionTestnet(ElectrumTestCase): prevout = TxOutpoint(txid=bfh('6d500966f9e494b38a04545f0cea35fc7b3944e341a64b804fed71cdee11d434'), out_idx=1) txin = PartialTxInput(prevout=prevout) txin.nsequence = 2 ** 32 - 3 - redeem_script = bfh(construct_script([ + redeem_script = construct_script([ locktime, opcodes.OP_CHECKLOCKTIMEVERIFY, opcodes.OP_DROP, pubkey, opcodes.OP_CHECKSIG, - ])) + ]) txin.redeem_script = redeem_script # Build the Transaction Output @@ -887,7 +887,7 @@ class TestTransactionTestnet(ElectrumTestCase): # Build and sign the transaction tx = PartialTransaction.from_io([txin], [txout], locktime=locktime, version=1) sig = tx.sign_txin(0, privkey) - txin.script_sig = bfh(construct_script([sig, redeem_script])) + txin.script_sig = construct_script([sig, redeem_script]) # note: in testnet3 chain, signature differs (no low-R grinding), # so txid there is: a8110bbdd40d65351f615897d98c33cbe33e4ebedb4ba2fc9e8c644423dadc93 @@ -903,10 +903,10 @@ class TestTransactionTestnet(ElectrumTestCase): # Build the Transaction Input _, privkey, compressed = deserialize_privkey(wif) pubkey = ECPrivkey(privkey).get_public_key_hex(compressed=compressed) - witness_script = bfh(construct_script([ + witness_script = construct_script([ locktime, opcodes.OP_CHECKLOCKTIMEVERIFY, opcodes.OP_DROP, pubkey, opcodes.OP_CHECKSIG, - ])) - from_addr = bitcoin.script_to_p2wsh(witness_script.hex()) + ]) + from_addr = bitcoin.script_to_p2wsh(witness_script) self.assertEqual("tb1q9dn6qke9924xe3zmptmhrdge0s043pjxpjndypgnu2t9fvsd4crs2qjuer", from_addr) prevout = TxOutpoint(txid=bfh('8680971efd5203025cffe746f8598d0a704fae81f236ffe009c2609ec673d59a'), out_idx=0) txin = PartialTxInput(prevout=prevout) @@ -922,7 +922,7 @@ class TestTransactionTestnet(ElectrumTestCase): # Build and sign the transaction tx = PartialTransaction.from_io([txin], [txout], locktime=locktime, version=2) sig = tx.sign_txin(0, privkey) - txin.witness = bfh(construct_witness([sig, witness_script])) + txin.witness = construct_witness([sig, witness_script]) self.assertEqual('1cdb274755b144090c7134b6459e8d4cb6b4552fe620102836d751e8389b2694', tx.txid()) @@ -949,7 +949,7 @@ class TestSighashTypes(ElectrumTestCase): tx = PartialTransaction.from_io(inputs=[self.txin], outputs=[self.txout1,self.txout2], locktime=self.locktime, version=1, BIP69_sort=False) sig = tx.sign_txin(0,privkey) self.assertEqual('304402206ac44d672dac41f9b00e28f4df20c52eeb087207e8d758d76d92c6fab3b73e2b0220367750dbbe19290069cba53d096f44530e4f98acaa594810388cf7409a1870ce01', - sig) + sig.hex()) def test_check_sighash_types_sighash_none(self): self.txin.sighash=Sighash.NONE @@ -957,7 +957,7 @@ class TestSighashTypes(ElectrumTestCase): tx = PartialTransaction.from_io(inputs=[self.txin], outputs=[self.txout1,self.txout2], locktime=self.locktime, version=1, BIP69_sort=False) sig = tx.sign_txin(0,privkey) self.assertEqual('3044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa2695578cc6432cdabce271502', - sig) + sig.hex()) def test_check_sighash_types_sighash_single(self): self.txin.sighash=Sighash.SINGLE @@ -965,7 +965,7 @@ class TestSighashTypes(ElectrumTestCase): tx = PartialTransaction.from_io(inputs=[self.txin], outputs=[self.txout1,self.txout2], locktime=self.locktime, version=1, BIP69_sort=False) sig = tx.sign_txin(0,privkey) self.assertEqual('3044022059ebf56d98010a932cf8ecfec54c48e6139ed6adb0728c09cbe1e4fa0915302e022007cd986c8fa870ff5d2b3a89139c9fe7e499259875357e20fcbb15571c76795403', - sig) + sig.hex()) @disable_ecdsa_r_value_grinding def test_check_sighash_types_sighash_all_anyonecanpay(self): @@ -974,7 +974,7 @@ class TestSighashTypes(ElectrumTestCase): tx = PartialTransaction.from_io(inputs=[self.txin], outputs=[self.txout1,self.txout2], locktime=self.locktime, version=1, BIP69_sort=False) sig = tx.sign_txin(0,privkey) self.assertEqual('3045022100fbefd94bd0a488d50b79102b5dad4ab6ced30c4069f1eaa69a4b5a763414067e02203156c6a5c9cf88f91265f5a942e96213afae16d83321c8b31bb342142a14d16381', - sig) + sig.hex()) @disable_ecdsa_r_value_grinding def test_check_sighash_types_sighash_none_anyonecanpay(self): @@ -983,7 +983,7 @@ class TestSighashTypes(ElectrumTestCase): tx = PartialTransaction.from_io(inputs=[self.txin], outputs=[self.txout1,self.txout2], locktime=self.locktime, version=1, BIP69_sort=False) sig = tx.sign_txin(0,privkey) self.assertEqual('3045022100a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a407022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e1a8a0882', - sig) + sig.hex()) def test_check_sighash_types_sighash_single_anyonecanpay(self): self.txin.sighash=Sighash.SINGLE|Sighash.ANYONECANPAY @@ -991,4 +991,4 @@ class TestSighashTypes(ElectrumTestCase): tx = PartialTransaction.from_io(inputs=[self.txin], outputs=[self.txout1,self.txout2], locktime=self.locktime, version=1, BIP69_sort=False) sig = tx.sign_txin(0,privkey) self.assertEqual('30440220525406a1482936d5a21888260dc165497a90a15669636d8edca6b9fe490d309c022032af0c646a34a44d1f4576bf6a4a74b67940f8faa84c7df9abe12a01a11e2b4783', - sig) + sig.hex())