Skip to content

Commit

Permalink
remove support for legacy Sphinx payloads.
Browse files Browse the repository at this point in the history
  • Loading branch information
fiatjaf committed Sep 5, 2022
1 parent 35faaad commit 7e7fe87
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 97 deletions.
92 changes: 37 additions & 55 deletions shared/src/main/scala/ln/PaymentOnion.scala
Original file line number Diff line number Diff line change
Expand Up @@ -178,25 +178,25 @@ object PaymentOnion {
/*
* We use the following architecture for payment onion payloads:
*
* PerHopPayload
* |
* |
* +--------------------------+------------------------------+
* | |
* | |
* RelayPayload FinalPayload
* | |
* | |
* +-------------------------+---------------------------+ +--------+---------+
* | | | |
* | | | |
* ChannelRelayPayload | | |
* | | | |
* | | | |
* +--------------------------+--------------------------+ | | |
* | | | | | |
* | | | | | |
* RelayLegacyPayload ChannelRelayTlvPayload BlindedChannelRelayPayload NodeRelayPayload FinalTlvPayload BlindedFinalPayload
* PerHopPayload
* |
* |
* +--------------------------+---------------------+
* | |
* | |
* RelayPayload FinalPayload
* | |
* | |
* +---------------------------------------+ +--------+---------+
* | | | |
* | | | |
* ChannelRelayPayload | | |
* | | | |
* | | | |
* +---------------------------------+ | | |
* | | | | |
* | | | | |
* ChannelRelayTlvPayload BlindedChannelRelayPayload NodeRelayPayload FinalTlvPayload BlindedFinalPayload
*
* We also introduce additional traits to separate payloads based on the type of onion packet they can be used with (PacketType).
*/
Expand Down Expand Up @@ -244,13 +244,6 @@ object PaymentOnion {
val expiry: CltvExpiry
}

case class RelayLegacyPayload(
outgoingChannelId: ShortChannelId,
amountToForward: MilliSatoshi,
outgoingCltv: CltvExpiry
) extends ChannelRelayPayload
with ChannelRelayData

case class ChannelRelayTlvPayload(records: TlvStream[OnionPaymentPayloadTlv])
extends ChannelRelayPayload
with ChannelRelayData {
Expand Down Expand Up @@ -532,38 +525,27 @@ object PaymentOnionCodecs {
.lengthPrefixedTlvStream[OnionPaymentPayloadTlv](onionTlvCodec)
.complete

private val legacyRelayPerHopPayloadCodec: Codec[RelayLegacyPayload] =
(("realm" | constant(ByteVector.fromByte(0))) ::
("short_channel_id" | shortchannelid) ::
("amt_to_forward" | millisatoshi) ::
("outgoing_cltv_value" | cltvExpiry) ::
("unused_with_v0_version_on_header" | ignore(8 * 12)))
.as[RelayLegacyPayload]

val channelRelayPerHopPayloadCodec: Codec[ChannelRelayPayload] =
fallback(tlvPerHopPayloadCodec, legacyRelayPerHopPayloadCodec).narrow(
{
case Left(tlvs) =>
tlvs.get[EncryptedRecipientData] match {
case Some(_) if tlvs.get[AmountToForward].isDefined =>
Attempt.failure(ForbiddenTlv(UInt64(2)))
case Some(_) if tlvs.get[OutgoingCltv].isDefined =>
Attempt.failure(ForbiddenTlv(UInt64(4)))
case Some(_) => Attempt.successful(BlindedChannelRelayPayload(tlvs))
case None if tlvs.get[AmountToForward].isEmpty =>
Attempt.failure(MissingRequiredTlv(UInt64(2)))
case None if tlvs.get[OutgoingCltv].isEmpty =>
Attempt.failure(MissingRequiredTlv(UInt64(4)))
case None if tlvs.get[OutgoingChannelId].isEmpty =>
Attempt.failure(MissingRequiredTlv(UInt64(6)))
case None => Attempt.successful(ChannelRelayTlvPayload(tlvs))
}
case Right(legacy) => Attempt.successful(legacy)
tlvPerHopPayloadCodec.narrow(
{ tlvs =>
tlvs.get[EncryptedRecipientData] match {
case Some(_) if tlvs.get[AmountToForward].isDefined =>
Attempt.failure(ForbiddenTlv(UInt64(2)))
case Some(_) if tlvs.get[OutgoingCltv].isDefined =>
Attempt.failure(ForbiddenTlv(UInt64(4)))
case Some(_) => Attempt.successful(BlindedChannelRelayPayload(tlvs))
case None if tlvs.get[AmountToForward].isEmpty =>
Attempt.failure(MissingRequiredTlv(UInt64(2)))
case None if tlvs.get[OutgoingCltv].isEmpty =>
Attempt.failure(MissingRequiredTlv(UInt64(4)))
case None if tlvs.get[OutgoingChannelId].isEmpty =>
Attempt.failure(MissingRequiredTlv(UInt64(6)))
case None => Attempt.successful(ChannelRelayTlvPayload(tlvs))
}
},
{
case legacy: RelayLegacyPayload => Right(legacy)
case ChannelRelayTlvPayload(tlvs) => Left(tlvs)
case BlindedChannelRelayPayload(tlvs) => Left(tlvs)
case ChannelRelayTlvPayload(tlvs) => tlvs
case BlindedChannelRelayPayload(tlvs) => tlvs
}
)

Expand Down
2 changes: 0 additions & 2 deletions shared/src/main/scala/ln/PaymentPacket.scala
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,6 @@ object IncomingPaymentPacket {
case _ if add.blinding_opt.isDefined =>
Left(InvalidOnionPayload(UInt64(12), 0))
// NB: we don't validate the ChannelRelayPacket here because its fees and cltv depend on what channel we'll choose to use.
case payload: PaymentOnion.RelayLegacyPayload =>
Right(ChannelRelayPacket(add, payload, next, None))
case payload: PaymentOnion.ChannelRelayTlvPayload =>
Right(ChannelRelayPacket(add, payload, next, None))
}
Expand Down
77 changes: 37 additions & 40 deletions shared/src/main/scala/ln/Sphinx.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,21 +103,12 @@ object Sphinx {
/** Peek at the first bytes of the per-hop payload to extract its length.
*/
def peekPayloadLength(payload: ByteVector): Int = {
payload.head match {
case 0 =>
// The 1.0 BOLT spec used 65-bytes frames inside the onion payload.
// The first byte of the frame (called `realm`) is set to 0x00, followed by 32 bytes of per-hop data, followed by a 32-bytes mac.
65
case _ =>
// The 1.1 BOLT spec changed the frame format to use variable-length per-hop payloads.
// The first bytes contain a varint encoding the length of the payload data (not including the trailing mac).
// Since messages are always smaller than 65535 bytes, this varint will either be 1 or 3 bytes long.
MacLength + PaymentOnionCodecs.payloadLengthDecoder
.decode(payload.bits)
.require
.value
.toInt
}
require(payload.head != 0, "legacy onion format is not supported anymore")
MacLength + PaymentOnionCodecs.payloadLengthDecoder
.decode(payload.bits)
.require
.value
.toInt
}

/** Decrypting an onion packet yields a payload for the current node and the
Expand Down Expand Up @@ -239,31 +230,37 @@ object Sphinx {
0
)) xor stream

val perHopPayloadLength = peekPayloadLength(bin)
val perHopPayload = bin.take(perHopPayloadLength - MacLength)

val hmac = ByteVector32(
bin.slice(perHopPayloadLength - MacLength, perHopPayloadLength)
)
val nextOnionPayload =
bin.drop(perHopPayloadLength).take(packet.payload.length)
val nextPubKey = blind(
packetEphKey,
computeBlindingFactor(packetEphKey, sharedSecret)
)

Right(
DecryptedPacket(
perHopPayload,
OnionRoutingPacket(
Version,
nextPubKey.value,
nextOnionPayload,
hmac
),
sharedSecret
)
)
Try(peekPayloadLength(bin)) match {
case Success(perHopPayloadLength) => {
val perHopPayload = bin.take(perHopPayloadLength - MacLength)
val hmac = ByteVector32(
bin.slice(
perHopPayloadLength - MacLength,
perHopPayloadLength
)
)
val nextOnionPayload =
bin.drop(perHopPayloadLength).take(packet.payload.length)
val nextPubKey = blind(
packetEphKey,
computeBlindingFactor(packetEphKey, sharedSecret)
)

Right(
DecryptedPacket(
perHopPayload,
OnionRoutingPacket(
Version,
nextPubKey.value,
nextOnionPayload,
hmac
),
sharedSecret
)
)
}
case Failure(_) => Left(InvalidOnionVersion(hash(packet)))
}
} else {
Left(InvalidOnionHmac(hash(packet)))
}
Expand Down

0 comments on commit 7e7fe87

Please sign in to comment.