Skip to content

Commit 8a5ac98

Browse files
committed
Add codecs for option_simple_close
This feature adds two new messages: - `closing_complete` sent after exchanging `shutdown` - `closing_sig` sent in response to `closing_complete`
1 parent 5ad122a commit 8a5ac98

File tree

4 files changed

+69
-1
lines changed

4 files changed

+69
-1
lines changed

eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/ChannelTlv.scala

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
package fr.acinq.eclair.wire.protocol
1818

19-
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Satoshi}
19+
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, Satoshi}
2020
import fr.acinq.eclair.channel.{ChannelType, ChannelTypes}
2121
import fr.acinq.eclair.wire.protocol.CommonCodecs._
2222
import fr.acinq.eclair.wire.protocol.TlvCodecs.{tlvField, tlvStream, tmillisatoshi}
@@ -201,3 +201,23 @@ object ClosingSignedTlv {
201201
)
202202

203203
}
204+
205+
sealed trait ClosingTlv extends Tlv
206+
207+
object ClosingTlv {
208+
/** Signature for a closing transaction containing only the closer's output. */
209+
case class CloserNoClosee(sig: ByteVector64) extends ClosingTlv
210+
211+
/** Signature for a closing transaction containing only the closee's output. */
212+
case class NoCloserClosee(sig: ByteVector64) extends ClosingTlv
213+
214+
/** Signature for a closing transaction containing the closer and closee's outputs. */
215+
case class CloserAndClosee(sig: ByteVector64) extends ClosingTlv
216+
217+
val closingTlvCodec: Codec[TlvStream[ClosingTlv]] = tlvStream(discriminated[ClosingTlv].by(varint)
218+
.typecase(UInt64(1), tlvField(bytes64.as[CloserNoClosee]))
219+
.typecase(UInt64(2), tlvField(bytes64.as[NoCloserClosee]))
220+
.typecase(UInt64(3), tlvField(bytes64.as[CloserAndClosee]))
221+
)
222+
223+
}

eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecs.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,15 @@ object LightningMessageCodecs {
227227
("signature" | bytes64) ::
228228
("tlvStream" | ClosingSignedTlv.closingSignedTlvCodec)).as[ClosingSigned]
229229

