feat(core): implement signing for OutputScriptType.PAYTOLNSWAP

pull/2136/head
Pavol Rusnak 2 years ago
parent 561169a557
commit 2fd2ffc419
No known key found for this signature in database
GPG Key ID: 91F3B339B9A02A3D

@ -0,0 +1 @@
Support for Lightning Submarine Swaps

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

@ -88,6 +88,7 @@ SEGWIT_OUTPUT_SCRIPT_TYPES = (
OutputScriptType.PAYTOP2SHWITNESS,
OutputScriptType.PAYTOWITNESS,
OutputScriptType.PAYTOTAPROOT,
OutputScriptType.PAYTOLNSWAP,
)
NONSEGWIT_INPUT_SCRIPT_TYPES = (

@ -2,7 +2,8 @@ from typing import TYPE_CHECKING
from trezor import utils, wire
from trezor.crypto import base58, cashaddr
from trezor.crypto.hashlib import sha256
from trezor.crypto.hashlib import ripemd160, sha256
from trezor.crypto.scripts import sha256_ripemd160
from trezor.enums import InputScriptType
from apps.common import address_type
@ -594,6 +595,45 @@ def output_script_paytoopreturn(data: bytes) -> bytearray:
return w
# LNSWAP
# ===
def redeem_script_paytolnswap(
payment_hash: bytes,
htlc: bytes,
cltv: int,
refund_pubkey: bytes,
) -> bytearray:
# create redeemscript
w = utils.empty_bytearray(3 + 20 + 4 + 33 + 2 + 3 + 5 + 20 + 3)
w.append(0x76) # OP_DUP
w.append(0xA9) # OP_HASH160
w.append(0x14) # OP_DATA_20
write_bytes_fixed(w, ripemd160(payment_hash).digest(), 20)
w.append(0x87) # OP_EQUAL
w.append(0x63) # OP_IF
w.append(0x75) # OP_DROP
w.append(0x21) # OP_DATA_33
write_bytes_fixed(w, htlc, 33)
w.append(0x67) # OP_ELSE
w.append(0x03) # OP_DATA_3
if not 500_000 <= cltv <= 0xFF_FFFF:
raise wire.DataError("Invalid CLTV value")
write_bytes_fixed(w, cltv.to_bytes(3, "little"), 3)
w.append(0xB1) # OP_CHECKLOCKTIMEVERIFY
w.append(0x75) # OP_DROP
w.append(0x76) # OP_DUP
w.append(0xA9) # OP_HASH160
w.append(0x14) # OP_DATA_20
# we support only SPENDADDRESS (P2PKH) and SPENDWITNESS (P2WPKH) for refunds
write_bytes_fixed(w, sha256_ripemd160(refund_pubkey).digest(), 20)
w.append(0x88) # OP_EQUALVERIFY
w.append(0x68) # OP_ENDIF
w.append(0xAC) # OP_CHECKSIG
return w
# BIP-322: SignatureProof container for scriptSig & witness
# ===
# https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki

@ -2,7 +2,9 @@ from micropython import const
from typing import TYPE_CHECKING
from trezor import wire
from trezor.crypto.bolt11 import bolt11_decode
from trezor.crypto.hashlib import sha256
from trezor.crypto.scripts import sha256_ripemd160
from trezor.enums import InputScriptType, OutputScriptType
from trezor.messages import TxRequest, TxRequestDetailsType, TxRequestSerializedType
from trezor.utils import HashWriter, empty_bytearray, ensure
@ -18,6 +20,7 @@ from ..common import (
input_is_external_unverified,
input_is_segwit,
)
from ..keychain import validate_path_against_script_type
from ..ownership import verify_nonownership
from ..verification import SignatureVerifier
from . import approvers, helpers, progress
@ -870,6 +873,55 @@ class Bitcoin:
assert txo.op_return_data is not None # checked in sanitize_tx_output
return scripts.output_script_paytoopreturn(txo.op_return_data)
if txo.script_type == OutputScriptType.PAYTOLNSWAP:
assert txo.lnswap is not None # checked in sanitize_tx_output
assert txo.address is not None # checked in sanitize_tx_output
if txo.lnswap.swap_script_type not in [
InputScriptType.SPENDP2SHWITNESS,
InputScriptType.SPENDWITNESS,
]:
raise wire.DataError("Invalid swap script type")
if txo.lnswap.refund_script_type not in [
InputScriptType.SPENDADDRESS,
InputScriptType.SPENDWITNESS,
]:
raise wire.DataError("Invalid refund script type")
if not validate_path_against_script_type(
self.coin,
address_n=txo.lnswap.refund_address_n,
script_type=txo.lnswap.refund_script_type,
):
raise wire.DataError("Invalid refund path")
invoice = bolt11_decode(txo.lnswap.invoice)
if invoice.network != self.coin.bech32_prefix:
raise wire.DataError("Invalid invoice network")
if invoice.amount is None:
raise wire.DataError("Invalid invoice amount")
if invoice.payment_hash is None:
raise wire.DataError("Invalid invoice payment hash")
refund_node = self.keychain.derive(txo.lnswap.refund_address_n)
redeem_script = scripts.redeem_script_paytolnswap(
invoice.payment_hash,
txo.lnswap.htlc,
txo.lnswap.cltv,
refund_node.public_key(),
)
redeem_script_hash = sha256(redeem_script).digest()
s = scripts.output_script_native_segwit(0, redeem_script_hash)
if txo.lnswap.swap_script_type == InputScriptType.SPENDWITNESS:
assert self.coin.bech32_prefix is not None
address = addresses.address_p2wsh(
redeem_script_hash, self.coin.bech32_prefix
)
else: # InputScriptType.SPENDP2SHWITNESS
# wrap p2wsh into p2sh if requested
scripthash = sha256_ripemd160(s).digest()
s = scripts.output_script_p2sh(scripthash)
address = addresses.address_p2sh(scripthash, self.coin)
if txo.address != address:
raise wire.DataError("Swap address mismatch")
return s
if txo.address_n:
# change output
try:

@ -485,6 +485,14 @@ def sanitize_tx_output(txo: TxOutput, coin: CoinInfo) -> TxOutput:
if txo.script_type == OutputScriptType.PAYTOTAPROOT and not coin.taproot:
raise wire.DataError("Taproot not enabled on this coin")
if txo.script_type == OutputScriptType.PAYTOLNSWAP:
if txo.lnswap is None:
raise wire.DataError("Lightning Swap output without lnswap")
if not txo.address:
raise wire.DataError("Missing swap address")
if not txo.lnswap.refund_address_n:
raise wire.DataError("Missing refund_address_n")
if txo.script_type == OutputScriptType.PAYTOOPRETURN:
# op_return output
if txo.op_return_data is None:

@ -3,6 +3,7 @@ from typing import TYPE_CHECKING
from ubinascii import hexlify
from trezor import ui, utils, wire
from trezor.crypto.bolt11 import bolt11_decode
from trezor.enums import AmountUnit, ButtonRequestType, OutputScriptType
from trezor.strings import format_amount, format_timestamp
from trezor.ui import layouts
@ -64,6 +65,20 @@ async def confirm_output(
data=data,
br_code=ButtonRequestType.ConfirmOutput,
)
elif output.script_type == OutputScriptType.PAYTOLNSWAP:
assert output.lnswap is not None
assert output.address is not None
invoice = bolt11_decode(output.lnswap.invoice)
if invoice.amount is None:
raise wire.DataError("Invalid invoice amount")
layout = layouts.confirm_lightning_swap(
ctx,
output.address,
format_coin_amount(output.amount, coin, amount_unit),
payee=hexlify(invoice.payee).decode(),
ln_amount=format_amount(invoice.amount, 3) + " sat",
description=invoice.description,
)
else:
assert output.address is not None
address_short = addresses.address_short(coin, output.address)

@ -14,6 +14,8 @@ def load_resource(name: str) -> bytes:
return b'TOIg\x10\x00\x10\x00M\x00\x00\x00\x13`\x00\x03F\x08\xc5\x02\xa1\x18\xd6\x17\x80)\x8e\xff?\xc1\xf4\xfc\xff\xbf!\xdc\xff\x13\x18\x02A\xdc\x9f\x0c\xac\x7f\x18\xd8A\xdc\xfe\xff\x1b\xea\xff\xffb`\xd8\xff\xff\xdf\xff\xff\x0b\xc0j@\\\x90"\x10\x17$\xf0\x0bb\xe6\xfc\x05\x10\x9a\x15L\n@\xed\x05\x00'
if name == "trezor/res/header_icons/cog.toif":
return b'TOIg\x18\x00\x18\x00\x8c\x00\x00\x00\x13```\xe0\xf9\xf7\xfe\x02\x90b\x04b\x06\xf9\xff\xff?2@\x81\xfd\xff\xff\x9f\xc1\x0c\x16\x06\x86\xfc\xff\xff\xbf308\x02\x19\xcf\xb4\xfe\x03\xc1\xa2\xbc\xef\x0c\x8c\xff\xe1@\x80\x13\xc1\x9e\xe0\x0f"\x7f\xef\x07\x91_\xfa\x81\xc4O\x06\x86\xf9@\xea\x07\x07H\x92\x81\x01D50\x00\x15\t0\x80\x8c\xf8\xc2\xc0\xc0\xf7\xff\x0f\xc8\x9a\xf3\xff\x1f\xa0\xb2\x91\xd4\xb0\x835A\xf4"\x9b\x89l\x17\xb2\x1b\x90\xdd\x06s\xf3&\xa0\x9b\x11~q@\xf5#\x92\xdf\x81v2\xf0B\xc3\x04\x00'
if name == "trezor/res/header_icons/lnswap.toif":
return b'TOIg\x18\x00\x18\x00o\x00\x00\x00c`\x00\x03v\x06\x04\xa8F0Y\xbf"\xd8\xb2\x17\x10\xec\xf3\x13\xe0L\xae\xff\x06pv\xfd\x1f\x84\xce\xff\xdf\xe1l\xde\xff\x0f\x10:\xffo\x8019\xff\xff\x7f\xf7\xee-T\xe7\x7f \xf8\x07a\xbf\x07\xb1\xa1\xba\xef\xbd{\xf7\xff?\xdc2\xee\xff\x08\x0b\xf8\xff#\xdc\xd3\xff\xdf\x01\xce\xbe\xff\r\xced\xfe\x8f\xe4\xcc\xdf\x08\xd7\xf3!\xf9$\x1f\xa1\x93a;R 4@i\x00'
if name == "trezor/res/header_icons/nocopy.toif":
return b'TOIg\x12\x00\x12\x00\x87\x00\x00\x00;\xa7\xc0\x00\x06\x8c\xef\x1f10\xb8\xb7\x02Y\xba\xff\x04\x18\xfe\xffO```\xf9\xff\x88!\xf2\xfc\xd7P\x01\xbf\xff\xff\x14\x18x\xff\xff\xff\xf2\xfe\x14P\x88i\xf5\xde\x7f\xff\x15t\xff\x0352\xfd\xff\xaf\xc0\xf2\xff!\x84\xc1\xf5\x1f\xa8\x11\xc4\xe0\xff\xba\xff\x11\x033\x90\xc1\xf7\x99\xe7\xbf\x02H\x84\xfd\x7f\xd5\xff\x8f\x0cm@-\xeb\xff\x83T\x01\x19,\xed\x86\xe7\x1f1\xfc\xdf9\xb3\xbd\xbc|\xff\x7f\x85\xfc\xff\x10\xf0\x89\xb9\x1c\x0c\xfa\x81\xaa\xc0\x80\xe9\r\x00'
if name == "trezor/res/header_icons/receive.toif":

@ -62,6 +62,7 @@ __all__ = (
"show_warning",
"confirm_output",
"confirm_payment_request",
"confirm_lightning_swap",
"confirm_blob",
"confirm_properties",
"confirm_total",
@ -544,6 +545,34 @@ async def confirm_payment_request(
)
async def confirm_lightning_swap(
ctx: wire.GenericContext,
address: str,
amount: str,
payee: str,
ln_amount: str,
description: str | None,
) -> None:
width_paginated = MONO_ADDR_PER_LINE - 1
para = [(ui.BOLD, amount)]
para.extend((ui.MONO, line) for line in chunks(address, width_paginated))
para.append(PAGEBREAK)
para.append((ui.NORMAL, "LN payee:"))
para.extend((ui.MONO, line) for line in chunks(payee, width_paginated))
para.append((ui.NORMAL, "LN amount:"))
para.append((ui.BOLD, ln_amount))
if description is not None:
para.append((ui.NORMAL, "LN description:"))
para.append((ui.BOLD, description))
content = paginate_paragraphs(
para, header="Confirm LN Swap", header_icon=ui.ICON_LNSWAP, icon_color=ui.YELLOW
)
await raise_if_cancelled(
interact(ctx, content, "confirm_output", ButtonRequestType.ConfirmOutput)
)
async def should_show_more(
ctx: wire.GenericContext,
title: str,

@ -56,6 +56,7 @@ ICON_WRONG = "trezor/res/header_icons/wrong.toif"
ICON_CONFIG = "trezor/res/header_icons/cog.toif"
ICON_RECEIVE = "trezor/res/header_icons/receive.toif"
ICON_SEND = "trezor/res/header_icons/send.toif"
ICON_LNSWAP = "trezor/res/header_icons/lnswap.toif"
ICON_DEFAULT = ICON_CONFIG

@ -0,0 +1,51 @@
from common import *
from apps.bitcoin.scripts import output_script_native_segwit, output_script_p2sh, redeem_script_paytolnswap
from trezor.crypto.hashlib import sha256
from trezor.crypto.scripts import sha256_ripemd160
class TestScripts(unittest.TestCase):
def test_redeem_script_paytolnswap(self):
VECTORS = [
(
unhexlify("53ada8e6de01c26ff43040887ba7b22bddce19f8658fd1ba00716ed79d15cd5e"),
unhexlify("03f8109578aae1e5cfc497e466cf6ae6625497cd31886e87b2f4f54f3f0f46b539"),
515924,
unhexlify("03ec0c1e45b709d708cd376a6f2daf19ac27be229647780d592e27d7fb7efb207a"),
unhexlify("76a914e2ac8cb97af3d59b1c057db4b0c4f9aa12a912738763752103f8109578aae1e5cfc497e466cf6ae6625497cd31886e87b2f4f54f3f0f46b539670354df07b17576a914ce99030daa71c4dfb155de212e475284d7a2cedb8868ac"),
unhexlify("0020727f212ed5f7c03cc0b9674ca383935ac9215006072db397aa305e11f43d7240"),
unhexlify("a914dd0d74da9b0b8b2d899c9c4f3e5ad2b2336f002987"),
),
(
unhexlify("0f817af9d08abe80e2d4980903ec329deaedb81ecfccada6a5cd6794e2a5a3a2"),
unhexlify("027f1d103826584b3c216d6a62600d8b6c7e2de62ab059d38dfd6427f3e3d7deca"),
725440,
unhexlify("0396070f2813933502e907c011ae7ba928683a9c2f0e888dae7ebd2c41120ee6b5"),
unhexlify("76a91426282c115718ef107f305272fb7d7723bacfd46a87637521027f1d103826584b3c216d6a62600d8b6c7e2de62ab059d38dfd6427f3e3d7deca6703c0110bb17576a914ece6935b2a5a5b5ff997c87370b16fa10f1644108868ac"),
unhexlify("00200600ace6f612cb757b0cfacaf5cfbdf36ea59b2b9350e7de958e1f98ff06621b"),
unhexlify("a91482e95b5ab909b8d560bdfb4dc2d70fe75aa48b3787"),
),
(
unhexlify("72d29ccfd52ca94b69e14fc8efdf41ad845ece4bac1ee04627a8f9c83a49de8c"),
unhexlify("022e05052f864845f67640651f5463e4a2921f07a46e24e79dd8bd0a4fdc896208"),
725457,
unhexlify("0396070f2813933502e907c011ae7ba928683a9c2f0e888dae7ebd2c41120ee6b5"),
unhexlify("76a91403d7eba1e13f81e87221be9392d7afdefdcb8ed987637521022e05052f864845f67640651f5463e4a2921f07a46e24e79dd8bd0a4fdc8962086703d1110bb17576a914ece6935b2a5a5b5ff997c87370b16fa10f1644108868ac"),
unhexlify("00200c2965d3a25f0207a013da7dfa208989f493e1f3ef45c920dcdc85d1af837e8a"),
unhexlify("a9142b503fe62b792866933ddeaa087dac5abbc1fcf987"),
),
]
for payment_hash, destination, ctlv, refund_pubkey, redeem_script, script_witness, script_p2sh_witness in VECTORS:
s = redeem_script_paytolnswap(payment_hash, destination, ctlv, refund_pubkey)
self.assertEqual(s, redeem_script)
h = sha256(s).digest()
w = output_script_native_segwit(0, h)
self.assertEqual(w, script_witness)
h = sha256_ripemd160(w).digest()
p = output_script_p2sh(h)
self.assertEqual(p, script_p2sh_witness)
if __name__ == '__main__':
unittest.main()

@ -0,0 +1,125 @@
# This file is part of the Trezor project.
#
# Copyright (C) 2022 SatoshiLabs and contributors
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3
# as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
from trezorlib import bech32, btc, messages
from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.tools import hash_160_to_bc_address, parse_path
from ...tx_cache import TxCache
TX_CACHE_MAINNET = TxCache("Bitcoin")
TXHASH_0dac36 = bytes.fromhex(
"0dac366fd8a67b2a89fbb0d31086e7acded7a5bbf9ef9daa935bc873229ef5b5"
)
VECTORS = [
(
messages.LightningNetworkSwapType(
invoice="lnbc538340n1p3q7l23pp57vysk3kakcfynz09du0zy87zy7n4gc6czqettc7c5v9v2fsrs9nqdpa2pskjepqw3hjq3r0deshgefqw3hjqjzjgcs8vv3qyq5y7unyv4ezqj2y8gszjxqy9ghlcqpjsp50lm6njtrm9qlyaac8252x4s4l3eu0aryx7zjjw4zrq6hgpk2evwqrzjqtesdx359t3gswn09838tur09zjk5m4zutvk7kyg5vnxg3xu74ptvzhchqqq3kgqqyqqqqqqqqqqqqgq9q9qyyssqlg84727az93gg8n37gv994w3r6dj0u8dk55qjlstappyuehq3mqjcafkyj2a39xp0w34mnzy04hzqnct7fecd380wfa0kc0en8v006sqxqchh5",
htlc=bytes.fromhex(
"02528a57b9b55e86f08562b2e49fe52649924600f865e14a41defac5cdc8d4a3ea"
),
cltv=725891,
swap_script_type=messages.InputScriptType.SPENDP2SHWITNESS,
# bc1qannfxke2tfd4l7vhepehpvt05y83v3qsf6nfkk
refund_address_n=parse_path("m/84h/0h/0h/0/0"),
refund_script_type=messages.InputScriptType.SPENDWITNESS,
),
"3NtRY64WfQk2vZy8GUhwQzzMmyY9ZhNEVi",
"0100000001b5f59e2273c85b93aa9deff9bba5d7deace78610d3b0fb892a7ba6d86f36ac0d000000006b483045022100b67aeab2efbd533606b6b808d7a990ca04ec73f84947d086ef954f36bb16425e02201578ce2f893f8c63812fc8438165044d68c4a2abca40f2f4e7ecd36f739bedf6012103d7f3a07085bee09697cf03125d5c8760dfed65403dba787f1d1d8b1251af2cbeffffffff0160ea00000000000017a914e882ed93fcb8ba83b088a04ff4180035671ef39d8700000000",
),
(
messages.LightningNetworkSwapType(
invoice="lnbc538340n1p3q7l23pp57vysk3kakcfynz09du0zy87zy7n4gc6czqettc7c5v9v2fsrs9nqdpa2pskjepqw3hjq3r0deshgefqw3hjqjzjgcs8vv3qyq5y7unyv4ezqj2y8gszjxqy9ghlcqpjsp50lm6njtrm9qlyaac8252x4s4l3eu0aryx7zjjw4zrq6hgpk2evwqrzjqtesdx359t3gswn09838tur09zjk5m4zutvk7kyg5vnxg3xu74ptvzhchqqq3kgqqyqqqqqqqqqqqqgq9q9qyyssqlg84727az93gg8n37gv994w3r6dj0u8dk55qjlstappyuehq3mqjcafkyj2a39xp0w34mnzy04hzqnct7fecd380wfa0kc0en8v006sqxqchh5",
htlc=bytes.fromhex(
"02528a57b9b55e86f08562b2e49fe52649924600f865e14a41defac5cdc8d4a3ea"
),
cltv=725891,
swap_script_type=messages.InputScriptType.SPENDWITNESS,
# bc1qannfxke2tfd4l7vhepehpvt05y83v3qsf6nfkk
refund_address_n=parse_path("m/84h/0h/0h/0/0"),
refund_script_type=messages.InputScriptType.SPENDWITNESS,
),
"bc1qx40dk7j6nkk0m8ffy5u93uujvf2jtqqqd9905l8t7fjtwsggu0rs0vmpd2",
"0100000001b5f59e2273c85b93aa9deff9bba5d7deace78610d3b0fb892a7ba6d86f36ac0d000000006a47304402200828f923298a73dde0a8e385e078964bd97063b89d0d216b767fff94fecc301202200805908bcf4147f53e7b4e5a54de294a24874a4331ae141705c6ea203cb7429f012103d7f3a07085bee09697cf03125d5c8760dfed65403dba787f1d1d8b1251af2cbeffffffff0160ea000000000000220020355edb7a5a9dacfd9d29253858f3926255258000694afa7cebf264b74108e3c700000000",
),
(
messages.LightningNetworkSwapType(
invoice="lnbc538340n1p3q7l23pp57vysk3kakcfynz09du0zy87zy7n4gc6czqettc7c5v9v2fsrs9nqdpa2pskjepqw3hjq3r0deshgefqw3hjqjzjgcs8vv3qyq5y7unyv4ezqj2y8gszjxqy9ghlcqpjsp50lm6njtrm9qlyaac8252x4s4l3eu0aryx7zjjw4zrq6hgpk2evwqrzjqtesdx359t3gswn09838tur09zjk5m4zutvk7kyg5vnxg3xu74ptvzhchqqq3kgqqyqqqqqqqqqqqqgq9q9qyyssqlg84727az93gg8n37gv994w3r6dj0u8dk55qjlstappyuehq3mqjcafkyj2a39xp0w34mnzy04hzqnct7fecd380wfa0kc0en8v006sqxqchh5",
htlc=bytes.fromhex(
"03bf9df301a514a83c0ccfea0e62951b19897fbb98f1612d56588edf1edc1bdf2a"
),
cltv=725893,
swap_script_type=messages.InputScriptType.SPENDP2SHWITNESS,
# 1JAd7XCBzGudGpJQSDSfpmJhiygtLQWaGL
refund_address_n=parse_path("m/44h/0h/0h/0/0"),
refund_script_type=messages.InputScriptType.SPENDADDRESS,
),
"31jjHe38fsZ4HHuBbCbqpbrhsD3TxCCY4D",
"0100000001b5f59e2273c85b93aa9deff9bba5d7deace78610d3b0fb892a7ba6d86f36ac0d000000006a47304402206c601351f1a21306ec4328da857cb7edbbe8c7b32e2ded19ad2888be3e010f7802200277a031944415a2f4d43b9dfb3f2aa7ee0f68603df91fc5356db8caa2557933012103d7f3a07085bee09697cf03125d5c8760dfed65403dba787f1d1d8b1251af2cbeffffffff0160ea00000000000017a91400835c51593132ba72c4e25b14fac7b412e6f2838700000000",
),
(
messages.LightningNetworkSwapType(
invoice="lnbc538340n1p3q7l23pp57vysk3kakcfynz09du0zy87zy7n4gc6czqettc7c5v9v2fsrs9nqdpa2pskjepqw3hjq3r0deshgefqw3hjqjzjgcs8vv3qyq5y7unyv4ezqj2y8gszjxqy9ghlcqpjsp50lm6njtrm9qlyaac8252x4s4l3eu0aryx7zjjw4zrq6hgpk2evwqrzjqtesdx359t3gswn09838tur09zjk5m4zutvk7kyg5vnxg3xu74ptvzhchqqq3kgqqyqqqqqqqqqqqqgq9q9qyyssqlg84727az93gg8n37gv994w3r6dj0u8dk55qjlstappyuehq3mqjcafkyj2a39xp0w34mnzy04hzqnct7fecd380wfa0kc0en8v006sqxqchh5",
htlc=bytes.fromhex(
"03bf9df301a514a83c0ccfea0e62951b19897fbb98f1612d56588edf1edc1bdf2a"
),
cltv=725893,
swap_script_type=messages.InputScriptType.SPENDWITNESS,
# 1JAd7XCBzGudGpJQSDSfpmJhiygtLQWaGL
refund_address_n=parse_path("m/44h/0h/0h/0/0"),
refund_script_type=messages.InputScriptType.SPENDADDRESS,
),
"bc1qagpvv4x8h5g0dr3v0fuvv43hqshchm8pa0fuhujkyc4sxz85fu8ssc08kc",
"0100000001b5f59e2273c85b93aa9deff9bba5d7deace78610d3b0fb892a7ba6d86f36ac0d000000006a47304402200bb063f6d0c56fffdca77555bc63598c9ddbcbf80e9b5ba112648cfd62b681bb0220446ffc34429a62f675432cc037b138eeb8c8c518760b2f4618eac41caafe4f8f012103d7f3a07085bee09697cf03125d5c8760dfed65403dba787f1d1d8b1251af2cbeffffffff0160ea000000000000220020ea02c654c7bd10f68e2c7a78c65637042f8bece1ebd3cbf256262b0308f44f0f00000000",
),
]
def test_lnswap(client: Client):
# input tx: 0dac366fd8a67b2a89fbb0d31086e7acded7a5bbf9ef9daa935bc873229ef5b5
# address = 1H2CRJBrDMhkvCGZMW7T4oQwYbL8eVuh7p
inp1 = messages.TxInputType(
address_n=parse_path("m/44h/0h/5h/0/9"),
amount=63_988,
prev_hash=TXHASH_0dac36,
prev_index=0,
)
for lnswap, swap_address, tx in VECTORS:
out1 = messages.TxOutputType(
script_type=messages.OutputScriptType.PAYTOLNSWAP,
lnswap=lnswap,
amount=60_000,
address=swap_address,
)
with client:
_, serialized_tx = btc.sign_tx(
client, "Bitcoin", [inp1], [out1], prev_txes=TX_CACHE_MAINNET
)
assert serialized_tx.hex() == tx
if lnswap.swap_script_type == messages.InputScriptType.SPENDWITNESS:
output_script = bytes.fromhex(tx[-76:-8])
output_address = bech32.encode("bc", output_script[0], output_script[2:])
else: # messages.InputScriptType.SPENDP2SHWITNESS
output_script = tx[-54:-8]
output_address = hash_160_to_bc_address(
bytes.fromhex(output_script[4:-2]), 5
)
assert swap_address == output_address

@ -912,6 +912,7 @@
"TT_bitcoin-test_signtx_invalid_path.py::test_invalid_path_fail_asap": "1c100ce4b7c1e47e72428f390de0846c1ff933e9f07894872644a369a9422738",
"TT_bitcoin-test_signtx_invalid_path.py::test_invalid_path_pass_forkid": "13168328e981bb88ebb3cc3739c594baa13992f75829e6c28b3c39066301d271",
"TT_bitcoin-test_signtx_invalid_path.py::test_invalid_path_prompt": "fffb490aa6246315b9ae407e426f6563b051219bda8f025498f178af57881635",
"TT_bitcoin-test_signtx_lnswap.py::test_lnswap": "4f221a3ee371deb7d05fc1aaf8cf367debf000d1d43fe72da42eb44425341231",
"TT_bitcoin-test_signtx_mixed_inputs.py::test_non_segwit_segwit_inputs": "c3b55df982a921a4fdb72ffb579a9b43543537b6c520b6742421f67de2f4845f",
"TT_bitcoin-test_signtx_mixed_inputs.py::test_non_segwit_segwit_non_segwit_inputs": "fb92593a2fceefffbc30a56a1df2b79b4de148d7db5d25ad17a2ee316260b18f",
"TT_bitcoin-test_signtx_mixed_inputs.py::test_segwit_non_segwit_inputs": "c3b55df982a921a4fdb72ffb579a9b43543537b6c520b6742421f67de2f4845f",

Loading…
Cancel
Save