Skip to content

Commit

Permalink
Update for latest lightning-kmp master
Browse files Browse the repository at this point in the history
- Either, Try, Chain are now provided by bitcoin-kmp
- NodeParams has been simplified
- Added a new Bolt12Invoice type; PaymentRequest is
  an interface.

See:
ACINQ/lightning-kmp#590
ACINQ/lightning-kmp#603
ACINQ/lightning-kmp#605
  • Loading branch information
dpad85 committed Feb 20, 2024
1 parent d51c743 commit 050519c
Show file tree
Hide file tree
Showing 32 changed files with 232 additions and 202 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ package fr.acinq.phoenix.db

import com.squareup.sqldelight.android.AndroidSqliteDriver
import com.squareup.sqldelight.db.SqlDriver
import fr.acinq.lightning.NodeParams
import fr.acinq.bitcoin.Bitcoin.Chain
import fr.acinq.phoenix.utils.PlatformContext
import java.util.*

actual fun createChannelsDbDriver(ctx: PlatformContext, chain: NodeParams.Chain, nodeIdHash: String): SqlDriver {
actual fun createChannelsDbDriver(ctx: PlatformContext, chain: Chain, nodeIdHash: String): SqlDriver {
return AndroidSqliteDriver(ChannelsDatabase.Schema, ctx.applicationContext, "channels-${chain.name.lowercase()}-$nodeIdHash.sqlite")
}

actual fun createPaymentsDbDriver(ctx: PlatformContext, chain: NodeParams.Chain, nodeIdHash: String): SqlDriver {
actual fun createPaymentsDbDriver(ctx: PlatformContext, chain: Chain, nodeIdHash: String): SqlDriver {
return AndroidSqliteDriver(PaymentsDatabase.Schema, ctx.applicationContext, "payments-${chain.name.lowercase()}-$nodeIdHash.sqlite")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package fr.acinq.phoenix

import fr.acinq.lightning.NodeParams
import fr.acinq.bitcoin.Bitcoin
import fr.acinq.lightning.blockchain.electrum.ElectrumClient
import fr.acinq.lightning.blockchain.electrum.ElectrumWatcher
import fr.acinq.lightning.io.TcpSocket
Expand Down Expand Up @@ -74,7 +74,7 @@ class PhoenixBusiness(
}
}

val chain: NodeParams.Chain = NodeParamsManager.chain
val chain: Bitcoin.Chain = NodeParamsManager.chain

val electrumClient by lazy { ElectrumClient(scope = MainScope(), loggerFactory = loggerFactory, pingInterval = 30.seconds, rpcTimeout = 10.seconds) }
internal val electrumWatcher by lazy { ElectrumWatcher(electrumClient, MainScope(), loggerFactory) }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package fr.acinq.phoenix.controllers.config

import fr.acinq.bitcoin.Bitcoin
import fr.acinq.bitcoin.ByteVector32
import fr.acinq.lightning.NodeParams
import fr.acinq.lightning.channel.*
import fr.acinq.lightning.channel.states.*
import fr.acinq.lightning.io.WrappedChannelCommand
Expand All @@ -18,7 +18,7 @@ import kotlinx.coroutines.launch
class AppCloseChannelsConfigurationController(
loggerFactory: LoggerFactory,
private val peerManager: PeerManager,
private val chain: NodeParams.Chain,
private val chain: Bitcoin.Chain,
private val isForceClose: Boolean
) : AppController<CloseChannelsConfiguration.Model, CloseChannelsConfiguration.Intent>(
loggerFactory = loggerFactory,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@

package fr.acinq.phoenix.controllers.payments

import co.touchlab.kermit.Logger
import fr.acinq.bitcoin.utils.Either
import fr.acinq.lightning.Lightning.randomBytes32
import fr.acinq.lightning.logging.LoggerFactory
import fr.acinq.lightning.utils.Either
import fr.acinq.phoenix.PhoenixBusiness
import fr.acinq.phoenix.controllers.AppController
import fr.acinq.phoenix.managers.PeerManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@

package fr.acinq.phoenix.controllers.payments

import fr.acinq.bitcoin.Bitcoin
import fr.acinq.bitcoin.Satoshi
import fr.acinq.lightning.MilliSatoshi
import fr.acinq.lightning.NodeParams
import fr.acinq.lightning.TrampolineFees
import fr.acinq.lightning.payment.Bolt11Invoice
import fr.acinq.lightning.payment.PaymentRequest
import fr.acinq.phoenix.controllers.MVI
import fr.acinq.phoenix.data.BitcoinUri
Expand All @@ -37,7 +38,7 @@ object Scan {
object UnknownFormat : BadRequestReason()
object AlreadyPaidInvoice : BadRequestReason()
data class Expired(val timestampSeconds: Long, val expirySeconds: Long) : BadRequestReason()
data class ChainMismatch(val expected: NodeParams.Chain) : BadRequestReason()
data class ChainMismatch(val expected: Bitcoin.Chain) : BadRequestReason()
data class ServiceError(val url: Url, val error: LnurlError.RemoteFailure) : BadRequestReason()
data class InvalidLnurl(val url: Url) : BadRequestReason()
data class UnsupportedLnurl(val url: Url) : BadRequestReason()
Expand All @@ -46,7 +47,7 @@ object Scan {
sealed class LnurlPayError {
data class RemoteError(val err: LnurlError.RemoteFailure) : LnurlPayError()
data class BadResponseError(val err: LnurlError.Pay.Invoice) : LnurlPayError()
data class ChainMismatch(val expected: NodeParams.Chain) : LnurlPayError()
data class ChainMismatch(val expected: Bitcoin.Chain) : LnurlPayError()
object AlreadyPaidInvoice : LnurlPayError()
}

Expand All @@ -68,12 +69,12 @@ object Scan {
val reason: BadRequestReason
) : Model()

sealed class InvoiceFlow : Model() {
data class InvoiceRequest(
sealed class Bolt11InvoiceFlow : Model() {
data class Bolt11InvoiceRequest(
val request: String,
val paymentRequest: PaymentRequest,
): InvoiceFlow()
object Sending: InvoiceFlow()
val invoice: Bolt11Invoice,
): Bolt11InvoiceFlow()
object Sending: Bolt11InvoiceFlow()
}

data class OnchainFlow(val uri: BitcoinUri): Model()
Expand Down Expand Up @@ -139,12 +140,12 @@ object Scan {
val request: String
) : Intent()

sealed class InvoiceFlow : Intent() {
data class SendInvoicePayment(
val paymentRequest: PaymentRequest,
sealed class Bolt11InvoiceFlow : Intent() {
data class SendBolt11Invoice(
val invoice: Bolt11Invoice,
val amount: MilliSatoshi,
val trampolineFees: TrampolineFees
) : InvoiceFlow()
) : Bolt11InvoiceFlow()
}

object CancelLnurlServiceFetch : Intent()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@

package fr.acinq.phoenix.controllers.payments

import co.touchlab.kermit.Logger
import fr.acinq.bitcoin.Bitcoin
import fr.acinq.bitcoin.BitcoinError
import fr.acinq.bitcoin.utils.Either
import fr.acinq.lightning.*
import fr.acinq.lightning.db.LightningOutgoingPayment
import fr.acinq.lightning.io.SendPayment
import fr.acinq.lightning.logging.LoggerFactory
import fr.acinq.lightning.payment.PaymentRequest
import fr.acinq.lightning.utils.*
import fr.acinq.phoenix.PhoenixBusiness
import fr.acinq.phoenix.controllers.AppController
Expand All @@ -35,6 +34,7 @@ import fr.acinq.phoenix.utils.Parser
import fr.acinq.phoenix.utils.extensions.chain
import fr.acinq.lightning.logging.error
import fr.acinq.lightning.logging.info
import fr.acinq.lightning.payment.Bolt11Invoice
import io.ktor.http.Url
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.filterNotNull
Expand All @@ -51,7 +51,7 @@ class AppScanController(
private val peerManager: PeerManager,
private val lnurlManager: LnurlManager,
private val databaseManager: DatabaseManager,
private val chain: NodeParams.Chain,
private val chain: Bitcoin.Chain,
) : AppController<Scan.Model, Scan.Intent>(
loggerFactory = loggerFactory,
firstModel = firstModel ?: Scan.Model.Ready
Expand Down Expand Up @@ -82,14 +82,14 @@ class AppScanController(
when (intent) {
is Scan.Intent.Reset -> launch { model(Scan.Model.Ready) }
is Scan.Intent.Parse -> launch { processScannedInput(intent) }
is Scan.Intent.InvoiceFlow.SendInvoicePayment -> launch {
sendPayment(
is Scan.Intent.Bolt11InvoiceFlow.SendBolt11Invoice -> launch {
payBolt11Invoice(
amountToSend = intent.amount,
trampolineFees = intent.trampolineFees,
paymentRequest = intent.paymentRequest,
invoice = intent.invoice,
metadata = null,
)
model(Scan.Model.InvoiceFlow.Sending)
model(Scan.Model.Bolt11InvoiceFlow.Sending)
}
is Scan.Intent.CancelLnurlServiceFetch -> launch { cancelLnurlFetch() }
is Scan.Intent.LnurlPayFlow.RequestInvoice -> launch { processLnurlPayRequestInvoice(intent) }
Expand All @@ -105,8 +105,8 @@ class AppScanController(
) {
val input = Parser.removeExcessInput(intent.request)

Parser.readPaymentRequest(input)?.let {
processLightningInvoice(it)
Parser.readBolt11Invoice(input)?.let {
processBolt11Invoice(it)
} ?: readLnurl(input)?.let {
processLnurl(it)
} ?: readBitcoinAddress(input)?.let {
Expand All @@ -119,12 +119,12 @@ class AppScanController(
}

/** Inspects the Lightning invoice for errors and update the model with the adequate value. */
private suspend fun processLightningInvoice(paymentRequest: PaymentRequest) {
val model = checkForBadRequest(paymentRequest)?.let {
Scan.Model.BadRequest(request = paymentRequest.write(), reason = it)
} ?: Scan.Model.InvoiceFlow.InvoiceRequest(
request = paymentRequest.write(),
paymentRequest = paymentRequest,
private suspend fun processBolt11Invoice(invoice: Bolt11Invoice) {
val model = checkForBadBolt11Invoice(invoice)?.let {
Scan.Model.BadRequest(request = invoice.write(), reason = it)
} ?: Scan.Model.Bolt11InvoiceFlow.Bolt11InvoiceRequest(
request = invoice.write(),
invoice = invoice,
)
model(model)
}
Expand Down Expand Up @@ -204,10 +204,10 @@ class AppScanController(
}

/** Extract invoice and send it to the Peer to make the payment, attaching custom trampoline fees if needed. */
private suspend fun sendPayment(
private suspend fun payBolt11Invoice(
amountToSend: MilliSatoshi,
trampolineFees: TrampolineFees,
paymentRequest: PaymentRequest,
invoice: Bolt11Invoice,
metadata: WalletPaymentMetadata?,
) {
val paymentId = UUID.randomUUID()
Expand All @@ -225,8 +225,8 @@ class AppScanController(
SendPayment(
paymentId = paymentId,
amount = amountToSend,
recipient = paymentRequest.nodeId,
paymentRequest = paymentRequest,
recipient = invoice.nodeId,
paymentRequest = invoice,
trampolineFeesOverride = listOf(trampolineFees)
)
)
Expand All @@ -253,7 +253,7 @@ class AppScanController(
requestPayInvoiceTask = task
try {
val invoice = task.await()
when (val check = checkForBadRequest(invoice.paymentRequest)) {
when (checkForBadBolt11Invoice(invoice.invoice)) {
is Scan.BadRequestReason.ChainMismatch -> Either.Left(
Scan.LnurlPayError.ChainMismatch(expected = chain)
)
Expand Down Expand Up @@ -289,10 +289,10 @@ class AppScanController(
)
}
is Either.Right -> {
sendPayment(
payBolt11Invoice(
amountToSend = intent.amount,
trampolineFees = intent.trampolineFees,
paymentRequest = result.value.paymentRequest,
invoice = result.value.invoice,
metadata = WalletPaymentMetadata(
lnurl = LnurlPayMetadata(
pay = intent.paymentIntent,
Expand Down Expand Up @@ -336,7 +336,7 @@ class AppScanController(
val paymentRequest = peerManager.getPeer().createInvoice(
paymentPreimage = Lightning.randomBytes32(),
amount = intent.amount,
description = fr.acinq.lightning.utils.Either.Left(intent.description ?: intent.lnurlWithdraw.defaultDescription),
description = Either.Left(intent.description ?: intent.lnurlWithdraw.defaultDescription),
expirySeconds = (3600 * 24 * 7).toLong(), // one week
)

Expand Down Expand Up @@ -439,7 +439,7 @@ class AppScanController(
fun inspectClipboard(data: String): Scan.ClipboardContent? {
val input = Parser.removeExcessInput(data)

return Parser.readPaymentRequest(input)?.let {
return Parser.readBolt11Invoice(input)?.let {
Scan.ClipboardContent.InvoiceRequest(it)
} ?: readLnurl(input)?.let {
when (it) {
Expand All @@ -462,21 +462,21 @@ class AppScanController(
}

/** Checks that the invoice is on same chain and has not already been paid. */
private suspend fun checkForBadRequest(
paymentRequest: PaymentRequest
private suspend fun checkForBadBolt11Invoice(
invoice: Bolt11Invoice
): Scan.BadRequestReason? {

val actualChain = paymentRequest.chain
val actualChain = invoice.chain
if (chain != actualChain) {
return Scan.BadRequestReason.ChainMismatch(expected = chain)
}

if (paymentRequest.isExpired(currentTimestampSeconds())) {
return Scan.BadRequestReason.Expired(paymentRequest.timestampSeconds, paymentRequest.expirySeconds ?: PaymentRequest.DEFAULT_EXPIRY_SECONDS.toLong())
if (invoice.isExpired(currentTimestampSeconds())) {
return Scan.BadRequestReason.Expired(invoice.timestampSeconds, invoice.expirySeconds ?: Bolt11Invoice.DEFAULT_EXPIRY_SECONDS.toLong())
}

val db = databaseManager.databases.filterNotNull().first()
return if (db.payments.listLightningOutgoingPayments(paymentRequest.paymentHash).any { it.status is LightningOutgoingPayment.Status.Completed.Succeeded }) {
return if (db.payments.listLightningOutgoingPayments(invoice.paymentHash).any { it.status is LightningOutgoingPayment.Status.Completed.Succeeded }) {
Scan.BadRequestReason.AlreadyPaidInvoice
} else {
null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,16 @@

package fr.acinq.phoenix.data

import fr.acinq.bitcoin.Bitcoin
import fr.acinq.bitcoin.BitcoinError
import fr.acinq.bitcoin.ByteVector
import fr.acinq.bitcoin.Satoshi
import fr.acinq.lightning.NodeParams
import fr.acinq.lightning.payment.Bolt11Invoice
import fr.acinq.lightning.payment.PaymentRequest
import io.ktor.http.*

data class BitcoinUri(
val chain: NodeParams.Chain,
val chain: Bitcoin.Chain,
/** Actual Bitcoin address; may be different than the source, e.g. if the source is an URI like "bitcoin:xyz?param=123". */
val address: String,
val script: ByteVector,
Expand All @@ -34,7 +35,7 @@ data class BitcoinUri(
/** Amount requested in the URI. */
val amount: Satoshi? = null,
/** A Bitcoin URI may contain a Lightning payment request as an alternative way to make the payment. */
val paymentRequest: PaymentRequest? = null,
val paymentRequest: Bolt11Invoice? = null,
/** Other bip-21 parameters in the URI that we do not handle. */
val ignoredParams: Parameters = Parameters.Empty,
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ package fr.acinq.phoenix.data.lnurl

import co.touchlab.kermit.Logger
import fr.acinq.bitcoin.ByteVector
import fr.acinq.bitcoin.utils.Try
import fr.acinq.lightning.MilliSatoshi
import fr.acinq.lightning.payment.PaymentRequest
import fr.acinq.lightning.utils.Try
import fr.acinq.lightning.payment.Bolt11Invoice
import fr.acinq.phoenix.data.lnurl.Lnurl.Companion.format
import fr.acinq.phoenix.db.cloud.b64Decode
import io.ktor.http.*
Expand Down Expand Up @@ -68,7 +68,7 @@ sealed class LnurlPay : Lnurl.Qualified {
*/
data class Invoice(
override val initialUrl: Url,
val paymentRequest: PaymentRequest,
val invoice: Bolt11Invoice,
val successAction: SuccessAction?
) : LnurlPay() {
sealed class SuccessAction {
Expand Down Expand Up @@ -111,13 +111,13 @@ sealed class LnurlPay : Lnurl.Qualified {
): Invoice {
try {
val pr = json["pr"]?.jsonPrimitive?.content ?: throw LnurlError.Pay.Invoice.Malformed(origin, "missing pr")
val paymentRequest = when (val res = PaymentRequest.read(pr)) {
val invoice = when (val res = Bolt11Invoice.read(pr)) {
is Try.Success -> res.result
is Try.Failure -> throw LnurlError.Pay.Invoice.Malformed(origin, res.error.message ?: res.error::class.toString())
}

val successAction = parseSuccessAction(origin, json)
return Invoice(intent.initialUrl, paymentRequest, successAction)
return Invoice(intent.initialUrl, invoice, successAction)
} catch (t: Throwable) {
when (t) {
is LnurlError.Pay.Invoice -> throw t
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
package fr.acinq.phoenix.db

import com.squareup.sqldelight.db.SqlDriver
import fr.acinq.lightning.NodeParams
import fr.acinq.bitcoin.Bitcoin
import fr.acinq.phoenix.utils.PlatformContext

expect fun createChannelsDbDriver(ctx: PlatformContext, chain: NodeParams.Chain, nodeIdHash: String): SqlDriver
expect fun createChannelsDbDriver(ctx: PlatformContext, chain: Bitcoin.Chain, nodeIdHash: String): SqlDriver

expect fun createPaymentsDbDriver(ctx: PlatformContext, chain: NodeParams.Chain, nodeIdHash: String): SqlDriver
expect fun createPaymentsDbDriver(ctx: PlatformContext, chain: Bitcoin.Chain, nodeIdHash: String): SqlDriver

expect fun createAppDbDriver(ctx: PlatformContext): SqlDriver
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.squareup.sqldelight.runtime.coroutines.asFlow
import fr.acinq.bitcoin.ByteVector32
import fr.acinq.bitcoin.Crypto
import fr.acinq.bitcoin.TxId
import fr.acinq.bitcoin.utils.Either
import fr.acinq.lightning.channel.ChannelException
import fr.acinq.lightning.db.*
import fr.acinq.lightning.logging.LoggerFactory
Expand Down
Loading

0 comments on commit 050519c

Please sign in to comment.