Skip to content

Commit f52c3dd

Browse files
authored
Decode warning messages (#1854)
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 f857368 commit f52c3dd

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

+7-1
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: {}", 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

+5
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

+14
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,20 @@ case class Init(features: Features, tlvs: TlvStream[InitTlv] = TlvStream.empty)
5151
val networks = tlvs.get[InitTlv.Networks].map(_.chainHashes).getOrElse(Nil)
5252
}
5353

54+
case class Warning(channelId: ByteVector32, data: ByteVector) extends SetupMessage with HasChannelId {
55+
// @formatter:off
56+
val isGlobal: Boolean = channelId == ByteVector32.Zeroes
57+
def toAscii: String = if (fr.acinq.eclair.isAsciiPrintable(data)) new String(data.toArray, StandardCharsets.US_ASCII) else "n/a"
58+
// @formatter:on
59+
}
60+
61+
object Warning {
62+
// @formatter:off
63+
def apply(channelId: ByteVector32, msg: String): Warning = Warning(channelId, ByteVector.view(msg.getBytes(Charsets.US_ASCII)))
64+
def apply(msg: String): Warning = Warning(ByteVector32.Zeroes, ByteVector.view(msg.getBytes(Charsets.US_ASCII)))
65+
// @formatter:on
66+
}
67+
5468
case class Error(channelId: ByteVector32, data: ByteVector) extends SetupMessage with HasChannelId {
5569
def toAscii: String = if (fr.acinq.eclair.isAsciiPrintable(data)) new String(data.toArray, StandardCharsets.US_ASCII) else "n/a"
5670
}

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

+15-1
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)