diff --git a/lightning-kmp-test-fixtures/src/commonMain/kotlin/fr/acinq/lightning/tests/io/peer/builders.kt b/lightning-kmp-test-fixtures/src/commonMain/kotlin/fr/acinq/lightning/tests/io/peer/builders.kt index 34370eddc..61b933f79 100644 --- a/lightning-kmp-test-fixtures/src/commonMain/kotlin/fr/acinq/lightning/tests/io/peer/builders.kt +++ b/lightning-kmp-test-fixtures/src/commonMain/kotlin/fr/acinq/lightning/tests/io/peer/builders.kt @@ -146,9 +146,8 @@ public suspend fun CoroutineScope.newPeer( nextLocalCommitmentNumber = state.commitments.localCommit.index + 1, nextRemoteRevocationNumber = state.commitments.remoteCommit.index, yourLastCommitmentSecret = PrivateKey(yourLastPerCommitmentSecret), - myCurrentPerCommitmentPoint = myCurrentPerCommitmentPoint, - state.commitments.remoteChannelData - ) + myCurrentPerCommitmentPoint = myCurrentPerCommitmentPoint + ).withChannelData(state.commitments.remoteChannelData) val msg = LightningMessage.encode(channelReestablish) peer.send(BytesReceived(msg)) diff --git a/src/commonMain/kotlin/fr/acinq/lightning/channel/Channel.kt b/src/commonMain/kotlin/fr/acinq/lightning/channel/Channel.kt index b86da0992..ef43de49c 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/channel/Channel.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/channel/Channel.kt @@ -222,12 +222,12 @@ sealed class ChannelState { private fun updateActions(actions: List): List = when { this is ChannelStateWithCommitments && this.isZeroReserve -> actions.map { when { - it is ChannelAction.Message.Send && it.message is FundingSigned -> it.copy(message = it.message.copy(channelData = Serialization.encrypt(privateKey.value, this))) - it is ChannelAction.Message.Send && it.message is CommitSig -> it.copy(message = it.message.copy(channelData = Serialization.encrypt(privateKey.value, this))) - it is ChannelAction.Message.Send && it.message is RevokeAndAck -> it.copy(message = it.message.copy(channelData = Serialization.encrypt(privateKey.value, this))) - it is ChannelAction.Message.Send && it.message is ClosingSigned -> it.copy(message = it.message.copy(channelData = Serialization.encrypt(privateKey.value, this))) + it is ChannelAction.Message.Send && it.message is FundingSigned -> it.copy(message = it.message.withChannelData(Serialization.encrypt(privateKey.value, this))) + it is ChannelAction.Message.Send && it.message is CommitSig -> it.copy(message = it.message.withChannelData(Serialization.encrypt(privateKey.value, this))) + it is ChannelAction.Message.Send && it.message is RevokeAndAck -> it.copy(message = it.message.withChannelData(Serialization.encrypt(privateKey.value, this))) + it is ChannelAction.Message.Send && it.message is Shutdown -> it.copy(message = it.message.withChannelData(Serialization.encrypt(privateKey.value, this))) + it is ChannelAction.Message.Send && it.message is ClosingSigned -> it.copy(message = it.message.withChannelData(Serialization.encrypt(privateKey.value, this))) else -> it - } } else -> actions @@ -738,9 +738,8 @@ data class Offline(val state: ChannelStateWithCommitments) : ChannelState() { nextLocalCommitmentNumber = state.commitments.localCommit.index + 1, nextRemoteRevocationNumber = state.commitments.remoteCommit.index, yourLastCommitmentSecret = PrivateKey(yourLastPerCommitmentSecret), - myCurrentPerCommitmentPoint = myCurrentPerCommitmentPoint, - state.commitments.remoteChannelData - ) + myCurrentPerCommitmentPoint = myCurrentPerCommitmentPoint + ).withChannelData(state.commitments.remoteChannelData) logger.info { "c:${state.channelId} syncing ${state::class}" } val nextState = state.updateCommitments(state.commitments.updateFeatures(event.localInit, event.remoteInit)) Pair(Syncing(nextState, false), listOf(ChannelAction.Message.Send(channelReestablish))) @@ -869,9 +868,8 @@ data class Syncing(val state: ChannelStateWithCommitments, val waitForTheirReest nextLocalCommitmentNumber = nextState.commitments.localCommit.index + 1, nextRemoteRevocationNumber = nextState.commitments.remoteCommit.index, yourLastCommitmentSecret = PrivateKey(yourLastPerCommitmentSecret), - myCurrentPerCommitmentPoint = myCurrentPerCommitmentPoint, - nextState.commitments.remoteChannelData - ) + myCurrentPerCommitmentPoint = myCurrentPerCommitmentPoint + ).withChannelData(nextState.commitments.remoteChannelData) val actions = listOf(ChannelAction.Message.Send(channelReestablish)) // now apply their reestablish message to the restored state val (nextState1, actions1) = Syncing(nextState, waitForTheirReestablishMessage = false).processInternal(event) diff --git a/src/commonMain/kotlin/fr/acinq/lightning/wire/ChannelTlv.kt b/src/commonMain/kotlin/fr/acinq/lightning/wire/ChannelTlv.kt index 1ffa569c4..3ffc47234 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/wire/ChannelTlv.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/wire/ChannelTlv.kt @@ -9,6 +9,7 @@ import fr.acinq.lightning.channel.ChannelOrigin import fr.acinq.lightning.channel.ChannelVersion import fr.acinq.lightning.serialization.ByteVectorKSerializer import fr.acinq.lightning.utils.BitField +import fr.acinq.lightning.utils.toByteVector import kotlinx.serialization.Serializable @OptIn(ExperimentalUnsignedTypes::class) @@ -99,3 +100,87 @@ sealed class ChannelTlv : Tlv { } } } + +@Serializable +sealed class FundingSignedTlv : Tlv { + @Serializable + data class ChannelData(val ecb: EncryptedChannelData) : FundingSignedTlv() { + override val tag: Long get() = ChannelData.tag + override fun write(out: Output) = LightningCodecs.writeBytes(ecb.data, out) + + companion object : TlvValueReader { + const val tag: Long = 0x47010000 + override fun read(input: Input): ChannelData = ChannelData(EncryptedChannelData(LightningCodecs.bytes(input, input.availableBytes).toByteVector())) + } + } +} + +@Serializable +sealed class CommitSigTlv : Tlv { + @Serializable + data class ChannelData(val ecb: EncryptedChannelData) : CommitSigTlv() { + override val tag: Long get() = ChannelData.tag + override fun write(out: Output) = LightningCodecs.writeBytes(ecb.data, out) + + companion object : TlvValueReader { + const val tag: Long = 0x47010000 + override fun read(input: Input): ChannelData = ChannelData(EncryptedChannelData(LightningCodecs.bytes(input, input.availableBytes).toByteVector())) + } + } +} + +@Serializable +sealed class RevokeAndAckTlv : Tlv { + @Serializable + data class ChannelData(val ecb: EncryptedChannelData) : RevokeAndAckTlv() { + override val tag: Long get() = ChannelData.tag + override fun write(out: Output) = LightningCodecs.writeBytes(ecb.data, out) + + companion object : TlvValueReader { + const val tag: Long = 0x47010000 + override fun read(input: Input): ChannelData = ChannelData(EncryptedChannelData(LightningCodecs.bytes(input, input.availableBytes).toByteVector())) + } + } +} + +@Serializable +sealed class ChannelReestablishTlv : Tlv { + @Serializable + data class ChannelData(val ecb: EncryptedChannelData) : ChannelReestablishTlv() { + override val tag: Long get() = ChannelData.tag + override fun write(out: Output) = LightningCodecs.writeBytes(ecb.data, out) + + companion object : TlvValueReader { + const val tag: Long = 0x47010000 + override fun read(input: Input): ChannelData = ChannelData(EncryptedChannelData(LightningCodecs.bytes(input, input.availableBytes).toByteVector())) + } + } +} + +@Serializable +sealed class ShutdownTlv : Tlv { + @Serializable + data class ChannelData(val ecb: EncryptedChannelData) : ShutdownTlv() { + override val tag: Long get() = ChannelData.tag + override fun write(out: Output) = LightningCodecs.writeBytes(ecb.data, out) + + companion object : TlvValueReader { + const val tag: Long = 0x47010000 + override fun read(input: Input): ChannelData = ChannelData(EncryptedChannelData(LightningCodecs.bytes(input, input.availableBytes).toByteVector())) + } + } +} + +@Serializable +sealed class ClosingSignedTlv : Tlv { + @Serializable + data class ChannelData(val ecb: EncryptedChannelData) : ClosingSignedTlv() { + override val tag: Long get() = ChannelData.tag + override fun write(out: Output) = LightningCodecs.writeBytes(ecb.data, out) + + companion object : TlvValueReader { + const val tag: Long = 0x47010000 + override fun read(input: Input): ChannelData = ChannelData(EncryptedChannelData(LightningCodecs.bytes(input, input.availableBytes).toByteVector())) + } + } +} diff --git a/src/commonMain/kotlin/fr/acinq/lightning/wire/LightningCodecs.kt b/src/commonMain/kotlin/fr/acinq/lightning/wire/LightningCodecs.kt index d64263393..1beaade12 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/wire/LightningCodecs.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/wire/LightningCodecs.kt @@ -7,8 +7,6 @@ import fr.acinq.bitcoin.io.ByteArrayOutput import fr.acinq.bitcoin.io.Input import fr.acinq.bitcoin.io.Output import fr.acinq.lightning.utils.leftPaddedCopyOf -import fr.acinq.lightning.utils.toByteVector -import fr.acinq.secp256k1.Hex import kotlin.jvm.JvmStatic @OptIn(ExperimentalUnsignedTypes::class) @@ -202,24 +200,4 @@ object LightningCodecs { return bytes(input, length) } - private val channelDataMagic = Hex.decode("fe 47010000").toByteVector() - - fun writeChannelData(msg: ByteArray, out: Output) { - if (msg.isNotEmpty()) { - out.write(channelDataMagic.toByteArray()) - writeBigSize(msg.size.toLong(), out) - writeBytes(msg, out) - } - } - - fun writeChannelData(msg: EncryptedChannelData, out: Output) = writeChannelData(msg.data.toByteArray(), out) - - fun channelData(input: Input): EncryptedChannelData { - if (input.availableBytes <= 5) return EncryptedChannelData.empty - val magic = bytes(input, 5) - if (!channelDataMagic.contentEquals(magic)) return EncryptedChannelData.empty - val length = bigSize(input) - return EncryptedChannelData(bytes(input, length).toByteVector()) - } - } \ No newline at end of file diff --git a/src/commonMain/kotlin/fr/acinq/lightning/wire/LightningMessages.kt b/src/commonMain/kotlin/fr/acinq/lightning/wire/LightningMessages.kt index 2d940f369..5e39641d2 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/wire/LightningMessages.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/wire/LightningMessages.kt @@ -134,11 +134,7 @@ data class EncryptedChannelData(@Serializable(with = ByteVectorKSerializer::clas /** We don't want to log the encrypted channel backups, they take a lot of space. We only keep the first bytes to help correlate mobile/server backups. */ override fun toString(): String { val bytes = data.take(min(data.size(), 10)) - return if (bytes.isEmpty()) { - "" - } else { - "$bytes (truncated)" - } + return if (bytes.isEmpty()) "" else "$bytes (truncated)" } fun isEmpty(): Boolean = data.isEmpty() @@ -150,6 +146,9 @@ data class EncryptedChannelData(@Serializable(with = ByteVectorKSerializer::clas interface HasEncryptedChannelData : LightningMessage { val channelData: EncryptedChannelData + fun withChannelData(data: ByteVector): HasEncryptedChannelData = withChannelData(EncryptedChannelData(data)) + fun withChannelData(ecd: EncryptedChannelData): HasEncryptedChannelData = if (ecd.isEmpty()) this else withNonEmptyChannelData(ecd) + fun withNonEmptyChannelData(ecd: EncryptedChannelData): HasEncryptedChannelData } interface ChannelMessage @@ -457,24 +456,30 @@ data class FundingCreated( data class FundingSigned( @Serializable(with = ByteVector32KSerializer::class) override val channelId: ByteVector32, @Serializable(with = ByteVector64KSerializer::class) val signature: ByteVector64, - override val channelData: EncryptedChannelData = EncryptedChannelData.empty + val tlvStream: TlvStream = TlvStream.empty() ) : ChannelMessage, HasChannelId, HasEncryptedChannelData { override val type: Long get() = FundingSigned.type + override val channelData: EncryptedChannelData get() = tlvStream.get()?.ecb ?: EncryptedChannelData.empty + override fun withNonEmptyChannelData(ecd: EncryptedChannelData): FundingSigned = copy(tlvStream = tlvStream.addOrUpdate(FundingSignedTlv.ChannelData(ecd))) + override fun write(out: Output) { LightningCodecs.writeBytes(channelId, out) LightningCodecs.writeBytes(signature, out) - LightningCodecs.writeChannelData(channelData, out) + TlvStreamSerializer(false, readers).write(tlvStream, out) } companion object : LightningMessageReader { const val type: Long = 35 + @Suppress("UNCHECKED_CAST") + val readers = mapOf(FundingSignedTlv.ChannelData.tag to FundingSignedTlv.ChannelData.Companion as TlvValueReader) + override fun read(input: Input): FundingSigned { return FundingSigned( ByteVector32(LightningCodecs.bytes(input, 32)), ByteVector64(LightningCodecs.bytes(input, 64)), - LightningCodecs.channelData(input) + TlvStreamSerializer(false, readers).read(input) ) } } @@ -635,21 +640,27 @@ data class CommitSig( @Serializable(with = ByteVector32KSerializer::class) override val channelId: ByteVector32, @Serializable(with = ByteVector64KSerializer::class) val signature: ByteVector64, val htlcSignatures: List<@Serializable(with = ByteVector64KSerializer::class) ByteVector64>, - override val channelData: EncryptedChannelData = EncryptedChannelData.empty + val tlvStream: TlvStream = TlvStream.empty() ) : HtlcMessage, HasChannelId, HasEncryptedChannelData { override val type: Long get() = CommitSig.type + override val channelData: EncryptedChannelData get() = tlvStream.get()?.ecb ?: EncryptedChannelData.empty + override fun withNonEmptyChannelData(ecd: EncryptedChannelData): CommitSig = copy(tlvStream = tlvStream.addOrUpdate(CommitSigTlv.ChannelData(ecd))) + override fun write(out: Output) { LightningCodecs.writeBytes(channelId, out) LightningCodecs.writeBytes(signature, out) LightningCodecs.writeU16(htlcSignatures.size, out) htlcSignatures.forEach { LightningCodecs.writeBytes(it, out) } - LightningCodecs.writeChannelData(channelData, out) + TlvStreamSerializer(false, readers).write(tlvStream, out) } companion object : LightningMessageReader { const val type: Long = 132 + @Suppress("UNCHECKED_CAST") + val readers = mapOf(CommitSigTlv.ChannelData.tag to CommitSigTlv.ChannelData.Companion as TlvValueReader) + override fun read(input: Input): CommitSig { val channelId = ByteVector32(LightningCodecs.bytes(input, 32)) val sig = ByteVector64(LightningCodecs.bytes(input, 64)) @@ -658,7 +669,7 @@ data class CommitSig( for (i in 1..numHtlcs) { htlcSigs += ByteVector64(LightningCodecs.bytes(input, 64)) } - return CommitSig(channelId, sig, htlcSigs.toList(), LightningCodecs.channelData(input)) + return CommitSig(channelId, sig, htlcSigs.toList(), TlvStreamSerializer(false, readers).read(input)) } } } @@ -668,26 +679,32 @@ data class RevokeAndAck( override val channelId: ByteVector32, val perCommitmentSecret: PrivateKey, val nextPerCommitmentPoint: PublicKey, - override val channelData: EncryptedChannelData = EncryptedChannelData.empty + val tlvStream: TlvStream = TlvStream.empty() ) : HtlcMessage, HasChannelId, HasEncryptedChannelData { override val type: Long get() = RevokeAndAck.type + override val channelData: EncryptedChannelData get() = tlvStream.get()?.ecb ?: EncryptedChannelData.empty + override fun withNonEmptyChannelData(ecd: EncryptedChannelData): RevokeAndAck = copy(tlvStream = tlvStream.addOrUpdate(RevokeAndAckTlv.ChannelData(ecd))) + override fun write(out: Output) { LightningCodecs.writeBytes(channelId, out) LightningCodecs.writeBytes(perCommitmentSecret.value, out) LightningCodecs.writeBytes(nextPerCommitmentPoint.value, out) - LightningCodecs.writeChannelData(channelData, out) + TlvStreamSerializer(false, readers).write(tlvStream, out) } companion object : LightningMessageReader { const val type: Long = 133 + @Suppress("UNCHECKED_CAST") + val readers = mapOf(RevokeAndAckTlv.ChannelData.tag to RevokeAndAckTlv.ChannelData.Companion as TlvValueReader) + override fun read(input: Input): RevokeAndAck { return RevokeAndAck( ByteVector32(LightningCodecs.bytes(input, 32)), PrivateKey(LightningCodecs.bytes(input, 32)), PublicKey(LightningCodecs.bytes(input, 33)), - LightningCodecs.channelData(input) + TlvStreamSerializer(false, readers).read(input) ) } } @@ -726,22 +743,28 @@ data class ChannelReestablish( val nextRemoteRevocationNumber: Long, @Serializable(with = PrivateKeyKSerializer::class) val yourLastCommitmentSecret: PrivateKey, @Serializable(with = PublicKeyKSerializer::class) val myCurrentPerCommitmentPoint: PublicKey, - override val channelData: EncryptedChannelData = EncryptedChannelData.empty + val tlvStream: TlvStream = TlvStream.empty() ) : HasChannelId, HasEncryptedChannelData { override val type: Long get() = ChannelReestablish.type + override val channelData: EncryptedChannelData get() = tlvStream.get()?.ecb ?: EncryptedChannelData.empty + override fun withNonEmptyChannelData(ecd: EncryptedChannelData): ChannelReestablish = copy(tlvStream = tlvStream.addOrUpdate(ChannelReestablishTlv.ChannelData(ecd))) + override fun write(out: Output) { LightningCodecs.writeBytes(channelId, out) LightningCodecs.writeU64(nextLocalCommitmentNumber, out) LightningCodecs.writeU64(nextRemoteRevocationNumber, out) LightningCodecs.writeBytes(yourLastCommitmentSecret.value, out) LightningCodecs.writeBytes(myCurrentPerCommitmentPoint.value, out) - LightningCodecs.writeChannelData(channelData, out) + TlvStreamSerializer(false, readers).write(tlvStream, out) } companion object : LightningMessageReader { const val type: Long = 136 + @Suppress("UNCHECKED_CAST") + val readers = mapOf(ChannelReestablishTlv.ChannelData.tag to ChannelReestablishTlv.ChannelData.Companion as TlvValueReader) + override fun read(input: Input): ChannelReestablish { return ChannelReestablish( ByteVector32(LightningCodecs.bytes(input, 32)), @@ -749,7 +772,7 @@ data class ChannelReestablish( LightningCodecs.u64(input), PrivateKey(LightningCodecs.bytes(input, 32)), PublicKey(LightningCodecs.bytes(input, 33)), - LightningCodecs.channelData(input) + TlvStreamSerializer(false, readers).read(input) ) } } @@ -940,25 +963,31 @@ data class ChannelUpdate( data class Shutdown( @Serializable(with = ByteVector32KSerializer::class) override val channelId: ByteVector32, @Serializable(with = ByteVectorKSerializer::class) val scriptPubKey: ByteVector, - override val channelData: EncryptedChannelData = EncryptedChannelData.empty + val tlvStream: TlvStream = TlvStream.empty() ) : ChannelMessage, HasChannelId, HasEncryptedChannelData { override val type: Long get() = Shutdown.type + override val channelData: EncryptedChannelData get() = tlvStream.get()?.ecb ?: EncryptedChannelData.empty + override fun withNonEmptyChannelData(ecd: EncryptedChannelData): Shutdown = copy(tlvStream = tlvStream.addOrUpdate(ShutdownTlv.ChannelData(ecd))) + override fun write(out: Output) { LightningCodecs.writeBytes(channelId, out) LightningCodecs.writeU16(scriptPubKey.size(), out) LightningCodecs.writeBytes(scriptPubKey, out) - LightningCodecs.writeChannelData(channelData, out) + TlvStreamSerializer(false, readers).write(tlvStream, out) } companion object : LightningMessageReader { const val type: Long = 38 + @Suppress("UNCHECKED_CAST") + val readers = mapOf(ShutdownTlv.ChannelData.tag to ShutdownTlv.ChannelData.Companion as TlvValueReader) + override fun read(input: Input): Shutdown { return Shutdown( ByteVector32(LightningCodecs.bytes(input, 32)), ByteVector(LightningCodecs.bytes(input, LightningCodecs.u16(input))), - LightningCodecs.channelData(input) + TlvStreamSerializer(false, readers).read(input) ) } } @@ -970,26 +999,32 @@ data class ClosingSigned( @Serializable(with = ByteVector32KSerializer::class) override val channelId: ByteVector32, @Serializable(with = SatoshiKSerializer::class) val feeSatoshis: Satoshi, @Serializable(with = ByteVector64KSerializer::class) val signature: ByteVector64, - override val channelData: EncryptedChannelData = EncryptedChannelData.empty + val tlvStream: TlvStream = TlvStream.empty() ) : ChannelMessage, HasChannelId, HasEncryptedChannelData { override val type: Long get() = ClosingSigned.type + override val channelData: EncryptedChannelData get() = tlvStream.get()?.ecb ?: EncryptedChannelData.empty + override fun withNonEmptyChannelData(ecd: EncryptedChannelData): ClosingSigned = copy(tlvStream = tlvStream.addOrUpdate(ClosingSignedTlv.ChannelData(ecd))) + override fun write(out: Output) { LightningCodecs.writeBytes(channelId, out) LightningCodecs.writeU64(feeSatoshis.toLong(), out) LightningCodecs.writeBytes(signature, out) - LightningCodecs.writeChannelData(channelData, out) + TlvStreamSerializer(false, readers).write(tlvStream, out) } companion object : LightningMessageReader { const val type: Long = 39 + @Suppress("UNCHECKED_CAST") + val readers = mapOf(ClosingSignedTlv.ChannelData.tag to ClosingSignedTlv.ChannelData.Companion as TlvValueReader) + override fun read(input: Input): ClosingSigned { return ClosingSigned( ByteVector32(LightningCodecs.bytes(input, 32)), Satoshi(LightningCodecs.u64(input)), ByteVector64(LightningCodecs.bytes(input, 64)), - LightningCodecs.channelData(input) + TlvStreamSerializer(false, readers).read(input) ) } } diff --git a/src/commonMain/kotlin/fr/acinq/lightning/wire/TlvCodecs.kt b/src/commonMain/kotlin/fr/acinq/lightning/wire/TlvCodecs.kt index 303d339e6..72875248c 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/wire/TlvCodecs.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/wire/TlvCodecs.kt @@ -154,6 +154,27 @@ data class TlvStream(val records: List, val unknown: List addOrUpdate(r: R): TlvStream { + var found = false + val updated = records.map { + if (it is R) { + found = true + r + } else { + it + } + } + return if (found) { + copy(records = updated) + } else { + copy(records = updated + r) + } + } + companion object { fun empty() = TlvStream(listOf(), listOf()) } diff --git a/src/commonTest/kotlin/fr/acinq/lightning/channel/states/NegotiatingTestsCommon.kt b/src/commonTest/kotlin/fr/acinq/lightning/channel/states/NegotiatingTestsCommon.kt index 6014500c7..976159555 100644 --- a/src/commonTest/kotlin/fr/acinq/lightning/channel/states/NegotiatingTestsCommon.kt +++ b/src/commonTest/kotlin/fr/acinq/lightning/channel/states/NegotiatingTestsCommon.kt @@ -19,6 +19,7 @@ import fr.acinq.lightning.wire.Error import fr.acinq.lightning.wire.Shutdown import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertFalse import kotlin.test.assertTrue class NegotiatingTestsCommon : LightningTestSuite() { @@ -104,7 +105,7 @@ class NegotiatingTestsCommon : LightningTestSuite() { @Test fun `recv ClosingSigned (theirCloseFee == ourCloseFee, different fee parameters)`() { - val (alice, bob, aliceCloseSig) = init(true) + val (alice, bob, aliceCloseSig) = init(tweakFees = true) assertTrue { converge(alice, bob, aliceCloseSig) != null } } @@ -137,6 +138,12 @@ class NegotiatingTestsCommon : LightningTestSuite() { actions.findTxs().contains(bob.commitments.localCommit.publishableTxs.commitTx.tx) } + @Test + fun `recv ClosingSigned with encrypted channel data`() { + val (_, _, aliceCloseSig) = init(ChannelVersion.STANDARD or ChannelVersion.ZERO_RESERVE) + assertFalse(aliceCloseSig.channelData.isEmpty()) + } + @Test fun `recv BITCOIN_FUNDING_SPENT (an older mutual close)`() { val (alice, bob, aliceCloseSig) = init() @@ -180,8 +187,8 @@ class NegotiatingTestsCommon : LightningTestSuite() { } companion object { - fun init(tweakFees: Boolean = false, pushMsat: MilliSatoshi = TestConstants.pushMsat): Triple { - val (alice, bob) = reachNormal(pushMsat = pushMsat) + fun init(channelVersion: ChannelVersion = ChannelVersion.STANDARD, tweakFees: Boolean = false, pushMsat: MilliSatoshi = TestConstants.pushMsat): Triple { + val (alice, bob) = reachNormal(channelVersion = channelVersion, pushMsat = pushMsat) return mutualClose(alice, bob, tweakFees) } diff --git a/src/commonTest/kotlin/fr/acinq/lightning/channel/states/OfflineTestsCommon.kt b/src/commonTest/kotlin/fr/acinq/lightning/channel/states/OfflineTestsCommon.kt index a47373dff..8d0d1699e 100644 --- a/src/commonTest/kotlin/fr/acinq/lightning/channel/states/OfflineTestsCommon.kt +++ b/src/commonTest/kotlin/fr/acinq/lightning/channel/states/OfflineTestsCommon.kt @@ -52,7 +52,7 @@ class OfflineTestsCommon : LightningTestSuite() { // alice didn't receive any update or sig assertEquals( ChannelReestablish(alice.channelId, 1, 0, PrivateKey(ByteVector32.Zeroes), aliceCurrentPerCommitmentPoint), - channelReestablishA.copy(channelData = EncryptedChannelData.empty) + channelReestablishA.copy(tlvStream = TlvStream.empty()) ) assertEquals( ChannelReestablish(bob.channelId, 1, 0, PrivateKey(ByteVector32.Zeroes), bobCurrentPerCommitmentPoint), diff --git a/src/commonTest/kotlin/fr/acinq/lightning/channel/states/ShutdownTestsCommon.kt b/src/commonTest/kotlin/fr/acinq/lightning/channel/states/ShutdownTestsCommon.kt index 2b53a916d..38992ff60 100644 --- a/src/commonTest/kotlin/fr/acinq/lightning/channel/states/ShutdownTestsCommon.kt +++ b/src/commonTest/kotlin/fr/acinq/lightning/channel/states/ShutdownTestsCommon.kt @@ -18,6 +18,7 @@ import fr.acinq.lightning.channel.TestsHelper.makeCmdAdd import fr.acinq.lightning.channel.TestsHelper.processEx import fr.acinq.lightning.channel.TestsHelper.reachNormal import fr.acinq.lightning.channel.TestsHelper.signAndRevack +import fr.acinq.lightning.serialization.Serialization import fr.acinq.lightning.tests.TestConstants import fr.acinq.lightning.tests.utils.LightningTestSuite import fr.acinq.lightning.utils.Either @@ -37,6 +38,15 @@ class ShutdownTestsCommon : LightningTestSuite() { assertTrue { actions1.any { it is ChannelAction.ProcessCmdRes.AddFailed && it.error == ChannelUnavailable(bob.channelId) } } } + @Test + fun `recv CMD_ADD_HTLC (zero-reserve)`() { + val (_, bob) = init(ChannelVersion.STANDARD or ChannelVersion.ZERO_RESERVE) + val add = CMD_ADD_HTLC(500000000.msat, r1, cltvExpiry = CltvExpiry(300000), TestConstants.emptyOnionPacket, UUID.randomUUID()) + val (bob1, actions1) = bob.processEx(ChannelEvent.ExecuteCommand(add)) + assertTrue { bob1 is ShuttingDown } + assertTrue { actions1.any { it is ChannelAction.ProcessCmdRes.AddFailed && it.error == ChannelUnavailable(bob.channelId) } } + } + @Test fun `recv CMD_FULFILL_HTLC`() { val (_, bob) = init() @@ -341,6 +351,16 @@ class ShutdownTestsCommon : LightningTestSuite() { actions1.hasOutgoingMessage() } + @Test + fun `recv Shutdown with encrypted channel data`() { + val (alice0, _) = reachNormal(ChannelVersion.STANDARD or ChannelVersion.ZERO_RESERVE) + val (alice1, actions1) = alice0.processEx(ChannelEvent.ExecuteCommand(CMD_CLOSE(null))) + assertTrue(alice1 is Normal) + val blob = Serialization.encrypt(alice1.staticParams.nodeParams.nodePrivateKey.value, alice1) + val shutdown = actions1.findOutgoingMessage() + assertEquals(blob, shutdown.channelData) + } + @Test fun `recv NewBlock (no htlc timed out)`() { val (alice, _) = init() @@ -512,8 +532,8 @@ class ShutdownTestsCommon : LightningTestSuite() { val r1 = randomBytes32() val r2 = randomBytes32() - fun init(currentBlockHeight: Int = TestConstants.defaultBlockHeight): Pair { - val (alice, bob) = reachNormal(ChannelVersion.STANDARD) + fun init(channelVersion: ChannelVersion = ChannelVersion.STANDARD, currentBlockHeight: Int = TestConstants.defaultBlockHeight): Pair { + val (alice, bob) = reachNormal(channelVersion) val (_, cmdAdd1) = makeCmdAdd(300_000_000.msat, bob.staticParams.nodeParams.nodeId, currentBlockHeight.toLong(), r1) val (alice1, actions) = alice.processEx(ChannelEvent.ExecuteCommand(cmdAdd1)) val htlc1 = actions.findOutgoingMessage() @@ -541,6 +561,8 @@ class ShutdownTestsCommon : LightningTestSuite() { val (alice2, _) = alice1.processEx(ChannelEvent.MessageReceived(shutdown1)) assertTrue(alice2 is ShuttingDown) assertTrue(bob1 is ShuttingDown) + if (alice2.commitments.isZeroReserve) assertFalse(shutdown.channelData.isEmpty()) + if (bob1.commitments.isZeroReserve) assertFalse(shutdown1.channelData.isEmpty()) return Pair(alice2, bob1) } } diff --git a/src/commonTest/kotlin/fr/acinq/lightning/channel/states/WaitForFundingConfirmedTestsCommon.kt b/src/commonTest/kotlin/fr/acinq/lightning/channel/states/WaitForFundingConfirmedTestsCommon.kt index b189596e1..5f1d8d954 100644 --- a/src/commonTest/kotlin/fr/acinq/lightning/channel/states/WaitForFundingConfirmedTestsCommon.kt +++ b/src/commonTest/kotlin/fr/acinq/lightning/channel/states/WaitForFundingConfirmedTestsCommon.kt @@ -255,9 +255,8 @@ class WaitForFundingConfirmedTestsCommon : LightningTestSuite() { nextLocalCommitmentNumber = alice.commitments.localCommit.index + 1, nextRemoteRevocationNumber = alice.commitments.remoteCommit.index, yourLastCommitmentSecret = PrivateKey(yourLastPerCommitmentSecret), - myCurrentPerCommitmentPoint = myCurrentPerCommitmentPoint, - alice.commitments.remoteChannelData - ) + myCurrentPerCommitmentPoint = myCurrentPerCommitmentPoint + ).withChannelData(alice.commitments.remoteChannelData) } val (bob3, actions3) = bob2.processEx(ChannelEvent.MessageReceived(channelReestablishAlice)) diff --git a/src/commonTest/kotlin/fr/acinq/lightning/channel/states/WaitForFundingSignedTestsCommon.kt b/src/commonTest/kotlin/fr/acinq/lightning/channel/states/WaitForFundingSignedTestsCommon.kt index 0cbce42f3..b2726ed6e 100644 --- a/src/commonTest/kotlin/fr/acinq/lightning/channel/states/WaitForFundingSignedTestsCommon.kt +++ b/src/commonTest/kotlin/fr/acinq/lightning/channel/states/WaitForFundingSignedTestsCommon.kt @@ -6,6 +6,7 @@ import fr.acinq.lightning.blockchain.BITCOIN_FUNDING_SPENT import fr.acinq.lightning.blockchain.WatchConfirmed import fr.acinq.lightning.blockchain.WatchSpent import fr.acinq.lightning.channel.* +import fr.acinq.lightning.serialization.Serialization import fr.acinq.lightning.tests.TestConstants import fr.acinq.lightning.tests.utils.LightningTestSuite import fr.acinq.lightning.utils.sat @@ -31,6 +32,13 @@ class WaitForFundingSignedTestsCommon : LightningTestSuite() { assertEquals(WatchSpent(alice1.channelId, fundingTx, 0, BITCOIN_FUNDING_SPENT), watchSpent) } + @Test + fun `recv FundingSigned with encrypted channel data`() { + val (_, bob, fundingSigned) = init(ChannelVersion.STANDARD or ChannelVersion.ZERO_RESERVE) + val blob = Serialization.encrypt(bob.staticParams.nodeParams.nodePrivateKey.value, bob) + assertEquals(blob, fundingSigned.channelData) + } + @Test fun `recv FundingSigned with invalid signature`() { val (alice, _, fundingSigned) = init() diff --git a/src/commonTest/kotlin/fr/acinq/lightning/io/peer/PeerTest.kt b/src/commonTest/kotlin/fr/acinq/lightning/io/peer/PeerTest.kt index e1d198e77..8925f98bf 100644 --- a/src/commonTest/kotlin/fr/acinq/lightning/io/peer/PeerTest.kt +++ b/src/commonTest/kotlin/fr/acinq/lightning/io/peer/PeerTest.kt @@ -146,9 +146,8 @@ class PeerTest : LightningTestSuite() { nextLocalCommitmentNumber = syncState.state.commitments.localCommit.index + 1, nextRemoteRevocationNumber = syncState.state.commitments.remoteCommit.index, yourLastCommitmentSecret = PrivateKey(yourLastPerCommitmentSecret), - myCurrentPerCommitmentPoint = myCurrentPerCommitmentPoint, - syncState.state.commitments.remoteChannelData - ) + myCurrentPerCommitmentPoint = myCurrentPerCommitmentPoint + ).withChannelData(syncState.state.commitments.remoteChannelData) val reestablishMsg = LightningMessage.encode(channelReestablish) peer.send(BytesReceived(reestablishMsg)) diff --git a/src/commonTest/kotlin/fr/acinq/lightning/wire/LightningCodecsTestsCommon.kt b/src/commonTest/kotlin/fr/acinq/lightning/wire/LightningCodecsTestsCommon.kt index 8273155a7..2cb1b5ce1 100644 --- a/src/commonTest/kotlin/fr/acinq/lightning/wire/LightningCodecsTestsCommon.kt +++ b/src/commonTest/kotlin/fr/acinq/lightning/wire/LightningCodecsTestsCommon.kt @@ -343,12 +343,12 @@ class LightningCodecsTestsCommon : LightningTestSuite() { @Test fun `encode - decode funding_signed (no channel data)`() { run { - val bin = Hex.decode("0x00232056b684b3a084f17467369e894502541d7e3207bb66ef614d55368d9575c365cf6739d3421d0e7e3b890f974547c0828a03539147e49ae9b80a523ceb8a7397513cf247fdb414fead296b04e5d5fe8e7156836f53559c031d90463dfa633c3b") + val bin = Hex.decode("00232056b684b3a084f17467369e894502541d7e3207bb66ef614d55368d9575c365cf6739d3421d0e7e3b890f974547c0828a03539147e49ae9b80a523ceb8a7397513cf247fdb414fead296b04e5d5fe8e7156836f53559c031d90463dfa633c3b") val decoded = LightningMessage.decode(bin)!! val expected = FundingSigned( ByteVector32("2056b684b3a084f17467369e894502541d7e3207bb66ef614d55368d9575c365"), ByteVector64("cf6739d3421d0e7e3b890f974547c0828a03539147e49ae9b80a523ceb8a7397513cf247fdb414fead296b04e5d5fe8e7156836f53559c031d90463dfa633c3b"), - EncryptedChannelData.empty + TlvStream.empty() ) assertEquals(expected, decoded) val reencoded = LightningMessage.encode(decoded) @@ -359,13 +359,14 @@ class LightningCodecsTestsCommon : LightningTestSuite() { @Test fun `encode - decode funding_signed (small channel data)`() { run { - val bin = - Hex.decode("0x00232056b684b3a084f17467369e894502541d7e3207bb66ef614d55368d9575c365cf6739d3421d0e7e3b890f974547c0828a03539147e49ae9b80a523ceb8a7397513cf247fdb414fead296b04e5d5fe8e7156836f53559c031d90463dfa633c3bfe47010000080101010101010101") + val bin = Hex.decode( + "00232056b684b3a084f17467369e894502541d7e3207bb66ef614d55368d9575c365cf6739d3421d0e7e3b890f974547c0828a03539147e49ae9b80a523ceb8a7397513cf247fdb414fead296b04e5d5fe8e7156836f53559c031d90463dfa633c3bfe47010000080101010101010101" + ) val decoded = LightningMessage.decode(bin)!! val expected = FundingSigned( ByteVector32("2056b684b3a084f17467369e894502541d7e3207bb66ef614d55368d9575c365"), ByteVector64("cf6739d3421d0e7e3b890f974547c0828a03539147e49ae9b80a523ceb8a7397513cf247fdb414fead296b04e5d5fe8e7156836f53559c031d90463dfa633c3b"), - EncryptedChannelData(ByteVector("0101010101010101")) + TlvStream(listOf(FundingSignedTlv.ChannelData(EncryptedChannelData(ByteVector("0101010101010101"))))) ) assertEquals(expected, decoded) val reencoded = LightningMessage.encode(decoded) @@ -383,7 +384,7 @@ class LightningCodecsTestsCommon : LightningTestSuite() { val expected = FundingSigned( ByteVector32("2056b684b3a084f17467369e894502541d7e3207bb66ef614d55368d9575c365"), ByteVector64("cf6739d3421d0e7e3b890f974547c0828a03539147e49ae9b80a523ceb8a7397513cf247fdb414fead296b04e5d5fe8e7156836f53559c031d90463dfa633c3b"), - EncryptedChannelData(ByteArray(1300) { 1.toByte() }.toByteVector()) + TlvStream(listOf(FundingSignedTlv.ChannelData(EncryptedChannelData(ByteArray(1300) { 1.toByte() }.toByteVector())))) ) assertEquals(expected, decoded) val reencoded = LightningMessage.encode(decoded) @@ -494,56 +495,57 @@ class LightningCodecsTestsCommon : LightningTestSuite() { val point = randomKey().publicKey() val randomData = randomBytes(42) - //@formatter:off + // @formatter:off val refs = mapOf( - Pair(Hex.decode("0023") + channelId.toByteArray() + signature.toByteArray(), Hex.decode("")) to FundingSigned(channelId, signature), - Pair(Hex.decode("0023") + channelId.toByteArray() + signature.toByteArray(), Hex.decode("deadbeef")) to FundingSigned(channelId, signature), - Pair(Hex.decode("0023") + channelId.toByteArray() + signature.toByteArray() + Hex.decode("fe47010000 00"), Hex.decode("")) to FundingSigned(channelId, signature), - Pair(Hex.decode("0023") + channelId.toByteArray() + signature.toByteArray() + Hex.decode("fe47010000 00"), Hex.decode("deadbeef")) to FundingSigned(channelId, signature), - Pair(Hex.decode("0023") + channelId.toByteArray() + signature.toByteArray() + Hex.decode("fe47010000 07 cccccccccccccc"), Hex.decode("")) to FundingSigned(channelId, signature, EncryptedChannelData(ByteVector("cccccccccccccc"))), - Pair(Hex.decode("0023") + channelId.toByteArray() + signature.toByteArray() + Hex.decode("fe47010000 07 cccccccccccccc"), Hex.decode("deadbeef")) to FundingSigned(channelId, signature, EncryptedChannelData(ByteVector("cccccccccccccc"))), - Pair(Hex.decode("0088") + channelId.toByteArray() + Hex.decode("0001020304050607 0809aabbccddeeff") + key.value.toByteArray() + point.value.toByteArray(), Hex.decode("")) to ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point), - Pair(Hex.decode("0088") + channelId.toByteArray() + Hex.decode("0001020304050607 0809aabbccddeeff") + key.value.toByteArray() + point.value.toByteArray(), Hex.decode("deadbeef")) to ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point), - Pair(Hex.decode("0088") + channelId.toByteArray() + Hex.decode("0001020304050607 0809aabbccddeeff") + key.value.toByteArray() + point.value.toByteArray() + Hex.decode("fe47010000 00"), Hex.decode("")) to ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point), - Pair(Hex.decode("0088") + channelId.toByteArray() + Hex.decode("0001020304050607 0809aabbccddeeff") + key.value.toByteArray() + point.value.toByteArray() + Hex.decode("fe47010000 00"), Hex.decode("deadbeef")) to ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point), - Pair(Hex.decode("0088") + channelId.toByteArray() + Hex.decode("0001020304050607 0809aabbccddeeff") + key.value.toByteArray() + point.value.toByteArray() + Hex.decode("fe47010000 07 bbbbbbbbbbbbbb"), Hex.decode("")) to ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point, EncryptedChannelData(ByteVector("bbbbbbbbbbbbbb"))), - Pair(Hex.decode("0088") + channelId.toByteArray() + Hex.decode("0001020304050607 0809aabbccddeeff") + key.value.toByteArray() + point.value.toByteArray() + Hex.decode("fe47010000 07 bbbbbbbbbbbbbb"), Hex.decode("deadbeef")) to ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point, EncryptedChannelData(ByteVector("bbbbbbbbbbbbbb"))), - - Pair(Hex.decode("0084") + channelId.toByteArray() + signature.toByteArray() + Hex.decode("0000"), Hex.decode("")) to CommitSig(channelId, signature, listOf()), - Pair(Hex.decode("0084") + channelId.toByteArray() + signature.toByteArray() + Hex.decode("0000"), Hex.decode("deadbeef")) to CommitSig(channelId, signature, listOf()), - Pair(Hex.decode("0084") + channelId.toByteArray() + signature.toByteArray() + Hex.decode("0000 fe47010000 00"), Hex.decode("")) to CommitSig(channelId, signature, listOf()), - Pair(Hex.decode("0084") + channelId.toByteArray() + signature.toByteArray() + Hex.decode("0000 fe47010000 00"), Hex.decode("deadbeef")) to CommitSig(channelId, signature, listOf()), - Pair(Hex.decode("0084") + channelId.toByteArray() + signature.toByteArray() + Hex.decode("0000 fe47010000 07 cccccccccccccc"), Hex.decode("")) to CommitSig(channelId, signature, listOf(), EncryptedChannelData(ByteVector("cccccccccccccc"))), - Pair(Hex.decode("0084") + channelId.toByteArray() + signature.toByteArray() + Hex.decode("0000 fe47010000 07 cccccccccccccc"), Hex.decode("deadbeef")) to CommitSig(channelId, signature, listOf(), EncryptedChannelData(ByteVector("cccccccccccccc"))), - - Pair(Hex.decode("0085") + channelId.toByteArray() + key.value.toByteArray() + point.value.toByteArray(), Hex.decode("")) to RevokeAndAck(channelId, key, point), - Pair(Hex.decode("0085") + channelId.toByteArray() + key.value.toByteArray() + point.value.toByteArray(), Hex.decode("deadbeef")) to RevokeAndAck(channelId, key, point), - Pair(Hex.decode("0085") + channelId.toByteArray() + key.value.toByteArray() + point.value.toByteArray() + Hex.decode("fe47010000 00"), Hex.decode("")) to RevokeAndAck(channelId, key, point), - Pair(Hex.decode("0085") + channelId.toByteArray() + key.value.toByteArray() + point.value.toByteArray() + Hex.decode("fe47010000 00"), Hex.decode("deadbeef")) to RevokeAndAck(channelId, key, point), - Pair(Hex.decode("0085") + channelId.toByteArray() + key.value.toByteArray() + point.value.toByteArray() + Hex.decode("fe47010000 07 cccccccccccccc"), Hex.decode("")) to RevokeAndAck(channelId, key, point, EncryptedChannelData(ByteVector("cccccccccccccc"))), - Pair(Hex.decode("0085") + channelId.toByteArray() + key.value.toByteArray() + point.value.toByteArray() + Hex.decode("fe47010000 07 cccccccccccccc"), Hex.decode("deadbeef")) to RevokeAndAck(channelId, key, point, EncryptedChannelData(ByteVector("cccccccccccccc"))), - - Pair(Hex.decode("0026") + channelId.toByteArray()+ Hex.decode("002a") + randomData, Hex.decode("")) to Shutdown(channelId, randomData.toByteVector()), - Pair(Hex.decode("0026") + channelId.toByteArray()+ Hex.decode("002a") + randomData, Hex.decode("deadbeef")) to Shutdown(channelId, randomData.toByteVector()), - Pair(Hex.decode("0026") + channelId.toByteArray()+ Hex.decode("002a") + randomData + Hex.decode("fe47010000 00"), Hex.decode("")) to Shutdown(channelId, randomData.toByteVector()), - Pair(Hex.decode("0026") + channelId.toByteArray()+ Hex.decode("002a") + randomData + Hex.decode("fe47010000 00"), Hex.decode("deadbeef")) to Shutdown(channelId, randomData.toByteVector()), - Pair(Hex.decode("0026") + channelId.toByteArray()+ Hex.decode("002a") + randomData + Hex.decode("fe47010000 07 cccccccccccccc"), Hex.decode("")) to Shutdown(channelId, randomData.toByteVector(), EncryptedChannelData(ByteVector("cccccccccccccc"))), - Pair(Hex.decode("0026") + channelId.toByteArray()+ Hex.decode("002a") + randomData + Hex.decode("fe47010000 07 cccccccccccccc"), Hex.decode("deadbeef")) to Shutdown(channelId, randomData.toByteVector(), EncryptedChannelData(ByteVector("cccccccccccccc"))), - - Pair(Hex.decode("0027") + channelId.toByteArray()+ Hex.decode("00000000075bcd15") + signature.toByteArray(), Hex.decode("")) to ClosingSigned(channelId, 123456789.sat, signature), - Pair(Hex.decode("0027") + channelId.toByteArray()+ Hex.decode("00000000075bcd15") + signature.toByteArray(), Hex.decode("deadbeef")) to ClosingSigned(channelId, 123456789.sat, signature), - Pair(Hex.decode("0027") + channelId.toByteArray()+ Hex.decode("00000000075bcd15") + signature.toByteArray() + Hex.decode("fe47010000 00"), Hex.decode("")) to ClosingSigned(channelId, 123456789.sat, signature), - Pair(Hex.decode("0027") + channelId.toByteArray()+ Hex.decode("00000000075bcd15") + signature.toByteArray() + Hex.decode("fe47010000 00"), Hex.decode("deadbeef")) to ClosingSigned(channelId, 123456789.sat, signature), - Pair(Hex.decode("0027") + channelId.toByteArray()+ Hex.decode("00000000075bcd15") + signature.toByteArray() + Hex.decode("fe47010000 07 cccccccccccccc"), Hex.decode("")) to ClosingSigned(channelId, 123456789.sat, signature, EncryptedChannelData(ByteVector("cccccccccccccc"))), - Pair(Hex.decode("0027") + channelId.toByteArray()+ Hex.decode("00000000075bcd15") + signature.toByteArray() + Hex.decode("fe47010000 07 cccccccccccccc"), Hex.decode("deadbeef")) to ClosingSigned(channelId, 123456789.sat, signature, EncryptedChannelData(ByteVector("cccccccccccccc"))) + Hex.decode("0023") + channelId.toByteArray() + signature.toByteArray() to FundingSigned(channelId, signature), + Hex.decode("0023") + channelId.toByteArray() + signature.toByteArray() + Hex.decode("01 02 0102") to FundingSigned(channelId, signature, TlvStream(listOf(), listOf(GenericTlv(1, ByteVector("0102"))))), + Hex.decode("0023") + channelId.toByteArray() + signature.toByteArray() + Hex.decode("fe47010000 00") to FundingSigned(channelId, signature, TlvStream(listOf(FundingSignedTlv.ChannelData(EncryptedChannelData.empty)))), + Hex.decode("0023") + channelId.toByteArray() + signature.toByteArray() + Hex.decode("01 02 0102") + Hex.decode("fe47010000 00") to FundingSigned(channelId, signature, TlvStream(listOf(FundingSignedTlv.ChannelData(EncryptedChannelData.empty)), listOf(GenericTlv(1, ByteVector("0102"))))), + Hex.decode("0023") + channelId.toByteArray() + signature.toByteArray() + Hex.decode("fe47010000 07 cccccccccccccc") to FundingSigned(channelId, signature).withChannelData(ByteVector("cccccccccccccc")), + Hex.decode("0023") + channelId.toByteArray() + signature.toByteArray() + Hex.decode("01 02 0102") + Hex.decode("fe47010000 07 cccccccccccccc") to FundingSigned(channelId, signature, TlvStream(listOf(FundingSignedTlv.ChannelData(EncryptedChannelData(ByteVector("cccccccccccccc")))), listOf(GenericTlv(1, ByteVector("0102"))))), + + Hex.decode("0088") + channelId.toByteArray() + Hex.decode("0001020304050607 0809aabbccddeeff") + key.value.toByteArray() + point.value.toByteArray() to ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point), + Hex.decode("0088") + channelId.toByteArray() + Hex.decode("0001020304050607 0809aabbccddeeff") + key.value.toByteArray() + point.value.toByteArray() + Hex.decode("01 02 0102") to ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point, TlvStream(listOf(), listOf(GenericTlv(1, ByteVector("0102"))))), + Hex.decode("0088") + channelId.toByteArray() + Hex.decode("0001020304050607 0809aabbccddeeff") + key.value.toByteArray() + point.value.toByteArray() + Hex.decode("fe47010000 00") to ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point, TlvStream(listOf(ChannelReestablishTlv.ChannelData(EncryptedChannelData.empty)))), + Hex.decode("0088") + channelId.toByteArray() + Hex.decode("0001020304050607 0809aabbccddeeff") + key.value.toByteArray() + point.value.toByteArray() + Hex.decode("01 02 0102") + Hex.decode("fe47010000 00") to ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point, TlvStream(listOf(ChannelReestablishTlv.ChannelData(EncryptedChannelData(ByteVector.empty))), listOf(GenericTlv(1, ByteVector("0102"))))), + Hex.decode("0088") + channelId.toByteArray() + Hex.decode("0001020304050607 0809aabbccddeeff") + key.value.toByteArray() + point.value.toByteArray() + Hex.decode("fe47010000 07 bbbbbbbbbbbbbb") to ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point).withChannelData(ByteVector("bbbbbbbbbbbbbb")), + Hex.decode("0088") + channelId.toByteArray() + Hex.decode("0001020304050607 0809aabbccddeeff") + key.value.toByteArray() + point.value.toByteArray() + Hex.decode("01 02 0102") + Hex.decode("fe47010000 07 bbbbbbbbbbbbbb") to ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point, TlvStream(listOf(ChannelReestablishTlv.ChannelData(EncryptedChannelData(ByteVector("bbbbbbbbbbbbbb")))), listOf(GenericTlv(1, ByteVector("0102"))))), + + Hex.decode("0084") + channelId.toByteArray() + signature.toByteArray() + Hex.decode("0000") to CommitSig(channelId, signature, listOf()), + Hex.decode("0084") + channelId.toByteArray() + signature.toByteArray() + Hex.decode("0000") + Hex.decode("01 02 0102") to CommitSig(channelId, signature, listOf(), TlvStream(listOf(), listOf(GenericTlv(1, ByteVector("0102"))))), + Hex.decode("0084") + channelId.toByteArray() + signature.toByteArray() + Hex.decode("0000 fe47010000 00") to CommitSig(channelId, signature, listOf(), TlvStream(listOf(CommitSigTlv.ChannelData(EncryptedChannelData.empty)))), + Hex.decode("0084") + channelId.toByteArray() + signature.toByteArray() + Hex.decode("0000 01020102 fe47010000 00") to CommitSig(channelId, signature, listOf(), TlvStream(listOf(CommitSigTlv.ChannelData(EncryptedChannelData.empty)), listOf(GenericTlv(1, ByteVector("0102"))))), + Hex.decode("0084") + channelId.toByteArray() + signature.toByteArray() + Hex.decode("0000 fe47010000 07 cccccccccccccc") to CommitSig(channelId, signature, listOf()).withChannelData(ByteVector("cccccccccccccc")), + Hex.decode("0084") + channelId.toByteArray() + signature.toByteArray() + Hex.decode("0000 01020102 fe47010000 07 cccccccccccccc") to CommitSig(channelId, signature, listOf(), TlvStream(listOf(CommitSigTlv.ChannelData(EncryptedChannelData(ByteVector("cccccccccccccc")))), listOf(GenericTlv(1, ByteVector("0102"))))), + + Hex.decode("0085") + channelId.toByteArray() + key.value.toByteArray() + point.value.toByteArray() to RevokeAndAck(channelId, key, point), + Hex.decode("0085") + channelId.toByteArray() + key.value.toByteArray() + point.value.toByteArray() + Hex.decode("01 02 0102") to RevokeAndAck(channelId, key, point, TlvStream(listOf(), listOf(GenericTlv(1, ByteVector("0102"))))), + Hex.decode("0085") + channelId.toByteArray() + key.value.toByteArray() + point.value.toByteArray() + Hex.decode("fe47010000 00") to RevokeAndAck(channelId, key, point, TlvStream(listOf(RevokeAndAckTlv.ChannelData(EncryptedChannelData.empty)))), + Hex.decode("0085") + channelId.toByteArray() + key.value.toByteArray() + point.value.toByteArray() + Hex.decode("01 02 0102") + Hex.decode("fe47010000 00") to RevokeAndAck(channelId, key, point, TlvStream(listOf(RevokeAndAckTlv.ChannelData(EncryptedChannelData.empty)), listOf(GenericTlv(1, ByteVector("0102"))))), + Hex.decode("0085") + channelId.toByteArray() + key.value.toByteArray() + point.value.toByteArray() + Hex.decode("fe47010000 07 cccccccccccccc") to RevokeAndAck(channelId, key, point).withChannelData(ByteVector("cccccccccccccc")), + Hex.decode("0085") + channelId.toByteArray() + key.value.toByteArray() + point.value.toByteArray() + Hex.decode("01 02 0102") + Hex.decode("fe47010000 07 cccccccccccccc") to RevokeAndAck(channelId, key, point, TlvStream(listOf(RevokeAndAckTlv.ChannelData(EncryptedChannelData(ByteVector("cccccccccccccc")))), listOf(GenericTlv(1, ByteVector("0102"))))), + + Hex.decode("0026") + channelId.toByteArray() + Hex.decode("002a") + randomData to Shutdown(channelId, randomData.toByteVector()), + Hex.decode("0026") + channelId.toByteArray() + Hex.decode("002a") + randomData + Hex.decode("01 02 0102") to Shutdown(channelId, randomData.toByteVector(), TlvStream(listOf(), listOf(GenericTlv(1, ByteVector("0102"))))), + Hex.decode("0026") + channelId.toByteArray() + Hex.decode("002a") + randomData + Hex.decode("fe47010000 00") to Shutdown(channelId, randomData.toByteVector(), TlvStream(listOf(ShutdownTlv.ChannelData(EncryptedChannelData.empty)))), + Hex.decode("0026") + channelId.toByteArray() + Hex.decode("002a") + randomData + Hex.decode("01 02 0102") + Hex.decode("fe47010000 00") to Shutdown(channelId, randomData.toByteVector(), TlvStream(listOf(ShutdownTlv.ChannelData(EncryptedChannelData.empty)), listOf(GenericTlv(1, ByteVector("0102"))))), + Hex.decode("0026") + channelId.toByteArray() + Hex.decode("002a") + randomData + Hex.decode("fe47010000 07 cccccccccccccc") to Shutdown(channelId, randomData.toByteVector()).withChannelData(ByteVector("cccccccccccccc")), + Hex.decode("0026") + channelId.toByteArray() + Hex.decode("002a") + randomData + Hex.decode("01 02 0102") + Hex.decode("fe47010000 07 cccccccccccccc") to Shutdown(channelId, randomData.toByteVector(), TlvStream(listOf(ShutdownTlv.ChannelData(EncryptedChannelData(ByteVector("cccccccccccccc")))), listOf(GenericTlv(1, ByteVector("0102"))))), + + Hex.decode("0027") + channelId.toByteArray() + Hex.decode("00000000075bcd15") + signature.toByteArray() to ClosingSigned(channelId, 123456789.sat, signature), + Hex.decode("0027") + channelId.toByteArray() + Hex.decode("00000000075bcd15") + signature.toByteArray() + Hex.decode("01 02 0102") to ClosingSigned(channelId, 123456789.sat, signature, TlvStream(listOf(), listOf(GenericTlv(1, ByteVector("0102"))))), + Hex.decode("0027") + channelId.toByteArray() + Hex.decode("00000000075bcd15") + signature.toByteArray() + Hex.decode("fe47010000 00") to ClosingSigned(channelId, 123456789.sat, signature, TlvStream(listOf(ClosingSignedTlv.ChannelData(EncryptedChannelData.empty)))), + Hex.decode("0027") + channelId.toByteArray() + Hex.decode("00000000075bcd15") + signature.toByteArray() + Hex.decode("01 02 0102") + Hex.decode("fe47010000 00") to ClosingSigned(channelId, 123456789.sat, signature, TlvStream(listOf(ClosingSignedTlv.ChannelData(EncryptedChannelData.empty)), listOf(GenericTlv(1, ByteVector("0102"))))), + Hex.decode("0027") + channelId.toByteArray() + Hex.decode("00000000075bcd15") + signature.toByteArray() + Hex.decode("fe47010000 07 cccccccccccccc") to ClosingSigned(channelId, 123456789.sat, signature).withChannelData(ByteVector("cccccccccccccc")), + Hex.decode("0027") + channelId.toByteArray() + Hex.decode("00000000075bcd15") + signature.toByteArray() + Hex.decode("01 02 0102") + Hex.decode("fe47010000 07 cccccccccccccc") to ClosingSigned(channelId, 123456789.sat, signature, TlvStream(listOf(ClosingSignedTlv.ChannelData(EncryptedChannelData(ByteVector("cccccccccccccc")))), listOf(GenericTlv(1, ByteVector("0102"))))) ) - //@formatter:on + // @formatter:on refs.forEach { - val decoded = LightningMessage.decode(it.key.first + it.key.second) + val decoded = LightningMessage.decode(it.key) assertEquals(it.value, decoded) val encoded = LightningMessage.encode(it.value) - assertArrayEquals(it.key.first, encoded) + assertArrayEquals(it.key, encoded) } } diff --git a/src/commonTest/kotlin/fr/acinq/lightning/wire/TlvTestsCommon.kt b/src/commonTest/kotlin/fr/acinq/lightning/wire/TlvTestsCommon.kt index f6d7f4d1c..39d386e2a 100644 --- a/src/commonTest/kotlin/fr/acinq/lightning/wire/TlvTestsCommon.kt +++ b/src/commonTest/kotlin/fr/acinq/lightning/wire/TlvTestsCommon.kt @@ -307,6 +307,24 @@ class TlvTestsCommon : LightningTestSuite() { assertNull(stream.get()) } + @Test + fun `add TLV field to stream`() { + val stream = TlvStream(listOf(TestTlv.TestType1(561), TestTlv.TestType254(1702)), listOf(GenericTlv(13, ByteVector("2a")))) + val updated = stream.addOrUpdate(TestTlv.TestType2(ShortChannelId(561))) + assertEquals(TestTlv.TestType2(ShortChannelId(561)), updated.get()) + val expected = TlvStream(listOf(TestTlv.TestType1(561), TestTlv.TestType254(1702), TestTlv.TestType2(ShortChannelId(561))), listOf(GenericTlv(13, ByteVector("2a")))) + assertEquals(updated, expected) + } + + @Test + fun `replace TLV field in stream`() { + val stream = TlvStream(listOf(TestTlv.TestType1(561), TestTlv.TestType254(1702)), listOf(GenericTlv(13, ByteVector("2a")))) + val updated = stream.addOrUpdate(TestTlv.TestType1(562)) + assertEquals(TestTlv.TestType1(562), updated.get()) + val expected = TlvStream(listOf(TestTlv.TestType1(562), TestTlv.TestType254(1702)), listOf(GenericTlv(13, ByteVector("2a")))) + assertEquals(updated, expected) + } + // See https://github.com/lightningnetwork/lightning-rfc/blob/master/01-messaging.md#appendix-a-type-length-value-test-vectors companion object { sealed class TestTlv : Tlv {