Skip to content

Commit

Permalink
Smaller default offer (#648)
Browse files Browse the repository at this point in the history
Shaved 32 bytes from the default offer by removing the pathId.
  • Loading branch information
thomash-acinq authored May 24, 2024
1 parent 653373d commit e5b7364
Show file tree
Hide file tree
Showing 6 changed files with 18 additions and 26 deletions.
12 changes: 5 additions & 7 deletions src/commonMain/kotlin/fr/acinq/lightning/NodeParams.kt
Original file line number Diff line number Diff line change
Expand Up @@ -238,17 +238,15 @@ data class NodeParams(
/**
* We generate a default, deterministic Bolt 12 offer based on the node's seed and its trampoline node.
* This offer will stay valid after restoring the seed on a different device.
* We also return the path_id included in this offer, which should be used to route onion messages.
*/
fun defaultOffer(trampolineNode: NodeUri): Pair<ByteVector32, OfferTypes.Offer> {
// We generate a deterministic path_id based on:
fun defaultOffer(trampolineNode: NodeUri): OfferTypes.Offer {
// We generate a deterministic blindingSecret based on:
// - a custom tag indicating that this is used in the Bolt 12 context
// - our trampoline node, which is used as an introduction node for the offer's blinded path
// - our private key, which ensures that nobody else can generate the same path_id
val pathId = Crypto.sha256("bolt 12 default offer".toByteArray(Charsets.UTF_8) + trampolineNode.id.value.toByteArray() + nodePrivateKey.value.toByteArray()).byteVector32()
// - our private key, which ensures that nobody else can generate the same blindingSecret
val blindingSecret = PrivateKey(Crypto.sha256("bolt 12 default offer".toByteArray(Charsets.UTF_8) + trampolineNode.id.value.toByteArray() + nodePrivateKey.value.toByteArray()).byteVector32())
// We don't use our currently activated features, otherwise the offer would change when we add support for new features.
// If we add a new feature that we would like to use by default, we will need to explicitly create a new offer.
val offer = OfferTypes.Offer.createBlindedOffer(amount = null, description = null, this, trampolineNode, Features.empty, pathId)
return Pair(pathId, offer)
return OfferTypes.Offer.createBlindedOffer(amount = null, description = null, this, trampolineNode, Features.empty, blindingSecret)
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package fr.acinq.lightning.message

import fr.acinq.bitcoin.ByteVector
import fr.acinq.bitcoin.PrivateKey
import fr.acinq.bitcoin.PublicKey
import fr.acinq.bitcoin.byteVector
import fr.acinq.bitcoin.*
import fr.acinq.bitcoin.utils.Either
import fr.acinq.lightning.EncodedNodeId
import fr.acinq.lightning.ShortChannelId
Expand Down Expand Up @@ -175,7 +172,7 @@ object OnionMessages {
val nextMessage = OnionMessage(relayInfo.value.nextBlindingOverride ?: nextBlinding, decrypted.value.nextPacket)
decryptMessage(privateKey, nextMessage, logger)
}
decrypted.value.isLastPacket && relayInfo.value.pathId != null -> DecryptedMessage(message, blindedPrivateKey, relayInfo.value.pathId!!)
decrypted.value.isLastPacket -> DecryptedMessage(message, blindedPrivateKey, relayInfo.value.pathId ?: ByteVector32.Zeroes)
else -> {
logger.warning { "ignoring onion message for which we're not the destination (next_node_id=${relayInfo.value.nextNodeId}, path_id=${relayInfo.value.pathId?.toHex()})" }
null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,11 @@ class OfferManager(val nodeParams: NodeParams, val walletParams: WalletParams, v
private val localOffers: HashMap<ByteVector32, OfferTypes.Offer> = HashMap()

init {
val (defaultOfferPathId, defaultOffer) = nodeParams.defaultOffer(walletParams.trampolineNode)
registerOffer(defaultOffer, defaultOfferPathId)
registerOffer(nodeParams.defaultOffer(walletParams.trampolineNode), null)
}

fun registerOffer(offer: OfferTypes.Offer, pathId: ByteVector32) {
localOffers[pathId] = offer
fun registerOffer(offer: OfferTypes.Offer, pathId: ByteVector32?) {
localOffers[pathId ?: ByteVector32.Zeroes] = offer
}

/**
Expand Down
6 changes: 3 additions & 3 deletions src/commonMain/kotlin/fr/acinq/lightning/wire/OfferTypes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -773,20 +773,20 @@ object OfferTypes {
* @param nodeParams our node parameters.
* @param trampolineNode our trampoline node.
* @param features features that should be advertised in the offer.
* @param pathId pathId on which we will listen for invoice requests.
* @param blindingSecret session key used for the blinded path included in the offer.
*/
fun createBlindedOffer(
amount: MilliSatoshi?,
description: String?,
nodeParams: NodeParams,
trampolineNode: NodeUri,
features: Features,
pathId: ByteVector32,
blindingSecret: PrivateKey,
additionalTlvs: Set<OfferTlv> = setOf(),
customTlvs: Set<GenericTlv> = setOf()
): Offer {
if (description == null) require(amount == null) { "an offer description must be provided if the amount isn't null" }
val path = OnionMessages.buildRoute(PrivateKey(pathId), listOf(OnionMessages.IntermediateNode(trampolineNode.id, ShortChannelId.peerId(nodeParams.nodeId))), OnionMessages.Destination.Recipient(nodeParams.nodeId, pathId))
val path = OnionMessages.buildRoute(blindingSecret, listOf(OnionMessages.IntermediateNode(trampolineNode.id, ShortChannelId.peerId(nodeParams.nodeId))), OnionMessages.Destination.Recipient(nodeParams.nodeId, null))
val tlvs: Set<OfferTlv> = setOfNotNull(
if (nodeParams.chainHash != Block.LivenetGenesisBlock.hash) OfferChains(listOf(nodeParams.chainHash)) else null,
amount?.let { OfferAmount(it) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,16 @@ class OfferManagerTestsCommon : LightningTestSuite() {
}

private fun createOffer(offerManager: OfferManager, amount: MilliSatoshi? = null): OfferTypes.Offer {
val pathId = randomBytes32()
val blindingSecret = randomKey()
val offer = OfferTypes.Offer.createBlindedOffer(
amount,
"Blockaccino",
offerManager.nodeParams,
offerManager.walletParams.trampolineNode,
offerManager.nodeParams.features,
pathId,
blindingSecret,
)
offerManager.registerOffer(offer, pathId)
offerManager.registerOffer(offer, null)
return offer
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,7 @@ class OfferTypesTestsCommon : LightningTestSuite() {
fun `generate deterministic blinded offer through trampoline node`() {
val trampolineNode = NodeUri(PublicKey.fromHex("03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f"), "3.33.236.230", 9735)
val nodeParams = TestConstants.Alice.nodeParams.copy(chain = Chain.Mainnet)
val (pathId, offer) = nodeParams.defaultOffer(trampolineNode)
val offer = nodeParams.defaultOffer(trampolineNode)
assertNull(offer.amount)
assertNull(offer.description)
assertEquals(Features.empty, offer.features) // the offer shouldn't have any feature to guarantee stability
Expand All @@ -518,9 +518,7 @@ class OfferTypesTestsCommon : LightningTestSuite() {
val path = offer.contactInfos.first()
assertIs<BlindedPath>(path)
assertEquals(EncodedNodeId(trampolineNode.id), path.route.introductionNodeId)
val expectedPathId = ByteVector32.fromValidHex("69e2c45e00f6e76c50f612b87294191cc634abfbf25eb2eb51f241bec3209897")
assertEquals(expectedPathId, pathId)
val expectedOffer = Offer.decode("lno1zr2s8pjw7qjlm68mtp7e3yvxee4y5xrgjhhyf2fxhlphpckrvevh50u0qf70a6j2x2akrhazctejaaqr8y4qtzjtjzmfesay6mzr3s789uryuqsr8dpgfgxuk56vh7cl89769zdpdrkqwtypzhu2t8ehp73dqeeq65lsqxhq3x0946e6y8hgjpqh5ej0dxpnftcmerz4320cx3luqwlpkg8vdcqsfvz89axkmv5sgdysmwn95tpsct6mdercmz8jh2r82qpjkzq2t69g44vdaj2hxed33qeatadtw8vzj0ze2ezd98u5q4dp9993dkzy8jpr883ayzf95kzv0dvm2fe4").get()
val expectedOffer = Offer.decode("lno1zzes8pjw7qjlm68mtp7e3yvxee4y5xrgjhhyf2fxhlphpckrvevh50u0qf70a6j2x2akrhazctejaaqr8y4qtzjtjzmfesay6mzr3s789uryuqsr8dpgfgxuk56vh7cl89769zdpdrkqwtypzhu2t8ehp73dqeeq65lsqxhq3x0946e6y8hgjpqh5ej0dxpnftcmerz4320cx3luqwlpkg8vdcqsfvz89axkmv5sgdysmwn95tpsct6mdercmz8jh2r82qqscrf6uc3tse5gw5sv5xjdfw8f6c").get()
assertEquals(expectedOffer, offer)
}
}

0 comments on commit e5b7364

Please sign in to comment.