230+
val closingCompleteCodec: Codec[ClosingComplete] = (
231+
("channelId" | bytes32) ::
232+
("fees" | satoshi) ::
233+
("tlvStream" | ClosingTlv.closingTlvCodec)).as[ClosingComplete]
234+
235+
val closingSigCodec: Codec[ClosingSig] = (
236+
("channelId" | bytes32) ::
237+
("tlvStream" | ClosingTlv.closingTlvCodec)).as[ClosingSig]
238+
230239
val updateAddHtlcCodec: Codec[UpdateAddHtlc] = (
231240
("channelId" | bytes32) ::
232241
("id" | uint64overflow) ::
@@ -448,6 +457,8 @@ object LightningMessageCodecs {
448457
.typecase(36, channelReadyCodec)
449458
.typecase(38, shutdownCodec)
450459
.typecase(39, closingSignedCodec)
460+
.typecase(40, closingCompleteCodec)
461+
.typecase(41, closingSigCodec)
451462
.typecase(64, openDualFundedChannelCodec)
452463
.typecase(65, acceptDualFundedChannelCodec)
453464
.typecase(66, txAddInputCodec)

eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageTypes.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,18 @@ case class ClosingSigned(channelId: ByteVector32,
335335
val feeRange_opt = tlvStream.get[ClosingSignedTlv.FeeRange]
336336
}
337337

338+
case class ClosingComplete(channelId: ByteVector32, fees: Satoshi, tlvStream: TlvStream[ClosingTlv] = TlvStream.empty) extends ChannelMessage with HasChannelId {
339+
val closerNoCloseeSig_opt: Option[ByteVector64] = tlvStream.get[ClosingTlv.CloserNoClosee].map(_.sig)
340+
val noCloserCloseeSig_opt: Option[ByteVector64] = tlvStream.get[ClosingTlv.NoCloserClosee].map(_.sig)
341+
val closerAndCloseeSig_opt: Option[ByteVector64] = tlvStream.get[ClosingTlv.CloserAndClosee].map(_.sig)
342+
}
343+
344+
case class ClosingSig(channelId: ByteVector32, tlvStream: TlvStream[ClosingTlv] = TlvStream.empty) extends ChannelMessage with HasChannelId {
345+
val closerNoCloseeSig_opt: Option[ByteVector64] = tlvStream.get[ClosingTlv.CloserNoClosee].map(_.sig)
346+
val noCloserCloseeSig_opt: Option[ByteVector64] = tlvStream.get[ClosingTlv.NoCloserClosee].map(_.sig)
347+
val closerAndCloseeSig_opt: Option[ByteVector64] = tlvStream.get[ClosingTlv.CloserAndClosee].map(_.sig)
348+
}
349+
338350
case class UpdateAddHtlc(channelId: ByteVector32,
339351
id: Long,
340352
amountMsat: MilliSatoshi,

eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecsSpec.scala

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,31 @@ class LightningMessageCodecsSpec extends AnyFunSuite {
379379
}
380380
}
381381

382+
test("encode/decode closing messages") {
383+
val channelId = ByteVector32(hex"58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86")
384+
val sig1 = ByteVector64(hex"01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101")
385+
val sig2 = ByteVector64(hex"02020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202")
386+
val sig3 = ByteVector64(hex"03030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303")
387+
val testCases = Seq(
388+
hex"0028 58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86 0000000000000451" -> ClosingComplete(channelId, 1105 sat),
389+
hex"0028 58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86 0000000000000451 024001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101" -> ClosingComplete(channelId, 1105 sat, TlvStream(ClosingTlv.NoCloserClosee(sig1))),
390+
hex"0028 58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86 0000000000000451 034001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101" -> ClosingComplete(channelId, 1105 sat, TlvStream(ClosingTlv.CloserAndClosee(sig1))),
391+
hex"0028 58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86 0000000000000451 014001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101 034002020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202" -> ClosingComplete(channelId, 1105 sat, TlvStream(ClosingTlv.CloserNoClosee(sig1), ClosingTlv.CloserAndClosee(sig2))),
392+
hex"0028 58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86 0000000000000451 014001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101 024002020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 034003030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303" -> ClosingComplete(channelId, 1105 sat, TlvStream(ClosingTlv.CloserNoClosee(sig1), ClosingTlv.NoCloserClosee(sig2), ClosingTlv.CloserAndClosee(sig3))),
393+
hex"0029 58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86" -> ClosingSig(channelId),
394+
hex"0029 58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86 024001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101" -> ClosingSig(channelId, TlvStream(ClosingTlv.NoCloserClosee(sig1))),
395+
hex"0029 58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86 034001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101" -> ClosingSig(channelId, TlvStream(ClosingTlv.CloserAndClosee(sig1))),
396+
hex"0029 58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86 014001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101 034002020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202" -> ClosingSig(channelId, TlvStream(ClosingTlv.CloserNoClosee(sig1), ClosingTlv.CloserAndClosee(sig2))),
397+
hex"0029 58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86 014001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101 024002020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 034003030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303" -> ClosingSig(channelId, TlvStream(ClosingTlv.CloserNoClosee(sig1), ClosingTlv.NoCloserClosee(sig2), ClosingTlv.CloserAndClosee(sig3))),
398+
)
399+
for ((encoded, expected) <- testCases) {
400+
val decoded = lightningMessageCodec.decode(encoded.bits).require.value
401+
assert(decoded == expected)
402+
val reEncoded = lightningMessageCodec.encode(expected).require.bytes
403+
assert(reEncoded == encoded)
404+
}
405+
}
406+
382407
test("encode/decode all channel messages") {
383408
val unknownTlv = GenericTlv(UInt64(5), ByteVector.fromValidHex("deadbeef"))
384409
val msgs = List(

0 commit comments

Comments
 (0)