Skip to content

Commit

Permalink
Decode warning messages (#1854)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
t-bast authored Jul 1, 2021
1 parent f857368 commit f52c3dd
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 2 deletions.
8 changes: 7 additions & 1 deletion eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import fr.acinq.eclair.io.Monitoring.Metrics
import fr.acinq.eclair.io.PeerConnection.KillReason
import fr.acinq.eclair.remote.EclairInternalsSerializer.RemoteTypes
import fr.acinq.eclair.wire.protocol
import fr.acinq.eclair.wire.protocol.{Error, HasChannelId, HasTemporaryChannelId, LightningMessage, NodeAddress, RoutingMessage, UnknownMessage}
import fr.acinq.eclair.wire.protocol.{Error, HasChannelId, HasTemporaryChannelId, LightningMessage, NodeAddress, RoutingMessage, UnknownMessage, Warning}
import scodec.bits.ByteVector

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

case Event(warning: Warning, _: ConnectedData) =>
log.warning("peer sent warning: {}", warning.channelId, warning.toAscii)
// NB: we don't forward warnings to the channel actors, they shouldn't take any automatic action.
// It's up to the node operator to decide what to do to address the warning.
stay

case Event(err@Error(channelId, reason), d: ConnectedData) if channelId == CHANNELID_ZERO =>
log.error(s"connection-level error, failing all channels! reason=${new String(reason.toArray)}")
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ object LightningMessageCodecs {
("channelId" | bytes32) ::
("data" | varsizebinarydata)).as[Error]

val warningCodec: Codec[Warning] = (
("channelId" | bytes32) ::
("data" | varsizebinarydata)).as[Warning]

val pingCodec: Codec[Ping] = (
("pongLength" | uint16) ::
("data" | varsizebinarydata)).as[Ping]
Expand Down Expand Up @@ -302,6 +306,7 @@ object LightningMessageCodecs {
).as[UnknownMessage]

val lightningMessageCodec = discriminated[LightningMessage].by(uint16)
.typecase(1, warningCodec)
.typecase(16, initCodec)
.typecase(17, errorCodec)
.typecase(18, pingCodec)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,20 @@ case class Init(features: Features, tlvs: TlvStream[InitTlv] = TlvStream.empty)
val networks = tlvs.get[InitTlv.Networks].map(_.chainHashes).getOrElse(Nil)
}

case class Warning(channelId: ByteVector32, data: ByteVector) extends SetupMessage with HasChannelId {
// @formatter:off
val isGlobal: Boolean = channelId == ByteVector32.Zeroes
def toAscii: String = if (fr.acinq.eclair.isAsciiPrintable(data)) new String(data.toArray, StandardCharsets.US_ASCII) else "n/a"
// @formatter:on
}

object Warning {
// @formatter:off
def apply(channelId: ByteVector32, msg: String): Warning = Warning(channelId, ByteVector.view(msg.getBytes(Charsets.US_ASCII)))
def apply(msg: String): Warning = Warning(ByteVector32.Zeroes, ByteVector.view(msg.getBytes(Charsets.US_ASCII)))
// @formatter:on
}

case class Error(channelId: ByteVector32, data: ByteVector) extends SetupMessage with HasChannelId {
def toAscii: String = if (fr.acinq.eclair.isAsciiPrintable(data)) new String(data.toArray, StandardCharsets.US_ASCII) else "n/a"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,20 @@ class LightningMessageCodecsSpec extends AnyFunSuite {
}
}

test("encode/decode warning") {
val testCases = Seq(
Warning("") -> hex"000100000000000000000000000000000000000000000000000000000000000000000000",
Warning("connection-level issue") -> hex"0x000100000000000000000000000000000000000000000000000000000000000000000016636f6e6e656374696f6e2d6c6576656c206973737565",
Warning(ByteVector32.One, "") -> hex"000101000000000000000000000000000000000000000000000000000000000000000000",
Warning(ByteVector32.One, "channel-specific issue") -> hex"0x0001010000000000000000000000000000000000000000000000000000000000000000166368616e6e656c2d7370656369666963206973737565"
)

for ((warning, expected) <- testCases) {
assert(lightningMessageCodec.encode(warning).require.bytes === expected)
assert(lightningMessageCodec.decode(expected.bits).require.value === warning)
}
}

test("encode/decode live node_announcements") {
val ann = hex"a58338c9660d135fd7d087eb62afd24a33562c54507a9334e79f0dc4f17d407e6d7c61f0e2f3d0d38599502f61704cf1ae93608df027014ade7ff592f27ce2690001025acdf50702d2eabbbacc7c25bbd73b39e65d28237705f7bde76f557e94fb41cb18a9ec00841122116c6e302e646563656e7465722e776f726c64000000000000000000000000000000130200000000000000000000ffffae8a0b082607"
val bin = ann.bits
Expand Down Expand Up @@ -212,7 +226,7 @@ class LightningMessageCodecsSpec extends AnyFunSuite {
}
}

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

0 comments on commit f52c3dd

Please sign in to comment.