Skip to content

Commit 19acba9

Browse files
committed
Decode warning messages
Add support for logging warning messages as introduced in lightning/bolts#834 Support for sending warning messages instead of current errors will be added in a later PR.
1 parent 85ed433 commit 19acba9

File tree

4 files changed

+41
-2
lines changed

4 files changed

+41
-2
lines changed

eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import fr.acinq.eclair.io.Monitoring.Metrics
3434
import fr.acinq.eclair.io.PeerConnection.KillReason
3535
import fr.acinq.eclair.remote.EclairInternalsSerializer.RemoteTypes
3636
import fr.acinq.eclair.wire.protocol
37-
import fr.acinq.eclair.wire.protocol.{Error, HasChannelId, HasTemporaryChannelId, LightningMessage, NodeAddress, RoutingMessage, UnknownMessage}
37+
import fr.acinq.eclair.wire.protocol.{Error, HasChannelId, HasTemporaryChannelId, LightningMessage, NodeAddress, RoutingMessage, UnknownMessage, Warning}
3838
import scodec.bits.ByteVector
3939

4040
import java.net.InetSocketAddress
@@ -105,6 +105,12 @@ class Peer(val nodeParams: NodeParams, remoteNodeId: PublicKey, wallet: EclairWa
105105
d.peerConnection forward msg
106106
stay
107107

108+
case Event(warning: Warning, _: ConnectedData) =>
109+
log.warning("peer sent warning: channelId={} message={}", warning.channelId, warning.toAscii)
110+
// NB: we don't forward warnings to the channel actors, they shouldn't take any automatic action.
111+
// It's up to the node operator to decide what to do to address the warning.
112+
stay
113+
108114
case Event(err@Error(channelId, reason), d: ConnectedData) if channelId == CHANNELID_ZERO =>
109115
log.error(s"connection-level error, failing all channels! reason=${new String(reason.toArray)}")
110116
d.channels.values.toSet[ActorRef].foreach(_ forward err) // we deduplicate with toSet because there might be two entries per channel (tmp id and final id)

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ object LightningMessageCodecs {
4949
("channelId" | bytes32) ::
5050
("data" | varsizebinarydata)).as[Error]
5151

52+
val warningCodec: Codec[Warning] = (
53+
("channelId" | bytes32) ::
54+
("data" | varsizebinarydata)).as[Warning]
55+
5256
val pingCodec: Codec[Ping] = (
5357
("pongLength" | uint16) ::
5458
("data" | varsizebinarydata)).as[Ping]
@@ -302,6 +306,7 @@ object LightningMessageCodecs {
302306
).as[UnknownMessage]
303307

304308
val lightningMessageCodec = discriminated[LightningMessage].by(uint16)
309+
.typecase(1, warningCodec)
305310
.typecase(16, initCodec)
306311
.typecase(17, errorCodec)
307312
.typecase(18, pingCodec)

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,20 @@ object Error {
5959
def apply(channelId: ByteVector32, msg: String): Error = Error(channelId, ByteVector.view(msg.getBytes(Charsets.US_ASCII)))
6060
}
6161

62+
case class Warning(channelId: ByteVector32, data: ByteVector) extends SetupMessage with HasChannelId {
63+
// @formatter:off
64+
val isGlobal: Boolean = channelId == ByteVector32.Zeroes
65+
def toAscii: String = if (fr.acinq.eclair.isAsciiPrintable(data)) new String(data.toArray, StandardCharsets.US_ASCII) else "n/a"
66+
// @formatter:on
67+
}
68+
69+
object Warning {
70+
// @formatter:off
71+
def apply(channelId: ByteVector32, msg: String): Warning = Warning(channelId, ByteVector.view(msg.getBytes(Charsets.US_ASCII)))
72+
def apply(msg: String): Warning = Warning(ByteVector32.Zeroes, ByteVector.view(msg.getBytes(Charsets.US_ASCII)))
73+
// @formatter:on
74+
}
75+
6276
case class Ping(pongLength: Int, data: ByteVector) extends SetupMessage
6377

6478
case class Pong(data: ByteVector) extends SetupMessage

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,20 @@ class LightningMessageCodecsSpec extends AnyFunSuite {
7979
}
8080
}
8181

82+
test("encode/decode warning") {
83+
val testCases = Seq(
84+
Warning("") -> hex"000100000000000000000000000000000000000000000000000000000000000000000000",
85+
Warning("connection-level issue") -> hex"0x000100000000000000000000000000000000000000000000000000000000000000000016636f6e6e656374696f6e2d6c6576656c206973737565",
86+
Warning(ByteVector32.One, "") -> hex"000101000000000000000000000000000000000000000000000000000000000000000000",
87+
Warning(ByteVector32.One, "channel-specific issue") -> hex"0x0001010000000000000000000000000000000000000000000000000000000000000000166368616e6e656c2d7370656369666963206973737565"
88+
)
89+
90+
for ((warning, expected) <- testCases) {
91+
assert(lightningMessageCodec.encode(warning).require.bytes === expected)
92+
assert(lightningMessageCodec.decode(expected.bits).require.value === warning)
93+
}
94+
}
95+
8296
test("encode/decode live node_announcements") {
8397
val ann = hex"a58338c9660d135fd7d087eb62afd24a33562c54507a9334e79f0dc4f17d407e6d7c61f0e2f3d0d38599502f61704cf1ae93608df027014ade7ff592f27ce2690001025acdf50702d2eabbbacc7c25bbd73b39e65d28237705f7bde76f557e94fb41cb18a9ec00841122116c6e302e646563656e7465722e776f726c64000000000000000000000000000000130200000000000000000000ffffae8a0b082607"
8498
val bin = ann.bits
@@ -212,7 +226,7 @@ class LightningMessageCodecsSpec extends AnyFunSuite {
212226
}
213227
}
214228

215-
test("Unknown messages") {
229+
test("unknown messages") {
216230
// Non-standard tag number so this message can only be handled by a codec with a fallback
217231
val unknown = UnknownMessage(tag = 47282, data = ByteVector32.Zeroes.bytes)
218232
assert(lightningMessageCodec.encode(unknown).isFailure)

0 commit comments

Comments
 (0)