From 7cfdb96722d3fb0a7d17a4d0e614eabdbfd51e34 Mon Sep 17 00:00:00 2001 From: Thomas HUET <81159533+thomash-acinq@users.noreply.github.com> Date: Fri, 29 Nov 2024 16:21:17 +0100 Subject: [PATCH] Rename `blinding` to `pathKey` (#735) Rename some variable to align with the spec. --- .../kotlin/fr/acinq/lightning/NodeParams.kt | 8 +- .../acinq/lightning/crypto/RouteBlinding.kt | 76 +++++++++---------- .../acinq/lightning/json/JsonSerializers.kt | 14 ++-- .../acinq/lightning/message/OnionMessages.kt | 48 ++++++------ .../payment/IncomingPaymentPacket.kt | 24 +++--- .../acinq/lightning/payment/OfferManager.kt | 4 +- .../kotlin/fr/acinq/lightning/wire/HtlcTlv.kt | 20 ++--- .../acinq/lightning/wire/LightningMessages.kt | 26 +++---- .../fr/acinq/lightning/wire/MessageOnion.kt | 14 ++-- .../fr/acinq/lightning/wire/OfferTypes.kt | 22 +++--- .../fr/acinq/lightning/wire/PaymentOnion.kt | 22 +++--- .../fr/acinq/lightning/wire/RouteBlinding.kt | 16 ++-- .../crypto/sphinx/SphinxTestsCommon.kt | 28 +++---- .../message/OnionMessagesTestsCommon.kt | 38 +++++----- .../IncomingPaymentHandlerTestsCommon.kt | 20 ++--- .../payment/OfferManagerTestsCommon.kt | 18 ++--- .../payment/PaymentPacketTestsCommon.kt | 6 +- .../wire/LightningCodecsTestsCommon.kt | 8 +- .../lightning/wire/OfferTypesTestsCommon.kt | 10 +-- .../lightning/wire/PaymentOnionTestsCommon.kt | 2 +- .../wire/RouteBlindingTestsCommon.kt | 2 +- 21 files changed, 213 insertions(+), 213 deletions(-) diff --git a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/NodeParams.kt b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/NodeParams.kt index 94f519965..a088bee87 100644 --- a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/NodeParams.kt +++ b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/NodeParams.kt @@ -262,13 +262,13 @@ data class NodeParams( * @return the default offer and the private key that will sign invoices for this offer. */ fun defaultOffer(trampolineNodeId: PublicKey): Pair { - // We generate a deterministic blindingSecret based on: + // We generate a deterministic session key 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 blindingSecret - val blindingSecret = PrivateKey(Crypto.sha256("bolt 12 default offer".toByteArray(Charsets.UTF_8).byteVector() + trampolineNodeId.value + nodePrivateKey.value).byteVector32()) + // - our private key, which ensures that nobody else can generate the same path key secret + val sessionKey = PrivateKey(Crypto.sha256("bolt 12 default offer".toByteArray(Charsets.UTF_8).byteVector() + trampolineNodeId.value + nodePrivateKey.value).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. - return OfferTypes.Offer.createBlindedOffer(amount = null, description = null, this, trampolineNodeId, Features.empty, blindingSecret) + return OfferTypes.Offer.createBlindedOffer(amount = null, description = null, this, trampolineNodeId, Features.empty, sessionKey) } } diff --git a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/crypto/RouteBlinding.kt b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/crypto/RouteBlinding.kt index 938304873..8fdf9dae7 100644 --- a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/crypto/RouteBlinding.kt +++ b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/crypto/RouteBlinding.kt @@ -14,58 +14,58 @@ import fr.acinq.lightning.wire.OnionPaymentPayloadTlv object RouteBlinding { /** - * @param nodeId introduction node's id (which cannot be blinded since the sender need to find a route to it). + * @param nodeId first node's id (which cannot be blinded since the sender need to find a route to it). * @param blindedPublicKey blinded public key, which hides the real public key. - * @param blindingEphemeralKey blinding tweak that can be used by the receiving node to derive the private key that matches the blinded public key. - * @param encryptedPayload encrypted payload that can be decrypted with the introduction node's private key and the blinding ephemeral key. + * @param pathKey blinding tweak that can be used by the receiving node to derive the private key that matches the blinded public key. + * @param encryptedPayload encrypted payload that can be decrypted with the introduction node's private key and the path key. */ - data class IntroductionNode( + data class FirstHop( val nodeId: EncodedNodeId, val blindedPublicKey: PublicKey, - val blindingEphemeralKey: PublicKey, + val pathKey: PublicKey, val encryptedPayload: ByteVector ) /** * @param blindedPublicKey blinded public key, which hides the real public key. - * @param encryptedPayload encrypted payload that can be decrypted with the receiving node's private key and the blinding ephemeral key. + * @param encryptedPayload encrypted payload that can be decrypted with the receiving node's private key and the path key. */ - data class BlindedNode(val blindedPublicKey: PublicKey, val encryptedPayload: ByteVector) + data class BlindedHop(val blindedPublicKey: PublicKey, val encryptedPayload: ByteVector) /** - * @param introductionNodeId the first node, not blinded so that the sender can locate it. - * @param blindingKey blinding tweak that can be used by the introduction node to derive the private key that matches the blinded public key. - * @param blindedNodes blinded nodes (including the introduction node). + * @param firstNodeId the first node, not blinded so that the sender can locate it. + * @param firstPathKey blinding tweak that can be used by the introduction node to derive the private key that matches the blinded public key. + * @param blindedHops blinded nodes (including the introduction node). */ data class BlindedRoute( - val introductionNodeId: EncodedNodeId, - val blindingKey: PublicKey, - val blindedNodes: List + val firstNodeId: EncodedNodeId, + val firstPathKey: PublicKey, + val blindedHops: List ) { - val introductionNode: IntroductionNode = IntroductionNode( - introductionNodeId, - blindedNodes.first().blindedPublicKey, - blindingKey, - blindedNodes.first().encryptedPayload + val firstHop: FirstHop = FirstHop( + firstNodeId, + blindedHops.first().blindedPublicKey, + firstPathKey, + blindedHops.first().encryptedPayload ) - val subsequentNodes: List = blindedNodes.drop(1) - val blindedNodeIds: List = blindedNodes.map { it.blindedPublicKey } - val encryptedPayloads: List = blindedNodes.map { it.encryptedPayload } + val subsequentHops: List = blindedHops.drop(1) + val blindedNodeIds: List = blindedHops.map { it.blindedPublicKey } + val encryptedPayloads: List = blindedHops.map { it.encryptedPayload } } /** * @param route blinded route. - * @param lastBlinding blinding point for the last node, which can be used to derive the blinded private key. + * @param lastPathKey path key for the last node, which can be used to derive the blinded private key. */ - data class BlindedRouteDetails(val route: BlindedRoute, val lastBlinding: PublicKey) { + data class BlindedRouteDetails(val route: BlindedRoute, val lastPathKey: PublicKey) { /** @param nodeKey private key associated with our non-blinded node_id. */ - fun blindedPrivateKey(nodeKey: PrivateKey): PrivateKey = derivePrivateKey(nodeKey, lastBlinding) + fun blindedPrivateKey(nodeKey: PrivateKey): PrivateKey = derivePrivateKey(nodeKey, lastPathKey) } /** * Blind the provided route and encrypt intermediate nodes' payloads. * - * @param sessionKey this node's session key. + * @param sessionKey session key of the blinded path, the corresponding public key will be the first path key. * @param publicKeys public keys of each node on the route, starting from the introduction point. * @param payloads payloads that should be encrypted for each node on the route. * @return a blinded route. @@ -73,9 +73,9 @@ object RouteBlinding { fun create(sessionKey: PrivateKey, publicKeys: List, payloads: List): BlindedRouteDetails { require(publicKeys.size == payloads.size) { "a payload must be provided for each node in the blinded path" } var e = sessionKey - val (blindedHops, blindingKeys) = publicKeys.zip(payloads).map { pair -> + val (blindedHops, pathKeys) = publicKeys.zip(payloads).map { pair -> val (publicKey, payload) = pair - val blindingKey = e.publicKey() + val pathKey = e.publicKey() val sharedSecret = Sphinx.computeSharedSecret(publicKey, e) val blindedPublicKey = Sphinx.blind(publicKey, Sphinx.generateKey("blinded_node_id", sharedSecret)) val rho = Sphinx.generateKey("rho", sharedSecret) @@ -85,21 +85,21 @@ object RouteBlinding { payload.toByteArray(), byteArrayOf() ) - e *= PrivateKey(Crypto.sha256(blindingKey.value.toByteArray() + sharedSecret.toByteArray())) - Pair(BlindedNode(blindedPublicKey, ByteVector(encryptedPayload + mac)), blindingKey) + e *= PrivateKey(Crypto.sha256(pathKey.value.toByteArray() + sharedSecret.toByteArray())) + Pair(BlindedHop(blindedPublicKey, ByteVector(encryptedPayload + mac)), pathKey) }.unzip() - return BlindedRouteDetails(BlindedRoute(EncodedNodeId(publicKeys.first()), blindingKeys.first(), blindedHops), blindingKeys.last()) + return BlindedRouteDetails(BlindedRoute(EncodedNodeId(publicKeys.first()), pathKeys.first(), blindedHops), pathKeys.last()) } /** * Compute the blinded private key that must be used to decrypt an incoming blinded onion. * * @param privateKey this node's private key. - * @param blindingEphemeralKey unblinding ephemeral key. + * @param pathKey unblinding ephemeral key. * @return this node's blinded private key. */ - fun derivePrivateKey(privateKey: PrivateKey, blindingEphemeralKey: PublicKey): PrivateKey { - val sharedSecret = Sphinx.computeSharedSecret(blindingEphemeralKey, privateKey) + fun derivePrivateKey(privateKey: PrivateKey, pathKey: PublicKey): PrivateKey { + val sharedSecret = Sphinx.computeSharedSecret(pathKey, privateKey) return privateKey * PrivateKey(Sphinx.generateKey("blinded_node_id", sharedSecret)) } @@ -107,16 +107,16 @@ object RouteBlinding { * Decrypt the encrypted payload (usually found in the onion) that contains instructions to locate the next node. * * @param privateKey this node's private key. - * @param blindingEphemeralKey unblinding ephemeral key. + * @param pathKey unblinding ephemeral key. * @param encryptedPayload encrypted payload for this node. * @return a tuple (decrypted payload, unblinding ephemeral key for the next node) */ fun decryptPayload( privateKey: PrivateKey, - blindingEphemeralKey: PublicKey, + pathKey: PublicKey, encryptedPayload: ByteVector ): Either> { - val sharedSecret = Sphinx.computeSharedSecret(blindingEphemeralKey, privateKey) + val sharedSecret = Sphinx.computeSharedSecret(pathKey, privateKey) val rho = Sphinx.generateKey("rho", sharedSecret) return try { val decrypted = ChaCha20Poly1305.decrypt( @@ -126,8 +126,8 @@ object RouteBlinding { byteArrayOf(), encryptedPayload.takeRight(16).toByteArray() ) - val nextBlindingEphemeralKey = Sphinx.blind(blindingEphemeralKey, Sphinx.computeBlindingFactor(blindingEphemeralKey, sharedSecret)) - Either.Right(Pair(ByteVector(decrypted), nextBlindingEphemeralKey)) + val nextPathKey = Sphinx.blind(pathKey, Sphinx.computeBlindingFactor(pathKey, sharedSecret)) + Either.Right(Pair(ByteVector(decrypted), nextPathKey)) } catch (_: Throwable) { Either.Left(CannotDecodeTlv(OnionPaymentPayloadTlv.EncryptedRecipientData.tag)) } diff --git a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/json/JsonSerializers.kt b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/json/JsonSerializers.kt index 20ba6aac0..00f3ac999 100644 --- a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/json/JsonSerializers.kt +++ b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/json/JsonSerializers.kt @@ -49,7 +49,7 @@ JsonSerializers.FundingSignedSerializer::class, JsonSerializers.UpdateAddHtlcSerializer::class, JsonSerializers.UpdateAddHtlcTlvSerializer::class, - JsonSerializers.UpdateAddHtlcTlvBlindingSerializer::class, + JsonSerializers.UpdateAddHtlcTlvPathKeySerializer::class, JsonSerializers.UpdateFulfillHtlcSerializer::class, JsonSerializers.UpdateFailHtlcSerializer::class, JsonSerializers.UpdateFailMalformedHtlcSerializer::class, @@ -98,7 +98,7 @@ JsonSerializers.Bolt11UnknownTagSerializer::class, JsonSerializers.Bolt11InvalidTagSerializer::class, JsonSerializers.EncodedNodeIdSerializer::class, - JsonSerializers.BlindedNodeSerializer::class, + JsonSerializers.BlindedHopSerializer::class, JsonSerializers.BlindedRouteSerializer::class, ) @file:UseContextualSerialization( @@ -209,7 +209,7 @@ object JsonSerializers { subclass(CommitSigTlv.Batch::class, CommitSigTlvBatchSerializer) subclass(ShutdownTlv.ChannelData::class, ShutdownTlvChannelDataSerializer) subclass(ClosingSignedTlv.FeeRange::class, ClosingSignedTlvFeeRangeSerializer) - subclass(UpdateAddHtlcTlv.Blinding::class, UpdateAddHtlcTlvBlindingSerializer) + subclass(UpdateAddHtlcTlv.PathKey::class, UpdateAddHtlcTlvPathKeySerializer) } // TODO The following declarations are required because serializers for [TransactionWithInputInfo] // depend themselves on @Contextual serializers. Once we get rid of v2/v3 serialization and we @@ -476,8 +476,8 @@ object JsonSerializers { @Serializer(forClass = EncryptedChannelData::class) object EncryptedChannelDataSerializer - @Serializer(forClass = UpdateAddHtlcTlv.Blinding::class) - object UpdateAddHtlcTlvBlindingSerializer + @Serializer(forClass = UpdateAddHtlcTlv.PathKey::class) + object UpdateAddHtlcTlvPathKeySerializer @Serializer(forClass = UpdateAddHtlcTlv::class) object UpdateAddHtlcTlvSerializer @@ -649,8 +649,8 @@ object JsonSerializers { delegateSerializer = EncodedNodeIdSurrogate.serializer() ) - @Serializer(forClass = RouteBlinding.BlindedNode::class) - object BlindedNodeSerializer + @Serializer(forClass = RouteBlinding.BlindedHop::class) + object BlindedHopSerializer @Serializer(forClass = RouteBlinding.BlindedRoute::class) object BlindedRouteSerializer diff --git a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/message/OnionMessages.kt b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/message/OnionMessages.kt index 9fca8f654..e5cb0aa62 100644 --- a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/message/OnionMessages.kt +++ b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/message/OnionMessages.kt @@ -12,10 +12,10 @@ import fr.acinq.lightning.wire.* object OnionMessages { data class IntermediateNode(val nodeId: EncodedNodeId.WithPublicKey, val outgoingChannelId: ShortChannelId? = null, val padding: ByteVector? = null, val customTlvs: Set = setOf()) { - fun toTlvStream(nextNodeId: EncodedNodeId, nextBlinding: PublicKey? = null): TlvStream { + fun toTlvStream(nextNodeId: EncodedNodeId, nextPathKey: PublicKey? = null): TlvStream { val tlvs = setOfNotNull( outgoingChannelId?.let { RouteBlindingEncryptedDataTlv.OutgoingChannelId(it) } ?: RouteBlindingEncryptedDataTlv.OutgoingNodeId(nextNodeId), - nextBlinding?.let { RouteBlindingEncryptedDataTlv.NextBlinding(it) }, + nextPathKey?.let { RouteBlindingEncryptedDataTlv.NextPathKey(it) }, padding?.let { RouteBlindingEncryptedDataTlv.Padding(it) }, ) return TlvStream(tlvs, customTlvs) @@ -38,7 +38,7 @@ object OnionMessages { private fun buildIntermediatePayloads( intermediateNodes: List, lastNodeId: EncodedNodeId, - lastBlinding: PublicKey? = null + lastPathKey: PublicKey? = null ): List { return if (intermediateNodes.isEmpty()) { listOf() @@ -46,14 +46,14 @@ object OnionMessages { val intermediatePayloads = intermediateNodes.dropLast(1).zip(intermediateNodes.drop(1)).map { (current, next) -> current.toTlvStream(next.nodeId) } - // The last intermediate node may contain a blinding override when the recipient is hidden behind a blinded path. - val lastPayload = intermediateNodes.last().toTlvStream(lastNodeId, lastBlinding) + // The last intermediate node may contain a path key override when the recipient is hidden behind a blinded path. + val lastPayload = intermediateNodes.last().toTlvStream(lastNodeId, lastPathKey) (intermediatePayloads + lastPayload).map { RouteBlindingEncryptedData(it).write().byteVector() } } } fun buildRouteToRecipient( - blindingSecret: PrivateKey, + sessionKey: PrivateKey, intermediateNodes: List, recipient: Destination.Recipient ): RouteBlinding. BlindedRouteDetails { @@ -64,20 +64,20 @@ object OnionMessages { ) val lastPayload = RouteBlindingEncryptedData(TlvStream(tlvs, recipient.customTlvs)).write().toByteVector() return RouteBlinding.create( - blindingSecret, + sessionKey, intermediateNodes.map { it.nodeId.publicKey } + recipient.nodeId.publicKey, intermediatePayloads + lastPayload ) } fun buildRoute( - blindingSecret: PrivateKey, + sessionKey: PrivateKey, intermediateNodes: List, destination: Destination ): RouteBlinding.BlindedRoute { return when (destination) { is Destination.Recipient -> { - buildRouteToRecipient(blindingSecret, intermediateNodes, destination).route + buildRouteToRecipient(sessionKey, intermediateNodes, destination).route } is Destination.BlindedPath -> when { intermediateNodes.isEmpty() -> destination.route @@ -85,18 +85,18 @@ object OnionMessages { // We concatenate our blinded path with the destination's blinded path. val intermediatePayloads = buildIntermediatePayloads( intermediateNodes, - destination.route.introductionNodeId, - destination.route.blindingKey + destination.route.firstNodeId, + destination.route.firstPathKey ) val routePrefix = RouteBlinding.create( - blindingSecret, + sessionKey, intermediateNodes.map { it.nodeId.publicKey }, intermediatePayloads ).route RouteBlinding.BlindedRoute( - routePrefix.introductionNodeId, - routePrefix.blindingKey, - routePrefix.blindedNodes + destination.route.blindedNodes + routePrefix.firstNodeId, + routePrefix.firstPathKey, + routePrefix.blindedHops + destination.route.blindedHops ) } } @@ -110,19 +110,19 @@ object OnionMessages { * Builds an encrypted onion containing a message that should be relayed to the destination. * * @param sessionKey a random key to encrypt the onion. - * @param blindingSecret a random key to create the blinded path. + * @param blindedPathSessionKey a random key to create the blinded path. * @param intermediateNodes list of intermediate nodes between us and the destination (can be empty if we want to contact the destination directly). * @param destination the destination of this message, can be a node id or a blinded route. * @param content list of TLVs to send to the recipient of the message. */ fun buildMessage( sessionKey: PrivateKey, - blindingSecret: PrivateKey, + blindedPathSessionKey: PrivateKey, intermediateNodes: List, destination: Destination, content: TlvStream ): Either { - val route = buildRoute(blindingSecret, intermediateNodes, destination) + val route = buildRoute(blindedPathSessionKey, intermediateNodes, destination) val payloads = buildList { // Intermediate nodes only receive blinded path relay information. addAll(route.encryptedPayloads.dropLast(1).map { MessageOnion(TlvStream(OnionMessagePayloadTlv.EncryptedData(it))).write() }) @@ -139,12 +139,12 @@ object OnionMessages { // Since we are setting the packet size based on the payload, the onion creation should never fail. val packet = Sphinx.create( sessionKey, - route.blindedNodes.map { it.blindedPublicKey }, + route.blindedHops.map { it.blindedPublicKey }, payloads, associatedData = null, packetSize ).packet - return Either.Right(OnionMessage(route.blindingKey, packet)) + return Either.Right(OnionMessage(route.firstPathKey, packet)) } /** @@ -155,7 +155,7 @@ object OnionMessages { data class DecryptedMessage(val content: MessageOnion, val blindedPrivateKey: PrivateKey, val pathId: ByteVector) fun decryptMessage(privateKey: PrivateKey, msg: OnionMessage, logger: MDCLogger): DecryptedMessage? { - val blindedPrivateKey = RouteBlinding.derivePrivateKey(privateKey, msg.blindingKey) + val blindedPrivateKey = RouteBlinding.derivePrivateKey(privateKey, msg.pathKey) return when (val decrypted = Sphinx.peel(blindedPrivateKey, associatedData = ByteVector.empty, msg.onionRoutingPacket)) { is Either.Right -> { val message = try { @@ -164,13 +164,13 @@ object OnionMessages { logger.warning { "ignoring onion message that couldn't be decoded: ${e.message}" } return null } - when (val payload = RouteBlinding.decryptPayload(privateKey, msg.blindingKey, message.encryptedData)) { + when (val payload = RouteBlinding.decryptPayload(privateKey, msg.pathKey, message.encryptedData)) { is Either.Left -> { logger.warning { "ignoring onion message that couldn't be decrypted: ${payload.value}" } null } is Either.Right -> { - val (decryptedPayload, nextBlinding) = payload.value + val (decryptedPayload, nextPathKey) = payload.value when (val relayInfo = RouteBlindingEncryptedData.read(decryptedPayload.toByteArray())) { is Either.Left -> { logger.warning { "ignoring onion message with invalid relay info: ${relayInfo.value}" } @@ -179,7 +179,7 @@ object OnionMessages { is Either.Right -> when { !decrypted.value.isLastPacket && relayInfo.value.nextNodeId == EncodedNodeId.WithPublicKey.Wallet(privateKey.publicKey()) -> { // We may add ourselves to the route several times at the end to hide the real length of the route. - val nextMessage = OnionMessage(relayInfo.value.nextBlindingOverride ?: nextBlinding, decrypted.value.nextPacket) + val nextMessage = OnionMessage(relayInfo.value.nextPathKeyOverride ?: nextPathKey, decrypted.value.nextPacket) decryptMessage(privateKey, nextMessage, logger) } decrypted.value.isLastPacket -> DecryptedMessage(message, blindedPrivateKey, relayInfo.value.pathId ?: ByteVector32.Zeroes) diff --git a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/payment/IncomingPaymentPacket.kt b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/payment/IncomingPaymentPacket.kt index ee709a8a6..63e1d99ec 100644 --- a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/payment/IncomingPaymentPacket.kt +++ b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/payment/IncomingPaymentPacket.kt @@ -40,9 +40,9 @@ object IncomingPaymentPacket { val htlcAmount = add.fold({ it.amount }, { it.amountMsat + (it.fundingFee?.amount ?: 0.msat) }) val htlcExpiry = add.fold({ it.expiry }, { it.cltvExpiry }) val paymentHash = add.fold({ it.paymentHash }, { it.paymentHash }) - val blinding = add.fold({ it.blinding }, { it.blinding }) + val pathKey = add.fold({ it.pathKey }, { it.pathKey }) val onion = add.fold({ it.finalPacket }, { it.onionRoutingPacket }) - return decryptOnion(paymentHash, onion, privateKey, blinding).flatMap { outer -> + return decryptOnion(paymentHash, onion, privateKey, pathKey).flatMap { outer -> when (outer) { is PaymentOnion.FinalPayload.Standard -> when (val trampolineOnion = outer.records.get()) { @@ -63,24 +63,24 @@ object IncomingPaymentPacket { } } - private fun decryptOnion(paymentHash: ByteVector32, packet: OnionRoutingPacket, privateKey: PrivateKey, blinding: PublicKey?): Either { - val onionDecryptionKey = blinding?.let { RouteBlinding.derivePrivateKey(privateKey, it) } ?: privateKey + private fun decryptOnion(paymentHash: ByteVector32, packet: OnionRoutingPacket, privateKey: PrivateKey, pathKey: PublicKey?): Either { + val onionDecryptionKey = pathKey?.let { RouteBlinding.derivePrivateKey(privateKey, it) } ?: privateKey return Sphinx.peel(onionDecryptionKey, paymentHash, packet).flatMap { decrypted -> when { !decrypted.isLastPacket -> Either.Left(UnknownNextPeer) else -> PaymentOnion.PerHopPayload.read(decrypted.payload.toByteArray()).flatMap { tlvs -> when (val encryptedRecipientData = tlvs.get()?.data) { null -> when { - blinding != null -> Either.Left(InvalidOnionBlinding(hash(packet))) - tlvs.get() != null -> Either.Left(InvalidOnionBlinding(hash(packet))) + pathKey != null -> Either.Left(InvalidOnionBlinding(hash(packet))) + tlvs.get() != null -> Either.Left(InvalidOnionBlinding(hash(packet))) else -> PaymentOnion.FinalPayload.Standard.read(decrypted.payload) } else -> when { // We're never the introduction node of a blinded path, since we don't have public channels. - tlvs.get() != null -> Either.Left(InvalidOnionBlinding(hash(packet))) - // We must receive the blinding point to be able to decrypt the blinded path data. - blinding == null -> Either.Left(InvalidOnionBlinding(hash(packet))) - else -> when (val payload = decryptRecipientData(privateKey, blinding, encryptedRecipientData, tlvs)) { + tlvs.get() != null -> Either.Left(InvalidOnionBlinding(hash(packet))) + // We must receive the path key to be able to decrypt the blinded path data. + pathKey == null -> Either.Left(InvalidOnionBlinding(hash(packet))) + else -> when (val payload = decryptRecipientData(privateKey, pathKey, encryptedRecipientData, tlvs)) { is Either.Left -> Either.Left(InvalidOnionBlinding(hash(packet))) is Either.Right -> Either.Right(payload.value) } @@ -91,8 +91,8 @@ object IncomingPaymentPacket { } } - private fun decryptRecipientData(privateKey: PrivateKey, blinding: PublicKey, data: ByteVector, tlvs: TlvStream): Either { - return RouteBlinding.decryptPayload(privateKey, blinding, data) + private fun decryptRecipientData(privateKey: PrivateKey, pathKey: PublicKey, data: ByteVector, tlvs: TlvStream): Either { + return RouteBlinding.decryptPayload(privateKey, pathKey, data) .flatMap { (decryptedRecipientData, _) -> RouteBlindingEncryptedData.read(decryptedRecipientData.toByteArray()) } .flatMap { blindedTlvs -> PaymentOnion.FinalPayload.Blinded.validate(tlvs, blindedTlvs) } } diff --git a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/payment/OfferManager.kt b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/payment/OfferManager.kt index 88bf50421..7814a49ff 100644 --- a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/payment/OfferManager.kt +++ b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/payment/OfferManager.kt @@ -201,7 +201,7 @@ class OfferManager(val nodeParams: NodeParams, val walletParams: WalletParams, v sendInvoiceError("failed to build onion message", decrypted.content.replyPath) } is Right -> { - logger.info { "sending BOLT 12 invoice with amount=${invoice.amount}, paymentHash=${invoice.paymentHash}, payerId=${invoice.invoiceRequest.payerId} to introduction node ${destination.route.introductionNodeId}" } + logger.info { "sending BOLT 12 invoice with amount=${invoice.amount}, paymentHash=${invoice.paymentHash}, payerId=${invoice.invoiceRequest.payerId} to introduction node ${destination.route.firstNodeId}" } OnionMessageAction.SendMessage(invoiceMessage.value) } } @@ -220,7 +220,7 @@ class OfferManager(val nodeParams: NodeParams, val walletParams: WalletParams, v /** If our trampoline node is the introduction node, we don't need an intermediate encryption step. */ private fun intermediateNodes(destination: Destination): List { val needIntermediateHop = when (destination) { - is Destination.BlindedPath -> when (val introduction = destination.route.introductionNodeId) { + is Destination.BlindedPath -> when (val introduction = destination.route.firstNodeId) { is EncodedNodeId.WithPublicKey.Plain -> introduction.publicKey != remoteNodeId is EncodedNodeId.WithPublicKey.Wallet -> true is EncodedNodeId.ShortChannelIdDir -> true // we don't have access to the graph data and rely on our peer to resolve the scid diff --git a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/HtlcTlv.kt b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/HtlcTlv.kt index 56f49b9b7..2ff273a94 100644 --- a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/HtlcTlv.kt +++ b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/HtlcTlv.kt @@ -8,15 +8,15 @@ import fr.acinq.bitcoin.io.Output import fr.acinq.lightning.utils.msat sealed class UpdateAddHtlcTlv : Tlv { - /** Blinding ephemeral public key that should be used to derive shared secrets when using route blinding. */ - data class Blinding(val publicKey: PublicKey) : UpdateAddHtlcTlv() { - override val tag: Long get() = Blinding.tag + /** Path key that should be used to derive shared secrets when using route blinding. */ + data class PathKey(val publicKey: PublicKey) : UpdateAddHtlcTlv() { + override val tag: Long get() = PathKey.tag override fun write(out: Output) = LightningCodecs.writeBytes(publicKey.value, out) - companion object : TlvValueReader { + companion object : TlvValueReader { const val tag: Long = 0 - override fun read(input: Input): Blinding = Blinding(PublicKey(LightningCodecs.bytes(input, 33))) + override fun read(input: Input): PathKey = PathKey(PublicKey(LightningCodecs.bytes(input, 33))) } } @@ -42,15 +42,15 @@ sealed class UpdateAddHtlcTlv : Tlv { } sealed class WillAddHtlcTlv : Tlv { - /** Blinding ephemeral public key that should be used to derive shared secrets when using route blinding. */ - data class Blinding(val publicKey: PublicKey) : WillAddHtlcTlv() { - override val tag: Long get() = Blinding.tag + /** Path key that should be used to derive shared secrets when using route blinding. */ + data class PathKey(val publicKey: PublicKey) : WillAddHtlcTlv() { + override val tag: Long get() = PathKey.tag override fun write(out: Output) = LightningCodecs.writeBytes(publicKey.value, out) - companion object : TlvValueReader { + companion object : TlvValueReader { const val tag: Long = 0 - override fun read(input: Input): Blinding = Blinding(PublicKey(LightningCodecs.bytes(input, 33))) + override fun read(input: Input): PathKey = PathKey(PublicKey(LightningCodecs.bytes(input, 33))) } } } \ No newline at end of file diff --git a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/LightningMessages.kt b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/LightningMessages.kt index 608e0715e..b022baa9b 100644 --- a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/LightningMessages.kt +++ b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/LightningMessages.kt @@ -1083,7 +1083,7 @@ data class UpdateAddHtlc( ) : HtlcMessage, UpdateMessage, HasChannelId, ForbiddenMessageDuringSplice { override val type: Long get() = UpdateAddHtlc.type - val blinding: PublicKey? = tlvStream.get()?.publicKey + val pathKey: PublicKey? = tlvStream.get()?.publicKey val fundingFee: LiquidityAds.FundingFee? = tlvStream.get()?.fee val usesOnTheFlyFunding: Boolean = fundingFee != null @@ -1102,7 +1102,7 @@ data class UpdateAddHtlc( @Suppress("UNCHECKED_CAST") private val readers = mapOf( - UpdateAddHtlcTlv.Blinding.tag to UpdateAddHtlcTlv.Blinding as TlvValueReader, + UpdateAddHtlcTlv.PathKey.tag to UpdateAddHtlcTlv.PathKey as TlvValueReader, UpdateAddHtlcTlv.FundingFeeTlv.tag to UpdateAddHtlcTlv.FundingFeeTlv as TlvValueReader, ) @@ -1124,11 +1124,11 @@ data class UpdateAddHtlc( paymentHash: ByteVector32, cltvExpiry: CltvExpiry, onionRoutingPacket: OnionRoutingPacket, - blinding: PublicKey?, + pathKey: PublicKey?, fundingFee: LiquidityAds.FundingFee? ): UpdateAddHtlc { val tlvs = setOfNotNull( - blinding?.let { UpdateAddHtlcTlv.Blinding(it) }, + pathKey?.let { UpdateAddHtlcTlv.PathKey(it) }, fundingFee?.let { UpdateAddHtlcTlv.FundingFeeTlv(it) } ) return UpdateAddHtlc(channelId, id, amountMsat, paymentHash, cltvExpiry, onionRoutingPacket, TlvStream(tlvs)) @@ -1613,27 +1613,27 @@ data class ClosingSigned( } data class OnionMessage( - val blindingKey: PublicKey, + val pathKey: PublicKey, val onionRoutingPacket: OnionRoutingPacket ) : LightningMessage { override val type: Long get() = OnionMessage.type override fun write(out: Output) { - LightningCodecs.writeBytes(blindingKey.value, out) + LightningCodecs.writeBytes(pathKey.value, out) LightningCodecs.writeU16(onionRoutingPacket.payload.size() + 66, out) OnionRoutingPacketSerializer(onionRoutingPacket.payload.size()).write(onionRoutingPacket, out) } override fun toString(): String = - "OnionMessage(blindingKey=$blindingKey, onionRoutingPacket=OnionRoutingPacket(version=${onionRoutingPacket.version}, publicKey=${onionRoutingPacket.publicKey.toHex()}, payload=<${onionRoutingPacket.payload.size()} bytes>, hmac=${onionRoutingPacket.hmac.toHex()}))" + "OnionMessage(pathKey=$pathKey, onionRoutingPacket=OnionRoutingPacket(version=${onionRoutingPacket.version}, publicKey=${onionRoutingPacket.publicKey.toHex()}, payload=<${onionRoutingPacket.payload.size()} bytes>, hmac=${onionRoutingPacket.hmac.toHex()}))" companion object : LightningMessageReader { const val type: Long = 513 override fun read(input: Input): OnionMessage { - val blindingKey = PublicKey(LightningCodecs.bytes(input, 33)) + val pathKey = PublicKey(LightningCodecs.bytes(input, 33)) val onion = OnionRoutingPacketSerializer(LightningCodecs.u16(input) - 66).read(input) - return OnionMessage(blindingKey, onion) + return OnionMessage(pathKey, onion) } } } @@ -1655,7 +1655,7 @@ data class WillAddHtlc( ) : OnTheFlyFundingMessage, HasChainHash { override val type: Long get() = WillAddHtlc.type - val blinding: PublicKey? = tlvStream.get()?.publicKey + val pathKey: PublicKey? = tlvStream.get()?.publicKey override fun write(out: Output) { LightningCodecs.writeBytes(chainHash.value, out) @@ -1672,7 +1672,7 @@ data class WillAddHtlc( @Suppress("UNCHECKED_CAST") private val readers = mapOf( - WillAddHtlcTlv.Blinding.tag to WillAddHtlcTlv.Blinding as TlvValueReader, + WillAddHtlcTlv.PathKey.tag to WillAddHtlcTlv.PathKey as TlvValueReader, ) override fun read(input: Input): WillAddHtlc = WillAddHtlc( @@ -1692,9 +1692,9 @@ data class WillAddHtlc( paymentHash: ByteVector32, cltvExpiry: CltvExpiry, onionRoutingPacket: OnionRoutingPacket, - blinding: PublicKey? + pathKey: PublicKey? ): WillAddHtlc { - val tlvStream = TlvStream(setOfNotNull(blinding?.let { WillAddHtlcTlv.Blinding(it) })) + val tlvStream = TlvStream(setOfNotNull(pathKey?.let { WillAddHtlcTlv.PathKey(it) })) return WillAddHtlc(chainHash, id, amount, paymentHash, cltvExpiry, onionRoutingPacket, tlvStream) } } diff --git a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/MessageOnion.kt b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/MessageOnion.kt index 465b467e8..e188fbd24 100644 --- a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/MessageOnion.kt +++ b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/MessageOnion.kt @@ -16,10 +16,10 @@ sealed class OnionMessagePayloadTlv : Tlv { data class ReplyPath(val blindedRoute: RouteBlinding.BlindedRoute) : OnionMessagePayloadTlv() { override val tag: Long get() = ReplyPath.tag override fun write(out: Output) { - LightningCodecs.writeEncodedNodeId(blindedRoute.introductionNodeId, out) - LightningCodecs.writeBytes(blindedRoute.blindingKey.value, out) - LightningCodecs.writeByte(blindedRoute.blindedNodes.size, out) - for (hop in blindedRoute.blindedNodes) { + LightningCodecs.writeEncodedNodeId(blindedRoute.firstNodeId, out) + LightningCodecs.writeBytes(blindedRoute.firstPathKey.value, out) + LightningCodecs.writeByte(blindedRoute.blindedHops.size, out) + for (hop in blindedRoute.blindedHops) { LightningCodecs.writeBytes(hop.blindedPublicKey.value, out) LightningCodecs.writeU16(hop.encryptedPayload.size(), out) LightningCodecs.writeBytes(hop.encryptedPayload, out) @@ -30,14 +30,14 @@ sealed class OnionMessagePayloadTlv : Tlv { const val tag: Long = 2 override fun read(input: Input): ReplyPath { val firstNodeId = LightningCodecs.encodedNodeId(input) - val blinding = PublicKey(LightningCodecs.bytes(input, 33)) + val pathKey = PublicKey(LightningCodecs.bytes(input, 33)) val numHops = LightningCodecs.byte(input) val path = (0 until numHops).map { val blindedPublicKey = PublicKey(LightningCodecs.bytes(input, 33)) val encryptedPayload = ByteVector(LightningCodecs.bytes(input, LightningCodecs.u16(input))) - RouteBlinding.BlindedNode(blindedPublicKey, encryptedPayload) + RouteBlinding.BlindedHop(blindedPublicKey, encryptedPayload) } - return ReplyPath(RouteBlinding.BlindedRoute(firstNodeId, blinding, path)) + return ReplyPath(RouteBlinding.BlindedRoute(firstNodeId, pathKey, path)) } } } diff --git a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/OfferTypes.kt b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/OfferTypes.kt index 55d8704a7..99da2d80f 100644 --- a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/OfferTypes.kt +++ b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/OfferTypes.kt @@ -33,10 +33,10 @@ object OfferTypes { } fun writePath(path: ContactInfo.BlindedPath, out: Output) { - LightningCodecs.writeEncodedNodeId(path.route.introductionNodeId, out) - LightningCodecs.writeBytes(path.route.blindingKey.value, out) - LightningCodecs.writeByte(path.route.blindedNodes.size, out) - for (node in path.route.blindedNodes) { + LightningCodecs.writeEncodedNodeId(path.route.firstNodeId, out) + LightningCodecs.writeBytes(path.route.firstPathKey.value, out) + LightningCodecs.writeByte(path.route.blindedHops.size, out) + for (node in path.route.blindedHops) { LightningCodecs.writeBytes(node.blindedPublicKey.value, out) LightningCodecs.writeU16(node.encryptedPayload.size(), out) LightningCodecs.writeBytes(node.encryptedPayload, out) @@ -45,15 +45,15 @@ object OfferTypes { fun readPath(input: Input): ContactInfo.BlindedPath { val introductionNodeId = LightningCodecs.encodedNodeId(input) - val blindingKey = PublicKey(LightningCodecs.bytes(input, 33)) - val blindedNodes = ArrayList() + val pathKey = PublicKey(LightningCodecs.bytes(input, 33)) + val blindedNodes = ArrayList() val numBlindedNodes = LightningCodecs.byte(input) for (i in 1..numBlindedNodes) { val blindedKey = PublicKey(LightningCodecs.bytes(input, 33)) val payload = ByteVector(LightningCodecs.bytes(input, LightningCodecs.u16(input))) - blindedNodes.add(RouteBlinding.BlindedNode(blindedKey, payload)) + blindedNodes.add(RouteBlinding.BlindedHop(blindedKey, payload)) } - return ContactInfo.BlindedPath(RouteBlinding.BlindedRoute(introductionNodeId, blindingKey, blindedNodes)) + return ContactInfo.BlindedPath(RouteBlinding.BlindedRoute(introductionNodeId, pathKey, blindedNodes)) } sealed class Bolt12Tlv : Tlv @@ -786,7 +786,7 @@ object OfferTypes { * @param nodeParams our node parameters. * @param trampolineNodeId our trampoline node. * @param features features that should be advertised in the offer. - * @param blindingSecret session key used for the blinded path included in the offer. + * @param blindedPathSessionKey session key used for the blinded path included in the offer, the corresponding public key will be the first path key. */ fun createBlindedOffer( amount: MilliSatoshi?, @@ -794,12 +794,12 @@ object OfferTypes { nodeParams: NodeParams, trampolineNodeId: PublicKey, features: Features, - blindingSecret: PrivateKey, + blindedPathSessionKey: PrivateKey, additionalTlvs: Set = setOf(), customTlvs: Set = setOf() ): Pair { if (description == null) require(amount == null) { "an offer description must be provided if the amount isn't null" } - val blindedRouteDetails = OnionMessages.buildRouteToRecipient(blindingSecret, listOf(OnionMessages.IntermediateNode(EncodedNodeId.WithPublicKey.Plain(trampolineNodeId))), OnionMessages.Destination.Recipient(EncodedNodeId.WithPublicKey.Wallet(nodeParams.nodeId), null)) + val blindedRouteDetails = OnionMessages.buildRouteToRecipient(blindedPathSessionKey, listOf(OnionMessages.IntermediateNode(EncodedNodeId.WithPublicKey.Plain(trampolineNodeId))), OnionMessages.Destination.Recipient(EncodedNodeId.WithPublicKey.Wallet(nodeParams.nodeId), null)) val tlvs: Set = setOfNotNull( if (nodeParams.chainHash != Block.LivenetGenesisBlock.hash) OfferChains(listOf(nodeParams.chainHash)) else null, amount?.let { OfferAmount(it) }, diff --git a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/PaymentOnion.kt b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/PaymentOnion.kt index 73c651239..51ad212b9 100644 --- a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/PaymentOnion.kt +++ b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/PaymentOnion.kt @@ -85,16 +85,16 @@ sealed class OnionPaymentPayloadTlv : Tlv { } } - /** Blinding ephemeral public key for the introduction node of a blinded route. */ - data class BlindingPoint(val publicKey: PublicKey) : OnionPaymentPayloadTlv() { - override val tag: Long get() = BlindingPoint.tag + /** Path key for the introduction node of a blinded route. */ + data class PathKey(val publicKey: PublicKey) : OnionPaymentPayloadTlv() { + override val tag: Long get() = PathKey.tag override fun write(out: Output) { LightningCodecs.writeBytes(publicKey.value, out) } - companion object : TlvValueReader { + companion object : TlvValueReader { const val tag: Long = 12 - override fun read(input: Input): BlindingPoint = BlindingPoint(PublicKey(LightningCodecs.bytes(input, 33))) + override fun read(input: Input): PathKey = PathKey(PublicKey(LightningCodecs.bytes(input, 33))) } } @@ -251,7 +251,7 @@ object PaymentOnion { OnionPaymentPayloadTlv.OutgoingChannelId.tag to OnionPaymentPayloadTlv.OutgoingChannelId.Companion as TlvValueReader, OnionPaymentPayloadTlv.PaymentData.tag to OnionPaymentPayloadTlv.PaymentData.Companion as TlvValueReader, OnionPaymentPayloadTlv.EncryptedRecipientData.tag to OnionPaymentPayloadTlv.EncryptedRecipientData.Companion as TlvValueReader, - OnionPaymentPayloadTlv.BlindingPoint.tag to OnionPaymentPayloadTlv.BlindingPoint.Companion as TlvValueReader, + OnionPaymentPayloadTlv.PathKey.tag to OnionPaymentPayloadTlv.PathKey.Companion as TlvValueReader, OnionPaymentPayloadTlv.PaymentMetadata.tag to OnionPaymentPayloadTlv.PaymentMetadata.Companion as TlvValueReader, OnionPaymentPayloadTlv.TotalAmount.tag to OnionPaymentPayloadTlv.TotalAmount.Companion as TlvValueReader, OnionPaymentPayloadTlv.InvoiceFeatures.tag to OnionPaymentPayloadTlv.InvoiceFeatures.Companion as TlvValueReader, @@ -375,12 +375,12 @@ object PaymentOnion { companion object { fun validate(records: TlvStream, blindedRecords: RouteBlindingEncryptedData): Either { - // Bolt 4: MUST return an error if the payload contains other tlv fields than `encrypted_recipient_data`, `current_blinding_point`, `amt_to_forward`, `outgoing_cltv_value` and `total_amount_msat`. + // Bolt 4: MUST return an error if the payload contains other tlv fields than `encrypted_recipient_data`, `current_path_key`, `amt_to_forward`, `outgoing_cltv_value` and `total_amount_msat`. val allowed = setOf( OnionPaymentPayloadTlv.AmountToForward.tag, OnionPaymentPayloadTlv.OutgoingCltv.tag, OnionPaymentPayloadTlv.EncryptedRecipientData.tag, - OnionPaymentPayloadTlv.BlindingPoint.tag, + OnionPaymentPayloadTlv.PathKey.tag, OnionPaymentPayloadTlv.TotalAmount.tag, ) return when { @@ -446,7 +446,7 @@ object PaymentOnion { tlvs.get() == null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.OutgoingCltv.tag, 0)) tlvs.get() == null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.OutgoingNodeId.tag, 0)) tlvs.get() != null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.EncryptedRecipientData.tag, 0)) - tlvs.get() != null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.BlindingPoint.tag, 0)) + tlvs.get() != null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.PathKey.tag, 0)) else -> Either.Right(NodeRelayPayload(tlvs)) } } @@ -489,7 +489,7 @@ object PaymentOnion { tlvs.get() == null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.InvoiceFeatures.tag, 0)) tlvs.get() == null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.InvoiceRoutingInfo.tag, 0)) tlvs.get() != null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.EncryptedRecipientData.tag, 0)) - tlvs.get() != null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.BlindingPoint.tag, 0)) + tlvs.get() != null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.PathKey.tag, 0)) else -> Either.Right(RelayToNonTrampolinePayload(tlvs)) } } @@ -529,7 +529,7 @@ object PaymentOnion { tlvs.get() == null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.InvoiceFeatures.tag, 0)) tlvs.get() == null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.OutgoingBlindedPaths.tag, 0)) tlvs.get() != null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.EncryptedRecipientData.tag, 0)) - tlvs.get() != null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.BlindingPoint.tag, 0)) + tlvs.get() != null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.PathKey.tag, 0)) else -> Either.Right(RelayToBlindedPayload(tlvs)) } } diff --git a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/RouteBlinding.kt b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/RouteBlinding.kt index bfdfddfbd..99c989f62 100644 --- a/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/RouteBlinding.kt +++ b/modules/core/src/commonMain/kotlin/fr/acinq/lightning/wire/RouteBlinding.kt @@ -59,14 +59,14 @@ sealed class RouteBlindingEncryptedDataTlv : Tlv { } } - /** Blinding override for the rest of the route. */ - data class NextBlinding(val blinding: PublicKey) : RouteBlindingEncryptedDataTlv() { - override val tag: Long get() = NextBlinding.tag - override fun write(out: Output) = LightningCodecs.writeBytes(blinding.value, out) + /** Path key override for the rest of the route. */ + data class NextPathKey(val pathKey: PublicKey) : RouteBlindingEncryptedDataTlv() { + override val tag: Long get() = NextPathKey.tag + override fun write(out: Output) = LightningCodecs.writeBytes(pathKey.value, out) - companion object : TlvValueReader { + companion object : TlvValueReader { const val tag: Long = 8 - override fun read(input: Input): NextBlinding = NextBlinding(PublicKey(LightningCodecs.bytes(input, 33))) + override fun read(input: Input): NextPathKey = NextPathKey(PublicKey(LightningCodecs.bytes(input, 33))) } } @@ -127,7 +127,7 @@ data class RouteBlindingEncryptedData(val records: TlvStream()?.nodeId val outgoingChannelId = records.get()?.shortChannelId val pathId = records.get()?.data - val nextBlindingOverride = records.get()?.blinding + val nextPathKeyOverride = records.get()?.pathKey val paymentConstraints = records.get() val allowedFeatures: Features = records.get()?.features ?: Features.empty @@ -146,7 +146,7 @@ data class RouteBlindingEncryptedData(val records: TlvStream, RouteBlindingEncryptedDataTlv.OutgoingNodeId.tag to RouteBlindingEncryptedDataTlv.OutgoingNodeId as TlvValueReader, RouteBlindingEncryptedDataTlv.PathId.tag to RouteBlindingEncryptedDataTlv.PathId as TlvValueReader, - RouteBlindingEncryptedDataTlv.NextBlinding.tag to RouteBlindingEncryptedDataTlv.NextBlinding as TlvValueReader, + RouteBlindingEncryptedDataTlv.NextPathKey.tag to RouteBlindingEncryptedDataTlv.NextPathKey as TlvValueReader, RouteBlindingEncryptedDataTlv.PaymentRelay.tag to RouteBlindingEncryptedDataTlv.PaymentRelay as TlvValueReader, RouteBlindingEncryptedDataTlv.PaymentConstraints.tag to RouteBlindingEncryptedDataTlv.PaymentConstraints as TlvValueReader, RouteBlindingEncryptedDataTlv.AllowedFeatures.tag to RouteBlindingEncryptedDataTlv.AllowedFeatures as TlvValueReader, diff --git a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/crypto/sphinx/SphinxTestsCommon.kt b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/crypto/sphinx/SphinxTestsCommon.kt index 188196f57..1aa54ceb7 100644 --- a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/crypto/sphinx/SphinxTestsCommon.kt +++ b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/crypto/sphinx/SphinxTestsCommon.kt @@ -585,11 +585,11 @@ class SphinxTestsCommon : LightningTestSuite() { fun `create blinded route -- reference test vector`() { val sessionKey = PrivateKey(ByteVector32("0101010101010101010101010101010101010101010101010101010101010101")) val (blindedRoute, lastBlinding) = RouteBlinding.create(sessionKey, publicKeys, routeBlindingPayloads) - assertEquals(blindedRoute.introductionNode.nodeId, EncodedNodeId(publicKeys[0])) - assertEquals(blindedRoute.introductionNodeId, EncodedNodeId(publicKeys[0])) - assertEquals(blindedRoute.introductionNode.blindedPublicKey, PublicKey.fromHex("02ec68ed555f5d18b12fe0e2208563c3566032967cf11dc29b20c345449f9a50a2")) - assertEquals(blindedRoute.introductionNode.blindingEphemeralKey, PublicKey.fromHex("031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f")) - assertEquals(blindedRoute.introductionNode.encryptedPayload, ByteVector("af4fbf67bd52520bdfab6a88cd4e7f22ffad08d8b153b17ff303f93fdb4712")) + assertEquals(blindedRoute.firstHop.nodeId, EncodedNodeId(publicKeys[0])) + assertEquals(blindedRoute.firstNodeId, EncodedNodeId(publicKeys[0])) + assertEquals(blindedRoute.firstHop.blindedPublicKey, PublicKey.fromHex("02ec68ed555f5d18b12fe0e2208563c3566032967cf11dc29b20c345449f9a50a2")) + assertEquals(blindedRoute.firstHop.pathKey, PublicKey.fromHex("031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f")) + assertEquals(blindedRoute.firstHop.encryptedPayload, ByteVector("af4fbf67bd52520bdfab6a88cd4e7f22ffad08d8b153b17ff303f93fdb4712")) assertEquals( blindedRoute.blindedNodeIds, listOf( PublicKey.fromHex("02ec68ed555f5d18b12fe0e2208563c3566032967cf11dc29b20c345449f9a50a2"), @@ -600,16 +600,16 @@ class SphinxTestsCommon : LightningTestSuite() { ) ) assertEquals( - blindedRoute.subsequentNodes.map { it.blindedPublicKey }, listOf( + blindedRoute.subsequentHops.map { it.blindedPublicKey }, listOf( PublicKey.fromHex("022b09d77fb3374ee3ed9d2153e15e9962944ad1690327cbb0a9acb7d90f168763"), PublicKey.fromHex("03d9f889364dc5a173460a2a6cc565b4ca78931792115dd6ef82c0e18ced837372"), PublicKey.fromHex("03bfddd2253b42fe12edd37f9071a3883830ed61a4bc347eeac63421629cf032b5"), PublicKey.fromHex("03a8588bc4a0a2f0d2fb8d5c0f8d062fb4d78bfba24a85d0ddeb4fd35dd3b34110"), ) ) - assertEquals(blindedRoute.encryptedPayloads, listOf(blindedRoute.introductionNode.encryptedPayload) + blindedRoute.subsequentNodes.map { it.encryptedPayload }) + assertEquals(blindedRoute.encryptedPayloads, listOf(blindedRoute.firstHop.encryptedPayload) + blindedRoute.subsequentHops.map { it.encryptedPayload }) assertEquals( - blindedRoute.subsequentNodes.map { it.encryptedPayload }, listOf( + blindedRoute.subsequentHops.map { it.encryptedPayload }, listOf( ByteVector("146c9694ead7de2a54fc43e8bb927bfc377dda7ed5a2e36b327b739e368aa602e43e07e14bfb81d66e1e295f848b6f15ee6483005abb830f4ef08a9da6"), ByteVector("8ad7d5d448f15208417a1840f82274101b3c254c24b1b49fd676fd0c4293c9aa66ed51da52579e934a869f016f213044d1b13b63bf586e9c9832106b59"), ByteVector("52a45a884542d180e76fe84fc13e71a01f65d943ff89aed29b94644a91b037b9143cfda8f1ff25ba61c37108a5ae57d9ddc5ab688ee8b2f9f6bd94522c"), @@ -619,7 +619,7 @@ class SphinxTestsCommon : LightningTestSuite() { assertEquals(blindedRoute.blindedNodeIds.last(), RouteBlinding.derivePrivateKey(privKeys.last(), lastBlinding).publicKey()) // The introduction point can decrypt its encrypted payload and obtain the next ephemeral public key. - val (payload0, ephKey1) = RouteBlinding.decryptPayload(privKeys[0], blindedRoute.introductionNode.blindingEphemeralKey, blindedRoute.encryptedPayloads[0]).right!! + val (payload0, ephKey1) = RouteBlinding.decryptPayload(privKeys[0], blindedRoute.firstHop.pathKey, blindedRoute.encryptedPayloads[0]).right!! assertEquals(payload0, routeBlindingPayloads[0]) assertEquals(ephKey1, PublicKey.fromHex("035cb4c003d58e16cc9207270b3596c2be3309eca64c36b208c946bbb599bfcad0")) @@ -658,8 +658,8 @@ class SphinxTestsCommon : LightningTestSuite() { ByteVector("010f000000000000000000000000000000 061000112233445566778899aabbccddeeff") ) val blindedRoute = RouteBlinding.create(sessionKey, publicKeys.drop(2), payloads).route - assertEquals(blindedRoute.blindingKey, PublicKey.fromHex("031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f")) - Triple(blindedRoute.blindingKey, blindedRoute, payloads) + assertEquals(blindedRoute.firstPathKey, PublicKey.fromHex("031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f")) + Triple(blindedRoute.firstPathKey, blindedRoute, payloads) } // The sender also wants to use route blinding to reach the introduction point. val (blindedRouteStart, payloadsStart) = run { @@ -671,8 +671,8 @@ class SphinxTestsCommon : LightningTestSuite() { ) Pair(RouteBlinding.create(sessionKey, publicKeys.take(2), payloads).route, payloads) } - val blindedRoute = RouteBlinding.BlindedRoute(EncodedNodeId(publicKeys[0]), blindedRouteStart.blindingKey, blindedRouteStart.blindedNodes + blindedRouteEnd.blindedNodes) - assertEquals(blindedRoute.blindingKey, PublicKey.fromHex("024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766")) + val blindedRoute = RouteBlinding.BlindedRoute(EncodedNodeId(publicKeys[0]), blindedRouteStart.firstPathKey, blindedRouteStart.blindedHops + blindedRouteEnd.blindedHops) + assertEquals(blindedRoute.firstPathKey, PublicKey.fromHex("024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766")) assertEquals( blindedRoute.blindedNodeIds, listOf( PublicKey.fromHex("0303176d13958a8a59d59517a6223e12cf291ba5f65c8011efcdca0a52c3850abc"), @@ -693,7 +693,7 @@ class SphinxTestsCommon : LightningTestSuite() { ) // The introduction point can decrypt its encrypted payload and obtain the next ephemeral public key. - val (payload0, ephKey1) = RouteBlinding.decryptPayload(privKeys[0], blindedRoute.blindingKey, blindedRoute.encryptedPayloads[0]).right!! + val (payload0, ephKey1) = RouteBlinding.decryptPayload(privKeys[0], blindedRoute.firstPathKey, blindedRoute.encryptedPayloads[0]).right!! assertEquals(payload0, payloadsStart[0]) assertEquals(ephKey1, PublicKey.fromHex("02be4b436dbc6cfa43d7d5652bc630ffdaf0dac93e6682db7950828506055ad1a7")) diff --git a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/message/OnionMessagesTestsCommon.kt b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/message/OnionMessagesTestsCommon.kt index 421a7c111..ba6ce101d 100644 --- a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/message/OnionMessagesTestsCommon.kt +++ b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/message/OnionMessagesTestsCommon.kt @@ -27,7 +27,7 @@ class OnionMessagesTestsCommon { val logger: MDCLogger = MDCLogger(testLoggerFactory.newLogger(this::class)) private fun relayMessage(privateKey: PrivateKey, msg: OnionMessage): Pair, OnionMessage> { - val blindedPrivateKey = RouteBlinding.derivePrivateKey(privateKey, msg.blindingKey) + val blindedPrivateKey = RouteBlinding.derivePrivateKey(privateKey, msg.pathKey) val decrypted = Sphinx.peel( blindedPrivateKey, ByteVector.empty, @@ -36,14 +36,14 @@ class OnionMessagesTestsCommon { val message = MessageOnion.read(decrypted.payload.toByteArray()) val (decryptedPayload, nextBlinding) = RouteBlinding.decryptPayload( privateKey, - msg.blindingKey, + msg.pathKey, message.encryptedData ).right!! val relayInfo = RouteBlindingEncryptedData.read(decryptedPayload.toByteArray()).right!! assertFalse(decrypted.isLastPacket) return Pair( relayInfo.nextNodeId?.let { Either.Right(it) } ?: Either.Left(relayInfo.outgoingChannelId!!), - OnionMessage(relayInfo.nextBlindingOverride ?: nextBlinding, decrypted.nextPacket) + OnionMessage(relayInfo.nextPathKeyOverride ?: nextBlinding, decrypted.nextPacket) ) } @@ -81,7 +81,7 @@ class OnionMessagesTestsCommon { val messageForAlice = RouteBlindingEncryptedData(TlvStream(RouteBlindingEncryptedDataTlv.OutgoingNodeId(EncodedNodeId(bob.publicKey())))) val encodedForAlice = messageForAlice.write().toByteVector() assertEquals("04210324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c", encodedForAlice.toHex()) - val messageForBob = RouteBlindingEncryptedData(TlvStream(RouteBlindingEncryptedDataTlv.OutgoingNodeId(EncodedNodeId(carol.publicKey())), RouteBlindingEncryptedDataTlv.NextBlinding(blindingOverride.publicKey()))) + val messageForBob = RouteBlindingEncryptedData(TlvStream(RouteBlindingEncryptedDataTlv.OutgoingNodeId(EncodedNodeId(carol.publicKey())), RouteBlindingEncryptedDataTlv.NextPathKey(blindingOverride.publicKey()))) val encodedForBob = messageForBob.write().toByteVector() assertEquals("0421027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007082102989c0b76cb563971fdc9bef31ec06c3560f3249d6ee9e5d83c57625596e05f6f", encodedForBob.toHex()) val messageForCarol = RouteBlindingEncryptedData(TlvStream(RouteBlindingEncryptedDataTlv.Padding(ByteVector.fromHex("0000000000000000000000000000000000000000000000000000000000000000000000")), RouteBlindingEncryptedDataTlv.OutgoingNodeId(EncodedNodeId(dave.publicKey())))) @@ -97,7 +97,7 @@ class OnionMessagesTestsCommon { // Building blinded path Alice -> Bob val routeToCarol = RouteBlinding.create(blindingSecret, listOf(alice.publicKey(), bob.publicKey()), listOf(encodedForAlice, encodedForBob)).route - val publicKeys = routeToCarol.blindedNodes.map { it.blindedPublicKey } + routeFromCarol.blindedNodes.map { it.blindedPublicKey } + val publicKeys = routeToCarol.blindedHops.map { it.blindedPublicKey } + routeFromCarol.blindedHops.map { it.blindedPublicKey } val encryptedPayloads = routeToCarol.encryptedPayloads + routeFromCarol.encryptedPayloads val payloads = encryptedPayloads.map { MessageOnion.tlvSerializer.write(TlvStream(OnionMessagePayloadTlv.EncryptedData(it))) } val expectedPayloads = listOf( @@ -150,10 +150,10 @@ class OnionMessagesTestsCommon { val encodedBlindedPayload = blindedPayload.write().toByteVector() assertEquals("04210324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c", encodedBlindedPayload.toHex()) val blindedRoute = RouteBlinding.create(blindingSecret, listOf(alice.publicKey()), listOf(encodedBlindedPayload)).route - assertEquals(blindedAlice, blindedRoute.blindedNodes.first().blindedPublicKey) + assertEquals(blindedAlice, blindedRoute.blindedHops.first().blindedPublicKey) assertEquals("bae3d9ea2b06efd1b7b9b49b6cdcaad0e789474a6939ffa54ff5ec9224d5b76c", Crypto.sha256(blindingKey.value + sharedSecret).toHex()) - assertEquals("6970e870b473ddbc27e3098bfa45bb1aa54f1f637f803d957e6271d8ffeba89da2665d62123763d9b634e30714144a1c165ac9", blindedRoute.blindedNodes.first().encryptedPayload.toHex()) - val decryptedPayload = RouteBlindingEncryptedData.read(RouteBlinding.decryptPayload(alice, blindingKey, blindedRoute.blindedNodes.first().encryptedPayload).right!!.first.toByteArray()).right!! + assertEquals("6970e870b473ddbc27e3098bfa45bb1aa54f1f637f803d957e6271d8ffeba89da2665d62123763d9b634e30714144a1c165ac9", blindedRoute.blindedHops.first().encryptedPayload.toHex()) + val decryptedPayload = RouteBlindingEncryptedData.read(RouteBlinding.decryptPayload(alice, blindingKey, blindedRoute.blindedHops.first().encryptedPayload).right!!.first.toByteArray()).right!! assertEquals(blindedPayload, decryptedPayload) } @@ -170,16 +170,16 @@ class OnionMessagesTestsCommon { assertEquals("8074773a3745818b0d97dd875023486cc35e7afd95f5e9ec1363f517979e8373", Sphinx.mac("blinded_node_id".encodeToByteArray().toByteVector(), sharedSecret).toHex()) val blindedBob = PublicKey.fromHex("026ea8e36f78e038c659beba9229699796127471d9c7a24a0308533371fd63ad48") val blindingOverride = PrivateKey.fromHex("070707070707070707070707070707070707070707070707070707070707070701").publicKey() - val blindedPayload = RouteBlindingEncryptedData(TlvStream(RouteBlindingEncryptedDataTlv.OutgoingNodeId(EncodedNodeId(carol.publicKey())), RouteBlindingEncryptedDataTlv.NextBlinding(blindingOverride))) + val blindedPayload = RouteBlindingEncryptedData(TlvStream(RouteBlindingEncryptedDataTlv.OutgoingNodeId(EncodedNodeId(carol.publicKey())), RouteBlindingEncryptedDataTlv.NextPathKey(blindingOverride))) val encodedBlindedPayload = blindedPayload.write().toByteVector() assertEquals("0421027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007082102989c0b76cb563971fdc9bef31ec06c3560f3249d6ee9e5d83c57625596e05f6f", encodedBlindedPayload.toHex()) val blindedRoute = RouteBlinding.create(blindingSecret, listOf(bob.publicKey()), listOf(encodedBlindedPayload)).route - assertEquals(blindedBob, blindedRoute.blindedNodes.first().blindedPublicKey) + assertEquals(blindedBob, blindedRoute.blindedHops.first().blindedPublicKey) assertEquals("9afb8b2ebc174dcf9e270be24771da7796542398d29d4ff6a4e7b6b4b9205cfe", Crypto.sha256(blindingKey.value + sharedSecret).toHex()) - assertEquals("1630da85e8759b8f3b94d74a539c6f0d870a87cf03d4986175865a2985553c997b560c32613bd9184c1a6d41a37027aabdab5433009d8409a1b638eb90373778a05716af2c2140b3196dca23997cdad4cfa7a7adc8d4", blindedRoute.blindedNodes.first().encryptedPayload.toHex()) - val decryptedPayload = RouteBlindingEncryptedData.read(RouteBlinding.decryptPayload(bob, blindingKey, blindedRoute.blindedNodes.first().encryptedPayload).right!!.first.toByteArray()).right!! + assertEquals("1630da85e8759b8f3b94d74a539c6f0d870a87cf03d4986175865a2985553c997b560c32613bd9184c1a6d41a37027aabdab5433009d8409a1b638eb90373778a05716af2c2140b3196dca23997cdad4cfa7a7adc8d4", blindedRoute.blindedHops.first().encryptedPayload.toHex()) + val decryptedPayload = RouteBlindingEncryptedData.read(RouteBlinding.decryptPayload(bob, blindingKey, blindedRoute.blindedHops.first().encryptedPayload).right!!.first.toByteArray()).right!! assertEquals(blindedPayload, decryptedPayload) - assertEquals(blindingOverride, decryptedPayload.nextBlindingOverride) + assertEquals(blindingOverride, decryptedPayload.nextPathKeyOverride) } @Test @@ -198,10 +198,10 @@ class OnionMessagesTestsCommon { val encodedBlindedPayload = blindedPayload.write().toByteVector() assertEquals("012300000000000000000000000000000000000000000000000000000000000000000000000421032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991", encodedBlindedPayload.toHex()) val blindedRoute = RouteBlinding.create(blindingSecret, listOf(carol.publicKey()), listOf(encodedBlindedPayload)).route - assertEquals(blindedCarol, blindedRoute.blindedNodes.first().blindedPublicKey) + assertEquals(blindedCarol, blindedRoute.blindedHops.first().blindedPublicKey) assertEquals("cc3b918cda6b1b049bdbe469c4dd952935e7c1518dd9c7ed0cd2cd5bc2742b82", Crypto.sha256(blindingKey.value + sharedSecret).toHex()) - assertEquals("8285acbceb37dfb38b877a888900539be656233cd74a55c55344fb068f9d8da365340d21db96fb41b76123207daeafdfb1f571e3fea07a22e10da35f03109a0380b3c69fcbed9c698086671809658761cf65ecbc3c07a2e5", blindedRoute.blindedNodes.first().encryptedPayload.toHex()) - val decryptedPayload = RouteBlindingEncryptedData.read(RouteBlinding.decryptPayload(carol, blindingKey, blindedRoute.blindedNodes.first().encryptedPayload).right!!.first.toByteArray()).right!! + assertEquals("8285acbceb37dfb38b877a888900539be656233cd74a55c55344fb068f9d8da365340d21db96fb41b76123207daeafdfb1f571e3fea07a22e10da35f03109a0380b3c69fcbed9c698086671809658761cf65ecbc3c07a2e5", blindedRoute.blindedHops.first().encryptedPayload.toHex()) + val decryptedPayload = RouteBlindingEncryptedData.read(RouteBlinding.decryptPayload(carol, blindingKey, blindedRoute.blindedHops.first().encryptedPayload).right!!.first.toByteArray()).right!! assertEquals(blindedPayload, decryptedPayload) } @@ -212,10 +212,10 @@ class OnionMessagesTestsCommon { val blindingOverride = randomKey() val destination = randomKey() val replyPath = buildRoute(blindingOverride, listOf(IntermediateNode(EncodedNodeId(destination.publicKey()))), Recipient(EncodedNodeId(destination.publicKey()), pathId = ByteVector.fromHex("01234567"))) - assertEquals(blindingOverride.publicKey(), replyPath.blindingKey) - assertEquals(EncodedNodeId(destination.publicKey()), replyPath.introductionNodeId) + assertEquals(blindingOverride.publicKey(), replyPath.firstPathKey) + assertEquals(EncodedNodeId(destination.publicKey()), replyPath.firstNodeId) val message = buildMessage(sessionKey, blindingSecret, listOf(), BlindedPath(replyPath), TlvStream.empty()).right!! - assertEquals(blindingOverride.publicKey(), message.blindingKey) // blindingSecret was not used as the replyPath was used as is + assertEquals(blindingOverride.publicKey(), message.pathKey) // blindingSecret was not used as the replyPath was used as is val (nextNodeId, message2) = relayMessage(destination, message) assertEquals(Either.Right(EncodedNodeId(destination.publicKey())), nextNodeId) diff --git a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/payment/IncomingPaymentHandlerTestsCommon.kt b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/payment/IncomingPaymentHandlerTestsCommon.kt index cb0ee6790..0e2da9fe4 100644 --- a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/payment/IncomingPaymentHandlerTestsCommon.kt +++ b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/payment/IncomingPaymentHandlerTestsCommon.kt @@ -1573,7 +1573,7 @@ class IncomingPaymentHandlerTestsCommon : LightningTestSuite() { val paymentHash = Crypto.sha256(preimage).toByteVector32() val cltvExpiry = TestConstants.Bob.nodeParams.minFinalCltvExpiryDelta.toCltvExpiry(TestConstants.defaultBlockHeight.toLong()) val (finalPayload, route) = makeBlindedPayload(TestConstants.Bob.nodeParams.nodeId, defaultAmount, defaultAmount, cltvExpiry, preimage = preimage) - val add = makeUpdateAddHtlc(8, randomBytes32(), paymentHandler, paymentHash, finalPayload, route.blindingKey) + val add = makeUpdateAddHtlc(8, randomBytes32(), paymentHandler, paymentHash, finalPayload, route.firstPathKey) val result = paymentHandler.process(add, Features.empty, TestConstants.defaultBlockHeight, TestConstants.feeratePerKw, remoteFundingRates = null) assertIs(result) @@ -1602,7 +1602,7 @@ class IncomingPaymentHandlerTestsCommon : LightningTestSuite() { // - Bob doesn't accept the MPP set yet run { val (finalPayload, route) = makeBlindedPayload(TestConstants.Bob.nodeParams.nodeId, amount1, totalAmount, cltvExpiry, preimage = preimage) - val add = makeUpdateAddHtlc(0, channelId, paymentHandler, paymentHash, finalPayload, route.blindingKey) + val add = makeUpdateAddHtlc(0, channelId, paymentHandler, paymentHash, finalPayload, route.firstPathKey) val result = paymentHandler.process(add, Features.empty, TestConstants.defaultBlockHeight, TestConstants.feeratePerKw, remoteFundingRates = null) assertIs(result) assertNull(result.incomingPayment.received) @@ -1614,7 +1614,7 @@ class IncomingPaymentHandlerTestsCommon : LightningTestSuite() { // - Bob now accepts the MPP set run { val (finalPayload, route) = makeBlindedPayload(TestConstants.Bob.nodeParams.nodeId, amount2, totalAmount, cltvExpiry, preimage = preimage) - val add = makeUpdateAddHtlc(1, channelId, paymentHandler, paymentHash, finalPayload, route.blindingKey) + val add = makeUpdateAddHtlc(1, channelId, paymentHandler, paymentHash, finalPayload, route.firstPathKey) val result = paymentHandler.process(add, Features.empty, TestConstants.defaultBlockHeight, TestConstants.feeratePerKw, remoteFundingRates = null) assertIs(result) val (expectedActions, expectedReceivedWith) = setOf( @@ -1637,7 +1637,7 @@ class IncomingPaymentHandlerTestsCommon : LightningTestSuite() { val paymentHash = Crypto.sha256(preimage).toByteVector32() val cltvExpiry = TestConstants.Bob.nodeParams.minFinalCltvExpiryDelta.toCltvExpiry(TestConstants.defaultBlockHeight.toLong()) val (finalPayload, route) = makeBlindedPayload(TestConstants.Bob.nodeParams.nodeId, defaultAmount, defaultAmount, cltvExpiry, preimage = preimage) - val willAddHtlc = makeWillAddHtlc(paymentHandler, paymentHash, finalPayload, route.blindingKey) + val willAddHtlc = makeWillAddHtlc(paymentHandler, paymentHash, finalPayload, route.firstPathKey) val result = paymentHandler.process(willAddHtlc, Features.empty, TestConstants.defaultBlockHeight, TestConstants.feeratePerKw, TestConstants.fundingRates) assertIs(result) assertEquals(1, result.actions.size) @@ -1672,7 +1672,7 @@ class IncomingPaymentHandlerTestsCommon : LightningTestSuite() { run { val cltvExpiry = TestConstants.Bob.nodeParams.fulfillSafetyBeforeTimeoutBlocks.toCltvExpiry(TestConstants.defaultBlockHeight.toLong()) val (finalPayload, route) = makeBlindedPayload(TestConstants.Bob.nodeParams.nodeId, defaultAmount, defaultAmount, cltvExpiry, preimage = preimage) - val add = makeUpdateAddHtlc(0, randomBytes32(), paymentHandler, paymentHash, finalPayload, route.blindingKey, payment.fundingFee) + val add = makeUpdateAddHtlc(0, randomBytes32(), paymentHandler, paymentHash, finalPayload, route.firstPathKey, payment.fundingFee) val result = paymentHandler.process(add, Features.empty, TestConstants.defaultBlockHeight, TestConstants.feeratePerKw, TestConstants.fundingRates) assertIs(result) } @@ -1680,7 +1680,7 @@ class IncomingPaymentHandlerTestsCommon : LightningTestSuite() { assertTrue((TestConstants.Bob.nodeParams.fulfillSafetyBeforeTimeoutBlocks * 2) < TestConstants.Bob.nodeParams.minFinalCltvExpiryDelta) val cltvExpiry = (TestConstants.Bob.nodeParams.fulfillSafetyBeforeTimeoutBlocks * 2).toCltvExpiry(TestConstants.defaultBlockHeight.toLong()) val (finalPayload, route) = makeBlindedPayload(TestConstants.Bob.nodeParams.nodeId, defaultAmount, defaultAmount, cltvExpiry, preimage = preimage) - val add = makeUpdateAddHtlc(0, randomBytes32(), paymentHandler, paymentHash, finalPayload, route.blindingKey, payment.fundingFee) + val add = makeUpdateAddHtlc(0, randomBytes32(), paymentHandler, paymentHash, finalPayload, route.firstPathKey, payment.fundingFee) val result = paymentHandler.process(add, Features.empty, TestConstants.defaultBlockHeight, TestConstants.feeratePerKw, TestConstants.fundingRates) assertIs(result) val fulfill = ChannelCommand.Htlc.Settlement.Fulfill(add.id, preimage, commit = true) @@ -1698,7 +1698,7 @@ class IncomingPaymentHandlerTestsCommon : LightningTestSuite() { val (paymentHandler, incomingPayment, _) = createFixture(defaultAmount) val cltvExpiry = TestConstants.Bob.nodeParams.minFinalCltvExpiryDelta.toCltvExpiry(TestConstants.defaultBlockHeight.toLong()) val (blindedPayload, route) = makeBlindedPayload(TestConstants.Bob.nodeParams.nodeId, defaultAmount, defaultAmount, cltvExpiry, preimage = incomingPayment.preimage) - val add = makeUpdateAddHtlc(8, randomBytes32(), paymentHandler, incomingPayment.paymentHash, blindedPayload, route.blindingKey) + val add = makeUpdateAddHtlc(8, randomBytes32(), paymentHandler, incomingPayment.paymentHash, blindedPayload, route.firstPathKey) val result = paymentHandler.process(add, Features.empty, TestConstants.defaultBlockHeight, TestConstants.feeratePerKw, remoteFundingRates = null) assertIs(result) @@ -1722,7 +1722,7 @@ class IncomingPaymentHandlerTestsCommon : LightningTestSuite() { // - Bob doesn't accept the MPP set yet run { val (finalPayload, route) = makeBlindedPayload(TestConstants.Bob.nodeParams.nodeId, amount1, totalAmount, cltvExpiry, preimage = preimage) - val add = makeUpdateAddHtlc(0, channelId, paymentHandler, paymentHash, finalPayload, route.blindingKey) + val add = makeUpdateAddHtlc(0, channelId, paymentHandler, paymentHash, finalPayload, route.firstPathKey) val result = paymentHandler.process(add, Features.empty, TestConstants.defaultBlockHeight, TestConstants.feeratePerKw, remoteFundingRates = null) assertIs(result) assertNull(result.incomingPayment.received) @@ -1749,7 +1749,7 @@ class IncomingPaymentHandlerTestsCommon : LightningTestSuite() { val pathId = metadata.toPathId(TestConstants.Bob.nodeParams.nodePrivateKey) val amountTooLow = metadata.amount - 10_000_000.msat val (finalPayload, route) = makeBlindedPayload(TestConstants.Bob.nodeParams.nodeId, amountTooLow, amountTooLow, cltvExpiry, pathId) - val add = makeUpdateAddHtlc(8, randomBytes32(), paymentHandler, metadata.paymentHash, finalPayload, route.blindingKey) + val add = makeUpdateAddHtlc(8, randomBytes32(), paymentHandler, metadata.paymentHash, finalPayload, route.firstPathKey) val result = paymentHandler.process(add, Features.empty, TestConstants.defaultBlockHeight, TestConstants.feeratePerKw, remoteFundingRates = null) assertIs(result) @@ -1765,7 +1765,7 @@ class IncomingPaymentHandlerTestsCommon : LightningTestSuite() { val metadata = OfferPaymentMetadata.V1(randomBytes32(), 100_000_000.msat, randomBytes32(), randomKey().publicKey(), null, 1, currentTimestampMillis()) val pathId = metadata.toPathId(TestConstants.Bob.nodeParams.nodePrivateKey) val (finalPayload, route) = makeBlindedPayload(TestConstants.Bob.nodeParams.nodeId, metadata.amount, metadata.amount, cltvExpiry, pathId) - val add = makeUpdateAddHtlc(8, randomBytes32(), paymentHandler, metadata.paymentHash.reversed(), finalPayload, route.blindingKey) + val add = makeUpdateAddHtlc(8, randomBytes32(), paymentHandler, metadata.paymentHash.reversed(), finalPayload, route.firstPathKey) val result = paymentHandler.process(add, Features.empty, TestConstants.defaultBlockHeight, TestConstants.feeratePerKw, remoteFundingRates = null) assertIs(result) diff --git a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/payment/OfferManagerTestsCommon.kt b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/payment/OfferManagerTestsCommon.kt index 8d1c55f0e..420c4a92b 100644 --- a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/payment/OfferManagerTestsCommon.kt +++ b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/payment/OfferManagerTestsCommon.kt @@ -36,17 +36,17 @@ class OfferManagerTestsCommon : LightningTestSuite() { /** Simulate the decryption step performed by the trampoline node when relaying onion messages. */ private fun trampolineRelay(msg: OnionMessage, trampolineKey: PrivateKey): Pair> { - val blindedPrivateKey = RouteBlinding.derivePrivateKey(trampolineKey, msg.blindingKey) + val blindedPrivateKey = RouteBlinding.derivePrivateKey(trampolineKey, msg.pathKey) val decrypted = Sphinx.peel(blindedPrivateKey, ByteVector.empty, msg.onionRoutingPacket) assertIs>(decrypted) assertFalse(decrypted.value.isLastPacket) val message = MessageOnion.read(decrypted.value.payload.toByteArray()) - val (decryptedPayload, nextBlinding) = RouteBlinding.decryptPayload(trampolineKey, msg.blindingKey, message.encryptedData).right!! + val (decryptedPayload, nextBlinding) = RouteBlinding.decryptPayload(trampolineKey, msg.pathKey, message.encryptedData).right!! val relayInfo = RouteBlindingEncryptedData.read(decryptedPayload.toByteArray()).right!! assertNull(relayInfo.pathId) assertEquals(Features.empty, relayInfo.allowedFeatures) val nextNode = relayInfo.nextNodeId?.let { Either.Right(it) } ?: Either.Left(relayInfo.outgoingChannelId!!) - return Pair(OnionMessage(relayInfo.nextBlindingOverride ?: nextBlinding, decrypted.value.nextPacket), nextNode) + return Pair(OnionMessage(relayInfo.nextPathKeyOverride ?: nextBlinding, decrypted.value.nextPacket), nextNode) } private fun createOffer(offerManager: OfferManager, amount: MilliSatoshi? = null): OfferTypes.Offer { @@ -66,7 +66,7 @@ class OfferManagerTestsCommon : LightningTestSuite() { private fun decryptPathId(invoice: Bolt12Invoice, trampolineKey: PrivateKey): OfferPaymentMetadata.V1 { val blindedRoute = invoice.blindedPaths.first().route.route assertEquals(2, blindedRoute.encryptedPayloads.size) - val (_, nextBlinding) = RouteBlinding.decryptPayload(trampolineKey, blindedRoute.blindingKey, blindedRoute.encryptedPayloads.first()).right!! + val (_, nextBlinding) = RouteBlinding.decryptPayload(trampolineKey, blindedRoute.firstPathKey, blindedRoute.encryptedPayloads.first()).right!! val (lastPayload, _) = RouteBlinding.decryptPayload(TestConstants.Alice.nodeParams.nodePrivateKey, nextBlinding, blindedRoute.encryptedPayloads.last()).right!! val pathId = RouteBlindingEncryptedData.read(lastPayload.toByteArray()).right!!.pathId!! return OfferPaymentMetadata.fromPathId(TestConstants.Alice.nodeParams.nodeId, pathId) as OfferPaymentMetadata.V1 @@ -97,12 +97,12 @@ class OfferManagerTestsCommon : LightningTestSuite() { assertEquals(payOffer, payInvoice.payOffer) assertEquals(1, payInvoice.invoice.blindedPaths.size) val path = payInvoice.invoice.blindedPaths.first() - assertEquals(EncodedNodeId(aliceTrampolineKey.publicKey()), path.route.route.introductionNodeId) + assertEquals(EncodedNodeId(aliceTrampolineKey.publicKey()), path.route.route.firstNodeId) assertEquals(aliceOfferManager.nodeParams.expiryDeltaBlocks + aliceOfferManager.nodeParams.minFinalCltvExpiryDelta, path.paymentInfo.cltvExpiryDelta) assertEquals(TestConstants.Alice.nodeParams.htlcMinimum, path.paymentInfo.minHtlc) assertEquals(payOffer.amount * 2, path.paymentInfo.maxHtlc) // The blinded path expires long after the invoice expiry to allow senders to add their own expiry delta. - val (alicePayload, _) = RouteBlinding.decryptPayload(aliceTrampolineKey, path.route.route.blindingKey, path.route.route.encryptedPayloads.first()).right!! + val (alicePayload, _) = RouteBlinding.decryptPayload(aliceTrampolineKey, path.route.route.firstPathKey, path.route.route.encryptedPayloads.first()).right!! val paymentConstraints = RouteBlindingEncryptedData.read(alicePayload.toByteArray()).right!!.paymentConstraints!! assertTrue(paymentConstraints.maxCltvExpiry > CltvExpiryDelta(720).toCltvExpiry(currentBlockHeight.toLong())) } @@ -135,7 +135,7 @@ class OfferManagerTestsCommon : LightningTestSuite() { assertEquals(payOffer, payInvoice.payOffer) assertEquals(1, payInvoice.invoice.blindedPaths.size) val path = payInvoice.invoice.blindedPaths.first() - assertEquals(EncodedNodeId(aliceTrampolineKey.publicKey()), path.route.route.introductionNodeId) + assertEquals(EncodedNodeId(aliceTrampolineKey.publicKey()), path.route.route.firstNodeId) assertEquals(aliceOfferManager.nodeParams.expiryDeltaBlocks + aliceOfferManager.nodeParams.minFinalCltvExpiryDelta, path.paymentInfo.cltvExpiryDelta) assertEquals(TestConstants.Alice.nodeParams.htlcMinimum, path.paymentInfo.minHtlc) assertEquals(payOffer.amount * 2, path.paymentInfo.maxHtlc) @@ -198,7 +198,7 @@ class OfferManagerTestsCommon : LightningTestSuite() { val payOffer = PayOffer(UUID.randomUUID(), randomKey(), null, 5500.msat, offer, 20.seconds) val (_, invoiceRequests) = bobOfferManager.requestInvoice(payOffer) val (messageForAlice, _) = trampolineRelay(invoiceRequests.first(), aliceTrampolineKey) - assertNull(aliceOfferManager.receiveMessage(messageForAlice.copy(blindingKey = randomKey().publicKey()), listOf(), 0)) + assertNull(aliceOfferManager.receiveMessage(messageForAlice.copy(pathKey = randomKey().publicKey()), listOf(), 0)) } @Test @@ -216,7 +216,7 @@ class OfferManagerTestsCommon : LightningTestSuite() { val invoiceResponse = aliceOfferManager.receiveMessage(messageForAlice, listOf(), 0) assertIs(invoiceResponse) val (messageForBob, _) = trampolineRelay(invoiceResponse.message, aliceTrampolineKey) - assertNull(bobOfferManager.receiveMessage(messageForBob.copy(blindingKey = randomKey().publicKey()), listOf(), 0)) + assertNull(bobOfferManager.receiveMessage(messageForBob.copy(pathKey = randomKey().publicKey()), listOf(), 0)) } @Test diff --git a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/payment/PaymentPacketTestsCommon.kt b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/payment/PaymentPacketTestsCommon.kt index e57d8390b..173f0386d 100644 --- a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/payment/PaymentPacketTestsCommon.kt +++ b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/payment/PaymentPacketTestsCommon.kt @@ -150,7 +150,7 @@ class PaymentPacketTestsCommon : LightningTestSuite() { blindedPayload ) val onionD = OutgoingPaymentPacket.buildOnion(listOf(blindedRoute.blindedNodeIds.last()), listOf(finalPayload), paymentHash, OnionRoutingPacket.PaymentPacketLength) - val addD = UpdateAddHtlc(randomBytes32(), 1, finalAmount, paymentHash, finalExpiry, onionD.packet, blindedRoute.blindingKey, null) + val addD = UpdateAddHtlc(randomBytes32(), 1, finalAmount, paymentHash, finalExpiry, onionD.packet, blindedRoute.firstPathKey, null) return Pair(addD, paymentMetadata) } @@ -342,7 +342,7 @@ class PaymentPacketTestsCommon : LightningTestSuite() { // C is the introduction node of the blinded path: it can decrypt the first blinded payload and relay to D. val addD = run { - val (dataC, blindingD) = RouteBlinding.decryptPayload(privC, blindedRoute.blindingKey, blindedRoute.encryptedPayloads.first()).right!! + val (dataC, blindingD) = RouteBlinding.decryptPayload(privC, blindedRoute.firstPathKey, blindedRoute.encryptedPayloads.first()).right!! val payloadC = RouteBlindingEncryptedData.read(dataC.toByteArray()).right!! assertEquals(channelUpdateCD.shortChannelId, payloadC.outgoingChannelId) // C would normally create this payload based on the payment_relay field it received. @@ -452,7 +452,7 @@ class PaymentPacketTestsCommon : LightningTestSuite() { blindedPayload ) val onionD = OutgoingPaymentPacket.buildOnion(listOf(blindedRoute.blindedNodeIds.last()), listOf(payloadD), paymentHash, OnionRoutingPacket.PaymentPacketLength) - val addD = UpdateAddHtlc(randomBytes32(), 1, finalAmount, paymentHash, finalExpiry, onionD.packet, blindedRoute.blindingKey, null) + val addD = UpdateAddHtlc(randomBytes32(), 1, finalAmount, paymentHash, finalExpiry, onionD.packet, blindedRoute.firstPathKey, null) val failure = IncomingPaymentPacket.decrypt(addD, privD) assertTrue(failure.isLeft) assertEquals(failure.left, InvalidOnionBlinding(hash(addD.onionRoutingPacket))) diff --git a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/wire/LightningCodecsTestsCommon.kt b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/wire/LightningCodecsTestsCommon.kt index 45ba39bf2..516a649e1 100644 --- a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/wire/LightningCodecsTestsCommon.kt +++ b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/wire/LightningCodecsTestsCommon.kt @@ -824,15 +824,15 @@ class LightningCodecsTestsCommon : LightningTestSuite() { fun `encode - decode on-the-fly funding messages`() { val channelId = ByteVector32("c11b8fbd682b3c6ee11f9d7268e22bb5887cd4d3bf3338bfcc340583f685733c") val paymentId = ByteVector32("3118a7954088c27b19923894ed27923c297f88ec3734f90b2b4aafcb11238503") - val blinding = PublicKey.fromHex("0296d5c32655a5eaa8be086479d7bcff967b6e9ca8319b69565747ae16ff20fad6") + val pathKey = PublicKey.fromHex("0296d5c32655a5eaa8be086479d7bcff967b6e9ca8319b69565747ae16ff20fad6") val paymentHash1 = ByteVector32("80417c0c91deb72606958425ea1552a045a55a250e91870231b486dcb2106734") val paymentHash2 = ByteVector32("3213a810a0bfc54566d9be09da1484538b5d19229e928dfa8b692966a8df6785") val fundingFee = LiquidityAds.FundingFee(5_000_100.msat, TxId(TxHash("24e1b2c94c4e734dd5b9c5f3c910fbb6b3b436ced6382c7186056a5a23f14566"))) val testCases = listOf( // @formatter:off - UpdateAddHtlc(channelId, 7, 75_000_000.msat, paymentHash1, CltvExpiry(840_000), TestConstants.emptyOnionPacket, blinding = null, fundingFee = fundingFee) to Hex.decode("0080 c11b8fbd682b3c6ee11f9d7268e22bb5887cd4d3bf3338bfcc340583f685733c 0000000000000007 00000000047868c0 80417c0c91deb72606958425ea1552a045a55a250e91870231b486dcb2106734 000cd140 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 fda0512800000000004c4ba424e1b2c94c4e734dd5b9c5f3c910fbb6b3b436ced6382c7186056a5a23f14566"), - WillAddHtlc(Block.RegtestGenesisBlock.hash, paymentId, 50_000_000.msat, paymentHash1, CltvExpiry(840_000), TestConstants.emptyOnionPacket, blinding = null) to Hex.decode("a051 06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f 3118a7954088c27b19923894ed27923c297f88ec3734f90b2b4aafcb11238503 0000000002faf080 80417c0c91deb72606958425ea1552a045a55a250e91870231b486dcb2106734 000cd140 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), - WillAddHtlc(Block.RegtestGenesisBlock.hash, paymentId, 50_000_000.msat, paymentHash1, CltvExpiry(840_000), TestConstants.emptyOnionPacket, blinding) to Hex.decode("a051 06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f 3118a7954088c27b19923894ed27923c297f88ec3734f90b2b4aafcb11238503 0000000002faf080 80417c0c91deb72606958425ea1552a045a55a250e91870231b486dcb2106734 000cd140 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 00210296d5c32655a5eaa8be086479d7bcff967b6e9ca8319b69565747ae16ff20fad6"), + UpdateAddHtlc(channelId, 7, 75_000_000.msat, paymentHash1, CltvExpiry(840_000), TestConstants.emptyOnionPacket, pathKey = null, fundingFee = fundingFee) to Hex.decode("0080 c11b8fbd682b3c6ee11f9d7268e22bb5887cd4d3bf3338bfcc340583f685733c 0000000000000007 00000000047868c0 80417c0c91deb72606958425ea1552a045a55a250e91870231b486dcb2106734 000cd140 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 fda0512800000000004c4ba424e1b2c94c4e734dd5b9c5f3c910fbb6b3b436ced6382c7186056a5a23f14566"), + WillAddHtlc(Block.RegtestGenesisBlock.hash, paymentId, 50_000_000.msat, paymentHash1, CltvExpiry(840_000), TestConstants.emptyOnionPacket, pathKey = null) to Hex.decode("a051 06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f 3118a7954088c27b19923894ed27923c297f88ec3734f90b2b4aafcb11238503 0000000002faf080 80417c0c91deb72606958425ea1552a045a55a250e91870231b486dcb2106734 000cd140 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + WillAddHtlc(Block.RegtestGenesisBlock.hash, paymentId, 50_000_000.msat, paymentHash1, CltvExpiry(840_000), TestConstants.emptyOnionPacket, pathKey) to Hex.decode("a051 06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f 3118a7954088c27b19923894ed27923c297f88ec3734f90b2b4aafcb11238503 0000000002faf080 80417c0c91deb72606958425ea1552a045a55a250e91870231b486dcb2106734 000cd140 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 00210296d5c32655a5eaa8be086479d7bcff967b6e9ca8319b69565747ae16ff20fad6"), WillFailHtlc(paymentId, paymentHash1, ByteVector("deadbeef")) to Hex.decode("a052 3118a7954088c27b19923894ed27923c297f88ec3734f90b2b4aafcb11238503 80417c0c91deb72606958425ea1552a045a55a250e91870231b486dcb2106734 0004 deadbeef"), WillFailMalformedHtlc(paymentId, paymentHash1, ByteVector32("9d60e5791eee0799ce7b00009f56f56c6b988f6129b6a88494cce2cf2fa8b319"), 49157) to Hex.decode("a053 3118a7954088c27b19923894ed27923c297f88ec3734f90b2b4aafcb11238503 80417c0c91deb72606958425ea1552a045a55a250e91870231b486dcb2106734 9d60e5791eee0799ce7b00009f56f56c6b988f6129b6a88494cce2cf2fa8b319 c005"), CancelOnTheFlyFunding(channelId, listOf(), ByteVector("deadbeef")) to Hex.decode("a054 c11b8fbd682b3c6ee11f9d7268e22bb5887cd4d3bf3338bfcc340583f685733c 0000 0004 deadbeef"), diff --git a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/wire/OfferTypesTestsCommon.kt b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/wire/OfferTypesTestsCommon.kt index 41da7e5d9..ef53841eb 100644 --- a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/wire/OfferTypesTestsCommon.kt +++ b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/wire/OfferTypesTestsCommon.kt @@ -461,7 +461,7 @@ class OfferTypesTestsCommon : LightningTestSuite() { RouteBlinding.BlindedRoute( EncodedNodeId.ShortChannelIdDir(isNode1 = true, ShortChannelId(1234)), PublicKey.fromHex("0379b470d00b78ded936f8972a0f3ecda2bb6e6df40dcd581dbaeb3742b30008ff"), - listOf(RouteBlinding.BlindedNode(PublicKey.fromHex("02fba71b72623187dd24670110eec870e28b848f255ba2edc0486d3a8e89ec44b7"), ByteVector.fromHex("1dea"))) + listOf(RouteBlinding.BlindedHop(PublicKey.fromHex("02fba71b72623187dd24670110eec870e28b848f255ba2edc0486d3a8e89ec44b7"), ByteVector.fromHex("1dea"))) ) ) ), @@ -471,7 +471,7 @@ class OfferTypesTestsCommon : LightningTestSuite() { RouteBlinding.BlindedRoute( EncodedNodeId.ShortChannelIdDir(isNode1 = false, ShortChannelId(56789)), PublicKey.fromHex("0353a081bb02d6e361be3df3e92b41b788ca65667f6ea0c01e2bfa03664460ef86"), - listOf(RouteBlinding.BlindedNode(PublicKey.fromHex("03bce3f0cdb4172caac82ec8a9251eb35df1201bdcb977c5a03f3624ec4156a65f"), ByteVector.fromHex("c0ffee"))) + listOf(RouteBlinding.BlindedHop(PublicKey.fromHex("03bce3f0cdb4172caac82ec8a9251eb35df1201bdcb977c5a03f3624ec4156a65f"), ByteVector.fromHex("c0ffee"))) ) ) ), @@ -481,7 +481,7 @@ class OfferTypesTestsCommon : LightningTestSuite() { RouteBlinding.BlindedRoute( EncodedNodeId.WithPublicKey.Plain(PublicKey.fromHex("022d3b15cea00ee4a8e710b082bef18f0f3409cc4e7aff41c26eb0a4d3ab20dd73")), PublicKey.fromHex("0379a3b6e4bceb7519d09db776994b1f82cf6a9fa4d3ec2e52314c5938f2f9f966"), - listOf(RouteBlinding.BlindedNode(PublicKey.fromHex("02b446aaa523df82a992ab468e5298eabb6168e2c466455c210d8c97dbb8981328"), ByteVector.fromHex("cafe"))) + listOf(RouteBlinding.BlindedHop(PublicKey.fromHex("02b446aaa523df82a992ab468e5298eabb6168e2c466455c210d8c97dbb8981328"), ByteVector.fromHex("cafe"))) ) ) ), @@ -491,7 +491,7 @@ class OfferTypesTestsCommon : LightningTestSuite() { RouteBlinding.BlindedRoute( EncodedNodeId.WithPublicKey.Plain(PublicKey.fromHex("03ba3c458e3299eb19d2e07ae86453f4290bcdf8689707f0862f35194397c45922")), PublicKey.fromHex("028aa5d1a10463d598a0a0ab7296af21619049f94fe03ef664a87561009e58c3dd"), - listOf(RouteBlinding.BlindedNode(PublicKey.fromHex("02988d7381d0434cfebbe521031505fb9987ae6cefd0bab0e5927852eb96bb6cc2"), ByteVector.fromHex("ec1a13"))) + listOf(RouteBlinding.BlindedHop(PublicKey.fromHex("02988d7381d0434cfebbe521031505fb9987ae6cefd0bab0e5927852eb96bb6cc2"), ByteVector.fromHex("ec1a13"))) ) ) ), @@ -518,7 +518,7 @@ class OfferTypesTestsCommon : LightningTestSuite() { assertEquals(1, offer.contactInfos.size) val path = offer.contactInfos.first() assertIs(path) - assertEquals(EncodedNodeId(trampolineNode), path.route.introductionNodeId) + assertEquals(EncodedNodeId(trampolineNode), path.route.firstNodeId) assertEquals(key.publicKey(), path.route.blindedNodeIds.last()) val expectedOffer = Offer.decode("lno1zrxq8pjw7qjlm68mtp7e3yvxee4y5xrgjhhyf2fxhlphpckrvevh50u0qf70a6j2x2akrhazctejaaqr8y4qtzjtjzmfesay6mzr3s789uryuqsr8dpgfgxuk56vh7cl89769zdpdrkqwtypzhu2t8ehp73dqeeq65lsqvlx5pj8mw2kz54p4f6ct66stdfxz0df8nqq7svjjdjn2dv8sz28y7z07yg3vqyfyy8ywevqc8kzp36lhd5cqwlpkg8vdcqsfvz89axkmv5sgdysmwn95tpsct6mdercmz8jh2r82qqscrf6uc3tse5gw5sv5xjdfw8f6c").get() assertEquals(expectedOffer, offer) diff --git a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/wire/PaymentOnionTestsCommon.kt b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/wire/PaymentOnionTestsCommon.kt index 1a2f75c2f..01404783c 100644 --- a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/wire/PaymentOnionTestsCommon.kt +++ b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/wire/PaymentOnionTestsCommon.kt @@ -188,7 +188,7 @@ class PaymentOnionTestsCommon : LightningTestSuite() { val testCases = mapOf( // @formatter:off TlvStream(OnionPaymentPayloadTlv.AmountToForward(561.msat), OnionPaymentPayloadTlv.OutgoingCltv(CltvExpiry(1234567)), OnionPaymentPayloadTlv.EncryptedRecipientData(ByteVector("deadbeef")), OnionPaymentPayloadTlv.TotalAmount(1105.msat)) to Hex.decode("13 02020231 040312d687 0a04deadbeef 12020451"), - TlvStream(OnionPaymentPayloadTlv.AmountToForward(561.msat), OnionPaymentPayloadTlv.OutgoingCltv(CltvExpiry(1234567)), OnionPaymentPayloadTlv.EncryptedRecipientData(ByteVector("deadbeef")), OnionPaymentPayloadTlv.BlindingPoint(PublicKey.fromHex("036d6caac248af96f6afa7f904f550253a0f3ef3f5aa2fe6838a95b216691468e2")), OnionPaymentPayloadTlv.TotalAmount(1105.msat)) to Hex.decode("36 02020231 040312d687 0a04deadbeef 0c21036d6caac248af96f6afa7f904f550253a0f3ef3f5aa2fe6838a95b216691468e2 12020451"), + TlvStream(OnionPaymentPayloadTlv.AmountToForward(561.msat), OnionPaymentPayloadTlv.OutgoingCltv(CltvExpiry(1234567)), OnionPaymentPayloadTlv.EncryptedRecipientData(ByteVector("deadbeef")), OnionPaymentPayloadTlv.PathKey(PublicKey.fromHex("036d6caac248af96f6afa7f904f550253a0f3ef3f5aa2fe6838a95b216691468e2")), OnionPaymentPayloadTlv.TotalAmount(1105.msat)) to Hex.decode("36 02020231 040312d687 0a04deadbeef 0c21036d6caac248af96f6afa7f904f550253a0f3ef3f5aa2fe6838a95b216691468e2 12020451"), // @formatter:on ) testCases.forEach { diff --git a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/wire/RouteBlindingTestsCommon.kt b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/wire/RouteBlindingTestsCommon.kt index 4adeb73fa..094d8786e 100644 --- a/modules/core/src/commonTest/kotlin/fr/acinq/lightning/wire/RouteBlindingTestsCommon.kt +++ b/modules/core/src/commonTest/kotlin/fr/acinq/lightning/wire/RouteBlindingTestsCommon.kt @@ -45,7 +45,7 @@ class RouteBlindingTestsCommon : LightningTestSuite() { ByteVector("0421027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007 0821031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f") to RouteBlindingEncryptedData( TlvStream( OutgoingNodeId(EncodedNodeId(PublicKey(ByteVector("027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007")))), - NextBlinding(PublicKey(ByteVector("031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"))) + NextPathKey(PublicKey(ByteVector("031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"))) ) ), )