Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minor API improvements for sending/receiving payments #619

Merged
merged 2 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading