1
0
mirror of https://github.com/ACINQ/eclair.git synced 2024-11-20 02:27:32 +01:00

Do not reply to htlc settlement commands (#1527)

The replies are always ignored currently anyway. A new trait `NoReplyTo`
has been introduced. Those commands have a particular workflow because
they are persisted in the pending relay db.
This commit is contained in:
Pierre-Marie Padiou 2020-09-16 10:39:06 +02:00 committed by GitHub
parent d0011005a0
commit 2fc118c291
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 52 additions and 79 deletions

View File

@ -1854,12 +1854,15 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
}
def handleCommandSuccess(c: Command, newData: Data) = {
val replyTo = c match {
case hasReplyTo: HasReplyTo if hasReplyTo.replyTo != ActorRef.noSender => hasReplyTo.replyTo
case _ => sender
val replyTo_opt = c match {
case _: NoReplyTo => None
case hasReplyTo: HasReplyTo if hasReplyTo.replyTo != ActorRef.noSender => Some(hasReplyTo.replyTo)
case _ => Some(sender)
}
replyTo_opt.foreach { replyTo =>
val channelId = Helpers.getChannelId(newData)
replyTo ! RES_SUCCESS(c, channelId)
}
val channelId = Helpers.getChannelId(newData)
replyTo ! RES_SUCCESS(c, channelId)
stay using newData
}

View File

@ -157,11 +157,12 @@ object Origin {
sealed trait Command
sealed trait HasReplyTo { this: Command => def replyTo: ActorRef }
sealed trait HasHtlcId { this: Command => def id: Long }
final case class CMD_FULFILL_HTLC(id: Long, r: ByteVector32, commit: Boolean = false) extends Command with HasHtlcId
final case class CMD_FAIL_HTLC(id: Long, reason: Either[ByteVector, FailureMessage], commit: Boolean = false) extends Command with HasHtlcId
final case class CMD_FAIL_MALFORMED_HTLC(id: Long, onionHash: ByteVector32, failureCode: Int, commit: Boolean = false) extends Command with HasHtlcId
sealed trait NoReplyTo { this: Command => }
final case class CMD_ADD_HTLC(replyTo: ActorRef, amount: MilliSatoshi, paymentHash: ByteVector32, cltvExpiry: CltvExpiry, onion: OnionRoutingPacket, origin: Origin.Hot, commit: Boolean = false) extends Command with HasReplyTo
sealed trait HtlcSettlementCommand extends Command with NoReplyTo { def id: Long }
final case class CMD_FULFILL_HTLC(id: Long, r: ByteVector32, commit: Boolean = false) extends HtlcSettlementCommand
final case class CMD_FAIL_HTLC(id: Long, reason: Either[ByteVector, FailureMessage], commit: Boolean = false) extends HtlcSettlementCommand
final case class CMD_FAIL_MALFORMED_HTLC(id: Long, onionHash: ByteVector32, failureCode: Int, commit: Boolean = false) extends HtlcSettlementCommand
final case class CMD_UPDATE_FEE(feeratePerKw: FeeratePerKw, commit: Boolean = false) extends Command
case object CMD_SIGN extends Command
sealed trait CloseCommand extends Command

View File

@ -38,11 +38,11 @@ import fr.acinq.eclair.wire.{UpdateFailHtlc, UpdateFailMalformedHtlc, UpdateFulf
*/
trait PendingRelayDb extends Closeable {
def addPendingRelay(channelId: ByteVector32, cmd: Command with HasHtlcId): Unit
def addPendingRelay(channelId: ByteVector32, cmd: HtlcSettlementCommand): Unit
def removePendingRelay(channelId: ByteVector32, htlcId: Long): Unit
def listPendingRelay(channelId: ByteVector32): Seq[Command with HasHtlcId]
def listPendingRelay(channelId: ByteVector32): Seq[HtlcSettlementCommand]
def listPendingRelay(): Set[(ByteVector32, Long)]
@ -54,16 +54,14 @@ object PendingRelayDb {
* in a database because we don't want to lose preimages, or to forget to fail
* incoming htlcs, which would lead to unwanted channel closings.
*/
def safeSend(register: ActorRef, db: PendingRelayDb, replyTo: ActorRef, channelId: ByteVector32, cmd: Command with HasHtlcId): Unit = {
register ! Register.Forward(replyTo, channelId, cmd)
def safeSend(register: ActorRef, db: PendingRelayDb, channelId: ByteVector32, cmd: HtlcSettlementCommand): Unit = {
// htlc settlement commands don't have replyTo
register ! Register.Forward(ActorRef.noSender, channelId, cmd)
// we store the command in a db (note that this happens *after* forwarding the command to the channel, so we don't add latency)
db.addPendingRelay(channelId, cmd)
}
def safeSend(register: ActorRef, db: PendingRelayDb, channelId: ByteVector32, cmd: Command with HasHtlcId)(implicit ctx: ActorContext): Unit =
safeSend(register, db, ctx.self, channelId, cmd)
def ackCommand(db: PendingRelayDb, channelId: ByteVector32, cmd: Command with HasHtlcId): Unit = {
def ackCommand(db: PendingRelayDb, channelId: ByteVector32, cmd: HtlcSettlementCommand): Unit = {
db.removePendingRelay(channelId, cmd.id)
}
@ -79,7 +77,7 @@ object PendingRelayDb {
db.removePendingRelay(u.channelId, u.id)
}
def getPendingFailsAndFulfills(db: PendingRelayDb, channelId: ByteVector32)(implicit log: LoggingAdapter): Seq[Command with HasHtlcId] = {
def getPendingFailsAndFulfills(db: PendingRelayDb, channelId: ByteVector32)(implicit log: LoggingAdapter): Seq[HtlcSettlementCommand] = {
db.listPendingRelay(channelId)
}
}

View File

@ -18,7 +18,7 @@ package fr.acinq.eclair.db.pg
import fr.acinq.bitcoin.ByteVector32
import fr.acinq.eclair.channel.{Command, HasHtlcId}
import fr.acinq.eclair.channel.{Command, HtlcSettlementCommand}
import fr.acinq.eclair.db.Monitoring.Metrics.withMetrics
import fr.acinq.eclair.db.PendingRelayDb
import fr.acinq.eclair.db.pg.PgUtils._
@ -44,7 +44,7 @@ class PgPendingRelayDb(implicit ds: DataSource, lock: DatabaseLock) extends Pend
}
}
override def addPendingRelay(channelId: ByteVector32, cmd: Command with HasHtlcId): Unit = withMetrics("pending-relay/add") {
override def addPendingRelay(channelId: ByteVector32, cmd: HtlcSettlementCommand): Unit = withMetrics("pending-relay/add") {
withLock { pg =>
using(pg.prepareStatement("INSERT INTO pending_relay VALUES (?, ?, ?) ON CONFLICT DO NOTHING")) { statement =>
statement.setString(1, channelId.toHex)
@ -65,7 +65,7 @@ class PgPendingRelayDb(implicit ds: DataSource, lock: DatabaseLock) extends Pend
}
}
override def listPendingRelay(channelId: ByteVector32): Seq[Command with HasHtlcId] = withMetrics("pending-relay/list-channel") {
override def listPendingRelay(channelId: ByteVector32): Seq[HtlcSettlementCommand] = withMetrics("pending-relay/list-channel") {
withLock { pg =>
using(pg.prepareStatement("SELECT htlc_id, data FROM pending_relay WHERE channel_id=?")) { statement =>
statement.setString(1, channelId.toHex)

View File

@ -19,7 +19,7 @@ package fr.acinq.eclair.db.sqlite
import java.sql.Connection
import fr.acinq.bitcoin.ByteVector32
import fr.acinq.eclair.channel.{Command, HasHtlcId}
import fr.acinq.eclair.channel.{Command, HtlcSettlementCommand}
import fr.acinq.eclair.db.Monitoring.Metrics.withMetrics
import fr.acinq.eclair.db.PendingRelayDb
import fr.acinq.eclair.wire.CommandCodecs.cmdCodec
@ -40,7 +40,7 @@ class SqlitePendingRelayDb(sqlite: Connection) extends PendingRelayDb {
statement.executeUpdate("CREATE TABLE IF NOT EXISTS pending_relay (channel_id BLOB NOT NULL, htlc_id INTEGER NOT NULL, data BLOB NOT NULL, PRIMARY KEY(channel_id, htlc_id))")
}
override def addPendingRelay(channelId: ByteVector32, cmd: Command with HasHtlcId): Unit = withMetrics("pending-relay/add") {
override def addPendingRelay(channelId: ByteVector32, cmd: HtlcSettlementCommand): Unit = withMetrics("pending-relay/add") {
using(sqlite.prepareStatement("INSERT OR IGNORE INTO pending_relay VALUES (?, ?, ?)")) { statement =>
statement.setBytes(1, channelId.toArray)
statement.setLong(2, cmd.id)
@ -57,7 +57,7 @@ class SqlitePendingRelayDb(sqlite: Connection) extends PendingRelayDb {
}
}
override def listPendingRelay(channelId: ByteVector32): Seq[Command with HasHtlcId] = withMetrics("pending-relay/list-channel") {
override def listPendingRelay(channelId: ByteVector32): Seq[HtlcSettlementCommand] = withMetrics("pending-relay/list-channel") {
using(sqlite.prepareStatement("SELECT data FROM pending_relay WHERE channel_id=?")) { statement =>
statement.setBytes(1, channelId.toArray)
val rs = statement.executeQuery()

View File

@ -87,10 +87,6 @@ class MultiPartPaymentFSM(nodeParams: NodeParams, paymentHash: ByteVector32, tot
stay
}
whenUnhandled {
case Event(_: RES_SUCCESS[_], _) => stay
}
onTransition {
case WAITING_FOR_HTLC -> WAITING_FOR_HTLC => () // don't do anything if we stay in that state
case WAITING_FOR_HTLC -> _ => cancelTimer(PaymentTimeout.toString)

View File

@ -77,7 +77,7 @@ object ChannelRelay {
}
}
def translateRelayFailure(originHtlcId: Long, fail: HtlcResult.Fail): channel.Command with channel.HasHtlcId = {
def translateRelayFailure(originHtlcId: Long, fail: HtlcResult.Fail): channel.Command with channel.HtlcSettlementCommand = {
fail match {
case f: HtlcResult.RemoteFail => CMD_FAIL_HTLC(originHtlcId, Left(f.fail.reason), commit = true)
case f: HtlcResult.RemoteFailMalformed => CMD_FAIL_MALFORMED_HTLC(originHtlcId, f.fail.onionHash, f.fail.failureCode, commit = true)
@ -154,9 +154,9 @@ class ChannelRelay private(nodeParams: NodeParams,
safeSendAndStop(o.originChannelId, cmd)
}
def safeSendAndStop(channelId: ByteVector32, cmd: channel.Command with channel.HasHtlcId): Behavior[Command] = {
def safeSendAndStop(channelId: ByteVector32, cmd: channel.Command with channel.HtlcSettlementCommand): Behavior[Command] = {
// NB: we are not using an adapter here because we are stopping anyway so we won't be there to get the result
PendingRelayDb.safeSend(register, nodeParams.db.pendingRelay, context.system.deadLetters.toClassic, channelId, cmd)
PendingRelayDb.safeSend(register, nodeParams.db.pendingRelay, channelId, cmd)
Behaviors.stopped
}

View File

@ -310,7 +310,7 @@ class NodeRelay private(nodeParams: NodeParams,
private def rejectHtlc(htlcId: Long, channelId: ByteVector32, amount: MilliSatoshi, failure: Option[FailureMessage] = None): Unit = {
val failureMessage = failure.getOrElse(IncorrectOrUnknownPaymentDetails(amount, nodeParams.currentBlockHeight))
val cmd = CMD_FAIL_HTLC(htlcId, Right(failureMessage), commit = true)
PendingRelayDb.safeSend(register, nodeParams.db.pendingRelay, context.system.deadLetters.toClassic, channelId, cmd)
PendingRelayDb.safeSend(register, nodeParams.db.pendingRelay, channelId, cmd)
}
private def rejectPayment(upstream: Upstream.Trampoline, failure: Option[FailureMessage]): Unit = {
@ -320,7 +320,7 @@ class NodeRelay private(nodeParams: NodeParams,
private def fulfillPayment(upstream: Upstream.Trampoline, paymentPreimage: ByteVector32): Unit = upstream.adds.foreach(add => {
val cmd = CMD_FULFILL_HTLC(add.id, paymentPreimage, commit = true)
PendingRelayDb.safeSend(register, nodeParams.db.pendingRelay, context.system.deadLetters.toClassic, add.channelId, cmd)
PendingRelayDb.safeSend(register, nodeParams.db.pendingRelay, add.channelId, cmd)
})
private def success(upstream: Upstream.Trampoline, fulfilledUpstream: Boolean, paymentSent: PaymentSent): Unit = {

View File

@ -115,8 +115,6 @@ class PostRestartHtlcCleaner(nodeParams: NodeParams, register: ActorRef, initial
handleDownstreamFailure(brokenHtlcs, o, htlc, fail)
case GetBrokenHtlcs => sender ! brokenHtlcs
case _: RES_SUCCESS[_] => // ignoring responses from channels
}
private def handleDownstreamFulfill(brokenHtlcs: BrokenHtlcs, origin: Origin.Cold, fulfilledHtlc: UpdateAddHtlc, paymentPreimage: ByteVector32): Unit =

View File

@ -89,8 +89,6 @@ class Relayer(nodeParams: NodeParams, router: ActorRef, register: ActorRef, paym
case o: Origin.Hot => o.replyTo ! r
}
case _: RES_SUCCESS[_] => () // ignoring responses from channels
case g: GetOutgoingChannels => channelRelayer ! ChannelRelayer.GetOutgoingChannels(sender, g)
case GetChildActors(replyTo) => replyTo ! ChildActors(postRestartCleaner, channelRelayer, nodeRelayer)

View File

@ -40,7 +40,7 @@ object CommandCodecs {
("failureCode" | uint16) ::
("commit" | provide(false))).as[CMD_FAIL_MALFORMED_HTLC]
val cmdCodec: Codec[Command with HasHtlcId] = discriminated[Command with HasHtlcId].by(uint16)
val cmdCodec: Codec[HtlcSettlementCommand] = discriminated[HtlcSettlementCommand].by(uint16)
.typecase(0, cmdFulfillCodec)
.typecase(1, cmdFailCodec)
.typecase(2, cmdFailMalformedCodec)

View File

@ -158,7 +158,6 @@ trait StateTestsHelperMethods extends TestKitBase with FixtureTestSuite with Par
def fulfillHtlc(id: Long, R: ByteVector32, s: TestFSMRef[State, Data, Channel], r: TestFSMRef[State, Data, Channel], s2r: TestProbe, r2s: TestProbe): Unit = {
val sender = TestProbe()
sender.send(s, CMD_FULFILL_HTLC(id, R))
sender.expectMsgType[RES_SUCCESS[CMD_FULFILL_HTLC]]
val fulfill = s2r.expectMsgType[UpdateFulfillHtlc]
s2r.forward(r)
awaitCond(r.stateData.asInstanceOf[HasCommitments].commitments.remoteChanges.proposed.contains(fulfill))

View File

@ -739,7 +739,6 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val (r, htlc) = addHtlc(50000000 msat, alice, bob, alice2bob, bob2alice)
crossSign(alice, bob, alice2bob, bob2alice)
sender.send(bob, CMD_FULFILL_HTLC(htlc.id, r))
sender.expectMsgType[RES_SUCCESS[CMD_FULFILL_HTLC]]
bob2alice.expectMsgType[UpdateFulfillHtlc]
// we listen to channel_update events
val listener = TestProbe()
@ -1115,7 +1114,6 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val (_, htlc) = addHtlc(50000000 msat, alice, bob, alice2bob, bob2alice)
crossSign(alice, bob, alice2bob, bob2alice)
sender.send(bob, CMD_FAIL_HTLC(htlc.id, Right(PermanentChannelFailure)))
sender.expectMsgType[RES_SUCCESS[CMD_FAIL_HTLC]]
val fail = bob2alice.expectMsgType[UpdateFailHtlc]
bob2alice.forward(alice)
sender.send(bob, CMD_SIGN)
@ -1144,7 +1142,6 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val (_, htlc) = addHtlc(50000000 msat, alice, bob, alice2bob, bob2alice)
crossSign(alice, bob, alice2bob, bob2alice)
sender.send(bob, CMD_FAIL_MALFORMED_HTLC(htlc.id, Sphinx.PaymentPacket.hash(htlc.onionRoutingPacket), FailureMessageCodecs.BADONION))
sender.expectMsgType[RES_SUCCESS[CMD_FAIL_MALFORMED_HTLC]]
val fail = bob2alice.expectMsgType[UpdateFailMalformedHtlc]
bob2alice.forward(alice)
sender.send(bob, CMD_SIGN)
@ -1239,7 +1236,6 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
// actual test begins
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
sender.send(bob, CMD_FULFILL_HTLC(htlc.id, r))
sender.expectMsgType[RES_SUCCESS[CMD_FULFILL_HTLC]]
val fulfill = bob2alice.expectMsgType[UpdateFulfillHtlc]
awaitCond(bob.stateData == initialState.copy(
commitments = initialState.commitments.copy(
@ -1301,7 +1297,6 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val (r, htlc) = addHtlc(50000000 msat, alice, bob, alice2bob, bob2alice)
crossSign(alice, bob, alice2bob, bob2alice)
sender.send(bob, CMD_FULFILL_HTLC(htlc.id, r))
sender.expectMsgType[RES_SUCCESS[CMD_FULFILL_HTLC]]
val fulfill = bob2alice.expectMsgType[UpdateFulfillHtlc]
// actual test begins
@ -1391,7 +1386,6 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
// actual test begins
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
sender.send(bob, CMD_FAIL_HTLC(htlc.id, Right(PermanentChannelFailure)))
sender.expectMsgType[RES_SUCCESS[CMD_FAIL_HTLC]]
val fail = bob2alice.expectMsgType[UpdateFailHtlc]
awaitCond(bob.stateData == initialState.copy(
commitments = initialState.commitments.copy(
@ -1442,7 +1436,6 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
// actual test begins
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
sender.send(bob, CMD_FAIL_MALFORMED_HTLC(htlc.id, Sphinx.PaymentPacket.hash(htlc.onionRoutingPacket), FailureMessageCodecs.BADONION))
sender.expectMsgType[RES_SUCCESS[CMD_FAIL_MALFORMED_HTLC]]
val fail = bob2alice.expectMsgType[UpdateFailMalformedHtlc]
awaitCond(bob.stateData == initialState.copy(
commitments = initialState.commitments.copy(
@ -1487,7 +1480,6 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val (_, htlc) = addHtlc(50000000 msat, alice, bob, alice2bob, bob2alice)
crossSign(alice, bob, alice2bob, bob2alice)
sender.send(bob, CMD_FAIL_HTLC(htlc.id, Right(PermanentChannelFailure)))
sender.expectMsgType[RES_SUCCESS[CMD_FAIL_HTLC]]
val fail = bob2alice.expectMsgType[UpdateFailHtlc]
// actual test begins
@ -1521,7 +1513,6 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
// Bob fails the HTLC because he cannot parse it
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
sender.send(bob, CMD_FAIL_MALFORMED_HTLC(htlc.id, Sphinx.PaymentPacket.hash(htlc.onionRoutingPacket), FailureMessageCodecs.BADONION))
sender.expectMsgType[RES_SUCCESS[CMD_FAIL_MALFORMED_HTLC]]
val fail = bob2alice.expectMsgType[UpdateFailMalformedHtlc]
bob2alice.forward(alice)
@ -1601,7 +1592,6 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
crossSign(alice, bob, alice2bob, bob2alice)
// Bob receives a failure with a completely invalid onion error (missing mac)
sender.send(bob, CMD_FAIL_HTLC(htlc.id, Left(ByteVector.fill(260)(42))))
sender.expectMsgType[RES_SUCCESS[CMD_FAIL_HTLC]]
val fail = bob2alice.expectMsgType[UpdateFailHtlc]
assert(fail.id === htlc.id)
// We should rectify the packet length before forwarding upstream.
@ -2065,7 +2055,6 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val HtlcSuccessTx(_, htlcSuccessTx, _) = initialState.commitments.localCommit.publishableTxs.htlcTxsAndSigs.head.txinfo
sender.send(bob, CMD_FULFILL_HTLC(htlc.id, r, commit = true))
sender.expectMsgType[RES_SUCCESS[CMD_FULFILL_HTLC]]
bob2alice.expectMsgType[UpdateFulfillHtlc]
sender.send(bob, CurrentBlockCount((htlc.cltvExpiry - Bob.nodeParams.fulfillSafetyBeforeTimeout).toLong))
@ -2100,7 +2089,6 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val HtlcSuccessTx(_, htlcSuccessTx, _) = initialState.commitments.localCommit.publishableTxs.htlcTxsAndSigs.head.txinfo
sender.send(bob, CMD_FULFILL_HTLC(htlc.id, r, commit = false))
sender.expectMsgType[RES_SUCCESS[CMD_FULFILL_HTLC]]
bob2alice.expectMsgType[UpdateFulfillHtlc]
sender.send(bob, CurrentBlockCount((htlc.cltvExpiry - Bob.nodeParams.fulfillSafetyBeforeTimeout).toLong))
@ -2135,7 +2123,6 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val HtlcSuccessTx(_, htlcSuccessTx, _) = initialState.commitments.localCommit.publishableTxs.htlcTxsAndSigs.head.txinfo
sender.send(bob, CMD_FULFILL_HTLC(htlc.id, r, commit = true))
sender.expectMsgType[RES_SUCCESS[CMD_FULFILL_HTLC]]
bob2alice.expectMsgType[UpdateFulfillHtlc]
bob2alice.forward(alice)
bob2alice.expectMsgType[CommitSig]

View File

@ -121,7 +121,6 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit
val sender = TestProbe()
val initialState = bob.stateData.asInstanceOf[DATA_SHUTDOWN]
sender.send(bob, CMD_FULFILL_HTLC(0, r1))
sender.expectMsgType[RES_SUCCESS[CMD_FULFILL_HTLC]]
val fulfill = bob2alice.expectMsgType[UpdateFulfillHtlc]
awaitCond(bob.stateData == initialState.copy(
commitments = initialState.commitments.copy(
@ -206,7 +205,6 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit
val sender = TestProbe()
val initialState = bob.stateData.asInstanceOf[DATA_SHUTDOWN]
sender.send(bob, CMD_FAIL_HTLC(1, Right(PermanentChannelFailure)))
sender.expectMsgType[RES_SUCCESS[CMD_FAIL_HTLC]]
val fail = bob2alice.expectMsgType[UpdateFailHtlc]
awaitCond(bob.stateData == initialState.copy(
commitments = initialState.commitments.copy(
@ -238,7 +236,6 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit
val sender = TestProbe()
val initialState = bob.stateData.asInstanceOf[DATA_SHUTDOWN]
sender.send(bob, CMD_FAIL_MALFORMED_HTLC(1, Crypto.sha256(ByteVector.empty), FailureMessageCodecs.BADONION))
sender.expectMsgType[RES_SUCCESS[CMD_FAIL_MALFORMED_HTLC]]
val fail = bob2alice.expectMsgType[UpdateFailMalformedHtlc]
awaitCond(bob.stateData == initialState.copy(
commitments = initialState.commitments.copy(
@ -357,7 +354,6 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit
import f._
val sender = TestProbe()
sender.send(bob, CMD_FULFILL_HTLC(0, r1))
sender.expectMsgType[RES_SUCCESS[CMD_FULFILL_HTLC]]
bob2alice.expectMsgType[UpdateFulfillHtlc]
sender.send(bob, CMD_SIGN)
sender.expectMsgType[RES_SUCCESS[CMD_SIGN.type]]
@ -376,7 +372,6 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit
import f._
val sender = TestProbe()
sender.send(bob, CMD_FULFILL_HTLC(0, r1))
sender.expectMsgType[RES_SUCCESS[CMD_FULFILL_HTLC]]
bob2alice.expectMsgType[UpdateFulfillHtlc]
bob2alice.forward(alice)
sender.send(bob, CMD_SIGN)
@ -453,7 +448,6 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit
val tx = bob.stateData.asInstanceOf[DATA_SHUTDOWN].commitments.localCommit.publishableTxs.commitTx.tx
val sender = TestProbe()
sender.send(bob, CMD_FULFILL_HTLC(0, r1))
sender.expectMsgType[RES_SUCCESS[CMD_FULFILL_HTLC]]
bob2alice.expectMsgType[UpdateFulfillHtlc]
bob2alice.forward(alice)
sender.send(bob, CMD_SIGN)
@ -493,7 +487,6 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit
import f._
val sender = TestProbe()
sender.send(bob, CMD_FAIL_HTLC(1, Right(PermanentChannelFailure)))
sender.expectMsgType[RES_SUCCESS[CMD_FAIL_HTLC]]
val fail = bob2alice.expectMsgType[UpdateFailHtlc]
bob2alice.forward(alice)
sender.send(bob, CMD_SIGN)
@ -519,7 +512,6 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit
import f._
val sender = TestProbe()
sender.send(bob, CMD_FAIL_MALFORMED_HTLC(1, Crypto.sha256(ByteVector.view("should be htlc.onionRoutingPacket".getBytes())), FailureMessageCodecs.BADONION))
sender.expectMsgType[RES_SUCCESS[CMD_FAIL_MALFORMED_HTLC]]
val fail = bob2alice.expectMsgType[UpdateFailMalformedHtlc]
bob2alice.forward(alice)
sender.send(bob, CMD_SIGN)

View File

@ -16,6 +16,7 @@
package fr.acinq.eclair.payment
import akka.actor.ActorRef
import akka.actor.Status.Failure
import akka.testkit.{TestActorRef, TestProbe}
import fr.acinq.bitcoin.{ByteVector32, Crypto}
@ -369,8 +370,8 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
val commands = f.register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]] :: f.register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]] :: Nil
assert(commands.toSet === Set(
Register.Forward(handler, ByteVector32.One, CMD_FAIL_HTLC(0, Right(PaymentTimeout), commit = true)),
Register.Forward(handler, ByteVector32.One, CMD_FAIL_HTLC(1, Right(PaymentTimeout), commit = true))
Register.Forward(ActorRef.noSender, ByteVector32.One, CMD_FAIL_HTLC(0, Right(PaymentTimeout), commit = true)),
Register.Forward(ActorRef.noSender, ByteVector32.One, CMD_FAIL_HTLC(1, Right(PaymentTimeout), commit = true))
))
awaitCond({
f.sender.send(handler, GetPendingPayments)
@ -379,7 +380,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)), Some(PaymentTimeout)))
f.register.expectMsg(Register.Forward(handler, ByteVector32.One, CMD_FAIL_HTLC(42, Right(PaymentTimeout), commit = true)))
f.register.expectMsg(Register.Forward(ActorRef.noSender, ByteVector32.One, CMD_FAIL_HTLC(42, Right(PaymentTimeout), commit = true)))
// The payment should still be pending in DB.
val Some(incomingPayment) = nodeParams.db.payments.getIncomingPayment(pr1.paymentHash)
@ -401,12 +402,12 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
val add3 = add2.copy(id = 43)
f.sender.send(handler, IncomingPacket.FinalPacket(add3, Onion.createMultiPartPayload(add3.amountMsat, 1000 msat, add3.cltvExpiry, pr.paymentSecret.get)))
f.register.expectMsg(Register.Forward(handler, add2.channelId, CMD_FAIL_HTLC(add2.id, Right(IncorrectOrUnknownPaymentDetails(1000 msat, nodeParams.currentBlockHeight)), commit = true)))
f.register.expectMsg(Register.Forward(ActorRef.noSender, add2.channelId, CMD_FAIL_HTLC(add2.id, Right(IncorrectOrUnknownPaymentDetails(1000 msat, nodeParams.currentBlockHeight)), commit = true)))
val cmd1 = f.register.expectMsgType[Register.Forward[CMD_FULFILL_HTLC]]
assert(cmd1.message.id === add1.id)
assert(cmd1.channelId === add1.channelId)
assert(Crypto.sha256(cmd1.message.r) === pr.paymentHash)
f.register.expectMsg(Register.Forward(handler, add3.channelId, CMD_FULFILL_HTLC(add3.id, cmd1.message.r, commit = true)))
f.register.expectMsg(Register.Forward(ActorRef.noSender, add3.channelId, CMD_FULFILL_HTLC(add3.id, cmd1.message.r, commit = true)))
val paymentReceived = f.eventListener.expectMsgType[PaymentReceived]
assert(paymentReceived.copy(parts = paymentReceived.parts.map(_.copy(timestamp = 0))) === PaymentReceived(pr.paymentHash, PartialPayment(800 msat, ByteVector32.One, 0) :: PartialPayment(200 msat, ByteVector32.Zeroes, 0) :: Nil))
@ -420,7 +421,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
// Extraneous HTLCs should be fulfilled.
f.sender.send(handler, MultiPartPaymentFSM.ExtraPaymentReceived(pr.paymentHash, HtlcPart(1000 msat, UpdateAddHtlc(ByteVector32.One, 44, 200 msat, pr.paymentHash, add1.cltvExpiry, add1.onionRoutingPacket)), None))
f.register.expectMsg(Register.Forward(handler, ByteVector32.One, CMD_FULFILL_HTLC(44, cmd1.message.r, commit = true)))
f.register.expectMsg(Register.Forward(ActorRef.noSender, ByteVector32.One, CMD_FULFILL_HTLC(44, cmd1.message.r, commit = true)))
assert(f.eventListener.expectMsgType[PaymentReceived].amount === 200.msat)
val received2 = nodeParams.db.payments.getIncomingPayment(pr.paymentHash)
assert(received2.get.status.asInstanceOf[IncomingPaymentStatus.Received].amount === 1200.msat)
@ -439,7 +440,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
val add1 = UpdateAddHtlc(ByteVector32.One, 0, 800 msat, pr.paymentHash, f.defaultExpiry, TestConstants.emptyOnionPacket)
f.sender.send(handler, IncomingPacket.FinalPacket(add1, Onion.createMultiPartPayload(add1.amountMsat, 1000 msat, add1.cltvExpiry, pr.paymentSecret.get)))
f.register.expectMsg(Register.Forward(handler, ByteVector32.One, CMD_FAIL_HTLC(0, Right(PaymentTimeout), commit = true)))
f.register.expectMsg(Register.Forward(ActorRef.noSender, ByteVector32.One, CMD_FAIL_HTLC(0, Right(PaymentTimeout), commit = true)))
awaitCond({
f.sender.send(handler, GetPendingPayments)
f.sender.expectMsgType[PendingPayments].paymentHashes.isEmpty
@ -454,7 +455,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
assert(cmd1.channelId === add2.channelId)
assert(cmd1.message.id === 2)
assert(Crypto.sha256(cmd1.message.r) === pr.paymentHash)
f.register.expectMsg(Register.Forward(handler, add3.channelId, CMD_FULFILL_HTLC(5, cmd1.message.r, commit = true)))
f.register.expectMsg(Register.Forward(ActorRef.noSender, add3.channelId, CMD_FULFILL_HTLC(5, cmd1.message.r, commit = true)))
val paymentReceived = f.eventListener.expectMsgType[PaymentReceived]
assert(paymentReceived.copy(parts = paymentReceived.parts.map(_.copy(timestamp = 0))) === PaymentReceived(pr.paymentHash, PartialPayment(300 msat, ByteVector32.One, 0) :: PartialPayment(700 msat, ByteVector32.Zeroes, 0) :: Nil))
@ -501,7 +502,7 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
val add = UpdateAddHtlc(ByteVector32.One, 0, amountMsat, paymentHash, defaultExpiry, TestConstants.emptyOnionPacket)
sender.send(normalHandler, IncomingPacket.FinalPacket(add, payload))
f.register.expectMsg(Register.Forward(normalHandler, add.channelId, CMD_FAIL_HTLC(add.id, Right(IncorrectOrUnknownPaymentDetails(42000 msat, nodeParams.currentBlockHeight)), commit = true)))
f.register.expectMsg(Register.Forward(ActorRef.noSender, add.channelId, CMD_FAIL_HTLC(add.id, Right(IncorrectOrUnknownPaymentDetails(42000 msat, nodeParams.currentBlockHeight)), commit = true)))
assert(nodeParams.db.payments.getIncomingPayment(paymentHash) === None)
}
}

View File

@ -413,8 +413,8 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit
val origin_2 = Origin.TrampolineRelayedCold(upstream_2.adds.map(u => (u.channelId, u.id)).toList)
sender.send(relayer, RES_ADD_SETTLED(origin_2, htlc_2_2, HtlcResult.OnChainFulfill(preimage2)))
register.expectMsgAllOf(
Register.Forward(replyTo = postRestartHtlcCleaner, channelId_ab_1, CMD_FULFILL_HTLC(5, preimage2, commit = true)),
Register.Forward(replyTo = postRestartHtlcCleaner, channelId_ab_2, CMD_FULFILL_HTLC(9, preimage2, commit = true))
Register.Forward(replyTo = ActorRef.noSender, channelId_ab_1, CMD_FULFILL_HTLC(5, preimage2, commit = true)),
Register.Forward(replyTo = ActorRef.noSender, channelId_ab_2, CMD_FULFILL_HTLC(9, preimage2, commit = true))
)
// Payment 3 should not be failed: we are still waiting for on-chain confirmation.
@ -448,7 +448,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit
val postRestartHtlcCleaner = sender.expectMsgType[Relayer.ChildActors].postRestartCleaner
sender.send(relayer, buildForwardFulfill(testCase.downstream, testCase.origin, preimage1))
register.expectMsg(Register.Forward(postRestartHtlcCleaner, testCase.origin.originChannelId, CMD_FULFILL_HTLC(testCase.origin.originHtlcId, preimage1, commit = true)))
register.expectMsg(Register.Forward(ActorRef.noSender, testCase.origin.originChannelId, CMD_FULFILL_HTLC(testCase.origin.originHtlcId, preimage1, commit = true)))
eventListener.expectMsgType[ChannelPaymentRelayed]
sender.send(relayer, buildForwardFulfill(testCase.downstream, testCase.origin, preimage1))
@ -471,7 +471,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit
sender.send(relayer, buildForwardFail(testCase.downstream_1_1, testCase.origin_1))
val fails = register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]] :: register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]] :: Nil
assert(fails.toSet === testCase.origin_1.htlcs.map {
case (channelId, htlcId) => Register.Forward(postRestartHtlcCleaner, channelId, CMD_FAIL_HTLC(htlcId, Right(TemporaryNodeFailure), commit = true))
case (channelId, htlcId) => Register.Forward(ActorRef.noSender, channelId, CMD_FAIL_HTLC(htlcId, Right(TemporaryNodeFailure), commit = true))
}.toSet)
sender.send(relayer, buildForwardFail(testCase.downstream_1_1, testCase.origin_1))
@ -483,7 +483,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit
sender.send(relayer, buildForwardFail(testCase.downstream_2_3, testCase.origin_2))
register.expectMsg(testCase.origin_2.htlcs.map {
case (channelId, htlcId) => Register.Forward(postRestartHtlcCleaner, channelId, CMD_FAIL_HTLC(htlcId, Right(TemporaryNodeFailure), commit = true))
case (channelId, htlcId) => Register.Forward(ActorRef.noSender, channelId, CMD_FAIL_HTLC(htlcId, Right(TemporaryNodeFailure), commit = true))
}.head)
register.expectNoMsg(100 millis)
@ -505,7 +505,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit
sender.send(relayer, buildForwardFulfill(testCase.downstream_1_1, testCase.origin_1, preimage1))
val fulfills = register.expectMsgType[Register.Forward[CMD_FULFILL_HTLC]] :: register.expectMsgType[Register.Forward[CMD_FULFILL_HTLC]] :: Nil
assert(fulfills.toSet === testCase.origin_1.htlcs.map {
case (channelId, htlcId) => Register.Forward(postRestartHtlcCleaner, channelId, CMD_FULFILL_HTLC(htlcId, preimage1, commit = true))
case (channelId, htlcId) => Register.Forward(ActorRef.noSender, channelId, CMD_FULFILL_HTLC(htlcId, preimage1, commit = true))
}.toSet)
sender.send(relayer, buildForwardFulfill(testCase.downstream_1_1, testCase.origin_1, preimage1))
@ -514,7 +514,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit
// This payment has 3 downstream HTLCs, but we should fulfill upstream as soon as we receive the preimage.
sender.send(relayer, buildForwardFulfill(testCase.downstream_2_1, testCase.origin_2, preimage2))
register.expectMsg(testCase.origin_2.htlcs.map {
case (channelId, htlcId) => Register.Forward(postRestartHtlcCleaner, channelId, CMD_FULFILL_HTLC(htlcId, preimage2, commit = true))
case (channelId, htlcId) => Register.Forward(ActorRef.noSender, channelId, CMD_FULFILL_HTLC(htlcId, preimage2, commit = true))
}.head)
sender.send(relayer, buildForwardFulfill(testCase.downstream_2_2, testCase.origin_2, preimage2))
@ -538,7 +538,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit
sender.send(relayer, buildForwardFulfill(testCase.downstream_2_2, testCase.origin_2, preimage2))
register.expectMsg(testCase.origin_2.htlcs.map {
case (channelId, htlcId) => Register.Forward(postRestartHtlcCleaner, channelId, CMD_FULFILL_HTLC(htlcId, preimage2, commit = true))
case (channelId, htlcId) => Register.Forward(ActorRef.noSender, channelId, CMD_FULFILL_HTLC(htlcId, preimage2, commit = true))
}.head)
sender.send(relayer, buildForwardFail(testCase.downstream_2_3, testCase.origin_2))

View File

@ -375,7 +375,7 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a
val u_disabled = createLocalUpdate(shortId1, enabled = false)
val downstream_htlc = UpdateAddHtlc(channelId1, 7, outgoingAmount, paymentHash, outgoingExpiry, emptyOnionPacket)
case class TestCase(result: HtlcResult, cmd: channel.Command with HasHtlcId)
case class TestCase(result: HtlcResult, cmd: channel.HtlcSettlementCommand)
val testCases = Seq(
TestCase(HtlcResult.RemoteFail(UpdateFailHtlc(channelId1, downstream_htlc.id, hex"deadbeef")), CMD_FAIL_HTLC(r.add.id, Left(hex"deadbeef"), commit = true)),

View File

@ -27,7 +27,7 @@ import org.scalatest.funsuite.AnyFunSuite
class CommandCodecsSpec extends AnyFunSuite {
test("encode/decode all channel messages") {
val msgs: List[Command with HasHtlcId] =
val msgs: List[HtlcSettlementCommand] =
CMD_FULFILL_HTLC(1573L, randomBytes32) ::
CMD_FAIL_HTLC(42456L, Left(randomBytes(145))) ::
CMD_FAIL_HTLC(253, Right(TemporaryNodeFailure)) ::