From 57452d6cd5edc678f82f77da51c29154c2801719 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Wed, 12 Mar 2025 17:17:14 +0000 Subject: [PATCH] lnutil: rm duplication of htlc witness script templates --- electrum/bitcoin.py | 13 ++++-- electrum/lnutil.py | 93 ++++++++++--------------------------- electrum/submarine_swaps.py | 4 +- 3 files changed, 37 insertions(+), 73 deletions(-) diff --git a/electrum/bitcoin.py b/electrum/bitcoin.py index f716ac67b..35e17dc39 100644 --- a/electrum/bitcoin.py +++ b/electrum/bitcoin.py @@ -23,7 +23,7 @@ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from typing import Tuple, TYPE_CHECKING, Optional, Union, Sequence +from typing import Tuple, TYPE_CHECKING, Optional, Union, Sequence, Mapping, Any import enum from enum import IntEnum, Enum @@ -37,6 +37,7 @@ from .crypto import sha256d, sha256, hash_160 if TYPE_CHECKING: from .network import Network + from .transaction import OPPushDataGeneric ################################## transactions @@ -295,12 +296,18 @@ def construct_witness(items: Sequence[Union[str, int, bytes]]) -> bytes: return bytes(witness) -def construct_script(items: Sequence[Union[str, int, bytes, opcodes]], values=None) -> bytes: +def construct_script( + items: Sequence[Union[str, int, bytes, opcodes, 'OPPushDataGeneric']], + *, + values: Optional[Mapping[int, Any]] = None, # can be used to substitute into OPPushDataGeneric +) -> bytes: """Constructs bitcoin script from given items.""" + from .transaction import OPPushDataGeneric script = bytearray() values = values or {} for i, item in enumerate(items): if i in values: + assert OPPushDataGeneric.is_instance(item), f"tried to substitute into {item=!r}" item = values[i] if isinstance(item, opcodes): script += bytes([item]) @@ -312,7 +319,7 @@ def construct_script(items: Sequence[Union[str, int, bytes, opcodes]], values=No assert is_hex_str(item) script += push_script(bfh(item)) else: - raise Exception(f'unexpected item for script: {item!r}') + raise Exception(f'unexpected item for script: {item!r} at idx={i}') return bytes(script) diff --git a/electrum/lnutil.py b/electrum/lnutil.py index 538271233..2a760b278 100644 --- a/electrum/lnutil.py +++ b/electrum/lnutil.py @@ -774,37 +774,17 @@ def make_offered_htlc( assert type(remote_htlcpubkey) is bytes assert type(local_htlcpubkey) is bytes assert type(payment_hash) is bytes - script_opcodes = [ - opcodes.OP_DUP, - opcodes.OP_HASH160, - bitcoin.hash_160(revocation_pubkey), - opcodes.OP_EQUAL, - opcodes.OP_IF, - opcodes.OP_CHECKSIG, - opcodes.OP_ELSE, - remote_htlcpubkey, - opcodes.OP_SWAP, - opcodes.OP_SIZE, - 32, - opcodes.OP_EQUAL, - opcodes.OP_NOTIF, - opcodes.OP_DROP, - 2, - opcodes.OP_SWAP, - local_htlcpubkey, - 2, - opcodes.OP_CHECKMULTISIG, - opcodes.OP_ELSE, - opcodes.OP_HASH160, - crypto.ripemd(payment_hash), - opcodes.OP_EQUALVERIFY, - opcodes.OP_CHECKSIG, - opcodes.OP_ENDIF, - ] - if has_anchors: - script_opcodes.extend([1, opcodes.OP_CHECKSEQUENCEVERIFY, opcodes.OP_DROP]) - script_opcodes.append(opcodes.OP_ENDIF) - script = construct_script(script_opcodes) + script_template = witness_template_offered_htlc(anchors=has_anchors) + script = construct_script( + script_template, + values={ + 2: bitcoin.hash_160(revocation_pubkey), + 7: remote_htlcpubkey, + 10: 32, + 16: local_htlcpubkey, + 21: crypto.ripemd(payment_hash), + }, + ) return script @@ -820,45 +800,22 @@ def make_received_htlc( for i in [revocation_pubkey, remote_htlcpubkey, local_htlcpubkey, payment_hash]: assert type(i) is bytes assert type(cltv_abs) is int - - script_opcodes = [ - opcodes.OP_DUP, - opcodes.OP_HASH160, - bitcoin.hash_160(revocation_pubkey), - opcodes.OP_EQUAL, - opcodes.OP_IF, - opcodes.OP_CHECKSIG, - opcodes.OP_ELSE, - remote_htlcpubkey, - opcodes.OP_SWAP, - opcodes.OP_SIZE, - 32, - opcodes.OP_EQUAL, - opcodes.OP_IF, - opcodes.OP_HASH160, - crypto.ripemd(payment_hash), - opcodes.OP_EQUALVERIFY, - 2, - opcodes.OP_SWAP, - local_htlcpubkey, - 2, - opcodes.OP_CHECKMULTISIG, - opcodes.OP_ELSE, - opcodes.OP_DROP, - cltv_abs, - opcodes.OP_CHECKLOCKTIMEVERIFY, - opcodes.OP_DROP, - opcodes.OP_CHECKSIG, - opcodes.OP_ENDIF, - ] - if has_anchors: - script_opcodes.extend([1, opcodes.OP_CHECKSEQUENCEVERIFY, opcodes.OP_DROP]) - script_opcodes.append(opcodes.OP_ENDIF) - script = construct_script(script_opcodes) + script_template = witness_template_received_htlc(anchors=has_anchors) + script = construct_script( + script_template, + values={ + 2: bitcoin.hash_160(revocation_pubkey), + 7: remote_htlcpubkey, + 10: 32, + 14: crypto.ripemd(payment_hash), + 18: local_htlcpubkey, + 23: cltv_abs, + }, + ) return script -def witness_template_offered_htlc(anchors): +def witness_template_offered_htlc(anchors: bool): return [ opcodes.OP_DUP, opcodes.OP_HASH160, @@ -899,7 +856,7 @@ WITNESS_TEMPLATE_OFFERED_HTLC = witness_template_offered_htlc(anchors=False) WITNESS_TEMPLATE_OFFERED_HTLC_ANCHORS = witness_template_offered_htlc(anchors=True) -def witness_template_received_htlc(anchors): +def witness_template_received_htlc(anchors: bool): return [ opcodes.OP_DUP, opcodes.OP_HASH160, diff --git a/electrum/submarine_swaps.py b/electrum/submarine_swaps.py index 7392103d6..fab40f8c0 100644 --- a/electrum/submarine_swaps.py +++ b/electrum/submarine_swaps.py @@ -516,7 +516,7 @@ class SwapManager(Logger): onchain_amount_sat = self._get_recv_amount(lightning_amount_sat, is_reverse=True) # what the client is going to receive redeem_script = construct_script( WITNESS_TEMPLATE_REVERSE_SWAP, - {1:32, 5:ripemd(payment_hash), 7:their_pubkey, 10:locktime, 13:our_pubkey} + values={1:32, 5:ripemd(payment_hash), 7:their_pubkey, 10:locktime, 13:our_pubkey} ) swap, invoice, prepay_invoice = self.add_normal_swap( redeem_script=redeem_script, @@ -611,7 +611,7 @@ class SwapManager(Logger): payment_hash = sha256(preimage) redeem_script = construct_script( WITNESS_TEMPLATE_REVERSE_SWAP, - {1:32, 5:ripemd(payment_hash), 7:our_pubkey, 10:locktime, 13:their_pubkey} + values={1:32, 5:ripemd(payment_hash), 7:our_pubkey, 10:locktime, 13:their_pubkey} ) swap = self.add_reverse_swap( redeem_script=redeem_script,