Skip to content

Commit

Permalink
Minor API improvements for sending/receiving payments (#619)
Browse files Browse the repository at this point in the history
Define a `NodeEvent.PaymentReceived` that aims to replace the eponym class in the `Peer` (which is marked as deprecated, but otherwise left untouched). The previous impl wasn't emitted for pay-to-open parts.

Also define a "synchronous' `sendLightning` method similar to other peer methods.
  • Loading branch information
pm47 authored Mar 11, 2024
1 parent d41e433 commit cb0f5c5
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 8 deletions.
10 changes: 10 additions & 0 deletions src/commonMain/kotlin/fr/acinq/lightning/NodeEvents.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import fr.acinq.lightning.channel.SharedFundingInput
import fr.acinq.lightning.channel.states.ChannelStateWithCommitments
import fr.acinq.lightning.channel.states.Normal
import fr.acinq.lightning.channel.states.WaitForFundingCreated
import fr.acinq.lightning.db.IncomingPayment
import fr.acinq.lightning.utils.sum
import fr.acinq.lightning.wire.Node
import fr.acinq.lightning.wire.PleaseOpenChannel
import kotlinx.coroutines.CompletableDeferred

Expand Down Expand Up @@ -58,3 +61,10 @@ sealed interface SensitiveTaskEvents : NodeEvents {

/** This will be emitted in a corner case where the user restores a wallet on an older version of the app, which is unable to read the channel data. */
data object UpgradeRequired : NodeEvents

sealed interface PaymentEvents : NodeEvents {
data class PaymentReceived(val paymentHash: ByteVector32, val receivedWith: List<IncomingPayment.ReceivedWith>) : PaymentEvents {
val amount: MilliSatoshi = receivedWith.map { it.amount }.sum()
val fees: MilliSatoshi = receivedWith.map { it.fees }.sum()
}
}
24 changes: 22 additions & 2 deletions src/commonMain/kotlin/fr/acinq/lightning/io/Peer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import fr.acinq.lightning.serialization.Encryption.from
import fr.acinq.lightning.serialization.Serialization.DeserializationResult
import fr.acinq.lightning.transactions.Transactions
import fr.acinq.lightning.utils.*
import fr.acinq.lightning.utils.UUID.Companion.randomUUID
import fr.acinq.lightning.wire.*
import fr.acinq.lightning.wire.Ping
import kotlinx.coroutines.*
Expand Down Expand Up @@ -77,10 +78,14 @@ data class SendPayment(val paymentId: UUID, val amount: MilliSatoshi, val recipi
data class PurgeExpiredPayments(val fromCreatedAt: Long, val toCreatedAt: Long) : PaymentCommand()

sealed class PeerEvent
@Deprecated("Replaced by NodeEvents", replaceWith = ReplaceWith("PaymentEvents.PaymentReceived", "fr.acinq.lightning.PaymentEvents"))
data class PaymentReceived(val incomingPayment: IncomingPayment, val received: IncomingPayment.Received) : PeerEvent()
data class PaymentProgress(val request: SendPayment, val fees: MilliSatoshi) : PeerEvent()
data class PaymentNotSent(val request: SendPayment, val reason: OutgoingPaymentFailure) : PeerEvent()
data class PaymentSent(val request: SendPayment, val payment: LightningOutgoingPayment) : PeerEvent()
sealed class SendPaymentResult : PeerEvent() {
abstract val request: SendPayment
}
data class PaymentNotSent(override val request: SendPayment, val reason: OutgoingPaymentFailure) : SendPaymentResult()
data class PaymentSent(override val request: SendPayment, val payment: LightningOutgoingPayment) : SendPaymentResult()
data class ChannelClosing(val channelId: ByteVector32) : PeerEvent()

/**
Expand Down Expand Up @@ -603,6 +608,20 @@ class Peer(
}
}

suspend fun sendLightning(amount: MilliSatoshi, paymentRequest: Bolt11Invoice): SendPaymentResult {
val res = CompletableDeferred<SendPaymentResult>()
val paymentId = randomUUID()
this.launch {
res.complete(eventsFlow
.filterIsInstance<SendPaymentResult>()
.filter { it.request.paymentId == paymentId }
.first()
)
}
send(SendPayment(paymentId, amount, paymentRequest.nodeId, paymentRequest))
return res.await()
}

suspend fun createInvoice(paymentPreimage: ByteVector32, amount: MilliSatoshi?, description: Either<String, ByteVector32>, expirySeconds: Long? = null): Bolt11Invoice {
// we add one extra hop which uses a virtual channel with a "peer id", using the highest remote fees and expiry across all
// channels to maximize the likelihood of success on the first payment attempt
Expand Down Expand Up @@ -795,6 +814,7 @@ class Peer(
// this was a multi-part payment, we signal that the task is finished
nodeParams._nodeEvents.tryEmit(SensitiveTaskEvents.TaskEnded(SensitiveTaskEvents.TaskIdentifier.IncomingMultiPartPayment(result.incomingPayment.paymentHash)))
}
@Suppress("DEPRECATION")
_eventsFlow.emit(PaymentReceived(result.incomingPayment, result.received))
}
is IncomingPaymentHandler.ProcessAddResult.Pending -> if (result.pendingPayment.parts.size == 1) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@ import fr.acinq.bitcoin.ByteVector32
import fr.acinq.bitcoin.Crypto
import fr.acinq.bitcoin.PrivateKey
import fr.acinq.bitcoin.utils.Either
import fr.acinq.lightning.CltvExpiry
import fr.acinq.lightning.*
import fr.acinq.lightning.Lightning.randomBytes32
import fr.acinq.lightning.LiquidityEvents
import fr.acinq.lightning.MilliSatoshi
import fr.acinq.lightning.NodeParams
import fr.acinq.lightning.channel.ChannelAction
import fr.acinq.lightning.channel.ChannelCommand
import fr.acinq.lightning.channel.Origin
Expand Down Expand Up @@ -136,12 +133,14 @@ class IncomingPaymentHandler(val nodeParams: NodeParams, val db: IncomingPayment
)
}
when (val origin = action.origin) {
is Origin.PayToOpenOrigin ->
is Origin.PayToOpenOrigin -> {
// there already is a corresponding Lightning invoice in the db
db.receivePayment(
paymentHash = origin.paymentHash,
receivedWith = listOf(receivedWith)
)
nodeParams._nodeEvents.emit(PaymentEvents.PaymentReceived(origin.paymentHash, listOf(receivedWith)))
}
else -> {
// this is a swap, there was no pre-existing invoice, we need to create a fake one
val incomingPayment = db.addIncomingPayment(
Expand All @@ -152,6 +151,7 @@ class IncomingPaymentHandler(val nodeParams: NodeParams, val db: IncomingPayment
paymentHash = incomingPayment.paymentHash,
receivedWith = listOf(receivedWith)
)
nodeParams._nodeEvents.emit(PaymentEvents.PaymentReceived(incomingPayment.paymentHash, listOf(receivedWith)))
}
}
}
Expand Down Expand Up @@ -299,7 +299,7 @@ class IncomingPaymentHandler(val nodeParams: NodeParams, val db: IncomingPayment
pending.remove(paymentPart.paymentHash)
val received = IncomingPayment.Received(receivedWith = receivedWith)
db.receivePayment(paymentPart.paymentHash, received.receivedWith)

nodeParams._nodeEvents.emit(PaymentEvents.PaymentReceived(paymentPart.paymentHash, received.receivedWith))
return ProcessAddResult.Accepted(actions, incomingPayment.copy(received = received), received)
}
}
Expand Down

0 comments on commit cb0f5c5

Please sign in to comment.