From a95058f8235a125ef76bb21de793f7ebe9b86c48 Mon Sep 17 00:00:00 2001 From: t-bast Date: Wed, 3 Aug 2022 17:22:00 +0200 Subject: [PATCH 1/2] Make htlc_maximum_msat mandatory in channel updates The specification is removing support for old channel updates that didn't include an `htlc_maximum_msat` (https://github.com/lightning/bolts/pull/996). Every implementation has been generating updates containing this field for years, so we can safely reject updates that don't contain it. --- .../fr/acinq/eclair/db/pg/PgAuditDb.scala | 2 +- .../eclair/db/sqlite/SqliteAuditDb.scala | 2 +- .../acinq/eclair/router/Announcements.scala | 9 ++-- .../scala/fr/acinq/eclair/router/Router.scala | 2 +- .../scala/fr/acinq/eclair/router/Sync.scala | 5 +- .../protocol/LightningMessageCodecs.scala | 49 +++++++------------ .../wire/protocol/LightningMessageTypes.scala | 13 +++-- .../fr/acinq/eclair/db/PaymentsDbSpec.scala | 2 +- .../MultiPartPaymentLifecycleSpec.scala | 12 ++--- .../eclair/payment/PaymentLifecycleSpec.scala | 18 +++---- .../eclair/payment/PaymentPacketSpec.scala | 34 ++++++------- .../payment/relay/ChannelRelayerSpec.scala | 5 +- .../router/ChannelRangeQueriesSpec.scala | 12 ++--- .../eclair/router/RouteCalculationSpec.scala | 35 +++++++------ .../internal/channel/ChannelCodecsSpec.scala | 39 ++------------- .../protocol/ExtendedQueriesCodecsSpec.scala | 12 ++--- .../protocol/FailureMessageCodecsSpec.scala | 18 +++---- .../protocol/LightningMessageCodecsSpec.scala | 18 ++++--- .../src/test/resources/api/findroute-full | 2 +- .../fr/acinq/eclair/api/ApiServiceSpec.scala | 6 +-- 20 files changed, 122 insertions(+), 173 deletions(-) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/db/pg/PgAuditDb.scala b/eclair-core/src/main/scala/fr/acinq/eclair/db/pg/PgAuditDb.scala index 920d484ebc..6ad3e25de7 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/db/pg/PgAuditDb.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/db/pg/PgAuditDb.scala @@ -309,7 +309,7 @@ class PgAuditDb(implicit ds: DataSource) extends AuditDb with Logging { statement.setLong(4, u.channelUpdate.feeProportionalMillionths) statement.setLong(5, u.channelUpdate.cltvExpiryDelta.toInt) statement.setLong(6, u.channelUpdate.htlcMinimumMsat.toLong) - statement.setLong(7, u.channelUpdate.htlcMaximumMsat.map(_.toLong).getOrElse(-1)) + statement.setLong(7, u.channelUpdate.htlcMaximumMsat.toLong) statement.setTimestamp(8, Timestamp.from(Instant.now())) statement.executeUpdate() } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqliteAuditDb.scala b/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqliteAuditDb.scala index 94fc3b1a5d..2b90dd219b 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqliteAuditDb.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqliteAuditDb.scala @@ -290,7 +290,7 @@ class SqliteAuditDb(val sqlite: Connection) extends AuditDb with Logging { statement.setLong(4, u.channelUpdate.feeProportionalMillionths) statement.setLong(5, u.channelUpdate.cltvExpiryDelta.toInt) statement.setLong(6, u.channelUpdate.htlcMinimumMsat.toLong) - statement.setLong(7, u.channelUpdate.htlcMaximumMsat.map(_.toLong).getOrElse(-1)) + statement.setLong(7, u.channelUpdate.htlcMaximumMsat.toLong) statement.setLong(8, TimestampMilli.now().toLong) statement.executeUpdate() } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/router/Announcements.scala b/eclair-core/src/main/scala/fr/acinq/eclair/router/Announcements.scala index 5441a3407f..cb54ef9b76 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/router/Announcements.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/router/Announcements.scala @@ -34,8 +34,8 @@ object Announcements { def nodeAnnouncementWitnessEncode(timestamp: TimestampSecond, nodeId: PublicKey, rgbColor: Color, alias: String, features: Features[Feature], addresses: List[NodeAddress], tlvStream: TlvStream[NodeAnnouncementTlv]): ByteVector = sha256(sha256(serializationResult(LightningMessageCodecs.nodeAnnouncementWitnessCodec.encode(features :: timestamp :: nodeId :: rgbColor :: alias :: addresses :: tlvStream :: HNil)))) - def channelUpdateWitnessEncode(chainHash: ByteVector32, shortChannelId: ShortChannelId, timestamp: TimestampSecond, channelFlags: ChannelUpdate.ChannelFlags, cltvExpiryDelta: CltvExpiryDelta, htlcMinimumMsat: MilliSatoshi, feeBaseMsat: MilliSatoshi, feeProportionalMillionths: Long, htlcMaximumMsat: Option[MilliSatoshi], tlvStream: TlvStream[ChannelUpdateTlv]): ByteVector = - sha256(sha256(serializationResult(LightningMessageCodecs.channelUpdateWitnessCodec.encode(chainHash :: shortChannelId :: timestamp :: channelFlags :: cltvExpiryDelta :: htlcMinimumMsat :: feeBaseMsat :: feeProportionalMillionths :: htlcMaximumMsat :: tlvStream :: HNil)))) + def channelUpdateWitnessEncode(chainHash: ByteVector32, shortChannelId: ShortChannelId, timestamp: TimestampSecond, channelFlags: ChannelUpdate.ChannelFlags, cltvExpiryDelta: CltvExpiryDelta, htlcMinimumMsat: MilliSatoshi, feeBaseMsat: MilliSatoshi, feeProportionalMillionths: Long, htlcMaximumMsat: MilliSatoshi, tlvStream: TlvStream[ChannelUpdateTlv]): ByteVector = + sha256(sha256(serializationResult(LightningMessageCodecs.channelUpdateWitnessCodec.encode(chainHash :: shortChannelId :: timestamp :: () :: channelFlags :: cltvExpiryDelta :: htlcMinimumMsat :: feeBaseMsat :: feeProportionalMillionths :: htlcMaximumMsat :: tlvStream :: HNil)))) def generateChannelAnnouncementWitness(chainHash: ByteVector32, shortChannelId: RealShortChannelId, localNodeId: PublicKey, remoteNodeId: PublicKey, localFundingKey: PublicKey, remoteFundingKey: PublicKey, features: Features[Feature]): ByteVector = if (isNode1(localNodeId, remoteNodeId)) { @@ -116,8 +116,7 @@ object Announcements { def makeChannelUpdate(chainHash: ByteVector32, nodeSecret: PrivateKey, remoteNodeId: PublicKey, shortChannelId: ShortChannelId, cltvExpiryDelta: CltvExpiryDelta, htlcMinimumMsat: MilliSatoshi, feeBaseMsat: MilliSatoshi, feeProportionalMillionths: Long, htlcMaximumMsat: MilliSatoshi, enable: Boolean = true, timestamp: TimestampSecond = TimestampSecond.now()): ChannelUpdate = { val channelFlags = ChannelUpdate.ChannelFlags(isNode1 = isNode1(nodeSecret.publicKey, remoteNodeId), isEnabled = enable) - val htlcMaximumMsatOpt = Some(htlcMaximumMsat) - val witness = channelUpdateWitnessEncode(chainHash, shortChannelId, timestamp, channelFlags, cltvExpiryDelta, htlcMinimumMsat, feeBaseMsat, feeProportionalMillionths, htlcMaximumMsatOpt, TlvStream.empty) + val witness = channelUpdateWitnessEncode(chainHash, shortChannelId, timestamp, channelFlags, cltvExpiryDelta, htlcMinimumMsat, feeBaseMsat, feeProportionalMillionths, htlcMaximumMsat, TlvStream.empty) val sig = Crypto.sign(witness, nodeSecret) ChannelUpdate( signature = sig, @@ -129,7 +128,7 @@ object Announcements { htlcMinimumMsat = htlcMinimumMsat, feeBaseMsat = feeBaseMsat, feeProportionalMillionths = feeProportionalMillionths, - htlcMaximumMsat = htlcMaximumMsatOpt + htlcMaximumMsat = htlcMaximumMsat ) } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala b/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala index 3327c1856f..f516690924 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala @@ -440,7 +440,7 @@ object Router { override def cltvExpiryDelta: CltvExpiryDelta = channelUpdate.cltvExpiryDelta override def relayFees: Relayer.RelayFees = channelUpdate.relayFees override def htlcMinimum: MilliSatoshi = channelUpdate.htlcMinimumMsat - override def htlcMaximum_opt: Option[MilliSatoshi] = channelUpdate.htlcMaximumMsat + override def htlcMaximum_opt: Option[MilliSatoshi] = Some(channelUpdate.htlcMaximumMsat) } /** We learnt about this channel from hints in an invoice */ case class FromHint(extraHop: Invoice.ExtraEdge) extends ChannelRelayParams { diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/router/Sync.scala b/eclair-core/src/main/scala/fr/acinq/eclair/router/Sync.scala index 4eadbb4988..d6f86e5b80 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/router/Sync.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/router/Sync.scala @@ -20,12 +20,11 @@ import akka.actor.{ActorContext, ActorRef} import akka.event.LoggingAdapter import fr.acinq.bitcoin.scalacompat.ByteVector32 import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey -import fr.acinq.eclair.RealShortChannelId import fr.acinq.eclair.crypto.TransportHandler import fr.acinq.eclair.router.Monitoring.{Metrics, Tags} import fr.acinq.eclair.router.Router._ import fr.acinq.eclair.wire.protocol._ -import fr.acinq.eclair.{BlockHeight, ShortChannelId, TimestampSecond, TimestampSecondLong, serializationResult} +import fr.acinq.eclair.{BlockHeight, RealShortChannelId, TimestampSecond, TimestampSecondLong, serializationResult} import scodec.bits.ByteVector import shapeless.HNil @@ -387,7 +386,7 @@ object Sync { def getChecksum(u: ChannelUpdate): Long = { import u._ - val data = serializationResult(LightningMessageCodecs.channelUpdateChecksumCodec.encode(chainHash :: shortChannelId :: channelFlags :: cltvExpiryDelta :: htlcMinimumMsat :: feeBaseMsat :: feeProportionalMillionths :: htlcMaximumMsat :: HNil)) + val data = serializationResult(LightningMessageCodecs.channelUpdateChecksumCodec.encode(chainHash :: shortChannelId :: () :: channelFlags :: cltvExpiryDelta :: htlcMinimumMsat :: feeBaseMsat :: feeProportionalMillionths :: htlcMaximumMsat :: HNil)) crc32c(data) } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecs.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecs.scala index a761e80720..4f4119a70c 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecs.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecs.scala @@ -20,10 +20,9 @@ import fr.acinq.bitcoin.scalacompat.ScriptWitness import fr.acinq.eclair.wire.Monitoring.{Metrics, Tags} import fr.acinq.eclair.wire.protocol.CommonCodecs._ import fr.acinq.eclair.{Feature, Features, InitFeature, KamonExt} -import scodec.bits.{BitVector, ByteVector} +import scodec.bits.{BitVector, ByteVector, HexStringSyntax} import scodec.codecs._ import scodec.{Attempt, Codec} -import shapeless._ /** * Created by PM on 15/11/2016. @@ -321,10 +320,6 @@ object LightningMessageCodecs { ("signature" | bytes64) :: nodeAnnouncementWitnessCodec).as[NodeAnnouncement] - private case class MessageFlags(optionChannelHtlcMax: Boolean) - - private val messageFlagsCodec = ("messageFlags" | (ignore(7) :: bool)).as[MessageFlags] - val reverseBool: Codec[Boolean] = bool.xmap[Boolean](b => !b, b => !b) /** BOLT 7 defines a 'disable' bit and a 'direction' bit, but it's easier to understand if we take the reverse. */ @@ -333,36 +328,26 @@ object LightningMessageCodecs { val channelUpdateChecksumCodec = ("chainHash" | bytes32) :: ("shortChannelId" | shortchannelid) :: - (messageFlagsCodec >>:~ { messageFlags => - channelFlagsCodec :: - ("cltvExpiryDelta" | cltvExpiryDelta) :: - ("htlcMinimumMsat" | millisatoshi) :: - ("feeBaseMsat" | millisatoshi32) :: - ("feeProportionalMillionths" | uint32) :: - ("htlcMaximumMsat" | conditional(messageFlags.optionChannelHtlcMax, millisatoshi)) - }).derive[MessageFlags].from { - // The purpose of this is to tell scodec how to derive the message flags from the data, so we can remove that field - // from the codec definition and the case class, making it purely a serialization detail. - // see: https://github.com/scodec/scodec/blob/series/1.11.x/unitTests/src/test/scala/scodec/examples/ProductsExample.scala#L108-L127 - case _ :: _ :: _ :: _ :: _ :: htlcMaximumMsat_opt :: HNil => MessageFlags(optionChannelHtlcMax = htlcMaximumMsat_opt.isDefined) - } + ("messageFlags" | constant(hex"01")) :: + channelFlagsCodec :: + ("cltvExpiryDelta" | cltvExpiryDelta) :: + ("htlcMinimumMsat" | millisatoshi) :: + ("feeBaseMsat" | millisatoshi32) :: + ("feeProportionalMillionths" | uint32) :: + ("htlcMaximumMsat" | millisatoshi) val channelUpdateWitnessCodec = - (("chainHash" | bytes32) :: + ("chainHash" | bytes32) :: ("shortChannelId" | shortchannelid) :: ("timestamp" | timestampSecond) :: - (messageFlagsCodec >>:~ { messageFlags => - channelFlagsCodec :: - ("cltvExpiryDelta" | cltvExpiryDelta) :: - ("htlcMinimumMsat" | millisatoshi) :: - ("feeBaseMsat" | millisatoshi32) :: - ("feeProportionalMillionths" | uint32) :: - ("htlcMaximumMsat" | conditional(messageFlags.optionChannelHtlcMax, millisatoshi)) :: - ("tlvStream" | ChannelUpdateTlv.channelUpdateTlvCodec) - })).derive[MessageFlags].from { - // same comment above - case _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: htlcMaximumMsat_opt :: _ :: HNil => MessageFlags(optionChannelHtlcMax = htlcMaximumMsat_opt.isDefined) - } + ("messageFlags" | constant(hex"01")) :: + channelFlagsCodec :: + ("cltvExpiryDelta" | cltvExpiryDelta) :: + ("htlcMinimumMsat" | millisatoshi) :: + ("feeBaseMsat" | millisatoshi32) :: + ("feeProportionalMillionths" | uint32) :: + ("htlcMaximumMsat" | millisatoshi) :: + ("tlvStream" | ChannelUpdateTlv.channelUpdateTlvCodec) val channelUpdateCodec: Codec[ChannelUpdate] = ( ("signature" | bytes64) :: diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageTypes.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageTypes.scala index e9db43b42f..d4a737f4fa 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageTypes.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageTypes.scala @@ -20,11 +20,11 @@ import com.google.common.base.Charsets import com.google.common.net.InetAddresses import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey} import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, Satoshi, ScriptWitness, Transaction} -import fr.acinq.eclair.{Alias, BlockHeight, CltvExpiry, CltvExpiryDelta, Feature, Features, InitFeature, MilliSatoshi, RealShortChannelId, ShortChannelId, TimestampSecond, UInt64} import fr.acinq.eclair.blockchain.fee.FeeratePerKw import fr.acinq.eclair.channel.{ChannelFlags, ChannelType} import fr.acinq.eclair.payment.relay.Relayer import fr.acinq.eclair.wire.protocol.ChannelReadyTlv.ShortChannelIdTlv +import fr.acinq.eclair.{Alias, BlockHeight, CltvExpiry, CltvExpiryDelta, Feature, Features, InitFeature, MilliSatoshi, RealShortChannelId, ShortChannelId, TimestampSecond, UInt64} import scodec.bits.ByteVector import java.net.{Inet4Address, Inet6Address, InetAddress} @@ -227,8 +227,8 @@ case class FundingSigned(channelId: ByteVector32, tlvStream: TlvStream[FundingSignedTlv] = TlvStream.empty) extends ChannelMessage with HasChannelId case class ChannelReady(channelId: ByteVector32, - nextPerCommitmentPoint: PublicKey, - tlvStream: TlvStream[ChannelReadyTlv] = TlvStream.empty) extends ChannelMessage with HasChannelId { + nextPerCommitmentPoint: PublicKey, + tlvStream: TlvStream[ChannelReadyTlv] = TlvStream.empty) extends ChannelMessage with HasChannelId { val alias_opt: Option[Alias] = tlvStream.get[ShortChannelIdTlv].map(_.alias) } @@ -260,7 +260,7 @@ object UpdateAddHtlc { paymentHash: ByteVector32, cltvExpiry: CltvExpiry, onionRoutingPacket: OnionRoutingPacket, - blinding_opt: Option[PublicKey]):UpdateAddHtlc = { + blinding_opt: Option[PublicKey]): UpdateAddHtlc = { val tlvs = Seq(blinding_opt.map(UpdateAddHtlcTlv.BlindingPoint)).flatten UpdateAddHtlc(channelId, id, amountMsat, paymentHash, cltvExpiry, onionRoutingPacket, TlvStream[UpdateAddHtlcTlv](tlvs)) } @@ -385,10 +385,9 @@ case class ChannelUpdate(signature: ByteVector64, htlcMinimumMsat: MilliSatoshi, feeBaseMsat: MilliSatoshi, feeProportionalMillionths: Long, - htlcMaximumMsat: Option[MilliSatoshi], + htlcMaximumMsat: MilliSatoshi, tlvStream: TlvStream[ChannelUpdateTlv] = TlvStream.empty) extends RoutingMessage with AnnouncementMessage with HasTimestamp with HasChainHash { - - def messageFlags: Byte = if (htlcMaximumMsat.isDefined) 1 else 0 + def messageFlags: Byte = 1 def toStringShort: String = s"cltvExpiryDelta=$cltvExpiryDelta,feeBase=$feeBaseMsat,feeProportionalMillionths=$feeProportionalMillionths" diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/db/PaymentsDbSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/db/PaymentsDbSpec.scala index 67fa493765..93f69ea1a2 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/db/PaymentsDbSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/db/PaymentsDbSpec.scala @@ -518,7 +518,7 @@ class PaymentsDbSpec extends AnyFunSuite { object PaymentsDbSpec { val (alicePriv, bobPriv, carolPriv, davePriv) = (randomKey(), randomKey(), randomKey(), randomKey()) val (alice, bob, carol, dave) = (alicePriv.publicKey, bobPriv.publicKey, carolPriv.publicKey, davePriv.publicKey) - val hop_ab = ChannelHop(ShortChannelId(42), alice, bob, ChannelRelayParams.FromAnnouncement(ChannelUpdate(randomBytes64(), randomBytes32(), ShortChannelId(42), 1 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(12), 1 msat, 1 msat, 1, None))) + val hop_ab = ChannelHop(ShortChannelId(42), alice, bob, ChannelRelayParams.FromAnnouncement(ChannelUpdate(randomBytes64(), randomBytes32(), ShortChannelId(42), 1 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(12), 1 msat, 1 msat, 1, 500_000_000 msat))) val hop_bc = NodeHop(bob, carol, CltvExpiryDelta(14), 1 msat) val (preimage1, preimage2, preimage3, preimage4) = (randomBytes32(), randomBytes32(), randomBytes32(), randomBytes32()) val (paymentHash1, paymentHash2, paymentHash3, paymentHash4) = (Crypto.sha256(preimage1), Crypto.sha256(preimage2), Crypto.sha256(preimage3), Crypto.sha256(preimage4)) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/MultiPartPaymentLifecycleSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/MultiPartPaymentLifecycleSpec.scala index d3f8bb191f..36602dbf4d 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/MultiPartPaymentLifecycleSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/MultiPartPaymentLifecycleSpec.scala @@ -156,8 +156,8 @@ class MultiPartPaymentLifecycleSpec extends TestKitBaseClass with FixtureAnyFunS router.expectMsgType[RouteRequest] router.send(payFsm, RouteResponse(Seq(Route(500000 msat, hop_ab_1 :: hop_be :: Nil), Route(501000 msat, hop_ac_1 :: hop_ce :: Nil)))) val childPayments = childPayFsm.expectMsgType[SendPaymentToRoute] :: childPayFsm.expectMsgType[SendPaymentToRoute] :: Nil - childPayments.map(_.finalPayload.asInstanceOf[PaymentOnion.FinalTlvPayload]).foreach(p => { - assert(p.records.get[OnionPaymentPayloadTlv.TrampolineOnion] == Some(trampolineTlv)) + childPayments.map(_.finalPayload).foreach(p => { + assert(p.records.get[OnionPaymentPayloadTlv.TrampolineOnion].contains(trampolineTlv)) assert(p.records.unknown.toSeq == Seq(userCustomTlv)) }) @@ -352,7 +352,7 @@ class MultiPartPaymentLifecycleSpec extends TestKitBaseClass with FixtureAnyFunS // B doesn't have enough liquidity on this channel. // NB: we need a channel update with a valid signature, otherwise we'll ignore the node instead of this specific channel. val channelUpdateBE = hop_be.params.asInstanceOf[ChannelRelayParams.FromAnnouncement].channelUpdate - val channelUpdateBE1 = Announcements.makeChannelUpdate(channelUpdateBE.chainHash, priv_b, e, channelUpdateBE.shortChannelId, channelUpdateBE.cltvExpiryDelta, channelUpdateBE.htlcMinimumMsat, channelUpdateBE.feeBaseMsat, channelUpdateBE.feeProportionalMillionths, channelUpdateBE.htlcMaximumMsat.get) + val channelUpdateBE1 = Announcements.makeChannelUpdate(channelUpdateBE.chainHash, priv_b, e, channelUpdateBE.shortChannelId, channelUpdateBE.cltvExpiryDelta, channelUpdateBE.htlcMinimumMsat, channelUpdateBE.feeBaseMsat, channelUpdateBE.feeProportionalMillionths, channelUpdateBE.htlcMaximumMsat) val childId = payFsm.stateData.asInstanceOf[PaymentProgress].pending.keys.head childPayFsm.send(payFsm, PaymentFailed(childId, paymentHash, Seq(RemoteFailure(route.amount, route.hops, Sphinx.DecryptedFailurePacket(b, TemporaryChannelFailure(channelUpdateBE1)))))) // We update the routing hints accordingly before requesting a new route and ignore the channel. @@ -381,7 +381,7 @@ class MultiPartPaymentLifecycleSpec extends TestKitBaseClass with FixtureAnyFunS BasicEdge(a, b, ShortChannelId(1), 10 msat, 0, CltvExpiryDelta(12)), BasicEdge(b, c, ShortChannelId(2), 15 msat, 150, CltvExpiryDelta(48)), BasicEdge(a, c, ShortChannelId(3), 1 msat, 10, CltvExpiryDelta(144)) ) - assert(extraEdges1.zip(PaymentFailure.updateExtraEdges(failures, extraEdges)).forall{case (e1, e2) => e1 == e2}) + assert(extraEdges1.zip(PaymentFailure.updateExtraEdges(failures, extraEdges)).forall { case (e1, e2) => e1 == e2 }) } { val failures = Seq( @@ -394,7 +394,7 @@ class MultiPartPaymentLifecycleSpec extends TestKitBaseClass with FixtureAnyFunS BasicEdge(a, b, ShortChannelId(1), 23 msat, 23, CltvExpiryDelta(23)), BasicEdge(b, c, ShortChannelId(2), 21 msat, 21, CltvExpiryDelta(21)), BasicEdge(a, c, ShortChannelId(3), 22 msat, 22, CltvExpiryDelta(22)) ) - assert(extraEdges1.zip(PaymentFailure.updateExtraEdges(failures, extraEdges)).forall{case (e1, e2) => e1 == e2}) + assert(extraEdges1.zip(PaymentFailure.updateExtraEdges(failures, extraEdges)).forall { case (e1, e2) => e1 == e2 }) } } @@ -691,7 +691,7 @@ object MultiPartPaymentLifecycleSpec { val channelId_ce = ShortChannelId(13) val channelId_ad = ShortChannelId(21) val channelId_de = ShortChannelId(22) - val defaultChannelUpdate = ChannelUpdate(randomBytes64(), Block.RegtestGenesisBlock.hash, ShortChannelId(0), 0 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(12), 1 msat, 100 msat, 0, Some(2000000 msat)) + val defaultChannelUpdate = ChannelUpdate(randomBytes64(), Block.RegtestGenesisBlock.hash, ShortChannelId(0), 0 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(12), 1 msat, 100 msat, 0, 2_000_000 msat) val channelUpdate_ab_1 = defaultChannelUpdate.copy(shortChannelId = channelId_ab_1) val channelUpdate_ab_2 = defaultChannelUpdate.copy(shortChannelId = channelId_ab_2) val channelUpdate_be = defaultChannelUpdate.copy(shortChannelId = channelId_be) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala index 600f422c1f..c78a488996 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala @@ -118,7 +118,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec { val ps = sender.expectMsgType[PaymentSent] assert(ps.id == parentId) - assert(ps.parts.head.route == Some(route.hops)) + assert(ps.parts.head.route.contains(route.hops)) awaitCond(nodeParams.db.payments.getOutgoingPayment(id).exists(_.status.isInstanceOf[OutgoingPaymentStatus.Succeeded])) metricsListener.expectNoMessage() @@ -498,7 +498,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec { register.expectMsg(ForwardShortId(paymentFSM.toTyped, scid_ab, cmd1)) // we change the cltv expiry - val channelUpdate_bc_modified = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, c, scid_bc, CltvExpiryDelta(42), htlcMinimumMsat = update_bc.htlcMinimumMsat, feeBaseMsat = update_bc.feeBaseMsat, feeProportionalMillionths = update_bc.feeProportionalMillionths, htlcMaximumMsat = update_bc.htlcMaximumMsat.get) + val channelUpdate_bc_modified = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, c, scid_bc, CltvExpiryDelta(42), htlcMinimumMsat = update_bc.htlcMinimumMsat, feeBaseMsat = update_bc.feeBaseMsat, feeProportionalMillionths = update_bc.feeProportionalMillionths, htlcMaximumMsat = update_bc.htlcMaximumMsat) val failure = IncorrectCltvExpiry(CltvExpiry(5), channelUpdate_bc_modified) // and node replies with a failure containing a new channel update sender.send(paymentFSM, addCompleted(HtlcResult.RemoteFail(UpdateFailHtlc(ByteVector32.Zeroes, 0, Sphinx.FailurePacket.create(sharedSecrets1.head._1, failure))))) @@ -515,7 +515,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec { register.expectMsg(ForwardShortId(paymentFSM.toTyped, scid_ab, cmd2)) // we change the cltv expiry one more time - val channelUpdate_bc_modified_2 = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, c, scid_bc, CltvExpiryDelta(43), htlcMinimumMsat = update_bc.htlcMinimumMsat, feeBaseMsat = update_bc.feeBaseMsat, feeProportionalMillionths = update_bc.feeProportionalMillionths, htlcMaximumMsat = update_bc.htlcMaximumMsat.get) + val channelUpdate_bc_modified_2 = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, c, scid_bc, CltvExpiryDelta(43), htlcMinimumMsat = update_bc.htlcMinimumMsat, feeBaseMsat = update_bc.feeBaseMsat, feeProportionalMillionths = update_bc.feeProportionalMillionths, htlcMaximumMsat = update_bc.htlcMaximumMsat) val failure2 = IncorrectCltvExpiry(CltvExpiry(5), channelUpdate_bc_modified_2) // and node replies with a failure containing a new channel update sender.send(paymentFSM, addCompleted(HtlcResult.RemoteFail(UpdateFailHtlc(ByteVector32.Zeroes, 0, Sphinx.FailurePacket.create(sharedSecrets2.head._1, failure2))))) @@ -583,7 +583,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec { register.expectMsg(ForwardShortId(paymentFSM.toTyped, scid_ab, cmd1)) // we change the cltv expiry - val channelUpdate_bc_modified = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, c, scid_bc, CltvExpiryDelta(42), htlcMinimumMsat = update_bc.htlcMinimumMsat, feeBaseMsat = update_bc.feeBaseMsat, feeProportionalMillionths = update_bc.feeProportionalMillionths, htlcMaximumMsat = update_bc.htlcMaximumMsat.get) + val channelUpdate_bc_modified = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, c, scid_bc, CltvExpiryDelta(42), htlcMinimumMsat = update_bc.htlcMinimumMsat, feeBaseMsat = update_bc.feeBaseMsat, feeProportionalMillionths = update_bc.feeProportionalMillionths, htlcMaximumMsat = update_bc.htlcMaximumMsat) val failure = IncorrectCltvExpiry(CltvExpiry(5), channelUpdate_bc_modified) // and node replies with a failure containing a new channel update sender.send(paymentFSM, addCompleted(HtlcResult.RemoteFail(UpdateFailHtlc(ByteVector32.Zeroes, 0, Sphinx.FailurePacket.create(sharedSecrets1.head._1, failure))))) @@ -592,7 +592,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec { routerForwarder.expectMsg(channelUpdate_bc_modified) awaitCond(paymentFSM.stateName == WAITING_FOR_ROUTE && nodeParams.db.payments.getOutgoingPayment(id).exists(_.status == OutgoingPaymentStatus.Pending)) // 1 failure but not final, the payment is still PENDING val extraEdges1 = Seq( - extraEdges(0).asInstanceOf[BasicEdge].update(channelUpdate_bc_modified), + extraEdges(0).update(channelUpdate_bc_modified), extraEdges(1) ) routerForwarder.expectMsg(defaultRouteRequest(nodeParams.nodeId, d, cfg).copy(extraEdges = extraEdges1)) @@ -623,7 +623,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec { register.expectMsg(ForwardShortId(paymentFSM.toTyped, scid_ab, cmd1)) // we disable the channel - val channelUpdate_cd_disabled = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_c, d, scid_cd, CltvExpiryDelta(42), update_cd.htlcMinimumMsat, update_cd.feeBaseMsat, update_cd.feeProportionalMillionths, update_cd.htlcMaximumMsat.get, enable = false) + val channelUpdate_cd_disabled = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_c, d, scid_cd, CltvExpiryDelta(42), update_cd.htlcMinimumMsat, update_cd.feeBaseMsat, update_cd.feeProportionalMillionths, update_cd.htlcMaximumMsat, enable = false) val failure = ChannelDisabled(channelUpdate_cd_disabled.messageFlags, channelUpdate_cd_disabled.channelFlags, channelUpdate_cd_disabled) val failureOnion = Sphinx.FailurePacket.wrap(Sphinx.FailurePacket.create(sharedSecrets1(1)._1, failure), sharedSecrets1.head._1) sender.send(paymentFSM, addCompleted(HtlcResult.RemoteFail(UpdateFailHtlc(ByteVector32.Zeroes, 0, failureOnion)))) @@ -824,11 +824,11 @@ class PaymentLifecycleSpec extends BaseRouterSpec { val Transition(_, WAITING_FOR_REQUEST, WAITING_FOR_ROUTE) = monitor.expectMsgClass(classOf[Transition[_]]) routerForwarder.forward(routerFixture.router) val Transition(_, WAITING_FOR_ROUTE, WAITING_FOR_PAYMENT_COMPLETE) = monitor.expectMsgClass(classOf[Transition[_]]) - assert(nodeParams.db.payments.getOutgoingPayment(id) == None) + assert(nodeParams.db.payments.getOutgoingPayment(id).isEmpty) sender.send(paymentFSM, addCompleted(HtlcResult.RemoteFulfill(UpdateFulfillHtlc(ByteVector32.Zeroes, 0, defaultPaymentPreimage)))) sender.expectMsgType[PaymentSent] - assert(nodeParams.db.payments.getOutgoingPayment(id) == None) + assert(nodeParams.db.payments.getOutgoingPayment(id).isEmpty) eventListener.expectNoMessage(100 millis) assert(routerForwarder.expectMsgType[RouteDidRelay].route.hops.map(_.nextNodeId) == Seq(b, c, d)) @@ -853,7 +853,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec { assert(outgoing.copy(createdAt = 0 unixms) == OutgoingPayment(id, parentId, Some(defaultExternalId), defaultPaymentHash, PaymentType.Standard, defaultAmountMsat, defaultAmountMsat, d, 0 unixms, Some(defaultInvoice), OutgoingPaymentStatus.Pending)) // we change the cltv expiry - val channelUpdate_bc_modified = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, c, scid_bc, CltvExpiryDelta(42), htlcMinimumMsat = update_bc.htlcMinimumMsat, feeBaseMsat = update_bc.feeBaseMsat, feeProportionalMillionths = update_bc.feeProportionalMillionths, htlcMaximumMsat = update_bc.htlcMaximumMsat.get) + val channelUpdate_bc_modified = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, c, scid_bc, CltvExpiryDelta(42), htlcMinimumMsat = update_bc.htlcMinimumMsat, feeBaseMsat = update_bc.feeBaseMsat, feeProportionalMillionths = update_bc.feeProportionalMillionths, htlcMaximumMsat = update_bc.htlcMaximumMsat) val failure = IncorrectCltvExpiry(CltvExpiry(5), channelUpdate_bc_modified) // and node replies with a failure containing a new channel update sender.send(paymentFSM, addCompleted(HtlcResult.RemoteFail(UpdateFailHtlc(ByteVector32.Zeroes, 0, Sphinx.FailurePacket.create(sharedSecrets1.head._1, failure))))) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentPacketSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentPacketSpec.scala index a0a58c5e52..e91cc8caa0 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentPacketSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentPacketSpec.scala @@ -28,7 +28,7 @@ import fr.acinq.eclair.crypto.Sphinx import fr.acinq.eclair.payment.IncomingPaymentPacket.{ChannelRelayPacket, FinalPacket, NodeRelayPacket, decrypt} import fr.acinq.eclair.payment.OutgoingPaymentPacket._ import fr.acinq.eclair.router.BaseRouterSpec.channelHopFromUpdate -import fr.acinq.eclair.router.Router.{ChannelHop, NodeHop} +import fr.acinq.eclair.router.Router.NodeHop import fr.acinq.eclair.transactions.Transactions.InputInfo import fr.acinq.eclair.wire.protocol.OnionPaymentPayloadTlv.{AmountToForward, OutgoingCltv, PaymentData} import fr.acinq.eclair.wire.protocol.PaymentOnion.{ChannelRelayTlvPayload, FinalTlvPayload} @@ -143,7 +143,7 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll { assert(payload_b.totalAmount == finalAmount) assert(payload_b.expiry == finalExpiry) assert(payload_b.paymentSecret == paymentSecret) - assert(payload_b.paymentMetadata == Some(paymentMetadata)) + assert(payload_b.paymentMetadata.contains(paymentMetadata)) } test("build a trampoline payment") { @@ -174,10 +174,10 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll { assert(inner_c.amountToForward == amount_cd) assert(inner_c.outgoingCltv == expiry_cd) assert(inner_c.outgoingNodeId == d) - assert(inner_c.invoiceRoutingInfo == None) - assert(inner_c.invoiceFeatures == None) - assert(inner_c.paymentSecret == None) - assert(inner_c.paymentMetadata == None) + assert(inner_c.invoiceRoutingInfo.isEmpty) + assert(inner_c.invoiceFeatures.isEmpty) + assert(inner_c.paymentSecret.isEmpty) + assert(inner_c.paymentMetadata.isEmpty) // c forwards the trampoline payment to d. val Success((amount_d, expiry_d, onion_d)) = buildPaymentPacket(paymentHash, channelHopFromUpdate(c, d, channelUpdate_cd) :: Nil, PaymentOnion.createTrampolinePayload(amount_cd, amount_cd, expiry_cd, randomBytes32(), packet_d)) @@ -192,10 +192,10 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll { assert(inner_d.amountToForward == amount_de) assert(inner_d.outgoingCltv == expiry_de) assert(inner_d.outgoingNodeId == e) - assert(inner_d.invoiceRoutingInfo == None) - assert(inner_d.invoiceFeatures == None) - assert(inner_d.paymentSecret == None) - assert(inner_d.paymentMetadata == None) + assert(inner_d.invoiceRoutingInfo.isEmpty) + assert(inner_d.invoiceFeatures.isEmpty) + assert(inner_d.paymentSecret.isEmpty) + assert(inner_d.paymentMetadata.isEmpty) // d forwards the trampoline payment to e. val Success((amount_e, expiry_e, onion_e)) = buildPaymentPacket(paymentHash, channelHopFromUpdate(d, e, channelUpdate_de) :: Nil, PaymentOnion.createTrampolinePayload(amount_de, amount_de, expiry_de, randomBytes32(), packet_e)) @@ -236,9 +236,9 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll { assert(inner_c.amountToForward == amount_cd) assert(inner_c.outgoingCltv == expiry_cd) assert(inner_c.outgoingNodeId == d) - assert(inner_c.invoiceRoutingInfo == None) - assert(inner_c.invoiceFeatures == None) - assert(inner_c.paymentSecret == None) + assert(inner_c.invoiceRoutingInfo.isEmpty) + assert(inner_c.invoiceFeatures.isEmpty) + assert(inner_c.paymentSecret.isEmpty) // c forwards the trampoline payment to d. val Success((amount_d, expiry_d, onion_d)) = buildPaymentPacket(paymentHash, channelHopFromUpdate(c, d, channelUpdate_cd) :: Nil, PaymentOnion.createTrampolinePayload(amount_cd, amount_cd, expiry_cd, randomBytes32(), packet_d)) @@ -255,9 +255,9 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll { assert(inner_d.outgoingNodeId == e) assert(inner_d.totalAmount == finalAmount) assert(inner_d.paymentSecret == invoice.paymentSecret) - assert(inner_d.paymentMetadata == Some(hex"010203")) - assert(inner_d.invoiceFeatures == Some(hex"024100")) // var_onion_optin, payment_secret, basic_mpp - assert(inner_d.invoiceRoutingInfo == Some(routingHints)) + assert(inner_d.paymentMetadata.contains(hex"010203")) + assert(inner_d.invoiceFeatures.contains(hex"024100")) // var_onion_optin, payment_secret, basic_mpp + assert(inner_d.invoiceRoutingInfo.contains(routingHints)) } test("fail to build a trampoline payment when too much invoice data is provided") { @@ -384,7 +384,7 @@ object PaymentPacketSpec { val (priv_a, priv_b, priv_c, priv_d, priv_e) = (TestConstants.Alice.nodeKeyManager.nodeKey, TestConstants.Bob.nodeKeyManager.nodeKey, randomExtendedPrivateKey, randomExtendedPrivateKey, randomExtendedPrivateKey) val (a, b, c, d, e) = (priv_a.publicKey, priv_b.publicKey, priv_c.publicKey, priv_d.publicKey, priv_e.publicKey) val sig = Crypto.sign(Crypto.sha256(ByteVector.empty), priv_a.privateKey) - val defaultChannelUpdate = ChannelUpdate(sig, Block.RegtestGenesisBlock.hash, ShortChannelId(0), 0 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(0), 42000 msat, 0 msat, 0, Some(500000000 msat)) + val defaultChannelUpdate = ChannelUpdate(sig, Block.RegtestGenesisBlock.hash, ShortChannelId(0), 0 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(0), 42000 msat, 0 msat, 0, 500_000_000 msat) val channelUpdate_ab = defaultChannelUpdate.copy(shortChannelId = ShortChannelId(1), cltvExpiryDelta = CltvExpiryDelta(4), feeBaseMsat = 642000 msat, feeProportionalMillionths = 7) val channelUpdate_bc = defaultChannelUpdate.copy(shortChannelId = ShortChannelId(2), cltvExpiryDelta = CltvExpiryDelta(5), feeBaseMsat = 153000 msat, feeProportionalMillionths = 4) val channelUpdate_cd = defaultChannelUpdate.copy(shortChannelId = ShortChannelId(3), cltvExpiryDelta = CltvExpiryDelta(10), feeBaseMsat = 60000 msat, feeProportionalMillionths = 1) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/relay/ChannelRelayerSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/relay/ChannelRelayerSpec.scala index 26e75eef88..ad5e4be471 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/relay/ChannelRelayerSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/relay/ChannelRelayerSpec.scala @@ -32,7 +32,7 @@ import fr.acinq.eclair.payment.IncomingPaymentPacket.ChannelRelayPacket import fr.acinq.eclair.payment.relay.ChannelRelayer._ import fr.acinq.eclair.payment.{ChannelPaymentRelayed, IncomingPaymentPacket, PaymentPacketSpec} import fr.acinq.eclair.router.Announcements -import fr.acinq.eclair.wire.protocol.PaymentOnion.{ChannelRelayData, ChannelRelayPayload, ChannelRelayTlvPayload, RelayLegacyPayload} +import fr.acinq.eclair.wire.protocol.PaymentOnion.{ChannelRelayData, ChannelRelayTlvPayload, RelayLegacyPayload} import fr.acinq.eclair.wire.protocol._ import fr.acinq.eclair.{CltvExpiry, NodeParams, RealShortChannelId, TestConstants, randomBytes32, _} import org.scalatest.Inside.inside @@ -154,7 +154,6 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a test("relay with onion tlv payload") { f => import f._ - import fr.acinq.eclair.wire.protocol.OnionPaymentPayloadTlv._ val payload = ChannelRelayTlvPayload(realScid1, outgoingAmount, outgoingExpiry) val r = createValidIncomingPacket(payload) @@ -596,7 +595,7 @@ object ChannelRelayerSpec { def createLocalUpdate(channelId: ByteVector32, channelUpdateScid_opt: Option[ShortChannelId] = None, balance: MilliSatoshi = 100_000_000 msat, capacity: Satoshi = 5_000_000 sat, enabled: Boolean = true, htlcMinimum: MilliSatoshi = 0 msat, timestamp: TimestampSecond = 0 unixsec, feeBaseMsat: MilliSatoshi = 1000 msat, feeProportionalMillionths: Long = 100, optionScidAlias: Boolean = false): LocalChannelUpdate = { val shortIds = createShortIds(channelId) val channelUpdateScid = channelUpdateScid_opt.getOrElse(shortIds.real.toOption.get) - val update = ChannelUpdate(ByteVector64(randomBytes(64)), Block.RegtestGenesisBlock.hash, channelUpdateScid, timestamp, ChannelUpdate.ChannelFlags(isNode1 = true, isEnabled = enabled), CltvExpiryDelta(100), htlcMinimum, feeBaseMsat, feeProportionalMillionths, Some(capacity.toMilliSatoshi)) + val update = ChannelUpdate(ByteVector64(randomBytes(64)), Block.RegtestGenesisBlock.hash, channelUpdateScid, timestamp, ChannelUpdate.ChannelFlags(isNode1 = true, isEnabled = enabled), CltvExpiryDelta(100), htlcMinimum, feeBaseMsat, feeProportionalMillionths, capacity.toMilliSatoshi) val features: Set[PermanentChannelFeature] = Set( if (optionScidAlias) Some(ScidAlias) else None, ).flatten diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/ChannelRangeQueriesSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/ChannelRangeQueriesSpec.scala index 2baf31a3a8..5e3d622ddb 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/ChannelRangeQueriesSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/ChannelRangeQueriesSpec.scala @@ -107,20 +107,20 @@ class ChannelRangeQueriesSpec extends AnyFunSuite { cd.shortChannelId -> PublicChannel(cd, ByteVector32.Zeroes, 0 sat, Some(ucd1), None, None) ) - assert(getChannelDigestInfo(channels)(ab.shortChannelId) == (Timestamps(now, now), Checksums(1697591108L, 3692323747L))) + assert(getChannelDigestInfo(channels)(ab.shortChannelId) == (Timestamps(now, now), Checksums(3352963162L, 2581904122L))) // no extended info but we know the channel: we ask for the updates assert(computeFlag(channels)(ab.shortChannelId, None, None, includeNodeAnnouncements = false) == (INCLUDE_CHANNEL_UPDATE_1 | INCLUDE_CHANNEL_UPDATE_2)) assert(computeFlag(channels)(ab.shortChannelId, None, None, includeNodeAnnouncements = true) == (INCLUDE_CHANNEL_UPDATE_1 | INCLUDE_CHANNEL_UPDATE_2 | INCLUDE_NODE_ANNOUNCEMENT_1 | INCLUDE_NODE_ANNOUNCEMENT_2)) // same checksums, newer timestamps: we don't ask anything - assert(computeFlag(channels)(ab.shortChannelId, Some(Timestamps(now + 1, now + 1)), Some(Checksums(1697591108L, 3692323747L)), includeNodeAnnouncements = true) == 0) + assert(computeFlag(channels)(ab.shortChannelId, Some(Timestamps(now + 1, now + 1)), Some(Checksums(3352963162L, 2581904122L)), includeNodeAnnouncements = true) == 0) // different checksums, newer timestamps: we ask for the updates - assert(computeFlag(channels)(ab.shortChannelId, Some(Timestamps(now + 1, now)), Some(Checksums(154654604, 3692323747L)), includeNodeAnnouncements = true) == (INCLUDE_CHANNEL_UPDATE_1 | INCLUDE_NODE_ANNOUNCEMENT_1 | INCLUDE_NODE_ANNOUNCEMENT_2)) - assert(computeFlag(channels)(ab.shortChannelId, Some(Timestamps(now, now + 1)), Some(Checksums(1697591108L, 45664546)), includeNodeAnnouncements = true) == (INCLUDE_CHANNEL_UPDATE_2 | INCLUDE_NODE_ANNOUNCEMENT_1 | INCLUDE_NODE_ANNOUNCEMENT_2)) + assert(computeFlag(channels)(ab.shortChannelId, Some(Timestamps(now + 1, now)), Some(Checksums(154654604, 2581904122L)), includeNodeAnnouncements = true) == (INCLUDE_CHANNEL_UPDATE_1 | INCLUDE_NODE_ANNOUNCEMENT_1 | INCLUDE_NODE_ANNOUNCEMENT_2)) + assert(computeFlag(channels)(ab.shortChannelId, Some(Timestamps(now, now + 1)), Some(Checksums(3352963162L, 45664546)), includeNodeAnnouncements = true) == (INCLUDE_CHANNEL_UPDATE_2 | INCLUDE_NODE_ANNOUNCEMENT_1 | INCLUDE_NODE_ANNOUNCEMENT_2)) assert(computeFlag(channels)(ab.shortChannelId, Some(Timestamps(now + 1, now + 1)), Some(Checksums(154654604, 45664546 + 6)), includeNodeAnnouncements = true) == (INCLUDE_CHANNEL_UPDATE_1 | INCLUDE_CHANNEL_UPDATE_2 | INCLUDE_NODE_ANNOUNCEMENT_1 | INCLUDE_NODE_ANNOUNCEMENT_2)) // different checksums, older timestamps: we don't ask anything - assert(computeFlag(channels)(ab.shortChannelId, Some(Timestamps(now - 1, now)), Some(Checksums(154654604, 3692323747L)), includeNodeAnnouncements = true) == 0) - assert(computeFlag(channels)(ab.shortChannelId, Some(Timestamps(now, now - 1)), Some(Checksums(1697591108L, 45664546)), includeNodeAnnouncements = true) == 0) + assert(computeFlag(channels)(ab.shortChannelId, Some(Timestamps(now - 1, now)), Some(Checksums(154654604, 2581904122L)), includeNodeAnnouncements = true) == 0) + assert(computeFlag(channels)(ab.shortChannelId, Some(Timestamps(now, now - 1)), Some(Checksums(3352963162L, 45664546)), includeNodeAnnouncements = true) == 0) assert(computeFlag(channels)(ab.shortChannelId, Some(Timestamps(now - 1, now - 1)), Some(Checksums(154654604, 45664546)), includeNodeAnnouncements = true) == 0) // missing channel update: we ask for it diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/RouteCalculationSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/RouteCalculationSpec.scala index 338b3f3ea6..3783965545 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/RouteCalculationSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/RouteCalculationSpec.scala @@ -19,7 +19,6 @@ package fr.acinq.eclair.router import com.softwaremill.quicklens.ModifyPimp import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, ByteVector64, Satoshi, SatoshiLong} -import fr.acinq.eclair.payment.Bolt11Invoice.ExtraHop import fr.acinq.eclair.payment.relay.Relayer.RelayFees import fr.acinq.eclair.router.BaseRouterSpec.channelHopFromUpdate import fr.acinq.eclair.router.Graph.GraphStructure.DirectedGraph.graphEdgeToHop @@ -29,7 +28,7 @@ import fr.acinq.eclair.router.RouteCalculation._ import fr.acinq.eclair.router.Router._ import fr.acinq.eclair.transactions.Transactions import fr.acinq.eclair.wire.protocol._ -import fr.acinq.eclair.{BlockHeight, CltvExpiryDelta, Features, MilliSatoshi, MilliSatoshiLong, RealShortChannelId, ShortChannelId, ShortChannelIdSpec, TimestampSecond, TimestampSecondLong, ToMilliSatoshiConversion, randomKey} +import fr.acinq.eclair.{BlockHeight, CltvExpiryDelta, Features, MilliSatoshi, MilliSatoshiLong, RealShortChannelId, ShortChannelId, TimestampSecond, TimestampSecondLong, ToMilliSatoshiConversion, randomKey} import org.scalatest.TryValues.convertTryToSuccessOrFailure import org.scalatest.funsuite.AnyFunSuite import org.scalatest.{ParallelTestExecution, Tag} @@ -419,14 +418,14 @@ class RouteCalculationSpec extends AnyFunSuite with ParallelTestExecution { test("calculate route and return metadata") { val DUMMY_SIG = Transactions.PlaceHolderSig - val uab = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(1L), 0 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(1), 42 msat, 2500 msat, 140, None) - val uba = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(1L), 1 unixsec, ChannelUpdate.ChannelFlags(isNode1 = false, isEnabled = false), CltvExpiryDelta(1), 43 msat, 2501 msat, 141, None) - val ubc = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(2L), 1 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(1), 44 msat, 2502 msat, 142, None) - val ucb = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(2L), 1 unixsec, ChannelUpdate.ChannelFlags(isNode1 = false, isEnabled = false), CltvExpiryDelta(1), 45 msat, 2503 msat, 143, None) - val ucd = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(3L), 1 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(1), 46 msat, 2504 msat, 144, Some(500000000 msat)) - val udc = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(3L), 1 unixsec, ChannelUpdate.ChannelFlags(isNode1 = false, isEnabled = false), CltvExpiryDelta(1), 47 msat, 2505 msat, 145, None) - val ude = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(4L), 1 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(1), 48 msat, 2506 msat, 146, None) - val ued = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(4L), 1 unixsec, ChannelUpdate.ChannelFlags(isNode1 = false, isEnabled = false), CltvExpiryDelta(1), 49 msat, 2507 msat, 147, None) + val uab = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(1L), 0 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(1), 42 msat, 2500 msat, 140, DEFAULT_CAPACITY.toMilliSatoshi) + val uba = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(1L), 1 unixsec, ChannelUpdate.ChannelFlags(isNode1 = false, isEnabled = false), CltvExpiryDelta(1), 43 msat, 2501 msat, 141, DEFAULT_CAPACITY.toMilliSatoshi) + val ubc = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(2L), 1 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(1), 44 msat, 2502 msat, 142, DEFAULT_CAPACITY.toMilliSatoshi) + val ucb = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(2L), 1 unixsec, ChannelUpdate.ChannelFlags(isNode1 = false, isEnabled = false), CltvExpiryDelta(1), 45 msat, 2503 msat, 143, DEFAULT_CAPACITY.toMilliSatoshi) + val ucd = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(3L), 1 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(1), 46 msat, 2504 msat, 144, 500_000_000 msat) + val udc = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(3L), 1 unixsec, ChannelUpdate.ChannelFlags(isNode1 = false, isEnabled = false), CltvExpiryDelta(1), 47 msat, 2505 msat, 145, DEFAULT_CAPACITY.toMilliSatoshi) + val ude = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(4L), 1 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(1), 48 msat, 2506 msat, 146, DEFAULT_CAPACITY.toMilliSatoshi) + val ued = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(4L), 1 unixsec, ChannelUpdate.ChannelFlags(isNode1 = false, isEnabled = false), CltvExpiryDelta(1), 49 msat, 2507 msat, 147, DEFAULT_CAPACITY.toMilliSatoshi) val edges = Seq( GraphEdge(ChannelDesc(ShortChannelId(1L), a, b), ChannelRelayParams.FromAnnouncement(uab), DEFAULT_CAPACITY, None), @@ -898,24 +897,24 @@ class RouteCalculationSpec extends AnyFunSuite with ParallelTestExecution { ann = makeChannel(ShortChannelId.fromCoordinates("565643x1216x0").success.value.toLong, PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f"), PublicKey(hex"024655b768ef40951b20053a5c4b951606d4d86085d51238f2c67c7dec29c792ca")), fundingTxid = ByteVector32.Zeroes, capacity = DEFAULT_CAPACITY, - update_1_opt = Some(ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId.fromCoordinates("565643x1216x0").success.value, 0 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(14), htlcMinimumMsat = 1 msat, feeBaseMsat = 1000 msat, 10, Some(4294967295L msat))), - update_2_opt = Some(ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId.fromCoordinates("565643x1216x0").success.value, 0 unixsec, ChannelUpdate.ChannelFlags(isEnabled = true, isNode1 = false), CltvExpiryDelta(144), htlcMinimumMsat = 0 msat, feeBaseMsat = 1000 msat, 100, Some(15000000000L msat))), + update_1_opt = Some(ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId.fromCoordinates("565643x1216x0").success.value, 0 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(14), htlcMinimumMsat = 1 msat, feeBaseMsat = 1000 msat, 10, 4_294_967_295L msat)), + update_2_opt = Some(ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId.fromCoordinates("565643x1216x0").success.value, 0 unixsec, ChannelUpdate.ChannelFlags(isEnabled = true, isNode1 = false), CltvExpiryDelta(144), htlcMinimumMsat = 0 msat, feeBaseMsat = 1000 msat, 100, 15_000_000_000L msat)), meta_opt = None ), RealShortChannelId(BlockHeight(542280), 2156, 0) -> PublicChannel( ann = makeChannel(ShortChannelId.fromCoordinates("542280x2156x0").success.value.toLong, PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f"), PublicKey(hex"03cb7983dc247f9f81a0fa2dfa3ce1c255365f7279c8dd143e086ca333df10e278")), fundingTxid = ByteVector32.Zeroes, capacity = DEFAULT_CAPACITY, - update_1_opt = Some(ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId.fromCoordinates("542280x2156x0").success.value, 0 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(144), htlcMinimumMsat = 1000 msat, feeBaseMsat = 1000 msat, 100, Some(16777000000L msat))), - update_2_opt = Some(ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId.fromCoordinates("542280x2156x0").success.value, 0 unixsec, ChannelUpdate.ChannelFlags(isEnabled = true, isNode1 = false), CltvExpiryDelta(144), htlcMinimumMsat = 1 msat, feeBaseMsat = 667 msat, 1, Some(16777000000L msat))), + update_1_opt = Some(ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId.fromCoordinates("542280x2156x0").success.value, 0 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(144), htlcMinimumMsat = 1000 msat, feeBaseMsat = 1000 msat, 100, 16_777_000_000L msat)), + update_2_opt = Some(ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId.fromCoordinates("542280x2156x0").success.value, 0 unixsec, ChannelUpdate.ChannelFlags(isEnabled = true, isNode1 = false), CltvExpiryDelta(144), htlcMinimumMsat = 1 msat, feeBaseMsat = 667 msat, 1, 16_777_000_000L msat)), meta_opt = None ), RealShortChannelId(BlockHeight(565779), 2711, 0) -> PublicChannel( ann = makeChannel(ShortChannelId.fromCoordinates("565779x2711x0").success.value.toLong, PublicKey(hex"036d65409c41ab7380a43448f257809e7496b52bf92057c09c4f300cbd61c50d96"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")), fundingTxid = ByteVector32.Zeroes, capacity = DEFAULT_CAPACITY, - update_1_opt = Some(ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId.fromCoordinates("565779x2711x0").success.value, 0 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(144), htlcMinimumMsat = 1 msat, feeBaseMsat = 1000 msat, 100, Some(230000000L msat))), - update_2_opt = Some(ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId.fromCoordinates("565779x2711x0").success.value, 0 unixsec, ChannelUpdate.ChannelFlags(isEnabled = false, isNode1 = false), CltvExpiryDelta(144), htlcMinimumMsat = 1 msat, feeBaseMsat = 1000 msat, 100, Some(230000000L msat))), + update_1_opt = Some(ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId.fromCoordinates("565779x2711x0").success.value, 0 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(144), htlcMinimumMsat = 1 msat, feeBaseMsat = 1000 msat, 100, 230_000_000L msat)), + update_2_opt = Some(ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId.fromCoordinates("565779x2711x0").success.value, 0 unixsec, ChannelUpdate.ChannelFlags(isEnabled = false, isNode1 = false), CltvExpiryDelta(144), htlcMinimumMsat = 1 msat, feeBaseMsat = 1000 msat, 100, 230_000_000L msat)), meta_opt = None ) ) @@ -1885,7 +1884,7 @@ class RouteCalculationSpec extends AnyFunSuite with ParallelTestExecution { test("small local edge with liquidity is better than big remote edge") { // A == B == C -- D - // \_________/ + // \_______/ val g = DirectedGraph(List( makeEdge(1L, a, b, 100 msat, 100, minHtlc = 1000 msat, capacity = 100000000 sat, balance_opt = Some(10000000 msat)), makeEdge(2L, b, c, 100 msat, 100, minHtlc = 1000 msat, capacity = 100000000 sat), @@ -1955,7 +1954,7 @@ object RouteCalculationSpec { htlcMinimumMsat = minHtlc, feeBaseMsat = feeBase, feeProportionalMillionths = feeProportionalMillionth, - htlcMaximumMsat = maxHtlc + htlcMaximumMsat = maxHtlc.getOrElse(500_000_000 msat) ) def hops2Ids(hops: Seq[ChannelHop]): Seq[Long] = hops.map(hop => hop.shortChannelId.toLong) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/internal/channel/ChannelCodecsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/internal/channel/ChannelCodecsSpec.scala index ee4b1971dc..9dbe961f8b 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/internal/channel/ChannelCodecsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/internal/channel/ChannelCodecsSpec.scala @@ -88,7 +88,7 @@ class ChannelCodecsSpec extends AnyFunSuite { assert(bin_old.startsWith(hex"000001")) // let's decode the old data (this will use the old codec that provides default values for new fields) val data_new = channelDataCodec.decode(bin_old.toBitVector).require.value - assert(data_new.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED].fundingTx == None) + assert(data_new.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED].fundingTx.isEmpty) assert(TimestampSecond.now().toLong - data_new.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED].waitingSince.toLong < 3600) // we just set this to current time // and re-encode it with the new codec val bin_new = ByteVector(channelDataCodec.encode(data_new).require.toByteVector.toArray) @@ -100,38 +100,6 @@ class ChannelCodecsSpec extends AnyFunSuite { assert(data_new == data_new2) } - test("backward compatibility DATA_NORMAL_COMPAT_03_Codec (roundtrip)") { - val oldbins = List( - hex"00000303933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b13400048453785023143c39fa5f8bf7e4f9a0150000000000000222000000174876e800000000000002710000000000000000010090001e000bd48a19f9be2acf1037f3632ffb37e6e5613c9282955ac380000000c5016fee307c64f41705fab7b51a56a1f56118cbb916c39830eca872137ac36d93b800000000000001110000000ba43b7400000000000001388000000000000000008048000f015fe46abd1416829157b1c7379060933d6471d8efa672e5b2eaa81a0291b25de101d1f1f81637ea5a256b71bbbadb81bb210f376f6624a4e5dc3d9294f074e37a7b81181887280c5c7273926681436dde48800fe70470ad1cde534657c343744765c78184f0dc408c8169c3c3a5297dd138f54305424f0fc7f16df16cc7ca01306c022d01aae968489568130ecc2125eeb8198e4593aeff05473d265fc4d8ac4b20d8c9fa00000000c4000000000000000000000000006900000000000000000000000001dcd650000012324c17328eb691b439c21b203707b454e58f66b1a71efad4192349981028504500000000001580127a000000000011001009221c69ce980b6d12405cabc93e8a9af14a63ea54843744a441fc10c57bd5190023a910815cc5e17e7f0dc7456166fe2d1730bdfa08836f74a660c5ad7e3768d84a942f8610815fe46abd1416829157b1c7379060933d6471d8efa672e5b2eaa81a0291b25de12957009701000000000080b24c17328eb691b439c21b203707b454e58f66b1a71efad419234998102850450000000000554b274000fb45f980000000000b000a2268a74aaad781a26adfa36a53c7942a1a5045b1020023982201100529e4bc5d10df63af4a4effef9fa0354ce6798206770e0f6b718e2303258b2481103001f8bbfbcf924829410e64e00f9d1a428d5eba5f8364fdb9d51b54c064b13900a3982201102c14c68a16011ccaa4347482cc8a7b06ba8dd703888cb02fd18d53231e2c6af20110363c444d81383182db2da665fc6ad41dc071b47f8d55feb5fed013d7e38b6c5f80a3a910815cc5e17e7f0dc7456166fe2d1730bdfa08836f74a660c5ad7e3768d84a942f8610815fe46abd1416829157b1c7379060933d6471d8efa672e5b2eaa81a0291b25de1295772268f900000000000000000000000000000690000000001dcd65000000000000000000043b44c9ad3b82c6f7ab351ca83337a387d7b33b162b5a6223eae3e0398e55905813fd45af503be67ec305e284d4f51f05c98dff7bdc06612f6080fb54ead158909800000000000000000000000000000000000000000000000000000000000408ecd0f787a2a7806d62f97f90295fef80190f6355fe4bad69b79f360a3a17c10400919260b99475b48da1ce10d901b83da2a72c7b358d38f7d6a0c91a4cc0814282280000000000ac0093d000000000008800804910e34e74c05b689202e55e49f454d78a531f52a421ba25220fe0862bdea8c8011d48840ae62f0bf3f86e3a2b0b37f168b985efd0441b7ba533062d6bf1bb46c254a17c30840aff2355e8a0b4148abd8e39bc830499eb238ec77d33972d975540d0148d92ef094ab80000c9305cca3ada46d0e7086c80dc1ed153963d9ac69c7beb50648d266040a1411427576e00002600013afa1799ece7b5465112ba86c6672d37fbdaddd446f76aa3337d6b187e3e8640bc11c70cc6086cb01ec5f4d56b6b4c7f8683a77a060817af49d00cfefce0a6d821a4bfebfc134ab8847a5187ece761d75d3ccb904274875680f519848000000009d5db80000980002d5bd28680008048000000000000000080001388000000320", - hex"00000303933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b13400044820ec2b786ab7f784a38b29041fc8980000000000000222000000174876e80000000000000186a000000000000000010090001e000bd48a3ce05b32f90846a133ef7c4e465acfccfd1bf3a04380000000c501c635d0f6880e6cd6553e2953ac2a46978581b45ef3b0a3e6764ca8435f36555200000000000001110000000ba43b7400000000000000c35000000000000000008048000f01c3acb0ccf7c8bdd346cc793c0a8181657fbaac2561a9fe1edfbf8049e57e73540167aac7796687f8853daff6ea1969e56db107b9912864f4d5107ca77fec7c1df2011677e5073fdf1285f514ac3ee89c5b2ecbcae46572d3d70f95e5f6866f59979801d6a7bc1860b39470c81bd67b62a51db06f43b39f4d7568cd6d5643dfbf3f266801714a4d5d4524eeac9684f8c0c6745e126810fe98420a0481de9654f25062541100000000c40000000000000000000000000069000000000000000000000000012a05f2000012238cd59815b8925fd2b37b1863501d6851ac016b7241e5beee7148a944d79360800000000015c04b4c00000000001100103edfca032c9b2b241bf99e178df2898a261a2d7817c0ecb32df7d10aa8825a360023a91081c3acb0ccf7c8bdd346cc793c0a8181657fbaac2561a9fe1edfbf8049e57e73541081df5d8e16513cdb48388769e806305a99ce139ae4092097c25617cca382e413e62957009781000000000080a38cd59815b8925fd2b37b1863501d6851ac016b7241e5beee7148a944d79360800000000076b4944000bb7f4b80000000000b000a00b02eb4d78ea28a9bb870371b6f5ddea3daa7e502002418228110805bb8e6e5fc150c6f250413318fe0ef4da3aa1f99d44a897a0dcb490171271ef181101d6ca8d71e70f60e150511d001d7545031cc12de2f13b76c01e41279465e8bd980a398220110341be8137f0e914b4174da64f26e824f593b16b72d686212d928b1a3b771a81b811035944edf115e67579e9c5ce828dc9f76fed6dc92816bfaa519621b93af27655900a3a91081c3acb0ccf7c8bdd346cc793c0a8181657fbaac2561a9fe1edfbf8049e57e73541081df5d8e16513cdb48388769e806305a99ce139ae4092097c25617cca382e413e629574067e99000000000000000000000000000006900000000012a05f20000000000000000005994c72ab64fc714c2df0a9168213a46de91abb6253584c44d4235d83749d27581b2564bf1d66347e017679a49afeebe4483405b03b97b2398ca1a9bd27d6802bb80000000000000000000000000000000000000000000000000000000000040c12a306373063e01d873216f22efed896e443fcf71a4665748a1750f4a473b7e400911c66acc0adc492fe959bd8c31a80eb428d600b5b920f2df7738a454a26bc9b040000000000ae025a600000000000880081f6fe501964d95920dfccf0bc6f944c5130d16bc0be0765996fbe88554412d1b0011d48840e1d658667be45ee9a3663c9e0540c0b2bfdd5612b0d4ff0f6fdfc024f2bf39aa0840efaec70b289e6da41c43b4f403182d4ce709cd7204904be12b0be651c17209f314ab800008e33566056e2497f4acdec618d4075a146b005adc90796fbb9c522a5135e4d8227597000054c0001257dafb6f3a2d0fa1f9ccaa73f1dc8eba4ccda6dd64b116dbc1980627dd6fdfd2d205cfb617c180ce4d17c63a44123623c91ca386414860cd18e7ab3b0556249a1a4bfebfc134ab8847a5187ece761d75d3ccb904274875680f519848000000009d65c00015300002d5dd56c800080480000000000000000800001f4000000320", - hex"00000303933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b13400042310140aee04fc3a1082c0ba12337e8c0000000000000222000000003b9aca00000000000000753000000000000000010090001e000bd48a324e1921e98b8e82235e4b91f0c43f66a8375d304380000000c5018dc2c1bdf8102ecea6bc5e8c3e6ec50872eda537c75bbf8374b18b89ed0b2d9880000000000001110000000ba43b74000000000000003a9800000000000000008048000f01442a596afe3658e33d508b1250a8a2424a2f05d732f9def71719f13fc6c2ba2001f3d12cedf8af25f136969e80bcdc5cb2a655ca0e5abefa27388e690f1fa7c5b281fcbf05b7508d1a1c4517122cb247306e87025e876da029fd69f9fc937bf5bb8a81a27e97139b5f3633ad83d7fd79d06a068f72043981e8a061a4d3291ebfec86fc018ce3c050049c4a7e13ab3c82cbc5e6a961510026b4f2e645321c500c1690b32480000000c400000000000000000c0000000069000000000000c11aac0000000058a7145400127de4dfd06a3ef178f16460e9d56accf79b916b78ea51eec7d70bd8e41247bd9c000000000015e06316800000000011001026860fb237c202673f6a5ffd8c3fefb6cd22be264c9d17533e5a93723683cf6f8023a91081442a596afe3658e33d508b1250a8a2424a2f05d732f9def71719f13fc6c2ba2010816a362d6f6e23213f27d9437b45a06ad0d970bf15ec8f205808ff0faa6110beeaa95700ac81000000000080fde4dfd06a3ef178f16460e9d56accf79b916b78ea51eec7d70bd8e41247bd9c00000000004924b140016f310000000000001100105ae2362cc327dce623b5db68025ee61308d10282c2f3b3f19665cbadd4e13f456be59600000000000b000a18552aec4e13ac9f0bfcbaf578caa197b036bf5882002418228110804dc0de11f5d3d16a50ed0f0d40689b80c588efb40b91ffa69c07495c84e4736601100687f0bb0b370708d117106defad2c5054f0a430a93326a9608d075e3aec927a80a3182181100eb6c682bcc7811441516094d7fcfd6f7d84b5a82ee6a7545e6069d6e9dc9cfb810fa3140dad08f6d2f375b94c890d615bc8a2cee555bb4477697b29f898ea0bb900a3a91081442a596afe3658e33d508b1250a8a2424a2f05d732f9def71719f13fc6c2ba2010816a362d6f6e23213f27d9437b45a06ad0d970bf15ec8f205808ff0faa6110beeaa95771d25a900000000000000000000c0000000069000000000058a714540000000000c11aac2ce2e2df2d62ed7156d84081e660c147cfca16e4d161e7b38765600145a5a99f01085ca6082d2b2c688b41b9e04e94c8d53228b519e485cf400ce3fd82dea93d2d80000000000000000000000000000000000000000000000000000006000040bcac26026a806d036cde7604d538a37c6ae77370116eed62524a32511611a34940093ef26fe8351f78bc78b23074eab5667bcdc8b5bc7528f763eb85ec720923dece00000000000af0318b4000000000088008134307d91be101339fb52ffec61ff7db66915f13264e8ba99f2d49b91b41e7b7c011d48840a2152cb57f1b2c719ea8458928545121251782eb997cef7b8b8cf89fe3615d100840b51b16b7b711909f93eca1bda2d035686cb85f8af647902c047f87d530885f7554ab8000800f00003ffffffffffc0081336fd0967a5266b8509a0cca2c070e324bb7877cc7041a1798a7a5bf2b9e1a1400f40003ffffffffffa0040cb42f0fe720c95aa524b2a6a9fd9aadd2907bbad6a6ebafcd787d31304a386a10000ffffffffffe8fbc9bfa0d47de2f1e2c8c1d3aad599ef3722d6f1d4a3dd8fae17b1c8248f7b3813adbf0003a2000090907e23eae5a374cf5b46993d23a99ae4f30cbc431eacdbd08f701b35a74a1092dea386c4e84d003de090f1d2fca13ff4ecc94a9a6137f41d349ef39d859c0e10d25ff5fe09a55c423d28c3f673b0ebae9e65c8213a43ab407a8cc24000000004eb6fc000e8800016b0b2bc400040240000000000000000400000fa000000190", - hex"00000303933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b13400045b99a64aeed970a4f92f84b3f38063090000000000000222000000003b9aca000000000000002af800000000000000010090001e000bd48a64bdaacd4b8d49aea9ceffe363deeb4c1124a048c380000000c501ecc90ed67abfa96f252bf6924edbedf8ebaaf6e148a50189db31959b045179cb00000000000001110000000ba43b7400000000000000157c00000000000000008048000f0158af8165d00273c406f0bf73ad5fd8467c595a43f1bad6f8af1da23dd7aa3bca017d003b4e4757da025d2164040c8e57c09de74bc5cee28b7efd2e30dd9809294801688e6f276aec12caa551029ba7145b7a0c3c813b212a1b0f753be75969b0965701e5c8d50f1e23a0f5cfde446605023520b2f74422f9903e32a8410757a5ba765101655c55164d6a3930054489c171af4ccf27b5a15a1fc53d52dab58c05c20805f400000000c400000000000000000000000000690000000000000000000000000020c85580001267e134d8edb38fdda01b44375f55825c4562ba64d3d82c848039fffcb89131bd000000000015f0640800000000001100106a193df9a96acadb9438baee04c119e320b2331bd3208d9eb1dd75c18433dc8a0023a9108158af8165d00273c406f0bf73ad5fd8467c595a43f1bad6f8af1da23dd7aa3bca1081c350e29f8d9da9000adc66ec0897296fb912c0da3725cb05bf40575ef9b2c342a957009781000000000080e7e134d8edb38fdda01b44375f55825c4562ba64d3d82c848039fffcb89131bd00000000002f7537c000eb180800000000000b000a50d500b470598c27b2f19860c1802d0666bc70a582002418228110804faac2eeeb521f9bbaa3902e50aed9361f331366b547801893f10beb4f7c8745811030c92fa725c360f64a46782ec62b1a0126fb06abeec926745a2f3345fcaa3f4e00a39822011038532f43281c1a1fd051a7bab7a9f54a2f77adf9e06e785e951366cd5004bd7081103002f7cba5acdbcf3a83eb147cd27db12027ee01217ffe77459468b36ec868f180a3a9108158af8165d00273c406f0bf73ad5fd8467c595a43f1bad6f8af1da23dd7aa3bca1081c350e29f8d9da9000adc66ec0897296fb912c0da3725cb05bf40575ef9b2c342a9571897d390000000000000000000000000000069000000000020c855800000000000000000469c5ab874ed9583c096692d602dfdc014932b0bc7b01d3e1b4ef47d638de68301dd58d60f41002738b4fe6833dc074d5e91ff8f0bda1983a27fdc18624d268cfc80000000000000000000000000000000000000000000000000000000000040d4ac8ce1e4619ca0db4ee743ecf5f2455c6c6a43d687c4cfb5b8e094fb6eb6e2c00933f09a6c76d9c7eed00da21bafaac12e22b15d3269ec1642401cfffe5c4898de80000000000af832040000000000088008350c9efcd4b5656dca1c5d7702608cf19059198de99046cf58eebae0c219ee450011d48840ac57c0b2e80139e203785fb9d6afec233e2cad21f8dd6b7c578ed11eebd51de50840e1a8714fc6ced480056e3376044b94b7dc89606d1b92e582dfa02baf7cd961a154ab800019f84d363b6ce3f76806d10dd7d560971158ae9934f60b21200e7fff2e244c6f4275dda00019e00016cc22d34b1d45d911d1c60dc491698c6b425c2fa257162a79d2a612e7daa961f36eb450feae62abba32814c8b0a53a774927602afcc00a11a267ad20ac15f8d321a4bfebfc134ab8847a5187ece761d75d3ccb904274875680f519848000000009d77680006780002d60ef1a800000480000000000000000800001f4000000320", - hex"00000303933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b1340004ebc503c4cebac33c0e40b4bd0062bb280000000000000222000000003b9aca0000000000000186a000000000000000010090001e000bd48a3e5d4c9395b104c1a78aff6e13bcea167ffd7adf4380000000c5012d45627c1bbda9bb4db929146a5c07b5bc940d7fa6c09757d261c408cc85e6cd00000000000001110000000ba43b7400000000000000c35000000000000000008048000f014df6f2054f92a033e51aa9f9f5801f1188683a33b2e73f4a72baa1f02f6256bb01c515ac0e0cae509eeb7cec2f278a38efa4128f70535a49b6f8b8b2091f86fe16010499aea25e05e121b14f9623b2171cdbc883b65d738af0f2723ae6d0886a240801dc68be0218a6e9886c66125f2ae9375b12da9141bab72c1d9fd059885071c7ee011df98a5392f227e5e4361cb1fd852d011600af6db8fe20d9140d662327a0320000000000c40000000000000000000000000069000000000000000000000000012a05f2000012204effb553765cf4b4fa9c4f9361b9bfdfc10f52a4e05441f7708ee33d788ca6800000000015c04b4c0000000000110010311114c1dbee96874db3f3922726069b12fcc33ce9d243371f7cc58b9207d66a0023a910814df6f2054f92a033e51aa9f9f5801f1188683a33b2e73f4a72baa1f02f6256bb108154b4e6fc1c898b1f01abbcced96215045aff200051443a71300eab2dfdda6e162957009781000000000080a04effb553765cf4b4fa9c4f9361b9bfdfc10f52a4e05441f7708ee33d788ca6800000000019e2b44000bb7f4b80000000000b000a46907ae3546268a91ee157200033d49090aba53a8200239822011038d9a30f62d19cc2abf4149f822f77fa3c709e9812659857a9c67b389c16f72b811027d5aefa2b71770bcbf06d284f74194586d42711cae372d409674ea537a0f17700a4182281108075ef5c946ddffa94597a02a3432821d305d083b87973e2cd1f5e20e3f9671d5f01103d891f78f58a8f3410fe884bba2ad18e7fd6a3cd1d8e6bebdff067abcb2b542700a3a910814df6f2054f92a033e51aa9f9f5801f1188683a33b2e73f4a72baa1f02f6256bb108154b4e6fc1c898b1f01abbcced96215045aff200051443a71300eab2dfdda6e1629576f2e611000000000000000000000000000006900000000012a05f20000000000000000001342c18cb3346441382db1f15e6f903266533d9e80fa3ffdaba4c121d9cf290e816f7792df2e55a3839a73e68064529a93d181bd0081b9408971e457c5d3e80f5780000000000000000000000000000000000000000000000000000000000040874c16811ffab089eb0654c253515ee95a9386865b1746590830a150e07ee19cc00910277fdaa9bb2e7a5a7d4e27c9b0dcdfefe087a952702a20fbb847719ebc465340000000000ae025a6000000000008800818888a60edf74b43a6d9f9c91393034d897e619e74e9219b8fbe62c5c903eb350011d48840a6fb7902a7c95019f28d54fcfac00f88c4341d19d9739fa5395d50f817b12b5d8840aa5a737e0e44c58f80d5de676cb10a822d7f900028a21d3898075596feed370b14ab80000813bfed54dd973d2d3ea713e4d86e6ff7f043d4a93815107ddc23b8cf5e2329a276a3c00001e00014c3587bacd751833989db440b04e3495a0e42ad2044fabcbd9e8b5bba724d6a8b62340e984620a26650265285b41c8a42f92a753d5da3bb79492b00f4cf0afe921a4bfebfc134ab8847a5187ece761d75d3ccb904274875680f519848000000009da8f00000780002d636fea000080480000000000000000800001f4000000320", - hex"00000303933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b13400042cd2e941f255281c26398db6a8a1de680000000000000222000000003b9aca000000000000000bb800000000000000010090001e000bd48a7f3570da68d351d084e067be3550e493965570124380000000c50159d8b6f824f1b8c32b4dd0886a9e5855fec70543947058d18e07c4322d3b1f0180000000000001110000000ba43b740000000000000005dc00000000000000008048000f01a1fe7ec0cac2a7e02a182feb9c697565f4835c6564c9786328db766c325d7bae8143548f6a7480907ab5c8e9ef78c95619d21b9b7ce70b8d4a05bae665749797718175b52d552ae33fe2017d7212d40949c4719695a671d883ad3e3e94e6565b243381f27bdfe6152fccb9671e631a5b941806efff67dc529b928dc55eed5e9d98497e019791b75f609c86bdb28c80ccd97735044bd5762d99634d7cceacb8e8407dab2e00000000c40000000000000000130000000061c780000000000000000000000008f0d180001266638004d82ecec4f60c3eaf09feecd41fe91f0ed45aaf236caad9d4fdf6b005800000000015f0498200000000001100103e8a3acc14bc46082d4826fd7e2da218e73008c7398b69ba5f9a3a107b9b3fc38023a91081a1fe7ec0cac2a7e02a182feb9c697565f4835c6564c9786328db766c325d7bae9081e73346fb1bab357137655f7fb449dd72fbbda5429c04b4b5ea544137157203dca957009781000000000080e6638004d82ecec4f60c3eaf09feecd41fe91f0ed45aaf236caad9d4fdf6b00580000000005341794000a5830200000000000b000a14d220ab715289038289a0d378431ffe062bd62a0200241822811080551aff2f4d16aef1e6457cdff3e4cf3cdfb34d539caef94f65e0cc4d78bae8aa01103cbd98822f7052917ddc158371960e23c47e717ce29003a96ef5e6dcea90d13480a39822011022b5c79a42a6f612db89f76dc01582c3645aaa3ece241910f10e62ad2f9fad5f011029ab547e3c220d446890170989d9973ea8e7c6a187aa98508216e679d0ee6aae80a3a91081a1fe7ec0cac2a7e02a182feb9c697565f4835c6564c9786328db766c325d7bae9081e73346fb1bab357137655f7fb449dd72fbbda5429c04b4b5ea544137157203dca9575f8ed790000000000000000000130000000061c78000000008f0d18000000000000000007105f14b446ec2464b0d6fe32b93d0b318727275cb973bd0ae41dafaf7a2071e81823595882dd284591edac9ab18fbbbb16c2b4df3a7c3e5af378beac3439b2f5000000000000000000000000000000000000000000000000000000009000040fcf6c6b31358035c98d2081a4b039387f1813d40e042d20d349aa97f4ce177b7c0093331c0026c1767627b061f5784ff766a0ff48f876a2d5791b6556cea7efb5802c0000000000af824c100000000000880081f451d660a5e230416a4137ebf16d10c739804639cc5b4dd2fcd1d083dcd9fe1c011d48840d0ff3f60656153f0150c17f5ce34bab2fa41ae32b264bc31946dbb36192ebdd74840f399a37d8dd59ab89bb2afbfda24eeb97dded2a14e025a5af52a209b8ab901ee54ab8000c00f80003ffffffffff70020193e97472cfeb4f6a1ca143864418dae2c3410863cc5cc8eb855cfe21923a5d8003b0000ffffffffffe0040e5a306e9b800a535873bdf73a16b4711706612ce7a2556faa55ef4ec3c36b642007e0001ffffffffffb400817aec2b40ea189534d55de91c40b1b8149362a9877f3f9a098c869914138d20220001ffffffffffb5998e001360bb3b13d830fabc27fbb3507fa47c3b516abc8db2ab6753f7dac0162773b20000c0000117a5bebbccb9cb5d7b1d56ba8498fc2ad6f7101f72faf0e5e3852b618c059a0ba358fdb0ec9fa7ca23ba27e2f43585748ab314035056d246e9f8263815c97148a1a4bfebfc134ab8847a5187ece761d75d3ccb904274875680f519848000000009dcec80003000002db14607000080480000000000000000800001f4000000320", - hex"00000303933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b134000418786b7ffc1d090ff422fb48a20d29c50000000000000222000000003b9aca00000000000000271000000000000000010090001e000bd48a7bef1576f01a4506a156e3e30aaa6e0ec125b3c2c380000000c5010354534b9ebaec0ae187e0a3697f92f831df3a18d2b3a3da158f47069176b30580000000000001110000000ba43b7400000000000000138800000000000000008048000f0184fbcd70fe378a7edc0a335fd39b060a40d76063e5b0d3bc6889e2ae65c1060a01cc0288fd93f70ace819922418aa118be539ccd6334aa2a5a8e20499330fb021e818cc753377e21bd0cb66ef7ad10f9f10fbd22eb5bb9250efc5028f14fb040ffc2810de16dff398d4657f0b28d52ecf82310600024e51821f107d8d099e05b6f4c148129c7f876b32b47c3de83fe7ffb645f9c7bdb29db902e898fd58b7d2e2675897e00000000c400000000000000000400000000690000000000000b73ef000000001dc1f1110012714dd238ed32ff6e81e9ad0a3a84b561e7e785dc120da79c9432382bac9cf8d6000000000015a02107800000000011001037a7f76b6efef5ac72c901f32db8e406d24746171635a377364ded75b763e7918023a910816cdc4a185fbd2a32c613ba25b722e9c5c9d9a50b26cf6fbc1e23e5392c2a2125908184fbcd70fe378a7edc0a335fd39b060a40d76063e5b0d3bc6889e2ae65c1060a295700ad01000000000080f14dd238ed32ff6e81e9ad0a3a84b561e7e785dc120da79c9432382bac9cf8d600000000001b52bcc0016e828000000000001100105eab83caf9fbec9f0a096120c1a393543fd68cd1c08df9b5b08d8b0775cf484e2c520700000000000b000a7185d95efec83c7c89d8430a5e84ed96b8d7471582002398220110135e7d229629d102ea8a96e5e5fcb0050a38855c2e307f71f3922ce75d8e6e7301101bfcc6d4caacf93b7550289851cc50b653b63fe73b7f3fc008ad2ba97b3f877b00a418228110805335b016ed586154b60a26aea1b107188c4e2552d4f32b3addfa10329bd444b50110027e798f3a1368ba9cdcd08694b4ed3a0fa195118999ea711a2db08bd282dc8500a3a910816cdc4a185fbd2a32c613ba25b722e9c5c9d9a50b26cf6fbc1e23e5392c2a2125908184fbcd70fe378a7edc0a335fd39b060a40d76063e5b0d3bc6889e2ae65c1060a29577547589000000000000000000004000000006900000000001dc1f11100000000000b73ef3cdec220d901aec16b5ec448f99e8cd7082b4e3ff7f0b211feec60b1c78b28cc01e51fcd6b19f388491459f68085af5f82ffafab8c565acfbcf7b6459e8c5c44d100000000000000000000000000000000000000000000000000000002000040de6c087f829e88a9323c0589c10e73ae84762c6b180befc1dd188455e117f28b000938a6e91c76997fb740f4d6851d425ab0f3f3c2ee0906d3ce4a191c15d64e7c6b00000000000ad01083c0000000000880081bd3fbb5b77f7ad6396480f996dc72036923a30b8b1ad1bb9b26f6badbb1f3c8c011d48840b66e250c2fde95196309dd12db9174e2e4ecd2859367b7de0f11f29c96151092c840c27de6b87f1bc53f6e0519afe9cd8305206bb031f2d869de3444f15732e0830514ab8000400f40003ffffffffffe0040441103ca17be2cdab1bc90a5469eefad9285a46b1dec58f3a3cc9c55f0d9809d0000fffffffffff8e29ba471da65fedd03d35a1475096ac3cfcf0bb8241b4f39286470575939f1ac13bf3500001b0000abcb596c165adc624b345b4e4425c1c30ff019cc82ea504745053ae94abcae87913271f55516d8adb27d782d8c178cafb42e8533d557bfbef2e479a62630d73cd0d25ff5fe09a55c423d28c3f673b0ebae9e65c8213a43ab407a8cc24000000004efcd400006c00016b805efc00040240000000000000000400000fa0000001900", - hex"00000303933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b13400046c04b597cf1d81b9b84c7c7c70bc6d800000000000000222000000003b9aca0000000000000003e800000000000000010090001e000bd48a54f5b8e9bd78ae126232c00455abc7e1e60844a9c380000000c501ca2634aa6ad1e63689ee41fca10fcdf57d77e4a26efa23f77331b5ad23bf3eb4800000000000011e8000000002f34f6000000000000001f400000000000001f4004800f181dd29f2812dd081b7cb819f400d166cd3e30d0bc7f0c6e9449212355e9b9218080102e53cb2fd643591b4542baccb599bec0a51844d12555ff354adbb967ced6619812088fdc0456c13b9ccff78bce7990845cea4dd30775641b018ae86200a7869a6016568e3a0348f0f410c03de75cb037d7d448db942f13d057a3d541abd992c1a95813e98e684dfe48d37a34a741ab8d27865724ff8795e24ba83ce05b0ff80168fd18000000000800000000000000000000000186a00000000000000000000000002faf08000124975c688708ceffbfa00f0fcc93870e9f6a2a59997523b142e8a7de2c3f42332800000000015d0430080000000001100103f4c709e66ac023abe6d04cb9d78da46697851be43f73ec573a7d7238061666d0023a91081401444725d8a3adc3ec4ae32ecdee5d613e25136c5a7c54e47b1618e874c6c7d9081dd29f2812dd081b7cb819f400d166cd3e30d0bc7f0c6e9449212355e9b9218082957009781000000000080c975c688708ceffbfa00f0fcc93870e9f6a2a59997523b142e8a7de2c3f4233280000000000ad8a54000a3318080000000000b000a4f19d1f5661d894d6bae2860386ea141864b0809020023982201102b090849b4f58e151beced54974499157789508b2f012294ecef706191f1b8a501103b4691d19046c01950ae9b366b6e31019bc0fb5f379c026c5218e9a7f5cff2ef80a4182281108040df79dcef15bf5567fc8bb3dd3053ce4d18b78ba1602d0fe0c0344133e402bd01101954041195024719b50fdcd93eecb814be70100d5203bd3fa20a217e0d3a39c180a3a91081401444725d8a3adc3ec4ae32ecdee5d613e25136c5a7c54e47b1618e874c6c7d9081dd29f2812dd081b7cb819f400d166cd3e30d0bc7f0c6e9449212355e9b92180829571598f7900000000000000000000000000000186a0000000002faf08000000000000000005123401c95c7b9e3a1aa09defedce700ccd54ac16a1357a65de3c5d1e75911d98178215b7d9d0c50400eb5cf577720032a439ea57f56101a638fa283415c4ed95280000000000000000000000000000000000000000000000000000000000040ae36987e760a08a09af69bf1204953a8a8459e1652b3191ab97252df4b6e2341c00924bae344384677fdfd00787e649c3874fb5152cccba91d8a17453ef161fa119940000000000ae8218040000000000880081fa6384f3356011d5f368265cebc6d2334bc28df21fb9f62b9d3eb91c030b3368011d48840a00a22392ec51d6e1f625719766f72eb09f1289b62d3e2a723d8b0c743a6363ec840ee94f94096e840dbe5c0cfa0068b3669f18685e3f86374a249091aaf4dc90c0414ab8000125d71a21c233bfefe803c3f324e1c3a7da8a96665d48ec50ba29f78b0fd08cca2793ee00007a0001fcc2f2c25b8ffbad20e1a51323e89a71a54e4258ee5ee4fd13dfa8d3077dcc4b90a4d9316fbcb23291b988364bec1c736e7fbed243f37ce29ca41287b5ecedf65d12699dcd67dcd22d2620df7500767ec601669f72fa2b153bdc0d11198cee39b3e5002b0c241c15dac6e5dc7571f30245c1d9fe852aeb898371ba60f922c1d796799fdb2a65210e7d3a0832f1e6bc8fe11132b6d327781e4b5795bc7e3661bbac65ce540f2657073ac112275f9a89878554012c541e4735984fa290a677129689cb15751cbaa1ee5782ea899df0ca12ce82e27a5dedd92c7a18240a80ea0cbb8ccec008b86b6df2c6ba688bf5fafed386a9eef3c41d0801221011c2e9aa3d11000021a4bfebfc134ab8847a5187ece761d75d3ccb904274875680f519848000000009e4fb80001e800001c99c425578eb58841cbf2f7f2e435e796c654697b8076d4cedc90a7e1389589a01ca2634aa6ad1e63689ee41fca10fcdf57d77e4a26efa23f77331b5ad23bf3eb481401444725d8a3adc3ec4ae32ecdee5d613e25136c5a7c54e47b1618e874c6c7d81dd29f2812dd081b7cb819f400d166cd3e30d0bc7f0c6e9449212355e9b92180829e096742df5136cff532085c0e5e14be8e15fea12c60886f7b2303f9b52bd60166b3a3692bf8ca0441221affb5b529f3a3b476ace8c9a6ce42604c28b009be6a1a4bfebfc134ab8847a5187ece761d75d3ccb904274875680f519848000000009e4fb80001e80002d78c6870000004800000000000001f4000001f4000000320", - hex"00000303933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b134000412097d0df71276a5f63f8e89fb099b5e0000000000000222000000003b9aca00000000000000138800000000000000010090001e000bd48a6fe687952d2abfec1db788e8362d9ca322a97b06c380000000c50124c327cbd080482144a5ead62152453771953e2c436b3274e417d4f06aa1c6bd8000000000000aaa000000003b9aca00000000000000138800000000000001f40048000c81a101511693e981f4d66cc3be8ebf2dacaeffc148359a54dbf8484194c635e5820182783c5e27ed7dbcb4ae8cfb3b424d434199944741a90a89e48b4e13bd4d2cc5019e4f2292a02ff95d4dea4bde6d8cb905b3a784247ed80eccc2d85df75150cea981354c5e9dada31d28ff594d4fb1886e4e96e74e11dacdeaed760b61b45407f49b818ff98e68eaabb7b0c3d1378dcf583f021bf58170c555446bf51cdd936d34d3a8000000008100000000000000000700000002a0db8000000000532d25000000000e93855b001211af2d9f5108fe8c6780269a7268739a088a5c095bd8533b2cf1e8c69d80927b00000000001590508380000000001100102592982f6a3d87edbdac73880345c703a3566e46a5c93e461239d60813aa722c0023a91081a101511693e981f4d66cc3be8ebf2dacaeffc148359a54dbf8484194c635e5821081bd33094577f9746a581f1c099e3bc7f3ac2bd42c12ee6b90c8d78d0425ea5417295700ad0100000000008091af2d9f5108fe8c6780269a7268739a088a5c095bd8533b2cf1e8c69d80927b000000000027d36640014b15000000000000110010795c0e513c6cb821be485543ad63fa40b757cbe0b766b10af69f1e289c141d821ed40180000000000b000a01460f121b1742b263c81e7c514b4845462a699b020023982201102c40e8cc2f700066249d00407069066bc2c5464cebc2d451b2a7c6f85e07059801103c00b5ed53ab65d50aaec7aeb0373504b3d69610218fd20494fd8f0c733cfaa480a41822811080578c4cc8cc478a3737f62f3594277d632c25f301abf1dcb32ddd8161b126625b01100463507612d429451c5e3c6b7e558f0fae8173985488f93260877fb3d2ea341b00a3a91081a101511693e981f4d66cc3be8ebf2dacaeffc148359a54dbf8484194c635e5821081bd33094577f9746a581f1c099e3bc7f3ac2bd42c12ee6b90c8d78d0425ea541729576a455e100000000000000000000700000002a0db800000000e93855b0000000000532d257b4e95eb654864c0c1a5a8bedea7ddae80ba8764bcc1901d774e4bdf4490dc5c014f6e3aa79b07ea36f6239245bf31e8779710939c8eff24dc801060177753537e00000000000000000000000000000000000000000000000000000002000040f7e75060488a8feca76d01572a81ba3e65cd756589fda2c29bb1bd38c6ebd40f400908d796cfa8847f4633c0134d393439cd04452e04adec299d9678f4634ec0493d80000000000ac82841c00000000008800812c94c17b51ec3f6ded639c401a2e381d1ab372352e49f23091ceb0409d539160011d48840d080a88b49f4c0fa6b3661df475f96d6577fe0a41acd2a6dfc2420ca631af2c10840de9984a2bbfcba352c0f8e04cf1de3f9d615ea16097735c8646bc68212f52a0b94ab8000c00f40003ffffffffffe00403628282371cbd50e312e846bc4bb1bdc25c3c9e15c153b6ddd9554d218fcdc90007c0001ffffffffffe80103144e0b3492841cee73bb7102da710023d0eb43dce3461c012c5637425b374e0801f80007ffffffffff90020fc6a3795285dc99baafffcf9eb2f432f427ff9deda875d21fff0efc8aedf31f580007ffffffffff911af2d9f5108fe8c6780269a7268739a088a5c095bd8533b2cf1e8c69d80927b09e60d800009800047c140c3d9812a251d71c21cfe25647e8213a46892955bea39a619d2d87b511bcb68b122e3ceac40167a22f75fc61d4f0606227f5fd974667e66d05426ef39d428692ffaff04d2ae211e9461fb39d875d74f32e4109d21d5a03d46612000000002798360000260000b71786f40002012000000000000007d0000007d0000000c800", - hex"00000303933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b1340004a4a962577971b6a380ff91e9e02f4fde000000000000022200000002540be400000000000000271000000000000000010090001e000bd48a7e7bdb6769b03f71d77ed506c775bc16604701604380000000c1013801c23a546ec519efc587c4bf9b88afd651d6857d3f29466dc5a5d45b086a220000000000000aaa000000003b9aca00000000000000271000000000000001f40048000c81a621eea5fb4bf52b494cb5c6f8e2239d3b00ded351bdb08918191c6e811992b781dcb985906a311f04d162241d0bdd95cdc064c1bc00ba66c9d74878578bec712401f028efe204476baac4cba09366af71e9548547ea58c5c776d616504d130ff8760152a5ecc6e185cc3714642a5e1abeb861d58caf8a469f03f9fb66a6e70001336781a2bdaf62f43d9b32fe402a8a551aabd75829ebf963b625cb720cfd248b6090a680000000810000000000000000000000000002710000000000000000000000001dcd65000012038147ba498c54032286b50fc64d00d03952ec2e8cc84889ecc9bcb435d1f4bc800000000015a0210780000000001100106287220a6f3fbcd618c14c8dd8c721980e88bb2309af9c339000b191ef450cff8023a91081a621eea5fb4bf52b494cb5c6f8e2239d3b00ded351bdb08918191c6e811992b79081ef261ede0d50d655d8a2b10e1517da9b3d1db9f6ab04e773daed4150836d0d12a957009701000000000080838147ba498c54032286b50fc64d00d03952ec2e8cc84889ecc9bcb435d1f4bc8000000000535472c000db9f0780000000000b000a32c34a7ece80c3f9abdb9bee77d17382359d3dd502002398220110070eac66bd8d8f47892fa238f47c9ff3358fe5d65f5e8d52e1385542aa9b1cc40110352a047be2074990f2618600ae095f09d68a3f120f87a6995eab34f23fb28ac800a398220110201569efcd4ea0dfd9a960304b2498bcc09aca24682075f6aaa8fda844c8d45181103f820404fcc0638bbb5900d370bbd4a96b24754f3b7dfbe00f4328aff17ea45200a3a91081a621eea5fb4bf52b494cb5c6f8e2239d3b00ded351bdb08918191c6e811992b79081ef261ede0d50d655d8a2b10e1517da9b3d1db9f6ab04e773daed4150836d0d12a95728a48b1000000000000000000000000000000271000000001dcd650000000000000000004ab1ded8f41c237c7696f83b49e9ef147442b038de36d0c12780919ac86a8e04016a8091e010f7088ee0100c0816d5b96361f30cfcb64a8fe3dfb16fe53ec5eca80000000000000000000000000000000000000000000000000000000000004096e590a43d31f1e3ff97afbde081569dbd0d4f2040503a3291fc4573ffdc6b78000901c0a3dd24c62a0191435a87e32680681ca9761746642444f664de5a1ae8fa5e40000000000ad01083c00000000008800831439105379fde6b0c60a646ec6390cc07445d9184d7ce19c80058c8f7a2867fc011d48840d310f752fda5fa95a4a65ae37c7111ce9d806f69a8ded8448c0c8e37408cc95bc840f7930f6f06a86b2aec5158870a8bed4d9e8edcfb558273b9ed76a0a841b6868954ab800000e051ee92631500c8a1ad43f19340340e54bb0ba33212227b326f2d0d747d2f22949ce000038000154911be04ca6953530013582f1dbbc219e8bbcb638ff8f50c6d083ce6267716e955127b2d47fdc36734f5dda822c41999f06565b3e5cf9fd740dd544f8a85b0021a4bfebfc134ab8847a5187ece761d75d3ccb904274875680f51984800000000a527380000e00002da2f5e98000804800000000000001f4000001f4000000320", - hex"00000303933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b13400042a85e26dff2a0673ca6296e001ac772c0000000000000222000000003b9aca00000000000000138800000000000000010090001e000bd48a4bbf36cccccda24ebc49ddbb5788a54f6472e0f94380000000c1017af289981f2b50125dd60e48097c4b206687e5e270d77addda5d33822f74d1ef8000000000000111000000001dcd650000000000000009c400000000000000008048000f01449d01657fea8c6addb6af91c3085177fc2ee4ea53c2e54f563483f2fa158346015da08f4834e5ebf637c0172e1891537f07cde25fba794b5aaf60357844779d04019926f90f40cee6ac1ebdfcbbca5e75a64c39b74701070e55bce95cdf2d2fcfe90113f0d92a02b97619183938fe2e33d5ce1731ac43fa1b9aaa07545bf0cacd6dee01f9d1f0d872ecf0c85c5ed2ceb3d44660970333d2412f531314fd139fa4fa8c3000000000c5000000000000000003000000003df000000000000e1f43800000000ed8933c80125e17209496a19fa72cf310e1b904367d920a52b1ca0bfb3f8d7778d8fce90721800000000015905083800000000011001001ad0cccb8ec1223eac85f40885e30d34c6768bcbcafd8b89569e6cd4ab870cf8023a91081449d01657fea8c6addb6af91c3085177fc2ee4ea53c2e54f563483f2fa1583461081ecadd4be4df0a73c91037884fff9c89ce56159bc702fac9ea5504c3991ee4e07a95700ac81000000000080de17209496a19fa72cf310e1b904367d920a52b1ca0bfb3f8d7778d8fce9072180000000004931a540011d838000000000001100103d8a2586fb04c925e4f42e81adc0591674443ed51eba9480ff266ef112ee05151aa00380000000000b000a735f150ab1fedb677f8542d06610946f45c546b80200239822011030923a3acfb7fe22b1ccc2abaa86e7ea7037066fcfd9b01826b45d5b0d153e1c8110109f79ab72544af4417ff66cca54f2c746a2795ad9469539c96197fcbf34f3bc00a3982201102aaf12427b38ccd5275c7f01519c3d51e437d9e324ba2350f57e5bf8aa5f48a6811029c0a5501742ae616b7465f0a70c54828ef261fa73f20828f39d283946a2b73a00a3a91081449d01657fea8c6addb6af91c3085177fc2ee4ea53c2e54f563483f2fa1583461081ecadd4be4df0a73c91037884fff9c89ce56159bc702fac9ea5504c3991ee4e07a95744bb8f9000000000000000000003000000003df0000000000ed8933c80000000000e1f43f44fec0b516ed5c36463cf41097c0a751be4fb1ad230f337de55fea885d4ea190172ec1ab7c699c0f19382f379449fce9fe0bf25edff9418619c6f02923ad963ef80000000000000000000000000000000000000000000000000000001000040a21436454107618d566629ff5bafbd35567da288a1dabb7b827599f8bf7477d900092f0b904a4b50cfd396798870dc821b3ec9052958e505fd9fc6bbbc6c7e748390c0000000000ac82841c00000000008800800d686665c760911f5642fa0442f1869a633b45e5e57ec5c4ab4f366a55c3867c011d48840a24e80b2bff546356edb57c8e18428bbfe17727529e172a7ab1a41f97d0ac1a30840f656ea5f26f8539e4881bc427ffce44e72b0acde3817d64f52a8261cc8f72703d4ab8000800f80003fffffffffff0020aa3ade905629ba9f18e965fed468f2d514fe6d146b5863e4c2087f4d65a8681c003f0000fffffffffffa0041862f3d4278167c52104b5f77f468da29945743dffd734eebe4576b9e2e156dab0000fffffffffffabc2e41292d433f4e59e621c372086cfb2414a5639417f67f1aeef1b1f9d20e4314af790000260000adb86dbd5ef456ad0f73fe92712aab2d8856bb72ee43242042fcd6e4a5786bdf451b89629976c2fc2a84cc37642b6bfeadea98b1ba0e264cbe93b1016fc6393650d25ff5fe09a55c423d28c3f673b0ebae9e65c8213a43ab407a8cc240000000052bde400009800016d6bda5c00040240000000000000000400000fa0000001900", - hex"00000303933884AAF1D6B108397E5EFE5C86BCF2D8CA8D2F700EDA99DB9214FC2712B134000456E4167E3C0EB8C856C79CA31C97C0AA0000000000000222000000012A05F2000000000000028F5C000000000000000102D0001E000BD48A2402E80B723C42EE3E42938866EC6686ABB7ABF64380000000C501A7F2974C5074E9E10DBB3F0D9B8C40932EC63ABC610FAD7EB6B21C6D081A459B000000000000011E80000001EEFFFE5C00000000000147AE00000000000001F403F000F18146779F781067ED04B4957E14F1C5623AB653039B2B1D49910240848E4E682DB20131AD64F76FAF90CD7DE26892F1BDAB82FB9E02EF6538D82FF4204B5348F02AE081A5388E9474769D69C4F60A763AE0CCDB5228A06281DE64408871A927297FDFD8818B6383985ABD4F0AC22E73791CF3A4D63C592FA2648242D34B8334B1539E823381BB1F1404C37D9C2318F5FC6B1BF7ECF5E6835B779E3BE09BADCF6DF1F51DCFBC80000000C0808000000000000EFD80000000007F00000000061A0A4880000001EDE5F3C3801203B9C79160B5123CADE575E370480B57B0C816EB35DD7B27372EDA858622EB1E808000000015FFFFFF800000000011001029DFB814F6502A68D6F83B6049E3D2948A2080084083750626532FDB437169C20023A9108146779F781067ED04B4957E14F1C5623AB653039B2B1D49910240848E4E682DB21081B30694071254D8B3B9537320C014B8CB1052E5514F5EFC19CF2EB806308D5CF1A95700AD0100000000008083B9C79160B5123CADE575E370480B57B0C816EB35DD7B27372EDA858622EB1E80800000001961B4C001618F8180000000001100102E648BA30998A28C02C2DFD9DDCD0E0BA064DA199C55186485AFAB296B94E704426FFE00000000000B000A67D9B9FAADB91650E0146B1F742E5C16006708890200239822011026A6925C659D006FEB42D639F1E42DD13224EE49AA34E71B612CF96DB66A8CD4011032C22F653C54CC5E41098227427650644266D80DED45B7387AE0FFC10E529C4680A418228110807CB47D9C1A14CB832FB361C398EA672C9542F34A90BAD4288FA6AC5FC9E9845C01101CF71CAE9252D389135D8C606225DCF1E0333CCDF1FAE84B74FC5D3D440C25F880A3A9108146779F781067ED04B4957E14F1C5623AB653039B2B1D49910240848E4E682DB21081B30694071254D8B3B9537320C014B8CB1052E5514F5EFC19CF2EB806308D5CF1A9573D7C531000000000000000000F3180000000007F00000001EDE5F3C380000000061A0A48D64CA627B243AD5915A2E5D0BAD026762028DDF3304992B83A26D6C11735FC5F01ED56D769BDE7F6A068AF1A4BCFDF950321F3A4744B01B1DDC7498677F112AE1A80000000000000000000000000000000000000658000000000000819800040D37301C10C9419287E9A3B704EB6D7F45CC145DD77DCE8A63B0A47C8AB67467D800901DCE3C8B05A891E56F2BAF1B82405ABD8640B759AEEBD939B976D42C311758F40400000000AFFFFFFC00000000008800814EFDC0A7B2815346B7C1DB024F1E94A451040042041BA83132997EDA1B8B4E10011D48840A33BCFBC0833F6825A4ABF0A78E2B11D5B2981CD958EA4C881204247273416D90840D9834A03892A6C59DCA9B990600A5C65882972A8A7AF7E0CE7975C031846AE78D4AB8002000EC0003FFFFFFFF86801076D98A575A4CDFD0E3F44D1BB3CD3BBAF3BD04C38FED439ED90D88DF932A9296801A80007FFFFFFFF4008136A9D5896669E8724C5120FB6B36C241EF3CEF68AE0316161F04A9EE3EAFF36000FC0003FFFFFFFF86780106E4B5CC4155733A2427082907338051A5DA1E7CA6432840A5528ECAFFA3FB628801B80007FFFFFFFF10020CA4E125E9126107745D4354D4187ABCDE323117857A1DCEB7CCF60B2AAFA80C6003A0000FFFFFFFFE1C0080981575FD981A73A848CC0243CB467BF451F6811DAF4D71CAD8CE8B1E96DB190C01000003FFFFFFFF867400814C747E0FD8290BE8A3B8B3F73015A261479A71780CD3A0A9270234E4B394409C00D80003FFFFFFFF90020E1B9C9B10A97F15F5E1BB27FC8AC670DF8DADEAE4EDFAFB23BDD0AC705FDF51600340000FFFFFFFFF0020AD2581F3494A17B0BE3F63516D53F028A204FD3156D8B21AA4E57A8738D2062080007FFFFFFFF0CE83B9C79160B5123CADE575E370480B57B0C816EB35DD7B27372EDA858622EB1E0B8C1E00000B8000FA46CC2C7E9AB4A37C64216CD65C944E6D73998419D1A1AD2827AB6BC85B32280230764E374064EC82A3751E789607E23BEAE93FB0EDDD5E7FA803767079662E80EAEF384E2AFCB68049D9DC246119E77BD2ED4112330760CAB6CD3671CFCE006C584B9C95E0B554261E00154D40806EA694F44751B328A9291BAD124EFD5664280936EC92D27B242737E7E3E83B4704BA367B7DA5108F2F6EDFB1C38EE721A369E77EED71B12090BAEAAAC322C1457E31AB0C4DE5D9351943F10FD747742616A1AABD09F680B37D4105A8872695EE9B97FAB8985FAA9D747D45046229BF265CEEB300A40FE23040C5F335E0515496C58EE47418B72331FCC6F47A31A9B33B8E000008692FFAFF04D2AE211E9461FB39D875D74F32E4109D21D5A03D46612000000002E307800002E0002069FCA5D3141D3A78436ECFC366E31024CBB18EAF1843EB5FADAC871B42069166C0726710955E3AD621072FCBDFCB90D79E5B1951A5EE01DB533B72429F84E2562680519DE7DE0419FB412D255F853C71588EAD94C0E6CAC7526440902123939A0B6C806CC1A501C495362CEE54DCC830052E32C414B95453D7BF0673CBAE018C23573C69C694A8F88483050257A7366B838489731E5776B6FA0F02573401176D3E7FAEEF11E95A671420586631255F51A0EC2CF4D4D9F69D587712070FE1FB9316B71868692FFAFF04D2AE211E9461FB39D875D74F32E4109D21D5A03D46612000000002E307800002E0002BA11BBBA0202012000000000000007D0000007D0000000C800000007CFFFF83000", - hex"00000303933884AAF1D6B108397E5EFE5C86BCF2D8CA8D2F700EDA99DB9214FC2712B1340004D443ECE9D9C43A11A19B554BAAA6AD150000000000000222000000003B9ACA0000000000000249F000000000000000010090001E800BD48A22F4C80A42CC8BB29A764DBAEFC95674931FBE9A4380000000C50134D4A745996002F219B5FDBA1E045374DF589ECA06ABE23CECAE47343E65EDCF800000000000011E80000001BA90824000000000000124F800000000000001F4038500F1810AE1AF8A1D6F56F80855E26705F191BB07CD4E2434BC5BB1698E7E5880E2266201E8BFEEEEED725775B8116F6F82CF8E87835A5B45B184E56F272AD70D6078118601E06212B8C8F2E25B73EE7974FDCDF007E389B437BBFE238CCC3F3BF7121B6C5E81AA8589D21E9584B24A11F3ABBA5DAD48D121DD63C57A69CD767119C05DA159CB81A649D8CC0E136EB8DFBD2268B69DCA86F8CE4A604235A03D9D37AE7B07FC563F80000000C080800000000000271C000000000177000000002808B14600000001970039BA00123767F0F4F00D5E9FDF24177EF2872343D9F8FAEC65D3048BA575E70E00A0AB08800000000015E070F20000000000110010584241B5FB364208F6E64A80D1166DAD866186B10C015ED0283FF1C308C2105A0023A910810AE1AF8A1D6F56F80855E26705F191BB07CD4E2434BC5BB1698E7E5880E226621081DE8ADFA110DC8A94D8B9E9EF616BAE8598287C8F82AFDF0FC068697D570266FDA95700AD81000000000080B767F0F4F00D5E9FDF24177EF2872343D9F8FAEC65D3048BA575E70E00A0AB0880000000003E7AEDC0011ABE8A00000000001100101A9CE4B6AEF469590BC7BCC51DCEEAE9C86084055A63CC01E443C733FBE400B9B5B16800000000000B000A5E5700106D1A7097E4DE87EBAF1F8F2773842FA482002418228110805E84989A81F51ABD9D11889AE43E68FAD93659DEC019F1B8C0ADBF15A57B118B81101DCC1256F9306439AD3962C043FC47A5179CAAA001CCB23342BE0E8D92E4022780A4182281108074F306DA3751B84EC5FFB155BDCA7B8E02208BBDBC8D4F3327ABA557BF27CD1701102EF4AC8CC92F469DA9642D4D4162BC545F8B34ADE15B7D6F99808AA22B086B0180A3A910810AE1AF8A1D6F56F80855E26705F191BB07CD4E2434BC5BB1698E7E5880E226621081DE8ADFA110DC8A94D8B9E9EF616BAE8598287C8F82AFDF0FC068697D570266FDA9576F8099900000000000000000271C00000000017700000001970039BA000000002808B14648CE00AE97051EE10A3C361263F81A98165CE4AA7BA076933D4266E533585F24815C15DEACF0691332B38ECF23EC39982C5C978C748374A01BA9B30D501EE4F26E8000000000000000000000000000000000001224000000000000004B800040A911C460F1467952E3B99BED072F81BFB4454FF389636DCB399FE6A78113C28580091BB3F87A7806AF4FEF920BBF794391A1ECFC7D7632E98245D2BAF3870050558440000000000AF0387900000000000880082C2120DAFD9B21047B732540688B36D6C330C3588600AF68141FF8E18461082D0011D488408570D7C50EB7AB7C042AF13382F8C8DD83E6A7121A5E2DD8B4C73F2C407113310840EF456FD0886E454A6C5CF4F7B0B5D742CC143E47C157EF87E03434BEAB81337ED4AB8001C00F40003FFFFFFFEC7200403248A1D44DFA3AC9EC237D452C936400CAA86E9517CCCF2A8F77B7493CD70B6A00780001FFFFFFFF63A0041826829646B907A97FBD1455EA8673A12B8E7AA6EA790F7802E955CE3B69DE57E006E0001FFFFFFFF640081E51EB1F91218821E680B50E4B22DF8B094385BD33ACAE36BFC9E8C2F5AD2DA5400EC0003FFFFFFFEC7801047C26AD5435658D063EBCF73A5D0EEFE73ED6B73426246E8DFB3A21D1C4C7465001900007FFFFFFFE0040B115AC58BAAA900195893EA3B2AB408D2AD348AD047E3B6CB15E599625E38608006A0001FFFFFFFF7002033C39A21A38BB61F6FB33623771A9356D8885B7C12C939C770C939EF826286C200360000FFFFFFFFB4008104EF4271064A0973B053727C3E67352D00E25CAEED944F50782449CEAE8F50960001FFFFFFFF6390DD9FC3D3C0357A7F7C905DFBCA1C8D0F67E3EBB1974C122E95D79C380282AC222B21FA0007920001295AA1FB77029F7620A90EF7AE6A6CD31E4588B93264A7ADB76152D535C52E90B9E1B7C2376DABA316A6290F1A9730D4E5E44D0B1CB0EE6A795702E6A6BCDFCDA1A4BFEBFC134AB8847A5187ECE761D75D3CCB904274875680F51984800000000AC87E8001E480002E884D2A8080804800000000000001F4000001F40000003200000001BF08EB000" - ) - - oldbins.foreach { oldbin => - // check that this data has been encoded with the old 0x03 codec - assert(oldbin.startsWith(hex"000003")) - // we decode with compat codec - val oldnormal = channelDataCodec.decode(oldbin.bits).require.value - // and we encode with new codec - val newbin = channelDataCodec.encode(oldnormal).require.bytes - // make sure that encoding used the new codec - assert(newbin.startsWith(hex"030009")) - // make sure that round-trip yields the same data - val newnormal = channelDataCodec.decode(newbin.bits).require.value - assert(newnormal == oldnormal) - } - } - test("backward compatibility older codecs (integrity)") { // It's not enough to just verify that codecs migrate without errors, we also need to make sure that the decoded // data is correct. To do that, we compare json-serialized representations of the data. @@ -214,9 +182,9 @@ class ChannelCodecsSpec extends AnyFunSuite { val negotiating = channelDataCodec.decode(dataNegotiating.bits).require.value.asInstanceOf[DATA_NEGOTIATING] assert(negotiating.bestUnpublishedClosingTx_opt.nonEmpty) - negotiating.bestUnpublishedClosingTx_opt.foreach(tx => assert(tx.toLocalOutput == None)) + negotiating.bestUnpublishedClosingTx_opt.foreach(tx => assert(tx.toLocalOutput.isEmpty)) assert(negotiating.closingTxProposed.flatten.nonEmpty) - negotiating.closingTxProposed.flatten.foreach(tx => assert(tx.unsignedTx.toLocalOutput == None)) + negotiating.closingTxProposed.flatten.foreach(tx => assert(tx.unsignedTx.toLocalOutput.isEmpty)) val normal = channelDataCodec.decode(dataNormal.bits).require.value.asInstanceOf[DATA_NORMAL] assert(normal.commitments.localCommit.htlcTxsAndRemoteSigs.nonEmpty) @@ -249,7 +217,6 @@ class ChannelCodecsSpec extends AnyFunSuite { hex"00000103933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b134000400633f73979e2e7cc9a76f2dbcccf4d9000000000000022200000002540be40000000000000003e800000000000000010090001e000bd48a6ca0842ad7ec52d29f4be4c7015ea447e435e5404380000000c5019a59a1964d2e91fa2ae22c629bd57e7458b98373a3503a6b3675778cc09c3298000000000000011e8000000002f34f6000000000000001f400000000000001f4004800f18145c7b7b7586534692bb0520ce003ebf3a611147729a240217370358f5712a72f01df72613647456031c11cf28c90e06230848223b1064616b89bb2e8d2557c631f011d20840603112fa5faaa4053f09f9f50bcdf94f392941569ce09310340dc50020117810d980eb608cae903683abfc8438fef5421397e688d8c4aa3a47e0fc3418601b035c2fabbe71271d3a07be023eae63e4ac5272bd09a6b990a5409160f3ab28a000000008400800000000000000000000000186a00000000000000000000000002faf08000124e7f7ef1634dc24da6981ce7b05c6afdaae490a8229d2d947a6e7e5861d76463800000000015d04300800000000011001070d0d1c4bd6ad8c9b7bcb36f6308b955dc28e4970b43991232fe217bf4a56deb0023a9108106cda8faa263d0e030d33025d6ebce83453f083c77035009f0bd1ce09e598179108145c7b7b7586534692bb0520ce003ebf3a611147729a240217370358f5712a72f2957009801000000000080ce7f7ef1634dc24da6981ce7b05c6afdaae490a8229d2d947a6e7e5861d7646380000000004e14ca4000a3318080000000000b000a0b0517d7dabd6f2da1a5bfef97681bd4958a94018200241822811080579dbcb14b3d3f2c7200693391d43977881357806ebd47b2633d0c1b5ac1a56481103c3a8f6a5e15cab0144f99ec5f875fabbdf32ef6748326f4a5b3a1b29b8c7f9e80a418228110806b4c99af1b3c8f74bef604336f1a1200a754f1680923a511f7af92391bac39ab811019085fba36cec85b5537807d07f2931858413a56c47b2cf7753fbcf4cc7206bc80a3a9108106cda8faa263d0e030d33025d6ebce83453f083c77035009f0bd1ce09e598179108145c7b7b7586534692bb0520ce003ebf3a611147729a240217370358f5712a72f2957435cc1100000000000000000000000000000186a0000000002faf08000000000000000005fbb84fad0781822e1268df19eb42468a5041d0b44dc8c7a21aa7aae19409bab81e514d9069fc6064c17f35d1d0f227fbe1b74ca89fec6c67b9668fe96341d43e200000000000000000000000000000000000000000000000000000000000040ee389313797b7f5177522d46afba5c43e37edebee6598c72e5a94c81fcc50a840009273fbf78b1a6e126d34c0e73d82e357ed5724854114e96ca3d373f2c30ebb231c0000000000ae821804000000000088008386868e25eb56c64dbde59b7b1845caaee14724b85a1cc89197f10bdfa52b6f58011d488408366d47d5131e87018699812eb75e741a29f841e3b81a804f85e8e704f2cc0bc8840a2e3dbdbac329a3495d829067001f5f9d3088a3b94d12010b9b81ac7ab89539794ab8000139fdfbc58d3709369a60739ec171abf6ab9242a08a74b651e9b9f961875d918ece7f7ef1634dc24da6981ce7b05c6afdaae490a8229d2d947a6e7e5861d76463cc80398ba555ae3310a427fe5a79299e970d0b199fdade631246d123ea6324e58f40736fc2307f587e516761e35b11c3d960991f1d905d523ff0cbdd88d527790", hex"000002010000000103933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b1340009c5b105f42a2aea346cf8759f7c78135158b93878faac0659b26f723cc549fded80000000000000000000022200000004a817c8000000000000028f5c000000000000000102d0001e000b000a490f9af5166835d37b0f2155fa3a7a2b4fae66fd8000000105450189fed9a69c67c55d9743b3dde05b031088442ad94e9e3437e15f479f5cd66a7e000000000000011e80000001eefffe5c00000000000147ae00000000000001f403f000f18162eadafb948e88e02967005ef0da28be984f95b9fcce3cc7e1f1994d90af198c8179a7ba86216d011a9c1c49684578a6e0ca111279e6641404dee194eabcbbd21c81aaa1f3d35df2711a08374da8278fe259f6085189cc539a144c1aa0744b23495601331777d0354951fa354a8a2ee70e2bf40c9bb3f4fe340c9dab02bba5b6bb1aa00158bc5a03c0c79b947e5ac70f76f600c7fab8561e244f1425935fc51ffbe330380000000181515080800000000000000000000000007e800000000000000000000001f3fffe0c00121c2ba37173c23b01a1781e65cdf373e97395526f9e855164dac95c37502e9af3800000000015ffffff80000000001100101621d570811311537fddef81ee6f7023e2d17c11d32a53f472887dd035a78a518023a9108162eadafb948e88e02967005ef0da28be984f95b9fcce3cc7e1f1994d90af198c908181c230fcd426eee78d0fc56fa125155292858d5856a9a82ebfd6729d0db8664ea9570097010000000000809c2ba37173c23b01a1781e65cdf373e97395526f9e855164dac95c37502e9af380000000005657b04000a47fff80000000000b000a66e6e46b946f3259039de2840809a4b340aa510e8200239822011030949de6ac0462e1b5a42ded6c7763779d9f6aab069cb3366a3ca330bd1fe52b81101564903650deeb2a1f719865742e99ea0986774184727607a69bfff9f552a45a00a398220110228710b1083bde27eeb793ad155142cb69655ad8424eeae2044808edc631840d8110225098637d7f91c20e00d4a82a56520ad3cead728dd5b82b0bdcfe40d4b6acfb00a3a9108162eadafb948e88e02967005ef0da28be984f95b9fcce3cc7e1f1994d90af198c908181c230fcd426eee78d0fc56fa125155292858d5856a9a82ebfd6729d0db8664ea95773ad9b100000000000000000000000000000007e80000001f3fffe0c000000000000000063aa3a9e7e5e45c265ae8bf12e96a239ddbae4224c6f4560e149cce8a2675b208178d03c15e7303b3415e3549606ecab96ff5250ce38b2052b0a9dd2ed0af5ee75800000000000000000000000000000000000000000000000000000000000409cb1d9e3463cd48a65e9a2fa95f0755e924dba3664026d3682892bbf9527396bc0090e15d1b8b9e11d80d0bc0f32e6f9b9f4b9caa937cf42a8b26d64ae1ba8174d79c0000000000affffffc0000000000880080b10eab8408988a9bfeef7c0f737b811f168be08e99529fa39443ee81ad3c528c011d48840b1756d7dca47447014b3802f786d145f4c27cadcfe671e63f0f8cca6c8578cc64840c0e1187e6a137773c687e2b7d0928aa94942c6ac2b54d4175feb394e86dc332754ab8000070ae8dc5cf08ec0685e0799737cdcfa5ce5549be7a1545936b2570dd40ba6bce32de50000008000070ae8dc5cf08ec0685e0799737cdcfa5ce5549be7a1545936b2570dd40ba6bce0656b85b7c0e2dd09a620e28ddf865c6197074354c922bb9b76fb27ffe7fbf19cc0", hex"00000203933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b13400041507665ff588dba1b9d4949e739ad05b0000000000000222000000003b9aca00000000000002718000000000000000010090001e000bd48a65a1ef54e50a013d73e46815abed7672835c83f34380000000c101d9ed460a901187b39b2c00d859f885104116abf778bf31c369342e4147db56e4800000000000011e80000001d87278e000000000000138c000000000000001f403c200f1811e1179456c73d778f073e17544813b4499e5053f9c8ac8f9fb1f0eb37f45ca4701e06722e502349a2857e99bf9299fe781a343aebce7557c0c3738e20f14b9e96f81985a99219fd30dcdf40438cc4d01d6027087f65d6dea4e0cd9864016febcbcbf8176c51f0ee44572f83a8a02f50053ed3cfcedbf5ce6c70f70970f5784c0bb0dfe0195a18fc266e7e7f1dabcc5234f493620de9f8e8d5932de3e8a44626974e0dd1080000000c100000000000000000000000005b8d8000000000000000000000001dd3826e0001208e981c8344120b2dce21aadce8a1ad9eb0123a574d898931d3d1449a33966f10080000000158c2b7a00000000001100101395ec5380c9641495ac8c4bb01605cf6ac58ee89a33da93d5eb09ab4be0e0ab8023a910811e1179456c73d778f073e17544813b4499e5053f9c8ac8f9fb1f0eb37f45ca471081ab89c14b9f60439db334231eff8706a1374282cf18444622420b7556a424830da95700980100000000008088e981c8344120b2dce21aadce8a1ad9eb0123a574d898931d3d1449a33966f100800000003eb8c1c0008006f600000000000b000a0eb37e79f65de8a4ca4df80979505338bcea6e76020024182281108070acce2513bf0df155ec51404333ac0397b6015bff2d87120d51d8dbf0b0f698811013a616842936434a093f8e9f85cbccb846ec2e0c49c9b541d46563cfb181886b00a418228110807ab650f61af7ce294df5a26e8cc1abb3658f0201c0cb9a2484e43fb9e61622d081102767e9f7608de2330774556121ddbd48c7a45d9598e38c821a3d9846c37e75f580a3a910811e1179456c73d778f073e17544813b4499e5053f9c8ac8f9fb1f0eb37f45ca471081ab89c14b9f60439db334231eff8706a1374282cf18444622420b7556a424830da957360f66900000000000000000000000000005b8d800000001dd3826e000000000000000005ef1d8da098d1f363033e64f2272a12b638bd3fe501c829d8350e8f3fc1c6b1481f12f3b5425786f43870f00174ddd36710d1864bbcf24aa54694cb183b43ebcbf000000000000000000000000000000000000000000000000000000000000408200a48c21439ebc4d7c70b310000bef69e454f808dee73be19aa960d54cac8100090474c0e41a2090596e710d56e7450d6cf58091d2ba6c4c498e9e8a24d19cb37880400000000ac615bd000000000008800809caf629c064b20a4ad64625d80b02e7b562c7744d19ed49eaf584d5a5f07055c011d488408f08bca2b639ebbc7839f0baa2409da24cf2829fce45647cfd8f8759bfa2e5238840d5c4e0a5cfb021ced99a118f7fc383509ba141678c2223112105baab52124186d4ab8000023a60720d10482cb73886ab73a286b67ac048e95d362624c74f451268ce59bc62b21ec0008d8000223a60720d10482cb73886ab73a286b67ac048e95d362624c74f451268ce59bc6049c6fe56892029844393a0afbf8c2c19088f565e80764b75a7e0591b97c1108ac0", - hex"00000303933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b134000401196cdf9d64977c6e5c6d76f94073d40000000000000222000000003b9aca0000000000000186a000000000000000010090001e000bd48a41a38f359dc4f53b01b2451e6b4553591b51b7a6c380000000c50181b4017fa7b5999a1e4efd65e3c1001ed52dc73112da9079642887cec1130fb90000000000000111000000001dcd6500000000000000c35000000000000000008048000f01b0c14e11c47802a9a45870d78dbfc1e755dc4d0fd066e00e7bcd7cbff0501572013cd68aefa84235f941b1cc4422ba91584a3f7c9fe6a6d9492a52b362e99c0fa3819f878819a1371845cf9c5c1c54a8e5a1c29b6f5c0eb49fffb54b7aee3cbfd555017e63ed9055db05f97f98616698949cc24314aafbca12b30d3673838524e0c2d5810fdd210f2c5ab5b8eb10bd7941e7cec8e10fe9b52f4da97b3f756db0553533db80000000c40000000000000000010000000550ab8000000000000000000000012a05f20000126ad11d64ce0a200074b523772cb2b83d47d5eda3fe926c19dcbcb25d7f711839000000000015c04b4c000000000011001013fd448842ba6c09e16a187427a270fd178c1fcea81af2dfaea0d0f9067727828023a91081b0c14e11c47802a9a45870d78dbfc1e755dc4d0fd066e00e7bcd7cbff05015721081b2bc10ec17c41c96579defc91739a70d6e160f4c8de10b71e60009ba07b250672957009781000000000080ead11d64ce0a200074b523772cb2b83d47d5eda3fe926c19dcbcb25d7f711839000000000024633e4000a0724800000000000b000a7ec7110a639e4df6602c902e085a50108d476d84820023982201103d382d7ac3c00b9e107740b34282f2665ea6813fda6ba643e1022078b9077658011037c4bdf8a6b68e3687cdeb650f2ff34ca1565c72452f74b202c9ae21f33f73dc00a4182281108064d376fce0d1822264ba099d0e1f23ae65a81ee3144564a18b6a9ea9c3cfe90e0110102f070312d5df2d4a056dd6ec167eecd49ee73d1b527acad4047c28855119d800a3a91081b0c14e11c47802a9a45870d78dbfc1e755dc4d0fd066e00e7bcd7cbff05015721081b2bc10ec17c41c96579defc91739a70d6e160f4c8de10b71e60009ba07b25067295760bdec10000000000000000000010000000550ab800000012a05f20000000000000000007a3685b682b628b7367b527481625a0b9b077f6bd79f2da1fb8325178997687801e292ae688fa3c2feca83618455ecdc13160c036ed8fa20df163afd443605630b0000000000000000000000000000000000000000000000000000000000004084a794a9140b8bff733dd25153972802ac0aa1a1f9970c5513a13f2c4b84fd57400935688eb2670510003a5a91bb96595c1ea3eaf6d1ff49360cee5e592ebfb88c1c80000000000ae025a6000000000008800809fea244215d3604f0b50c3a13d1387e8bc60fe7540d796fd750687c833b93c14011d48840d860a708e23c0154d22c386bc6dfe0f3aaee2687e83370073de6be5ff8280ab90840d95e08760be20e4b2bcef7e48b9cd386b70b07a646f085b8f30004dd03d9283394ab8000400fc0003fffffffffff8010698cb8b8fdeef64d356179b432c40a5a3abba23d07fca70137f2e10c9972c92cc0003fffffffffffb5688eb2670510003a5a91bb96595c1ea3eaf6d1ff49360cee5e592ebfb88c1c84efa9800019800023ae764af5c1229787c57b6ccafa9e8ca2e8bcb1a4eac39b202194a89987c86f606bd1004e9f5eac0c223530a1b9fee435a1c5bc2d8f849940273e0a6dd4a8cd343497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea33090000000013bea600006600005b307a3d000100900000000000000001000003e8000000640", hex"000005010000000003933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b13400045d37887e8d909085b00af1d8b09c514b000000000000022200000004a817c8000000000000000b18000000000000000102d0001e000bd48a3d3ca19286d69a373eb6dda039da0cccf0bb6330c380000000c501df7e8244b99cc46d750bcef5830755886bad20416438b5973b3c8174f9d85b93000000000000011e80000000086131ec000000000000058c00000000000001f4004800f1817ce5a3cfc79898018c8d4489a393b1a46f6890a79a56737f867bfa56c511aa388107f85642fedecfcb7ef3cea6b38e26aead2759263d123692fd0c7d458fbc188881580b52cf6cdb449abc060bce9db3f35eebec2c87b316fd7a2c3193296d4a6e2e81fd2e032dd33ba9fa974246fccfc3ab5ccc1eb9ede9c900fc4e5d455c42150b3a8113237ef34839fc8055cbb63c0b9892defe6554c9dc70813681c2f3f35dedc13300000000c08080000000000000000000000010de8000000000000000000000000876dccc00125f6d0ab2880e92007f31c38dc0a8460c95a4c15291a24c92af1bca9264d8a915808000000015b7aa82000000000011001004a9c0705b42fa733f200639ed816601f0f413b9f29616f9c23f02f0dfc1df7e0023a910810115b6a5e11518dc1598fbc803018f3d87edac78de6e5544ff868bacb803485c10817ce5a3cfc79898018c8d4489a393b1a46f6890a79a56737f867bfa56c511aa38a957009781000000000080df6d0ab2880e92007f31c38dc0a8460c95a4c15291a24c92af1bca9264d8a91580800000003df2714000811e8200000000000b000a11e33689533b2499781f9b2f9c1893add8c57c4e02002418228110806567eeed21d6e87a045f3debf5ca10a05339c25f021400d96f7b492a957a94d301101df62a1c365062cce3b9a3ef530a4cd37eae4346491ed37e050dfd54213ef54f00a3982201103484562bcbb22ae3d892808bb04ff25f309a4732474a57bf6dd7691e17fc3df6811006854f362bcec61184d4721dfd30f777485be3580cd116564d139668bb6ee3a280a3a910810115b6a5e11518dc1598fbc803018f3d87edac78de6e5544ff868bacb803485c10817ce5a3cfc79898018c8d4489a393b1a46f6890a79a56737f867bfa56c511aa38a9573b51e910000000000000000000000000000010de800000000876dccc000000000000000025f43cb5a86018845618974486d0cb225355649b88690a914ee6cfec1a75286481d9daff2fcb7edb8d99f8fab9f77ff1033d5fc33a42ea7f8b70d29614b6d3195d000000000000000000000000000000000000000000000000000000000000409799acc6bbef72e7bc3dbed7831ac4e4ecfdad169d19628429edaa495d3c79ef00092fb68559440749003f98e1c6e05423064ad260a948d12649578de549326c548ac0400000000adbd54100000000000880080254e0382da17d399f90031cf6c0b300f87a09dcf94b0b7ce11f81786fe0efbf0011d48840808adb52f08a8c6e0acc7de40180c79ec3f6d63c6f372aa27fc345d65c01a42e0840be72d1e7e3cc4c00c646a244d1c9d8d237b44853cd2b39bfc33dfd2b6288d51c54ab800017db42aca203a4801fcc70e3702a118325693054a4689324abc6f2a499362a4557db42aca203a4801fcc70e3702a118325693054a4689324abc6f2a499362a454002f5228f4f2864a1b5a68dcfadb7680e7683333c2ed8cc30f7db42aca203a4801fcc70e3702a118325693054a4689324abc6f2a499362a454002c0028c95c468f1c569d43c9af6d335b8820bdda4888fe000200000", hex"00000503933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b134000454699830eddf332655e0ff86e6c536f30000000000000222000000003b9aca0000000000000186a000000000000000010090001e000bd48a482e9d4f3df4621c059bdf7df20f5ec9c4df26364380000000c101bf0694efa44b50d2817c3ac271bd38ad5110d72162deb6328cc919097ed7727f8000000000000111000000001dcd6500000000000000c35000000000000000008048000f01f7f51dd8abfc590feb6569d6f656b2a6205f339ec297e7c90239bf40aa432e9d01125974b8fc60d0122160b22db09edf15265c9f8d42c407a4b6e286445ce2897e01c8dfdd96184b8a8a0faa4d5d92acea243bb257e2bf911767efbf3e0fee216d3b81f4207be48f4ebbc397fb0bf3d223696ce431919a583ad69a7f3825323977b38a811b7e73e5b685f78f56e8d250e68d99452deed356a826fb5f207d314d0b61b97780000000c50000000000000000000000000001770000000000000000000000012a05f20000121a8b778cb9a0d244c835acbedbfc43713175d98e2d1a48316af9b751f4c75686800000000015c04b4c000000000011001029e7f0fe79b14835816eb3cb89338bf442f040ee84ea6fdfb401ac2aac96919a0023a91081eabde40c6773291d64bb71962995d7fd3401470b2e53a320331df956212b0c171081f7f51dd8abfc590feb6569d6f656b2a6205f339ec297e7c90239bf40aa432e9d29570097810000000000809a8b778cb9a0d244c835acbedbfc43713175d98e2d1a48316af9b751f4c7568680000000003b78dfc000b0ca4c00000000000b000a62d6449887f18b11f7c38a7d73a58620a23bda4682002418228110805b5b0d8c18572b99a6496d58eefe9bc20bcf359d91cc480c022591ba7c86d2280110096244c4df48199bd71b654c29ac49aa2a1dfcb6f2554a89b7d38bd28de98f6200a39822011013d445ecdc92ced0ebe8920580d3e5e7a1cdfd19fd39a5e57495a80e8a7cc0630110163a3f38ba3f23acc687ac397fb98d8dedb9a07a4085dec28d5a792e9ea2aece00a3a91081eabde40c6773291d64bb71962995d7fd3401470b2e53a320331df956212b0c171081f7f51dd8abfc590feb6569d6f656b2a6205f339ec297e7c90239bf40aa432e9d29570cf71b1000000000000000000000000000000177000000012a05f200000000000000000054e507323b7ab08f00c834e2ba694233f9d50293312291fc893691e512805f45814c861ee65261b9b7bdaaaebd59d316e5bf00417c2c9afeb5db143096feff164480000000000000000000000000000000000000000000000000000000000040b625f1f1e5239ce80103faf7851d4adbfbed7cf5bace51b959d5d6050f88e65d40090d45bbc65cd06922641ad65f6dfe21b898baecc7168d2418b57cdba8fa63ab4340000000000ae025a6000000000008800814f3f87f3cd8a41ac0b759e5c499c5fa21782077427537efda00d615564b48cd0011d48840f55ef20633b9948eb25db8cb14caebfe9a00a3859729d190198efcab1095860b8840fbfa8eec55fe2c87f5b2b4eb7b2b5953102f99cf614bf3e4811cdfa05521974e94ab800006a2dde32e683491320d6b2fb6ff10dc4c5d76638b46920c5abe6dd47d31d5a1a6a2dde32e683491320d6b2fb6ff10dc4c5d76638b46920c5abe6dd47d31d5a1a002f522920ba753cf7d18870166f7df7c83d7b27137c98d90e6a2dde32e683491320d6b2fb6ff10dc4c5d76638b46920c5abe6dd47d31d5a1a002f522994760b5d8d708a41fe54bdd87888baf41f10e7bd0e000200000", hex"00000603933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b1340004004c3bfd866d20986aa90ec265fdfcbe0000000000000222000000012a05f20000000000000186a000000000000000010090001e000bd48a3b48ca7482f0442970651ba40bf45091fddef3a84380000000c501a0528fcd6c721246634deb024236eb2f4c124e13b587093a6652e7bfc12db3250000000000000111000000009502f900000000000000c350000000000000000083f0000f01210cce90e63d17d62e20b45b61b1add86631f6a5c90328c19fc1caf8085e600e0110d8d9aa7be8b4553b0af05b921c72c48356c4a0af61ffed7f1eadb7c5071c64816bf131172fe61d3145d39511755ee8095927534b76c4bb2464b706eaaee1486e01dd6caa8417804e26ce56f83a2525aa05fba25d3bab6db2cae5a203ce126da1ec01c8dfca1943df0dd0d59ffda66e207316c4af1cd3a4b6443fc5298a7a3864658f00000001404500000000000000001400000000017700000000015b95aa0000000128aa5c560012338c4e025330542050c2387b69ebb64e9c884113bb1045e996ae73e2b1b00a45800000000015c04b4c00000000001100104c97a415e599a3817965ff9a64e87d2af9bbcd77cae148ea934f9488216ffee10023a91081210cce90e63d17d62e20b45b61b1add86631f6a5c90328c19fc1caf8085e600e10814be9ac4ddf6ef583cde6ca4aea965a3f51028afcb1280f86f0ad47a533149d08a95700ad01000000000080b38c4e025330542050c2387b69ebb64e9c884113bb1045e996ae73e2b1b00a4580000000003eef56c0017b588000000000001100100783bea5072cc7b3be1475a5a8d7c45f16978bbc8b914937c89631a4e7f0d7e3b5714b80000000000b000a0c933f5731198ebb4e0fda5b7ba130c1c4d2fc870200239822011032e32cd21310cf1864c862bb32b9c78b1b8a626d1094d47d0e1938166388243f81101bbf4ba992687498419d57da8e6d700809fdf1f882425296292ff55b67ec82bd80a4182281108054815d830690f798ccd75722576882f62a088d37e81cde50c40bc5b080615c3e011030965e18229cbf3a03024a97988d75cb1b1569eaa1ce381d0689958161c4799900a3a91081210cce90e63d17d62e20b45b61b1add86631f6a5c90328c19fc1caf8085e600e10814be9ac4ddf6ef583cde6ca4aea965a3f51028afcb1280f86f0ad47a533149d08a9571b2c3e90000000000000000000140000000001770000000128aa5c5600000000015b95aa392494f078bec5b681d4997f2497d52112ad54eaaa81dec63f6480336139048b81fc2e019907aefb5efc5922187f575e10635611bde69ae0bd46b0b6ea44aea7748000000000000000000000000000000000000000000000000000000a000040f7dd9de11426234d0121ec1f5d2d19c3b4e78679b2e07a5c4f91bc624458352bc00919c6270129982a1028611c3db4f5db274e442089dd8822f4cb5739f158d80522c0000000000ae025a60000000000088008264bd20af2ccd1c0bcb2ffcd32743e957cdde6bbe570a47549a7ca4410b7ff708011d4884090866748731e8beb17105a2db0d8d6ec3318fb52e4819460cfe0e57c042f30070840a5f4d626efb77ac1e6f36525754b2d1fa881457e589407c37856a3d2998a4e8454ab8000800ec0003ffffffffff80106b5a98105912cee7f3cd2a4ed131283ee6fa4f5aef2e74aec08ebc2d6770ee38001e80007fffffffffec0082e868d9d8ebcc94bff771ca9d328bccfdfafdbc17e2b078b728caa0bb7179c6420001ffffffffffb0ce3138094cc150814308e1eda7aed93a7221044eec4117a65ab9cf8ac6c02916000200e60400000002ce3138094cc150814308e1eda7aed93a7221044eec4117a65ab9cf8ac6c029160000000001fffffffe05ed620000000000002f5228ed2329d20bc110a5c1946e902fd14247f77bcea10f17c52e00000000002f52295b2c2f65d18b177c1a91981b0335c8d209f93ea10e000000000002029e04000000000202ce3138094cc150814308e1eda7aed93a7221044eec4117a65ab9cf8ac6c029160000000001fffffffe05ed620000000000002f5228ed2329d20bc110a5c1946e902fd14247f77bcea10f17c52e00000000002f52295b2c2f65d18b177c1a91981b0335c8d209f93ea10e08008e608804401c9f21ea6ff52325709bc8fec460de1d9f584cbf6aced6abeda5842595fbb968044076a3a4b918db66a59ee47c4d3a2f8bb0f17665e6d904df5cab74ee06cbafbb3e028e60880440b6af6af4787b1229039a7351f7156d0cc30755a1902c6d483ea85915a29b40f6044023ea5be6088489f6dda062498c485465c7451d7bf49eb46dae206bf952ddc860028ea4420484333a4398f45f58b882d16d86c6b76198c7da97240ca3067f072be02179803842052fa6b1377dbbd60f379b292baa5968fd440a2bf2c4a03e1bc2b51e94cc527422a55c0000000000000", diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/ExtendedQueriesCodecsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/ExtendedQueriesCodecsSpec.scala index fa3c241427..e1db7925fb 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/ExtendedQueriesCodecsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/ExtendedQueriesCodecsSpec.scala @@ -147,7 +147,7 @@ class ExtendedQueriesCodecsSpec extends AnyFunSuite { assert(decoded == replyChannelRange) } - test("compute checksums correctly (CL test #1)") { + test("compute checksums correctly") { val update = ChannelUpdate( chainHash = ByteVector32.fromValidHex("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f"), signature = ByteVector64.fromValidHex("76df7e70c63cc2b63ef1c062b99c6d934a80ef2fd4dae9e1d86d277f47674af3255a97fa52ade7f129263f591ed784996eba6383135896cc117a438c80293282"), @@ -156,18 +156,18 @@ class ExtendedQueriesCodecsSpec extends AnyFunSuite { channelFlags = ChannelUpdate.ChannelFlags.DUMMY, cltvExpiryDelta = CltvExpiryDelta(144), htlcMinimumMsat = 0 msat, - htlcMaximumMsat = None, + htlcMaximumMsat = 250_000_000 msat, feeBaseMsat = 1000 msat, feeProportionalMillionths = 10 ) - val check = ByteVector.fromValidHex("010276df7e70c63cc2b63ef1c062b99c6d934a80ef2fd4dae9e1d86d277f47674af3255a97fa52ade7f129263f591ed784996eba6383135896cc117a438c8029328206226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100005d50f933000000900000000000000000000003e80000000a") + val check = ByteVector.fromValidHex("010276df7e70c63cc2b63ef1c062b99c6d934a80ef2fd4dae9e1d86d277f47674af3255a97fa52ade7f129263f591ed784996eba6383135896cc117a438c8029328206226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100005d50f933010000900000000000000000000003e80000000a000000000ee6b280") assert(LightningMessageCodecs.channelUpdateCodec.encode(update).require.bytes == check.drop(2)) val checksum = Sync.getChecksum(update) - assert(checksum == 0x1112fa30L) + assert(checksum == 0xeb8277a6L) } - test("compute checksums correctly (CL test #2)") { + test("compute checksums correctly (cln test)") { val update = ChannelUpdate( chainHash = ByteVector32.fromValidHex("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f"), signature = ByteVector64.fromValidHex("06737e9e18d3e4d0ab4066ccaecdcc10e648c5f1c5413f1610747e0d463fa7fa39c1b02ea2fd694275ecfefe4fe9631f24afd182ab75b805e16cd550941f858c"), @@ -176,7 +176,7 @@ class ExtendedQueriesCodecsSpec extends AnyFunSuite { channelFlags = ChannelUpdate.ChannelFlags.DUMMY, cltvExpiryDelta = CltvExpiryDelta(48), htlcMinimumMsat = 0 msat, - htlcMaximumMsat = Some(100000 msat), + htlcMaximumMsat = 100_000 msat, feeBaseMsat = 100 msat, feeProportionalMillionths = 11 ) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/FailureMessageCodecsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/FailureMessageCodecsSpec.scala index 5a2ebade3f..10a7d7eda7 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/FailureMessageCodecsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/FailureMessageCodecsSpec.scala @@ -38,7 +38,7 @@ class FailureMessageCodecsSpec extends AnyFunSuite { htlcMinimumMsat = 1000 msat, feeBaseMsat = 12 msat, feeProportionalMillionths = 76, - htlcMaximumMsat = None) + htlcMaximumMsat = 150_000_000 msat) test("encode/decode all failure messages") { val msgs: List[FailureMessage] = @@ -146,14 +146,14 @@ class FailureMessageCodecsSpec extends AnyFunSuite { } test("support encoding of channel_update with/without type in failure messages") { - val tmp_channel_failure_notype = hex"10070080cc3e80149073ed487c76e48e9622bf980f78267b8a34a3f61921f2d8fce6063b08e74f34a073a13f2097337e4915bb4c001f3b5c4d81e9524ed575e1f45782196fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000008260500041300005b91b52f0003000e00000000000003e80000000100000001" - val tmp_channel_failure_withtype = hex"100700820102cc3e80149073ed487c76e48e9622bf980f78267b8a34a3f61921f2d8fce6063b08e74f34a073a13f2097337e4915bb4c001f3b5c4d81e9524ed575e1f45782196fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000008260500041300005b91b52f0003000e00000000000003e80000000100000001" - val ref = TemporaryChannelFailure(ChannelUpdate(ByteVector64(hex"cc3e80149073ed487c76e48e9622bf980f78267b8a34a3f61921f2d8fce6063b08e74f34a073a13f2097337e4915bb4c001f3b5c4d81e9524ed575e1f4578219"), Block.LivenetGenesisBlock.hash, ShortChannelId(0x826050004130000L), 1536275759 unixsec, ChannelUpdate.ChannelFlags(isEnabled = false, isNode1 = false), CltvExpiryDelta(14), 1000 msat, 1 msat, 1, None)) - - val u = failureMessageCodec.decode(tmp_channel_failure_notype.toBitVector).require.value - assert(u == ref) - val bin = ByteVector(failureMessageCodec.encode(u).require.toByteArray) - assert(bin == tmp_channel_failure_withtype) + val tmpChannelFailureWithoutType = hex"10070088cc3e80149073ed487c76e48e9622bf980f78267b8a34a3f61921f2d8fce6063b08e74f34a073a13f2097337e4915bb4c001f3b5c4d81e9524ed575e1f45782196fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000008260500041300005b91b52f0103000e00000000000003e800000001000000010000000008f0d180" + val tmpChannelFailureWithType = hex"1007008a0102cc3e80149073ed487c76e48e9622bf980f78267b8a34a3f61921f2d8fce6063b08e74f34a073a13f2097337e4915bb4c001f3b5c4d81e9524ed575e1f45782196fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000008260500041300005b91b52f0103000e00000000000003e800000001000000010000000008f0d180" + val ref = TemporaryChannelFailure(ChannelUpdate(ByteVector64(hex"cc3e80149073ed487c76e48e9622bf980f78267b8a34a3f61921f2d8fce6063b08e74f34a073a13f2097337e4915bb4c001f3b5c4d81e9524ed575e1f4578219"), Block.LivenetGenesisBlock.hash, ShortChannelId(0x826050004130000L), 1536275759 unixsec, ChannelUpdate.ChannelFlags(isEnabled = false, isNode1 = false), CltvExpiryDelta(14), 1000 msat, 1 msat, 1, 150_000_000 msat)) + + val u1 = failureMessageCodec.decode(tmpChannelFailureWithoutType.toBitVector).require.value + assert(u1 == ref) + val bin = failureMessageCodec.encode(u1).require.bytes + assert(bin == tmpChannelFailureWithType) val u2 = failureMessageCodec.decode(bin.toBitVector).require.value assert(u2 == ref) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecsSpec.scala index 4c02a7a7b6..4b49d0bdbe 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecsSpec.scala @@ -261,8 +261,8 @@ class LightningMessageCodecsSpec extends AnyFunSuite { val defaultEncoded = hex"0040 0000000000000000000000000000000000000000000000000000000000000000 0100000000000000000000000000000000000000000000000000000000000000 00001388 00000fa0 000000000003d090 00000000000001f4 000000000000c350 000000000000000f 0090 01e3 0009eb10 031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f 024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766 02531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe337 03462779ad4aad39514614751a71085f2f10e1c7a593e4e030efb5b8721ce55b0b 0362c0a046dacce86ddd0343c6d3c7c79c2208ba0d9c9cf24a6d046d21d21f90f7 03f006a18d5653c4edf5391ff23a61f03ff83d237e880ee61187fa9f379a028e0a 01" val testCases = Seq( defaultOpen -> defaultEncoded, - defaultOpen.copy(tlvStream = TlvStream(ChannelTypeTlv(ChannelTypes.AnchorOutputsZeroFeeHtlcTx(false, false)))) -> (defaultEncoded ++ hex"0103401000"), - defaultOpen.copy(tlvStream = TlvStream(UpfrontShutdownScriptTlv(hex"00143adb2d0445c4d491cc7568b10323bd6615a91283"), ChannelTypeTlv(ChannelTypes.AnchorOutputsZeroFeeHtlcTx(false, false)))) -> (defaultEncoded ++ hex"001600143adb2d0445c4d491cc7568b10323bd6615a91283 0103401000"), + defaultOpen.copy(tlvStream = TlvStream(ChannelTypeTlv(ChannelTypes.AnchorOutputsZeroFeeHtlcTx(scidAlias = false, zeroConf = false)))) -> (defaultEncoded ++ hex"0103401000"), + defaultOpen.copy(tlvStream = TlvStream(UpfrontShutdownScriptTlv(hex"00143adb2d0445c4d491cc7568b10323bd6615a91283"), ChannelTypeTlv(ChannelTypes.AnchorOutputsZeroFeeHtlcTx(scidAlias = false, zeroConf = false)))) -> (defaultEncoded ++ hex"001600143adb2d0445c4d491cc7568b10323bd6615a91283 0103401000"), ) testCases.foreach { case (open, bin) => val decoded = lightningMessageCodec.decode(bin.bits).require.value @@ -366,7 +366,7 @@ class LightningMessageCodecsSpec extends AnyFunSuite { RevokeAndAck(randomBytes32(), scalar(0), point(1)), ChannelAnnouncement(randomBytes64(), randomBytes64(), randomBytes64(), randomBytes64(), Features(bin(7, 9)), Block.RegtestGenesisBlock.hash, RealShortChannelId(1), randomKey().publicKey, randomKey().publicKey, randomKey().publicKey, randomKey().publicKey), NodeAnnouncement(randomBytes64(), Features(DataLossProtect -> Optional), 1 unixsec, randomKey().publicKey, Color(100.toByte, 200.toByte, 300.toByte), "node-alias", IPv4(InetAddress.getByAddress(Array[Byte](192.toByte, 168.toByte, 1.toByte, 42.toByte)).asInstanceOf[Inet4Address], 42000) :: Nil), - ChannelUpdate(randomBytes64(), Block.RegtestGenesisBlock.hash, ShortChannelId(1), 2 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(3), 4 msat, 5 msat, 6, None), + ChannelUpdate(randomBytes64(), Block.RegtestGenesisBlock.hash, ShortChannelId(1), 2 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(3), 4 msat, 5 msat, 6, 25_000_000 msat), AnnouncementSignatures(randomBytes32(), RealShortChannelId(42), randomBytes64(), randomBytes64()), GossipTimestampFilter(Block.RegtestGenesisBlock.blockId, 100000 unixsec, 1500), QueryShortChannelIds(Block.RegtestGenesisBlock.blockId, EncodedShortChannelIds(EncodingType.UNCOMPRESSED, List(RealShortChannelId(142), RealShortChannelId(15465), RealShortChannelId(4564676))), TlvStream.empty), @@ -522,23 +522,25 @@ class LightningMessageCodecsSpec extends AnyFunSuite { // this was generated by c-lightning val bin = hex"010258fff7d0e987e2cdd560e3bb5a046b4efe7b26c969c2f51da1dceec7bcb8ae1b634790503d5290c1a6c51d681cf8f4211d27ed33a257dcc1102862571bf1792306226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f0005a100000200005bc75919010100060000000000000001000000010000000a000000003a699d00" val update = lightningMessageCodec.decode(bin.bits).require.value.asInstanceOf[ChannelUpdate] - assert(update == ChannelUpdate(ByteVector64(hex"58fff7d0e987e2cdd560e3bb5a046b4efe7b26c969c2f51da1dceec7bcb8ae1b634790503d5290c1a6c51d681cf8f4211d27ed33a257dcc1102862571bf17923"), ByteVector32(hex"06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f"), ShortChannelId(0x5a10000020000L), 1539791129 unixsec, ChannelUpdate.ChannelFlags(isEnabled = true, isNode1 = false), CltvExpiryDelta(6), 1 msat, 1 msat, 10, Some(980000000 msat))) + assert(update == ChannelUpdate(ByteVector64(hex"58fff7d0e987e2cdd560e3bb5a046b4efe7b26c969c2f51da1dceec7bcb8ae1b634790503d5290c1a6c51d681cf8f4211d27ed33a257dcc1102862571bf17923"), ByteVector32(hex"06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f"), ShortChannelId(0x5a10000020000L), 1539791129 unixsec, ChannelUpdate.ChannelFlags(isEnabled = true, isNode1 = false), CltvExpiryDelta(6), 1 msat, 1 msat, 10, 980_000_000 msat)) val nodeId = PublicKey(hex"03370c9bac836e557eb4f017fe8f9cc047f44db39c1c4e410ff0f7be142b817ae4") assert(Announcements.checkSig(update, nodeId)) val bin2 = ByteVector(lightningMessageCodec.encode(update).require.toByteArray) assert(bin == bin2) } + test("reject channel_update without htlc_maximum_msat") { + // {"signature":"12540b6a236e21932622d61432f52913d9442cc09a1057c386119a286153f8681c66d2a0f17d32505ba71bb37c8edcfa9c11e151b2b38dae98b825eff1c040b3","chainHash":"6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000","shortChannelId":"558351x1422x1","timestamp":{"iso":"2020-03-12T17:58:06Z","unix":1584035886},"channelFlags":{"isEnabled":true,"isNode1":true},"cltvExpiryDelta":144,"htlcMinimumMsat":1000,"feeBaseMsat":1000,"feeProportionalMillionths":2} + val bin = hex"12540b6a236e21932622d61432f52913d9442cc09a1057c386119a286153f8681c66d2a0f17d32505ba71bb37c8edcfa9c11e151b2b38dae98b825eff1c040b36fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000008850f00058e00015e6a782e0000009000000000000003e8000003e800000002" + assert(channelUpdateCodec.decode(bin.bits).isFailure) + } + test("non-regression on channel_update") { val bins = Map( hex"3b6bb4872825450ff29d0b46f5835751329b0394a10ac792e4ba2a23b4f17bcc4e5834d1424787830be0ee3d22ac99e674d121f25d19ed931aaabb8ed0eec0fb6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000086a4e000a9700016137e9e9010200900000000000000001000003e800000001000000001dcd6500" -> """{"signature":"3b6bb4872825450ff29d0b46f5835751329b0394a10ac792e4ba2a23b4f17bcc4e5834d1424787830be0ee3d22ac99e674d121f25d19ed931aaabb8ed0eec0fb","chainHash":"6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000","shortChannelId":"551502x2711x1","timestamp":{"iso":"2021-09-07T22:38:33Z","unix":1631054313},"channelFlags":{"isEnabled":false,"isNode1":true},"cltvExpiryDelta":144,"htlcMinimumMsat":1,"feeBaseMsat":1000,"feeProportionalMillionths":1,"htlcMaximumMsat":500000000,"tlvStream":{"records":[],"unknown":[]}}""", - hex"12540b6a236e21932622d61432f52913d9442cc09a1057c386119a286153f8681c66d2a0f17d32505ba71bb37c8edcfa9c11e151b2b38dae98b825eff1c040b36fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000008850f00058e00015e6a782e0000009000000000000003e8000003e800000002" -> - """{"signature":"12540b6a236e21932622d61432f52913d9442cc09a1057c386119a286153f8681c66d2a0f17d32505ba71bb37c8edcfa9c11e151b2b38dae98b825eff1c040b3","chainHash":"6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000","shortChannelId":"558351x1422x1","timestamp":{"iso":"2020-03-12T17:58:06Z","unix":1584035886},"channelFlags":{"isEnabled":true,"isNode1":true},"cltvExpiryDelta":144,"htlcMinimumMsat":1000,"feeBaseMsat":1000,"feeProportionalMillionths":2,"tlvStream":{"records":[],"unknown":[]}}""", hex"8efb98c939aba422a1f2ccd3e05e5471be41c54ac5d7cb27b9aaaecea45f3abb363907644c44b385d83ef6b577061847396d6d3464e4f1fa9e779395e36703ef6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d61900000000000a79dd00098800006137f9ba0100002800000000000003e800000000000003e800000000938580c0" -> """{"signature":"8efb98c939aba422a1f2ccd3e05e5471be41c54ac5d7cb27b9aaaecea45f3abb363907644c44b385d83ef6b577061847396d6d3464e4f1fa9e779395e36703ef","chainHash":"6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000","shortChannelId":"686557x2440x0","timestamp":{"iso":"2021-09-07T23:46:02Z","unix":1631058362},"channelFlags":{"isEnabled":true,"isNode1":true},"cltvExpiryDelta":40,"htlcMinimumMsat":1000,"feeBaseMsat":0,"feeProportionalMillionths":1000,"htlcMaximumMsat":2475000000,"tlvStream":{"records":[],"unknown":[]}}""", - hex"4d77b955573527208ac391cf0aeffdfd5efbd99f365ef59f050e373dfbbec69337023a2ce66439cf9c56255486785af0760a08fec51cefce57e0b85ba3c594f46fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000008409700047800005df69f480000009000000000000003e8000003e800000001" -> - """{"signature":"4d77b955573527208ac391cf0aeffdfd5efbd99f365ef59f050e373dfbbec69337023a2ce66439cf9c56255486785af0760a08fec51cefce57e0b85ba3c594f4","chainHash":"6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000","shortChannelId":"540823x1144x0","timestamp":{"iso":"2019-12-15T21:02:00Z","unix":1576443720},"channelFlags":{"isEnabled":true,"isNode1":true},"cltvExpiryDelta":144,"htlcMinimumMsat":1000,"feeBaseMsat":1000,"feeProportionalMillionths":1,"tlvStream":{"records":[],"unknown":[]}}""", hex"b212e4d88a5ce3201ec34160d90a07eeb0601207d7d53bcf2b8f99b21146d7eb00d6a5b4b80b878eac0d25c2209eda05c913851730a65260c943fec8956cb22e6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d61900000000000a48ce0006900000613792e40100002800000000000003e8000003e8000000010000000056d35b20" -> """{"signature":"b212e4d88a5ce3201ec34160d90a07eeb0601207d7d53bcf2b8f99b21146d7eb00d6a5b4b80b878eac0d25c2209eda05c913851730a65260c943fec8956cb22e","chainHash":"6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000","shortChannelId":"673998x1680x0","timestamp":{"iso":"2021-09-07T16:27:16Z","unix":1631032036},"channelFlags":{"isEnabled":true,"isNode1":true},"cltvExpiryDelta":40,"htlcMinimumMsat":1000,"feeBaseMsat":1000,"feeProportionalMillionths":1,"htlcMaximumMsat":1456692000,"tlvStream":{"records":[],"unknown":[]}}""", hex"29396591aee1bfd292193b4329d24eb9f57ddb143f303d029ae004113a7402af015c721ddc3e5d2e36cc67c92af3bdcd22d55eaf1e532503f9972207b226984f6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000096f010006ea000061375a440102002800000000000003e8000003e800000001000000024e160300" -> diff --git a/eclair-node/src/test/resources/api/findroute-full b/eclair-node/src/test/resources/api/findroute-full index dff011caa1..ae20c38987 100644 --- a/eclair-node/src/test/resources/api/findroute-full +++ b/eclair-node/src/test/resources/api/findroute-full @@ -1 +1 @@ -{"routes":[{"amount":456,"hops":[{"nodeId":"03007e67dc5a8fd2b2ef21cb310ab6359ddb51f3f86a8b79b8b1e23bc3a6ea150a","nextNodeId":"026105f6cb4862810be989385d16f04b0f748f6f2a14040338b1a534d45b4be1c1","source":{"type":"announcement","channelUpdate":{"signature":"92cf3f12e161391986eb2cd7106ddab41a23c734f8f1ed120fb64f4b91f98f690ecf930388e62965f8aefbf1adafcd25a572669a125396dcfb83615208754679","chainHash":"024b7b3626554c44dcc2454ee3812458bfa68d9fced466edfab470844cb7ffe2","shortChannelId":"1x2x3","timestamp":{"iso":"1970-01-01T00:00:00Z","unix":0},"channelFlags":{"isEnabled":true,"isNode1":true},"cltvExpiryDelta":0,"htlcMinimumMsat":1,"feeBaseMsat":1,"feeProportionalMillionths":1,"tlvStream":{"records":[],"unknown":[]}}}},{"nodeId":"026105f6cb4862810be989385d16f04b0f748f6f2a14040338b1a534d45b4be1c1","nextNodeId":"038cfa2b5857843ee90cff91b06f692c0d8fe201921ee6387aee901d64f43699f0","source":{"type":"announcement","channelUpdate":{"signature":"92cf3f12e161391986eb2cd7106ddab41a23c734f8f1ed120fb64f4b91f98f690ecf930388e62965f8aefbf1adafcd25a572669a125396dcfb83615208754679","chainHash":"024b7b3626554c44dcc2454ee3812458bfa68d9fced466edfab470844cb7ffe2","shortChannelId":"1x2x4","timestamp":{"iso":"1970-01-01T00:00:00Z","unix":0},"channelFlags":{"isEnabled":true,"isNode1":true},"cltvExpiryDelta":0,"htlcMinimumMsat":1,"feeBaseMsat":1,"feeProportionalMillionths":1,"tlvStream":{"records":[],"unknown":[]}}}},{"nodeId":"038cfa2b5857843ee90cff91b06f692c0d8fe201921ee6387aee901d64f43699f0","nextNodeId":"02be60276e294c6921240daae33a361d214d02578656df0e74c61a09c3196e51df","source":{"type":"announcement","channelUpdate":{"signature":"92cf3f12e161391986eb2cd7106ddab41a23c734f8f1ed120fb64f4b91f98f690ecf930388e62965f8aefbf1adafcd25a572669a125396dcfb83615208754679","chainHash":"024b7b3626554c44dcc2454ee3812458bfa68d9fced466edfab470844cb7ffe2","shortChannelId":"1x2x5","timestamp":{"iso":"1970-01-01T00:00:00Z","unix":0},"channelFlags":{"isEnabled":true,"isNode1":true},"cltvExpiryDelta":0,"htlcMinimumMsat":1,"feeBaseMsat":1,"feeProportionalMillionths":1,"tlvStream":{"records":[],"unknown":[]}}}}]}]} \ No newline at end of file +{"routes":[{"amount":456,"hops":[{"nodeId":"03007e67dc5a8fd2b2ef21cb310ab6359ddb51f3f86a8b79b8b1e23bc3a6ea150a","nextNodeId":"026105f6cb4862810be989385d16f04b0f748f6f2a14040338b1a534d45b4be1c1","source":{"type":"announcement","channelUpdate":{"signature":"92cf3f12e161391986eb2cd7106ddab41a23c734f8f1ed120fb64f4b91f98f690ecf930388e62965f8aefbf1adafcd25a572669a125396dcfb83615208754679","chainHash":"024b7b3626554c44dcc2454ee3812458bfa68d9fced466edfab470844cb7ffe2","shortChannelId":"1x2x3","timestamp":{"iso":"1970-01-01T00:00:00Z","unix":0},"channelFlags":{"isEnabled":true,"isNode1":true},"cltvExpiryDelta":0,"htlcMinimumMsat":1,"feeBaseMsat":1,"feeProportionalMillionths":1,"htlcMaximumMsat":20000000,"tlvStream":{"records":[],"unknown":[]}}}},{"nodeId":"026105f6cb4862810be989385d16f04b0f748f6f2a14040338b1a534d45b4be1c1","nextNodeId":"038cfa2b5857843ee90cff91b06f692c0d8fe201921ee6387aee901d64f43699f0","source":{"type":"announcement","channelUpdate":{"signature":"92cf3f12e161391986eb2cd7106ddab41a23c734f8f1ed120fb64f4b91f98f690ecf930388e62965f8aefbf1adafcd25a572669a125396dcfb83615208754679","chainHash":"024b7b3626554c44dcc2454ee3812458bfa68d9fced466edfab470844cb7ffe2","shortChannelId":"1x2x4","timestamp":{"iso":"1970-01-01T00:00:00Z","unix":0},"channelFlags":{"isEnabled":true,"isNode1":true},"cltvExpiryDelta":0,"htlcMinimumMsat":1,"feeBaseMsat":1,"feeProportionalMillionths":1,"htlcMaximumMsat":20000000,"tlvStream":{"records":[],"unknown":[]}}}},{"nodeId":"038cfa2b5857843ee90cff91b06f692c0d8fe201921ee6387aee901d64f43699f0","nextNodeId":"02be60276e294c6921240daae33a361d214d02578656df0e74c61a09c3196e51df","source":{"type":"announcement","channelUpdate":{"signature":"92cf3f12e161391986eb2cd7106ddab41a23c734f8f1ed120fb64f4b91f98f690ecf930388e62965f8aefbf1adafcd25a572669a125396dcfb83615208754679","chainHash":"024b7b3626554c44dcc2454ee3812458bfa68d9fced466edfab470844cb7ffe2","shortChannelId":"1x2x5","timestamp":{"iso":"1970-01-01T00:00:00Z","unix":0},"channelFlags":{"isEnabled":true,"isNode1":true},"cltvExpiryDelta":0,"htlcMinimumMsat":1,"feeBaseMsat":1,"feeProportionalMillionths":1,"htlcMaximumMsat":20000000,"tlvStream":{"records":[],"unknown":[]}}}}]}]} \ No newline at end of file diff --git a/eclair-node/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala b/eclair-node/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala index dba2e0834a..1e88e1adcc 100644 --- a/eclair-node/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala +++ b/eclair-node/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala @@ -1006,10 +1006,10 @@ class ApiServiceSpec extends AnyFunSuite with ScalatestRouteTest with IdiomaticM timestamp = 0 unixsec, channelFlags = ChannelUpdate.ChannelFlags.DUMMY, cltvExpiryDelta = CltvExpiryDelta(0), - htlcMinimumMsat = MilliSatoshi(1), - feeBaseMsat = MilliSatoshi(1), + htlcMinimumMsat = 1 msat, + feeBaseMsat = 1 msat, feeProportionalMillionths = 1, - htlcMaximumMsat = None + htlcMaximumMsat = 20_000_000 msat ) val mockChannelUpdate2 = mockChannelUpdate1.copy(shortChannelId = RealShortChannelId(BlockHeight(1), 2, 4)) val mockChannelUpdate3 = mockChannelUpdate1.copy(shortChannelId = RealShortChannelId(BlockHeight(1), 2, 5)) From 06b4ad952e69524ffedeec2adaad516f5e6e75ae Mon Sep 17 00:00:00 2001 From: t-bast Date: Mon, 8 Aug 2022 13:32:21 +0200 Subject: [PATCH 2/2] fixup! Make htlc_maximum_msat mandatory in channel updates --- .../src/main/scala/fr/acinq/eclair/router/Announcements.scala | 2 +- eclair-core/src/main/scala/fr/acinq/eclair/router/Sync.scala | 2 +- .../acinq/eclair/wire/protocol/LightningMessageCodecs.scala | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/router/Announcements.scala b/eclair-core/src/main/scala/fr/acinq/eclair/router/Announcements.scala index cb54ef9b76..82e752a702 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/router/Announcements.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/router/Announcements.scala @@ -35,7 +35,7 @@ object Announcements { sha256(sha256(serializationResult(LightningMessageCodecs.nodeAnnouncementWitnessCodec.encode(features :: timestamp :: nodeId :: rgbColor :: alias :: addresses :: tlvStream :: HNil)))) def channelUpdateWitnessEncode(chainHash: ByteVector32, shortChannelId: ShortChannelId, timestamp: TimestampSecond, channelFlags: ChannelUpdate.ChannelFlags, cltvExpiryDelta: CltvExpiryDelta, htlcMinimumMsat: MilliSatoshi, feeBaseMsat: MilliSatoshi, feeProportionalMillionths: Long, htlcMaximumMsat: MilliSatoshi, tlvStream: TlvStream[ChannelUpdateTlv]): ByteVector = - sha256(sha256(serializationResult(LightningMessageCodecs.channelUpdateWitnessCodec.encode(chainHash :: shortChannelId :: timestamp :: () :: channelFlags :: cltvExpiryDelta :: htlcMinimumMsat :: feeBaseMsat :: feeProportionalMillionths :: htlcMaximumMsat :: tlvStream :: HNil)))) + sha256(sha256(serializationResult(LightningMessageCodecs.channelUpdateWitnessCodec.encode(chainHash :: shortChannelId :: timestamp :: channelFlags :: cltvExpiryDelta :: htlcMinimumMsat :: feeBaseMsat :: feeProportionalMillionths :: htlcMaximumMsat :: tlvStream :: HNil)))) def generateChannelAnnouncementWitness(chainHash: ByteVector32, shortChannelId: RealShortChannelId, localNodeId: PublicKey, remoteNodeId: PublicKey, localFundingKey: PublicKey, remoteFundingKey: PublicKey, features: Features[Feature]): ByteVector = if (isNode1(localNodeId, remoteNodeId)) { diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/router/Sync.scala b/eclair-core/src/main/scala/fr/acinq/eclair/router/Sync.scala index d6f86e5b80..f3195ab5d0 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/router/Sync.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/router/Sync.scala @@ -386,7 +386,7 @@ object Sync { def getChecksum(u: ChannelUpdate): Long = { import u._ - val data = serializationResult(LightningMessageCodecs.channelUpdateChecksumCodec.encode(chainHash :: shortChannelId :: () :: channelFlags :: cltvExpiryDelta :: htlcMinimumMsat :: feeBaseMsat :: feeProportionalMillionths :: htlcMaximumMsat :: HNil)) + val data = serializationResult(LightningMessageCodecs.channelUpdateChecksumCodec.encode(chainHash :: shortChannelId :: channelFlags :: cltvExpiryDelta :: htlcMinimumMsat :: feeBaseMsat :: feeProportionalMillionths :: htlcMaximumMsat :: HNil)) crc32c(data) } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecs.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecs.scala index 4f4119a70c..57259011f8 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecs.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecs.scala @@ -328,7 +328,7 @@ object LightningMessageCodecs { val channelUpdateChecksumCodec = ("chainHash" | bytes32) :: ("shortChannelId" | shortchannelid) :: - ("messageFlags" | constant(hex"01")) :: + ("messageFlags" | constant(hex"01")) :~>: channelFlagsCodec :: ("cltvExpiryDelta" | cltvExpiryDelta) :: ("htlcMinimumMsat" | millisatoshi) :: @@ -340,7 +340,7 @@ object LightningMessageCodecs { ("chainHash" | bytes32) :: ("shortChannelId" | shortchannelid) :: ("timestamp" | timestampSecond) :: - ("messageFlags" | constant(hex"01")) :: + ("messageFlags" | constant(hex"01")) :~>: channelFlagsCodec :: ("cltvExpiryDelta" | cltvExpiryDelta) :: ("htlcMinimumMsat" | millisatoshi) ::