Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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]

Comment thread
pm47 marked this conversation as resolved.
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