1
0
Fork 0
mirror of https://github.com/ACINQ/eclair.git synced 2025-02-22 06:21:42 +01:00

Add HTLC endorsement/confidence (#2884)

Implements https://github.com/lightning/blips/pull/27, a subsequent PR will implement a confidence estimator.
This commit is contained in:
Thomas HUET 2024-07-31 12:00:56 +02:00 committed by GitHub
parent e298ba96ea
commit 7aacd4b460
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 451 additions and 400 deletions

View file

@ -196,7 +196,7 @@ sealed trait HasOptionalReplyToCommand extends Command { def replyTo_opt: Option
sealed trait ForbiddenCommandDuringSplice extends Command
sealed trait ForbiddenCommandDuringQuiescence extends Command
final case class CMD_ADD_HTLC(replyTo: ActorRef, amount: MilliSatoshi, paymentHash: ByteVector32, cltvExpiry: CltvExpiry, onion: OnionRoutingPacket, nextBlindingKey_opt: Option[PublicKey], origin: Origin.Hot, commit: Boolean = false) extends HasReplyToCommand with ForbiddenCommandDuringSplice with ForbiddenCommandDuringQuiescence
final case class CMD_ADD_HTLC(replyTo: ActorRef, amount: MilliSatoshi, paymentHash: ByteVector32, cltvExpiry: CltvExpiry, onion: OnionRoutingPacket, nextBlindingKey_opt: Option[PublicKey], confidence: Double, origin: Origin.Hot, commit: Boolean = false) extends HasReplyToCommand with ForbiddenCommandDuringSplice with ForbiddenCommandDuringQuiescence
sealed trait HtlcSettlementCommand extends HasOptionalReplyToCommand with ForbiddenCommandDuringSplice with ForbiddenCommandDuringQuiescence { def id: Long }
final case class CMD_FULFILL_HTLC(id: Long, r: ByteVector32, commit: Boolean = false, replyTo_opt: Option[ActorRef] = None) extends HtlcSettlementCommand
final case class CMD_FAIL_HTLC(id: Long, reason: Either[ByteVector, FailureMessage], delay_opt: Option[FiniteDuration] = None, commit: Boolean = false, replyTo_opt: Option[ActorRef] = None) extends HtlcSettlementCommand

View file

@ -854,7 +854,7 @@ case class Commitments(params: ChannelParams,
return Left(HtlcValueTooSmall(params.channelId, minimum = htlcMinimum, actual = cmd.amount))
}
val add = UpdateAddHtlc(channelId, changes.localNextHtlcId, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion, cmd.nextBlindingKey_opt)
val add = UpdateAddHtlc(channelId, changes.localNextHtlcId, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion, cmd.nextBlindingKey_opt, cmd.confidence)
// we increment the local htlc index and add an entry to the origins map
val changes1 = changes.addLocalProposal(add).copy(localNextHtlcId = changes.localNextHtlcId + 1)
val originChannels1 = originChannels + (add.id -> cmd.origin)

View file

@ -67,6 +67,10 @@ object Monitoring {
PaymentNodeOutAmount.withoutTags().record(bucket, amount.truncateToSatoshi.toLong)
PaymentNodeOut.withoutTags().record(bucket)
}
private val RelayConfidence = Kamon.histogram("payment.relay.confidence", "Confidence (in percent) that the relayed HTLC will be fulfilled")
def relayFulfill(confidence: Double) = RelayConfidence.withTag("status", "fulfill").record((confidence * 100).toLong)
def relayFail(confidence: Double) = RelayConfidence.withTag("status", "fail").record((confidence * 100).toLong)
}
object Tags {

View file

@ -285,12 +285,12 @@ object OutgoingPaymentPacket {
}
/** Build the command to add an HTLC for the given recipient using the provided route. */
def buildOutgoingPayment(origin: Origin.Hot, paymentHash: ByteVector32, route: Route, recipient: Recipient): Either[OutgoingPaymentError, OutgoingPaymentPacket] = {
def buildOutgoingPayment(origin: Origin.Hot, paymentHash: ByteVector32, route: Route, recipient: Recipient, confidence: Double): Either[OutgoingPaymentError, OutgoingPaymentPacket] = {
for {
payment <- recipient.buildPayloads(paymentHash, route)
onion <- buildOnion(payment.payloads, paymentHash, Some(PaymentOnionCodecs.paymentOnionPayloadLength)) // BOLT 2 requires that associatedData == paymentHash
} yield {
val cmd = CMD_ADD_HTLC(origin.replyTo, payment.amount, paymentHash, payment.expiry, onion.packet, payment.outerBlinding_opt, origin, commit = true)
val cmd = CMD_ADD_HTLC(origin.replyTo, payment.amount, paymentHash, payment.expiry, onion.packet, payment.outerBlinding_opt, confidence, origin, commit = true)
OutgoingPaymentPacket(cmd, route.hops.head.shortChannelId, onion.sharedSecrets)
}
}

View file

@ -16,11 +16,11 @@
package fr.acinq.eclair.payment.relay
import akka.actor.ActorRef
import akka.actor.typed.Behavior
import akka.actor.typed.eventstream.EventStream
import akka.actor.typed.scaladsl.adapter.TypedActorRefOps
import akka.actor.typed.scaladsl.{ActorContext, Behaviors}
import akka.actor.{ActorRef, typed}
import fr.acinq.bitcoin.scalacompat.ByteVector32
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.eclair.channel._
@ -54,7 +54,12 @@ object ChannelRelay {
case class RelaySuccess(selectedChannelId: ByteVector32, cmdAdd: CMD_ADD_HTLC) extends RelayResult
// @formatter:on
def apply(nodeParams: NodeParams, register: ActorRef, channels: Map[ByteVector32, Relayer.OutgoingChannel], originNode: PublicKey, relayId: UUID, r: IncomingPaymentPacket.ChannelRelayPacket): Behavior[Command] =
def apply(nodeParams: NodeParams,
register: ActorRef,
channels: Map[ByteVector32, Relayer.OutgoingChannel],
originNode:PublicKey,
relayId: UUID,
r: IncomingPaymentPacket.ChannelRelayPacket): Behavior[Command] =
Behaviors.setup { context =>
Behaviors.withMdc(Logs.mdc(
category_opt = Some(Logs.LogCategory.PAYMENT),
@ -63,7 +68,8 @@ object ChannelRelay {
nodeAlias_opt = Some(nodeParams.alias))) {
val upstream = Upstream.Hot.Channel(r.add.removeUnknownTlvs(), TimestampMilli.now(), originNode)
context.self ! DoRelay
new ChannelRelay(nodeParams, register, channels, r, upstream, context).relay(Seq.empty)
val confidence = (r.add.endorsement + 0.5) / 8
new ChannelRelay(nodeParams, register, channels, r, upstream, confidence, context).relay(Seq.empty)
}
}
@ -107,6 +113,7 @@ class ChannelRelay private(nodeParams: NodeParams,
channels: Map[ByteVector32, Relayer.OutgoingChannel],
r: IncomingPaymentPacket.ChannelRelayPacket,
upstream: Upstream.Hot.Channel,
confidence: Double,
context: ActorContext[ChannelRelay.Command]) {
import ChannelRelay._
@ -149,22 +156,24 @@ class ChannelRelay private(nodeParams: NodeParams,
context.self ! DoRelay
relay(previousFailures :+ PreviouslyTried(selectedChannelId, addFailed))
case WrappedAddResponse(_: RES_SUCCESS[_]) =>
case WrappedAddResponse(r: RES_SUCCESS[_]) =>
context.log.debug("sent htlc to the downstream channel")
waitForAddSettled()
waitForAddSettled(r.channelId)
}
def waitForAddSettled(): Behavior[Command] =
def waitForAddSettled(channelId: ByteVector32): Behavior[Command] =
Behaviors.receiveMessagePartial {
case WrappedAddResponse(RES_ADD_SETTLED(_, htlc, fulfill: HtlcResult.Fulfill)) =>
context.log.debug("relaying fulfill to upstream")
context.log.info("relaying fulfill to upstream, startedAt={}, endedAt={}, confidence={}, originNode={}, outgoingChannel={}", upstream.receivedAt, TimestampMilli.now(), confidence, upstream.receivedFrom, channelId)
Metrics.relayFulfill(confidence)
val cmd = CMD_FULFILL_HTLC(upstream.add.id, fulfill.paymentPreimage, commit = true)
context.system.eventStream ! EventStream.Publish(ChannelPaymentRelayed(upstream.amountIn, htlc.amountMsat, htlc.paymentHash, upstream.add.channelId, htlc.channelId, upstream.receivedAt, TimestampMilli.now()))
recordRelayDuration(isSuccess = true)
safeSendAndStop(upstream.add.channelId, cmd)
case WrappedAddResponse(RES_ADD_SETTLED(_, _, fail: HtlcResult.Fail)) =>
context.log.debug("relaying fail to upstream")
context.log.info("relaying fail to upstream, startedAt={}, endedAt={}, confidence={}, originNode={}, outgoingChannel={}", upstream.receivedAt, TimestampMilli.now(), confidence, upstream.receivedFrom, channelId)
Metrics.relayFail(confidence)
Metrics.recordPaymentRelayFailed(Tags.FailureType.Remote, Tags.RelayType.Channel)
val cmd = translateRelayFailure(upstream.add.id, fail)
recordRelayDuration(isSuccess = false)
@ -312,7 +321,7 @@ class ChannelRelay private(nodeParams: NodeParams,
case payload: IntermediatePayload.ChannelRelay.Blinded => Some(payload.nextBlinding)
case _: IntermediatePayload.ChannelRelay.Standard => None
}
RelaySuccess(c.channelId, CMD_ADD_HTLC(addResponseAdapter.toClassic, r.amountToForward, r.add.paymentHash, r.outgoingCltv, r.nextPacket, nextBlindingKey_opt, origin, commit = true))
RelaySuccess(c.channelId, CMD_ADD_HTLC(addResponseAdapter.toClassic, r.amountToForward, r.add.paymentHash, r.outgoingCltv, r.nextPacket, nextBlindingKey_opt, confidence, origin, commit = true))
}
}

View file

@ -259,7 +259,8 @@ class NodeRelay private(nodeParams: NodeParams,
private def doSend(upstream: Upstream.Hot.Trampoline, nextPayload: IntermediatePayload.NodeRelay, nextPacket_opt: Option[OnionRoutingPacket]): Behavior[Command] = {
context.log.debug(s"relaying trampoline payment (amountIn=${upstream.amountIn} expiryIn=${upstream.expiryIn} amountOut=${nextPayload.amountToForward} expiryOut=${nextPayload.outgoingCltv})")
relay(upstream, nextPayload, nextPacket_opt)
val confidence = (upstream.received.map(_.add.endorsement).min + 0.5) / 8
relay(upstream, nextPayload, nextPacket_opt, confidence)
}
/**
@ -316,12 +317,12 @@ class NodeRelay private(nodeParams: NodeParams,
context.messageAdapter[PaymentFailed](WrappedPaymentFailed)
}.toClassic
private def relay(upstream: Upstream.Hot.Trampoline, payloadOut: IntermediatePayload.NodeRelay, packetOut_opt: Option[OnionRoutingPacket]): Behavior[Command] = {
private def relay(upstream: Upstream.Hot.Trampoline, payloadOut: IntermediatePayload.NodeRelay, packetOut_opt: Option[OnionRoutingPacket], confidence: Double): Behavior[Command] = {
val displayNodeId = payloadOut match {
case payloadOut: IntermediatePayload.NodeRelay.Standard => payloadOut.outgoingNodeId
case _: IntermediatePayload.NodeRelay.ToBlindedPaths => randomKey().publicKey
}
val paymentCfg = SendPaymentConfig(relayId, relayId, None, paymentHash, displayNodeId, upstream, None, None, storeInDb = false, publishEvent = false, recordPathFindingMetrics = true)
val paymentCfg = SendPaymentConfig(relayId, relayId, None, paymentHash, displayNodeId, upstream, None, None, storeInDb = false, publishEvent = false, recordPathFindingMetrics = true, confidence)
val routeParams = computeRouteParams(nodeParams, upstream.amountIn, upstream.expiryIn, payloadOut.amountToForward, payloadOut.outgoingCltv)
payloadOut match {
case payloadOut: IntermediatePayload.NodeRelay.Standard =>

View file

@ -50,7 +50,7 @@ class PaymentInitiator(nodeParams: NodeParams, outgoingPaymentFactory: PaymentIn
// Immediately return the paymentId
replyTo ! paymentId
}
val paymentCfg = SendPaymentConfig(paymentId, paymentId, r.externalId, r.paymentHash, r.invoice.nodeId, Upstream.Local(paymentId), Some(r.invoice), r.payerKey_opt, storeInDb = true, publishEvent = true, recordPathFindingMetrics = true)
val paymentCfg = SendPaymentConfig(paymentId, paymentId, r.externalId, r.paymentHash, r.invoice.nodeId, Upstream.Local(paymentId), Some(r.invoice), r.payerKey_opt, storeInDb = true, publishEvent = true, recordPathFindingMetrics = true, confidence = 1.0)
val finalExpiry = r.finalExpiry(nodeParams)
val recipient = r.invoice match {
case invoice: Bolt11Invoice => ClearRecipient(invoice, r.recipientAmount, finalExpiry, r.userCustomTlvs)
@ -71,7 +71,7 @@ class PaymentInitiator(nodeParams: NodeParams, outgoingPaymentFactory: PaymentIn
case r: SendSpontaneousPayment =>
val paymentId = UUID.randomUUID()
sender() ! paymentId
val paymentCfg = SendPaymentConfig(paymentId, paymentId, r.externalId, r.paymentHash, r.recipientNodeId, Upstream.Local(paymentId), None, None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = r.recordPathFindingMetrics)
val paymentCfg = SendPaymentConfig(paymentId, paymentId, r.externalId, r.paymentHash, r.recipientNodeId, Upstream.Local(paymentId), None, None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = r.recordPathFindingMetrics, confidence = 1.0)
val finalExpiry = nodeParams.paymentFinalExpiry.computeFinalExpiry(nodeParams.currentBlockHeight, Channel.MIN_CLTV_EXPIRY_DELTA)
val recipient = SpontaneousRecipient(r.recipientNodeId, r.recipientAmount, finalExpiry, r.paymentPreimage, r.userCustomTlvs)
val fsm = outgoingPaymentFactory.spawnOutgoingPayment(context, paymentCfg)
@ -104,13 +104,13 @@ class PaymentInitiator(nodeParams: NodeParams, outgoingPaymentFactory: PaymentIn
val trampolineHop = NodeHop(trampolineNodeId, r.recipientNodeId, trampolineAttempt.cltvExpiryDelta, trampolineAttempt.fees)
val recipient = buildTrampolineRecipient(r, trampolineHop)
sender() ! SendPaymentToRouteResponse(paymentId, parentPaymentId, Some(recipient.trampolinePaymentSecret))
val paymentCfg = SendPaymentConfig(paymentId, parentPaymentId, r.externalId, r.paymentHash, r.recipientNodeId, Upstream.Local(paymentId), Some(r.invoice), None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = false)
val paymentCfg = SendPaymentConfig(paymentId, parentPaymentId, r.externalId, r.paymentHash, r.recipientNodeId, Upstream.Local(paymentId), Some(r.invoice), None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = false, confidence = 1.0)
val payFsm = outgoingPaymentFactory.spawnOutgoingPayment(context, paymentCfg)
payFsm ! PaymentLifecycle.SendPaymentToRoute(self, Left(r.route), recipient)
context become main(pending + (paymentId -> PendingPaymentToRoute(sender(), r)))
case None =>
sender() ! SendPaymentToRouteResponse(paymentId, parentPaymentId, None)
val paymentCfg = SendPaymentConfig(paymentId, parentPaymentId, r.externalId, r.paymentHash, r.recipientNodeId, Upstream.Local(paymentId), Some(r.invoice), None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = false)
val paymentCfg = SendPaymentConfig(paymentId, parentPaymentId, r.externalId, r.paymentHash, r.recipientNodeId, Upstream.Local(paymentId), Some(r.invoice), None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = false, confidence = 1.0)
val finalExpiry = r.finalExpiry(nodeParams)
val recipient = r.invoice match {
case invoice: Bolt11Invoice => ClearRecipient(invoice, r.recipientAmount, finalExpiry, Set.empty)
@ -192,7 +192,7 @@ class PaymentInitiator(nodeParams: NodeParams, outgoingPaymentFactory: PaymentIn
private def sendTrampolinePayment(paymentId: UUID, r: SendTrampolinePayment, trampolineFees: MilliSatoshi, trampolineExpiryDelta: CltvExpiryDelta): Unit = {
val trampolineHop = NodeHop(r.trampolineNodeId, r.recipientNodeId, trampolineExpiryDelta, trampolineFees)
val paymentCfg = SendPaymentConfig(paymentId, paymentId, None, r.paymentHash, r.recipientNodeId, Upstream.Local(paymentId), Some(r.invoice), None, storeInDb = true, publishEvent = false, recordPathFindingMetrics = true)
val paymentCfg = SendPaymentConfig(paymentId, paymentId, None, r.paymentHash, r.recipientNodeId, Upstream.Local(paymentId), Some(r.invoice), None, storeInDb = true, publishEvent = false, recordPathFindingMetrics = true, confidence = 1.0)
val recipient = buildTrampolineRecipient(r, trampolineHop)
val fsm = outgoingPaymentFactory.spawnOutgoingMultiPartPayment(context, paymentCfg, publishPreimage = false)
fsm ! MultiPartPaymentLifecycle.SendMultiPartPayment(self, recipient, nodeParams.maxPaymentAttempts, r.routeParams)
@ -395,6 +395,7 @@ object PaymentInitiator {
* @param publishEvent whether to publish a [[fr.acinq.eclair.payment.PaymentEvent]] on success/failure (e.g. for
* multi-part child payments, we don't want to emit events for each child, only for the whole payment).
* @param recordPathFindingMetrics We don't record metrics for payments that don't use path finding or that are a part of a bigger payment.
* @param confidence How confident we are that this payment will succeed. Used to set the outgoing endorsement value.
*/
case class SendPaymentConfig(id: UUID,
parentId: UUID,
@ -406,7 +407,8 @@ object PaymentInitiator {
payerKey_opt: Option[PrivateKey],
storeInDb: Boolean, // e.g. for trampoline we don't want to store in the DB when we're relaying payments
publishEvent: Boolean,
recordPathFindingMetrics: Boolean) {
recordPathFindingMetrics: Boolean,
confidence: Double) {
val paymentContext: PaymentContext = PaymentContext(id, parentId, paymentHash)
val paymentType = invoice match {
case Some(_: Bolt12Invoice) => PaymentType.Blinded

View file

@ -75,7 +75,7 @@ class PaymentLifecycle(nodeParams: NodeParams, cfg: SendPaymentConfig, router: A
when(WAITING_FOR_ROUTE) {
case Event(RouteResponse(route +: _), WaitingForRoute(request, failures, ignore)) =>
log.info(s"route found: attempt=${failures.size + 1}/${request.maxAttempts} route=${route.printNodes()} channels=${route.printChannels()}")
OutgoingPaymentPacket.buildOutgoingPayment(Origin.Hot(self, cfg.upstream), paymentHash, route, request.recipient) match {
OutgoingPaymentPacket.buildOutgoingPayment(Origin.Hot(self, cfg.upstream), paymentHash, route, request.recipient, cfg.confidence) match {
case Right(payment) =>
register ! Register.ForwardShortId(self.toTyped[Register.ForwardShortIdFailure[CMD_ADD_HTLC]], payment.outgoingChannel, payment.cmd)
goto(WAITING_FOR_PAYMENT_COMPLETE) using WaitingForComplete(request, payment.cmd, failures, payment.sharedSecrets, ignore, route)

View file

@ -20,7 +20,7 @@ import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.eclair.UInt64
import fr.acinq.eclair.wire.protocol.CommonCodecs._
import fr.acinq.eclair.wire.protocol.TlvCodecs.{tlvField, tlvStream, tu16}
import scodec.Codec
import scodec.{Attempt, Codec, Err}
import scodec.bits.HexStringSyntax
import scodec.codecs._
@ -34,9 +34,15 @@ object UpdateAddHtlcTlv {
/** Blinding ephemeral public key that should be used to derive shared secrets when using route blinding. */
case class BlindingPoint(publicKey: PublicKey) extends UpdateAddHtlcTlv
case class Endorsement(level: Int) extends UpdateAddHtlcTlv
private val blindingPoint: Codec[BlindingPoint] = (("length" | constant(hex"21")) :: ("blinding" | publicKey)).as[BlindingPoint]
val addHtlcTlvCodec: Codec[TlvStream[UpdateAddHtlcTlv]] = tlvStream(discriminated[UpdateAddHtlcTlv].by(varint).typecase(UInt64(0), blindingPoint))
private val endorsement: Codec[Endorsement] = tlvField(uint8.narrow[Endorsement](n => if (n >= 8) Attempt.failure(Err(s"invalid endorsement level: $n")) else Attempt.successful(Endorsement(n)), _.level))
val addHtlcTlvCodec: Codec[TlvStream[UpdateAddHtlcTlv]] = tlvStream(discriminated[UpdateAddHtlcTlv].by(varint)
.typecase(UInt64(0), blindingPoint)
.typecase(UInt64(106823), endorsement))
}
sealed trait UpdateFulfillHtlcTlv extends Tlv

View file

@ -351,9 +351,11 @@ case class UpdateAddHtlc(channelId: ByteVector32,
paymentHash: ByteVector32,
cltvExpiry: CltvExpiry,
onionRoutingPacket: OnionRoutingPacket,
tlvStream: TlvStream[UpdateAddHtlcTlv]) extends HtlcMessage with UpdateMessage with HasChannelId {
tlvStream: TlvStream[UpdateAddHtlcTlv] = TlvStream.empty) extends HtlcMessage with UpdateMessage with HasChannelId {
val blinding_opt: Option[PublicKey] = tlvStream.get[UpdateAddHtlcTlv.BlindingPoint].map(_.publicKey)
val endorsement: Int = tlvStream.get[UpdateAddHtlcTlv.Endorsement].map(_.level).getOrElse(0)
/** When storing in our DB, we avoid wasting storage with unknown data. */
def removeUnknownTlvs(): UpdateAddHtlc = this.copy(tlvStream = tlvStream.copy(unknown = Set.empty))
}
@ -365,8 +367,9 @@ object UpdateAddHtlc {
paymentHash: ByteVector32,
cltvExpiry: CltvExpiry,
onionRoutingPacket: OnionRoutingPacket,
blinding_opt: Option[PublicKey]): UpdateAddHtlc = {
val tlvs = blinding_opt.map(UpdateAddHtlcTlv.BlindingPoint).toSet[UpdateAddHtlcTlv]
blinding_opt: Option[PublicKey],
confidence: Double): UpdateAddHtlc = {
val tlvs: Set[UpdateAddHtlcTlv] = Set(blinding_opt.map(UpdateAddHtlcTlv.BlindingPoint), Some(UpdateAddHtlcTlv.Endorsement((confidence * 7.999).toInt))).flatten
UpdateAddHtlc(channelId, id, amountMsat, paymentHash, cltvExpiry, onionRoutingPacket, TlvStream(tlvs))
}
}

View file

@ -409,7 +409,7 @@ class CommitmentsSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
test("can receive availableForReceive") { f =>
for (isInitiator <- Seq(true, false)) {
val c = CommitmentsSpec.makeCommitments(31000000 msat, 702000000 msat, FeeratePerKw(2679 sat), 546 sat, isInitiator)
val add = UpdateAddHtlc(randomBytes32(), c.changes.remoteNextHtlcId, c.availableBalanceForReceive, randomBytes32(), CltvExpiry(f.currentBlockHeight), TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(randomBytes32(), c.changes.remoteNextHtlcId, c.availableBalanceForReceive, randomBytes32(), CltvExpiry(f.currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0)
c.receiveAdd(add, feerates, feeConfNoMismatch)
}
}
@ -460,14 +460,14 @@ class CommitmentsSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
// Add some initial HTLCs to the pending list (bigger commit tx).
for (_ <- 1 to t.pendingHtlcs) {
val amount = Random.nextInt(maxPendingHtlcAmount.toLong.toInt).msat.max(1 msat)
val add = UpdateAddHtlc(randomBytes32(), c.changes.remoteNextHtlcId, amount, randomBytes32(), CltvExpiry(f.currentBlockHeight), TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(randomBytes32(), c.changes.remoteNextHtlcId, amount, randomBytes32(), CltvExpiry(f.currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0)
c.receiveAdd(add, feerates, feeConfNoMismatch) match {
case Right(cc) => c = cc
case Left(e) => ignore(s"$t -> could not setup initial htlcs: $e")
}
}
if (c.availableBalanceForReceive > 0.msat) {
val add = UpdateAddHtlc(randomBytes32(), c.changes.remoteNextHtlcId, c.availableBalanceForReceive, randomBytes32(), CltvExpiry(f.currentBlockHeight), TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(randomBytes32(), c.changes.remoteNextHtlcId, c.availableBalanceForReceive, randomBytes32(), CltvExpiry(f.currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0)
c.receiveAdd(add, feerates, feeConfNoMismatch) match {
case Right(_) => ()
case Left(e) => fail(s"$t -> $e")

View file

@ -26,7 +26,7 @@ import org.scalatest.funsuite.AnyFunSuiteLike
class DustExposureSpec extends AnyFunSuiteLike {
def createHtlc(id: Long, amount: MilliSatoshi): UpdateAddHtlc = {
UpdateAddHtlc(ByteVector32.Zeroes, id, amount, randomBytes32(), CltvExpiry(500), TestConstants.emptyOnionPacket, None)
UpdateAddHtlc(ByteVector32.Zeroes, id, amount, randomBytes32(), CltvExpiry(500), TestConstants.emptyOnionPacket, None, 1.0)
}
test("compute dust exposure") {

View file

@ -38,7 +38,6 @@ import grizzled.slf4j.Logging
import org.scalatest.funsuite.FixtureAnyFunSuiteLike
import org.scalatest.{Outcome, Tag}
import java.util.UUID
import java.util.concurrent.CountDownLatch
import scala.concurrent.duration._
import scala.util.Random
@ -119,7 +118,7 @@ class FuzzySpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with Channe
// allow overpaying (no more than 2 times the required amount)
val amount = requiredAmount + Random.nextInt(requiredAmount.toLong.toInt).msat
val expiry = (Channel.MIN_CLTV_EXPIRY_DELTA + 1).toCltvExpiry(currentBlockHeight = BlockHeight(400000))
val Right(payment) = OutgoingPaymentPacket.buildOutgoingPayment(localOrigin(self), invoice.paymentHash, makeSingleHopRoute(amount, invoice.nodeId), ClearRecipient(invoice, amount, expiry, Set.empty))
val Right(payment) = OutgoingPaymentPacket.buildOutgoingPayment(localOrigin(self), invoice.paymentHash, makeSingleHopRoute(amount, invoice.nodeId), ClearRecipient(invoice, amount, expiry, Set.empty), 1.0)
payment.cmd
}

View file

@ -68,14 +68,14 @@ class HelpersSpec extends TestKitBaseClass with AnyFunSuiteLike with ChannelStat
awaitCond(bob.stateName == NORMAL)
// We have two identical HTLCs (MPP):
val (_, htlca1a) = addHtlc(15_000_000 msat, alice, bob, alice2bob, bob2alice)
val aliceMppCmd = CMD_ADD_HTLC(TestProbe().ref, 15_000_000 msat, htlca1a.paymentHash, htlca1a.cltvExpiry, htlca1a.onionRoutingPacket, None, Origin.Hot(TestProbe().ref, Upstream.Local(UUID.randomUUID())))
val aliceMppCmd = CMD_ADD_HTLC(TestProbe().ref, 15_000_000 msat, htlca1a.paymentHash, htlca1a.cltvExpiry, htlca1a.onionRoutingPacket, None, 1.0, Origin.Hot(TestProbe().ref, Upstream.Local(UUID.randomUUID())))
val htlca1b = addHtlc(aliceMppCmd, alice, bob, alice2bob, bob2alice)
val (ra2, htlca2) = addHtlc(16_000_000 msat, alice, bob, alice2bob, bob2alice)
addHtlc(500_000 msat, alice, bob, alice2bob, bob2alice) // below dust
crossSign(alice, bob, alice2bob, bob2alice)
// We have two identical HTLCs (MPP):
val (_, htlcb1a) = addHtlc(17_000_000 msat, bob, alice, bob2alice, alice2bob)
val bobMppCmd = CMD_ADD_HTLC(TestProbe().ref, 17_000_000 msat, htlcb1a.paymentHash, htlcb1a.cltvExpiry, htlcb1a.onionRoutingPacket, None, Origin.Hot(TestProbe().ref, Upstream.Local(UUID.randomUUID())))
val bobMppCmd = CMD_ADD_HTLC(TestProbe().ref, 17_000_000 msat, htlcb1a.paymentHash, htlcb1a.cltvExpiry, htlcb1a.onionRoutingPacket, None, 1.0, Origin.Hot(TestProbe().ref, Upstream.Local(UUID.randomUUID())))
val htlcb1b = addHtlc(bobMppCmd, bob, alice, bob2alice, alice2bob)
val (rb2, htlcb2) = addHtlc(18_000_000 msat, bob, alice, bob2alice, alice2bob)
addHtlc(400_000 msat, bob, alice, bob2alice, alice2bob) // below dust

View file

@ -388,7 +388,7 @@ trait ChannelStateTestsBase extends Assertions with Eventually {
val paymentHash = Crypto.sha256(paymentPreimage)
val expiry = cltvExpiryDelta.toCltvExpiry(currentBlockHeight)
val recipient = SpontaneousRecipient(destination, amount, expiry, paymentPreimage)
val Right(payment) = OutgoingPaymentPacket.buildOutgoingPayment(Origin.Hot(replyTo, upstream), paymentHash, makeSingleHopRoute(amount, destination), recipient)
val Right(payment) = OutgoingPaymentPacket.buildOutgoingPayment(Origin.Hot(replyTo, upstream), paymentHash, makeSingleHopRoute(amount, destination), recipient, 1.0)
(paymentPreimage, payment.cmd.copy(commit = false))
}

View file

@ -148,7 +148,7 @@ class NormalQuiescentStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteL
// initiator should reject commands that change the commitment once it became quiescent
val sender1, sender2, sender3 = TestProbe()
val cmds = Seq(
CMD_ADD_HTLC(sender1.ref, 1_000_000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender1.ref)),
CMD_ADD_HTLC(sender1.ref, 1_000_000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender1.ref)),
CMD_UPDATE_FEE(FeeratePerKw(100 sat), replyTo_opt = Some(sender2.ref)),
CMD_CLOSE(sender3.ref, None, None)
)
@ -164,7 +164,7 @@ class NormalQuiescentStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteL
// both should reject commands that change the commitment while quiescent
val sender1, sender2, sender3 = TestProbe()
val cmds = Seq(
CMD_ADD_HTLC(sender1.ref, 1_000_000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender1.ref)),
CMD_ADD_HTLC(sender1.ref, 1_000_000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender1.ref)),
CMD_UPDATE_FEE(FeeratePerKw(100 sat), replyTo_opt = Some(sender2.ref)),
CMD_CLOSE(sender3.ref, None, None)
)
@ -352,7 +352,7 @@ class NormalQuiescentStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteL
import f._
initiateQuiescence(f, sendInitialStfu = true)
// have to build a htlc manually because eclair would refuse to accept this command as it's forbidden
val forbiddenMsg = UpdateAddHtlc(channelId = randomBytes32(), id = 5656, amountMsat = 50000000 msat, cltvExpiry = CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), paymentHash = randomBytes32(), onionRoutingPacket = TestConstants.emptyOnionPacket, blinding_opt = None)
val forbiddenMsg = UpdateAddHtlc(channelId = randomBytes32(), id = 5656, amountMsat = 50000000 msat, cltvExpiry = CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), paymentHash = randomBytes32(), onionRoutingPacket = TestConstants.emptyOnionPacket, blinding_opt = None, confidence = 1.0)
// both parties will respond to a forbidden msg while quiescent with a warning (and disconnect)
bob2alice.forward(alice, forbiddenMsg)
alice2bob.expectMsg(Warning(channelId(alice), ForbiddenDuringSplice(channelId(alice), "UpdateAddHtlc").getMessage))

View file

@ -588,7 +588,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
val sender = TestProbe()
// command for a large payment (larger than local balance pre-slice)
val cmd = CMD_ADD_HTLC(sender.ref, 1_000_000_000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val cmd = CMD_ADD_HTLC(sender.ref, 1_000_000_000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
// first attempt at payment fails (not enough balance)
alice ! cmd
sender.expectMsgType[RES_ADD_FAILED[_]]
@ -840,7 +840,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
import f._
initiateSplice(f, spliceIn_opt = Some(SpliceIn(500_000 sat)))
val sender = TestProbe()
alice ! CMD_ADD_HTLC(sender.ref, 500_000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
alice ! CMD_ADD_HTLC(sender.ref, 500_000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
alice2bob.expectMsgType[UpdateAddHtlc]
alice2bob.forward(bob)
@ -869,7 +869,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
import f._
initiateSplice(f, spliceIn_opt = Some(SpliceIn(500_000 sat)))
val sender = TestProbe()
alice ! CMD_ADD_HTLC(sender.ref, 500_000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
alice ! CMD_ADD_HTLC(sender.ref, 500_000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
alice2bob.expectMsgType[UpdateAddHtlc]
alice2bob.forward(bob)
@ -903,7 +903,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
val cmd = CMD_SPLICE(sender.ref, spliceIn_opt = Some(SpliceIn(500_000 sat, pushAmount = 0 msat)), spliceOut_opt = None)
alice ! cmd
alice2bob.expectMsgType[SpliceInit]
alice ! CMD_ADD_HTLC(sender.ref, 500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
alice ! CMD_ADD_HTLC(sender.ref, 500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_ADD_FAILED[_]]
alice2bob.expectNoMessage(100 millis)
}
@ -918,7 +918,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
bob2alice.expectMsgType[SpliceAck]
bob2alice.forward(alice)
alice2bob.expectMsgType[TxAddInput]
alice ! CMD_ADD_HTLC(sender.ref, 500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
alice ! CMD_ADD_HTLC(sender.ref, 500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_ADD_FAILED[_]]
alice2bob.expectNoMessage(100 millis)
}
@ -960,7 +960,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
alice2bob.expectMsgType[TxAddInput]
// have to build a htlc manually because eclair would refuse to accept this command as it's forbidden
val fakeHtlc = UpdateAddHtlc(channelId = randomBytes32(), id = 5656, amountMsat = 50000000 msat, cltvExpiry = CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), paymentHash = randomBytes32(), onionRoutingPacket = TestConstants.emptyOnionPacket, blinding_opt = None)
val fakeHtlc = UpdateAddHtlc(channelId = randomBytes32(), id = 5656, amountMsat = 50000000 msat, cltvExpiry = CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), paymentHash = randomBytes32(), onionRoutingPacket = TestConstants.emptyOnionPacket, blinding_opt = None, confidence = 1.0)
bob2alice.forward(alice, fakeHtlc)
// alice returns a warning and schedules a disconnect after receiving UpdateAddHtlc
alice2bob.expectMsg(Warning(channelId(alice), ForbiddenDuringSplice(channelId(alice), "UpdateAddHtlc").getMessage))

View file

@ -77,7 +77,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val listener = TestProbe()
alice.underlying.system.eventStream.subscribe(listener.ref, classOf[AvailableBalanceChanged])
val h = randomBytes32()
val add = CMD_ADD_HTLC(sender.ref, 50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
val e = listener.expectMsgType[AvailableBalanceChanged]
@ -103,7 +103,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val sender = TestProbe()
val h = randomBytes32()
for (i <- 0 until 10) {
alice ! CMD_ADD_HTLC(sender.ref, 500000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
alice ! CMD_ADD_HTLC(sender.ref, 500000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
val htlc = alice2bob.expectMsgType[UpdateAddHtlc]
assert(htlc.id == i && htlc.paymentHash == h)
@ -115,9 +115,9 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
val sender = TestProbe()
val h = randomBytes32()
val originHtlc = UpdateAddHtlc(channelId = randomBytes32(), id = 5656, amountMsat = 50000000 msat, cltvExpiry = CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), paymentHash = h, onionRoutingPacket = TestConstants.emptyOnionPacket, blinding_opt = None)
val originHtlc = UpdateAddHtlc(channelId = randomBytes32(), id = 5656, amountMsat = 50000000 msat, cltvExpiry = CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), paymentHash = h, onionRoutingPacket = TestConstants.emptyOnionPacket, blinding_opt = None, confidence = 1.0)
val origin = Origin.Hot(sender.ref, Upstream.Hot.Channel(originHtlc, TimestampMilli.now(), randomKey().publicKey))
val cmd = CMD_ADD_HTLC(sender.ref, originHtlc.amountMsat - 10_000.msat, h, originHtlc.cltvExpiry - CltvExpiryDelta(7), TestConstants.emptyOnionPacket, None, origin)
val cmd = CMD_ADD_HTLC(sender.ref, originHtlc.amountMsat - 10_000.msat, h, originHtlc.cltvExpiry - CltvExpiryDelta(7), TestConstants.emptyOnionPacket, None, 1.0, origin)
alice ! cmd
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
val htlc = alice2bob.expectMsgType[UpdateAddHtlc]
@ -133,10 +133,10 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
val sender = TestProbe()
val h = randomBytes32()
val originHtlc1 = UpdateAddHtlc(randomBytes32(), 47, 30000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None)
val originHtlc2 = UpdateAddHtlc(randomBytes32(), 32, 20000000 msat, h, CltvExpiryDelta(160).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None)
val originHtlc1 = UpdateAddHtlc(randomBytes32(), 47, 30000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0)
val originHtlc2 = UpdateAddHtlc(randomBytes32(), 32, 20000000 msat, h, CltvExpiryDelta(160).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0)
val origin = Origin.Hot(sender.ref, Upstream.Hot.Trampoline(Seq(originHtlc1, originHtlc2).map(htlc => Upstream.Hot.Channel(htlc, TimestampMilli.now(), randomKey().publicKey))))
val cmd = CMD_ADD_HTLC(sender.ref, originHtlc1.amountMsat + originHtlc2.amountMsat - 10000.msat, h, originHtlc2.cltvExpiry - CltvExpiryDelta(7), TestConstants.emptyOnionPacket, None, origin)
val cmd = CMD_ADD_HTLC(sender.ref, originHtlc1.amountMsat + originHtlc2.amountMsat - 10000.msat, h, originHtlc2.cltvExpiry - CltvExpiryDelta(7), TestConstants.emptyOnionPacket, None, 1.0, origin)
alice ! cmd
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
val htlc = alice2bob.expectMsgType[UpdateAddHtlc]
@ -152,7 +152,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val sender = TestProbe()
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
val expiryTooSmall = CltvExpiry(currentBlockHeight)
val add = CMD_ADD_HTLC(sender.ref, 500000000 msat, randomBytes32(), expiryTooSmall, TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 500000000 msat, randomBytes32(), expiryTooSmall, TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add
val error = ExpiryTooSmall(channelId(alice), CltvExpiry(currentBlockHeight + 3), expiryTooSmall, currentBlockHeight)
sender.expectMsg(RES_ADD_FAILED(add, error, Some(initialState.channelUpdate)))
@ -165,7 +165,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
val maxAllowedExpiryDelta = alice.underlyingActor.nodeParams.channelConf.maxExpiryDelta
val expiryTooBig = (maxAllowedExpiryDelta + 1).toCltvExpiry(currentBlockHeight)
val add = CMD_ADD_HTLC(sender.ref, 500000000 msat, randomBytes32(), expiryTooBig, TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 500000000 msat, randomBytes32(), expiryTooBig, TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add
val error = ExpiryTooBig(channelId(alice), maximum = maxAllowedExpiryDelta.toCltvExpiry(currentBlockHeight), actual = expiryTooBig, blockHeight = currentBlockHeight)
sender.expectMsg(RES_ADD_FAILED(add, error, Some(initialState.channelUpdate)))
@ -176,7 +176,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
import f._
val sender = TestProbe()
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
val add = CMD_ADD_HTLC(sender.ref, 50 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 50 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add
val error = HtlcValueTooSmall(channelId(alice), 1000 msat, 50 msat)
sender.expectMsg(RES_ADD_FAILED(add, error, Some(initialState.channelUpdate)))
@ -189,7 +189,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
// Alice has a minimum set to 0 msat (which should be invalid, but may mislead Bob into relaying 0-value HTLCs which is forbidden by the spec).
assert(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.params.localParams.htlcMinimum == 0.msat)
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
val add = CMD_ADD_HTLC(sender.ref, 0 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 0 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
bob ! add
val error = HtlcValueTooSmall(channelId(bob), 1 msat, 0 msat)
sender.expectMsg(RES_ADD_FAILED(add, error, Some(initialState.channelUpdate)))
@ -201,7 +201,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val sender = TestProbe()
// channel starts with all funds on alice's side, alice sends some funds to bob, but not enough to make it go above reserve
val h = randomBytes32()
val add = CMD_ADD_HTLC(sender.ref, 50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
}
@ -210,7 +210,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
import f._
val sender = TestProbe()
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
val add = CMD_ADD_HTLC(sender.ref, MilliSatoshi(Int.MaxValue), randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, MilliSatoshi(Int.MaxValue), randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add
val error = InsufficientFunds(channelId(alice), amount = MilliSatoshi(Int.MaxValue), missing = 1388843 sat, reserve = 20000 sat, fees = 8960 sat)
sender.expectMsg(RES_ADD_FAILED(add, error, Some(initialState.channelUpdate)))
@ -223,7 +223,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
// The anchor outputs commitment format costs more fees for the funder (bigger commit tx + cost of anchor outputs)
assert(initialState.commitments.availableBalanceForSend < initialState.commitments.modify(_.params.channelFeatures).setTo(ChannelFeatures()).availableBalanceForSend)
val add = CMD_ADD_HTLC(sender.ref, initialState.commitments.availableBalanceForSend + 1.msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, initialState.commitments.availableBalanceForSend + 1.msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add
val error = InsufficientFunds(channelId(alice), amount = add.amount, missing = 0 sat, reserve = 20000 sat, fees = 3900 sat)
@ -235,7 +235,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
import f._
val sender = TestProbe()
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
val add = CMD_ADD_HTLC(sender.ref, initialState.commitments.availableBalanceForSend + 1.msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, initialState.commitments.availableBalanceForSend + 1.msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
bob ! add
val error = InsufficientFunds(channelId(alice), amount = add.amount, missing = 0 sat, reserve = 10000 sat, fees = 0 sat)
@ -253,7 +253,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
// At this point alice has the minimal amount to sustain a channel.
// Alice maintains an extra reserve to accommodate for a few more HTLCs, so the first few HTLCs should be allowed.
val htlcs = (1 to 7).map { _ =>
bob ! CMD_ADD_HTLC(sender.ref, 12_000_000 msat, randomBytes32(), CltvExpiry(400144), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
bob ! CMD_ADD_HTLC(sender.ref, 12_000_000 msat, randomBytes32(), CltvExpiry(400144), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
val add = bob2alice.expectMsgType[UpdateAddHtlc]
bob2alice.forward(alice, add)
@ -261,7 +261,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
}
// But this one will dip alice below her reserve: we must wait for the previous HTLCs to settle before sending any more.
val failedAdd = CMD_ADD_HTLC(sender.ref, 11_000_000 msat, randomBytes32(), CltvExpiry(400144), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val failedAdd = CMD_ADD_HTLC(sender.ref, 11_000_000 msat, randomBytes32(), CltvExpiry(400144), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
bob ! failedAdd
val error = RemoteCannotAffordFeesForNewHtlc(channelId(bob), failedAdd.amount, missing = 1360 sat, 20_000 sat, 22_720 sat)
sender.expectMsg(RES_ADD_FAILED(failedAdd, error, Some(bob.stateData.asInstanceOf[DATA_NORMAL].channelUpdate)))
@ -285,13 +285,13 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
// At this point alice has the minimal amount to sustain a channel.
// Alice maintains an extra reserve to accommodate for a one more HTLCs, so the first few HTLCs should be allowed.
bob ! CMD_ADD_HTLC(sender.ref, 25_000_000 msat, randomBytes32(), CltvExpiry(400144), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
bob ! CMD_ADD_HTLC(sender.ref, 25_000_000 msat, randomBytes32(), CltvExpiry(400144), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
val add = bob2alice.expectMsgType[UpdateAddHtlc]
bob2alice.forward(alice, add)
// But this one will dip alice below her reserve: we must wait for the previous HTLCs to settle before sending any more.
val failedAdd = CMD_ADD_HTLC(sender.ref, 25_000_000 msat, randomBytes32(), CltvExpiry(400144), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val failedAdd = CMD_ADD_HTLC(sender.ref, 25_000_000 msat, randomBytes32(), CltvExpiry(400144), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
bob ! failedAdd
val error = RemoteCannotAffordFeesForNewHtlc(channelId(bob), failedAdd.amount, missing = 340 sat, 20_000 sat, 21_700 sat)
sender.expectMsg(RES_ADD_FAILED(failedAdd, error, Some(bob.stateData.asInstanceOf[DATA_NORMAL].channelUpdate)))
@ -306,16 +306,16 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
import f._
val sender = TestProbe()
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
alice ! CMD_ADD_HTLC(sender.ref, 500000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
alice ! CMD_ADD_HTLC(sender.ref, 500000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
alice2bob.expectMsgType[UpdateAddHtlc]
alice ! CMD_ADD_HTLC(sender.ref, 200000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
alice ! CMD_ADD_HTLC(sender.ref, 200000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
alice2bob.expectMsgType[UpdateAddHtlc]
alice ! CMD_ADD_HTLC(sender.ref, 51760000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
alice ! CMD_ADD_HTLC(sender.ref, 51760000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
alice2bob.expectMsgType[UpdateAddHtlc]
val add = CMD_ADD_HTLC(sender.ref, 1000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 1000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add
val error = InsufficientFunds(channelId(alice), amount = 1000000 msat, missing = 1000 sat, reserve = 20000 sat, fees = 12400 sat)
sender.expectMsg(RES_ADD_FAILED(add, error, Some(initialState.channelUpdate)))
@ -326,13 +326,13 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
import f._
val sender = TestProbe()
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
alice ! CMD_ADD_HTLC(sender.ref, 300000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
alice ! CMD_ADD_HTLC(sender.ref, 300000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
alice2bob.expectMsgType[UpdateAddHtlc]
alice ! CMD_ADD_HTLC(sender.ref, 300000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
alice ! CMD_ADD_HTLC(sender.ref, 300000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
alice2bob.expectMsgType[UpdateAddHtlc]
val add = CMD_ADD_HTLC(sender.ref, 500000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 500000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add
val error = InsufficientFunds(channelId(alice), amount = 500000000 msat, missing = 348240 sat, reserve = 20000 sat, fees = 12400 sat)
sender.expectMsg(RES_ADD_FAILED(add, error, Some(initialState.channelUpdate)))
@ -345,7 +345,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
assert(initialState.commitments.params.localParams.maxHtlcValueInFlightMsat == initialState.commitments.latest.capacity.toMilliSatoshi)
assert(initialState.commitments.params.remoteParams.maxHtlcValueInFlightMsat == UInt64(150000000))
val add = CMD_ADD_HTLC(sender.ref, 151000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 151000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
bob ! add
val error = HtlcValueTooHighInFlight(channelId(bob), maximum = 150000000 msat, actual = 151000000 msat)
sender.expectMsg(RES_ADD_FAILED(add, error, Some(initialState.channelUpdate)))
@ -358,11 +358,11 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
assert(initialState.commitments.params.localParams.maxHtlcValueInFlightMsat == initialState.commitments.latest.capacity.toMilliSatoshi)
assert(initialState.commitments.params.remoteParams.maxHtlcValueInFlightMsat == UInt64(150000000))
val add = CMD_ADD_HTLC(sender.ref, 75500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 75500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
bob ! add
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
bob2alice.expectMsgType[UpdateAddHtlc]
val add1 = CMD_ADD_HTLC(sender.ref, 75500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add1 = CMD_ADD_HTLC(sender.ref, 75500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
bob ! add1
val error = HtlcValueTooHighInFlight(channelId(bob), maximum = 150000000 msat, actual = 151000000 msat)
sender.expectMsg(RES_ADD_FAILED(add1, error, Some(initialState.channelUpdate)))
@ -375,7 +375,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
assert(initialState.commitments.params.localParams.maxHtlcValueInFlightMsat == 150000000.msat)
assert(initialState.commitments.params.remoteParams.maxHtlcValueInFlightMsat == UInt64(initialState.commitments.latest.capacity.toMilliSatoshi.toLong))
val add = CMD_ADD_HTLC(sender.ref, 151000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 151000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add
val error = HtlcValueTooHighInFlight(channelId(alice), maximum = 150000000 msat, actual = 151000000 msat)
sender.expectMsg(RES_ADD_FAILED(add, error, Some(initialState.channelUpdate)))
@ -389,11 +389,11 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
assert(initialState.commitments.params.localParams.maxAcceptedHtlcs == 100)
assert(initialState.commitments.params.remoteParams.maxAcceptedHtlcs == 30) // Bob accepts a maximum of 30 htlcs
for (_ <- 0 until 30) {
alice ! CMD_ADD_HTLC(sender.ref, 10000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
alice ! CMD_ADD_HTLC(sender.ref, 10000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
alice2bob.expectMsgType[UpdateAddHtlc]
}
val add = CMD_ADD_HTLC(sender.ref, 10000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 10000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add
val error = TooManyAcceptedHtlcs(channelId(alice), maximum = 30)
sender.expectMsg(RES_ADD_FAILED(add, error, Some(initialState.channelUpdate)))
@ -407,11 +407,11 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
assert(initialState.commitments.params.localParams.maxAcceptedHtlcs == 30) // Bob accepts a maximum of 30 htlcs
assert(initialState.commitments.params.remoteParams.maxAcceptedHtlcs == 100) // Alice accepts more, but Bob will stop at 30 HTLCs
for (_ <- 0 until 30) {
bob ! CMD_ADD_HTLC(sender.ref, 500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
bob ! CMD_ADD_HTLC(sender.ref, 500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
bob2alice.expectMsgType[UpdateAddHtlc]
}
val add = CMD_ADD_HTLC(sender.ref, 500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
bob ! add
val error = TooManyAcceptedHtlcs(channelId(bob), maximum = 30)
sender.expectMsg(RES_ADD_FAILED(add, error, Some(initialState.channelUpdate)))
@ -444,18 +444,18 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
crossSign(bob, alice, bob2alice, alice2bob)
// HTLCs that take Alice's dust exposure above her threshold are rejected.
val dustAdd = CMD_ADD_HTLC(sender.ref, 501.sat.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val dustAdd = CMD_ADD_HTLC(sender.ref, 501.sat.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! dustAdd
sender.expectMsg(RES_ADD_FAILED(dustAdd, LocalDustHtlcExposureTooHigh(channelId(alice), 25000.sat, 25001.sat.toMilliSatoshi), Some(initialState.channelUpdate)))
val trimmedAdd = CMD_ADD_HTLC(sender.ref, 5000.sat.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val trimmedAdd = CMD_ADD_HTLC(sender.ref, 5000.sat.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! trimmedAdd
sender.expectMsg(RES_ADD_FAILED(trimmedAdd, LocalDustHtlcExposureTooHigh(channelId(alice), 25000.sat, 29500.sat.toMilliSatoshi), Some(initialState.channelUpdate)))
val justAboveTrimmedAdd = CMD_ADD_HTLC(sender.ref, 8500.sat.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val justAboveTrimmedAdd = CMD_ADD_HTLC(sender.ref, 8500.sat.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! justAboveTrimmedAdd
sender.expectMsg(RES_ADD_FAILED(justAboveTrimmedAdd, LocalDustHtlcExposureTooHigh(channelId(alice), 25000.sat, 33000.sat.toMilliSatoshi), Some(initialState.channelUpdate)))
// HTLCs that don't contribute to dust exposure are accepted.
alice ! CMD_ADD_HTLC(sender.ref, 25000.sat.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
alice ! CMD_ADD_HTLC(sender.ref, 25000.sat.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
alice2bob.expectMsgType[UpdateAddHtlc]
}
@ -481,7 +481,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
addHtlc(1500.sat.toMilliSatoshi, alice, bob, alice2bob, bob2alice)
// HTLCs that take Alice's dust exposure above her threshold are rejected.
val add = CMD_ADD_HTLC(sender.ref, 1001.sat.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 1001.sat.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add
sender.expectMsg(RES_ADD_FAILED(add, LocalDustHtlcExposureTooHigh(channelId(alice), 25000.sat, 25001.sat.toMilliSatoshi), Some(initialState.channelUpdate)))
}
@ -505,7 +505,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
(1 to 3).foreach(_ => addHtlc(1050.sat.toMilliSatoshi, alice, bob, alice2bob, bob2alice))
// HTLCs that take Alice's dust exposure above her threshold are rejected.
val add = CMD_ADD_HTLC(sender.ref, 1050.sat.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 1050.sat.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add
sender.expectMsg(RES_ADD_FAILED(add, LocalDustHtlcExposureTooHigh(channelId(alice), 25000.sat, 25200.sat.toMilliSatoshi), Some(initialState.channelUpdate)))
}
@ -529,7 +529,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
(1 to 8).foreach(_ => addHtlc(1050.sat.toMilliSatoshi, bob, alice, bob2alice, alice2bob))
// HTLCs that take Bob's dust exposure above his threshold are rejected.
val add = CMD_ADD_HTLC(sender.ref, 1050.sat.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 1050.sat.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
bob ! add
sender.expectMsg(RES_ADD_FAILED(add, RemoteDustHtlcExposureTooHigh(channelId(bob), 30000.sat, 30450.sat.toMilliSatoshi), Some(initialState.channelUpdate)))
}
@ -538,14 +538,14 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
import f._
val sender = TestProbe()
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
val add1 = CMD_ADD_HTLC(sender.ref, TestConstants.fundingSatoshis.toMilliSatoshi * 2 / 3, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add1 = CMD_ADD_HTLC(sender.ref, TestConstants.fundingSatoshis.toMilliSatoshi * 2 / 3, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add1
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
alice2bob.expectMsgType[UpdateAddHtlc]
alice ! CMD_SIGN()
alice2bob.expectMsgType[CommitSig]
// this is over channel-capacity
val add2 = CMD_ADD_HTLC(sender.ref, TestConstants.fundingSatoshis.toMilliSatoshi * 2 / 3, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add2 = CMD_ADD_HTLC(sender.ref, TestConstants.fundingSatoshis.toMilliSatoshi * 2 / 3, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add2
val error = InsufficientFunds(channelId(alice), add2.amount, 578133 sat, 20000 sat, 10680 sat)
sender.expectMsg(RES_ADD_FAILED(add2, error, Some(initialState.channelUpdate)))
@ -562,7 +562,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
val upstream = localOrigin(sender.ref)
val add = CMD_ADD_HTLC(sender.ref, 500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, upstream)
val add = CMD_ADD_HTLC(sender.ref, 500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, upstream)
bob ! add
val error = FeerateTooDifferent(channelId(bob), FeeratePerKw(20000 sat), FeeratePerKw(10000 sat))
sender.expectMsg(RES_ADD_FAILED(add, error, Some(initialState.channelUpdate)))
@ -587,7 +587,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].localShutdown.isDefined && alice.stateData.asInstanceOf[DATA_NORMAL].remoteShutdown.isEmpty)
// actual test starts here
val add = CMD_ADD_HTLC(sender.ref, 500000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 500000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add
val error = NoMoreHtlcsClosingInProgress(channelId(alice))
sender.expectMsg(RES_ADD_FAILED(add, error, Some(initialState.channelUpdate)))
@ -599,14 +599,14 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val sender = TestProbe()
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
// let's make alice send an htlc
val add1 = CMD_ADD_HTLC(sender.ref, 50000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add1 = CMD_ADD_HTLC(sender.ref, 50000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add1
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
// at the same time bob initiates a closing
bob ! CMD_CLOSE(sender.ref, None, None)
sender.expectMsgType[RES_SUCCESS[CMD_CLOSE]]
// this command will be received by alice right after having received the shutdown
val add2 = CMD_ADD_HTLC(sender.ref, 10000000 msat, randomBytes32(), CltvExpiry(300000), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add2 = CMD_ADD_HTLC(sender.ref, 10000000 msat, randomBytes32(), CltvExpiry(300000), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
// messages cross
alice2bob.expectMsgType[UpdateAddHtlc]
alice2bob.forward(bob)
@ -620,7 +620,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
test("recv UpdateAddHtlc") { f =>
import f._
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, 150000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None)
val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, 150000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0)
bob ! htlc
awaitCond(bob.stateData == initialState
.modify(_.commitments.changes.remoteChanges.proposed).using(_ :+ htlc)
@ -632,7 +632,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
test("recv UpdateAddHtlc (unexpected id)") { f =>
import f._
val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx
val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 42, 150000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None)
val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 42, 150000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0)
bob ! htlc.copy(id = 0)
bob ! htlc.copy(id = 1)
bob ! htlc.copy(id = 2)
@ -649,7 +649,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
test("recv UpdateAddHtlc (value too small)") { f =>
import f._
val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx
val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, 150 msat, randomBytes32(), cltvExpiry = CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None)
val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, 150 msat, randomBytes32(), cltvExpiry = CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0)
alice2bob.forward(bob, htlc)
val error = bob2alice.expectMsgType[Error]
assert(new String(error.data.toArray) == HtlcValueTooSmall(channelId(bob), minimum = 1000 msat, actual = 150 msat).getMessage)
@ -664,7 +664,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
test("recv UpdateAddHtlc (insufficient funds)") { f =>
import f._
val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx
val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(Long.MaxValue), randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None)
val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(Long.MaxValue), randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0)
alice2bob.forward(bob, htlc)
val error = bob2alice.expectMsgType[Error]
assert(new String(error.data.toArray) == InsufficientFunds(channelId(bob), amount = MilliSatoshi(Long.MaxValue), missing = 9223372036083735L sat, reserve = 20000 sat, fees = 8960 sat).getMessage)
@ -679,9 +679,9 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
test("recv UpdateAddHtlc (insufficient funds w/ pending htlcs) (anchor outputs)", Tag(ChannelStateTestsTags.AnchorOutputs)) { f =>
import f._
val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, 400000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None))
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 1, 300000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None))
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 2, 100000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None))
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, 400000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0))
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 1, 300000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0))
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 2, 100000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0))
val error = bob2alice.expectMsgType[Error]
assert(new String(error.data.toArray) == InsufficientFunds(channelId(bob), amount = 100000000 msat, missing = 24760 sat, reserve = 20000 sat, fees = 4760 sat).getMessage)
awaitCond(bob.stateName == CLOSING)
@ -693,10 +693,10 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
test("recv UpdateAddHtlc (insufficient funds w/ pending htlcs 1/2)") { f =>
import f._
val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, 400000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None))
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 1, 200000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None))
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 2, 167600000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None))
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 3, 10000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None))
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, 400000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0))
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 1, 200000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0))
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 2, 167600000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0))
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 3, 10000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0))
val error = bob2alice.expectMsgType[Error]
assert(new String(error.data.toArray) == InsufficientFunds(channelId(bob), amount = 10000000 msat, missing = 11720 sat, reserve = 20000 sat, fees = 14120 sat).getMessage)
awaitCond(bob.stateName == CLOSING)
@ -710,9 +710,9 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
test("recv UpdateAddHtlc (insufficient funds w/ pending htlcs 2/2)") { f =>
import f._
val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, 300000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None))
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 1, 300000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None))
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 2, 500000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None))
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, 300000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0))
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 1, 300000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0))
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 2, 500000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0))
val error = bob2alice.expectMsgType[Error]
assert(new String(error.data.toArray) == InsufficientFunds(channelId(bob), amount = 500000000 msat, missing = 332400 sat, reserve = 20000 sat, fees = 12400 sat).getMessage)
awaitCond(bob.stateName == CLOSING)
@ -726,7 +726,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
test("recv UpdateAddHtlc (over max inflight htlc value)", Tag(ChannelStateTestsTags.AliceLowMaxHtlcValueInFlight)) { f =>
import f._
val tx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx
alice2bob.forward(alice, UpdateAddHtlc(ByteVector32.Zeroes, 0, 151000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None))
alice2bob.forward(alice, UpdateAddHtlc(ByteVector32.Zeroes, 0, 151000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0))
val error = alice2bob.expectMsgType[Error]
assert(new String(error.data.toArray) == HtlcValueTooHighInFlight(channelId(alice), maximum = 150000000 msat, actual = 151000000 msat).getMessage)
awaitCond(alice.stateName == CLOSING)
@ -742,9 +742,9 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx
// Bob accepts a maximum of 30 htlcs
for (i <- 0 until 30) {
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, i, 1000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None))
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, i, 1000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0))
}
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 30, 1000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None))
alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 30, 1000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0))
val error = bob2alice.expectMsgType[Error]
assert(new String(error.data.toArray) == TooManyAcceptedHtlcs(channelId(bob), maximum = 30).getMessage)
awaitCond(bob.stateName == CLOSING)
@ -767,7 +767,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
test("recv CMD_SIGN (two identical htlcs in each direction)") { f =>
import f._
val sender = TestProbe()
val add = CMD_ADD_HTLC(sender.ref, 10000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 10000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
alice2bob.expectMsgType[UpdateAddHtlc]
@ -818,19 +818,19 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
assert(a2b_2 > aliceMinOffer && a2b_2 > bobMinReceive)
assert(b2a_1 > aliceMinReceive && b2a_1 > bobMinOffer)
assert(b2a_2 < aliceMinReceive && b2a_2 > bobMinOffer)
alice ! CMD_ADD_HTLC(sender.ref, a2b_1.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
alice ! CMD_ADD_HTLC(sender.ref, a2b_1.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
alice2bob.expectMsgType[UpdateAddHtlc]
alice2bob.forward(bob)
alice ! CMD_ADD_HTLC(sender.ref, a2b_2.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
alice ! CMD_ADD_HTLC(sender.ref, a2b_2.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
alice2bob.expectMsgType[UpdateAddHtlc]
alice2bob.forward(bob)
bob ! CMD_ADD_HTLC(sender.ref, b2a_1.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
bob ! CMD_ADD_HTLC(sender.ref, b2a_1.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
bob2alice.expectMsgType[UpdateAddHtlc]
bob2alice.forward(alice)
bob ! CMD_ADD_HTLC(sender.ref, b2a_2.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
bob ! CMD_ADD_HTLC(sender.ref, b2a_2.toMilliSatoshi, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
bob2alice.expectMsgType[UpdateAddHtlc]
bob2alice.forward(alice)
@ -850,7 +850,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
test("recv CMD_SIGN (htlcs with same pubkeyScript but different amounts)") { f =>
import f._
val sender = TestProbe()
val add = CMD_ADD_HTLC(sender.ref, 10000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 10000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
val epsilons = List(3, 1, 5, 7, 6) // unordered on purpose
val htlcCount = epsilons.size
for (i <- epsilons) {
@ -1184,12 +1184,12 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val r = randomBytes32()
val h = Crypto.sha256(r)
alice ! CMD_ADD_HTLC(sender.ref, 50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
alice ! CMD_ADD_HTLC(sender.ref, 50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
val htlc1 = alice2bob.expectMsgType[UpdateAddHtlc]
alice2bob.forward(bob)
alice ! CMD_ADD_HTLC(sender.ref, 50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
alice ! CMD_ADD_HTLC(sender.ref, 50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
val htlc2 = alice2bob.expectMsgType[UpdateAddHtlc]
alice2bob.forward(bob)
@ -2271,7 +2271,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
bob2alice.expectNoMessage(250 millis) // we don't close because the commitment doesn't contain any HTLC
// when we try to add an HTLC, we still disagree on the feerate so we close
alice2bob.send(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, 2500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None))
alice2bob.send(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, 2500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0))
val error = bob2alice.expectMsgType[Error]
assert(new String(error.data.toArray).contains("local/remote feerates are too different"))
awaitCond(bob.stateName == CLOSING)
@ -2299,7 +2299,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
assert(initialState.commitments.latest.localCommit.spec.commitTxFeerate == TestConstants.anchorOutputsFeeratePerKw)
val add = UpdateAddHtlc(ByteVector32.Zeroes, 0, 2500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(ByteVector32.Zeroes, 0, 2500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0)
alice2bob.send(bob, add)
val fee = UpdateFee(initialState.channelId, FeeratePerKw(FeeratePerByte(2 sat)))
alice2bob.send(bob, fee)
@ -3067,7 +3067,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
bob2alice.expectNoMessage(250 millis) // we don't close because the commitment doesn't contain any HTLC
// when we try to add an HTLC, we still disagree on the feerate so we close
alice2bob.send(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, 2500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None))
alice2bob.send(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, 2500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0))
bob2alice.expectMsgType[Error]
bob2blockchain.expectMsgType[PublishTx] // commit tx
bob2blockchain.expectMsgType[PublishTx] // main delayed
@ -3315,7 +3315,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
// alice = 800 000
// bob = 200 000
val add = CMD_ADD_HTLC(sender.ref, 10000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 10000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
alice2bob.expectMsgType[UpdateAddHtlc]

View file

@ -80,7 +80,7 @@ class OfflineStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
// |--- sig --X |
// | |
val sender = TestProbe()
alice ! CMD_ADD_HTLC(sender.ref, 1000000 msat, ByteVector32.Zeroes, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
alice ! CMD_ADD_HTLC(sender.ref, 1000000 msat, ByteVector32.Zeroes, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
val htlc = alice2bob.expectMsgType[UpdateAddHtlc]
// bob receives the htlc
alice2bob.forward(bob)
@ -134,7 +134,7 @@ class OfflineStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
// | X-- rev ---|
// | X-- sig ---|
val sender = TestProbe()
alice ! CMD_ADD_HTLC(ActorRef.noSender, 1000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
alice ! CMD_ADD_HTLC(ActorRef.noSender, 1000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
val htlc = alice2bob.expectMsgType[UpdateAddHtlc]
// bob receives the htlc and the signature
alice2bob.forward(bob, htlc)
@ -178,7 +178,7 @@ class OfflineStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
// |<--- rev ---|
// | X-- sig ---|
val sender = TestProbe()
alice ! CMD_ADD_HTLC(ActorRef.noSender, 1000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
alice ! CMD_ADD_HTLC(ActorRef.noSender, 1000000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
val htlc = alice2bob.expectMsgType[UpdateAddHtlc]
// bob receives the htlc and the signature
alice2bob.forward(bob, htlc)
@ -511,7 +511,7 @@ class OfflineStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
channelUpdateListener.expectNoMessage(300 millis)
// we attempt to send a payment
alice ! CMD_ADD_HTLC(sender.ref, 4200 msat, randomBytes32(), CltvExpiry(123456), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
alice ! CMD_ADD_HTLC(sender.ref, 4200 msat, randomBytes32(), CltvExpiry(123456), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
sender.expectMsgType[RES_ADD_FAILED[ChannelUnavailable]]
// alice will broadcast a new disabled channel_update

View file

@ -60,7 +60,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit
// alice sends an HTLC to bob
val h1 = Crypto.sha256(r1)
val recipient1 = SpontaneousRecipient(TestConstants.Bob.nodeParams.nodeId, 300_000_000 msat, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), r1)
val Right(cmd1) = OutgoingPaymentPacket.buildOutgoingPayment(localOrigin(sender.ref), h1, makeSingleHopRoute(recipient1.totalAmount, recipient1.nodeId), recipient1).map(_.cmd.copy(commit = false))
val Right(cmd1) = OutgoingPaymentPacket.buildOutgoingPayment(localOrigin(sender.ref), h1, makeSingleHopRoute(recipient1.totalAmount, recipient1.nodeId), recipient1, 1.0).map(_.cmd.copy(commit = false))
alice ! cmd1
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
val htlc1 = alice2bob.expectMsgType[UpdateAddHtlc]
@ -69,7 +69,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit
// alice sends another HTLC to bob
val h2 = Crypto.sha256(r2)
val recipient2 = SpontaneousRecipient(TestConstants.Bob.nodeParams.nodeId, 200_000_000 msat, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), r2)
val Right(cmd2) = OutgoingPaymentPacket.buildOutgoingPayment(localOrigin(sender.ref), h2, makeSingleHopRoute(recipient2.totalAmount, recipient2.nodeId), recipient2).map(_.cmd.copy(commit = false))
val Right(cmd2) = OutgoingPaymentPacket.buildOutgoingPayment(localOrigin(sender.ref), h2, makeSingleHopRoute(recipient2.totalAmount, recipient2.nodeId), recipient2, 1.0).map(_.cmd.copy(commit = false))
alice ! cmd2
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
val htlc2 = alice2bob.expectMsgType[UpdateAddHtlc]
@ -141,7 +141,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit
test("recv CMD_ADD_HTLC") { f =>
import f._
val sender = TestProbe()
val add = CMD_ADD_HTLC(sender.ref, 500000000 msat, r1, cltvExpiry = CltvExpiry(300000), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 500000000 msat, r1, cltvExpiry = CltvExpiry(300000), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add
val error = ChannelUnavailable(channelId(alice))
sender.expectMsg(RES_ADD_FAILED(add, error, None))

View file

@ -123,7 +123,7 @@ class NegotiatingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
aliceClose(f)
alice2bob.expectMsgType[ClosingSigned]
val sender = TestProbe()
val add = CMD_ADD_HTLC(sender.ref, 5000000000L msat, randomBytes32(), CltvExpiry(300000), TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 5000000000L msat, randomBytes32(), CltvExpiry(300000), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add
val error = ChannelUnavailable(channelId(alice))
sender.expectMsg(RES_ADD_FAILED(add, error, None))

View file

@ -267,7 +267,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
// actual test starts here
val sender = TestProbe()
val add = CMD_ADD_HTLC(sender.ref, 500000000 msat, ByteVector32(ByteVector.fill(32)(1)), cltvExpiry = CltvExpiry(300000), onion = TestConstants.emptyOnionPacket, None, localOrigin(sender.ref))
val add = CMD_ADD_HTLC(sender.ref, 500000000 msat, ByteVector32(ByteVector.fill(32)(1)), cltvExpiry = CltvExpiry(300000), onion = TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref))
alice ! add
val error = ChannelUnavailable(channelId(alice))
sender.expectMsg(RES_ADD_FAILED(add, error, None))

View file

@ -57,7 +57,7 @@ class SynchronizationPipe(latch: CountDownLatch) extends Actor with ActorLogging
(script: @unchecked) match {
case offer(x, amount, rhash) :: rest =>
resolve(x) ! CMD_ADD_HTLC(self, MilliSatoshi(amount.toInt), ByteVector32.fromValidHex(rhash), CltvExpiry(144), TestConstants.emptyOnionPacket, None, Origin.Hot(self, Upstream.Local(UUID.randomUUID())))
resolve(x) ! CMD_ADD_HTLC(self, MilliSatoshi(amount.toInt), ByteVector32.fromValidHex(rhash), CltvExpiry(144), TestConstants.emptyOnionPacket, None, 1.0, Origin.Hot(self, Upstream.Local(UUID.randomUUID())))
exec(rest, a, b)
case fulfill(x, id, r) :: rest =>
resolve(x) ! CMD_FULFILL_HTLC(id.toInt, ByteVector32.fromValidHex(r))

View file

@ -253,7 +253,8 @@ class JsonSerializersSpec extends TestKitBaseClass with AnyFunSuiteLike with Mat
payload = hex"3c7a66997c681a3de1bae56438abeee4fc50a16554725a430ade1dc8db6bdd76704d45c6151c4051d710cf487e63f8cbe9f5e537e6e518d7998f11c40277d551bee036d227a421f344c0185b4533660d200acbc4f4aa6148e29f813bb537e950a79ba961b80ccaa6ad808cb88f858ee73f8b1f129d3214d194f76fc011c46e18c2caec0fcb69715e79cb381e449e5be20281e0aaa92defa089c98b7e29c22181f2d1af9f5fe2a37ede4ac3163b123aa72318201b1128b17053c381e7bf111620dfb7ea3dcc5c28cafdd9cb7bb1a4202b64199aa02af145c563ba1b9f10288203ce2666f486aacb2ee385dc0b67915864c95174dfaac1e0ea195329d1741cd1febb4b49b33f84e0d10a5ec8ee0a1a94f73abf081ec69bb863edfeb46d24ce424b025ac3f593a7ba9419ec9b17fb39f0fbeac80e0898f95b6709cbc95f7c097e22e3a6ca0efbc947bbcd4a9077f6bd9daba25b18bb16179fca9d9cb2ce49fc7cbd2de589237926cacb87ea60e2cc60a90b47575517921b5529b8a95823dd0c3d02a7747d74c4ca927ba6b70c06c1c1ef27e14d371e8dd8f5d9380a65b08ae1e6384f9b3575c5d7278de22ce80e63612a27f3b3f45dbe32ee855185293c719e5a7203a682a08fd810c46fa12b67e61349831f8fae3f558090ea988e1a22ec877b790ea09169055529247c4dd597857aad74eaeb3a5879e96453e681e213f2796ed704d620509f34f91d9d16f881fd397e2836a0a4d2f1bcd230067f7acb5381a2b17e8c5135e38c4d258afbe4f69ac7ad39b789e99686ee926b3ad31b98993673313b7b18a4faaea238d8055824fde7339a791fc7777ef28cc4a1a5d177b3c3882ced4921c6cd85ae82e1fe13fe680ae432a9918ce37b15f88d4d18fb16b69e5369d18c204aaf7ee49b830bf2328380e6ad96e8f6a9e01bc2c97ffbaa402d5406dc7b83c6eb5d515ffc3bea8c42cf299c9e2bea693515246c6ff859d33ba6c4da4c4c1706e0c6b4a574e927f02eb92b7d56722cff80c3e6f6b98d1c84cb576abdcc27a6bc7b110fc2ac5fead57f05ad854c3331ce1ff94c0dc97303540ee797d71566497af09f20e3554d467528e1fed8e69438171072fe2deca3979a8f5ec9043b9bc4da921b095c29dc0294148c1b7001dafda4c48600d1194f745e6d0689c561bf19d20758c4d25fac64d81780607a4106e220ef546fc4026af7b9da8defb2fe3c21d48798ac67c794fb40aabe44618a8911673466be06808c6f54a772b87bcfafb4d120a9bebffb8051bf24bb332eaa769cf175c1aadb0186f8946dc32513fd81fe1a61bfb860886bdd070359a43e06e74607d300bd2e01a3f1ee900c4039e8db742170228db61ef0c77724c49d1573144564a80cc1ebc0449b34f84be35187ceba3fbc2facf5ad1f1e15945e3c6c236579aca7bc97e4cc76a3310022693b64008562b254a7d11c0086813e551c4817bbb72a1d6fbfc84d326ce973651200f80aa8ab0976c53c390249ca8e7e5ec21b80e70c3e0205983d313b28a5d1b9d9149501e05d3257c8ae88c6308e9e00feeab19121d1032a582e68ca1f9f64a1fd91cb5d8613b985fd4be22a4d5c14a132c20811a75ee3cc61de0b3fbc3254d61995d086603032269888b942ec0971ad26ea4b8df1746c5ec1de904ddeb5045abc0a6ede9d6a199ed0782cb69efa3a4dc00747553dbef12fb8299ca364b4cdf2ac3eba03b0d8b273684116bba4458b5717bc4aca5406901173a89b3643ccc076f22779fccf1ad69981e24eef18c711a2d58dfe5834b41f9e7166d54dc8628e754baaca1cbb7db8256f88ebc889de6078ba83a1af14a4",
hmac = ByteVector32(hex"9442626f72c475963dbddf8a57ab2cef3013eb3d6a5e8afbea9e631dac4481f5")
),
blinding_opt = None
blinding_opt = None,
confidence = 0.7,
)
val expectedIn = """{"direction":"IN","id":926,"amountMsat":12365,"paymentHash":"9fcd45bbaa09c60c991ac0425704163c3f3d2d683c789fa409455b9c97792692","cltvExpiry":621500}"""

View file

@ -96,7 +96,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
}
def createBlindedPacket(amount: MilliSatoshi, paymentHash: ByteVector32, expiry: CltvExpiry, finalExpiry: CltvExpiry, pathId: ByteVector): IncomingPaymentPacket.FinalPacket = {
val add = UpdateAddHtlc(ByteVector32.One, 0, amount, paymentHash, expiry, TestConstants.emptyOnionPacket, Some(randomKey().publicKey))
val add = UpdateAddHtlc(ByteVector32.One, 0, amount, paymentHash, expiry, TestConstants.emptyOnionPacket, Some(randomKey().publicKey), 1.0)
val payload = FinalPayload.Blinded(TlvStream(AmountToForward(amount), TotalAmount(amount), OutgoingCltv(finalExpiry), EncryptedRecipientData(hex"deadbeef")), TlvStream(PathId(pathId), PaymentConstraints(CltvExpiry(500_000), 1 msat)))
IncomingPaymentPacket.FinalPacket(add, payload)
}
@ -115,7 +115,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
assert(!incoming.get.invoice.isExpired())
assert(Crypto.sha256(incoming.get.paymentPreimage) == invoice.paymentHash)
val add = UpdateAddHtlc(ByteVector32.One, 1, amountMsat, invoice.paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(ByteVector32.One, 1, amountMsat, invoice.paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
sender.send(handlerWithoutMpp, IncomingPaymentPacket.FinalPacket(add, FinalPayload.Standard.createPayload(add.amountMsat, add.amountMsat, add.cltvExpiry, invoice.paymentSecret, invoice.paymentMetadata)))
assert(register.expectMsgType[Register.Forward[CMD_FULFILL_HTLC]].message.id == add.id)
@ -131,7 +131,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
sender.send(handlerWithoutMpp, ReceiveStandardPayment(sender.ref, Some(50_000 msat), Left("1 coffee with extra fees and expiry")))
val invoice = sender.expectMsgType[Bolt11Invoice]
val add = UpdateAddHtlc(ByteVector32.One, 1, 75_000 msat, invoice.paymentHash, defaultExpiry + CltvExpiryDelta(12), TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(ByteVector32.One, 1, 75_000 msat, invoice.paymentHash, defaultExpiry + CltvExpiryDelta(12), TestConstants.emptyOnionPacket, None, 1.0)
sender.send(handlerWithoutMpp, IncomingPaymentPacket.FinalPacket(add, FinalPayload.Standard.createPayload(70_000 msat, 70_000 msat, defaultExpiry, invoice.paymentSecret, invoice.paymentMetadata)))
assert(register.expectMsgType[Register.Forward[CMD_FULFILL_HTLC]].message.id == add.id)
@ -149,7 +149,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
assert(invoice.features.hasFeature(BasicMultiPartPayment))
assert(nodeParams.db.payments.getIncomingPayment(invoice.paymentHash).get.status == IncomingPaymentStatus.Pending)
val add = UpdateAddHtlc(ByteVector32.One, 2, amountMsat, invoice.paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(ByteVector32.One, 2, amountMsat, invoice.paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
sender.send(handlerWithMpp, IncomingPaymentPacket.FinalPacket(add, FinalPayload.Standard.createPayload(add.amountMsat, add.amountMsat, add.cltvExpiry, invoice.paymentSecret, invoice.paymentMetadata)))
assert(register.expectMsgType[Register.Forward[CMD_FULFILL_HTLC]].message.id == add.id)
@ -196,7 +196,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
val invoice = sender.expectMsgType[Bolt11Invoice]
assert(nodeParams.db.payments.getIncomingPayment(invoice.paymentHash).get.status == IncomingPaymentStatus.Pending)
val add = UpdateAddHtlc(ByteVector32.One, 0, amountMsat, invoice.paymentHash, CltvExpiryDelta(3).toCltvExpiry(nodeParams.currentBlockHeight), TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(ByteVector32.One, 0, amountMsat, invoice.paymentHash, CltvExpiryDelta(3).toCltvExpiry(nodeParams.currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0)
sender.send(handlerWithMpp, IncomingPaymentPacket.FinalPacket(add, FinalPayload.Standard.createPayload(add.amountMsat, add.amountMsat, add.cltvExpiry, invoice.paymentSecret, invoice.paymentMetadata)))
val cmd = register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message
assert(cmd.reason == Right(IncorrectOrUnknownPaymentDetails(amountMsat, nodeParams.currentBlockHeight)))
@ -356,7 +356,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
assert(!invoice.features.hasFeature(BasicMultiPartPayment))
assert(invoice.isExpired())
val add = UpdateAddHtlc(ByteVector32.One, 0, 1000 msat, invoice.paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(ByteVector32.One, 0, 1000 msat, invoice.paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
sender.send(handlerWithoutMpp, IncomingPaymentPacket.FinalPacket(add, FinalPayload.Standard.createPayload(add.amountMsat, add.amountMsat, add.cltvExpiry, invoice.paymentSecret, invoice.paymentMetadata)))
register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]]
val Some(incoming) = nodeParams.db.payments.getIncomingPayment(invoice.paymentHash)
@ -371,7 +371,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
assert(invoice.features.hasFeature(BasicMultiPartPayment))
assert(invoice.isExpired())
val add = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, invoice.paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, invoice.paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
sender.send(handlerWithMpp, IncomingPaymentPacket.FinalPacket(add, FinalPayload.Standard.createPayload(add.amountMsat, 1000 msat, add.cltvExpiry, invoice.paymentSecret, invoice.paymentMetadata)))
val cmd = register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message
assert(cmd.reason == Right(IncorrectOrUnknownPaymentDetails(1000 msat, nodeParams.currentBlockHeight)))
@ -386,7 +386,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
val invoice = sender.expectMsgType[Bolt11Invoice]
assert(!invoice.features.hasFeature(BasicMultiPartPayment))
val add = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, invoice.paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, invoice.paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
sender.send(handlerWithoutMpp, IncomingPaymentPacket.FinalPacket(add, FinalPayload.Standard.createPayload(add.amountMsat, 1000 msat, add.cltvExpiry, invoice.paymentSecret, invoice.paymentMetadata)))
val cmd = register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message
assert(cmd.reason == Right(IncorrectOrUnknownPaymentDetails(1000 msat, nodeParams.currentBlockHeight)))
@ -401,7 +401,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
assert(invoice.features.hasFeature(BasicMultiPartPayment))
val lowCltvExpiry = nodeParams.channelConf.fulfillSafetyBeforeTimeout.toCltvExpiry(nodeParams.currentBlockHeight)
val add = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, invoice.paymentHash, lowCltvExpiry, TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, invoice.paymentHash, lowCltvExpiry, TestConstants.emptyOnionPacket, None, 1.0)
sender.send(handlerWithMpp, IncomingPaymentPacket.FinalPacket(add, FinalPayload.Standard.createPayload(add.amountMsat, 1000 msat, add.cltvExpiry, invoice.paymentSecret, invoice.paymentMetadata)))
val cmd = register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message
assert(cmd.reason == Right(IncorrectOrUnknownPaymentDetails(1000 msat, nodeParams.currentBlockHeight)))
@ -415,7 +415,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
val invoice = sender.expectMsgType[Bolt11Invoice]
assert(invoice.features.hasFeature(BasicMultiPartPayment))
val add = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, invoice.paymentHash.reverse, defaultExpiry, TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, invoice.paymentHash.reverse, defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
sender.send(handlerWithMpp, IncomingPaymentPacket.FinalPacket(add, FinalPayload.Standard.createPayload(add.amountMsat, 1000 msat, add.cltvExpiry, invoice.paymentSecret, invoice.paymentMetadata)))
val cmd = register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message
assert(cmd.reason == Right(IncorrectOrUnknownPaymentDetails(1000 msat, nodeParams.currentBlockHeight)))
@ -429,7 +429,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
val invoice = sender.expectMsgType[Bolt11Invoice]
assert(invoice.features.hasFeature(BasicMultiPartPayment))
val add = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, invoice.paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, invoice.paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
sender.send(handlerWithMpp, IncomingPaymentPacket.FinalPacket(add, FinalPayload.Standard.createPayload(add.amountMsat, 999 msat, add.cltvExpiry, invoice.paymentSecret, invoice.paymentMetadata)))
val cmd = register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message
assert(cmd.reason == Right(IncorrectOrUnknownPaymentDetails(999 msat, nodeParams.currentBlockHeight)))
@ -443,7 +443,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
val invoice = sender.expectMsgType[Bolt11Invoice]
assert(invoice.features.hasFeature(BasicMultiPartPayment))
val add = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, invoice.paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, invoice.paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
sender.send(handlerWithMpp, IncomingPaymentPacket.FinalPacket(add, FinalPayload.Standard.createPayload(add.amountMsat, 2001 msat, add.cltvExpiry, invoice.paymentSecret, invoice.paymentMetadata)))
val cmd = register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message
assert(cmd.reason == Right(IncorrectOrUnknownPaymentDetails(2001 msat, nodeParams.currentBlockHeight)))
@ -458,7 +458,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
assert(invoice.features.hasFeature(BasicMultiPartPayment))
// Invalid payment secret.
val add = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, invoice.paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, invoice.paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
sender.send(handlerWithMpp, IncomingPaymentPacket.FinalPacket(add, FinalPayload.Standard.createPayload(add.amountMsat, 1000 msat, add.cltvExpiry, invoice.paymentSecret.reverse, invoice.paymentMetadata)))
val cmd = register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message
assert(cmd.reason == Right(IncorrectOrUnknownPaymentDetails(1000 msat, nodeParams.currentBlockHeight)))
@ -490,7 +490,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
sender.send(handlerWithRouteBlinding, ReceiveOfferPayment(sender.ref, nodeKey, invoiceReq, createEmptyReceivingRoute(), TestProbe().ref, randomBytes32(), randomBytes32()))
val invoice = sender.expectMsgType[CreateInvoiceActor.InvoiceCreated].invoice
val add = UpdateAddHtlc(ByteVector32.One, 0, 5000 msat, invoice.paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(ByteVector32.One, 0, 5000 msat, invoice.paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
sender.send(handlerWithMpp, IncomingPaymentPacket.FinalPacket(add, FinalPayload.Standard.createPayload(add.amountMsat, add.amountMsat, add.cltvExpiry, randomBytes32(), None)))
val cmd = register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message
assert(cmd.reason == Right(IncorrectOrUnknownPaymentDetails(5000 msat, nodeParams.currentBlockHeight)))
@ -571,13 +571,13 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
// Partial payment missing additional parts.
f.sender.send(handler, ReceiveStandardPayment(f.sender.ref, Some(1000 msat), Left("1 slow coffee")))
val pr1 = f.sender.expectMsgType[Bolt11Invoice]
val add1 = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, pr1.paymentHash, f.defaultExpiry, TestConstants.emptyOnionPacket, None)
val add1 = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, pr1.paymentHash, f.defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
f.sender.send(handler, IncomingPaymentPacket.FinalPacket(add1, FinalPayload.Standard.createPayload(add1.amountMsat, 1000 msat, add1.cltvExpiry, pr1.paymentSecret, pr1.paymentMetadata)))
// Partial payment exceeding the invoice amount, but incomplete because it promises to overpay.
f.sender.send(handler, ReceiveStandardPayment(f.sender.ref, Some(1500 msat), Left("1 slow latte")))
val pr2 = f.sender.expectMsgType[Bolt11Invoice]
val add2 = UpdateAddHtlc(ByteVector32.One, 1, 1600 msat, pr2.paymentHash, f.defaultExpiry, TestConstants.emptyOnionPacket, None)
val add2 = UpdateAddHtlc(ByteVector32.One, 1, 1600 msat, pr2.paymentHash, f.defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
f.sender.send(handler, IncomingPaymentPacket.FinalPacket(add2, FinalPayload.Standard.createPayload(add2.amountMsat, 2000 msat, add2.cltvExpiry, pr2.paymentSecret, pr2.paymentMetadata)))
awaitCond {
@ -596,7 +596,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
})
// Extraneous HTLCs should be failed.
f.sender.send(handler, MultiPartPaymentFSM.ExtraPaymentReceived(pr1.paymentHash, HtlcPart(1000 msat, UpdateAddHtlc(ByteVector32.One, 42, 200 msat, pr1.paymentHash, add1.cltvExpiry, add1.onionRoutingPacket, None)), Some(PaymentTimeout())))
f.sender.send(handler, MultiPartPaymentFSM.ExtraPaymentReceived(pr1.paymentHash, HtlcPart(1000 msat, UpdateAddHtlc(ByteVector32.One, 42, 200 msat, pr1.paymentHash, add1.cltvExpiry, add1.onionRoutingPacket, None, 1.0)), Some(PaymentTimeout())))
f.register.expectMsg(Register.Forward(null, ByteVector32.One, CMD_FAIL_HTLC(42, Right(PaymentTimeout()), commit = true)))
// The payment should still be pending in DB.
@ -612,10 +612,10 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
f.sender.send(handler, ReceiveStandardPayment(f.sender.ref, Some(1000 msat), Left("1 fast coffee"), paymentPreimage_opt = Some(preimage)))
val invoice = f.sender.expectMsgType[Bolt11Invoice]
val add1 = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, invoice.paymentHash, f.defaultExpiry, TestConstants.emptyOnionPacket, None)
val add1 = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, invoice.paymentHash, f.defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
f.sender.send(handler, IncomingPaymentPacket.FinalPacket(add1, FinalPayload.Standard.createPayload(add1.amountMsat, 1000 msat, add1.cltvExpiry, invoice.paymentSecret, invoice.paymentMetadata)))
// Invalid payment secret -> should be rejected.
val add2 = UpdateAddHtlc(ByteVector32.Zeroes, 42, 200 msat, invoice.paymentHash, f.defaultExpiry, TestConstants.emptyOnionPacket, None)
val add2 = UpdateAddHtlc(ByteVector32.Zeroes, 42, 200 msat, invoice.paymentHash, f.defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
f.sender.send(handler, IncomingPaymentPacket.FinalPacket(add2, FinalPayload.Standard.createPayload(add2.amountMsat, 1000 msat, add2.cltvExpiry, invoice.paymentSecret.reverse, invoice.paymentMetadata)))
val add3 = add2.copy(id = 43)
f.sender.send(handler, IncomingPaymentPacket.FinalPacket(add3, FinalPayload.Standard.createPayload(add3.amountMsat, 1000 msat, add3.cltvExpiry, invoice.paymentSecret, invoice.paymentMetadata)))
@ -637,7 +637,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
})
// Extraneous HTLCs should be fulfilled.
f.sender.send(handler, MultiPartPaymentFSM.ExtraPaymentReceived(invoice.paymentHash, HtlcPart(1000 msat, UpdateAddHtlc(ByteVector32.One, 44, 200 msat, invoice.paymentHash, add1.cltvExpiry, add1.onionRoutingPacket, None)), None))
f.sender.send(handler, MultiPartPaymentFSM.ExtraPaymentReceived(invoice.paymentHash, HtlcPart(1000 msat, UpdateAddHtlc(ByteVector32.One, 44, 200 msat, invoice.paymentHash, add1.cltvExpiry, add1.onionRoutingPacket, None, 1.0)), None))
f.register.expectMsg(Register.Forward(null, ByteVector32.One, CMD_FULFILL_HTLC(44, preimage, commit = true)))
assert(f.eventListener.expectMsgType[PaymentReceived].amount == 200.msat)
val received2 = nodeParams.db.payments.getIncomingPayment(invoice.paymentHash)
@ -655,9 +655,9 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
f.sender.send(handler, ReceiveStandardPayment(f.sender.ref, Some(1000 msat), Left("1 coffee with tip please"), paymentPreimage_opt = Some(preimage)))
val invoice = f.sender.expectMsgType[Bolt11Invoice]
val add1 = UpdateAddHtlc(randomBytes32(), 0, 1100 msat, invoice.paymentHash, f.defaultExpiry, TestConstants.emptyOnionPacket, None)
val add1 = UpdateAddHtlc(randomBytes32(), 0, 1100 msat, invoice.paymentHash, f.defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
f.sender.send(handler, IncomingPaymentPacket.FinalPacket(add1, FinalPayload.Standard.createPayload(add1.amountMsat, 1500 msat, add1.cltvExpiry, invoice.paymentSecret, invoice.paymentMetadata)))
val add2 = UpdateAddHtlc(randomBytes32(), 1, 500 msat, invoice.paymentHash, f.defaultExpiry, TestConstants.emptyOnionPacket, None)
val add2 = UpdateAddHtlc(randomBytes32(), 1, 500 msat, invoice.paymentHash, f.defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
f.sender.send(handler, IncomingPaymentPacket.FinalPacket(add2, FinalPayload.Standard.createPayload(add2.amountMsat, 1500 msat, add2.cltvExpiry, invoice.paymentSecret, invoice.paymentMetadata)))
f.register.expectMsgAllOf(
@ -682,7 +682,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
assert(invoice.features.hasFeature(BasicMultiPartPayment))
assert(invoice.paymentHash == Crypto.sha256(preimage))
val add1 = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, invoice.paymentHash, f.defaultExpiry, TestConstants.emptyOnionPacket, None)
val add1 = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, invoice.paymentHash, f.defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
f.sender.send(handler, IncomingPaymentPacket.FinalPacket(add1, FinalPayload.Standard.createPayload(add1.amountMsat, 1000 msat, add1.cltvExpiry, invoice.paymentSecret, invoice.paymentMetadata)))
f.register.expectMsg(Register.Forward(null, ByteVector32.One, CMD_FAIL_HTLC(0, Right(PaymentTimeout()), commit = true)))
awaitCond({
@ -690,9 +690,9 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
f.sender.expectMsgType[PendingPayments].paymentHashes.isEmpty
})
val add2 = UpdateAddHtlc(ByteVector32.One, 2, 300 msat, invoice.paymentHash, f.defaultExpiry, TestConstants.emptyOnionPacket, None)
val add2 = UpdateAddHtlc(ByteVector32.One, 2, 300 msat, invoice.paymentHash, f.defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
f.sender.send(handler, IncomingPaymentPacket.FinalPacket(add2, FinalPayload.Standard.createPayload(add2.amountMsat, 1000 msat, add2.cltvExpiry, invoice.paymentSecret, invoice.paymentMetadata)))
val add3 = UpdateAddHtlc(ByteVector32.Zeroes, 5, 700 msat, invoice.paymentHash, f.defaultExpiry, TestConstants.emptyOnionPacket, None)
val add3 = UpdateAddHtlc(ByteVector32.Zeroes, 5, 700 msat, invoice.paymentHash, f.defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
f.sender.send(handler, IncomingPaymentPacket.FinalPacket(add3, FinalPayload.Standard.createPayload(add3.amountMsat, 1000 msat, add3.cltvExpiry, invoice.paymentSecret, invoice.paymentMetadata)))
// the fulfill are not necessarily in the same order as the commands
@ -724,7 +724,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
assert(nodeParams.db.payments.getIncomingPayment(paymentHash).isEmpty)
val add = UpdateAddHtlc(ByteVector32.One, 0, amountMsat, paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(ByteVector32.One, 0, amountMsat, paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
sender.send(handlerWithKeySend, IncomingPaymentPacket.FinalPacket(add, payload))
register.expectMsgType[Register.Forward[CMD_FULFILL_HTLC]]
@ -745,7 +745,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
assert(nodeParams.db.payments.getIncomingPayment(paymentHash).isEmpty)
val add = UpdateAddHtlc(ByteVector32.One, 0, amountMsat, paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(ByteVector32.One, 0, amountMsat, paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
sender.send(handlerWithKeySend, IncomingPaymentPacket.FinalPacket(add, payload))
register.expectMsgType[Register.Forward[CMD_FULFILL_HTLC]]
@ -767,7 +767,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
assert(nodeParams.db.payments.getIncomingPayment(paymentHash).isEmpty)
val add = UpdateAddHtlc(ByteVector32.One, 0, amountMsat, paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(ByteVector32.One, 0, amountMsat, paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
sender.send(handlerWithMpp, IncomingPaymentPacket.FinalPacket(add, payload))
f.register.expectMsg(Register.Forward(null, add.channelId, CMD_FAIL_HTLC(add.id, Right(IncorrectOrUnknownPaymentDetails(42000 msat, nodeParams.currentBlockHeight)), commit = true)))
@ -781,7 +781,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
val paymentSecret = randomBytes32()
assert(nodeParams.db.payments.getIncomingPayment(paymentHash).isEmpty)
val add = UpdateAddHtlc(ByteVector32.One, 0, 1000 msat, paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(ByteVector32.One, 0, 1000 msat, paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
sender.send(handlerWithoutMpp, IncomingPaymentPacket.FinalPacket(add, FinalPayload.Standard.createPayload(add.amountMsat, add.amountMsat, add.cltvExpiry, paymentSecret, None)))
val cmd = register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message
assert(cmd.id == add.id)
@ -795,7 +795,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
val paymentSecret = randomBytes32()
assert(nodeParams.db.payments.getIncomingPayment(paymentHash).isEmpty)
val add = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
sender.send(handlerWithMpp, IncomingPaymentPacket.FinalPacket(add, FinalPayload.Standard.createPayload(add.amountMsat, 1000 msat, add.cltvExpiry, paymentSecret, Some(hex"012345"))))
val cmd = register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message
assert(cmd.id == add.id)
@ -809,7 +809,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
val paymentHash = Crypto.sha256(paymentPreimage)
assert(nodeParams.db.payments.getIncomingPayment(paymentHash).isEmpty)
val add = UpdateAddHtlc(ByteVector32.One, 0, 1000 msat, paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(ByteVector32.One, 0, 1000 msat, paymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0)
val invoice = Bolt11Invoice(Block.TestnetGenesisBlock.hash, None, paymentHash, randomKey(), Left("dummy"), CltvExpiryDelta(12))
val incomingPayment = IncomingStandardPayment(invoice, paymentPreimage, PaymentType.Standard, invoice.createdAt.toTimestampMilli, IncomingPaymentStatus.Pending)
val fulfill = DoFulfill(incomingPayment, MultiPartPaymentFSM.MultiPartPaymentSucceeded(paymentHash, Queue(HtlcPart(1000 msat, add))))

View file

@ -232,7 +232,7 @@ object MultiPartPaymentFSMSpec {
def htlcIdToChannelId(htlcId: Long) = ByteVector32(ByteVector.fromLong(htlcId).padLeft(32))
def createMultiPartHtlc(totalAmount: MilliSatoshi, htlcAmount: MilliSatoshi, htlcId: Long): HtlcPart = {
val htlc = UpdateAddHtlc(htlcIdToChannelId(htlcId), htlcId, htlcAmount, paymentHash, CltvExpiry(42), TestConstants.emptyOnionPacket, None)
val htlc = UpdateAddHtlc(htlcIdToChannelId(htlcId), htlcId, htlcAmount, paymentHash, CltvExpiry(42), TestConstants.emptyOnionPacket, None, 1.0)
HtlcPart(totalAmount, htlc)
}

View file

@ -66,7 +66,7 @@ class MultiPartPaymentLifecycleSpec extends TestKitBaseClass with FixtureAnyFunS
override def withFixture(test: OneArgTest): Outcome = {
val id = UUID.randomUUID()
val cfg = SendPaymentConfig(id, id, Some("42"), paymentHash, randomKey().publicKey, Upstream.Local(id), None, None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = true)
val cfg = SendPaymentConfig(id, id, Some("42"), paymentHash, randomKey().publicKey, Upstream.Local(id), None, None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = true, confidence = 1.0)
val nodeParams = TestConstants.Alice.nodeParams
val (childPayFsm, router, sender, eventListener, metricsListener) = (TestProbe(), TestProbe(), TestProbe(), TestProbe(), TestProbe())
val paymentHandler = TestFSMRef(new MultiPartPaymentLifecycle(nodeParams, cfg, publishPreimage = true, router.ref, FakePaymentFactory(childPayFsm)))

View file

@ -174,7 +174,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
val request = SendPaymentToRoute(finalAmount, invoice, Nil, route, None, None, None)
sender.send(initiator, request)
val payment = sender.expectMsgType[SendPaymentToRouteResponse]
payFsm.expectMsg(SendPaymentConfig(payment.paymentId, payment.parentId, None, paymentHash, c, Upstream.Local(payment.paymentId), Some(invoice), None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = false))
payFsm.expectMsg(SendPaymentConfig(payment.paymentId, payment.parentId, None, paymentHash, c, Upstream.Local(payment.paymentId), Some(invoice), None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = false, confidence = 1.0))
payFsm.expectMsg(PaymentLifecycle.SendPaymentToRoute(initiator, Left(route), ClearRecipient(invoice, finalAmount, finalExpiryDelta.toCltvExpiry(nodeParams.currentBlockHeight + 1), Set.empty)))
sender.send(initiator, GetPayment(PaymentIdentifier.PaymentUUID(payment.paymentId)))
@ -199,7 +199,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
assert(req.finalExpiry(nodeParams) == (finalExpiryDelta + 1).toCltvExpiry(nodeParams.currentBlockHeight))
sender.send(initiator, req)
val id = sender.expectMsgType[UUID]
payFsm.expectMsg(SendPaymentConfig(id, id, None, paymentHash, c, Upstream.Local(id), Some(invoice), None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = true))
payFsm.expectMsg(SendPaymentConfig(id, id, None, paymentHash, c, Upstream.Local(id), Some(invoice), None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = true, confidence = 1.0))
payFsm.expectMsg(PaymentLifecycle.SendPaymentToNode(initiator, ClearRecipient(invoice, finalAmount, req.finalExpiry(nodeParams), Set.empty), 1, nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams))
sender.send(initiator, GetPayment(PaymentIdentifier.PaymentUUID(id)))
@ -222,7 +222,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
val req = SendPaymentToNode(sender.ref, finalAmount + 100.msat, invoice, Nil, 1, routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams)
sender.send(initiator, req)
val id = sender.expectMsgType[UUID]
multiPartPayFsm.expectMsg(SendPaymentConfig(id, id, None, paymentHash, c, Upstream.Local(id), Some(invoice), None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = true))
multiPartPayFsm.expectMsg(SendPaymentConfig(id, id, None, paymentHash, c, Upstream.Local(id), Some(invoice), None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = true, confidence = 1.0))
multiPartPayFsm.expectMsg(SendMultiPartPayment(initiator, ClearRecipient(invoice, finalAmount + 100.msat, req.finalExpiry(nodeParams), Set.empty), 1, nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams))
sender.send(initiator, GetPayment(PaymentIdentifier.PaymentUUID(id)))
@ -246,7 +246,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
val req = SendPaymentToNode(sender.ref, finalAmount, invoice, Nil, 1, routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams)
sender.send(initiator, req)
val id = sender.expectMsgType[UUID]
multiPartPayFsm.expectMsg(SendPaymentConfig(id, id, None, paymentHash, c, Upstream.Local(id), Some(invoice), None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = true))
multiPartPayFsm.expectMsg(SendPaymentConfig(id, id, None, paymentHash, c, Upstream.Local(id), Some(invoice), None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = true, confidence = 1.0))
val payment = multiPartPayFsm.expectMsgType[SendMultiPartPayment]
val expiry = payment.recipient.asInstanceOf[ClearRecipient].expiry
assert(nodeParams.currentBlockHeight + invoiceFinalExpiryDelta.toInt + 50 <= expiry.blockHeight)
@ -260,7 +260,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
val req = SendPaymentToRoute(finalAmount, invoice, Nil, route, None, None, None)
sender.send(initiator, req)
val payment = sender.expectMsgType[SendPaymentToRouteResponse]
payFsm.expectMsg(SendPaymentConfig(payment.paymentId, payment.parentId, None, paymentHash, c, Upstream.Local(payment.paymentId), Some(invoice), None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = false))
payFsm.expectMsg(SendPaymentConfig(payment.paymentId, payment.parentId, None, paymentHash, c, Upstream.Local(payment.paymentId), Some(invoice), None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = false, confidence = 1.0))
val msg = payFsm.expectMsgType[PaymentLifecycle.SendPaymentToRoute]
assert(msg.replyTo == initiator)
assert(msg.route == Left(route))
@ -302,7 +302,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
val req = SendPaymentToNode(sender.ref, finalAmount, invoice, resolvedPaths, 1, routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams, payerKey_opt = Some(payerKey))
sender.send(initiator, req)
val id = sender.expectMsgType[UUID]
payFsm.expectMsg(SendPaymentConfig(id, id, None, paymentHash, invoice.nodeId, Upstream.Local(id), Some(invoice), Some(payerKey), storeInDb = true, publishEvent = true, recordPathFindingMetrics = true))
payFsm.expectMsg(SendPaymentConfig(id, id, None, paymentHash, invoice.nodeId, Upstream.Local(id), Some(invoice), Some(payerKey), storeInDb = true, publishEvent = true, recordPathFindingMetrics = true, confidence = 1.0))
val payment = payFsm.expectMsgType[PaymentLifecycle.SendPaymentToNode]
assert(payment.amount == finalAmount)
assert(payment.recipient.nodeId == invoice.nodeId)
@ -336,7 +336,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
val req = SendPaymentToNode(sender.ref, finalAmount, invoice, resolvedPaths, 1, routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams, payerKey_opt = Some(payerKey))
sender.send(initiator, req)
val id = sender.expectMsgType[UUID]
multiPartPayFsm.expectMsg(SendPaymentConfig(id, id, None, paymentHash, invoice.nodeId, Upstream.Local(id), Some(invoice), Some(payerKey), storeInDb = true, publishEvent = true, recordPathFindingMetrics = true))
multiPartPayFsm.expectMsg(SendPaymentConfig(id, id, None, paymentHash, invoice.nodeId, Upstream.Local(id), Some(invoice), Some(payerKey), storeInDb = true, publishEvent = true, recordPathFindingMetrics = true, confidence = 1.0))
val payment = multiPartPayFsm.expectMsgType[SendMultiPartPayment]
assert(payment.recipient.nodeId == invoice.nodeId)
assert(payment.recipient.totalAmount == finalAmount)
@ -529,7 +529,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
sender.send(initiator, req)
val payment = sender.expectMsgType[SendPaymentToRouteResponse]
assert(payment.trampolineSecret.contains(trampolineAttempt.paymentSecret))
payFsm.expectMsg(SendPaymentConfig(payment.paymentId, payment.parentId, None, paymentHash, c, Upstream.Local(payment.paymentId), Some(invoice), None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = false))
payFsm.expectMsg(SendPaymentConfig(payment.paymentId, payment.parentId, None, paymentHash, c, Upstream.Local(payment.paymentId), Some(invoice), None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = false, confidence = 1.0))
val msg = payFsm.expectMsgType[PaymentLifecycle.SendPaymentToRoute]
assert(msg.route == Left(route))
assert(msg.amount == finalAmount + trampolineAttempt.fees)

View file

@ -81,7 +81,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec {
def createPaymentLifecycle(invoice: Invoice, storeInDb: Boolean = true, publishEvent: Boolean = true, recordMetrics: Boolean = true): PaymentFixture = {
val (id, parentId) = (UUID.randomUUID(), UUID.randomUUID())
val nodeParams = TestConstants.Alice.nodeParams.copy(nodeKeyManager = testNodeKeyManager, channelKeyManager = testChannelKeyManager)
val cfg = SendPaymentConfig(id, parentId, Some(defaultExternalId), defaultPaymentHash, invoice.nodeId, Upstream.Local(id), Some(invoice), None, storeInDb, publishEvent, recordMetrics)
val cfg = SendPaymentConfig(id, parentId, Some(defaultExternalId), defaultPaymentHash, invoice.nodeId, Upstream.Local(id), Some(invoice), None, storeInDb, publishEvent, recordMetrics, confidence = 1.0)
val (routerForwarder, register, sender, monitor, eventListener, metricsListener) = (TestProbe(), TestProbe(), TestProbe(), TestProbe(), TestProbe(), TestProbe())
val paymentFSM = TestFSMRef(new PaymentLifecycle(nodeParams, cfg, routerForwarder.ref, register.ref))
paymentFSM ! SubscribeTransitionCallBack(monitor.ref)
@ -94,7 +94,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec {
def addCompleted(result: HtlcResult) = {
RES_ADD_SETTLED(
origin = defaultOrigin,
htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, defaultAmountMsat, defaultPaymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None),
htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, defaultAmountMsat, defaultPaymentHash, defaultExpiry, TestConstants.emptyOnionPacket, None, 1.0),
result)
}

View file

@ -68,7 +68,7 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
def testBuildOutgoingPayment(): Unit = {
val recipient = ClearRecipient(e, Features.empty, finalAmount, finalExpiry, paymentSecret)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(finalAmount, hops, None), recipient)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(finalAmount, hops, None), recipient, 1.0)
assert(payment.outgoingChannel == channelUpdate_ab.shortChannelId)
assert(payment.cmd.amount == amount_ab)
assert(payment.cmd.cltvExpiry == expiry_ab)
@ -79,7 +79,7 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
}
def testPeelOnion(packet_b: OnionRoutingPacket): Unit = {
val add_b = UpdateAddHtlc(randomBytes32(), 0, amount_ab, paymentHash, expiry_ab, packet_b, None)
val add_b = UpdateAddHtlc(randomBytes32(), 0, amount_ab, paymentHash, expiry_ab, packet_b, None, 1.0)
val Right(relay_b@ChannelRelayPacket(add_b2, payload_b, packet_c)) = decrypt(add_b, priv_b.privateKey, Features.empty)
assert(add_b2 == add_b)
assert(packet_c.payload.length == PaymentOnionCodecs.paymentOnionPayloadLength)
@ -89,7 +89,7 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
assert(relay_b.relayFeeMsat == fee_b)
assert(relay_b.expiryDelta == channelUpdate_bc.cltvExpiryDelta)
val add_c = UpdateAddHtlc(randomBytes32(), 1, amount_bc, paymentHash, expiry_bc, packet_c, None)
val add_c = UpdateAddHtlc(randomBytes32(), 1, amount_bc, paymentHash, expiry_bc, packet_c, None, 1.0)
val Right(relay_c@ChannelRelayPacket(add_c2, payload_c, packet_d)) = decrypt(add_c, priv_c.privateKey, Features.empty)
assert(add_c2 == add_c)
assert(packet_d.payload.length == PaymentOnionCodecs.paymentOnionPayloadLength)
@ -99,7 +99,7 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
assert(relay_c.relayFeeMsat == fee_c)
assert(relay_c.expiryDelta == channelUpdate_cd.cltvExpiryDelta)
val add_d = UpdateAddHtlc(randomBytes32(), 2, amount_cd, paymentHash, expiry_cd, packet_d, None)
val add_d = UpdateAddHtlc(randomBytes32(), 2, amount_cd, paymentHash, expiry_cd, packet_d, None, 1.0)
val Right(relay_d@ChannelRelayPacket(add_d2, payload_d, packet_e)) = decrypt(add_d, priv_d.privateKey, Features.empty)
assert(add_d2 == add_d)
assert(packet_e.payload.length == PaymentOnionCodecs.paymentOnionPayloadLength)
@ -109,7 +109,7 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
assert(relay_d.relayFeeMsat == fee_d)
assert(relay_d.expiryDelta == channelUpdate_de.cltvExpiryDelta)
val add_e = UpdateAddHtlc(randomBytes32(), 2, amount_de, paymentHash, expiry_de, packet_e, None)
val add_e = UpdateAddHtlc(randomBytes32(), 2, amount_de, paymentHash, expiry_de, packet_e, None, 1.0)
val Right(FinalPacket(add_e2, payload_e)) = decrypt(add_e, priv_e.privateKey, Features.empty)
assert(add_e2 == add_e)
assert(payload_e.isInstanceOf[FinalPayload.Standard])
@ -126,14 +126,14 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
test("build outgoing payment for direct peer") {
val recipient = ClearRecipient(b, Features.empty, finalAmount, finalExpiry, paymentSecret, paymentMetadata_opt = Some(paymentMetadata))
val route = Route(finalAmount, hops.take(1), None)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient, 1.0)
assert(payment.cmd.amount == finalAmount)
assert(payment.cmd.cltvExpiry == finalExpiry)
assert(payment.cmd.paymentHash == paymentHash)
assert(payment.cmd.onion.payload.length == PaymentOnionCodecs.paymentOnionPayloadLength)
// let's peel the onion
val add_b = UpdateAddHtlc(randomBytes32(), 0, finalAmount, paymentHash, finalExpiry, payment.cmd.onion, None)
val add_b = UpdateAddHtlc(randomBytes32(), 0, finalAmount, paymentHash, finalExpiry, payment.cmd.onion, None, 1.0)
val Right(FinalPacket(add_b2, payload_b)) = decrypt(add_b, priv_b.privateKey, Features.empty)
assert(add_b2 == add_b)
assert(payload_b.isInstanceOf[FinalPayload.Standard])
@ -147,10 +147,10 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
test("build outgoing payment with greater amount and expiry") {
val recipient = ClearRecipient(b, Features.empty, finalAmount, finalExpiry, paymentSecret, paymentMetadata_opt = Some(paymentMetadata))
val route = Route(finalAmount, hops.take(1), None)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient, 1.0)
// let's peel the onion
val add_b = UpdateAddHtlc(randomBytes32(), 0, finalAmount + 100.msat, paymentHash, finalExpiry + CltvExpiryDelta(6), payment.cmd.onion, None)
val add_b = UpdateAddHtlc(randomBytes32(), 0, finalAmount + 100.msat, paymentHash, finalExpiry + CltvExpiryDelta(6), payment.cmd.onion, None, 1.0)
val Right(FinalPacket(_, payload_b)) = decrypt(add_b, priv_b.privateKey, Features.empty)
assert(payload_b.isInstanceOf[FinalPayload.Standard])
assert(payload_b.amount == finalAmount)
@ -164,13 +164,13 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
assert(recipient.extraEdges.length == 1)
assert(recipient.extraEdges.head.sourceNodeId == c)
assert(recipient.extraEdges.head.targetNodeId == invoice.nodeId)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient, 1.0)
assert(payment.outgoingChannel == channelUpdate_ab.shortChannelId)
assert(payment.cmd.amount >= amount_ab)
assert(payment.cmd.cltvExpiry == expiry_ab)
assert(payment.cmd.nextBlindingKey_opt.isEmpty)
val add_b = UpdateAddHtlc(randomBytes32(), 0, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt)
val add_b = UpdateAddHtlc(randomBytes32(), 0, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt, 1.0)
val Right(relay_b@ChannelRelayPacket(_, payload_b, packet_c)) = decrypt(add_b, priv_b.privateKey, Features.empty)
assert(packet_c.payload.length == PaymentOnionCodecs.paymentOnionPayloadLength)
assert(relay_b.amountToForward >= amount_bc)
@ -180,7 +180,7 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
assert(relay_b.expiryDelta == channelUpdate_bc.cltvExpiryDelta)
assert(payload_b.isInstanceOf[IntermediatePayload.ChannelRelay.Standard])
val add_c = UpdateAddHtlc(randomBytes32(), 1, relay_b.amountToForward, relay_b.add.paymentHash, relay_b.outgoingCltv, packet_c, None)
val add_c = UpdateAddHtlc(randomBytes32(), 1, relay_b.amountToForward, relay_b.add.paymentHash, relay_b.outgoingCltv, packet_c, None, 1.0)
val Right(relay_c@ChannelRelayPacket(_, payload_c, packet_d)) = decrypt(add_c, priv_c.privateKey, Features(RouteBlinding -> Optional))
assert(packet_d.payload.length == PaymentOnionCodecs.paymentOnionPayloadLength)
assert(relay_c.amountToForward >= amount_cd)
@ -191,7 +191,7 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
assert(payload_c.isInstanceOf[IntermediatePayload.ChannelRelay.Blinded])
val blinding_d = payload_c.asInstanceOf[IntermediatePayload.ChannelRelay.Blinded].nextBlinding
val add_d = UpdateAddHtlc(randomBytes32(), 2, relay_c.amountToForward, relay_c.add.paymentHash, relay_c.outgoingCltv, packet_d, Some(blinding_d))
val add_d = UpdateAddHtlc(randomBytes32(), 2, relay_c.amountToForward, relay_c.add.paymentHash, relay_c.outgoingCltv, packet_d, Some(blinding_d), 1.0)
val Right(relay_d@ChannelRelayPacket(_, payload_d, packet_e)) = decrypt(add_d, priv_d.privateKey, Features(RouteBlinding -> Optional))
assert(packet_e.payload.length == PaymentOnionCodecs.paymentOnionPayloadLength)
assert(relay_d.amountToForward >= amount_de)
@ -202,7 +202,7 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
assert(payload_d.isInstanceOf[IntermediatePayload.ChannelRelay.Blinded])
val blinding_e = payload_d.asInstanceOf[IntermediatePayload.ChannelRelay.Blinded].nextBlinding
val add_e = UpdateAddHtlc(randomBytes32(), 2, relay_d.amountToForward, relay_d.add.paymentHash, relay_d.outgoingCltv, packet_e, Some(blinding_e))
val add_e = UpdateAddHtlc(randomBytes32(), 2, relay_d.amountToForward, relay_d.add.paymentHash, relay_d.outgoingCltv, packet_e, Some(blinding_e), 1.0)
val Right(FinalPacket(_, payload_e)) = decrypt(add_e, priv_e.privateKey, Features(RouteBlinding -> Optional))
assert(payload_e.amount == finalAmount)
assert(payload_e.totalAmount == finalAmount)
@ -227,13 +227,13 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
})
val recipient = BlindedRecipient(invoice, resolvedPaths, amount_bc, expiry_bc, Set.empty)
val hops = Seq(channelHopFromUpdate(a, b, channelUpdate_ab), channelHopFromUpdate(b, c, channelUpdate_bc))
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(amount_bc, hops, Some(recipient.blindedHops.head)), recipient)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(amount_bc, hops, Some(recipient.blindedHops.head)), recipient, 1.0)
assert(payment.outgoingChannel == channelUpdate_ab.shortChannelId)
assert(payment.cmd.amount == amount_ab)
assert(payment.cmd.cltvExpiry == expiry_ab)
assert(payment.cmd.nextBlindingKey_opt.isEmpty)
val add_b = UpdateAddHtlc(randomBytes32(), 0, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt)
val add_b = UpdateAddHtlc(randomBytes32(), 0, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt, 1.0)
val Right(relay_b@ChannelRelayPacket(_, payload_b, packet_c)) = decrypt(add_b, priv_b.privateKey, Features.empty)
assert(packet_c.payload.length == PaymentOnionCodecs.paymentOnionPayloadLength)
assert(relay_b.amountToForward >= amount_bc)
@ -243,7 +243,7 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
assert(relay_b.expiryDelta == channelUpdate_bc.cltvExpiryDelta)
assert(payload_b.isInstanceOf[IntermediatePayload.ChannelRelay.Standard])
val add_c = UpdateAddHtlc(randomBytes32(), 1, amount_bc, paymentHash, expiry_bc, packet_c, None)
val add_c = UpdateAddHtlc(randomBytes32(), 1, amount_bc, paymentHash, expiry_bc, packet_c, None, 1.0)
val Right(FinalPacket(_, payload_c)) = decrypt(add_c, priv_c.privateKey, Features(RouteBlinding -> Optional))
assert(payload_c.amount == amount_bc)
assert(payload_c.totalAmount == amount_bc)
@ -259,7 +259,7 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
assert(payment.cmd.cltvExpiry == finalExpiry)
assert(payment.cmd.nextBlindingKey_opt.nonEmpty)
val add_b = UpdateAddHtlc(randomBytes32(), 0, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt)
val add_b = UpdateAddHtlc(randomBytes32(), 0, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt, 1.0)
val Right(FinalPacket(_, payload_b)) = decrypt(add_b, priv_b.privateKey, Features(RouteBlinding -> Optional))
assert(payload_b.amount == finalAmount)
assert(payload_b.totalAmount == finalAmount)
@ -272,7 +272,7 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
val Right(payment) = buildOutgoingBlindedPaymentAB(paymentHash)
assert(payment.outgoingChannel == channelUpdate_ab.shortChannelId)
val add_b = UpdateAddHtlc(randomBytes32(), 0, payment.cmd.amount + 100.msat, payment.cmd.paymentHash, payment.cmd.cltvExpiry + CltvExpiryDelta(6), payment.cmd.onion, payment.cmd.nextBlindingKey_opt)
val add_b = UpdateAddHtlc(randomBytes32(), 0, payment.cmd.amount + 100.msat, payment.cmd.paymentHash, payment.cmd.cltvExpiry + CltvExpiryDelta(6), payment.cmd.onion, payment.cmd.nextBlindingKey_opt, 1.0)
val Right(FinalPacket(_, payload_b)) = decrypt(add_b, priv_b.privateKey, Features(RouteBlinding -> Optional))
assert(payload_b.amount == finalAmount)
assert(payload_b.totalAmount == finalAmount)
@ -288,17 +288,17 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
val recipient = TrampolineRecipient(invoice, finalAmount, finalExpiry, trampolineHop, randomBytes32())
assert(recipient.trampolineAmount == amount_bc)
assert(recipient.trampolineExpiry == expiry_bc)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(recipient.trampolineAmount, trampolineChannelHops, Some(trampolineHop)), recipient)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(recipient.trampolineAmount, trampolineChannelHops, Some(trampolineHop)), recipient, 1.0)
assert(payment.outgoingChannel == channelUpdate_ab.shortChannelId)
assert(payment.cmd.amount == amount_ab)
assert(payment.cmd.cltvExpiry == expiry_ab)
val add_b = UpdateAddHtlc(randomBytes32(), 1, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None)
val add_b = UpdateAddHtlc(randomBytes32(), 1, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None, 1.0)
val Right(ChannelRelayPacket(add_b2, payload_b, packet_c)) = decrypt(add_b, priv_b.privateKey, Features.empty)
assert(add_b2 == add_b)
assert(payload_b == IntermediatePayload.ChannelRelay.Standard(channelUpdate_bc.shortChannelId, amount_bc, expiry_bc))
val add_c = UpdateAddHtlc(randomBytes32(), 2, amount_bc, paymentHash, expiry_bc, packet_c, None)
val add_c = UpdateAddHtlc(randomBytes32(), 2, amount_bc, paymentHash, expiry_bc, packet_c, None, 1.0)
val Right(RelayToTrampolinePacket(add_c2, outer_c, inner_c, trampolinePacket_e)) = decrypt(add_c, priv_c.privateKey, Features.empty)
assert(add_c2 == add_c)
assert(outer_c.amount == amount_bc)
@ -315,16 +315,16 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
// c forwards the trampoline payment to e through d.
val recipient_e = ClearRecipient(e, Features.empty, inner_c.amountToForward, inner_c.outgoingCltv, randomBytes32(), nextTrampolineOnion_opt = Some(trampolinePacket_e))
val Right(payment_e) = buildOutgoingPayment(Origin.Hot(ActorRef.noSender, Upstream.Hot.Trampoline(Seq(Upstream.Hot.Channel(add_c, TimestampMilli(1687345927000L), b)))), paymentHash, Route(inner_c.amountToForward, afterTrampolineChannelHops, None), recipient_e)
val Right(payment_e) = buildOutgoingPayment(Origin.Hot(ActorRef.noSender, Upstream.Hot.Trampoline(Seq(Upstream.Hot.Channel(add_c, TimestampMilli(1687345927000L), b)))), paymentHash, Route(inner_c.amountToForward, afterTrampolineChannelHops, None), recipient_e, 1.0)
assert(payment_e.outgoingChannel == channelUpdate_cd.shortChannelId)
assert(payment_e.cmd.amount == amount_cd)
assert(payment_e.cmd.cltvExpiry == expiry_cd)
val add_d = UpdateAddHtlc(randomBytes32(), 3, payment_e.cmd.amount, paymentHash, payment_e.cmd.cltvExpiry, payment_e.cmd.onion, None)
val add_d = UpdateAddHtlc(randomBytes32(), 3, payment_e.cmd.amount, paymentHash, payment_e.cmd.cltvExpiry, payment_e.cmd.onion, None, 1.0)
val Right(ChannelRelayPacket(add_d2, payload_d, packet_e)) = decrypt(add_d, priv_d.privateKey, Features.empty)
assert(add_d2 == add_d)
assert(payload_d == IntermediatePayload.ChannelRelay.Standard(channelUpdate_de.shortChannelId, amount_de, expiry_de))
val add_e = UpdateAddHtlc(randomBytes32(), 4, amount_de, paymentHash, expiry_de, packet_e, None)
val add_e = UpdateAddHtlc(randomBytes32(), 4, amount_de, paymentHash, expiry_de, packet_e, None, 1.0)
val Right(FinalPacket(add_e2, payload_e)) = decrypt(add_e, priv_e.privateKey, Features.empty)
assert(add_e2 == add_e)
assert(payload_e == FinalPayload.Standard(TlvStream(AmountToForward(finalAmount), OutgoingCltv(finalExpiry), PaymentData(paymentSecret, finalAmount), OnionPaymentPayloadTlv.PaymentMetadata(hex"010203"))))
@ -341,15 +341,15 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
val recipient = TrampolineRecipient(invoice, finalAmount, finalExpiry, trampolineHop, randomBytes32())
assert(recipient.trampolineAmount == amount_bc)
assert(recipient.trampolineExpiry == expiry_bc)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(recipient.trampolineAmount, trampolineChannelHops, Some(trampolineHop)), recipient)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(recipient.trampolineAmount, trampolineChannelHops, Some(trampolineHop)), recipient, 1.0)
assert(payment.outgoingChannel == channelUpdate_ab.shortChannelId)
assert(payment.cmd.amount == amount_ab)
assert(payment.cmd.cltvExpiry == expiry_ab)
val add_b = UpdateAddHtlc(randomBytes32(), 1, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None)
val add_b = UpdateAddHtlc(randomBytes32(), 1, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None, 1.0)
val Right(ChannelRelayPacket(_, _, packet_c)) = decrypt(add_b, priv_b.privateKey, Features.empty)
val add_c = UpdateAddHtlc(randomBytes32(), 2, amount_bc, paymentHash, expiry_bc, packet_c, None)
val add_c = UpdateAddHtlc(randomBytes32(), 2, amount_bc, paymentHash, expiry_bc, packet_c, None, 1.0)
val Right(RelayToTrampolinePacket(_, outer_c, inner_c, _)) = decrypt(add_c, priv_c.privateKey, Features.empty)
assert(outer_c.amount == amount_bc)
assert(outer_c.totalAmount == amount_bc)
@ -367,16 +367,16 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
// c forwards the trampoline payment to e through d.
val recipient_e = ClearRecipient(e, Features.empty, inner_c.amountToForward, inner_c.outgoingCltv, inner_c.paymentSecret.get, invoice.extraEdges, inner_c.paymentMetadata)
val Right(payment_e) = buildOutgoingPayment(Origin.Hot(ActorRef.noSender, Upstream.Hot.Trampoline(Seq(Upstream.Hot.Channel(add_c, TimestampMilli(1687345927000L), b)))), paymentHash, Route(inner_c.amountToForward, afterTrampolineChannelHops, None), recipient_e)
val Right(payment_e) = buildOutgoingPayment(Origin.Hot(ActorRef.noSender, Upstream.Hot.Trampoline(Seq(Upstream.Hot.Channel(add_c, TimestampMilli(1687345927000L), b)))), paymentHash, Route(inner_c.amountToForward, afterTrampolineChannelHops, None), recipient_e, 1.0)
assert(payment_e.outgoingChannel == channelUpdate_cd.shortChannelId)
assert(payment_e.cmd.amount == amount_cd)
assert(payment_e.cmd.cltvExpiry == expiry_cd)
val add_d = UpdateAddHtlc(randomBytes32(), 3, payment_e.cmd.amount, paymentHash, payment_e.cmd.cltvExpiry, payment_e.cmd.onion, None)
val add_d = UpdateAddHtlc(randomBytes32(), 3, payment_e.cmd.amount, paymentHash, payment_e.cmd.cltvExpiry, payment_e.cmd.onion, None, 1.0)
val Right(ChannelRelayPacket(add_d2, payload_d, packet_e)) = decrypt(add_d, priv_d.privateKey, Features.empty)
assert(add_d2 == add_d)
assert(payload_d == IntermediatePayload.ChannelRelay.Standard(channelUpdate_de.shortChannelId, amount_de, expiry_de))
val add_e = UpdateAddHtlc(randomBytes32(), 4, amount_de, paymentHash, expiry_de, packet_e, None)
val add_e = UpdateAddHtlc(randomBytes32(), 4, amount_de, paymentHash, expiry_de, packet_e, None, 1.0)
val Right(FinalPacket(add_e2, payload_e)) = decrypt(add_e, priv_e.privateKey, Features.empty)
assert(add_e2 == add_e)
assert(payload_e == FinalPayload.Standard(TlvStream(AmountToForward(finalAmount), OutgoingCltv(finalExpiry), PaymentData(invoice.paymentSecret, finalAmount), OnionPaymentPayloadTlv.PaymentMetadata(hex"010203"))))
@ -393,13 +393,13 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
val invoiceFeatures = Features[Bolt11Feature](VariableLengthOnion -> Mandatory, PaymentSecret -> Mandatory, BasicMultiPartPayment -> Optional)
val invoice = Bolt11Invoice(Block.RegtestGenesisBlock.hash, Some(finalAmount), paymentHash, priv_e.privateKey, Left("#reckless"), CltvExpiryDelta(18), extraHops = routingHints, features = invoiceFeatures, paymentMetadata = Some(paymentMetadata))
val recipient = TrampolineRecipient(invoice, finalAmount, finalExpiry, trampolineHop, randomBytes32())
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(recipient.trampolineAmount, trampolineChannelHops, Some(trampolineHop)), recipient)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(recipient.trampolineAmount, trampolineChannelHops, Some(trampolineHop)), recipient, 1.0)
assert(payment.outgoingChannel == channelUpdate_ab.shortChannelId)
val add_b = UpdateAddHtlc(randomBytes32(), 1, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None)
val add_b = UpdateAddHtlc(randomBytes32(), 1, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None, 1.0)
val Right(ChannelRelayPacket(_, _, packet_c)) = decrypt(add_b, priv_b.privateKey, Features.empty)
val add_c = UpdateAddHtlc(randomBytes32(), 2, amount_bc, paymentHash, expiry_bc, packet_c, None)
val add_c = UpdateAddHtlc(randomBytes32(), 2, amount_bc, paymentHash, expiry_bc, packet_c, None, 1.0)
val Right(RelayToTrampolinePacket(_, outer_c, inner_c, _)) = decrypt(add_c, priv_c.privateKey, Features.empty)
assert(outer_c.records.get[OnionPaymentPayloadTlv.TrampolineOnion].get.packet.payload.size > 800)
assert(inner_c.outgoingNodeId == e)
@ -408,14 +408,14 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
// c forwards the trampoline payment to e through d.
val recipient_e = ClearRecipient(e, Features.empty, inner_c.amountToForward, inner_c.outgoingCltv, inner_c.paymentSecret.get, invoice.extraEdges, inner_c.paymentMetadata)
val Right(payment_e) = buildOutgoingPayment(Origin.Hot(ActorRef.noSender, Upstream.Hot.Trampoline(Seq(Upstream.Hot.Channel(add_c, TimestampMilli(1687345927000L), b)))), paymentHash, Route(inner_c.amountToForward, afterTrampolineChannelHops, None), recipient_e)
val Right(payment_e) = buildOutgoingPayment(Origin.Hot(ActorRef.noSender, Upstream.Hot.Trampoline(Seq(Upstream.Hot.Channel(add_c, TimestampMilli(1687345927000L), b)))), paymentHash, Route(inner_c.amountToForward, afterTrampolineChannelHops, None), recipient_e, 1.0)
assert(payment_e.outgoingChannel == channelUpdate_cd.shortChannelId)
val add_d = UpdateAddHtlc(randomBytes32(), 3, payment_e.cmd.amount, paymentHash, payment_e.cmd.cltvExpiry, payment_e.cmd.onion, None)
val add_d = UpdateAddHtlc(randomBytes32(), 3, payment_e.cmd.amount, paymentHash, payment_e.cmd.cltvExpiry, payment_e.cmd.onion, None, 1.0)
val Right(ChannelRelayPacket(add_d2, payload_d, packet_e)) = decrypt(add_d, priv_d.privateKey, Features.empty)
assert(add_d2 == add_d)
assert(payload_d == IntermediatePayload.ChannelRelay.Standard(channelUpdate_de.shortChannelId, amount_de, expiry_de))
val add_e = UpdateAddHtlc(randomBytes32(), 4, amount_de, paymentHash, expiry_de, packet_e, None)
val add_e = UpdateAddHtlc(randomBytes32(), 4, amount_de, paymentHash, expiry_de, packet_e, None, 1.0)
val Right(FinalPacket(add_e2, payload_e)) = decrypt(add_e, priv_e.privateKey, Features.empty)
assert(add_e2 == add_e)
assert(payload_e == FinalPayload.Standard(TlvStream(AmountToForward(finalAmount), OutgoingCltv(finalExpiry), PaymentData(invoice.paymentSecret, finalAmount), OnionPaymentPayloadTlv.PaymentMetadata(paymentMetadata))))
@ -424,7 +424,7 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
test("fail to build outgoing payment with invalid route") {
val recipient = ClearRecipient(e, Features.empty, finalAmount, finalExpiry, paymentSecret)
val route = Route(finalAmount, hops.dropRight(1), None) // route doesn't reach e
val Left(failure) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient)
val Left(failure) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient, 1.0)
assert(failure == InvalidRouteRecipient(e, d))
}
@ -433,22 +433,22 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
val invoice = Bolt11Invoice(Block.RegtestGenesisBlock.hash, None, paymentHash, priv_e.privateKey, Left("invoice"), CltvExpiryDelta(6), paymentSecret = paymentSecret, features = invoiceFeatures)
val recipient = TrampolineRecipient(invoice, finalAmount, finalExpiry, trampolineHop, randomBytes32())
val route = Route(finalAmount, trampolineChannelHops, None) // missing trampoline hop
val Left(failure) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient)
val Left(failure) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient, 1.0)
assert(failure == MissingTrampolineHop(c))
}
test("fail to build outgoing blinded payment with invalid route") {
val (_, route, recipient) = longBlindedHops(hex"deadbeef")
assert(buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient).isRight)
assert(buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient, 1.0).isRight)
val routeMissingBlindedHop = route.copy(finalHop_opt = None)
val Left(failure) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, routeMissingBlindedHop, recipient)
val Left(failure) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, routeMissingBlindedHop, recipient, 1.0)
assert(failure == MissingBlindedHop(Set(c)))
}
test("fail to decrypt when the onion is invalid") {
val recipient = ClearRecipient(e, Features.empty, finalAmount, finalExpiry, paymentSecret)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(finalAmount, hops, None), recipient)
val add = UpdateAddHtlc(randomBytes32(), 1, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion.copy(payload = payment.cmd.onion.payload.reverse), None)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(finalAmount, hops, None), recipient, 1.0)
val add = UpdateAddHtlc(randomBytes32(), 1, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion.copy(payload = payment.cmd.onion.payload.reverse), None, 1.0)
val Left(failure) = decrypt(add, priv_b.privateKey, Features.empty)
assert(failure.isInstanceOf[InvalidOnionHmac])
}
@ -457,30 +457,30 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
val invoiceFeatures = Features[Bolt11Feature](VariableLengthOnion -> Mandatory, PaymentSecret -> Mandatory, BasicMultiPartPayment -> Optional, PaymentMetadata -> Optional, TrampolinePaymentPrototype -> Optional)
val invoice = Bolt11Invoice(Block.RegtestGenesisBlock.hash, None, paymentHash, priv_e.privateKey, Left("invoice"), CltvExpiryDelta(6), paymentSecret = paymentSecret, features = invoiceFeatures, paymentMetadata = Some(hex"010203"))
val recipient = TrampolineRecipient(invoice, finalAmount, finalExpiry, trampolineHop, randomBytes32())
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(recipient.trampolineAmount, trampolineChannelHops, Some(trampolineHop)), recipient)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(recipient.trampolineAmount, trampolineChannelHops, Some(trampolineHop)), recipient, 1.0)
val add_b = UpdateAddHtlc(randomBytes32(), 1, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None)
val add_b = UpdateAddHtlc(randomBytes32(), 1, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None, 1.0)
val Right(ChannelRelayPacket(_, _, packet_c)) = decrypt(add_b, priv_b.privateKey, Features.empty)
val add_c = UpdateAddHtlc(randomBytes32(), 2, amount_bc, paymentHash, expiry_bc, packet_c, None)
val add_c = UpdateAddHtlc(randomBytes32(), 2, amount_bc, paymentHash, expiry_bc, packet_c, None, 1.0)
val Right(RelayToTrampolinePacket(_, _, inner_c, trampolinePacket_e)) = decrypt(add_c, priv_c.privateKey, Features.empty)
// c forwards an invalid trampoline onion to e through d.
val recipient_e = ClearRecipient(e, Features.empty, inner_c.amountToForward, inner_c.outgoingCltv, randomBytes32(), nextTrampolineOnion_opt = Some(trampolinePacket_e.copy(payload = trampolinePacket_e.payload.reverse)))
val Right(payment_e) = buildOutgoingPayment(Origin.Hot(ActorRef.noSender, Upstream.Hot.Trampoline(Seq(Upstream.Hot.Channel(add_c, TimestampMilli(1687345927000L), b)))), paymentHash, Route(inner_c.amountToForward, afterTrampolineChannelHops, None), recipient_e)
val Right(payment_e) = buildOutgoingPayment(Origin.Hot(ActorRef.noSender, Upstream.Hot.Trampoline(Seq(Upstream.Hot.Channel(add_c, TimestampMilli(1687345927000L), b)))), paymentHash, Route(inner_c.amountToForward, afterTrampolineChannelHops, None), recipient_e, 1.0)
assert(payment_e.outgoingChannel == channelUpdate_cd.shortChannelId)
val add_d = UpdateAddHtlc(randomBytes32(), 3, payment_e.cmd.amount, paymentHash, payment_e.cmd.cltvExpiry, payment_e.cmd.onion, None)
val add_d = UpdateAddHtlc(randomBytes32(), 3, payment_e.cmd.amount, paymentHash, payment_e.cmd.cltvExpiry, payment_e.cmd.onion, None, 1.0)
val Right(ChannelRelayPacket(_, _, packet_e)) = decrypt(add_d, priv_d.privateKey, Features.empty)
val add_e = UpdateAddHtlc(randomBytes32(), 4, amount_de, paymentHash, expiry_de, packet_e, None)
val add_e = UpdateAddHtlc(randomBytes32(), 4, amount_de, paymentHash, expiry_de, packet_e, None, 1.0)
val Left(failure) = decrypt(add_e, priv_e.privateKey, Features.empty)
assert(failure.isInstanceOf[InvalidOnionHmac])
}
test("fail to decrypt when payment hash doesn't match associated data") {
val recipient = ClearRecipient(e, Features.empty, finalAmount, finalExpiry, paymentSecret)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash.reverse, Route(finalAmount, hops, None), recipient)
val add = UpdateAddHtlc(randomBytes32(), 1, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash.reverse, Route(finalAmount, hops, None), recipient, 1.0)
val add = UpdateAddHtlc(randomBytes32(), 1, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None, 1.0)
val Left(failure) = decrypt(add, priv_b.privateKey, Features.empty)
assert(failure.isInstanceOf[InvalidOnionHmac])
}
@ -503,19 +503,19 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
val route = Route(amount_bc, Seq(channelHopFromUpdate(a, b, channelUpdate_ab)), Some(recipient.blindedHops.head))
(route, recipient)
}
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient, 1.0)
assert(payment.outgoingChannel == channelUpdate_ab.shortChannelId)
assert(payment.cmd.amount == amount_bc + fee_b)
val add_b = UpdateAddHtlc(randomBytes32(), 0, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt)
val add_b = UpdateAddHtlc(randomBytes32(), 0, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt, 1.0)
val Left(failure) = decrypt(add_b, priv_b.privateKey, Features(RouteBlinding -> Optional))
assert(failure.isInstanceOf[InvalidOnionBlinding])
}
test("fail to decrypt blinded payment when route blinding is disabled") {
val (route, recipient) = shortBlindedHops()
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient)
val add_d = UpdateAddHtlc(randomBytes32(), 0, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient, 1.0)
val add_d = UpdateAddHtlc(randomBytes32(), 0, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt, 1.0)
val Left(failure) = decrypt(add_d, priv_d.privateKey, Features.empty) // d doesn't support route blinding
assert(failure == InvalidOnionPayload(UInt64(10), 0))
}
@ -523,8 +523,8 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
test("fail to decrypt at the final node when amount has been modified by next-to-last node") {
val recipient = ClearRecipient(b, Features.empty, finalAmount, finalExpiry, paymentSecret)
val route = Route(finalAmount, hops.take(1), None)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient)
val add = UpdateAddHtlc(randomBytes32(), 1, payment.cmd.amount - 100.msat, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient, 1.0)
val add = UpdateAddHtlc(randomBytes32(), 1, payment.cmd.amount - 100.msat, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None, 1.0)
val Left(failure) = decrypt(add, priv_b.privateKey, Features.empty)
assert(failure == FinalIncorrectHtlcAmount(payment.cmd.amount - 100.msat))
}
@ -532,20 +532,20 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
test("fail to decrypt at the final node when expiry has been modified by next-to-last node") {
val recipient = ClearRecipient(b, Features.empty, finalAmount, finalExpiry, paymentSecret)
val route = Route(finalAmount, hops.take(1), None)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient)
val add = UpdateAddHtlc(randomBytes32(), 1, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry - CltvExpiryDelta(12), payment.cmd.onion, None)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient, 1.0)
val add = UpdateAddHtlc(randomBytes32(), 1, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry - CltvExpiryDelta(12), payment.cmd.onion, None, 1.0)
val Left(failure) = decrypt(add, priv_b.privateKey, Features.empty)
assert(failure == FinalIncorrectCltvExpiry(payment.cmd.cltvExpiry - CltvExpiryDelta(12)))
}
test("fail to decrypt blinded payment at the final node when amount is too low") {
val (route, recipient) = shortBlindedHops()
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient, 1.0)
assert(payment.outgoingChannel == channelUpdate_cd.shortChannelId)
assert(payment.cmd.amount == amount_cd)
// A smaller amount is sent to d, who doesn't know that it's invalid.
val add_d = UpdateAddHtlc(randomBytes32(), 0, amount_de, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt)
val add_d = UpdateAddHtlc(randomBytes32(), 0, amount_de, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt, 1.0)
val Right(relay_d@ChannelRelayPacket(_, payload_d, packet_e)) = decrypt(add_d, priv_d.privateKey, Features(RouteBlinding -> Optional))
assert(payload_d.outgoingChannelId == channelUpdate_de.shortChannelId)
assert(relay_d.amountToForward < amount_de)
@ -553,21 +553,21 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
val blinding_e = payload_d.asInstanceOf[IntermediatePayload.ChannelRelay.Blinded].nextBlinding
// When e receives a smaller amount than expected, it rejects the payment.
val add_e = UpdateAddHtlc(randomBytes32(), 0, relay_d.amountToForward, paymentHash, relay_d.outgoingCltv, packet_e, Some(blinding_e))
val add_e = UpdateAddHtlc(randomBytes32(), 0, relay_d.amountToForward, paymentHash, relay_d.outgoingCltv, packet_e, Some(blinding_e), 1.0)
val Left(failure) = decrypt(add_e, priv_e.privateKey, Features(RouteBlinding -> Optional))
assert(failure.isInstanceOf[InvalidOnionBlinding])
}
test("fail to decrypt blinded payment at the final node when expiry is too low") {
val (route, recipient) = shortBlindedHops()
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient, 1.0)
assert(payment.outgoingChannel == channelUpdate_cd.shortChannelId)
assert(payment.cmd.cltvExpiry == expiry_cd)
// A smaller expiry is sent to d, who doesn't know that it's invalid.
// Intermediate nodes can reduce the expiry by at most min_final_expiry_delta.
val invalidExpiry = payment.cmd.cltvExpiry - Channel.MIN_CLTV_EXPIRY_DELTA - CltvExpiryDelta(1)
val add_d = UpdateAddHtlc(randomBytes32(), 0, payment.cmd.amount, paymentHash, invalidExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt)
val add_d = UpdateAddHtlc(randomBytes32(), 0, payment.cmd.amount, paymentHash, invalidExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt, 1.0)
val Right(relay_d@ChannelRelayPacket(_, payload_d, packet_e)) = decrypt(add_d, priv_d.privateKey, Features(RouteBlinding -> Optional))
assert(payload_d.outgoingChannelId == channelUpdate_de.shortChannelId)
assert(relay_d.outgoingCltv < CltvExpiry(currentBlockCount))
@ -575,7 +575,7 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
val blinding_e = payload_d.asInstanceOf[IntermediatePayload.ChannelRelay.Blinded].nextBlinding
// When e receives a smaller expiry than expected, it rejects the payment.
val add_e = UpdateAddHtlc(randomBytes32(), 0, relay_d.amountToForward, paymentHash, relay_d.outgoingCltv, packet_e, Some(blinding_e))
val add_e = UpdateAddHtlc(randomBytes32(), 0, relay_d.amountToForward, paymentHash, relay_d.outgoingCltv, packet_e, Some(blinding_e), 1.0)
val Left(failure) = decrypt(add_e, priv_e.privateKey, Features(RouteBlinding -> Optional))
assert(failure.isInstanceOf[InvalidOnionBlinding])
}
@ -583,11 +583,11 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
test("fail to decrypt blinded payment at intermediate node when expiry is too high") {
val routeExpiry = expiry_de - channelUpdate_de.cltvExpiryDelta
val (route, recipient) = shortBlindedHops(routeExpiry)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient, 1.0)
assert(payment.outgoingChannel == channelUpdate_cd.shortChannelId)
assert(payment.cmd.cltvExpiry > expiry_de)
val add_d = UpdateAddHtlc(randomBytes32(), 0, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt)
val add_d = UpdateAddHtlc(randomBytes32(), 0, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt, 1.0)
val Left(failure) = decrypt(add_d, priv_d.privateKey, Features(RouteBlinding -> Optional))
assert(failure.isInstanceOf[InvalidOnionBlinding])
}
@ -602,12 +602,12 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
val invoiceFeatures = Features[Bolt11Feature](VariableLengthOnion -> Mandatory, PaymentSecret -> Mandatory, BasicMultiPartPayment -> Optional, TrampolinePaymentPrototype -> Optional)
val invoice = Bolt11Invoice(Block.RegtestGenesisBlock.hash, None, paymentHash, priv_e.privateKey, Left("invoice"), CltvExpiryDelta(6), paymentSecret = paymentSecret, features = invoiceFeatures)
val recipient = TrampolineRecipient(invoice, finalAmount, finalExpiry, trampolineHop, randomBytes32())
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(recipient.trampolineAmount, trampolineChannelHops, Some(trampolineHop)), recipient)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(recipient.trampolineAmount, trampolineChannelHops, Some(trampolineHop)), recipient, 1.0)
val add_b = UpdateAddHtlc(randomBytes32(), 1, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None)
val add_b = UpdateAddHtlc(randomBytes32(), 1, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None, 1.0)
val Right(ChannelRelayPacket(_, _, packet_c)) = decrypt(add_b, priv_b.privateKey, Features.empty)
UpdateAddHtlc(randomBytes32(), 2, amount_bc, paymentHash, expiry_bc, packet_c, None)
UpdateAddHtlc(randomBytes32(), 2, amount_bc, paymentHash, expiry_bc, packet_c, None, 1.0)
}
test("fail to decrypt at the final trampoline node when amount has been decreased by next-to-last trampoline") {
@ -617,11 +617,11 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
// c forwards an invalid amount to e through (the outer total amount doesn't match the inner amount).
val invalidTotalAmount = inner_c.amountToForward - 1.msat
val recipient_e = ClearRecipient(e, Features.empty, invalidTotalAmount, inner_c.outgoingCltv, randomBytes32(), nextTrampolineOnion_opt = Some(trampolinePacket_e))
val Right(payment_e) = buildOutgoingPayment(Origin.Hot(ActorRef.noSender, Upstream.Hot.Trampoline(Seq(Upstream.Hot.Channel(add_c, TimestampMilli(1687345927000L), b)))), paymentHash, Route(invalidTotalAmount, afterTrampolineChannelHops, None), recipient_e)
val add_d = UpdateAddHtlc(randomBytes32(), 3, payment_e.cmd.amount, paymentHash, payment_e.cmd.cltvExpiry, payment_e.cmd.onion, None)
val Right(payment_e) = buildOutgoingPayment(Origin.Hot(ActorRef.noSender, Upstream.Hot.Trampoline(Seq(Upstream.Hot.Channel(add_c, TimestampMilli(1687345927000L), b)))), paymentHash, Route(invalidTotalAmount, afterTrampolineChannelHops, None), recipient_e, 1.0)
val add_d = UpdateAddHtlc(randomBytes32(), 3, payment_e.cmd.amount, paymentHash, payment_e.cmd.cltvExpiry, payment_e.cmd.onion, None, 1.0)
val Right(ChannelRelayPacket(_, payload_d, packet_e)) = decrypt(add_d, priv_d.privateKey, Features.empty)
val add_e = UpdateAddHtlc(randomBytes32(), 4, payload_d.amountToForward(add_d.amountMsat), paymentHash, payload_d.outgoingCltv(add_d.cltvExpiry), packet_e, None)
val add_e = UpdateAddHtlc(randomBytes32(), 4, payload_d.amountToForward(add_d.amountMsat), paymentHash, payload_d.outgoingCltv(add_d.cltvExpiry), packet_e, None, 1.0)
val Left(failure) = decrypt(add_e, priv_e.privateKey, Features.empty)
assert(failure == FinalIncorrectHtlcAmount(invalidTotalAmount))
}
@ -633,11 +633,11 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
// c forwards an invalid amount to e through (the outer expiry doesn't match the inner expiry).
val invalidExpiry = inner_c.outgoingCltv - CltvExpiryDelta(12)
val recipient_e = ClearRecipient(e, Features.empty, inner_c.amountToForward, invalidExpiry, randomBytes32(), nextTrampolineOnion_opt = Some(trampolinePacket_e))
val Right(payment_e) = buildOutgoingPayment(Origin.Hot(ActorRef.noSender, Upstream.Hot.Trampoline(Seq(Upstream.Hot.Channel(add_c, TimestampMilli(1687345927000L), b)))), paymentHash, Route(inner_c.amountToForward, afterTrampolineChannelHops, None), recipient_e)
val add_d = UpdateAddHtlc(randomBytes32(), 3, payment_e.cmd.amount, paymentHash, payment_e.cmd.cltvExpiry, payment_e.cmd.onion, None)
val Right(payment_e) = buildOutgoingPayment(Origin.Hot(ActorRef.noSender, Upstream.Hot.Trampoline(Seq(Upstream.Hot.Channel(add_c, TimestampMilli(1687345927000L), b)))), paymentHash, Route(inner_c.amountToForward, afterTrampolineChannelHops, None), recipient_e, 1.0)
val add_d = UpdateAddHtlc(randomBytes32(), 3, payment_e.cmd.amount, paymentHash, payment_e.cmd.cltvExpiry, payment_e.cmd.onion, None, 1.0)
val Right(ChannelRelayPacket(_, payload_d, packet_e)) = decrypt(add_d, priv_d.privateKey, Features.empty)
val add_e = UpdateAddHtlc(randomBytes32(), 4, payload_d.amountToForward(add_d.amountMsat), paymentHash, payload_d.outgoingCltv(add_d.cltvExpiry), packet_e, None)
val add_e = UpdateAddHtlc(randomBytes32(), 4, payload_d.amountToForward(add_d.amountMsat), paymentHash, payload_d.outgoingCltv(add_d.cltvExpiry), packet_e, None, 1.0)
val Left(failure) = decrypt(add_e, priv_e.privateKey, Features.empty)
assert(failure == FinalIncorrectCltvExpiry(invalidExpiry))
}
@ -660,14 +660,14 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
test("build htlc failure onion") {
// a -> b -> c -> d -> e
val recipient = ClearRecipient(e, Features.empty, finalAmount, finalExpiry, paymentSecret)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(finalAmount, hops, None), recipient)
val add_b = UpdateAddHtlc(randomBytes32(), 0, amount_ab, paymentHash, expiry_ab, payment.cmd.onion, None)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(finalAmount, hops, None), recipient, 1.0)
val add_b = UpdateAddHtlc(randomBytes32(), 0, amount_ab, paymentHash, expiry_ab, payment.cmd.onion, None, 1.0)
val Right(ChannelRelayPacket(_, _, packet_c)) = decrypt(add_b, priv_b.privateKey, Features.empty)
val add_c = UpdateAddHtlc(randomBytes32(), 1, amount_bc, paymentHash, expiry_bc, packet_c, None)
val add_c = UpdateAddHtlc(randomBytes32(), 1, amount_bc, paymentHash, expiry_bc, packet_c, None, 1.0)
val Right(ChannelRelayPacket(_, _, packet_d)) = decrypt(add_c, priv_c.privateKey, Features.empty)
val add_d = UpdateAddHtlc(randomBytes32(), 2, amount_cd, paymentHash, expiry_cd, packet_d, None)
val add_d = UpdateAddHtlc(randomBytes32(), 2, amount_cd, paymentHash, expiry_cd, packet_d, None, 1.0)
val Right(ChannelRelayPacket(_, _, packet_e)) = decrypt(add_d, priv_d.privateKey, Features.empty)
val add_e = UpdateAddHtlc(randomBytes32(), 3, amount_de, paymentHash, expiry_de, packet_e, None)
val add_e = UpdateAddHtlc(randomBytes32(), 3, amount_de, paymentHash, expiry_de, packet_e, None, 1.0)
val Right(FinalPacket(_, payload_e)) = decrypt(add_e, priv_e.privateKey, Features.empty)
assert(payload_e.isInstanceOf[FinalPayload.Standard])
@ -689,16 +689,16 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
test("build htlc failure onion (blinded payment)") {
// a -> b -> c -> d -> e, blinded after c
val (_, route, recipient) = longBlindedHops(hex"0451")
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient)
val add_b = UpdateAddHtlc(randomBytes32(), 0, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient, 1.0)
val add_b = UpdateAddHtlc(randomBytes32(), 0, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt, 1.0)
val Right(ChannelRelayPacket(_, _, packet_c)) = decrypt(add_b, priv_b.privateKey, Features.empty)
val add_c = UpdateAddHtlc(randomBytes32(), 1, amount_bc, paymentHash, expiry_bc, packet_c, None)
val add_c = UpdateAddHtlc(randomBytes32(), 1, amount_bc, paymentHash, expiry_bc, packet_c, None, 1.0)
val Right(ChannelRelayPacket(_, payload_c, packet_d)) = decrypt(add_c, priv_c.privateKey, Features(RouteBlinding -> Optional))
val blinding_d = payload_c.asInstanceOf[IntermediatePayload.ChannelRelay.Blinded].nextBlinding
val add_d = UpdateAddHtlc(randomBytes32(), 2, amount_cd, paymentHash, expiry_cd, packet_d, Some(blinding_d))
val add_d = UpdateAddHtlc(randomBytes32(), 2, amount_cd, paymentHash, expiry_cd, packet_d, Some(blinding_d), 1.0)
val Right(ChannelRelayPacket(_, payload_d, packet_e)) = decrypt(add_d, priv_d.privateKey, Features(RouteBlinding -> Optional))
val blinding_e = payload_d.asInstanceOf[IntermediatePayload.ChannelRelay.Blinded].nextBlinding
val add_e = UpdateAddHtlc(randomBytes32(), 3, amount_de, paymentHash, expiry_de, packet_e, Some(blinding_e))
val add_e = UpdateAddHtlc(randomBytes32(), 3, amount_de, paymentHash, expiry_de, packet_e, Some(blinding_e), 1.0)
val Right(FinalPacket(_, payload_e)) = decrypt(add_e, priv_e.privateKey, Features(RouteBlinding -> Optional))
assert(payload_e.isInstanceOf[FinalPayload.Blinded])
@ -796,7 +796,7 @@ object PaymentPacketSpec {
val blindedRoute = BlindedRouteCreation.createBlindedRouteWithoutHops(b, hex"deadbeef", 1.msat, routeExpiry).route
val finalPayload = NodePayload(blindedRoute.introductionNode.blindedPublicKey, OutgoingBlindedPerHopPayload.createFinalPayload(finalAmount, finalAmount, finalExpiry, blindedRoute.introductionNode.encryptedPayload))
val onion = buildOnion(Seq(finalPayload), paymentHash, Some(PaymentOnionCodecs.paymentOnionPayloadLength)).toOption.get // BOLT 2 requires that associatedData == paymentHash
val cmd = CMD_ADD_HTLC(ActorRef.noSender, finalAmount, paymentHash, finalExpiry, onion.packet, Some(blindedRoute.blindingKey), TestConstants.emptyOrigin, commit = true)
val cmd = CMD_ADD_HTLC(ActorRef.noSender, finalAmount, paymentHash, finalExpiry, onion.packet, Some(blindedRoute.blindingKey), 1.0, TestConstants.emptyOrigin, commit = true)
Right(OutgoingPaymentPacket(cmd, channelUpdate_ab.shortChannelId, onion.sharedSecrets))
}

View file

@ -700,9 +700,9 @@ object PostRestartHtlcCleanerSpec {
buildOutgoingBlindedPaymentAB(paymentHash)
} else {
val (route, recipient) = (Route(finalAmount, hops, None), SpontaneousRecipient(e, finalAmount, finalExpiry, randomBytes32()))
buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient)
buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient, 1.0)
}
UpdateAddHtlc(channelId, htlcId, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt)
UpdateAddHtlc(channelId, htlcId, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt, 1.0)
}
def buildHtlcIn(htlcId: Long, channelId: ByteVector32, paymentHash: ByteVector32, blinded: Boolean = false): DirectedHtlc = IncomingHtlc(buildHtlc(htlcId, channelId, paymentHash, blinded))
@ -710,8 +710,8 @@ object PostRestartHtlcCleanerSpec {
def buildHtlcOut(htlcId: Long, channelId: ByteVector32, paymentHash: ByteVector32, blinded: Boolean = false): DirectedHtlc = OutgoingHtlc(buildHtlc(htlcId, channelId, paymentHash, blinded))
def buildFinalHtlc(htlcId: Long, channelId: ByteVector32, paymentHash: ByteVector32): DirectedHtlc = {
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(finalAmount, Seq(channelHopFromUpdate(a, b, channelUpdate_ab)), None), SpontaneousRecipient(b, finalAmount, finalExpiry, randomBytes32()))
IncomingHtlc(UpdateAddHtlc(channelId, htlcId, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None))
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(finalAmount, Seq(channelHopFromUpdate(a, b, channelUpdate_ab)), None), SpontaneousRecipient(b, finalAmount, finalExpiry, randomBytes32()), 1.0)
IncomingHtlc(UpdateAddHtlc(channelId, htlcId, payment.cmd.amount, paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None, 1.0))
}
def buildForwardFail(add: UpdateAddHtlc, upstream: Upstream.Cold): RES_ADD_SETTLED[Origin.Cold, HtlcResult.Fail] =
@ -734,11 +734,11 @@ object PostRestartHtlcCleanerSpec {
val parentId = UUID.randomUUID()
val (id1, id2, id3) = (UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID())
val add1 = UpdateAddHtlc(channelId_bc_1, 72, 561 msat, paymentHash1, CltvExpiry(4200), onionRoutingPacket = TestConstants.emptyOnionPacket, blinding_opt = None)
val add1 = UpdateAddHtlc(channelId_bc_1, 72, 561 msat, paymentHash1, CltvExpiry(4200), onionRoutingPacket = TestConstants.emptyOnionPacket, blinding_opt = None, confidence = 1.0)
val origin1 = Origin.Cold(Upstream.Local(id1))
val add2 = UpdateAddHtlc(channelId_bc_1, 75, 1105 msat, paymentHash2, CltvExpiry(4250), onionRoutingPacket = TestConstants.emptyOnionPacket, blinding_opt = None)
val add2 = UpdateAddHtlc(channelId_bc_1, 75, 1105 msat, paymentHash2, CltvExpiry(4250), onionRoutingPacket = TestConstants.emptyOnionPacket, blinding_opt = None, confidence = 1.0)
val origin2 = Origin.Cold(Upstream.Local(id2))
val add3 = UpdateAddHtlc(channelId_bc_1, 78, 1729 msat, paymentHash2, CltvExpiry(4300), onionRoutingPacket = TestConstants.emptyOnionPacket, blinding_opt = None)
val add3 = UpdateAddHtlc(channelId_bc_1, 78, 1729 msat, paymentHash2, CltvExpiry(4300), onionRoutingPacket = TestConstants.emptyOnionPacket, blinding_opt = None, confidence = 1.0)
val origin3 = Origin.Cold(Upstream.Local(id3))
// Prepare channels and payment state before restart.
@ -847,10 +847,10 @@ object PostRestartHtlcCleanerSpec {
val notRelayed = Set((1L, channelId_bc_1), (0L, channelId_bc_2), (3L, channelId_bc_3), (5L, channelId_bc_3), (7L, channelId_bc_4))
val downstream_1_1 = UpdateAddHtlc(channelId_bc_1, 6L, finalAmount, paymentHash1, finalExpiry, TestConstants.emptyOnionPacket, None)
val downstream_2_1 = UpdateAddHtlc(channelId_bc_1, 8L, finalAmount, paymentHash2, finalExpiry, TestConstants.emptyOnionPacket, None)
val downstream_2_2 = UpdateAddHtlc(channelId_bc_2, 1L, finalAmount, paymentHash2, finalExpiry, TestConstants.emptyOnionPacket, None)
val downstream_2_3 = UpdateAddHtlc(channelId_bc_3, 4L, finalAmount, paymentHash2, finalExpiry, TestConstants.emptyOnionPacket, None)
val downstream_1_1 = UpdateAddHtlc(channelId_bc_1, 6L, finalAmount, paymentHash1, finalExpiry, TestConstants.emptyOnionPacket, None, 1.0)
val downstream_2_1 = UpdateAddHtlc(channelId_bc_1, 8L, finalAmount, paymentHash2, finalExpiry, TestConstants.emptyOnionPacket, None, 1.0)
val downstream_2_2 = UpdateAddHtlc(channelId_bc_2, 1L, finalAmount, paymentHash2, finalExpiry, TestConstants.emptyOnionPacket, None, 1.0)
val downstream_2_3 = UpdateAddHtlc(channelId_bc_3, 4L, finalAmount, paymentHash2, finalExpiry, TestConstants.emptyOnionPacket, None, 1.0)
val data_ab_1 = ChannelCodecsSpec.makeChannelDataNormal(htlc_ab_1, Map.empty)
val data_ab_2 = ChannelCodecsSpec.makeChannelDataNormal(htlc_ab_2, Map.empty)

View file

@ -68,11 +68,12 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a
fwd
}
def expectFwdAdd(register: TestProbe[Any], channelId: ByteVector32, outAmount: MilliSatoshi, outExpiry: CltvExpiry): Register.Forward[CMD_ADD_HTLC] = {
def expectFwdAdd(register: TestProbe[Any], channelId: ByteVector32, outAmount: MilliSatoshi, outExpiry: CltvExpiry, outEndorsement: Int): Register.Forward[CMD_ADD_HTLC] = {
val fwd = register.expectMessageType[Register.Forward[CMD_ADD_HTLC]]
inside(fwd.message) { case add: CMD_ADD_HTLC =>
assert(add.amount == outAmount)
assert(add.cltvExpiry == outExpiry)
assert((add.confidence * 7.999).toInt == outEndorsement)
}
assert(fwd.channelId == channelId)
fwd
@ -88,7 +89,7 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a
channelRelayer ! Relay(r, TestConstants.Alice.nodeParams.nodeId)
if (success) {
expectFwdAdd(register, lcu.channelId, outgoingAmount, outgoingExpiry)
expectFwdAdd(register, lcu.channelId, outgoingAmount, outgoingExpiry, 7)
} else {
expectFwdFail(register, r.add.channelId, CMD_FAIL_HTLC(r.add.id, Right(UnknownNextPeer()), commit = true))
}
@ -135,7 +136,7 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a
val r1 = createValidIncomingPacket(payload1)
channelRelayer ! WrappedLocalChannelUpdate(lcu1)
channelRelayer ! Relay(r1, TestConstants.Alice.nodeParams.nodeId)
expectFwdAdd(register, lcu1.channelId, outgoingAmount, outgoingExpiry)
expectFwdAdd(register, lcu1.channelId, outgoingAmount, outgoingExpiry, 7)
// reorg happens
val realScid1AfterReorg = RealShortChannelId(111112)
@ -146,10 +147,10 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a
// both old and new real scids work
channelRelayer ! Relay(r1, TestConstants.Alice.nodeParams.nodeId)
expectFwdAdd(register, lcu1.channelId, outgoingAmount, outgoingExpiry)
expectFwdAdd(register, lcu1.channelId, outgoingAmount, outgoingExpiry, 7)
// new real scid works
channelRelayer ! Relay(r2, TestConstants.Alice.nodeParams.nodeId)
expectFwdAdd(register, lcu2.channelId, outgoingAmount, outgoingExpiry)
expectFwdAdd(register, lcu2.channelId, outgoingAmount, outgoingExpiry, 7)
}
test("relay blinded payment") { f =>
@ -162,7 +163,7 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a
channelRelayer ! WrappedLocalChannelUpdate(u)
channelRelayer ! Relay(r, TestConstants.Alice.nodeParams.nodeId)
expectFwdAdd(register, channelIds(realScid1), outgoingAmount, outgoingExpiry)
expectFwdAdd(register, channelIds(realScid1), outgoingAmount, outgoingExpiry, 7)
}
test("relay with retries") { f =>
@ -182,12 +183,12 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a
channelRelayer ! Relay(r, TestConstants.Alice.nodeParams.nodeId)
// first try
val fwd1 = expectFwdAdd(register, channelIds(realScid2), outgoingAmount, outgoingExpiry)
val fwd1 = expectFwdAdd(register, channelIds(realScid2), outgoingAmount, outgoingExpiry, 7)
// channel returns an error
fwd1.message.replyTo ! RES_ADD_FAILED(fwd1.message, HtlcValueTooHighInFlight(channelIds(realScid2), 1000000000 msat, 1516977616 msat), Some(u2.channelUpdate))
// second try
val fwd2 = expectFwdAdd(register, channelIds(realScid1), outgoingAmount, outgoingExpiry)
val fwd2 = expectFwdAdd(register, channelIds(realScid1), outgoingAmount, outgoingExpiry, 7)
// failure again
fwd1.message.replyTo ! RES_ADD_FAILED(fwd2.message, HtlcValueTooHighInFlight(channelIds(realScid1), 1000000000 msat, 1516977616 msat), Some(u1.channelUpdate))
@ -216,7 +217,7 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a
channelRelayer ! WrappedLocalChannelUpdate(u)
channelRelayer ! Relay(r, TestConstants.Alice.nodeParams.nodeId)
val fwd = expectFwdAdd(register, channelIds(realScid1), outgoingAmount, outgoingExpiry)
val fwd = expectFwdAdd(register, channelIds(realScid1), outgoingAmount, outgoingExpiry, 7)
fwd.replyTo ! Register.ForwardFailure(fwd)
expectFwdFail(register, r.add.channelId, CMD_FAIL_HTLC(r.add.id, Right(UnknownNextPeer()), commit = true))
@ -302,7 +303,7 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a
channelRelayer ! WrappedLocalChannelUpdate(u)
channelRelayer ! Relay(r, TestConstants.Alice.nodeParams.nodeId)
expectFwdAdd(register, channelIds(realScid1), r.amountToForward, r.outgoingCltv).message
expectFwdAdd(register, channelIds(realScid1), r.amountToForward, r.outgoingCltv, 7).message
}
test("fail to relay when expiry is too small") { f =>
@ -342,7 +343,7 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a
channelRelayer ! Relay(r, TestConstants.Alice.nodeParams.nodeId)
// relay succeeds with current channel update (u1) with lower fees
expectFwdAdd(register, channelIds(realScid1), outgoingAmount, outgoingExpiry)
expectFwdAdd(register, channelIds(realScid1), outgoingAmount, outgoingExpiry, 7)
val u2 = createLocalUpdate(channelId1, timestamp = TimestampSecond.now() - 530)
@ -350,7 +351,7 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a
channelRelayer ! Relay(r, TestConstants.Alice.nodeParams.nodeId)
// relay succeeds because the current update (u2) with higher fees occurred less than 10 minutes ago
expectFwdAdd(register, channelIds(realScid1), outgoingAmount, outgoingExpiry)
expectFwdAdd(register, channelIds(realScid1), outgoingAmount, outgoingExpiry, 7)
val u3 = createLocalUpdate(channelId1, timestamp = TimestampSecond.now() - 601)
@ -386,7 +387,7 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a
testCases.foreach { testCase =>
channelRelayer ! WrappedLocalChannelUpdate(u)
channelRelayer ! Relay(r, TestConstants.Alice.nodeParams.nodeId)
val fwd = expectFwdAdd(register, channelIds(realScid1), outgoingAmount, outgoingExpiry)
val fwd = expectFwdAdd(register, channelIds(realScid1), outgoingAmount, outgoingExpiry, 7)
fwd.message.replyTo ! RES_ADD_FAILED(fwd.message, testCase.exc, Some(testCase.update))
expectFwdFail(register, r.add.channelId, CMD_FAIL_HTLC(r.add.id, Right(testCase.failure), commit = true))
}
@ -419,19 +420,19 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a
{
val payload = ChannelRelay.Standard(ShortChannelId(12345), 998900 msat, CltvExpiry(60))
val r = createValidIncomingPacket(payload, 1000000 msat, CltvExpiry(70))
val r = createValidIncomingPacket(payload, 1000000 msat, CltvExpiry(70), endorsementIn = 5)
channelRelayer ! Relay(r, TestConstants.Alice.nodeParams.nodeId)
// select the channel to the same node, with the lowest capacity and balance but still high enough to handle the payment
val cmd1 = expectFwdAdd(register, channelUpdates(ShortChannelId(22223)).channelId, r.amountToForward, r.outgoingCltv).message
val cmd1 = expectFwdAdd(register, channelUpdates(ShortChannelId(22223)).channelId, r.amountToForward, r.outgoingCltv, 5).message
cmd1.replyTo ! RES_ADD_FAILED(cmd1, ChannelUnavailable(randomBytes32()), None)
// select 2nd-to-best channel: higher capacity and balance
val cmd2 = expectFwdAdd(register, channelUpdates(ShortChannelId(22222)).channelId, r.amountToForward, r.outgoingCltv).message
val cmd2 = expectFwdAdd(register, channelUpdates(ShortChannelId(22222)).channelId, r.amountToForward, r.outgoingCltv, 5).message
cmd2.replyTo ! RES_ADD_FAILED(cmd2, TooManyAcceptedHtlcs(randomBytes32(), 42), Some(channelUpdates(ShortChannelId(22222)).channelUpdate))
// select 3rd-to-best channel: same balance but higher capacity
val cmd3 = expectFwdAdd(register, channelUpdates(ShortChannelId(12345)).channelId, r.amountToForward, r.outgoingCltv).message
val cmd3 = expectFwdAdd(register, channelUpdates(ShortChannelId(12345)).channelId, r.amountToForward, r.outgoingCltv, 5).message
cmd3.replyTo ! RES_ADD_FAILED(cmd3, TooManyAcceptedHtlcs(randomBytes32(), 42), Some(channelUpdates(ShortChannelId(12345)).channelUpdate))
// select 4th-to-best channel: same capacity but higher balance
val cmd4 = expectFwdAdd(register, channelUpdates(ShortChannelId(11111)).channelId, r.amountToForward, r.outgoingCltv).message
val cmd4 = expectFwdAdd(register, channelUpdates(ShortChannelId(11111)).channelId, r.amountToForward, r.outgoingCltv, 5).message
cmd4.replyTo ! RES_ADD_FAILED(cmd4, HtlcValueTooHighInFlight(randomBytes32(), 100000000 msat, 100000000 msat), Some(channelUpdates(ShortChannelId(11111)).channelUpdate))
// all the suitable channels have been tried
expectFwdFail(register, r.add.channelId, CMD_FAIL_HTLC(r.add.id, Right(TemporaryChannelFailure(Some(channelUpdates(ShortChannelId(12345)).channelUpdate))), commit = true))
@ -439,30 +440,30 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a
{
// higher amount payment (have to increased incoming htlc amount for fees to be sufficient)
val payload = ChannelRelay.Standard(ShortChannelId(12345), 50000000 msat, CltvExpiry(60))
val r = createValidIncomingPacket(payload, 60000000 msat, CltvExpiry(70))
val r = createValidIncomingPacket(payload, 60000000 msat, CltvExpiry(70), endorsementIn = 0)
channelRelayer ! Relay(r, TestConstants.Alice.nodeParams.nodeId)
expectFwdAdd(register, channelUpdates(ShortChannelId(11111)).channelId, r.amountToForward, r.outgoingCltv).message
expectFwdAdd(register, channelUpdates(ShortChannelId(11111)).channelId, r.amountToForward, r.outgoingCltv, 0).message
}
{
// lower amount payment
val payload = ChannelRelay.Standard(ShortChannelId(12345), 1000 msat, CltvExpiry(60))
val r = createValidIncomingPacket(payload, 60000000 msat, CltvExpiry(70))
val r = createValidIncomingPacket(payload, 60000000 msat, CltvExpiry(70), endorsementIn = 6)
channelRelayer ! Relay(r, TestConstants.Alice.nodeParams.nodeId)
expectFwdAdd(register, channelUpdates(ShortChannelId(33333)).channelId, r.amountToForward, r.outgoingCltv).message
expectFwdAdd(register, channelUpdates(ShortChannelId(33333)).channelId, r.amountToForward, r.outgoingCltv, 6).message
}
{
// payment too high, no suitable channel found, we keep the requested one
val payload = ChannelRelay.Standard(ShortChannelId(12345), 1000000000 msat, CltvExpiry(60))
val r = createValidIncomingPacket(payload, 1010000000 msat, CltvExpiry(70))
channelRelayer ! Relay(r, TestConstants.Alice.nodeParams.nodeId)
expectFwdAdd(register, channelUpdates(ShortChannelId(12345)).channelId, r.amountToForward, r.outgoingCltv).message
expectFwdAdd(register, channelUpdates(ShortChannelId(12345)).channelId, r.amountToForward, r.outgoingCltv, 7).message
}
{
// cltv expiry larger than our requirements
val payload = ChannelRelay.Standard(ShortChannelId(12345), 998900 msat, CltvExpiry(50))
val r = createValidIncomingPacket(payload, 1000000 msat, CltvExpiry(70))
channelRelayer ! Relay(r, TestConstants.Alice.nodeParams.nodeId)
expectFwdAdd(register, channelUpdates(ShortChannelId(22223)).channelId, r.amountToForward, r.outgoingCltv).message
expectFwdAdd(register, channelUpdates(ShortChannelId(22223)).channelId, r.amountToForward, r.outgoingCltv, 7).message
}
{
// cltv expiry too small, no suitable channel found
@ -480,7 +481,7 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a
val payload = ChannelRelay.Standard(realScid1, outgoingAmount, outgoingExpiry)
val r = createValidIncomingPacket(payload, outgoingAmount + u.channelUpdate.feeBaseMsat, outgoingExpiry + u.channelUpdate.cltvExpiryDelta)
val u_disabled = createLocalUpdate(channelId1, enabled = false)
val downstream_htlc = UpdateAddHtlc(channelId1, 7, outgoingAmount, paymentHash, outgoingExpiry, emptyOnionPacket, None)
val downstream_htlc = UpdateAddHtlc(channelId1, 7, outgoingAmount, paymentHash, outgoingExpiry, emptyOnionPacket, None, 1.0)
case class TestCase(result: HtlcResult, cmd: channel.HtlcSettlementCommand)
@ -495,7 +496,7 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a
testCases.foreach { testCase =>
channelRelayer ! WrappedLocalChannelUpdate(u)
channelRelayer ! Relay(r, TestConstants.Alice.nodeParams.nodeId)
val fwd = expectFwdAdd(register, channelIds(realScid1), outgoingAmount, outgoingExpiry)
val fwd = expectFwdAdd(register, channelIds(realScid1), outgoingAmount, outgoingExpiry, 7)
fwd.message.replyTo ! RES_SUCCESS(fwd.message, channelId1)
fwd.message.origin.replyTo ! RES_ADD_SETTLED(fwd.message.origin, downstream_htlc, testCase.result)
expectFwdFail(register, r.add.channelId, testCase.cmd)
@ -506,7 +507,7 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a
import f._
val u = createLocalUpdate(channelId1, feeBaseMsat = 5000 msat, feeProportionalMillionths = 0)
val downstream = UpdateAddHtlc(channelId1, 7, outgoingAmount, paymentHash, outgoingExpiry, emptyOnionPacket, None)
val downstream = UpdateAddHtlc(channelId1, 7, outgoingAmount, paymentHash, outgoingExpiry, emptyOnionPacket, None, 0.0625)
val testCases = Seq(
HtlcResult.RemoteFail(UpdateFailHtlc(channelId1, downstream.id, hex"deadbeef")),
@ -518,10 +519,10 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a
Seq(true, false).foreach { isIntroduction =>
testCases.foreach { htlcResult =>
val r = createValidIncomingPacket(createBlindedPayload(u.channelUpdate, isIntroduction), outgoingAmount + u.channelUpdate.feeBaseMsat, outgoingExpiry + u.channelUpdate.cltvExpiryDelta)
val r = createValidIncomingPacket(createBlindedPayload(u.channelUpdate, isIntroduction), outgoingAmount + u.channelUpdate.feeBaseMsat, outgoingExpiry + u.channelUpdate.cltvExpiryDelta, endorsementIn = 0)
channelRelayer ! WrappedLocalChannelUpdate(u)
channelRelayer ! Relay(r, TestConstants.Alice.nodeParams.nodeId)
val fwd = expectFwdAdd(register, channelId1, outgoingAmount, outgoingExpiry)
val fwd = expectFwdAdd(register, channelId1, outgoingAmount, outgoingExpiry, 0)
fwd.message.replyTo ! RES_SUCCESS(fwd.message, channelId1)
fwd.message.origin.replyTo ! RES_ADD_SETTLED(fwd.message.origin, downstream, htlcResult)
val cmd = register.expectMessageType[Register.Forward[channel.Command]]
@ -550,9 +551,9 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a
val channelId1 = channelIds(realScid1)
val payload = ChannelRelay.Standard(realScid1, outgoingAmount, outgoingExpiry)
val r = createValidIncomingPacket(payload)
val r = createValidIncomingPacket(payload, endorsementIn = 3)
val u = createLocalUpdate(channelId1)
val downstream_htlc = UpdateAddHtlc(channelId1, 7, outgoingAmount, paymentHash, outgoingExpiry, emptyOnionPacket, None)
val downstream_htlc = UpdateAddHtlc(channelId1, 7, outgoingAmount, paymentHash, outgoingExpiry, emptyOnionPacket, None, 0.4375)
case class TestCase(result: HtlcResult)
@ -565,7 +566,7 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a
channelRelayer ! WrappedLocalChannelUpdate(u)
channelRelayer ! Relay(r, TestConstants.Alice.nodeParams.nodeId)
val fwd1 = expectFwdAdd(register, channelIds(realScid1), outgoingAmount, outgoingExpiry)
val fwd1 = expectFwdAdd(register, channelIds(realScid1), outgoingAmount, outgoingExpiry, 3)
fwd1.message.replyTo ! RES_SUCCESS(fwd1.message, channelId1)
fwd1.message.origin.replyTo ! RES_ADD_SETTLED(fwd1.message.origin, downstream_htlc, testCase.result)
@ -665,12 +666,13 @@ object ChannelRelayerSpec {
ChannelRelay.Blinded(tlvs, PaymentRelayData(blindedTlvs), randomKey().publicKey)
}
def createValidIncomingPacket(payload: IntermediatePayload.ChannelRelay, amountIn: MilliSatoshi = 11_000_000 msat, expiryIn: CltvExpiry = CltvExpiry(400_100)): IncomingPaymentPacket.ChannelRelayPacket = {
def createValidIncomingPacket(payload: IntermediatePayload.ChannelRelay, amountIn: MilliSatoshi = 11_000_000 msat, expiryIn: CltvExpiry = CltvExpiry(400_100), endorsementIn: Int = 7): IncomingPaymentPacket.ChannelRelayPacket = {
val nextBlinding_opt = payload match {
case p: ChannelRelay.Blinded => Some(p.nextBlinding)
case p: ChannelRelay.Blinded => Some(UpdateAddHtlcTlv.BlindingPoint(p.nextBlinding))
case _: ChannelRelay.Standard => None
}
val add_ab = UpdateAddHtlc(channelId = randomBytes32(), id = 123456, amountIn, paymentHash, expiryIn, emptyOnionPacket, nextBlinding_opt)
val tlvs = TlvStream(Set[Option[UpdateAddHtlcTlv]](nextBlinding_opt, Some(UpdateAddHtlcTlv.Endorsement(endorsementIn))).flatten)
val add_ab = UpdateAddHtlc(channelId = randomBytes32(), id = 123456, amountIn, paymentHash, expiryIn, emptyOnionPacket, tlvs)
ChannelRelayPacket(add_ab, payload, emptyOnionPacket)
}

View file

@ -225,7 +225,7 @@ class NodeRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("appl
incomingMultiPart.foreach(incoming => nodeRelayer ! NodeRelay.Relay(incoming, randomKey().publicKey))
// and then one extra
val extra = IncomingPaymentPacket.RelayToTrampolinePacket(
UpdateAddHtlc(randomBytes32(), Random.nextInt(100), 1000 msat, paymentHash, CltvExpiry(499990), TestConstants.emptyOnionPacket, None),
UpdateAddHtlc(randomBytes32(), Random.nextInt(100), 1000 msat, paymentHash, CltvExpiry(499990), TestConstants.emptyOnionPacket, None, 1.0),
FinalPayload.Standard.createPayload(1000 msat, incomingAmount, CltvExpiry(499990), incomingSecret, None),
IntermediatePayload.NodeRelay.Standard(outgoingAmount, outgoingExpiry, outgoingNodeId),
nextTrampolinePacket)
@ -248,13 +248,13 @@ class NodeRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("appl
incomingMultiPart.foreach(incoming => nodeRelayer ! NodeRelay.Relay(incoming, randomKey().publicKey))
val outgoingCfg = mockPayFSM.expectMessageType[SendPaymentConfig]
validateOutgoingCfg(outgoingCfg, Upstream.Hot.Trampoline(incomingMultiPart.map(p => Upstream.Hot.Channel(p.add, TimestampMilli.now(), randomKey().publicKey))))
validateOutgoingCfg(outgoingCfg, Upstream.Hot.Trampoline(incomingMultiPart.map(p => Upstream.Hot.Channel(p.add, TimestampMilli.now(), randomKey().publicKey))), 5)
val outgoingPayment = mockPayFSM.expectMessageType[SendMultiPartPayment]
validateOutgoingPayment(outgoingPayment)
// Receive new extraneous multi-part HTLC.
val i1 = IncomingPaymentPacket.RelayToTrampolinePacket(
UpdateAddHtlc(randomBytes32(), Random.nextInt(100), 1000 msat, paymentHash, CltvExpiry(499990), TestConstants.emptyOnionPacket, None),
UpdateAddHtlc(randomBytes32(), Random.nextInt(100), 1000 msat, paymentHash, CltvExpiry(499990), TestConstants.emptyOnionPacket, None, 1.0),
FinalPayload.Standard.createPayload(1000 msat, incomingAmount, CltvExpiry(499990), incomingSecret, None),
IntermediatePayload.NodeRelay.Standard(outgoingAmount, outgoingExpiry, outgoingNodeId),
nextTrampolinePacket)
@ -267,7 +267,7 @@ class NodeRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("appl
// Receive new HTLC with different details, but for the same payment hash.
val i2 = IncomingPaymentPacket.RelayToTrampolinePacket(
UpdateAddHtlc(randomBytes32(), Random.nextInt(100), 1500 msat, paymentHash, CltvExpiry(499990), TestConstants.emptyOnionPacket, None),
UpdateAddHtlc(randomBytes32(), Random.nextInt(100), 1500 msat, paymentHash, CltvExpiry(499990), TestConstants.emptyOnionPacket, None, 1.0),
PaymentOnion.FinalPayload.Standard.createPayload(1500 msat, 1500 msat, CltvExpiry(499990), incomingSecret, None),
IntermediatePayload.NodeRelay.Standard(1250 msat, outgoingExpiry, outgoingNodeId),
nextTrampolinePacket)
@ -377,7 +377,7 @@ class NodeRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("appl
// upstream payment relayed
val outgoingCfg = mockPayFSM.expectMessageType[SendPaymentConfig]
validateOutgoingCfg(outgoingCfg, Upstream.Hot.Trampoline(incomingAsyncPayment.map(p => Upstream.Hot.Channel(p.add, TimestampMilli.now(), randomKey().publicKey))))
validateOutgoingCfg(outgoingCfg, Upstream.Hot.Trampoline(incomingAsyncPayment.map(p => Upstream.Hot.Channel(p.add, TimestampMilli.now(), randomKey().publicKey))), 5)
val outgoingPayment = mockPayFSM.expectMessageType[SendMultiPartPayment]
validateOutgoingPayment(outgoingPayment)
// those are adapters for pay-fsm messages
@ -454,7 +454,7 @@ class NodeRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("appl
// upstream payment relayed
val outgoingCfg = mockPayFSM.expectMessageType[SendPaymentConfig]
validateOutgoingCfg(outgoingCfg, Upstream.Hot.Trampoline(incomingAsyncPayment.map(p => Upstream.Hot.Channel(p.add, TimestampMilli.now(), randomKey().publicKey))))
validateOutgoingCfg(outgoingCfg, Upstream.Hot.Trampoline(incomingAsyncPayment.map(p => Upstream.Hot.Channel(p.add, TimestampMilli.now(), randomKey().publicKey))), 5)
val outgoingPayment = mockPayFSM.expectMessageType[SendMultiPartPayment]
validateOutgoingPayment(outgoingPayment)
// those are adapters for pay-fsm messages
@ -662,7 +662,7 @@ class NodeRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("appl
nodeRelayer ! NodeRelay.Relay(incomingMultiPart.last, randomKey().publicKey)
val outgoingCfg = mockPayFSM.expectMessageType[SendPaymentConfig]
validateOutgoingCfg(outgoingCfg, Upstream.Hot.Trampoline(incomingMultiPart.map(p => Upstream.Hot.Channel(p.add, TimestampMilli.now(), randomKey().publicKey))))
validateOutgoingCfg(outgoingCfg, Upstream.Hot.Trampoline(incomingMultiPart.map(p => Upstream.Hot.Channel(p.add, TimestampMilli.now(), randomKey().publicKey))), 5)
val outgoingPayment = mockPayFSM.expectMessageType[SendMultiPartPayment]
validateOutgoingPayment(outgoingPayment)
// those are adapters for pay-fsm messages
@ -698,7 +698,7 @@ class NodeRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("appl
nodeRelayer ! NodeRelay.Relay(incomingSinglePart, randomKey().publicKey)
val outgoingCfg = mockPayFSM.expectMessageType[SendPaymentConfig]
validateOutgoingCfg(outgoingCfg, Upstream.Hot.Trampoline(Upstream.Hot.Channel(incomingSinglePart.add, TimestampMilli.now(), randomKey().publicKey) :: Nil))
validateOutgoingCfg(outgoingCfg, Upstream.Hot.Trampoline(Upstream.Hot.Channel(incomingSinglePart.add, TimestampMilli.now(), randomKey().publicKey) :: Nil), 7)
val outgoingPayment = mockPayFSM.expectMessageType[SendMultiPartPayment]
validateOutgoingPayment(outgoingPayment)
// those are adapters for pay-fsm messages
@ -733,7 +733,7 @@ class NodeRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("appl
incomingPayments.foreach(incoming => nodeRelayer ! NodeRelay.Relay(incoming, randomKey().publicKey))
val outgoingCfg = mockPayFSM.expectMessageType[SendPaymentConfig]
validateOutgoingCfg(outgoingCfg, Upstream.Hot.Trampoline(incomingMultiPart.map(p => Upstream.Hot.Channel(p.add, TimestampMilli.now(), randomKey().publicKey))))
validateOutgoingCfg(outgoingCfg, Upstream.Hot.Trampoline(incomingMultiPart.map(p => Upstream.Hot.Channel(p.add, TimestampMilli.now(), randomKey().publicKey))), 5)
val outgoingPayment = mockPayFSM.expectMessageType[SendMultiPartPayment]
assert(outgoingPayment.recipient.nodeId == outgoingNodeId)
assert(outgoingPayment.recipient.totalAmount == outgoingAmount)
@ -777,7 +777,7 @@ class NodeRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("appl
incomingPayments.foreach(incoming => nodeRelayer ! NodeRelay.Relay(incoming, randomKey().publicKey))
val outgoingCfg = mockPayFSM.expectMessageType[SendPaymentConfig]
validateOutgoingCfg(outgoingCfg, Upstream.Hot.Trampoline(incomingMultiPart.map(p => Upstream.Hot.Channel(p.add, TimestampMilli.now(), randomKey().publicKey))))
validateOutgoingCfg(outgoingCfg, Upstream.Hot.Trampoline(incomingMultiPart.map(p => Upstream.Hot.Channel(p.add, TimestampMilli.now(), randomKey().publicKey))), 5)
val outgoingPayment = mockPayFSM.expectMessageType[SendPaymentToNode]
assert(outgoingPayment.recipient.nodeId == outgoingNodeId)
assert(outgoingPayment.amount == outgoingAmount)
@ -846,7 +846,7 @@ class NodeRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("appl
incomingPayments.foreach(incoming => nodeRelayer ! NodeRelay.Relay(incoming, randomKey().publicKey))
val outgoingCfg = mockPayFSM.expectMessageType[SendPaymentConfig]
validateOutgoingCfg(outgoingCfg, Upstream.Hot.Trampoline(incomingMultiPart.map(p => Upstream.Hot.Channel(p.add, TimestampMilli.now(), randomKey().publicKey))), ignoreNodeId = true)
validateOutgoingCfg(outgoingCfg, Upstream.Hot.Trampoline(incomingMultiPart.map(p => Upstream.Hot.Channel(p.add, TimestampMilli.now(), randomKey().publicKey))), 5, ignoreNodeId = true)
val outgoingPayment = mockPayFSM.expectMessageType[SendPaymentToNode]
assert(outgoingPayment.amount == outgoingAmount)
assert(outgoingPayment.recipient.expiry == outgoingExpiry)
@ -885,7 +885,7 @@ class NodeRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("appl
incomingPayments.foreach(incoming => nodeRelayer ! NodeRelay.Relay(incoming, randomKey().publicKey))
val outgoingCfg = mockPayFSM.expectMessageType[SendPaymentConfig]
validateOutgoingCfg(outgoingCfg, Upstream.Hot.Trampoline(incomingMultiPart.map(p => Upstream.Hot.Channel(p.add, TimestampMilli.now(), randomKey().publicKey))), ignoreNodeId = true)
validateOutgoingCfg(outgoingCfg, Upstream.Hot.Trampoline(incomingMultiPart.map(p => Upstream.Hot.Channel(p.add, TimestampMilli.now(), randomKey().publicKey))), 5, ignoreNodeId = true)
val outgoingPayment = mockPayFSM.expectMessageType[SendMultiPartPayment]
assert(outgoingPayment.recipient.totalAmount == outgoingAmount)
assert(outgoingPayment.recipient.expiry == outgoingExpiry)
@ -932,7 +932,7 @@ class NodeRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("appl
getNodeId.replyTo ! Some(outgoingNodeId)
val outgoingCfg = mockPayFSM.expectMessageType[SendPaymentConfig]
validateOutgoingCfg(outgoingCfg, Upstream.Hot.Trampoline(incomingMultiPart.map(p => Upstream.Hot.Channel(p.add, TimestampMilli.now(), randomKey().publicKey))), ignoreNodeId = true)
validateOutgoingCfg(outgoingCfg, Upstream.Hot.Trampoline(incomingMultiPart.map(p => Upstream.Hot.Channel(p.add, TimestampMilli.now(), randomKey().publicKey))), 5, ignoreNodeId = true)
val outgoingPayment = mockPayFSM.expectMessageType[SendPaymentToNode]
assert(outgoingPayment.amount == outgoingAmount)
assert(outgoingPayment.recipient.expiry == outgoingExpiry)
@ -987,7 +987,7 @@ class NodeRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("appl
}
}
def validateOutgoingCfg(outgoingCfg: SendPaymentConfig, upstream: Upstream, ignoreNodeId: Boolean = false): Unit = {
def validateOutgoingCfg(outgoingCfg: SendPaymentConfig, upstream: Upstream, endorsement: Int, ignoreNodeId: Boolean = false): Unit = {
assert(!outgoingCfg.publishEvent)
assert(!outgoingCfg.storeInDb)
assert(outgoingCfg.paymentHash == paymentHash)
@ -997,6 +997,7 @@ class NodeRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("appl
case (Upstream.Hot.Trampoline(adds1), Upstream.Hot.Trampoline(adds2)) => assert(adds1.map(_.add) == adds2.map(_.add))
case _ => assert(outgoingCfg.upstream == upstream)
}
assert((outgoingCfg.confidence * 7.999).toInt == endorsement)
}
def validateOutgoingPayment(outgoingPayment: SendMultiPartPayment): Unit = {
@ -1037,9 +1038,9 @@ object NodeRelayerSpec {
val incomingAmount = 41_000_000 msat
val incomingSecret = randomBytes32()
val incomingMultiPart = Seq(
createValidIncomingPacket(15_000_000 msat, incomingAmount, CltvExpiry(500000), outgoingAmount, outgoingExpiry),
createValidIncomingPacket(15_000_000 msat, incomingAmount, CltvExpiry(499999), outgoingAmount, outgoingExpiry),
createValidIncomingPacket(11_000_000 msat, incomingAmount, CltvExpiry(499999), outgoingAmount, outgoingExpiry)
createValidIncomingPacket(15_000_000 msat, incomingAmount, CltvExpiry(500000), outgoingAmount, outgoingExpiry, endorsementIn = 6),
createValidIncomingPacket(15_000_000 msat, incomingAmount, CltvExpiry(499999), outgoingAmount, outgoingExpiry, endorsementIn = 5),
createValidIncomingPacket(11_000_000 msat, incomingAmount, CltvExpiry(499999), outgoingAmount, outgoingExpiry, endorsementIn = 7)
)
val incomingSinglePart = createValidIncomingPacket(incomingAmount, incomingAmount, CltvExpiry(500000), outgoingAmount, outgoingExpiry)
val incomingAsyncPayment: Seq[RelayToTrampolinePacket] = incomingMultiPart.map(p => p.copy(innerPayload = IntermediatePayload.NodeRelay.Standard.createNodeRelayForAsyncPayment(p.innerPayload.amountToForward, p.innerPayload.outgoingCltv, outgoingNodeId)))
@ -1053,10 +1054,11 @@ object NodeRelayerSpec {
def createSuccessEvent(): PaymentSent =
PaymentSent(relayId, paymentHash, paymentPreimage, outgoingAmount, outgoingNodeId, Seq(PaymentSent.PartialPayment(UUID.randomUUID(), outgoingAmount, 10 msat, randomBytes32(), None)))
def createValidIncomingPacket(amountIn: MilliSatoshi, totalAmountIn: MilliSatoshi, expiryIn: CltvExpiry, amountOut: MilliSatoshi, expiryOut: CltvExpiry): RelayToTrampolinePacket = {
def createValidIncomingPacket(amountIn: MilliSatoshi, totalAmountIn: MilliSatoshi, expiryIn: CltvExpiry, amountOut: MilliSatoshi, expiryOut: CltvExpiry, endorsementIn: Int = 7): RelayToTrampolinePacket = {
val outerPayload = FinalPayload.Standard.createPayload(amountIn, totalAmountIn, expiryIn, incomingSecret, None)
val tlvs = TlvStream[UpdateAddHtlcTlv](UpdateAddHtlcTlv.Endorsement(endorsementIn))
RelayToTrampolinePacket(
UpdateAddHtlc(randomBytes32(), Random.nextInt(100), amountIn, paymentHash, expiryIn, TestConstants.emptyOnionPacket, None),
UpdateAddHtlc(randomBytes32(), Random.nextInt(100), amountIn, paymentHash, expiryIn, TestConstants.emptyOnionPacket, tlvs),
outerPayload,
IntermediatePayload.NodeRelay.Standard(amountOut, expiryOut, outgoingNodeId),
nextTrampolinePacket)
@ -1066,7 +1068,7 @@ object NodeRelayerSpec {
val (expiryIn, expiryOut) = (CltvExpiry(500000), CltvExpiry(490000))
val amountIn = incomingAmount / 2
RelayToTrampolinePacket(
UpdateAddHtlc(randomBytes32(), Random.nextInt(100), amountIn, paymentHash, expiryIn, TestConstants.emptyOnionPacket, None),
UpdateAddHtlc(randomBytes32(), Random.nextInt(100), amountIn, paymentHash, expiryIn, TestConstants.emptyOnionPacket, None, 1.0),
FinalPayload.Standard.createPayload(amountIn, incomingAmount, expiryIn, paymentSecret, None),
IntermediatePayload.NodeRelay.Standard(outgoingAmount, expiryOut, outgoingNodeId),
nextTrampolinePacket)

View file

@ -90,9 +90,9 @@ class RelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("applicat
}
// we use this to build a valid onion
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(finalAmount, hops, None), ClearRecipient(e, Features.empty, finalAmount, finalExpiry, paymentSecret))
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(finalAmount, hops, None), ClearRecipient(e, Features.empty, finalAmount, finalExpiry, paymentSecret), 1.0)
// and then manually build an htlc
val add_ab = UpdateAddHtlc(randomBytes32(), 123456, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None)
val add_ab = UpdateAddHtlc(randomBytes32(), 123456, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None, 1.0)
relayer ! RelayForward(add_ab, priv_a.publicKey)
register.expectMessageType[Register.Forward[CMD_ADD_HTLC]]
}
@ -100,8 +100,8 @@ class RelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("applicat
test("relay an htlc-add at the final node to the payment handler") { f =>
import f._
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(finalAmount, hops.take(1), None), ClearRecipient(b, Features.empty, finalAmount, finalExpiry, paymentSecret))
val add_ab = UpdateAddHtlc(channelId_ab, 123456, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(finalAmount, hops.take(1), None), ClearRecipient(b, Features.empty, finalAmount, finalExpiry, paymentSecret), 1.0)
val add_ab = UpdateAddHtlc(channelId_ab, 123456, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None, 1.0)
relayer ! RelayForward(add_ab, priv_a.publicKey)
val fp = paymentHandler.expectMessageType[FinalPacket]
@ -119,10 +119,10 @@ class RelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("applicat
val finalTrampolinePayload = NodePayload(b, FinalPayload.Standard.createPayload(finalAmount, totalAmount, finalExpiry, paymentSecret))
val Right(trampolineOnion) = buildOnion(Seq(finalTrampolinePayload), paymentHash, None)
val recipient = ClearRecipient(b, nodeParams.features.invoiceFeatures(), finalAmount, finalExpiry, randomBytes32(), nextTrampolineOnion_opt = Some(trampolineOnion.packet))
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(finalAmount, Seq(channelHopFromUpdate(priv_a.publicKey, b, channelUpdate_ab)), None), recipient)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(finalAmount, Seq(channelHopFromUpdate(priv_a.publicKey, b, channelUpdate_ab)), None), recipient, 1.0)
assert(payment.cmd.amount == finalAmount)
assert(payment.cmd.cltvExpiry == finalExpiry)
val add_ab = UpdateAddHtlc(channelId_ab, 123456, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None)
val add_ab = UpdateAddHtlc(channelId_ab, 123456, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None, 1.0)
relayer ! RelayForward(add_ab, priv_a.publicKey)
val fp = paymentHandler.expectMessageType[FinalPacket]
@ -140,9 +140,9 @@ class RelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("applicat
import f._
// we use this to build a valid onion
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(finalAmount, hops, None), ClearRecipient(e, Features.empty, finalAmount, finalExpiry, paymentSecret))
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(finalAmount, hops, None), ClearRecipient(e, Features.empty, finalAmount, finalExpiry, paymentSecret), 1.0)
// and then manually build an htlc with an invalid onion (hmac)
val add_ab = UpdateAddHtlc(channelId_ab, 123456, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion.copy(hmac = payment.cmd.onion.hmac.reverse), None)
val add_ab = UpdateAddHtlc(channelId_ab, 123456, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion.copy(hmac = payment.cmd.onion.hmac.reverse), None, 1.0)
relayer ! RelayForward(add_ab, priv_a.publicKey)
val fail = register.expectMessageType[Register.Forward[CMD_FAIL_MALFORMED_HTLC]].message
@ -160,8 +160,8 @@ class RelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("applicat
val routeExpiry = CltvExpiry(nodeParams.currentBlockHeight - 10)
val (_, blindedHop, recipient) = blindedRouteFromHops(finalAmount, finalExpiry, Seq(channelHopFromUpdate(b, c, channelUpdate_bc)), routeExpiry, paymentPreimage, hex"deadbeef")
val route = Route(finalAmount, Seq(channelHopFromUpdate(priv_a.publicKey, b, channelUpdate_ab)), Some(blindedHop))
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient)
val add_ab = UpdateAddHtlc(channelId_ab, 0, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, route, recipient, 1.0)
val add_ab = UpdateAddHtlc(channelId_ab, 0, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt, 1.0)
relayer ! RelayForward(add_ab, priv_a.publicKey)
val fail = register.expectMessageType[Register.Forward[CMD_FAIL_HTLC]].message
@ -177,7 +177,7 @@ class RelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("applicat
// we use an expired blinded route.
val Right(payment) = buildOutgoingBlindedPaymentAB(paymentHash, routeExpiry = CltvExpiry(nodeParams.currentBlockHeight - 1))
val add_ab = UpdateAddHtlc(channelId_ab, 0, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt)
val add_ab = UpdateAddHtlc(channelId_ab, 0, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, payment.cmd.nextBlindingKey_opt, 1.0)
relayer ! RelayForward(add_ab, priv_a.publicKey)
val fail = register.expectMessageType[Register.Forward[CMD_FAIL_MALFORMED_HTLC]].message
@ -196,10 +196,10 @@ class RelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("applicat
val invoice = Bolt11Invoice(Block.RegtestGenesisBlock.hash, None, paymentHash, priv_c.privateKey, Left("invoice"), CltvExpiryDelta(6), paymentSecret = paymentSecret, features = invoiceFeatures)
val trampolineHop = NodeHop(b, c, channelUpdate_bc.cltvExpiryDelta, fee_b)
val recipient = TrampolineRecipient(invoice, finalAmount, finalExpiry, trampolineHop, randomBytes32())
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(recipient.trampolineAmount, Seq(channelHopFromUpdate(priv_a.publicKey, b, channelUpdate_ab)), Some(trampolineHop)), recipient)
val Right(payment) = buildOutgoingPayment(TestConstants.emptyOrigin, paymentHash, Route(recipient.trampolineAmount, Seq(channelHopFromUpdate(priv_a.publicKey, b, channelUpdate_ab)), Some(trampolineHop)), recipient, 1.0)
// and then manually build an htlc
val add_ab = UpdateAddHtlc(channelId_ab, 123456, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None)
val add_ab = UpdateAddHtlc(channelId_ab, 123456, payment.cmd.amount, payment.cmd.paymentHash, payment.cmd.cltvExpiry, payment.cmd.onion, None, 1.0)
relayer ! RelayForward(add_ab, priv_a.publicKey)
val fail = register.expectMessageType[Register.Forward[CMD_FAIL_HTLC]].message
@ -213,8 +213,8 @@ class RelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("applicat
import f._
val replyTo = TestProbe[Any]()
val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 42, amountMsat = 11000000 msat, paymentHash = ByteVector32.Zeroes, CltvExpiry(4200), TestConstants.emptyOnionPacket, None)
val add_bc = UpdateAddHtlc(channelId_bc, 72, 1000 msat, paymentHash, CltvExpiry(1), TestConstants.emptyOnionPacket, None)
val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 42, amountMsat = 11000000 msat, paymentHash = ByteVector32.Zeroes, CltvExpiry(4200), TestConstants.emptyOnionPacket, None, 1.0)
val add_bc = UpdateAddHtlc(channelId_bc, 72, 1000 msat, paymentHash, CltvExpiry(1), TestConstants.emptyOnionPacket, None, 1.0)
val channelOrigin = Origin.Hot(replyTo.ref.toClassic, Upstream.Hot.Channel(add_ab, TimestampMilli.now(), priv_a.publicKey))
val trampolineOrigin = Origin.Hot(replyTo.ref.toClassic, Upstream.Hot.Trampoline(Seq(Upstream.Hot.Channel(add_ab, TimestampMilli.now(), priv_a.publicKey))))

View file

@ -29,11 +29,11 @@ class CommitmentSpecSpec extends AnyFunSuite {
val R = randomBytes32()
val H = Crypto.sha256(R)
val add1 = UpdateAddHtlc(ByteVector32.Zeroes, 1, (2000 * 1000) msat, H, CltvExpiry(400), TestConstants.emptyOnionPacket, None)
val add1 = UpdateAddHtlc(ByteVector32.Zeroes, 1, (2000 * 1000) msat, H, CltvExpiry(400), TestConstants.emptyOnionPacket, None, 1.0)
val spec1 = CommitmentSpec.reduce(spec, add1 :: Nil, Nil)
assert(spec1 == spec.copy(htlcs = Set(OutgoingHtlc(add1)), toLocal = 3000000 msat))
val add2 = UpdateAddHtlc(ByteVector32.Zeroes, 2, (1000 * 1000) msat, H, CltvExpiry(400), TestConstants.emptyOnionPacket, None)
val add2 = UpdateAddHtlc(ByteVector32.Zeroes, 2, (1000 * 1000) msat, H, CltvExpiry(400), TestConstants.emptyOnionPacket, None, 1.0)
val spec2 = CommitmentSpec.reduce(spec1, add2 :: Nil, Nil)
assert(spec2 == spec1.copy(htlcs = Set(OutgoingHtlc(add1), OutgoingHtlc(add2)), toLocal = 2000000 msat))
@ -51,11 +51,11 @@ class CommitmentSpecSpec extends AnyFunSuite {
val R = randomBytes32()
val H = Crypto.sha256(R)
val add1 = UpdateAddHtlc(ByteVector32.Zeroes, 1, (2000 * 1000) msat, H, CltvExpiry(400), TestConstants.emptyOnionPacket, None)
val add1 = UpdateAddHtlc(ByteVector32.Zeroes, 1, (2000 * 1000) msat, H, CltvExpiry(400), TestConstants.emptyOnionPacket, None, 1.0)
val spec1 = CommitmentSpec.reduce(spec, Nil, add1 :: Nil)
assert(spec1 == spec.copy(htlcs = Set(IncomingHtlc(add1)), toRemote = 3000 * 1000 msat))
val add2 = UpdateAddHtlc(ByteVector32.Zeroes, 2, (1000 * 1000) msat, H, CltvExpiry(400), TestConstants.emptyOnionPacket, None)
val add2 = UpdateAddHtlc(ByteVector32.Zeroes, 2, (1000 * 1000) msat, H, CltvExpiry(400), TestConstants.emptyOnionPacket, None, 1.0)
val spec2 = CommitmentSpec.reduce(spec1, Nil, add2 :: Nil)
assert(spec2 == spec1.copy(htlcs = Set(IncomingHtlc(add1), IncomingHtlc(add2)), toRemote = (2000 * 1000) msat))
@ -76,7 +76,7 @@ class CommitmentSpecSpec extends AnyFunSuite {
}
def createHtlc(amount: MilliSatoshi): UpdateAddHtlc = {
UpdateAddHtlc(ByteVector32.Zeroes, 0, amount, randomBytes32(), CltvExpiry(500), TestConstants.emptyOnionPacket, None)
UpdateAddHtlc(ByteVector32.Zeroes, 0, amount, randomBytes32(), CltvExpiry(500), TestConstants.emptyOnionPacket, None, 1.0)
}
}

View file

@ -153,13 +153,13 @@ trait TestVectorsSpec extends AnyFunSuite with Logging {
)
val htlcs = Seq[DirectedHtlc](
IncomingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 0, 1000000 msat, Crypto.sha256(paymentPreimages(0)), CltvExpiry(500), TestConstants.emptyOnionPacket, None)),
IncomingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 1, 2000000 msat, Crypto.sha256(paymentPreimages(1)), CltvExpiry(501), TestConstants.emptyOnionPacket, None)),
OutgoingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 0, 2000000 msat, Crypto.sha256(paymentPreimages(2)), CltvExpiry(502), TestConstants.emptyOnionPacket, None)),
OutgoingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 1, 3000000 msat, Crypto.sha256(paymentPreimages(3)), CltvExpiry(503), TestConstants.emptyOnionPacket, None)),
IncomingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 2, 4000000 msat, Crypto.sha256(paymentPreimages(4)), CltvExpiry(504), TestConstants.emptyOnionPacket, None)),
OutgoingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 2, 5000001.msat, Crypto.sha256(paymentPreimages(5)), CltvExpiry(505), TestConstants.emptyOnionPacket, None)),
OutgoingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 3, 5000000.msat, Crypto.sha256(paymentPreimages(5)), CltvExpiry(506), TestConstants.emptyOnionPacket, None))
IncomingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 0, 1000000 msat, Crypto.sha256(paymentPreimages(0)), CltvExpiry(500), TestConstants.emptyOnionPacket, None, 1.0)),
IncomingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 1, 2000000 msat, Crypto.sha256(paymentPreimages(1)), CltvExpiry(501), TestConstants.emptyOnionPacket, None, 1.0)),
OutgoingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 0, 2000000 msat, Crypto.sha256(paymentPreimages(2)), CltvExpiry(502), TestConstants.emptyOnionPacket, None, 1.0)),
OutgoingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 1, 3000000 msat, Crypto.sha256(paymentPreimages(3)), CltvExpiry(503), TestConstants.emptyOnionPacket, None, 1.0)),
IncomingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 2, 4000000 msat, Crypto.sha256(paymentPreimages(4)), CltvExpiry(504), TestConstants.emptyOnionPacket, None, 1.0)),
OutgoingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 2, 5000001.msat, Crypto.sha256(paymentPreimages(5)), CltvExpiry(505), TestConstants.emptyOnionPacket, None, 1.0)),
OutgoingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 3, 5000000.msat, Crypto.sha256(paymentPreimages(5)), CltvExpiry(506), TestConstants.emptyOnionPacket, None, 1.0))
)
val htlcScripts = htlcs.map {
case OutgoingHtlc(add) => Scripts.htlcOffered(Local.htlc_privkey.publicKey, Remote.htlc_privkey.publicKey, Local.revocation_pubkey, Crypto.ripemd160(add.paymentHash), commitmentFormat)

View file

@ -100,10 +100,10 @@ class TransactionsSpec extends AnyFunSuite with Logging {
test("compute fees") {
// see BOLT #3 specs
val htlcs = Set[DirectedHtlc](
OutgoingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 0, 5000000 msat, ByteVector32.Zeroes, CltvExpiry(552), TestConstants.emptyOnionPacket, None)),
OutgoingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 0, 1000000 msat, ByteVector32.Zeroes, CltvExpiry(553), TestConstants.emptyOnionPacket, None)),
IncomingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 0, 7000000 msat, ByteVector32.Zeroes, CltvExpiry(550), TestConstants.emptyOnionPacket, None)),
IncomingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 0, 800000 msat, ByteVector32.Zeroes, CltvExpiry(551), TestConstants.emptyOnionPacket, None))
OutgoingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 0, 5000000 msat, ByteVector32.Zeroes, CltvExpiry(552), TestConstants.emptyOnionPacket, None, 1.0)),
OutgoingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 0, 1000000 msat, ByteVector32.Zeroes, CltvExpiry(553), TestConstants.emptyOnionPacket, None, 1.0)),
IncomingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 0, 7000000 msat, ByteVector32.Zeroes, CltvExpiry(550), TestConstants.emptyOnionPacket, None, 1.0)),
IncomingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 0, 800000 msat, ByteVector32.Zeroes, CltvExpiry(551), TestConstants.emptyOnionPacket, None, 1.0))
)
val spec = CommitmentSpec(htlcs, FeeratePerKw(5000 sat), toLocal = 0 msat, toRemote = 0 msat)
val fee = commitTxFeeMsat(546 sat, spec, DefaultCommitmentFormat)
@ -154,7 +154,7 @@ class TransactionsSpec extends AnyFunSuite with Logging {
// HtlcPenaltyTx
// first we create a fake commitTx tx, containing only the output that will be spent by the ClaimHtlcSuccessTx
val paymentPreimage = randomBytes32()
val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, (20000 * 1000) msat, sha256(paymentPreimage), CltvExpiryDelta(144).toCltvExpiry(blockHeight), TestConstants.emptyOnionPacket, None)
val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, (20000 * 1000) msat, sha256(paymentPreimage), CltvExpiryDelta(144).toCltvExpiry(blockHeight), TestConstants.emptyOnionPacket, None, 1.0)
val redeemScript = htlcReceived(localHtlcPriv.publicKey, remoteHtlcPriv.publicKey, localRevocationPriv.publicKey, ripemd160(htlc.paymentHash), htlc.cltvExpiry, DefaultCommitmentFormat)
val pubKeyScript = write(pay2wsh(redeemScript))
val commitTx = Transaction(version = 2, txIn = Nil, txOut = TxOut(htlc.amountMsat.truncateToSatoshi, pubKeyScript) :: Nil, lockTime = 0)
@ -168,7 +168,7 @@ class TransactionsSpec extends AnyFunSuite with Logging {
// ClaimHtlcSuccessTx
// first we create a fake commitTx tx, containing only the output that will be spent by the ClaimHtlcSuccessTx
val paymentPreimage = randomBytes32()
val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, (20000 * 1000) msat, sha256(paymentPreimage), CltvExpiryDelta(144).toCltvExpiry(blockHeight), TestConstants.emptyOnionPacket, None)
val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, (20000 * 1000) msat, sha256(paymentPreimage), CltvExpiryDelta(144).toCltvExpiry(blockHeight), TestConstants.emptyOnionPacket, None, 1.0)
val spec = CommitmentSpec(Set(OutgoingHtlc(htlc)), feeratePerKw, toLocal = 0 msat, toRemote = 0 msat)
val outputs = makeCommitTxOutputs(localPaysCommitTxFees = true, localDustLimit, localRevocationPriv.publicKey, toLocalDelay, localDelayedPaymentPriv.publicKey, remotePaymentPriv.publicKey, localHtlcPriv.publicKey, remoteHtlcPriv.publicKey, localFundingPriv.publicKey, remoteFundingPriv.publicKey, spec, DefaultCommitmentFormat)
val pubKeyScript = write(pay2wsh(htlcOffered(localHtlcPriv.publicKey, remoteHtlcPriv.publicKey, localRevocationPriv.publicKey, ripemd160(htlc.paymentHash), DefaultCommitmentFormat)))
@ -183,7 +183,7 @@ class TransactionsSpec extends AnyFunSuite with Logging {
// ClaimHtlcTimeoutTx
// first we create a fake commitTx tx, containing only the output that will be spent by the ClaimHtlcTimeoutTx
val paymentPreimage = randomBytes32()
val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, (20000 * 1000) msat, sha256(paymentPreimage), toLocalDelay.toCltvExpiry(blockHeight), TestConstants.emptyOnionPacket, None)
val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, (20000 * 1000) msat, sha256(paymentPreimage), toLocalDelay.toCltvExpiry(blockHeight), TestConstants.emptyOnionPacket, None, 1.0)
val spec = CommitmentSpec(Set(IncomingHtlc(htlc)), feeratePerKw, toLocal = 0 msat, toRemote = 0 msat)
val outputs = makeCommitTxOutputs(localPaysCommitTxFees = true, localDustLimit, localRevocationPriv.publicKey, toLocalDelay, localDelayedPaymentPriv.publicKey, remotePaymentPriv.publicKey, localHtlcPriv.publicKey, remoteHtlcPriv.publicKey, localFundingPriv.publicKey, remoteFundingPriv.publicKey, spec, DefaultCommitmentFormat)
val pubKeyScript = write(pay2wsh(htlcReceived(localHtlcPriv.publicKey, remoteHtlcPriv.publicKey, localRevocationPriv.publicKey, ripemd160(htlc.paymentHash), htlc.cltvExpiry, DefaultCommitmentFormat)))
@ -261,17 +261,17 @@ class TransactionsSpec extends AnyFunSuite with Logging {
// htlc1 and htlc2 are regular IN/OUT htlcs
val paymentPreimage1 = randomBytes32()
val htlc1 = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliBtc(100).toMilliSatoshi, sha256(paymentPreimage1), CltvExpiry(300), TestConstants.emptyOnionPacket, None)
val htlc1 = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliBtc(100).toMilliSatoshi, sha256(paymentPreimage1), CltvExpiry(300), TestConstants.emptyOnionPacket, None, 1.0)
val paymentPreimage2 = randomBytes32()
val htlc2 = UpdateAddHtlc(ByteVector32.Zeroes, 1, MilliBtc(200).toMilliSatoshi, sha256(paymentPreimage2), CltvExpiry(310), TestConstants.emptyOnionPacket, None)
val htlc2 = UpdateAddHtlc(ByteVector32.Zeroes, 1, MilliBtc(200).toMilliSatoshi, sha256(paymentPreimage2), CltvExpiry(310), TestConstants.emptyOnionPacket, None, 1.0)
// htlc3 and htlc4 are dust IN/OUT htlcs, with an amount large enough to be included in the commit tx, but too small to be claimed at 2nd stage
val paymentPreimage3 = randomBytes32()
val htlc3 = UpdateAddHtlc(ByteVector32.Zeroes, 2, (localDustLimit + weight2fee(feeratePerKw, DefaultCommitmentFormat.htlcTimeoutWeight)).toMilliSatoshi, sha256(paymentPreimage3), CltvExpiry(295), TestConstants.emptyOnionPacket, None)
val htlc3 = UpdateAddHtlc(ByteVector32.Zeroes, 2, (localDustLimit + weight2fee(feeratePerKw, DefaultCommitmentFormat.htlcTimeoutWeight)).toMilliSatoshi, sha256(paymentPreimage3), CltvExpiry(295), TestConstants.emptyOnionPacket, None, 1.0)
val paymentPreimage4 = randomBytes32()
val htlc4 = UpdateAddHtlc(ByteVector32.Zeroes, 3, (localDustLimit + weight2fee(feeratePerKw, DefaultCommitmentFormat.htlcSuccessWeight)).toMilliSatoshi, sha256(paymentPreimage4), CltvExpiry(300), TestConstants.emptyOnionPacket, None)
val htlc4 = UpdateAddHtlc(ByteVector32.Zeroes, 3, (localDustLimit + weight2fee(feeratePerKw, DefaultCommitmentFormat.htlcSuccessWeight)).toMilliSatoshi, sha256(paymentPreimage4), CltvExpiry(300), TestConstants.emptyOnionPacket, None, 1.0)
// htlc5 and htlc6 are dust IN/OUT htlcs
val htlc5 = UpdateAddHtlc(ByteVector32.Zeroes, 4, (localDustLimit * 0.9).toMilliSatoshi, sha256(randomBytes32()), CltvExpiry(295), TestConstants.emptyOnionPacket, None)
val htlc6 = UpdateAddHtlc(ByteVector32.Zeroes, 5, (localDustLimit * 0.9).toMilliSatoshi, sha256(randomBytes32()), CltvExpiry(305), TestConstants.emptyOnionPacket, None)
val htlc5 = UpdateAddHtlc(ByteVector32.Zeroes, 4, (localDustLimit * 0.9).toMilliSatoshi, sha256(randomBytes32()), CltvExpiry(295), TestConstants.emptyOnionPacket, None, 1.0)
val htlc6 = UpdateAddHtlc(ByteVector32.Zeroes, 5, (localDustLimit * 0.9).toMilliSatoshi, sha256(randomBytes32()), CltvExpiry(305), TestConstants.emptyOnionPacket, None, 1.0)
val spec = CommitmentSpec(
htlcs = Set(
OutgoingHtlc(htlc1),
@ -492,21 +492,21 @@ class TransactionsSpec extends AnyFunSuite with Logging {
// htlc1, htlc2a and htlc2b are regular IN/OUT htlcs
val paymentPreimage1 = randomBytes32()
val htlc1 = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliBtc(100).toMilliSatoshi, sha256(paymentPreimage1), CltvExpiry(300), TestConstants.emptyOnionPacket, None)
val htlc1 = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliBtc(100).toMilliSatoshi, sha256(paymentPreimage1), CltvExpiry(300), TestConstants.emptyOnionPacket, None, 1.0)
val paymentPreimage2 = randomBytes32()
val htlc2a = UpdateAddHtlc(ByteVector32.Zeroes, 1, MilliBtc(50).toMilliSatoshi, sha256(paymentPreimage2), CltvExpiry(310), TestConstants.emptyOnionPacket, None)
val htlc2b = UpdateAddHtlc(ByteVector32.Zeroes, 2, MilliBtc(150).toMilliSatoshi, sha256(paymentPreimage2), CltvExpiry(310), TestConstants.emptyOnionPacket, None)
val htlc2a = UpdateAddHtlc(ByteVector32.Zeroes, 1, MilliBtc(50).toMilliSatoshi, sha256(paymentPreimage2), CltvExpiry(310), TestConstants.emptyOnionPacket, None, 1.0)
val htlc2b = UpdateAddHtlc(ByteVector32.Zeroes, 2, MilliBtc(150).toMilliSatoshi, sha256(paymentPreimage2), CltvExpiry(310), TestConstants.emptyOnionPacket, None, 1.0)
// htlc3 and htlc4 are dust IN/OUT htlcs, with an amount large enough to be included in the commit tx, but too small to be claimed at 2nd stage
val paymentPreimage3 = randomBytes32()
val htlc3 = UpdateAddHtlc(ByteVector32.Zeroes, 3, (localDustLimit + weight2fee(feeratePerKw, UnsafeLegacyAnchorOutputsCommitmentFormat.htlcTimeoutWeight)).toMilliSatoshi, sha256(paymentPreimage3), CltvExpiry(295), TestConstants.emptyOnionPacket, None)
val htlc3 = UpdateAddHtlc(ByteVector32.Zeroes, 3, (localDustLimit + weight2fee(feeratePerKw, UnsafeLegacyAnchorOutputsCommitmentFormat.htlcTimeoutWeight)).toMilliSatoshi, sha256(paymentPreimage3), CltvExpiry(295), TestConstants.emptyOnionPacket, None, 1.0)
val paymentPreimage4 = randomBytes32()
val htlc4 = UpdateAddHtlc(ByteVector32.Zeroes, 4, (localDustLimit + weight2fee(feeratePerKw, UnsafeLegacyAnchorOutputsCommitmentFormat.htlcSuccessWeight)).toMilliSatoshi, sha256(paymentPreimage4), CltvExpiry(300), TestConstants.emptyOnionPacket, None)
val htlc4 = UpdateAddHtlc(ByteVector32.Zeroes, 4, (localDustLimit + weight2fee(feeratePerKw, UnsafeLegacyAnchorOutputsCommitmentFormat.htlcSuccessWeight)).toMilliSatoshi, sha256(paymentPreimage4), CltvExpiry(300), TestConstants.emptyOnionPacket, None, 1.0)
// htlc5 and htlc6 are dust IN/OUT htlcs
val htlc5 = UpdateAddHtlc(ByteVector32.Zeroes, 5, (localDustLimit * 0.9).toMilliSatoshi, sha256(randomBytes32()), CltvExpiry(295), TestConstants.emptyOnionPacket, None)
val htlc6 = UpdateAddHtlc(ByteVector32.Zeroes, 6, (localDustLimit * 0.9).toMilliSatoshi, sha256(randomBytes32()), CltvExpiry(305), TestConstants.emptyOnionPacket, None)
val htlc5 = UpdateAddHtlc(ByteVector32.Zeroes, 5, (localDustLimit * 0.9).toMilliSatoshi, sha256(randomBytes32()), CltvExpiry(295), TestConstants.emptyOnionPacket, None, 1.0)
val htlc6 = UpdateAddHtlc(ByteVector32.Zeroes, 6, (localDustLimit * 0.9).toMilliSatoshi, sha256(randomBytes32()), CltvExpiry(305), TestConstants.emptyOnionPacket, None, 1.0)
// htlc7 and htlc8 are at the dust limit when we ignore 2nd-stage tx fees
val htlc7 = UpdateAddHtlc(ByteVector32.Zeroes, 7, localDustLimit.toMilliSatoshi, sha256(randomBytes32()), CltvExpiry(300), TestConstants.emptyOnionPacket, None)
val htlc8 = UpdateAddHtlc(ByteVector32.Zeroes, 8, localDustLimit.toMilliSatoshi, sha256(randomBytes32()), CltvExpiry(302), TestConstants.emptyOnionPacket, None)
val htlc7 = UpdateAddHtlc(ByteVector32.Zeroes, 7, localDustLimit.toMilliSatoshi, sha256(randomBytes32()), CltvExpiry(300), TestConstants.emptyOnionPacket, None, 1.0)
val htlc8 = UpdateAddHtlc(ByteVector32.Zeroes, 8, localDustLimit.toMilliSatoshi, sha256(randomBytes32()), CltvExpiry(302), TestConstants.emptyOnionPacket, None, 1.0)
val spec = CommitmentSpec(
htlcs = Set(
OutgoingHtlc(htlc1),
@ -760,11 +760,11 @@ class TransactionsSpec extends AnyFunSuite with Logging {
val paymentPreimage1 = ByteVector32(hex"1111111111111111111111111111111111111111111111111111111111111111")
val paymentPreimage2 = ByteVector32(hex"2222222222222222222222222222222222222222222222222222222222222222")
val paymentPreimage3 = ByteVector32(hex"3333333333333333333333333333333333333333333333333333333333333333")
val htlc1 = UpdateAddHtlc(randomBytes32(), 1, millibtc2satoshi(MilliBtc(100)).toMilliSatoshi, sha256(paymentPreimage1), CltvExpiry(300), TestConstants.emptyOnionPacket, None)
val htlc2 = UpdateAddHtlc(randomBytes32(), 2, millibtc2satoshi(MilliBtc(200)).toMilliSatoshi, sha256(paymentPreimage2), CltvExpiry(300), TestConstants.emptyOnionPacket, None)
val htlc3 = UpdateAddHtlc(randomBytes32(), 3, millibtc2satoshi(MilliBtc(200)).toMilliSatoshi, sha256(paymentPreimage3), CltvExpiry(300), TestConstants.emptyOnionPacket, None)
val htlc4 = UpdateAddHtlc(randomBytes32(), 4, millibtc2satoshi(MilliBtc(200)).toMilliSatoshi, sha256(paymentPreimage3), CltvExpiry(300), TestConstants.emptyOnionPacket, None)
val htlc5 = UpdateAddHtlc(randomBytes32(), 5, millibtc2satoshi(MilliBtc(200)).toMilliSatoshi, sha256(paymentPreimage3), CltvExpiry(301), TestConstants.emptyOnionPacket, None)
val htlc1 = UpdateAddHtlc(randomBytes32(), 1, millibtc2satoshi(MilliBtc(100)).toMilliSatoshi, sha256(paymentPreimage1), CltvExpiry(300), TestConstants.emptyOnionPacket, None, 1.0)
val htlc2 = UpdateAddHtlc(randomBytes32(), 2, millibtc2satoshi(MilliBtc(200)).toMilliSatoshi, sha256(paymentPreimage2), CltvExpiry(300), TestConstants.emptyOnionPacket, None, 1.0)
val htlc3 = UpdateAddHtlc(randomBytes32(), 3, millibtc2satoshi(MilliBtc(200)).toMilliSatoshi, sha256(paymentPreimage3), CltvExpiry(300), TestConstants.emptyOnionPacket, None, 1.0)
val htlc4 = UpdateAddHtlc(randomBytes32(), 4, millibtc2satoshi(MilliBtc(200)).toMilliSatoshi, sha256(paymentPreimage3), CltvExpiry(300), TestConstants.emptyOnionPacket, None, 1.0)
val htlc5 = UpdateAddHtlc(randomBytes32(), 5, millibtc2satoshi(MilliBtc(200)).toMilliSatoshi, sha256(paymentPreimage3), CltvExpiry(301), TestConstants.emptyOnionPacket, None, 1.0)
val spec = CommitmentSpec(
htlcs = Set(
@ -877,9 +877,9 @@ class TransactionsSpec extends AnyFunSuite with Logging {
formatted
}
def htlcIn(amount: Satoshi): DirectedHtlc = IncomingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 0, amount.toMilliSatoshi, ByteVector32.Zeroes, CltvExpiry(144), TestConstants.emptyOnionPacket, None))
def htlcIn(amount: Satoshi): DirectedHtlc = IncomingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 0, amount.toMilliSatoshi, ByteVector32.Zeroes, CltvExpiry(144), TestConstants.emptyOnionPacket, None, 1.0))
def htlcOut(amount: Satoshi): DirectedHtlc = OutgoingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 0, amount.toMilliSatoshi, ByteVector32.Zeroes, CltvExpiry(144), TestConstants.emptyOnionPacket, None))
def htlcOut(amount: Satoshi): DirectedHtlc = OutgoingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 0, amount.toMilliSatoshi, ByteVector32.Zeroes, CltvExpiry(144), TestConstants.emptyOnionPacket, None, 1.0))
case class TestVector(name: String, spec: CommitmentSpec, expectedFee: Satoshi)

