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:
parent
9d17b1dfc1
commit
3191878685
3 changed files with 17 additions and 7 deletions
|
@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Add table
Reference in a new issue