Skip to content

Commit

Permalink
Rename blinding to pathKey (#2951)
Browse files Browse the repository at this point in the history
Rename some variable to align with the spec.
  • Loading branch information
thomash-acinq authored Nov 28, 2024
1 parent ab94128 commit 0d2d380
Show file tree
Hide file tree
Showing 54 changed files with 458 additions and 459 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ final case class CMD_ADD_HTLC(replyTo: ActorRef,
paymentHash: ByteVector32,
cltvExpiry: CltvExpiry,
onion: OnionRoutingPacket,
nextBlindingKey_opt: Option[PublicKey],
nextPathKey_opt: Option[PublicKey],
confidence: Double,
fundingFee_opt: Option[LiquidityAds.FundingFee],
origin: Origin.Hot,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -851,7 +851,7 @@ case class Commitments(params: ChannelParams,
return Left(HtlcValueTooSmall(params.channelId, minimum = htlcMinimum, actual = cmd.amount))
}

val add = UpdateAddHtlc(channelId, changes.localNextHtlcId, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion, cmd.nextBlindingKey_opt, cmd.confidence, cmd.fundingFee_opt)
val add = UpdateAddHtlc(channelId, changes.localNextHtlcId, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion, cmd.nextPathKey_opt, cmd.confidence, cmd.fundingFee_opt)
// we increment the local htlc index and add an entry to the origins map
val changes1 = changes.addLocalProposal(add).copy(localNextHtlcId = changes.localNextHtlcId + 1)
val originChannels1 = originChannels + (add.id -> cmd.origin)
Expand Down
78 changes: 39 additions & 39 deletions eclair-core/src/main/scala/fr/acinq/eclair/crypto/Sphinx.scala
Original file line number Diff line number Diff line change
Expand Up @@ -342,42 +342,42 @@ object Sphinx extends Logging {
object RouteBlinding {

/**
* @param nodeId introduction 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 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 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.
*/
case class IntroductionNode(nodeId: EncodedNodeId, blindedPublicKey: PublicKey, blindingEphemeralKey: PublicKey, encryptedPayload: ByteVector)
case class FirstNode(nodeId: EncodedNodeId, blindedPublicKey: PublicKey, pathKey: PublicKey, 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.
* path key.
*/
case class BlindedNode(blindedPublicKey: PublicKey, encryptedPayload: ByteVector)
case class BlindedHop(blindedPublicKey: PublicKey, 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
* @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 blindedNodes blinded nodes (including the introduction node).
* @param blindedHops blinded nodes (including the introduction node).
*/
case class BlindedRoute(introductionNodeId: EncodedNodeId, blindingKey: PublicKey, blindedNodes: Seq[BlindedNode]) {
require(blindedNodes.nonEmpty, "blinded route must not be empty")
val introductionNode: IntroductionNode = IntroductionNode(introductionNodeId, blindedNodes.head.blindedPublicKey, blindingKey, blindedNodes.head.encryptedPayload)
val subsequentNodes: Seq[BlindedNode] = blindedNodes.tail
val blindedNodeIds: Seq[PublicKey] = blindedNodes.map(_.blindedPublicKey)
val encryptedPayloads: Seq[ByteVector] = blindedNodes.map(_.encryptedPayload)
val length: Int = blindedNodes.length - 1
case class BlindedRoute(firstNodeId: EncodedNodeId, firstPathKey: PublicKey, blindedHops: Seq[BlindedHop]) {
require(blindedHops.nonEmpty, "blinded route must not be empty")
val firstNode: FirstNode = FirstNode(firstNodeId, blindedHops.head.blindedPublicKey, firstPathKey, blindedHops.head.encryptedPayload)
val subsequentNodes: Seq[BlindedHop] = blindedHops.tail
val blindedNodeIds: Seq[PublicKey] = blindedHops.map(_.blindedPublicKey)
val encryptedPayloads: Seq[ByteVector] = blindedHops.map(_.encryptedPayload)
val length: Int = blindedHops.length - 1
}

/**
* @param route blinded route.
* @param lastBlinding blinding point for the last node, which can be used to derive the blinded private key.
* @param route blinded route.
* @param lastPathKey path key for the last node, which can be used to derive the blinded private key.
*/
case class BlindedRouteDetails(route: BlindedRoute, lastBlinding: PublicKey)
case class BlindedRouteDetails(route: BlindedRoute, lastPathKey: PublicKey)

/**
* Blind the provided route and encrypt intermediate nodes' payloads.
Expand All @@ -390,44 +390,44 @@ object Sphinx extends Logging {
def create(sessionKey: PrivateKey, publicKeys: Seq[PublicKey], payloads: Seq[ByteVector]): BlindedRouteDetails = {
require(publicKeys.length == payloads.length, "a payload must be provided for each node in the blinded path")
var e = sessionKey
val (blindedHops, blindingKeys) = publicKeys.zip(payloads).map { case (publicKey, payload) =>
val blindingKey = e.publicKey
val (blindedHops, pathKeys) = publicKeys.zip(payloads).map { case (publicKey, payload) =>
val pathKey = e.publicKey
val sharedSecret = computeSharedSecret(publicKey, e)
val blindedPublicKey = blind(publicKey, generateKey("blinded_node_id", sharedSecret))
val rho = generateKey("rho", sharedSecret)
val (encryptedPayload, mac) = ChaCha20Poly1305.encrypt(rho, zeroes(12), payload, ByteVector.empty)
e = e.multiply(PrivateKey(Crypto.sha256(blindingKey.value ++ sharedSecret.bytes)))
(BlindedNode(blindedPublicKey, encryptedPayload ++ mac), blindingKey)
e = e.multiply(PrivateKey(Crypto.sha256(pathKey.value ++ sharedSecret.bytes)))
(BlindedHop(blindedPublicKey, encryptedPayload ++ mac), pathKey)
}.unzip
BlindedRouteDetails(BlindedRoute(EncodedNodeId.WithPublicKey.Plain(publicKeys.head), blindingKeys.head, blindedHops), blindingKeys.last)
BlindedRouteDetails(BlindedRoute(EncodedNodeId.WithPublicKey.Plain(publicKeys.head), pathKeys.head, 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 privateKey this node's private key.
* @param pathKey unblinding ephemeral key.
* @return this node's blinded private key.
*/
def derivePrivateKey(privateKey: PrivateKey, blindingEphemeralKey: PublicKey): PrivateKey = {
val sharedSecret = computeSharedSecret(blindingEphemeralKey, privateKey)
def derivePrivateKey(privateKey: PrivateKey, pathKey: PublicKey): PrivateKey = {
val sharedSecret = computeSharedSecret(pathKey, privateKey)
privateKey.multiply(PrivateKey(generateKey("blinded_node_id", sharedSecret)))
}

/**
* 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 encryptedPayload encrypted payload for this node.
* @return a tuple (decrypted payload, unblinding ephemeral key for the next node)
* @param privateKey this node's private key.
* @param pathKey unblinding ephemeral key.
* @param encryptedPayload encrypted payload for this node.
* @return a tuple (decrypted payload, path key for the next node)
*/
def decryptPayload(privateKey: PrivateKey, blindingEphemeralKey: PublicKey, encryptedPayload: ByteVector): Try[(ByteVector, PublicKey)] = Try {
val sharedSecret = computeSharedSecret(blindingEphemeralKey, privateKey)
def decryptPayload(privateKey: PrivateKey, pathKey: PublicKey, encryptedPayload: ByteVector): Try[(ByteVector, PublicKey)] = Try {
val sharedSecret = computeSharedSecret(pathKey, privateKey)
val rho = generateKey("rho", sharedSecret)
val decrypted = ChaCha20Poly1305.decrypt(rho, zeroes(12), encryptedPayload.dropRight(16), ByteVector.empty, encryptedPayload.takeRight(16))
val nextBlindingEphemeralKey = blind(blindingEphemeralKey, computeBlindingFactor(blindingEphemeralKey, sharedSecret))
(decrypted, nextBlindingEphemeralKey)
val nextPathKey = blind(pathKey, computeBlindingFactor(pathKey, sharedSecret))
(decrypted, nextPathKey)
}

}
Expand Down
4 changes: 2 additions & 2 deletions eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ class Peer(val nodeParams: NodeParams,

case Event(cmd: ProposeOnTheFlyFunding, d: ConnectedData) =>
// We send the funding proposal to our peer, and report it to the sender.
val htlc = WillAddHtlc(nodeParams.chainHash, randomBytes32(), cmd.amount, cmd.paymentHash, cmd.expiry, cmd.onion, cmd.nextBlindingKey_opt)
val htlc = WillAddHtlc(nodeParams.chainHash, randomBytes32(), cmd.amount, cmd.paymentHash, cmd.expiry, cmd.onion, cmd.nextPathKey_opt)
cmd.replyTo ! ProposeOnTheFlyFundingResponse.Proposed
// We update our list of pending proposals for that payment.
val pending = pendingOnTheFlyFunding.get(htlc.paymentHash) match {
Expand Down Expand Up @@ -983,7 +983,7 @@ object Peer {
case class SpawnChannelNonInitiator(open: Either[protocol.OpenChannel, protocol.OpenDualFundedChannel], channelConfig: ChannelConfig, channelType: SupportedChannelType, addFunding_opt: Option[LiquidityAds.AddFunding], localParams: LocalParams, peerConnection: ActorRef)

/** If [[Features.OnTheFlyFunding]] is supported and we're connected, relay a funding proposal to our peer. */
case class ProposeOnTheFlyFunding(replyTo: typed.ActorRef[ProposeOnTheFlyFundingResponse], amount: MilliSatoshi, paymentHash: ByteVector32, expiry: CltvExpiry, onion: OnionRoutingPacket, nextBlindingKey_opt: Option[PublicKey], upstream: Upstream.Hot)
case class ProposeOnTheFlyFunding(replyTo: typed.ActorRef[ProposeOnTheFlyFundingResponse], amount: MilliSatoshi, paymentHash: ByteVector32, expiry: CltvExpiry, onion: OnionRoutingPacket, nextPathKey_opt: Option[PublicKey], upstream: Upstream.Hot)

sealed trait ProposeOnTheFlyFundingResponse
object ProposeOnTheFlyFundingResponse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -468,8 +468,8 @@ object InvoiceSerializer extends MinimalSerializer({
UnknownFeatureSerializer
))),
Some(JField("blindedPaths", JArray(p.blindedPaths.map(path => {
val introductionNode = path.route.introductionNodeId.toString
val blindedNodes = path.route.blindedNodes
val introductionNode = path.route.firstNodeId.toString
val blindedNodes = path.route.blindedHops
JObject(List(
JField("introductionNodeId", JString(introductionNode)),
JField("blindedNodeIds", JArray(blindedNodes.map(n => JString(n.blindedPublicKey.toString)).toList))
Expand Down
Loading

0 comments on commit 0d2d380

Please sign in to comment.