View file

@ -291,11 +291,11 @@ object ChannelCodecsSpec {
)
val htlcs: Seq[DirectedHtlc] = Seq[DirectedHtlc](
IncomingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 0, 1000000 msat, Crypto.sha256(paymentPreimages(0)), CltvExpiry(500), TestConstants.emptyOnionPacket, None)),
IncomingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 1, 2000000 msat, Crypto.sha256(paymentPreimages(1)), CltvExpiry(501), TestConstants.emptyOnionPacket, None)),
OutgoingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 30, 2000000 msat, Crypto.sha256(paymentPreimages(2)), CltvExpiry(502), TestConstants.emptyOnionPacket, None)),
OutgoingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 31, 3000000 msat, Crypto.sha256(paymentPreimages(3)), CltvExpiry(503), TestConstants.emptyOnionPacket, None)),
IncomingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 2, 4000000 msat, Crypto.sha256(paymentPreimages(4)), CltvExpiry(504), TestConstants.emptyOnionPacket, None))
IncomingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 0, 1000000 msat, Crypto.sha256(paymentPreimages(0)), CltvExpiry(500), TestConstants.emptyOnionPacket, None, 1.0)),
IncomingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 1, 2000000 msat, Crypto.sha256(paymentPreimages(1)), CltvExpiry(501), TestConstants.emptyOnionPacket, None, 1.0)),
OutgoingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 30, 2000000 msat, Crypto.sha256(paymentPreimages(2)), CltvExpiry(502), TestConstants.emptyOnionPacket, None, 1.0)),
OutgoingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 31, 3000000 msat, Crypto.sha256(paymentPreimages(3)), CltvExpiry(503), TestConstants.emptyOnionPacket, None, 1.0)),
IncomingHtlc(UpdateAddHtlc(ByteVector32.Zeroes, 2, 4000000 msat, Crypto.sha256(paymentPreimages(4)), CltvExpiry(504), TestConstants.emptyOnionPacket, None, 1.0))
)
val normal: DATA_NORMAL = {

View file

@ -4,7 +4,7 @@ import fr.acinq.bitcoin.scalacompat.ByteVector32
import fr.acinq.eclair.transactions.{IncomingHtlc, OutgoingHtlc}
import fr.acinq.eclair.wire.internal.channel.version0.ChannelCodecs0.Codecs._
import fr.acinq.eclair.wire.internal.channel.version0.ChannelTypes0.ChannelVersion
import fr.acinq.eclair.wire.protocol.{OnionRoutingPacket, UpdateAddHtlc}
import fr.acinq.eclair.wire.protocol.{OnionRoutingPacket, TlvStream, UpdateAddHtlc}
import fr.acinq.eclair.{CltvExpiry, MilliSatoshiLong}
import org.scalatest.funsuite.AnyFunSuite
import scodec.bits._
@ -58,7 +58,6 @@ class ChannelCodecs0Spec extends AnyFunSuite {
hex"1e3a0e7c55b9f74b209e3704695e38874dd0950c54089a2be675bbf1a83679f6ff",
hex"2db02ba44906455d5ba19cf75979889cc23ecd86b88728478133ccf180ee407abf882bd86f6f318d8e993dda100dcd9641e56c270aaee5810d9dcc0c85677387232c4f90ef4c54afb9ed9c0077db8a7516f41af552364609df16a25fc3534087c821af9a6013dbff96ba980b9f6a8b59da95fd5177c4d8bfe924c05dbf3a9d6e62a83cc1c91fa35cbc676094c2868df62dc1399b3797120ffd3f850eeafd525014068c4533d2a144e983b8a7f75d18843ccfafbc6ae13db41e2379a82f81e42accfd171995c206ba05024295e4b7ed202056301cba96ae647a0556b9dcba6cd368600a73a05dfeaa6287e10b9ae1ca8516f5e64c483154ecc9aad87fa5380145f114d4bdd2d0be8b6c30588ba925642e16125c96b12248f79ffd04c4770cc6f7d85239943b8e53eae8fb085d9be5f849d5f2b8a4597d7d35083cf9ff06fce2b6d13e166cd725450a10eac6d2c574850c756dbe25dd2715b4dcd5cf2bf169f7d035bd48f19553133fda1ad99bef442e76d365a7fe372790581189b4c7684da5f2922421332fd1dcb0618bffc76c192e8715c2a43452adce7534c1e2db8274bccacb209c097ecd9db47b6d27f8f418d5a9efb9196fe3dea8a4f822b136f86b9cb641cfe47415620f480df4e8e86bfe1012d4ea675156feba6c61ab841922c2203f2458ec0f292fc01c794c579c06765760cbd42e678a16b40c925a568ce2b024007e5350ea96bb82c92105127cf7cecaa18b1b31d02aadc9bbe414489e6c77847cead92a44866ba1dd99a7b40d522c3898e55c7b275fd205500dd5ba42cfa2b8099e6051f8c3a10877a4e1fae05458b5f11356b78f3452908f229f1ba81353732152c72fb208d870b953021f6f8f658c29238ce4c84af4c037cffd188f50b36ad5e8395e0d7cfd439b6a80e33f87784c06ceb6f3fa6d4de52220876f1b53e30da5403e2413a1b22a11d1d7d99c13fae5047a182cca85eda0b3f50e4bb3ae3344a64513911ef45234d77c03eb63f07984465ae2defbf8d4207f70c6faeb35572735544f19ffc094c9e8284ac82261004ed7dcfa7d8c5c7f10f071c7043e1bae2666f2e5bf3282c1db853997372c6188353d8f932997de4a034c21c386d09cd2fbe461fadede20a4d9288dd060f43f6fc93119beff9154659141240c227b048f555c85c728581ffa523acf06fa591046390b104cceb05d943a4acc26989dc2603bd1c2c6fe128cf68e7747c6a73249100917a6964db98dede8e8ea36f58b775a8d18c9db455d57fcd5242a149f556284453af2698944884e8830a1a1bd5cbb700560528086be739d550bc5a7a44d2a21103564d24d862ce90f54271a71739eca1eb3e154170852e8f24e3840139bcc3cb8b184d7f142b5750d0d35f07283d8292e5b276d5c94dd9ecb084702a14c290fad7a729b681421ae21fa5a0cb0a1ca5d4ca6d4e9b1128e890443839c927fd97e40e1331c09aa4c726a9118526be5a75fda9a1f8e8e5807cca5f251cd431ef0052087e433eca5b325c208a5229352f1cb8cc180103fcd42f3b7cc5b96b2fe769c92f8c093604abf1e60dc963192f86739304e157f0d49d635f27629b101ddb440776774b6dc6227a1c007f1cabe7a88d7a9b9b4d0d66af9415be3fc4a720ecf481fe10d524b1a09833608e8911555f58643e10fa57a1b81c0ad5b3eb6b5f4be7b05787e31667bd2088a52c6ffda0b0f3ed7a881e66380c011ba7185f7045845f88403d2ff3df3f86a300f808bbd9c28fa33fa034d0c098796d6bc1b6369a3d7c70ece00420cfb2840df7b93da67583e93b0ff2c396ba89e9100bcabf0c6f947bc9d93bb2d3289",
ByteVector32(hex"dac3bc8b2e16fdf2db3e627483bc395c701c1fc96ace53e4ebc54150e807921d")),
None
)
val remaining = bin"0000000" // 7 bits remainder because the direction is encoded with 1 bit and we are dealing with bytes

