Browse Source

bitcoin.py/transaction.py: API changes: rm most hex usage

Instead of some functions operating with hex strings,
and others using bytes, this consolidates most things to use bytes.

This mainly focuses on bitcoin.py and transaction.py,
and then adapts the API usages in other files.

Notably,
- scripts,
- pubkeys,
- signatures
should be bytes in almost all places now.
master
SomberNight 2 years ago
parent
commit
2f1095510c
No known key found for this signature in database
GPG Key ID: B33B5F232C6271E9
  1. 4
      electrum/address_synchronizer.py
  2. 6
      electrum/bip32.py
  3. 145
      electrum/bitcoin.py
  4. 35
      electrum/blockchain.py
  5. 4
      electrum/commands.py
  6. 2
      electrum/constants.py
  7. 30
      electrum/descriptor.py
  8. 2
      electrum/ecc.py
  9. 2
      electrum/gui/text.py
  10. 32
      electrum/lnchannel.py
  11. 20
      electrum/lnpeer.py
  12. 28
      electrum/lnsweep.py
  13. 40
      electrum/lnutil.py
  14. 2
      electrum/lnworker.py
  15. 12
      electrum/payment_identifier.py
  16. 4
      electrum/paymentrequest.py
  17. 4
      electrum/plugins/bitbox02/bitbox02.py
  18. 2
      electrum/plugins/coldcard/coldcard.py
  19. 6
      electrum/plugins/digitalbitbox/digitalbitbox.py
  20. 9
      electrum/plugins/jade/jade.py
  21. 4
      electrum/plugins/keepkey/keepkey.py
  22. 33
      electrum/plugins/ledger/ledger.py
  23. 4
      electrum/plugins/safe_t/safe_t.py
  24. 4
      electrum/plugins/trezor/trezor.py
  25. 2
      electrum/scripts/peers.py
  26. 27
      electrum/submarine_swaps.py
  27. 195
      electrum/transaction.py
  28. 16
      electrum/wallet.py
  29. 4
      electrum/wallet_db.py
  30. 152
      tests/test_bitcoin.py
  31. 2
      tests/test_lnpeer.py
  32. 16
      tests/test_lnutil.py
  33. 34
      tests/test_transaction.py

4
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)

6
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.

145
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:

35
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

4
electrum/commands.py

@ -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):

2
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):

30
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:

2
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

2
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,

32
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
@ -1618,8 +1618,8 @@ class Channel(AbstractChannel):
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

20
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)

28
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
@ -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

40
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,13 +1072,13 @@ 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())
@ -1087,10 +1089,10 @@ def sign_and_get_sig_string(tx: PartialTransaction, local_config, remote_config)
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)

2
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,

12
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()

4
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:

4
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:

2
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)

6
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:

9
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()

4
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

33
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:

4
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

4
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

2
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)

27
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(

195
electrum/transaction.py

@ -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,28 +2104,29 @@ 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
@ -2141,20 +2143,26 @@ class PartialTransaction(Transaction):
_logger.info(f"adding signature for {pubkey}. spending utxo {txin.prevout.to_str()}")
sec, compressed = 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)
self.add_signature_to_txin(txin_idx=i, signing_pubkey=bfh(pubkey), sig=sig)
_logger.debug(f"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 +2197,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 +2209,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

16
electrum/wallet.py

@ -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)
@ -182,7 +182,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 +191,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 +1297,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 +2266,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 +3804,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):

4
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

152
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):

2
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

16
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,
@ -728,14 +728,14 @@ class TestLNUtil(ElectrumTestCase):
def sign_and_insert_remote_sig(self, tx: PartialTransaction, remote_pubkey, remote_signature, pubkey, privkey):
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)
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',

34
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())

Loading…
Cancel
Save