Browse Source

swapserver: add test for refund path

master
ThomasV 3 years ago
parent
commit
1411b75584
  1. 8
      electrum/commands.py
  2. 5
      electrum/lnworker.py
  3. 1
      electrum/simple_config.py
  4. 24
      electrum/submarine_swaps.py
  5. 7
      electrum/tests/regtest.py
  6. 43
      electrum/tests/regtest/regtest.sh

8
electrum/commands.py

@ -1318,22 +1318,22 @@ class Commands:
await sm.get_pairs() await sm.get_pairs()
lightning_amount_sat = satoshis(lightning_amount) lightning_amount_sat = satoshis(lightning_amount)
onchain_amount_sat = sm.get_recv_amount(lightning_amount_sat, is_reverse=True) onchain_amount_sat = sm.get_recv_amount(lightning_amount_sat, is_reverse=True)
success = None funding_txid = None
elif lightning_amount == 'dryrun': elif lightning_amount == 'dryrun':
await sm.get_pairs() await sm.get_pairs()
onchain_amount_sat = satoshis(onchain_amount) onchain_amount_sat = satoshis(onchain_amount)
lightning_amount_sat = sm.get_send_amount(onchain_amount_sat, is_reverse=True) lightning_amount_sat = sm.get_send_amount(onchain_amount_sat, is_reverse=True)
success = None funding_txid = None
else: else:
lightning_amount_sat = satoshis(lightning_amount) lightning_amount_sat = satoshis(lightning_amount)
claim_fee = sm.get_claim_fee() claim_fee = sm.get_claim_fee()
onchain_amount_sat = satoshis(onchain_amount) + claim_fee onchain_amount_sat = satoshis(onchain_amount) + claim_fee
success = await wallet.lnworker.swap_manager.reverse_swap( funding_txid = await wallet.lnworker.swap_manager.reverse_swap(
lightning_amount_sat=lightning_amount_sat, lightning_amount_sat=lightning_amount_sat,
expected_onchain_amount_sat=onchain_amount_sat, expected_onchain_amount_sat=onchain_amount_sat,
) )
return { return {
'success': success, 'funding_txid': funding_txid,
'lightning_amount': format_satoshis(lightning_amount_sat), 'lightning_amount': format_satoshis(lightning_amount_sat),
'onchain_amount': format_satoshis(onchain_amount_sat), 'onchain_amount': format_satoshis(onchain_amount_sat),
} }

5
electrum/lnworker.py

@ -2704,3 +2704,8 @@ class LNWallet(LNWorker):
self._channel_backups[bfh(channel_id)] = cb self._channel_backups[bfh(channel_id)] = cb
util.trigger_callback('channels_updated', self.wallet) util.trigger_callback('channels_updated', self.wallet)
self.lnwatcher.add_channel(cb.funding_outpoint.to_str(), cb.get_funding_address()) self.lnwatcher.add_channel(cb.funding_outpoint.to_str(), cb.get_funding_address())
def fail_trampoline_forwarding(self, payment_key):
""" use this to fail htlcs received for hold invoices"""
e = OnionRoutingFailure(code=OnionFailureCode.UNKNOWN_NEXT_PEER, data=b'')
self.trampoline_forwarding_failures[payment_key] = e

1
electrum/simple_config.py

@ -967,6 +967,7 @@ class SimpleConfig(Logger):
SWAPSERVER_URL_MAINNET = ConfigVar('swapserver_url_mainnet', default='https://swaps.electrum.org/api', type_=str) SWAPSERVER_URL_MAINNET = ConfigVar('swapserver_url_mainnet', default='https://swaps.electrum.org/api', type_=str)
SWAPSERVER_URL_TESTNET = ConfigVar('swapserver_url_testnet', default='https://swaps.electrum.org/testnet', type_=str) SWAPSERVER_URL_TESTNET = ConfigVar('swapserver_url_testnet', default='https://swaps.electrum.org/testnet', type_=str)
SWAPSERVER_URL_REGTEST = ConfigVar('swapserver_url_regtest', default='http://localhost:5455/api', type_=str) SWAPSERVER_URL_REGTEST = ConfigVar('swapserver_url_regtest', default='http://localhost:5455/api', type_=str)
TEST_SWAPSERVER_REFUND = ConfigVar('test_swapserver_refund', default=False, type_=bool)
# connect to remote WT # connect to remote WT
WATCHTOWER_CLIENT_ENABLED = ConfigVar('use_watchtower', default=False, type_=bool) WATCHTOWER_CLIENT_ENABLED = ConfigVar('use_watchtower', default=False, type_=bool)
WATCHTOWER_CLIENT_URL = ConfigVar('watchtower_url', default=None, type_=str) WATCHTOWER_CLIENT_URL = ConfigVar('watchtower_url', default=None, type_=str)

24
electrum/submarine_swaps.py

@ -232,11 +232,18 @@ class SwapManager(Logger):
# #
tx = self.lnwatcher.adb.get_transaction(txin.spent_txid) tx = self.lnwatcher.adb.get_transaction(txin.spent_txid)
preimage = tx.inputs()[0].witness_elements()[1] preimage = tx.inputs()[0].witness_elements()[1]
assert swap.payment_hash == sha256(preimage) if sha256(preimage) == swap.payment_hash:
swap.preimage = preimage swap.preimage = preimage
self.logger.info(f'found preimage: {preimage.hex()}') self.logger.info(f'found preimage: {preimage.hex()}')
self.lnworker.preimages[swap.payment_hash.hex()] = preimage.hex() self.lnworker.preimages[swap.payment_hash.hex()] = preimage.hex()
# note: we must check the payment secret before we broadcast the funding tx # note: we must check the payment secret before we broadcast the funding tx
else:
# refund tx
if spent_height > 0:
self.logger.info(f'found confirmed refund')
payment_secret = self.lnworker.get_payment_secret(swap.payment_hash)
payment_key = swap.payment_hash + payment_secret
self.lnworker.fail_trampoline_forwarding(payment_key)
if spent_height > 0: if spent_height > 0:
if current_height - spent_height > REDEEM_AFTER_DOUBLE_SPENT_DELAY: if current_height - spent_height > REDEEM_AFTER_DOUBLE_SPENT_DELAY:
@ -257,6 +264,8 @@ class SwapManager(Logger):
if swap.is_reverse and swap.preimage is None: if swap.is_reverse and swap.preimage is None:
self.logger.info('preimage not available yet') self.logger.info('preimage not available yet')
continue continue
if swap.is_reverse and self.network.config.TEST_SWAPSERVER_REFUND:
continue
try: try:
tx = self._create_and_sign_claim_tx(txin=txin, swap=swap, config=self.wallet.config) tx = self._create_and_sign_claim_tx(txin=txin, swap=swap, config=self.wallet.config)
except BelowDustLimit: except BelowDustLimit:
@ -586,13 +595,12 @@ class SwapManager(Logger):
asyncio.ensure_future(self.lnworker.pay_invoice(fee_invoice, attempts=10)) asyncio.ensure_future(self.lnworker.pay_invoice(fee_invoice, attempts=10))
# we return if we detect funding # we return if we detect funding
async def wait_for_funding(swap): async def wait_for_funding(swap):
while swap.spending_txid is None: while swap.funding_txid is None:
await asyncio.sleep(1) await asyncio.sleep(1)
# initiate main payment # initiate main payment
tasks = [asyncio.create_task(self.lnworker.pay_invoice(invoice, attempts=10, channels=channels)), asyncio.create_task(wait_for_funding(swap))] tasks = [asyncio.create_task(self.lnworker.pay_invoice(invoice, attempts=10, channels=channels)), asyncio.create_task(wait_for_funding(swap))]
await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED) await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
success = swap.spending_txid is not None return swap.funding_txid
return success
def _add_or_reindex_swap(self, swap: SwapData) -> None: def _add_or_reindex_swap(self, swap: SwapData) -> None:
if swap.payment_hash.hex() not in self.swaps: if swap.payment_hash.hex() not in self.swaps:

7
electrum/tests/regtest.py

@ -47,8 +47,11 @@ class TestLightningAB(TestLightning):
def test_collaborative_close(self): def test_collaborative_close(self):
self.run_shell(['collaborative_close']) self.run_shell(['collaborative_close'])
def test_submarine_swap(self): def test_swapserver_success(self):
self.run_shell(['reverse_swap']) self.run_shell(['swapserver_success'])
def test_swapserver_refund(self):
self.run_shell(['swapserver_refund'])
def test_backup(self): def test_backup(self):
self.run_shell(['backup']) self.run_shell(['backup'])

43
electrum/tests/regtest/regtest.sh

@ -15,6 +15,19 @@ function new_blocks()
$bitcoin_cli generatetoaddress $1 $($bitcoin_cli getnewaddress) > /dev/null $bitcoin_cli generatetoaddress $1 $($bitcoin_cli getnewaddress) > /dev/null
} }
function wait_until_htlcs_settled()
{
msg="wait until $1's local_unsettled_sent is zero"
cmd="./run_electrum --regtest -D /tmp/$1"
while unsettled=$($alice list_channels | jq '.[] | .local_unsettled_sent') && [ $unsettled != "0" ]; do
sleep 1
msg="$msg."
printf "$msg\r"
done
printf "\n"
}
function wait_for_balance() function wait_for_balance()
{ {
msg="wait until $1's balance reaches $2" msg="wait until $1's balance reaches $2"
@ -171,7 +184,7 @@ if [[ $1 == "collaborative_close" ]]; then
fi fi
if [[ $1 == "reverse_swap" ]]; then if [[ $1 == "swapserver_success" ]]; then
wait_for_balance alice 1 wait_for_balance alice 1
echo "alice opens channel" echo "alice opens channel"
bob_node=$($bob nodeid) bob_node=$($bob nodeid)
@ -180,12 +193,34 @@ if [[ $1 == "reverse_swap" ]]; then
wait_until_channel_open alice wait_until_channel_open alice
echo "alice initiates swap" echo "alice initiates swap"
dryrun=$($alice reverse_swap 0.02 dryrun) dryrun=$($alice reverse_swap 0.02 dryrun)
echo $dryrun | jq
onchain_amount=$(echo $dryrun| jq -r ".onchain_amount") onchain_amount=$(echo $dryrun| jq -r ".onchain_amount")
$alice reverse_swap 0.02 $onchain_amount swap=$($alice reverse_swap 0.02 $onchain_amount)
echo $swap | jq
funding_txid=$(echo $swap| jq -r ".funding_txid")
new_blocks 1 new_blocks 1
sleep 1 wait_until_spent $funding_txid 0
wait_until_htlcs_settled alice
fi
if [[ $1 == "swapserver_refund" ]]; then
$alice setconfig test_swapserver_refund true
wait_for_balance alice 1
echo "alice opens channel"
bob_node=$($bob nodeid)
channel=$($alice open_channel $bob_node 0.15)
new_blocks 3
wait_until_channel_open alice
echo "alice initiates swap"
dryrun=$($alice reverse_swap 0.02 dryrun)
onchain_amount=$(echo $dryrun| jq -r ".onchain_amount")
swap=$($alice reverse_swap 0.02 $onchain_amount)
echo $swap | jq
funding_txid=$(echo $swap| jq -r ".funding_txid")
new_blocks 140
wait_until_spent $funding_txid 0
new_blocks 1 new_blocks 1
wait_until_htlcs_settled alice
fi fi

Loading…
Cancel
Save