View file

@ -75,7 +75,8 @@ class ChannelCodecs1Spec extends AnyFunSuite {
cltvExpiry = CltvExpiry(Random.nextInt(Int.MaxValue)),
paymentHash = randomBytes32(),
onionRoutingPacket = TestConstants.emptyOnionPacket,
blinding_opt = None)
blinding_opt = None,
confidence = 1.0)
val htlc1 = IncomingHtlc(add)
val htlc2 = OutgoingHtlc(add)
assert(htlcCodec.decodeValue(htlcCodec.encode(htlc1).require).require == htlc1)
@ -90,7 +91,8 @@ class ChannelCodecs1Spec extends AnyFunSuite {
cltvExpiry = CltvExpiry(Random.nextInt(Int.MaxValue)),
paymentHash = randomBytes32(),
onionRoutingPacket = TestConstants.emptyOnionPacket,
blinding_opt = None)
blinding_opt = None,
confidence = 1.0)
val add2 = UpdateAddHtlc(
channelId = randomBytes32(),
id = Random.nextInt(Int.MaxValue),
@ -98,7 +100,8 @@ class ChannelCodecs1Spec extends AnyFunSuite {
cltvExpiry = CltvExpiry(Random.nextInt(Int.MaxValue)),
paymentHash = randomBytes32(),
onionRoutingPacket = TestConstants.emptyOnionPacket,
blinding_opt = None)
blinding_opt = None,
confidence = 1.0)
val htlc1 = IncomingHtlc(add1)
val htlc2 = OutgoingHtlc(add2)
val htlcs = Set[DirectedHtlc](htlc1, htlc2)
@ -122,16 +125,16 @@ class ChannelCodecs1Spec extends AnyFunSuite {
assert(originCodec.decodeValue(originCodec.encode(localHot).require).require == localCold)
assert(originCodec.decodeValue(originCodec.encode(localCold).require).require == localCold)
val add = UpdateAddHtlc(randomBytes32(), 4324, 11000000 msat, randomBytes32(), CltvExpiry(400000), TestConstants.emptyOnionPacket, None)
val add = UpdateAddHtlc(randomBytes32(), 4324, 11000000 msat, randomBytes32(), CltvExpiry(400000), TestConstants.emptyOnionPacket, None, 1.0)
val relayedHot = Origin.Hot(replyTo, Upstream.Hot.Channel(add, TimestampMilli(0), randomKey().publicKey))
val relayedCold = Origin.Cold(Upstream.Cold.Channel(add.channelId, add.id, add.amountMsat))
assert(originCodec.decodeValue(originCodec.encode(relayedHot).require).require == relayedCold)
assert(originCodec.decodeValue(originCodec.encode(relayedCold).require).require == relayedCold)
val adds = Seq(
UpdateAddHtlc(randomBytes32(), 1L, 1000 msat, randomBytes32(), CltvExpiry(400000), TestConstants.emptyOnionPacket, None),
UpdateAddHtlc(randomBytes32(), 1L, 2000 msat, randomBytes32(), CltvExpiry(400000), TestConstants.emptyOnionPacket, None),
UpdateAddHtlc(randomBytes32(), 2L, 3000 msat, randomBytes32(), CltvExpiry(400000), TestConstants.emptyOnionPacket, None),
UpdateAddHtlc(randomBytes32(), 1L, 1000 msat, randomBytes32(), CltvExpiry(400000), TestConstants.emptyOnionPacket, None, 1.0),
UpdateAddHtlc(randomBytes32(), 1L, 2000 msat, randomBytes32(), CltvExpiry(400000), TestConstants.emptyOnionPacket, None, 1.0),
UpdateAddHtlc(randomBytes32(), 2L, 3000 msat, randomBytes32(), CltvExpiry(400000), TestConstants.emptyOnionPacket, None, 1.0),
)
val trampolineRelayedHot = Origin.Hot(replyTo, Upstream.Hot.Trampoline(adds.map(add => Upstream.Hot.Channel(add, TimestampMilli(0), randomKey().publicKey))))
// We didn't encode the incoming HTLC amount.

View file

@ -391,7 +391,7 @@ class LightningMessageCodecsSpec extends AnyFunSuite {
UpdateFee(randomBytes32(), FeeratePerKw(2 sat)),
Shutdown(randomBytes32(), bin(47, 0)),
ClosingSigned(randomBytes32(), 2 sat, randomBytes64()),
UpdateAddHtlc(randomBytes32(), 2, 3 msat, bin32(0), CltvExpiry(4), TestConstants.emptyOnionPacket, None),
UpdateAddHtlc(randomBytes32(), 2, 3 msat, bin32(0), CltvExpiry(4), TestConstants.emptyOnionPacket, None, 1.0),
UpdateFulfillHtlc(randomBytes32(), 2, bin32(0)),
UpdateFailHtlc(randomBytes32(), 2, bin(154, 0)),
UpdateFailMalformedHtlc(randomBytes32(), 2, randomBytes32(), 1111),
@ -556,4 +556,24 @@ class LightningMessageCodecsSpec extends AnyFunSuite {
}
}
test("encode/decode UpdateAddHtlc") {
val testCases = Map(
hex"2e184fc141277ba9a3fbe752206f5714c3cfe50765c258dfbdb10cead1ef57f9 0000000000000001 000000000000b5bb 05fc4f9f94ecb97574a90c9154740a3a6c16195d6d0136b71d60d9dae33ce999 000d5fff 00 02c606691a88f80fdc10d007ef5dfa0b91ce33b7b3fa40a6df84f7285aaf37174e 708636c4c5bab2c45e0e9b94f48791e468a9e54af63ee9dfd09d947d6a845b03133eb69226293754caf18b41b99c66830e327938b2fe44e54e31cd8a2c0ee1c43c6c50a8d29bd3f27eb88f70d6ecd7f1b2afb7d721dbad9ac34a511c5e49e5c44e0beb5e513b930fea34eb3ce22e5cebc55c85efa2b24a698ee4f45207977693cebc59f3cad9088387ea89ae45cbb9700bb3e0d93b82d10ea994979a90f4d6265312ae370c80a0c323f5abaa8dcc09aa637b85f2b40d7324885fc744719ec966154c2cd4a512abc618232b82855261886937af9f92d308eba5a5b03e99f4e96535a7e4aeb29c4a260938b85bc16218eb0fe2c765519dd811e0e633bb6e26004393db285ffd04bf6be33ec410bcad437d515484e910960b3d2b1f719963c215c26a0f29b86dfde41c098780d5aaf9b48c95f7e3d6582955feee058e5d0f87671202caf4899a2f5f238f4938b20d14f3d1e94893666e9c36f9c559f05f065ec547f417b58b81d7f5e71563f0565c30f82e9e8e4755f74b8633fb5c7645a551ebf27b9535aa1bde6f12df2b85cc30b9083d602f7eea0e9093f86aa2346e8900851e884470026f6f46e9748322c786145cd0cc8a0d712aef89466a06e5c2795cf5d326f78a5af746f61f4df3b08f17104b1ce3099a20fab9b2751b3635aec743a986173861d790c31942bd608258a927d75309c15ffd690a0713179d62a60b459be7486d03b774119d12168a9d0761134d789264662ed1e21329c840aa6f958cd0bfddd8cbeb61ac9fb5f379ee8557e3962f85871928d2fae5d9f1026eb95248f38689f44597d1b316a1860597abb77e08fa58778f39fbb13f38c727cdabf58592f3932a272195dde4ccd62d57239cb82d274ed239f39132cf83fa4629435af985ef24a8aecc4e8837ce53658cdbe97951b83f5f3643525f19f3e46312285684631b93e12e47b6922855cdf81ef5459bc26667a17459c537fbc169485bc23daa9c573a86010b9627864842eb3fb01afd90b288e86050c87e5f1e8d49fd6fae7c5c5256d27471baf29017e092b4c5a96f063a8c56ccf90838e2da89f42a9d4af35236e3f12b3253f6981e5db1a4cf453622fcc2e11b51afd2a88b09ed13905bbaeef91b9b80523e13fe6b8c2386f6c83c3cad5de89405e2da894bf30733846266904be964ac8d67e8eff54b9ee3f8e6c88797fdaf9832a2693e7d0b6471bbc234fd72c1f02a8e48f3fb43cf0609802d129e6b46ad542dac1feee2128cf2688a354dacddd0a50ec88b517e9f315c7df81d5002f2809b4009b0ed55b2d960f0390eec4c1824afe013332aa6d0e1f0d65877485471918e7667addd27e592731011e813a5085035f1dc11b4d7dac122f05a033b702d91708e0c708337b3be0fa8463cce23e52f667520908dc5e8a94ff051ffaf50fec11b8a3f880e4d0ededd7b0a6c71f6e939553402eb2ea370334776336e726880a184d7dcfd7c84d12c8feb31a479259f2c6520c5432cf71babc522b7f090cc527df41b1c7d8e3b5e2c1f5a8d4a3e1da578921321e472be5f6f5076be9e9d2255a46e072e19771c461973c44bb47e154e85bb76f0f1152e9bb1209c1707b0f42e6507cd9e4f026156a788ef65b221f0c864efc334132624ba1b96a1ecd7c7d460acb7c1d7b408395410b189c4ae374d143c96c48392abceee4366903a05d9ba884496bbff603b65967fd5a434530fe5accd48f40f00f1a5347f1602dd4abe545e5826f24344bbe88cd3b2ffd1320fb40407ec175f8c17c16a52ade59383357e52eff8b8d5c318172b703c69c4a04786088ee63f2fc9cea63294a33546ea1a954ae9a79c 47de7249c4b76e4db71df5d070dab15ea294f22011fc21a544c26416aaff2682 00 21 0396b9c21b054a49f35ee7abb96e677ebbcaae876d602349cd58b3854380782818 fe0001a147 01 05" ->
UpdateAddHtlc(ByteVector32(hex"2e184fc141277ba9a3fbe752206f5714c3cfe50765c258dfbdb10cead1ef57f9"), 1, 46523 msat, ByteVector32(hex"05fc4f9f94ecb97574a90c9154740a3a6c16195d6d0136b71d60d9dae33ce999"), CltvExpiry(876543), OnionRoutingPacket(0, hex"02c606691a88f80fdc10d007ef5dfa0b91ce33b7b3fa40a6df84f7285aaf37174e", payload = hex"708636c4c5bab2c45e0e9b94f48791e468a9e54af63ee9dfd09d947d6a845b03133eb69226293754caf18b41b99c66830e327938b2fe44e54e31cd8a2c0ee1c43c6c50a8d29bd3f27eb88f70d6ecd7f1b2afb7d721dbad9ac34a511c5e49e5c44e0beb5e513b930fea34eb3ce22e5cebc55c85efa2b24a698ee4f45207977693cebc59f3cad9088387ea89ae45cbb9700bb3e0d93b82d10ea994979a90f4d6265312ae370c80a0c323f5abaa8dcc09aa637b85f2b40d7324885fc744719ec966154c2cd4a512abc618232b82855261886937af9f92d308eba5a5b03e99f4e96535a7e4aeb29c4a260938b85bc16218eb0fe2c765519dd811e0e633bb6e26004393db285ffd04bf6be33ec410bcad437d515484e910960b3d2b1f719963c215c26a0f29b86dfde41c098780d5aaf9b48c95f7e3d6582955feee058e5d0f87671202caf4899a2f5f238f4938b20d14f3d1e94893666e9c36f9c559f05f065ec547f417b58b81d7f5e71563f0565c30f82e9e8e4755f74b8633fb5c7645a551ebf27b9535aa1bde6f12df2b85cc30b9083d602f7eea0e9093f86aa2346e8900851e884470026f6f46e9748322c786145cd0cc8a0d712aef89466a06e5c2795cf5d326f78a5af746f61f4df3b08f17104b1ce3099a20fab9b2751b3635aec743a986173861d790c31942bd608258a927d75309c15ffd690a0713179d62a60b459be7486d03b774119d12168a9d0761134d789264662ed1e21329c840aa6f958cd0bfddd8cbeb61ac9fb5f379ee8557e3962f85871928d2fae5d9f1026eb95248f38689f44597d1b316a1860597abb77e08fa58778f39fbb13f38c727cdabf58592f3932a272195dde4ccd62d57239cb82d274ed239f39132cf83fa4629435af985ef24a8aecc4e8837ce53658cdbe97951b83f5f3643525f19f3e46312285684631b93e12e47b6922855cdf81ef5459bc26667a17459c537fbc169485bc23daa9c573a86010b9627864842eb3fb01afd90b288e86050c87e5f1e8d49fd6fae7c5c5256d27471baf29017e092b4c5a96f063a8c56ccf90838e2da89f42a9d4af35236e3f12b3253f6981e5db1a4cf453622fcc2e11b51afd2a88b09ed13905bbaeef91b9b80523e13fe6b8c2386f6c83c3cad5de89405e2da894bf30733846266904be964ac8d67e8eff54b9ee3f8e6c88797fdaf9832a2693e7d0b6471bbc234fd72c1f02a8e48f3fb43cf0609802d129e6b46ad542dac1feee2128cf2688a354dacddd0a50ec88b517e9f315c7df81d5002f2809b4009b0ed55b2d960f0390eec4c1824afe013332aa6d0e1f0d65877485471918e7667addd27e592731011e813a5085035f1dc11b4d7dac122f05a033b702d91708e0c708337b3be0fa8463cce23e52f667520908dc5e8a94ff051ffaf50fec11b8a3f880e4d0ededd7b0a6c71f6e939553402eb2ea370334776336e726880a184d7dcfd7c84d12c8feb31a479259f2c6520c5432cf71babc522b7f090cc527df41b1c7d8e3b5e2c1f5a8d4a3e1da578921321e472be5f6f5076be9e9d2255a46e072e19771c461973c44bb47e154e85bb76f0f1152e9bb1209c1707b0f42e6507cd9e4f026156a788ef65b221f0c864efc334132624ba1b96a1ecd7c7d460acb7c1d7b408395410b189c4ae374d143c96c48392abceee4366903a05d9ba884496bbff603b65967fd5a434530fe5accd48f40f00f1a5347f1602dd4abe545e5826f24344bbe88cd3b2ffd1320fb40407ec175f8c17c16a52ade59383357e52eff8b8d5c318172b703c69c4a04786088ee63f2fc9cea63294a33546ea1a954ae9a79c", ByteVector32(hex"47de7249c4b76e4db71df5d070dab15ea294f22011fc21a544c26416aaff2682")), TlvStream(UpdateAddHtlcTlv.BlindingPoint(PublicKey(hex"0396b9c21b054a49f35ee7abb96e677ebbcaae876d602349cd58b3854380782818")), UpdateAddHtlcTlv.Endorsement(5))),
hex"f865a44f81f02f3539842b863668403a68ddb3703e03cd91045c9ac114dbd28e 0000000000000002 0000000000000092 d3708298fd195572cceb86a1745210543c42f931a1a2baeed7f705d333ebed22 00013368 00 037cb785d5a9de762adc62e3f2407d452bd1f13d368d0429caee5541a89488e3b9 5b90dd3803ff56400a40dc539efa0a0e29736c76e83d2d8b44775adcb4d485be5eb84eefbfedc69b687ee5c7080f83ff31760ec036990d904de480064966bba393af729d57437c91bea905a66464461a6462c5a0c66976ecaeded73d330dfaeb5651ba68e74b210c123e63ac6ee15d6673cf126f046840bbf4364c907f56382870da85101045fc9392357b0b32081cde0e28460d20c1d5d61a5d56fe50d107304e4b184dd005048f9bcd159eff3cfaaef4ab5a8f29d38de109ea41a7ceaa886a0559a4fd0c7448cf28db85c3758fbde23443776e08dd29c435b1195f205aa787f2ec4a7259afeff078faa419c9af5706b9c08e7ce5772326e09df22eb85b0abc625b969aae34a881c12bd9653a08dc62b8e82cf89d0d66974f96149c6dcd7fc4363eecbf4fdac39e500c416b8d6a2e5871d80775acd1c13df4e4ad30a150390d500869ee6a4eab1c4285952d0549c45960a0b1b5ccfb93c75b63923fbc02b1d5a91e53f7424478cc58bf4366a612d40deec5efea700a7b127f7fdded1c4232e5b2eb7190f964e20b156e62d63d097005a24d96457a3db387a6b25e2a65fb169de0323a48f275780dc320d1c7d22642d3371e65559bf5510a3990b67aceb87daccd40e7f82fbba5a0065a63849dbb6600b420e8cb3561154dc69a3b063784737a7eb9d3f770fa1312c03e8518200cefc1be356bf0b0d1928816e712d24b5021b72f20d84a062a47ef50acf5cc9de025378611fbd7fd6473bc0258aa1697b057f6ce4c99f7a6ade34411a008aaccb3e3b2f78e2bc0720c3050960ad8b2988907a410a8fe565b671d3f2a274b9ed230786da769ebc73e9212b41902bec589d602dc941a6dc8a3c37ede467fdb21101cc8befe111b8a365b94612be8eef16a14dc1f163647744c2d0eabc17dc0bd297ba2e9237fea5d8c845e11ae207c4ce8d5d17b2dffdb6ba20c0474ab9e8547b90bdf61d41602b64b84b3d725279e818dccb82f25c6a75dc473af074c97bd6ea775eb575dd07bd2574dfe308748ecc0d39df14659e1958dbc0413fa7f214b6ffb4059b0ef21e9c2602723695988382201c36dfb9bb5916246f0ecad281b1431846e43b5651a85745a4b81586282285f56625fdb8f46a7b752f1a81159f04c12ec20723c22ae2dadd384bcedecc16de9ae253081ec8b5616b94ec716eb2e91c6af58eddcb360841ec942782fe7b44e9ea191e98007faa5722f12b5e0ff23304297f3aeb369a3ff79434f2ff2e7575bd7e7a1d2592043ea245ca0f69cc0c781b42258230b45391d1c1545ed0a9dcdbedc454c6e7dd313b2e6e757f91309d8fa7801bba864f60f04a1b12e0770e4aa62b7d388f8a0b85d38defa8a21a7764388dd7273b941a17f1f1b1a7acefc8f1726a7cb4ea3f19ddb7a70082c1c5cb6921d4c0eea07cf4d226c98ed1c57a92652b2687181da8091db3ac77bbf5634c351990296494868dd1e365af320c88148a0ed887a06f4be3256cf4556f00ce3376a7016ffddc26f36272d48fc5500451a2ea6de5f6778948e9ba856db5a38129ad1367b983d1f2b624ac5e2e35ce9145eee563d047c853a30c6cabb8064ea183dba622adebae8d2149b93752776e55155319009f2f9a3c1aec9a2b266fdb0451c97fbcc8ca7c1adff0806adc1dc8105b678a30af95cfc5f370dd0e4a6db9811b2deac2ed1fc8716b0571210ee7208316f24dc2af7394519401173d36c3f25fbe4ebd965aa19f53e6fef550ab72216dcfd20ac974b40b456ce143bd4e495dd51fd58e23877ac91e28abed7e9a12c7fbf694e5b5cf144f011da4aca445cf5546d68b2341401460bf2d717663c 022406339068be457a4430d0de697ee810f0911afc344bd3d4d662771874d2ce 00 21 02039885cd5b9ffd24b5ce83f464a7d4c0e3f23c1a8061c9fc85730db67ffdbd0c fe0001a147 01 00" ->
UpdateAddHtlc(ByteVector32(hex"f865a44f81f02f3539842b863668403a68ddb3703e03cd91045c9ac114dbd28e"), 2, 146 msat, ByteVector32(hex"d3708298fd195572cceb86a1745210543c42f931a1a2baeed7f705d333ebed22"), CltvExpiry(78696), OnionRoutingPacket(0, hex"037cb785d5a9de762adc62e3f2407d452bd1f13d368d0429caee5541a89488e3b9", payload = hex"5b90dd3803ff56400a40dc539efa0a0e29736c76e83d2d8b44775adcb4d485be5eb84eefbfedc69b687ee5c7080f83ff31760ec036990d904de480064966bba393af729d57437c91bea905a66464461a6462c5a0c66976ecaeded73d330dfaeb5651ba68e74b210c123e63ac6ee15d6673cf126f046840bbf4364c907f56382870da85101045fc9392357b0b32081cde0e28460d20c1d5d61a5d56fe50d107304e4b184dd005048f9bcd159eff3cfaaef4ab5a8f29d38de109ea41a7ceaa886a0559a4fd0c7448cf28db85c3758fbde23443776e08dd29c435b1195f205aa787f2ec4a7259afeff078faa419c9af5706b9c08e7ce5772326e09df22eb85b0abc625b969aae34a881c12bd9653a08dc62b8e82cf89d0d66974f96149c6dcd7fc4363eecbf4fdac39e500c416b8d6a2e5871d80775acd1c13df4e4ad30a150390d500869ee6a4eab1c4285952d0549c45960a0b1b5ccfb93c75b63923fbc02b1d5a91e53f7424478cc58bf4366a612d40deec5efea700a7b127f7fdded1c4232e5b2eb7190f964e20b156e62d63d097005a24d96457a3db387a6b25e2a65fb169de0323a48f275780dc320d1c7d22642d3371e65559bf5510a3990b67aceb87daccd40e7f82fbba5a0065a63849dbb6600b420e8cb3561154dc69a3b063784737a7eb9d3f770fa1312c03e8518200cefc1be356bf0b0d1928816e712d24b5021b72f20d84a062a47ef50acf5cc9de025378611fbd7fd6473bc0258aa1697b057f6ce4c99f7a6ade34411a008aaccb3e3b2f78e2bc0720c3050960ad8b2988907a410a8fe565b671d3f2a274b9ed230786da769ebc73e9212b41902bec589d602dc941a6dc8a3c37ede467fdb21101cc8befe111b8a365b94612be8eef16a14dc1f163647744c2d0eabc17dc0bd297ba2e9237fea5d8c845e11ae207c4ce8d5d17b2dffdb6ba20c0474ab9e8547b90bdf61d41602b64b84b3d725279e818dccb82f25c6a75dc473af074c97bd6ea775eb575dd07bd2574dfe308748ecc0d39df14659e1958dbc0413fa7f214b6ffb4059b0ef21e9c2602723695988382201c36dfb9bb5916246f0ecad281b1431846e43b5651a85745a4b81586282285f56625fdb8f46a7b752f1a81159f04c12ec20723c22ae2dadd384bcedecc16de9ae253081ec8b5616b94ec716eb2e91c6af58eddcb360841ec942782fe7b44e9ea191e98007faa5722f12b5e0ff23304297f3aeb369a3ff79434f2ff2e7575bd7e7a1d2592043ea245ca0f69cc0c781b42258230b45391d1c1545ed0a9dcdbedc454c6e7dd313b2e6e757f91309d8fa7801bba864f60f04a1b12e0770e4aa62b7d388f8a0b85d38defa8a21a7764388dd7273b941a17f1f1b1a7acefc8f1726a7cb4ea3f19ddb7a70082c1c5cb6921d4c0eea07cf4d226c98ed1c57a92652b2687181da8091db3ac77bbf5634c351990296494868dd1e365af320c88148a0ed887a06f4be3256cf4556f00ce3376a7016ffddc26f36272d48fc5500451a2ea6de5f6778948e9ba856db5a38129ad1367b983d1f2b624ac5e2e35ce9145eee563d047c853a30c6cabb8064ea183dba622adebae8d2149b93752776e55155319009f2f9a3c1aec9a2b266fdb0451c97fbcc8ca7c1adff0806adc1dc8105b678a30af95cfc5f370dd0e4a6db9811b2deac2ed1fc8716b0571210ee7208316f24dc2af7394519401173d36c3f25fbe4ebd965aa19f53e6fef550ab72216dcfd20ac974b40b456ce143bd4e495dd51fd58e23877ac91e28abed7e9a12c7fbf694e5b5cf144f011da4aca445cf5546d68b2341401460bf2d717663c", ByteVector32(hex"022406339068be457a4430d0de697ee810f0911afc344bd3d4d662771874d2ce")), TlvStream(UpdateAddHtlcTlv.BlindingPoint(PublicKey(hex"02039885cd5b9ffd24b5ce83f464a7d4c0e3f23c1a8061c9fc85730db67ffdbd0c")), UpdateAddHtlcTlv.Endorsement(0))),
hex"2c2c2f7eb2eed5b415aed6671228a90d428d9c9fa1dbf492b0625dc4c7d243c3 0000000000000003 00000000000ba6f3 0b7cb0fb7cedeb92573b8865018730232430c8d2365c4e22017f306ae3853ff7 00001211 00 0344ad77d64a38466f8cabc92a956ccceb64e451d09945a45d4be7a16bcb59d84c a62cd346fe5002786e00d538f85d0606b7e946717c62f0df9cd806c04c27d02ce1e536560633099d81fa158aba333c4ccfb91b30bc7e361fcde9705457a0efb1aa5e5983448833b3603d4459a1322275cc29b79d285b762c38b916e176fb779c5ca8cb1804300342462cf4c10d6229e73e5a382976306d0d65583490a5b595fb7f41f4fef20496791381b16a51840c17e805ee44961316cb0ca62d7a0a23a1d42c0d8098128b09ab21ce4a6b5b8749c45f5e0f761c1e24c2e141714fa32bba27da8a113ea26f9af687dd6bafc902b4fb7e53af3dea9fc675928207c898eb2327cea938b342cab7e57cf9c34ec443ace8e66bdc41f98f934e8ba18db357f49d1bdf540632b369435b2e378cd97304e49ce037f531f2faf381a70aaef06582eb2b0956a6b7e39dcaf469253ed6a508947f1a715f6c42c028af2342ced466d7d65bf7d3282ee403b6f220403abad14541d806b355ac38c262dc943c7c239c23b1f863f87259838288a6b5868da8436a56d4d14e7eca32b92070f95ef332c09f3693e952841d6771cd5904a903910b8333337786acdc3099733534ac237e0fbef3acd8e0b4fd665afae94f886dcd86ba0ffe39b6a2fff7e761125ed48c9ef7340d73c3b52a4acf4336b9cc891196158ad77442b242016c1b2333d4be6708c51e5d4ca42cf90438cf21bb7e63731a3be083b20c74954997114c4d08ca886e93055f0fdb34efc3237ca40d28f386b441a699dbaf27f2abb82a49b864d67bca6db2b8b393c02181ec058e350f0f28037ccdca3815fe3f85af3fbcc9c2bcfbffec9ddbc292539ccd16df09c6c892533b3831d7463ac0091d6e3ddd1a5a282a686ce037d47a7c6e373f98110616a1f5f2031a8d231532beebc1703cbaf262c286db4d42ddcdf11c338a0b15dddf422ca09e76a43d453414b8900db9a1e2a6791543e8d9d3d0e3cbe82f2c6ffaa433bb675322e9ae104a9d7b7af7052a44f4b58b522418563ff4c859d5e954c50a8af1295d71f575a888fc30ce25c9d23f1954636ae6f6b2f987ce15a25fbfd7432ca83d2d6f1292dbb1c557b82b9d5bd0fed0b9e21e15401c23d08dfd613403d9127d6b8e4b7673c6ff6c07c47f806f251e36e71e5778f38e73233008d1968a5e6adab26cf77c6fcadd62ae3304c7ba89614107faeaa8eec0c9e9ea9307abcca1e44e40228991aec789cac3d190bb9ab425ad834b6fc69ca8776d926dc6215de382a1275bc327447a5f5ef6c92ae1a2c45cf27441692a5f5ff13a1d5b365aec77c726923f14e376a6aaa9b4ada3931350b8b7eab50e101a9714b884c73ad4fef78520f2582c4f4328b18b1102ea5c93b96055bdc9c955adbeda29a58acc1937e4cb185a481a7d9ec56050d5216869bdcb7773718c68f36de348043116f6f33d98c9b56f8111a2a08f76cf1dbcb0e86660dab947900ed0c6592429b23a9f1d21c72d9a27544a8e135241eb52e3080fa517efd232d6f7f7fb03f82e9332ab5292be9bdf8d67978dd1bc99eff426e02fb3dc9dd15c660747c88a46dd92aeba448be690bf1659a30b1ef304db9d5d607e82a5120439c36e70225a234ef3e4699920426826098ea215553fcb933a4e1ee8a86e53d9cbbcb1b3da0122cfaa2c245b0c60abd5a6dcbf27d4d5cc3374c0285c973bce4f2ab75c7fe19b1e75694568db79c7181a91f36737bb02d635a831e35afa93cb068e8389c3968443a6d2f679ace7e71c1525689b34cfb714e46843fe268320193ce5afdc9dd7aa506f5ed845292dbdd96f91dcbd436e870d59aa4dcac71b19c9756498b2ac9e4d8c7bb7c456559a0775494c326510a2d84a60ac eb26d892176ae2cddd393e1bb626ec2df1f1ae4c65e89f091cb4201e6aa132a5 fe0001a147 01 03" ->
UpdateAddHtlc(ByteVector32(hex"2c2c2f7eb2eed5b415aed6671228a90d428d9c9fa1dbf492b0625dc4c7d243c3"), 3, 763635 msat, ByteVector32(hex"0b7cb0fb7cedeb92573b8865018730232430c8d2365c4e22017f306ae3853ff7"), CltvExpiry(4625), OnionRoutingPacket(0, hex"0344ad77d64a38466f8cabc92a956ccceb64e451d09945a45d4be7a16bcb59d84c", payload = hex"a62cd346fe5002786e00d538f85d0606b7e946717c62f0df9cd806c04c27d02ce1e536560633099d81fa158aba333c4ccfb91b30bc7e361fcde9705457a0efb1aa5e5983448833b3603d4459a1322275cc29b79d285b762c38b916e176fb779c5ca8cb1804300342462cf4c10d6229e73e5a382976306d0d65583490a5b595fb7f41f4fef20496791381b16a51840c17e805ee44961316cb0ca62d7a0a23a1d42c0d8098128b09ab21ce4a6b5b8749c45f5e0f761c1e24c2e141714fa32bba27da8a113ea26f9af687dd6bafc902b4fb7e53af3dea9fc675928207c898eb2327cea938b342cab7e57cf9c34ec443ace8e66bdc41f98f934e8ba18db357f49d1bdf540632b369435b2e378cd97304e49ce037f531f2faf381a70aaef06582eb2b0956a6b7e39dcaf469253ed6a508947f1a715f6c42c028af2342ced466d7d65bf7d3282ee403b6f220403abad14541d806b355ac38c262dc943c7c239c23b1f863f87259838288a6b5868da8436a56d4d14e7eca32b92070f95ef332c09f3693e952841d6771cd5904a903910b8333337786acdc3099733534ac237e0fbef3acd8e0b4fd665afae94f886dcd86ba0ffe39b6a2fff7e761125ed48c9ef7340d73c3b52a4acf4336b9cc891196158ad77442b242016c1b2333d4be6708c51e5d4ca42cf90438cf21bb7e63731a3be083b20c74954997114c4d08ca886e93055f0fdb34efc3237ca40d28f386b441a699dbaf27f2abb82a49b864d67bca6db2b8b393c02181ec058e350f0f28037ccdca3815fe3f85af3fbcc9c2bcfbffec9ddbc292539ccd16df09c6c892533b3831d7463ac0091d6e3ddd1a5a282a686ce037d47a7c6e373f98110616a1f5f2031a8d231532beebc1703cbaf262c286db4d42ddcdf11c338a0b15dddf422ca09e76a43d453414b8900db9a1e2a6791543e8d9d3d0e3cbe82f2c6ffaa433bb675322e9ae104a9d7b7af7052a44f4b58b522418563ff4c859d5e954c50a8af1295d71f575a888fc30ce25c9d23f1954636ae6f6b2f987ce15a25fbfd7432ca83d2d6f1292dbb1c557b82b9d5bd0fed0b9e21e15401c23d08dfd613403d9127d6b8e4b7673c6ff6c07c47f806f251e36e71e5778f38e73233008d1968a5e6adab26cf77c6fcadd62ae3304c7ba89614107faeaa8eec0c9e9ea9307abcca1e44e40228991aec789cac3d190bb9ab425ad834b6fc69ca8776d926dc6215de382a1275bc327447a5f5ef6c92ae1a2c45cf27441692a5f5ff13a1d5b365aec77c726923f14e376a6aaa9b4ada3931350b8b7eab50e101a9714b884c73ad4fef78520f2582c4f4328b18b1102ea5c93b96055bdc9c955adbeda29a58acc1937e4cb185a481a7d9ec56050d5216869bdcb7773718c68f36de348043116f6f33d98c9b56f8111a2a08f76cf1dbcb0e86660dab947900ed0c6592429b23a9f1d21c72d9a27544a8e135241eb52e3080fa517efd232d6f7f7fb03f82e9332ab5292be9bdf8d67978dd1bc99eff426e02fb3dc9dd15c660747c88a46dd92aeba448be690bf1659a30b1ef304db9d5d607e82a5120439c36e70225a234ef3e4699920426826098ea215553fcb933a4e1ee8a86e53d9cbbcb1b3da0122cfaa2c245b0c60abd5a6dcbf27d4d5cc3374c0285c973bce4f2ab75c7fe19b1e75694568db79c7181a91f36737bb02d635a831e35afa93cb068e8389c3968443a6d2f679ace7e71c1525689b34cfb714e46843fe268320193ce5afdc9dd7aa506f5ed845292dbdd96f91dcbd436e870d59aa4dcac71b19c9756498b2ac9e4d8c7bb7c456559a0775494c326510a2d84a60ac", ByteVector32(hex"eb26d892176ae2cddd393e1bb626ec2df1f1ae4c65e89f091cb4201e6aa132a5")), TlvStream(UpdateAddHtlcTlv.Endorsement(3))),
hex"6c963f8e8b9be358f190a3ac3e12a34400bda4796ed9c23daf179794474a9b62 0000000000000004 00000000000000f5 d9b2563807d4830dc7a42e2df0a146b2acecd54ca3870a928f2b4ac5b489d0eb 000b3c4e 00 033df0a97d288ef59a42b68c03083c36f06b75e651f2620275347e49456e924949 afe9ae18f4780afe43a1450247b5c790e47a27983aa63b82356d049c277517f4991776396cfbbbb5905059a8ebcd49a1c63299a40df59bb8e1842025c8644defa4a0f0bd80d159c68b49747ad1625fbb5182a48634238d42b2678d39d5db9a67fbb3624cf10249b286ba780ced9ede8e37d93a248f756dc134401656d787d2106303082d26601a48aa30804632877de8bc721556f30e57caa3787b04f3712b4d320c24afa7891e70e6f76751cc47a09ddf86aea7099c43809c7f244b21e551d63d363f1c6b5db02504c46449fcfc8038e057713ed1bc5e6daa1b44a90a9db259964b963be6cbdfb4aa000caaf9984aa12ae5a2dc2323b9ab57c1ca35f722c29adeb08789aff2f25936070f38b9b390937983ba8d6434fed6cfd9077e6508b85a2ba020ffc9dc2507beb3278fda821f2ae61ef0ec6a4a226f7b067cc7e69122eeb91dda7885bf9d358d1dfd4ea5af1df4bae30eebe79ddc27abb4edfa4882e9167e557bd0aabb71c5b906f4d5c537a816ee958a1d7a76597e262b50198ba25fd0fb4971c5e22ad0724d1686afa1edb8a5ddf8ad57443258d8044f331463c6ce0f278b16a9a11b8c7b88a494c2c524bdfc37d67f0635f36b15356762f825d23e8228602421e065d828d628f3e76a0505be69179772aa62ee481def1ce1621f874e1ea74afcf0f42c3ab559163afd06c493a56ab0e0ce2563f3351dda1096ff7f7215d61689dd3adc51f2204c664c2cb429237423c7cca52d222662577ab5411b1ed05810b2b1e43ade1958fed3b21623cbf19933dd35e6596c886b3fcfb11b7efa78067786740f0ea887921c8d6a6841b74d2166b6cf83d4432b1f17cefdcffebc0ef08fe5416f5f1f5072d44fa835b5f7078723727aba801343669c8a1afc4e9a2ec3c3821af297c5ee5fd6364b866d7f8b47b6709303246a09274f5d17640b6c60fa9eeb2f7e35472f33db8538ca1cff95e39dd09ac3680faa4ca8ba1ed33f9726adc84a50619605a1b763765f1c26d884d74b884351cbca23d935b25095e08b8cce04e360e0587c034d883f1c7a44cfebf82c7c67dc13c6b76d396cb90a8158fa8d270084f716237eb9b6dc464b2c3e857443f0e8f3073079fefdd7f757abaf19b38da991956034ce1be47315022433bbe766e1d6d02c822314706702c2a61234345ff374c4291f5bd00d8d4caeef0a48785c63afb8196d4874f9c19bb53199bc7ed81a0e94108a7b6851b9a2e3a6f4e12a0eaddf16d4ff1cf6ff9f9da6d81cfc167896ecc3b7a2b6f774a1f394a321bdfb40bdaedc2ecc7148a6d6b1ef64e38c6ea35b0bb17986351e82be82aa2233ed069a6913bbf3a87e5b1094bc2c0ff28b918974357217e160562748a2440670ea1055df53e18a9a3afc0f9f34e40f222cb4f9f35a19488f0ca1b23ada14804f32d183971cb918d7b2430b3f2e4b7633204b0793862521d130e926b6583ca466acf4300020e2c85297f617e29e1c4f0e1ea5676062d8fabc8035f71d2598e3cf7f38e5f61b0f4896442b1c0b102f85fbd1068339dafc9debf90b88e89420337ac34643acf017debff60d030de65c22883205327c0af6cdf70349722073195e2597775514a86f766590c43a3b844f78618b7c7a63d2665a800d5bd1edee916c93ede8c0c8dc980ab9f85ff33c3b4740a4b0fc3f3b3e324a349e9c21e0aec8fdcc0a14b0e35b68b3d46cfcfd991eefc8b616f1a376030de33c1662c0210cfbaa27653ff8a814b4acd2ad0a09761db5f0ba8ef2a00cf66053725a4e422b0cd22f9d4881e28573ccfd3b9b3088698c1acb647d8ddeda65303fc57d9ad663a016b1c1a0dd6712 f6514b5e1eae383e2c5ae1ec1820f28583304274fa11ef2d2e2d6f3cafa2ede0 fe0001a147 01 07" ->
UpdateAddHtlc(ByteVector32(hex"6c963f8e8b9be358f190a3ac3e12a34400bda4796ed9c23daf179794474a9b62"), 4, 245 msat, ByteVector32(hex"d9b2563807d4830dc7a42e2df0a146b2acecd54ca3870a928f2b4ac5b489d0eb"), CltvExpiry(736334), OnionRoutingPacket(0, hex"033df0a97d288ef59a42b68c03083c36f06b75e651f2620275347e49456e924949", payload = hex"afe9ae18f4780afe43a1450247b5c790e47a27983aa63b82356d049c277517f4991776396cfbbbb5905059a8ebcd49a1c63299a40df59bb8e1842025c8644defa4a0f0bd80d159c68b49747ad1625fbb5182a48634238d42b2678d39d5db9a67fbb3624cf10249b286ba780ced9ede8e37d93a248f756dc134401656d787d2106303082d26601a48aa30804632877de8bc721556f30e57caa3787b04f3712b4d320c24afa7891e70e6f76751cc47a09ddf86aea7099c43809c7f244b21e551d63d363f1c6b5db02504c46449fcfc8038e057713ed1bc5e6daa1b44a90a9db259964b963be6cbdfb4aa000caaf9984aa12ae5a2dc2323b9ab57c1ca35f722c29adeb08789aff2f25936070f38b9b390937983ba8d6434fed6cfd9077e6508b85a2ba020ffc9dc2507beb3278fda821f2ae61ef0ec6a4a226f7b067cc7e69122eeb91dda7885bf9d358d1dfd4ea5af1df4bae30eebe79ddc27abb4edfa4882e9167e557bd0aabb71c5b906f4d5c537a816ee958a1d7a76597e262b50198ba25fd0fb4971c5e22ad0724d1686afa1edb8a5ddf8ad57443258d8044f331463c6ce0f278b16a9a11b8c7b88a494c2c524bdfc37d67f0635f36b15356762f825d23e8228602421e065d828d628f3e76a0505be69179772aa62ee481def1ce1621f874e1ea74afcf0f42c3ab559163afd06c493a56ab0e0ce2563f3351dda1096ff7f7215d61689dd3adc51f2204c664c2cb429237423c7cca52d222662577ab5411b1ed05810b2b1e43ade1958fed3b21623cbf19933dd35e6596c886b3fcfb11b7efa78067786740f0ea887921c8d6a6841b74d2166b6cf83d4432b1f17cefdcffebc0ef08fe5416f5f1f5072d44fa835b5f7078723727aba801343669c8a1afc4e9a2ec3c3821af297c5ee5fd6364b866d7f8b47b6709303246a09274f5d17640b6c60fa9eeb2f7e35472f33db8538ca1cff95e39dd09ac3680faa4ca8ba1ed33f9726adc84a50619605a1b763765f1c26d884d74b884351cbca23d935b25095e08b8cce04e360e0587c034d883f1c7a44cfebf82c7c67dc13c6b76d396cb90a8158fa8d270084f716237eb9b6dc464b2c3e857443f0e8f3073079fefdd7f757abaf19b38da991956034ce1be47315022433bbe766e1d6d02c822314706702c2a61234345ff374c4291f5bd00d8d4caeef0a48785c63afb8196d4874f9c19bb53199bc7ed81a0e94108a7b6851b9a2e3a6f4e12a0eaddf16d4ff1cf6ff9f9da6d81cfc167896ecc3b7a2b6f774a1f394a321bdfb40bdaedc2ecc7148a6d6b1ef64e38c6ea35b0bb17986351e82be82aa2233ed069a6913bbf3a87e5b1094bc2c0ff28b918974357217e160562748a2440670ea1055df53e18a9a3afc0f9f34e40f222cb4f9f35a19488f0ca1b23ada14804f32d183971cb918d7b2430b3f2e4b7633204b0793862521d130e926b6583ca466acf4300020e2c85297f617e29e1c4f0e1ea5676062d8fabc8035f71d2598e3cf7f38e5f61b0f4896442b1c0b102f85fbd1068339dafc9debf90b88e89420337ac34643acf017debff60d030de65c22883205327c0af6cdf70349722073195e2597775514a86f766590c43a3b844f78618b7c7a63d2665a800d5bd1edee916c93ede8c0c8dc980ab9f85ff33c3b4740a4b0fc3f3b3e324a349e9c21e0aec8fdcc0a14b0e35b68b3d46cfcfd991eefc8b616f1a376030de33c1662c0210cfbaa27653ff8a814b4acd2ad0a09761db5f0ba8ef2a00cf66053725a4e422b0cd22f9d4881e28573ccfd3b9b3088698c1acb647d8ddeda65303fc57d9ad663a016b1c1a0dd6712", ByteVector32(hex"f6514b5e1eae383e2c5ae1ec1820f28583304274fa11ef2d2e2d6f3cafa2ede0")), TlvStream(UpdateAddHtlcTlv.Endorsement(7))),
hex"2af2f6410744de2c8c5fe949443d8e137064bd97ef782278c8e02189b6f0231c 0000000000000005 000000002ef6eb30 9fbe04e7e3f8e71768cfc95d1b67a30ff995dbd2a312e44a839123e95f944258 00008e68 00 02c606691a88f80fdc10d007ef5dfa0b91ce33b7b3fa40a6df84f7285aaf37174e cf939423f7ca7c34b07034acef7e19feab1eba59462cf32fb785d4ecf61008247a17c87380ba957f2503ea6a899707f2167ad3972dfc0e7a00d9a0b6aae7b23b5b57903697f74f98808b35f16f546bd4d32395297ed0caddbd744bfe4ab301d8584756d43d6d1e005a35ef9a71ce2e0c88164165f6f125ea3aaed89dba3291e6adfaa29721a9694df0c3cf1f8d09626cf2026e4c10a7bcd42a5220c0f13a5f0caa397c5685aee4bc487f831a28d98b327522c8f63a36e76a367ca82e88ea0ea77a511447142f655fd35e44fb595c96ac7b0c32c79a6bac6d7b3035abbf7fe7ce05c1d2b4b7c25b9626f249a7b9ebc830461b899f460ecd11037c4bec9b0f4c0a4e32b41ae2010fe2c523c56f4d86ef9f0b6aa73b150734f616b8bf20c201620cd05555b7e2e3a822ee02602b3f5f39f9ec933a418b3da707b4f29a42b3346e79a29d9ebb57c14d6c29ad94bc400e2941db92d3f90ebb4af0dd395747f6b0e0bb42648ea37280279ca0d8762ad1af4c96f56f41082cfe446f0a0c923e5fa97fec390382b401940c53ea9ac3f71a20ff61792557ab9d520816e1aa489ea2a38caa20c40e4aff6954e6b8d7469c496f9372a0c031267fa247f1901f1cebe87d2181288740345537f7af69e3f21b65ea1a622e1741cd5504e33afb6922544b3ee022e2be4f9f400ae401db1a43240d9c272a6dc653b647e6d91b392246b98f9a15547bbf6ddf289844c4910c880b362a383728f91379173b527f8ac5bef772267e5633ec6e758d0c3671903b32c8dad8d21ffec3a0b6f27cc5004fc5a12bcf6bd57aa5c6a982f82c8271e9177ccc2b118957d16c760aa1d60cc7e9408040acbfd186d3c76fcdc7b57b180d6e6b8ea311495d7611e73c4548bd3cccc7d12f0ca69b6240f578a635187713aaa9b561e0dff791c437f95a61818ebf55cdc35a44b0860aac41c3abfedccb4eb8970152fa324e10afdbdebc80dbfbb09e4800cbe51c32daf942e3f54f3d3bee352c3c31290a76f07f4b1a079f5f38109b0d0520a253c9feb0b2e0982730ab2b5f3afd41a9f22d7f4380f4ea6f795540daaae771fe6a9119a13ba3f07ca861ed78698447d070dcfc6cb4843c348e33eaf5e576e1ce9412fd72b69673a3c30b9cc528b041b5489c48c265d7f2251a204e8dc40991b3c8f5620e7a207df2eee41f3c42520b21bba2208e5eff594928271250861982334a139738c030e70606c3da9e26c4ec3c286dd678f3b8ffc4dcd4b0bf5ac595764887de862d5c241368d9f2481eb81a529b0cf4d8d75140b440e26e5af4effa7ad05b1b41a2bf223e902e70af44277ebd1690b5d6da0a3dad18485d967dbb77a3d319fd6e4693e6ee9e99ed8d66f2fb0f355560d87fa8d4ed6e1aac8db481be2091b922df75eb738f64246ad5497ef67c0193d7d8cd0a22694ff63b7bdd915217d93bec2c26077f4de4c3a111b19d4455fb4f77ff72c755e89a71f58155fa47b7f8cd775daa881077b908c89be5e7064c588dc6f520f60d4d6816507c156e553775c843a13fafb9db03de8422c36426f3856124f22236dc8151539ae18a927a4a6fa7e4aed21898184c4b9383b0dbaf9b2938bec0a64a6a8cd606eeb72076655ded0d0f71f95f075c03fed936e688c2d202c7c7e93586d8b49eac1dd9871b5fbc2ab854aa4e86119bf317902cb362e03e0c5bb7b79f80071652a64dc1f5172575edcb0e3fac774d853083b6faca3860b661163f4fe4f8595238c76987d088eaedf8c2fafc14a2995b0cfa951c5df92c55e3784215b0722d08bc9a43bb32c6531393465714b190ae78dd1b18b0f3e0c0432c034a11273d36 931651e9b75081bf2acdf3b9b5f1dc9871b47ff39841b5b17e0a2ea2c9bf4fdc" ->
UpdateAddHtlc(ByteVector32(hex"2af2f6410744de2c8c5fe949443d8e137064bd97ef782278c8e02189b6f0231c"), 5, 787934000 msat, ByteVector32(hex"9fbe04e7e3f8e71768cfc95d1b67a30ff995dbd2a312e44a839123e95f944258"), CltvExpiry(36456), OnionRoutingPacket(0, hex"02c606691a88f80fdc10d007ef5dfa0b91ce33b7b3fa40a6df84f7285aaf37174e", payload = hex"cf939423f7ca7c34b07034acef7e19feab1eba59462cf32fb785d4ecf61008247a17c87380ba957f2503ea6a899707f2167ad3972dfc0e7a00d9a0b6aae7b23b5b57903697f74f98808b35f16f546bd4d32395297ed0caddbd744bfe4ab301d8584756d43d6d1e005a35ef9a71ce2e0c88164165f6f125ea3aaed89dba3291e6adfaa29721a9694df0c3cf1f8d09626cf2026e4c10a7bcd42a5220c0f13a5f0caa397c5685aee4bc487f831a28d98b327522c8f63a36e76a367ca82e88ea0ea77a511447142f655fd35e44fb595c96ac7b0c32c79a6bac6d7b3035abbf7fe7ce05c1d2b4b7c25b9626f249a7b9ebc830461b899f460ecd11037c4bec9b0f4c0a4e32b41ae2010fe2c523c56f4d86ef9f0b6aa73b150734f616b8bf20c201620cd05555b7e2e3a822ee02602b3f5f39f9ec933a418b3da707b4f29a42b3346e79a29d9ebb57c14d6c29ad94bc400e2941db92d3f90ebb4af0dd395747f6b0e0bb42648ea37280279ca0d8762ad1af4c96f56f41082cfe446f0a0c923e5fa97fec390382b401940c53ea9ac3f71a20ff61792557ab9d520816e1aa489ea2a38caa20c40e4aff6954e6b8d7469c496f9372a0c031267fa247f1901f1cebe87d2181288740345537f7af69e3f21b65ea1a622e1741cd5504e33afb6922544b3ee022e2be4f9f400ae401db1a43240d9c272a6dc653b647e6d91b392246b98f9a15547bbf6ddf289844c4910c880b362a383728f91379173b527f8ac5bef772267e5633ec6e758d0c3671903b32c8dad8d21ffec3a0b6f27cc5004fc5a12bcf6bd57aa5c6a982f82c8271e9177ccc2b118957d16c760aa1d60cc7e9408040acbfd186d3c76fcdc7b57b180d6e6b8ea311495d7611e73c4548bd3cccc7d12f0ca69b6240f578a635187713aaa9b561e0dff791c437f95a61818ebf55cdc35a44b0860aac41c3abfedccb4eb8970152fa324e10afdbdebc80dbfbb09e4800cbe51c32daf942e3f54f3d3bee352c3c31290a76f07f4b1a079f5f38109b0d0520a253c9feb0b2e0982730ab2b5f3afd41a9f22d7f4380f4ea6f795540daaae771fe6a9119a13ba3f07ca861ed78698447d070dcfc6cb4843c348e33eaf5e576e1ce9412fd72b69673a3c30b9cc528b041b5489c48c265d7f2251a204e8dc40991b3c8f5620e7a207df2eee41f3c42520b21bba2208e5eff594928271250861982334a139738c030e70606c3da9e26c4ec3c286dd678f3b8ffc4dcd4b0bf5ac595764887de862d5c241368d9f2481eb81a529b0cf4d8d75140b440e26e5af4effa7ad05b1b41a2bf223e902e70af44277ebd1690b5d6da0a3dad18485d967dbb77a3d319fd6e4693e6ee9e99ed8d66f2fb0f355560d87fa8d4ed6e1aac8db481be2091b922df75eb738f64246ad5497ef67c0193d7d8cd0a22694ff63b7bdd915217d93bec2c26077f4de4c3a111b19d4455fb4f77ff72c755e89a71f58155fa47b7f8cd775daa881077b908c89be5e7064c588dc6f520f60d4d6816507c156e553775c843a13fafb9db03de8422c36426f3856124f22236dc8151539ae18a927a4a6fa7e4aed21898184c4b9383b0dbaf9b2938bec0a64a6a8cd606eeb72076655ded0d0f71f95f075c03fed936e688c2d202c7c7e93586d8b49eac1dd9871b5fbc2ab854aa4e86119bf317902cb362e03e0c5bb7b79f80071652a64dc1f5172575edcb0e3fac774d853083b6faca3860b661163f4fe4f8595238c76987d088eaedf8c2fafc14a2995b0cfa951c5df92c55e3784215b0722d08bc9a43bb32c6531393465714b190ae78dd1b18b0f3e0c0432c034a11273d36", ByteVector32(hex"931651e9b75081bf2acdf3b9b5f1dc9871b47ff39841b5b17e0a2ea2c9bf4fdc")), TlvStream()),
)
for ((bin, ref) <- testCases) {
val decoded = updateAddHtlcCodec.decode(bin.bits).require
assert(decoded.value == ref)
assert(decoded.remainder.isEmpty)
assert(updateAddHtlcCodec.encode(decoded.value).require.bytes == bin)
}
}
}