Skip to content

Commit 140dda4

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 b0fe686 commit 140dda4

File tree

4 files changed

+79
-2
lines changed

4 files changed

+79
-2
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.{Satoshi, TxId}
19+
import fr.acinq.bitcoin.scalacompat.{ByteVector64, Satoshi, TxId}
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}
@@ -270,3 +270,23 @@ object ClosingSignedTlv {
270270
)
271271

272272
}
273+
274+
sealed trait ClosingTlv extends Tlv
275+
276+
object ClosingTlv {
277+
/** Signature for a closing transaction containing only the closer's output. */
278+
case class CloserOutputOnly(sig: ByteVector64) extends ClosingTlv
279+
280+
/** Signature for a closing transaction containing only the closee's output. */
281+
case class CloseeOutputOnly(sig: ByteVector64) extends ClosingTlv
282+
283+
/** Signature for a closing transaction containing the closer and closee's outputs. */
284+
case class CloserAndCloseeOutputs(sig: ByteVector64) extends ClosingTlv
285+
286+
val closingTlvCodec: Codec[TlvStream[ClosingTlv]] = tlvStream(discriminated[ClosingTlv].by(varint)
287+
.typecase(UInt64(1), tlvField(bytes64.as[CloserOutputOnly]))
288+
.typecase(UInt64(2), tlvField(bytes64.as[CloseeOutputOnly]))
289+
.typecase(UInt64(3), tlvField(bytes64.as[CloserAndCloseeOutputs]))
290+
)
291+
292+
}

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import fr.acinq.eclair.wire.protocol.CommonCodecs._
2222
import fr.acinq.eclair.{Features, InitFeature, KamonExt}
2323
import scodec.bits.{BinStringSyntax, BitVector, ByteVector}
2424
import scodec.codecs._
25-
import scodec.{Attempt, Codec, Err}
25+
import scodec.{Attempt, Codec}
2626

