Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package fr.acinq.lightning

import co.touchlab.kermit.Logger
import fr.acinq.bitcoin.Chain
import fr.acinq.bitcoin.PrivateKey
import fr.acinq.bitcoin.PublicKey
import fr.acinq.bitcoin.Satoshi
import fr.acinq.lightning.Lightning.nodeFee
Expand Down Expand Up @@ -267,7 +266,7 @@ data class NodeParams(
*
* @return the default offer and the private key that will sign invoices for this offer.
*/
fun defaultOffer(trampolineNodeId: PublicKey): Pair<OfferTypes.Offer, PrivateKey> = OfferManager.deterministicOffer(chainHash, nodePrivateKey, trampolineNodeId, null, null, null)
fun defaultOffer(trampolineNodeId: PublicKey): OfferTypes.OfferAndKey = OfferManager.deterministicOffer(chainHash, nodePrivateKey, trampolineNodeId, null, null, null)

/**
* Generate a random Bolt 12 offer based on the node's seed and its trampoline node.
Expand All @@ -276,7 +275,7 @@ data class NodeParams(
*
* @return a random offer and the private key that will sign invoices for this offer.
*/
fun randomOffer(trampolineNodeId: PublicKey, amount: MilliSatoshi?, description: String?): Pair<OfferTypes.Offer, PrivateKey> {
fun randomOffer(trampolineNodeId: PublicKey, amount: MilliSatoshi?, description: String?): OfferTypes.OfferAndKey {
// We generate a random nonce to ensure that this offer is unique.
val nonce = randomBytes32()
return OfferManager.deterministicOffer(chainHash, nodePrivateKey, trampolineNodeId, amount, description, nonce)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -860,7 +860,7 @@ class Peer(
.first()
.let { event -> replyTo.complete(event.address) }
}
peerConnection?.send(DNSAddressRequest(nodeParams.chainHash, nodeParams.defaultOffer(walletParams.trampolineNode.id).first, languageSubtag))
peerConnection?.send(DNSAddressRequest(nodeParams.chainHash, nodeParams.defaultOffer(walletParams.trampolineNode.id).offer, languageSubtag))
return replyTo.await()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ class IncomingPaymentHandler(val nodeParams: NodeParams, val db: PaymentsDb) {
}
is PaymentOnion.FinalPayload.Blinded -> {
// We encrypted the payment metadata for ourselves in the blinded path we included in the invoice.
return when (val metadata = OfferPaymentMetadata.fromPathId(nodeParams.nodeId, finalPayload.pathId)) {
return when (val metadata = OfferPaymentMetadata.fromPathId(nodeParams.nodePrivateKey, finalPayload.pathId, paymentPart.paymentHash)) {
null -> {
logger.warning { "invalid path_id: ${finalPayload.pathId.toHex()}" }
Either.Left(rejectPaymentPart(privateKey, paymentPart, null, currentBlockHeight))
Expand All @@ -503,7 +503,7 @@ class IncomingPaymentHandler(val nodeParams: NodeParams, val db: PaymentsDb) {
logger.warning { "payment with expiry too small: ${paymentPart.htlc.cltvExpiry}, min is ${minFinalCltvExpiry(nodeParams, paymentPart, incomingPayment, currentBlockHeight)}" }
Either.Left(rejectPaymentPart(privateKey, paymentPart, incomingPayment, currentBlockHeight))
}
metadata.createdAtMillis + nodeParams.bolt12InvoiceExpiry.inWholeMilliseconds < currentTimestampMillis() && incomingPayment.parts.isEmpty() && !paysPreviousOnTheFlyFunding -> {
metadata.createdAtSeconds + (metadata.relativeExpirySeconds ?: nodeParams.bolt12InvoiceExpiry.inWholeSeconds) < currentTimestampSeconds() && incomingPayment.parts.isEmpty() && !paysPreviousOnTheFlyFunding -> {
logger.warning { "the invoice is expired" }
Either.Left(rejectPaymentPart(privateKey, paymentPart, incomingPayment, currentBlockHeight))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import fr.acinq.lightning.message.OnionMessages
import fr.acinq.lightning.message.OnionMessages.Destination
import fr.acinq.lightning.message.OnionMessages.IntermediateNode
import fr.acinq.lightning.message.OnionMessages.buildMessage
import fr.acinq.lightning.utils.currentTimestampMillis
import fr.acinq.lightning.utils.currentTimestampSeconds
import fr.acinq.lightning.utils.toByteVector
import fr.acinq.lightning.wire.*
import kotlinx.coroutines.flow.MutableSharedFlow
Expand Down Expand Up @@ -153,14 +153,19 @@ class OfferManager(val nodeParams: NodeParams, val walletParams: WalletParams, v
else -> {
val amount = request.requestedAmount
val preimage = randomBytes32()
val truncatedPayerNote = (request.payerNote ?: request.offer.description)?.let {
if (it.length <= 64) {
it
} else {
it.take(63) + "…"
}
}
val metadata = OfferPaymentMetadata.V1(request.offer.offerId, amount, preimage, request.payerId, truncatedPayerNote, request.quantity, currentTimestampMillis()).toPathId(nodeParams.nodePrivateKey)
val (truncatedPayerNote, truncatedDescription) = OfferPaymentMetadata.truncateNotes(request.payerNote, request.offer.description)
val expirySeconds = request.offer.expirySeconds ?: nodeParams.bolt12InvoiceExpiry.inWholeSeconds
val metadata = OfferPaymentMetadata.V2(
offerId = request.offer.offerId,
amount = amount,
preimage = preimage,
createdAtSeconds = currentTimestampSeconds(),
relativeExpirySeconds = expirySeconds,
description = truncatedDescription,
payerKey = request.payerId,
payerNote = truncatedPayerNote,
quantity = request.quantity_opt
).toPathId(nodeParams.nodePrivateKey)
val recipientPayload = RouteBlindingEncryptedData(TlvStream(RouteBlindingEncryptedDataTlv.PathId(metadata))).write().toByteVector()
val cltvExpiryDelta = remoteChannelUpdates.maxOfOrNull { it.cltvExpiryDelta } ?: walletParams.invoiceDefaultRoutingFees.cltvExpiryDelta
val paymentInfo = OfferTypes.PaymentInfo(
Expand Down Expand Up @@ -191,7 +196,7 @@ class OfferManager(val nodeParams: NodeParams, val walletParams: WalletParams, v
).write().toByteVector()
val blindedRoute = RouteBlinding.create(randomKey(), listOf(remoteNodeId, nodeParams.nodeId), listOf(remoteNodePayload, recipientPayload)).route
val path = Bolt12Invoice.Companion.PaymentBlindedContactInfo(OfferTypes.ContactInfo.BlindedPath(blindedRoute), paymentInfo)
val invoice = Bolt12Invoice(request, preimage, blindedPrivateKey, nodeParams.bolt12InvoiceExpiry.inWholeSeconds, nodeParams.features.bolt12Features(), listOf(path))
val invoice = Bolt12Invoice(request, preimage, blindedPrivateKey, expirySeconds, nodeParams.features.bolt12Features(), listOf(path))
val destination = Destination.BlindedPath(replyPath)
when (val invoiceMessage = buildMessage(randomKey(), randomKey(), intermediateNodes(destination), destination, TlvStream(OnionMessagePayloadTlv.Invoice(invoice.records)))) {
is Left -> {
Expand Down Expand Up @@ -233,7 +238,7 @@ class OfferManager(val nodeParams: NodeParams, val walletParams: WalletParams, v
pathId != null && pathId.size() != 32 -> false
else -> {
val expected = deterministicOffer(nodeParams.chainHash, nodeParams.nodePrivateKey, walletParams.trampolineNode.id, offer.amount, offer.description, pathId?.let { ByteVector32(it) })
expected == Pair(offer, blindedPrivateKey)
expected == OfferTypes.OfferAndKey(offer, blindedPrivateKey)
}
}

Expand All @@ -249,7 +254,7 @@ class OfferManager(val nodeParams: NodeParams, val walletParams: WalletParams, v
amount: MilliSatoshi?,
description: String?,
pathId: ByteVector32?,
): Pair<OfferTypes.Offer, PrivateKey> {
): OfferTypes.OfferAndKey {
// We generate a deterministic session key based on:
// - a custom tag indicating that this is used in the Bolt 12 context
// - the offer parameters (amount, description and pathId)
Expand Down
Loading