1
0

New flow for submarine swaps:

- client requests payment_hash from the server
 - client sends an invoice with that hash
 - client waits to receive HTLCs, then broadcasts funding tx

This means that we now use same script for normal and reverse swaps.
The new flow is enabled by setting option LIGHTNING_SWAP_HTLC_FIRST
in the client. The old protocol is still supported server-side.
This commit is contained in:
ThomasV
2023-07-28 09:28:45 +02:00
parent 11af4e47a8
commit fd10ae3a3b
4 changed files with 308 additions and 147 deletions

View File

@@ -5,11 +5,10 @@ from collections import defaultdict
from aiohttp import web
from aiorpcx import NetAddress
from electrum.util import log_exceptions, ignore_exceptions
from electrum.logging import Logger
from electrum.util import EventListener
from electrum.lnaddr import lndecode
class SwapServer(Logger, EventListener):
"""
@@ -24,6 +23,7 @@ class SwapServer(Logger, EventListener):
Logger.__init__(self)
self.config = config
self.wallet = wallet
self.sm = self.wallet.lnworker.swap_manager
self.addr = NetAddress.from_string(self.config.SWAPSERVER_ADDRESS)
self.register_callbacks() # eventlistener
@@ -36,6 +36,8 @@ class SwapServer(Logger, EventListener):
app = web.Application()
app.add_routes([web.get('/api/getpairs', self.get_pairs)])
app.add_routes([web.post('/api/createswap', self.create_swap)])
app.add_routes([web.post('/api/createnormalswap', self.create_normal_swap)])
app.add_routes([web.post('/api/addswapinvoice', self.add_swap_invoice)])
runner = web.AppRunner(app)
await runner.setup()
@@ -44,7 +46,7 @@ class SwapServer(Logger, EventListener):
self.logger.info(f"now running and listening. addr={self.addr}")
async def get_pairs(self, r):
sm = self.wallet.lnworker.swap_manager
sm = self.sm
sm.init_pairs()
pairs = {
"info": [],
@@ -84,9 +86,36 @@ class SwapServer(Logger, EventListener):
}
return web.json_response(pairs)
async def add_swap_invoice(self, r):
request = await r.json()
invoice = request['invoice']
self.sm.add_invoice(invoice, pay_now=True)
return web.json_response({})
async def create_normal_swap(self, r):
# normal for client, reverse for server
request = await r.json()
lightning_amount_sat = request['invoiceAmount']
their_pubkey = bytes.fromhex(request['refundPublicKey'])
assert len(their_pubkey) == 33
swap = self.sm.create_reverse_swap(
payment_hash=None,
lightning_amount_sat=lightning_amount_sat,
their_pubkey=their_pubkey
)
response = {
"id": swap.payment_hash.hex(),
'preimageHash': swap.payment_hash.hex(),
"acceptZeroConf": False,
"expectedAmount": swap.onchain_amount,
"timeoutBlockHeight": swap.locktime,
"address": swap.lockup_address,
"redeemScript": swap.redeem_script.hex(),
}
return web.json_response(response)
async def create_swap(self, r):
sm = self.wallet.lnworker.swap_manager
sm.init_pairs()
self.sm.init_pairs()
request = await r.json()
req_type = request['type']
assert request['pairId'] == 'BTC/BTC'
@@ -96,7 +125,7 @@ class SwapServer(Logger, EventListener):
their_pubkey=bytes.fromhex(request['claimPublicKey'])
assert len(payment_hash) == 32
assert len(their_pubkey) == 33
swap, payment_hash, invoice, prepay_invoice = sm.add_server_swap(
swap, invoice, prepay_invoice = self.sm.create_normal_swap(
lightning_amount_sat=lightning_amount_sat,
payment_hash=payment_hash,
their_pubkey=their_pubkey
@@ -111,13 +140,19 @@ class SwapServer(Logger, EventListener):
"onchainAmount": swap.onchain_amount,
}
elif req_type == 'submarine':
# old protocol
their_invoice=request['invoice']
their_pubkey=bytes.fromhex(request['refundPublicKey'])
assert len(their_pubkey) == 33
swap, payment_hash, invoice, prepay_invoice = sm.add_server_swap(
invoice=their_invoice,
lnaddr = lndecode(their_invoice)
payment_hash = lnaddr.paymenthash
lightning_amount_sat = int(lnaddr.get_amount_sat()) # should return int
swap = self.sm.create_reverse_swap(
lightning_amount_sat=lightning_amount_sat,
payment_hash=payment_hash,
their_pubkey=their_pubkey
)
self.sm.add_invoice(their_invoice, pay_now=False)
response = {
"id": payment_hash.hex(),
"acceptZeroConf": False,

View File

@@ -55,7 +55,6 @@ class SwapServerPlugin(BasePlugin):
self.server = SwapServer(self.config, wallet)
sm = wallet.lnworker.swap_manager
for coro in [
sm.pay_pending_invoices(), # FIXME this method can raise, which is not properly handled...?
self.server.run(),
]:
asyncio.run_coroutine_threadsafe(daemon.taskgroup.spawn(coro), daemon.asyncio_loop)