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 083a620d6..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,105 +800,104 @@ 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_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 - script_opcodes = [ + +def witness_template_offered_htlc(anchors: bool): + return [ opcodes.OP_DUP, opcodes.OP_HASH160, - bitcoin.hash_160(revocation_pubkey), + OPPushDataGeneric(None), opcodes.OP_EQUAL, opcodes.OP_IF, opcodes.OP_CHECKSIG, opcodes.OP_ELSE, - remote_htlcpubkey, + OPPushDataGeneric(None), opcodes.OP_SWAP, opcodes.OP_SIZE, - 32, + OPPushDataGeneric(lambda x: x==1), + opcodes.OP_EQUAL, + opcodes.OP_NOTIF, + opcodes.OP_DROP, + opcodes.OP_2, + opcodes.OP_SWAP, + OPPushDataGeneric(None), + opcodes.OP_2, + opcodes.OP_CHECKMULTISIG, + opcodes.OP_ELSE, + opcodes.OP_HASH160, + OPPushDataGeneric(None), + opcodes.OP_EQUALVERIFY, + opcodes.OP_CHECKSIG, + opcodes.OP_ENDIF, + ] + ([ + opcodes.OP_1, + opcodes.OP_CHECKSEQUENCEVERIFY, + opcodes.OP_DROP, + ] if anchors else [ + ]) + [ + opcodes.OP_ENDIF, + ] + + +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: bool): + return [ + opcodes.OP_DUP, + opcodes.OP_HASH160, + OPPushDataGeneric(None), + opcodes.OP_EQUAL, + opcodes.OP_IF, + opcodes.OP_CHECKSIG, + opcodes.OP_ELSE, + OPPushDataGeneric(None), + opcodes.OP_SWAP, + opcodes.OP_SIZE, + OPPushDataGeneric(lambda x: x==1), opcodes.OP_EQUAL, opcodes.OP_IF, opcodes.OP_HASH160, - crypto.ripemd(payment_hash), + OPPushDataGeneric(None), opcodes.OP_EQUALVERIFY, - 2, + opcodes.OP_2, opcodes.OP_SWAP, - local_htlcpubkey, - 2, + OPPushDataGeneric(None), + opcodes.OP_2, opcodes.OP_CHECKMULTISIG, opcodes.OP_ELSE, opcodes.OP_DROP, - cltv_abs, + OPPushDataGeneric(None), opcodes.OP_CHECKLOCKTIMEVERIFY, opcodes.OP_DROP, opcodes.OP_CHECKSIG, opcodes.OP_ENDIF, + ] + ([ + opcodes.OP_1, + opcodes.OP_CHECKSEQUENCEVERIFY, + opcodes.OP_DROP, + ] if anchors else [ + ]) + [ + 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) - return script -WITNESS_TEMPLATE_OFFERED_HTLC = [ - opcodes.OP_DUP, - opcodes.OP_HASH160, - OPPushDataGeneric(None), - opcodes.OP_EQUAL, - opcodes.OP_IF, - opcodes.OP_CHECKSIG, - opcodes.OP_ELSE, - OPPushDataGeneric(None), - opcodes.OP_SWAP, - opcodes.OP_SIZE, - OPPushDataGeneric(lambda x: x==1), - opcodes.OP_EQUAL, - opcodes.OP_NOTIF, - opcodes.OP_DROP, - opcodes.OP_2, - opcodes.OP_SWAP, - OPPushDataGeneric(None), - opcodes.OP_2, - opcodes.OP_CHECKMULTISIG, - opcodes.OP_ELSE, - opcodes.OP_HASH160, - OPPushDataGeneric(None), - opcodes.OP_EQUALVERIFY, - opcodes.OP_CHECKSIG, - opcodes.OP_ENDIF, - opcodes.OP_ENDIF, -] - - -WITNESS_TEMPLATE_RECEIVED_HTLC = [ - opcodes.OP_DUP, - opcodes.OP_HASH160, - OPPushDataGeneric(None), - opcodes.OP_EQUAL, - opcodes.OP_IF, - opcodes.OP_CHECKSIG, - opcodes.OP_ELSE, - OPPushDataGeneric(None), - opcodes.OP_SWAP, - opcodes.OP_SIZE, - OPPushDataGeneric(lambda x: x==1), - opcodes.OP_EQUAL, - opcodes.OP_IF, - opcodes.OP_HASH160, - OPPushDataGeneric(None), - opcodes.OP_EQUALVERIFY, - opcodes.OP_2, - opcodes.OP_SWAP, - OPPushDataGeneric(None), - opcodes.OP_2, - opcodes.OP_CHECKMULTISIG, - opcodes.OP_ELSE, - opcodes.OP_DROP, - OPPushDataGeneric(None), - opcodes.OP_CHECKLOCKTIMEVERIFY, - opcodes.OP_DROP, - opcodes.OP_CHECKSIG, - opcodes.OP_ENDIF, - opcodes.OP_ENDIF, -] +WITNESS_TEMPLATE_RECEIVED_HTLC = witness_template_received_htlc(anchors=False) +WITNESS_TEMPLATE_RECEIVED_HTLC_ANCHORS = witness_template_received_htlc(anchors=True) def make_htlc_output_witness_script( 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,