1
0
Fork 0
mirror of https://github.com/ACINQ/eclair.git synced 2025-02-23 22:46:44 +01:00

Allow receiving to blinded routes (#2418)

Enable receiving blinded payments.
Unroll the dummy hops at the end of a blinded route.
This commit is contained in:
Thomas HUET 2022-09-14 10:32:05 +02:00 committed by GitHub
parent 9d17b1dfc1
commit 3191878685
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 17 additions and 7 deletions

View file

@ -95,6 +95,9 @@ object ShortChannelId {
}
case _ => Failure(new IllegalArgumentException(s"Invalid short channel id: $s"))
}
// A special alias for a virtual channel to ourselves. Used to add extra hops at the end of a blinded route.
val toSelf: Alias = Alias(aliasUpperBound)
}
/**

View file

@ -25,7 +25,7 @@ import fr.acinq.eclair.crypto.Sphinx
import fr.acinq.eclair.router.Router.{ChannelHop, Hop, NodeHop}
import fr.acinq.eclair.wire.protocol.PaymentOnion.{FinalPayload, IntermediatePayload, PerHopPayload}
import fr.acinq.eclair.wire.protocol._
import fr.acinq.eclair.{CltvExpiry, CltvExpiryDelta, Feature, Features, MilliSatoshi, UInt64, randomBytes32, randomKey}
import fr.acinq.eclair.{CltvExpiry, CltvExpiryDelta, Feature, Features, MilliSatoshi, ShortChannelId, UInt64, randomBytes32, randomKey}
import scodec.bits.ByteVector
import scodec.{Attempt, DecodeResult}
@ -120,7 +120,11 @@ object IncomingPaymentPacket {
case Some(encrypted) =>
decryptEncryptedRecipientData(add, privateKey, payload, encrypted.data).flatMap {
case DecodedEncryptedRecipientData(blindedPayload, nextBlinding) =>
validateBlindedChannelRelayPayload(add, payload, blindedPayload, nextBlinding, nextPacket)
validateBlindedChannelRelayPayload(add, payload, blindedPayload, nextBlinding, nextPacket).flatMap {
case ChannelRelayPacket(_, payload, nextPacket) if payload.outgoingChannelId == ShortChannelId.toSelf =>
decrypt(add.copy(onionRoutingPacket = nextPacket, tlvStream = add.tlvStream.copy(records = Seq(UpdateAddHtlcTlv.BlindingPoint(nextBlinding)))), privateKey, features)
case relayPacket => Right(relayPacket)
}
}
case None if add.blinding_opt.isDefined => Left(InvalidOnionBlinding(Sphinx.hash(add.onionRoutingPacket)))
case None => IntermediatePayload.ChannelRelay.Standard.validate(payload).left.map(_.failureMessage).map {
@ -151,7 +155,11 @@ object IncomingPaymentPacket {
}
}
private def validateBlindedChannelRelayPayload(add: UpdateAddHtlc, payload: TlvStream[OnionPaymentPayloadTlv], blindedPayload: TlvStream[RouteBlindingEncryptedDataTlv], nextBlinding: PublicKey, nextPacket: OnionRoutingPacket): Either[FailureMessage, ChannelRelayPacket] = {
private def validateBlindedChannelRelayPayload(add: UpdateAddHtlc,
payload: TlvStream[OnionPaymentPayloadTlv],
blindedPayload: TlvStream[RouteBlindingEncryptedDataTlv],
nextBlinding: PublicKey,
nextPacket: OnionRoutingPacket): Either[FailureMessage, ChannelRelayPacket] = {
IntermediatePayload.ChannelRelay.Blinded.validate(payload, blindedPayload, nextBlinding).left.map(_.failureMessage).flatMap {
case payload if add.amountMsat < payload.paymentConstraints.minAmount => Left(InvalidOnionBlinding(Sphinx.hash(add.onionRoutingPacket)))
case payload if add.cltvExpiry > payload.paymentConstraints.maxCltvExpiry => Left(InvalidOnionBlinding(Sphinx.hash(add.onionRoutingPacket)))
@ -173,8 +181,7 @@ object IncomingPaymentPacket {
case payload if add.amountMsat < payload.paymentConstraints.minAmount => Left(InvalidOnionBlinding(Sphinx.hash(add.onionRoutingPacket)))
case payload if add.cltvExpiry > payload.paymentConstraints.maxCltvExpiry => Left(InvalidOnionBlinding(Sphinx.hash(add.onionRoutingPacket)))
case payload if !Features.areCompatible(Features.empty, payload.allowedFeatures) => Left(InvalidOnionBlinding(Sphinx.hash(add.onionRoutingPacket)))
// TODO: receiving through blinded routes is not supported yet.
case _ => Left(InvalidOnionBlinding(Sphinx.hash(add.onionRoutingPacket)))
case payload => Right(FinalPacket(add, payload))
}
}

View file

@ -299,10 +299,10 @@ object MultiPartHandler {
invoice
case r: ReceiveOfferPayment =>
// TODO: get blinded paths from the router instead
val pathId = RouteBlindingEncryptedDataTlv.PathId(randomBytes(32))
val pathId = RouteBlindingEncryptedDataTlv.PathId(randomBytes32())
val dummyConstraints = RouteBlindingEncryptedDataTlv.PaymentConstraints(CltvExpiry(nodeParams.currentBlockHeight + 144), 1 msat)
val dummyRelay = RouteBlindingEncryptedDataTlv.PaymentRelay(CltvExpiryDelta(0), 0, 0 msat)
val dummyScid = RouteBlindingEncryptedDataTlv.OutgoingChannelId(ShortChannelId(0))
val dummyScid = RouteBlindingEncryptedDataTlv.OutgoingChannelId(ShortChannelId.toSelf)
val dummyPath = Seq(
(nodeParams.nodeId, RouteBlindingEncryptedDataCodecs.blindedRouteDataCodec.encode(TlvStream(dummyScid, dummyRelay, dummyConstraints)).require.bytes),
(nodeParams.nodeId, RouteBlindingEncryptedDataCodecs.blindedRouteDataCodec.encode(TlvStream(dummyConstraints, pathId)).require.bytes),