2727
/**
2828
* Created by PM on 15/11/2016.
@@ -227,6 +227,22 @@ object LightningMessageCodecs {
227227
("signature" | bytes64) ::
228228
("tlvStream" | ClosingSignedTlv.closingSignedTlvCodec)).as[ClosingSigned]
229229

230+
val closingCompleteCodec: Codec[ClosingComplete] = (
231+
("channelId" | bytes32) ::
232+
("closerScriptPubKey" | varsizebinarydata) ::
233+
("closeeScriptPubKey" | varsizebinarydata) ::
234+
("fees" | satoshi) ::
235+
("lockTime" | uint32) ::
236+
("tlvStream" | ClosingTlv.closingTlvCodec)).as[ClosingComplete]
237+
238+
val closingSigCodec: Codec[ClosingSig] = (
239+
("channelId" | bytes32) ::
240+
("closerScriptPubKey" | varsizebinarydata) ::
241+
("closeeScriptPubKey" | varsizebinarydata) ::
242+
("fees" | satoshi) ::
243+
("lockTime" | uint32) ::
244+
("tlvStream" | ClosingTlv.closingTlvCodec)).as[ClosingSig]
245+
230246
val updateAddHtlcCodec: Codec[UpdateAddHtlc] = (
231247
("channelId" | bytes32) ::
232248
("id" | uint64overflow) ::
@@ -497,6 +513,8 @@ object LightningMessageCodecs {
497513
.typecase(36, channelReadyCodec)
498514
.typecase(38, shutdownCodec)
499515
.typecase(39, closingSignedCodec)
516+
.typecase(40, closingCompleteCodec)
517+
.typecase(41, closingSigCodec)
500518
.typecase(64, openDualFundedChannelCodec)
501519
.typecase(65, acceptDualFundedChannelCodec)
502520
.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
@@ -362,6 +362,18 @@ case class ClosingSigned(channelId: ByteVector32,
362362
val feeRange_opt = tlvStream.get[ClosingSignedTlv.FeeRange]
363363
}
364364

365+
case class ClosingComplete(channelId: ByteVector32, closerScriptPubKey: ByteVector, closeeScriptPubKey: ByteVector, fees: Satoshi, lockTime: Long, tlvStream: TlvStream[ClosingTlv] = TlvStream.empty) extends ChannelMessage with HasChannelId {
366+
val closerOutputOnlySig_opt: Option[ByteVector64] = tlvStream.get[ClosingTlv.CloserOutputOnly].map(_.sig)
367+
val closeeOutputOnlySig_opt: Option[ByteVector64] = tlvStream.get[ClosingTlv.CloseeOutputOnly].map(_.sig)
368+
val closerAndCloseeOutputsSig_opt: Option[ByteVector64] = tlvStream.get[ClosingTlv.CloserAndCloseeOutputs].map(_.sig)
369+
}
370+
371+
case class ClosingSig(channelId: ByteVector32, closerScriptPubKey: ByteVector, closeeScriptPubKey: ByteVector, fees: Satoshi, lockTime: Long, tlvStream: TlvStream[ClosingTlv] = TlvStream.empty) extends ChannelMessage with HasChannelId {
372+
val closerOutputOnlySig_opt: Option[ByteVector64] = tlvStream.get[ClosingTlv.CloserOutputOnly].map(_.sig)
373+
val closeeOutputOnlySig_opt: Option[ByteVector64] = tlvStream.get[ClosingTlv.CloseeOutputOnly].map(_.sig)
374+
val closerAndCloseeOutputsSig_opt: Option[ByteVector64] = tlvStream.get[ClosingTlv.CloserAndCloseeOutputs].map(_.sig)
375+
}
376+
365377
case class UpdateAddHtlc(channelId: ByteVector32,
366378
id: Long,
367379
amountMsat: MilliSatoshi,

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,33 @@ class LightningMessageCodecsSpec extends AnyFunSuite {
519519
}
520520
}
521521

522+
test("encode/decode closing messages") {
523+
val channelId = ByteVector32(hex"58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86")
524+
val sig1 = ByteVector64(hex"01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101")
525+
val sig2 = ByteVector64(hex"02020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202")
526+
val sig3 = ByteVector64(hex"03030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303")
527+
val closerScript = hex"deadbeef"
528+
val closeeScript = hex"d43db3ef1234"
529+
val testCases = Seq(
530+
hex"0028 58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86 0004deadbeef 0006d43db3ef1234 0000000000000451 00000000" -> ClosingComplete(channelId, closerScript, closeeScript, 1105 sat, 0),
531+
hex"0028 58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86 0004deadbeef 0006d43db3ef1234 0000000000000451 000c96a8 024001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101" -> ClosingComplete(channelId, closerScript, closeeScript, 1105 sat, 825_000, TlvStream(ClosingTlv.CloseeOutputOnly(sig1))),
532+
hex"0028 58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86 0004deadbeef 0006d43db3ef1234 0000000000000451 00000000 034001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101" -> ClosingComplete(channelId, closerScript, closeeScript, 1105 sat, 0, TlvStream(ClosingTlv.CloserAndCloseeOutputs(sig1))),
533+
hex"0028 58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86 0004deadbeef 0006d43db3ef1234 0000000000000451 00000000 014001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101 034002020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202" -> ClosingComplete(channelId, closerScript, closeeScript, 1105 sat, 0, TlvStream(ClosingTlv.CloserOutputOnly(sig1), ClosingTlv.CloserAndCloseeOutputs(sig2))),
534+
hex"0028 58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86 0004deadbeef 0006d43db3ef1234 0000000000000451 00000000 014001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101 024002020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 034003030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303" -> ClosingComplete(channelId, closerScript, closeeScript, 1105 sat, 0, TlvStream(ClosingTlv.CloserOutputOnly(sig1), ClosingTlv.CloseeOutputOnly(sig2), ClosingTlv.CloserAndCloseeOutputs(sig3))),
535+
hex"0029 58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86 0004deadbeef 0006d43db3ef1234 0000000000000451 00000000" -> ClosingSig(channelId, closerScript, closeeScript, 1105 sat, 0),
536+
hex"0029 58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86 0004deadbeef 0006d43db3ef1234 0000000000000451 00000000 024001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101" -> ClosingSig(channelId, closerScript, closeeScript, 1105 sat, 0, TlvStream(ClosingTlv.CloseeOutputOnly(sig1))),
537+
hex"0029 58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86 0004deadbeef 0006d43db3ef1234 0000000000000451 00000000 034001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101" -> ClosingSig(channelId, closerScript, closeeScript, 1105 sat, 0, TlvStream(ClosingTlv.CloserAndCloseeOutputs(sig1))),
538+
hex"0029 58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86 0004deadbeef 0006d43db3ef1234 0000000000000451 00000000 014001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101 034002020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202" -> ClosingSig(channelId, closerScript, closeeScript, 1105 sat, 0, TlvStream(ClosingTlv.CloserOutputOnly(sig1), ClosingTlv.CloserAndCloseeOutputs(sig2))),
539+
hex"0029 58a00a6f14e69a2e97b18cf76f755c8551fea9947cf7b6ece9d641013eba5f86 0004deadbeef 0006d43db3ef1234 0000000000000451 00000000 014001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101 024002020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 034003030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303" -> ClosingSig(channelId, closerScript, closeeScript, 1105 sat, 0, TlvStream(ClosingTlv.CloserOutputOnly(sig1), ClosingTlv.CloseeOutputOnly(sig2), ClosingTlv.CloserAndCloseeOutputs(sig3))),
540+
)
541+
for ((encoded, expected) <- testCases) {
542+
val decoded = lightningMessageCodec.decode(encoded.bits).require.value
543+
assert(decoded == expected)
544+
val reEncoded = lightningMessageCodec.encode(expected).require.bytes
545+
assert(reEncoded == encoded)
546+
}
547+
}
548+
522549
test("encode/decode all channel messages") {
523550
val unknownTlv = GenericTlv(UInt64(5), ByteVector.fromValidHex("deadbeef"))
524551
val msgs = List(

0 commit comments

Comments
 (0)