async def reverse_swap(
            self,
            *,
            transport: 'SwapServerTransport',
            lightning_amount_sat: int,
            expected_onchain_amount_sat: int,
            channels: Optional[Sequence['Channel']] = None,
    ) -> Optional[str]:
        """send on Lightning, receive on-chain

        - User generates preimage, RHASH. Sends RHASH to server.
        - Server creates an LN invoice for RHASH.
        - User pays LN invoice - except server needs to hold the HTLC as preimage is unknown.
            - if the server requested a fee prepayment (using 'minerFeeInvoice'),
              the server will have the preimage for that. The user will send HTLCs for both the main RHASH,
              and for the fee prepayment. Once both MPP sets arrive at the server, the server will fulfill
              the HTLCs for the fee prepayment (before creating the on-chain output).
        - Server creates on-chain output locked to RHASH.
        - User spends on-chain output, revealing preimage.
        - Server fulfills HTLC using preimage.

        Note: expected_onchain_amount_sat is BEFORE deducting the on-chain claim tx fee.
        """
        assert self.network
        assert self.lnwatcher
        privkey = os.urandom(32)
        our_pubkey = ECPrivkey(privkey).get_public_key_bytes(compressed=True)
        preimage = os.urandom(32)
        payment_hash = sha256(preimage)
        request_data = {
            "type": "reversesubmarine",
            "pairId": "BTC/BTC",
            "orderSide": "buy",
            "invoiceAmount": lightning_amount_sat,
            "preimageHash": payment_hash.hex(),
            "claimPublicKey": our_pubkey.hex()
        }
        self.logger.debug(f'rswap: sending request for {lightning_amount_sat}')
        data = await transport.send_request_to_server('createswap', request_data)
        invoice = data['invoice']
        fee_invoice = data.get('minerFeeInvoice')
        lockup_address = data['lockupAddress']
        redeem_script = bytes.fromhex(data['redeemScript'])
        locktime = data['timeoutBlockHeight']
        onchain_amount = data["onchainAmount"]
        response_id = data['id']
        self.logger.debug(f'rswap: {response_id=}')
        # verify redeem_script is built with our pubkey and preimage
        check_reverse_redeem_script(
            redeem_script=redeem_script,
            lockup_address=lockup_address,
            payment_hash=payment_hash,
            locktime=locktime,
            refund_pubkey=None,
            claim_pubkey=our_pubkey,
        )
        # check that the onchain amount is what we expected
        if onchain_amount < expected_onchain_amount_sat:
            raise Exception(f"rswap check failed: onchain_amount is less than what we expected: "
                            f"{onchain_amount} < {expected_onchain_amount_sat}")
        # verify that we will have enough time to get our tx confirmed
        if locktime - self.network.get_local_height() <= MIN_LOCKTIME_DELTA:
            raise Exception("rswap check failed: locktime too close")
        # verify invoice payment_hash
        lnaddr = self.lnworker._check_bolt11_invoice(invoice)
        invoice_amount = int(lnaddr.get_amount_sat())
        if lnaddr.paymenthash != payment_hash:
            raise Exception("rswap check failed: inconsistent RHASH and invoice")
        # check that the lightning amount is what we requested
        if fee_invoice:
            fee_lnaddr = self.lnworker._check_bolt11_invoice(fee_invoice)
            invoice_amount += fee_lnaddr.get_amount_sat()
            prepay_hash = fee_lnaddr.paymenthash
        else:
            prepay_hash = None
        if int(invoice_amount) != lightning_amount_sat:
            raise Exception(f"rswap check failed: invoice_amount ({invoice_amount}) "
                            f"not what we requested ({lightning_amount_sat})")
        # save swap data to wallet file
        swap = self.add_reverse_swap(
            redeem_script=redeem_script,
            locktime=locktime,
            privkey=privkey,
            preimage=preimage,
            payment_hash=payment_hash,
            prepay_hash=prepay_hash,
            onchain_amount_sat=onchain_amount,
            lightning_amount_sat=lightning_amount_sat)
        # initiate fee payment.
        if fee_invoice:
            fee_invoice_obj = Invoice.from_bech32(fee_invoice)
            asyncio.ensure_future(self.lnworker.pay_invoice(fee_invoice_obj))
        # we return if we detect funding
        async def wait_for_funding(swap):
            while swap.funding_txid is None:
                await asyncio.sleep(1)
        # initiate main payment
        invoice_obj = Invoice.from_bech32(invoice)
        tasks = [asyncio.create_task(self.lnworker.pay_invoice(invoice_obj, channels=channels)), asyncio.create_task(wait_for_funding(swap))]
        await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
        return swap.funding_txid
send on Lightning, receive on-chain

        - User generates preimage, RHASH. Sends RHASH to server.
        - Server creates an LN invoice for RHASH.
        - User pays LN invoice - except server needs to hold the HTLC as preimage is unknown.
            - if the server requested a fee prepayment (using 'minerFeeInvoice'),
              the server will have the preimage for that. The user will send HTLCs for both the main RHASH,
              and for the fee prepayment. Once both MPP sets arrive at the server, the server will fulfill
              the HTLCs for the fee prepayment (before creating the on-chain output).
        - Server creates on-chain output locked to RHASH.
        - User spends on-chain output, revealing preimage.
        - Server fulfills HTLC using preimage.

        Note: expected_onchain_amount_sat is BEFORE deducting the on-chain claim tx fee.
        
electrum/submarine_swaps.py