diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/FailureMessage.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/FailureMessage.scala index bf2e2dad6..ea8308321 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/FailureMessage.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/FailureMessage.scala @@ -39,6 +39,7 @@ case object IncorrectPaymentAmount extends Perm case object FinalExpiryTooSoon extends FailureMessage case class FinalIncorrectCltvExpiry(expiry: Long) extends FailureMessage case class FinalIncorrectHtlcAmount(amountMsat: Long) extends FailureMessage +case object ExpiryTooFar extends FailureMessage // @formatter:on object FailureMessageCodecs { @@ -49,6 +50,8 @@ object FailureMessageCodecs { val sha256Codec: Codec[BinaryData] = ("sha256Codec" | binarydata(32)) + val channelUpdateWithLengthCodec = variableSizeBytes(uint16, channelUpdateCodec) + val failureMessageCodec = discriminated[FailureMessage].by(uint16) .typecase(PERM | 1, provide(InvalidRealm)) .typecase(NODE | 2, provide(TemporaryNodeFailure)) @@ -57,18 +60,19 @@ object FailureMessageCodecs { .typecase(BADONION | PERM | 4, sha256Codec.as[InvalidOnionVersion]) .typecase(BADONION | PERM | 5, sha256Codec.as[InvalidOnionHmac]) .typecase(BADONION | PERM | 6, sha256Codec.as[InvalidOnionKey]) - .typecase(UPDATE | 7, (("channelUpdate" | channelUpdateCodec)).as[TemporaryChannelFailure]) + .typecase(UPDATE | 7, (("channelUpdate" | channelUpdateWithLengthCodec)).as[TemporaryChannelFailure]) .typecase(PERM | 8, provide(PermanentChannelFailure)) .typecase(PERM | 9, provide(RequiredChannelFeatureMissing)) .typecase(PERM | 10, provide(UnknownNextPeer)) - .typecase(UPDATE | 11, (("amountMsat" | uint64) :: ("channelUpdate" | channelUpdateCodec)).as[AmountBelowMinimum]) - .typecase(UPDATE | 12, (("amountMsat" | uint64) :: ("channelUpdate" | channelUpdateCodec)).as[FeeInsufficient]) - .typecase(UPDATE | 13, (("expiry" | uint32) :: ("channelUpdate" | channelUpdateCodec)).as[IncorrectCltvExpiry]) - .typecase(UPDATE | 14, (("channelUpdate" | channelUpdateCodec)).as[ExpiryTooSoon]) - .typecase(UPDATE | 20, (("flags" | binarydata(2)) :: ("channelUpdate" | channelUpdateCodec)).as[ChannelDisabled]) + .typecase(UPDATE | 11, (("amountMsat" | uint64) :: ("channelUpdate" | channelUpdateWithLengthCodec)).as[AmountBelowMinimum]) + .typecase(UPDATE | 12, (("amountMsat" | uint64) :: ("channelUpdate" | channelUpdateWithLengthCodec)).as[FeeInsufficient]) + .typecase(UPDATE | 13, (("expiry" | uint32) :: ("channelUpdate" | channelUpdateWithLengthCodec)).as[IncorrectCltvExpiry]) + .typecase(UPDATE | 14, (("channelUpdate" | channelUpdateWithLengthCodec)).as[ExpiryTooSoon]) + .typecase(UPDATE | 20, (("flags" | binarydata(2)) :: ("channelUpdate" | channelUpdateWithLengthCodec)).as[ChannelDisabled]) .typecase(PERM | 15, provide(UnknownPaymentHash)) .typecase(PERM | 16, provide(IncorrectPaymentAmount)) .typecase(17, provide(FinalExpiryTooSoon)) .typecase(18, (("expiry" | uint32)).as[FinalIncorrectCltvExpiry]) .typecase(19, (("amountMsat" | uint32)).as[FinalIncorrectHtlcAmount]) + .typecase(21, provide(ExpiryTooFar)) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/FailureMessageLightningMessageCodecsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/FailureMessageLightningMessageCodecsSpec.scala index 92ec58fc5..3dd0d7812 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/FailureMessageLightningMessageCodecsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/FailureMessageLightningMessageCodecsSpec.scala @@ -1,6 +1,6 @@ package fr.acinq.eclair.wire -import fr.acinq.bitcoin.BinaryData +import fr.acinq.bitcoin.{BinaryData, Block} import org.junit.runner.RunWith import org.scalatest.FunSuite import org.scalatest.junit.JUnitRunner @@ -12,6 +12,16 @@ import scala.util.Random */ @RunWith(classOf[JUnitRunner]) class FailureMessageLightningMessageCodecsSpec extends FunSuite { + val channelUpdate = ChannelUpdate( + signature = BinaryData("3045022100c451cd65c88f55b1767941a247e849e12f5f4d4a93a07316659e22f5267d2088022009042a595c6bc8942cd9d729317b82b306edc259fb6b3a3cecb3dd1bd446e90601"), + chainHash = Block.RegtestGenesisBlock.hash, + shortChannelId = 12345, + timestamp = 1234567L, + cltvExpiryDelta = 100, + flags = BinaryData("0001"), + htlcMinimumMsat = 1000, + feeBaseMsat = 12, + feeProportionalMillionths = 76) def randomBytes(size: Int): BinaryData = { val bin = new Array[Byte](size) @@ -20,14 +30,12 @@ class FailureMessageLightningMessageCodecsSpec extends FunSuite { } test("encode/decode all channel messages") { - - val invalidRealm = InvalidRealm - val temporaryNodeFailure = TemporaryNodeFailure - val permanentNodeFailure = PermanentNodeFailure - - val msgs: List[FailureMessage] = - invalidRealm :: temporaryNodeFailure :: permanentNodeFailure :: Nil + InvalidRealm :: TemporaryNodeFailure :: PermanentNodeFailure :: RequiredNodeFeatureMissing :: + InvalidOnionVersion(randomBytes(32)) :: InvalidOnionHmac(randomBytes(32)) :: InvalidOnionKey(randomBytes(32)) :: + TemporaryChannelFailure(channelUpdate) :: PermanentChannelFailure :: RequiredChannelFeatureMissing :: UnknownNextPeer :: + AmountBelowMinimum(123456, channelUpdate) :: FeeInsufficient(546463, channelUpdate) :: IncorrectCltvExpiry(1211, channelUpdate) :: ExpiryTooSoon(channelUpdate) :: + UnknownPaymentHash :: IncorrectPaymentAmount :: FinalExpiryTooSoon :: FinalIncorrectCltvExpiry(1234) :: ChannelDisabled(BinaryData("0101"), channelUpdate) :: ExpiryTooFar :: Nil msgs.foreach { case msg => { @@ -37,5 +45,4 @@ class FailureMessageLightningMessageCodecsSpec extends FunSuite { } } } - }