1
0
mirror of https://github.com/ACINQ/eclair.git synced 2024-11-19 09:54:02 +01:00

Minor fixes (#163)

* improved router/payment-lifecycle logging
* now periodically publish CurrentFeerate events
* added a reference to `channelId` in `ChannelException`
This commit is contained in:
Pierre-Marie Padiou 2017-09-13 18:35:22 +02:00 committed by GitHub
parent a18fac135d
commit 9e3fbbe5da
11 changed files with 150 additions and 146 deletions

View File

@ -99,6 +99,7 @@ class Setup(datadir: File, overrideDefaults: Config = ConfigFactory.empty(), act
system.scheduler.schedule(0 seconds, 10 minutes)(feeProvider.getFeeratePerKB.map {
case feeratePerKB =>
Globals.feeratePerKw.set(feerateKb2Kw(feeratePerKB))
system.eventStream.publish(CurrentFeerate(Globals.feeratePerKw.get()))
logger.info(s"current feeratePerKw=${Globals.feeratePerKw.get()}")
})

View File

@ -408,7 +408,7 @@ class Channel(val nodeParams: NodeParams, wallet: EclairWallet, remoteNodeId: Pu
when(NORMAL)(handleExceptions {
case Event(c: CMD_ADD_HTLC, d: DATA_NORMAL) if d.localShutdown.isDefined =>
handleCommandError(ClosingInProgress)
handleCommandError(ClosingInProgress(d.channelId))
case Event(c@CMD_ADD_HTLC(_, _, _, _, downstream_opt, do_commit), d: DATA_NORMAL) =>
Try(Commitments.sendAdd(d.commitments, c)) match {
@ -551,22 +551,22 @@ class Channel(val nodeParams: NodeParams, wallet: EclairWallet, remoteNodeId: Pu
case Event(CMD_CLOSE(localScriptPubKey_opt), d: DATA_NORMAL) =>
val localScriptPubKey = localScriptPubKey_opt.getOrElse(d.commitments.localParams.defaultFinalScriptPubKey)
if (d.localShutdown.isDefined)
handleCommandError(ClosingAlreadyInProgress)
handleCommandError(ClosingAlreadyInProgress((d.channelId)))
else if (Commitments.localHasChanges(d.commitments))
// TODO: simplistic behavior, we could also sign-then-close
handleCommandError(CannotCloseWithPendingChanges)
handleCommandError(CannotCloseWithPendingChanges((d.channelId)))
else if (!Closing.isValidFinalScriptPubkey(localScriptPubKey))
handleCommandError(InvalidFinalScript)
handleCommandError(InvalidFinalScript(d.channelId))
else {
val shutdown = Shutdown(d.channelId, localScriptPubKey)
handleCommandSuccess(sender, store(d.copy(localShutdown = Some(shutdown)))) sending shutdown
}
case Event(Shutdown(_, _), d: DATA_NORMAL) if d.commitments.remoteChanges.proposed.size > 0 =>
handleLocalError(CannotCloseWithPendingChanges, d)
handleLocalError(CannotCloseWithPendingChanges(d.channelId), d)
case Event(remoteShutdown@Shutdown(_, remoteScriptPubKey), d: DATA_NORMAL) =>
if (!Closing.isValidFinalScriptPubkey(remoteScriptPubKey)) throw InvalidFinalScript
if (!Closing.isValidFinalScriptPubkey(remoteScriptPubKey)) throw InvalidFinalScript(d.channelId)
Try(d.localShutdown.map(s => (s, d.commitments)).getOrElse {
// first if we have pending changes, we need to commit them
val commitments2 = if (Commitments.localHasChanges(d.commitments)) {
@ -596,7 +596,7 @@ class Channel(val nodeParams: NodeParams, wallet: EclairWallet, remoteNodeId: Pu
}
case Event(CurrentBlockCount(count), d: DATA_NORMAL) if d.commitments.hasTimedoutOutgoingHtlcs(count) =>
handleLocalError(HtlcTimedout, d)
handleLocalError(HtlcTimedout(d.channelId), d)
case Event(CurrentFeerate(feeratePerKw), d: DATA_NORMAL) =>
d.commitments.localParams.isFunder match {
@ -604,7 +604,7 @@ class Channel(val nodeParams: NodeParams, wallet: EclairWallet, remoteNodeId: Pu
self ! CMD_UPDATE_FEE(feeratePerKw, commit = true)
stay
case false if Helpers.isFeeDiffTooHigh(d.commitments.localCommit.spec.feeratePerKw, feeratePerKw, nodeParams.maxFeerateMismatch) =>
handleLocalError(FeerateTooDifferent(localFeeratePerKw = feeratePerKw, remoteFeeratePerKw = d.commitments.localCommit.spec.feeratePerKw), d)
handleLocalError(FeerateTooDifferent(d.channelId, localFeeratePerKw = feeratePerKw, remoteFeeratePerKw = d.commitments.localCommit.spec.feeratePerKw), d)
case _ => stay
}
@ -811,7 +811,7 @@ class Channel(val nodeParams: NodeParams, wallet: EclairWallet, remoteNodeId: Pu
}
case Event(CurrentBlockCount(count), d: DATA_SHUTDOWN) if d.commitments.hasTimedoutOutgoingHtlcs(count) =>
handleLocalError(HtlcTimedout, d)
handleLocalError(HtlcTimedout(d.channelId), d)
case Event(CurrentFeerate(feeratePerKw), d: DATA_SHUTDOWN) =>
d.commitments.localParams.isFunder match {
@ -819,7 +819,7 @@ class Channel(val nodeParams: NodeParams, wallet: EclairWallet, remoteNodeId: Pu
self ! CMD_UPDATE_FEE(feeratePerKw, commit = true)
stay
case false if Helpers.isFeeDiffTooHigh(d.commitments.localCommit.spec.feeratePerKw, feeratePerKw, nodeParams.maxFeerateMismatch) =>
handleLocalError(FeerateTooDifferent(localFeeratePerKw = feeratePerKw, remoteFeeratePerKw = d.commitments.localCommit.spec.feeratePerKw), d)
handleLocalError(FeerateTooDifferent(d.channelId, localFeeratePerKw = feeratePerKw, remoteFeeratePerKw = d.commitments.localCommit.spec.feeratePerKw), d)
case _ => stay
}
@ -829,7 +829,7 @@ class Channel(val nodeParams: NodeParams, wallet: EclairWallet, remoteNodeId: Pu
case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_SHUTDOWN) => handleRemoteSpentOther(tx, d)
case Event(CMD_CLOSE(_), d: DATA_SHUTDOWN) => handleCommandError(ClosingAlreadyInProgress)
case Event(CMD_CLOSE(_), d: DATA_SHUTDOWN) => handleCommandError(ClosingAlreadyInProgress(d.channelId))
case Event(e: Error, d: DATA_SHUTDOWN) => handleRemoteError(e, d)
@ -850,7 +850,7 @@ class Channel(val nodeParams: NodeParams, wallet: EclairWallet, remoteNodeId: Pu
}
case Failure(cause) =>
log.error(cause, "cannot verify their close signature")
throw InvalidCloseSignature
throw InvalidCloseSignature(d.channelId)
}
case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_NEGOTIATING) if tx.txid == Closing.makeClosingTx(d.commitments, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, Satoshi(d.localClosingSigned.feeSatoshis))._1.tx.txid =>
@ -863,7 +863,7 @@ class Channel(val nodeParams: NodeParams, wallet: EclairWallet, remoteNodeId: Pu
case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_NEGOTIATING) => handleRemoteSpentOther(tx, d)
case Event(CMD_CLOSE(_), d: DATA_NEGOTIATING) => handleCommandError(ClosingAlreadyInProgress)
case Event(CMD_CLOSE(_), d: DATA_NEGOTIATING) => handleCommandError(ClosingAlreadyInProgress(d.channelId))
case Event(e: Error, d: DATA_NEGOTIATING) => handleRemoteError(e, d)
@ -934,7 +934,7 @@ class Channel(val nodeParams: NodeParams, wallet: EclairWallet, remoteNodeId: Pu
case Event(WatchEventConfirmed(BITCOIN_PENALTY_DONE, _, _), d: DATA_CLOSING) if d.revokedCommitPublished.size > 0 => goto(CLOSED)
case Event(CMD_CLOSE(_), d: DATA_CLOSING) => handleCommandError(ClosingAlreadyInProgress)
case Event(CMD_CLOSE(_), d: DATA_CLOSING) => handleCommandError(ClosingAlreadyInProgress(d.channelId))
case Event(e: Error, d: DATA_CLOSING) => stay // nothing to do, there is already a spending tx published
@ -967,13 +967,13 @@ class Channel(val nodeParams: NodeParams, wallet: EclairWallet, remoteNodeId: Pu
case Event(c: CMD_ADD_HTLC, d: DATA_NORMAL) =>
log.info(s"rejecting htlc (disconnected)")
relayer ! AddHtlcFailed(c, ChannelUnavailable)
handleCommandError(ChannelUnavailable)
relayer ! AddHtlcFailed(c, ChannelUnavailable(d.channelId))
handleCommandError(ChannelUnavailable(d.channelId))
case Event(CMD_CLOSE(_), d: HasCommitments) => handleLocalError(ForcedLocalCommit("can't do a mutual close while disconnected"), d) replying "ok"
case Event(CMD_CLOSE(_), d: HasCommitments) => handleLocalError(ForcedLocalCommit(d.channelId, "can't do a mutual close while disconnected"), d) replying "ok"
case Event(CurrentBlockCount(count), d: HasCommitments) if d.commitments.hasTimedoutOutgoingHtlcs(count) =>
handleLocalError(HtlcTimedout, d)
handleLocalError(HtlcTimedout(d.channelId), d)
case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx: Transaction), d: HasCommitments) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d)
@ -1040,13 +1040,13 @@ class Channel(val nodeParams: NodeParams, wallet: EclairWallet, remoteNodeId: Pu
case Event(c: CMD_ADD_HTLC, d: DATA_NORMAL) =>
log.info(s"rejecting htlc (syncing)")
relayer ! AddHtlcFailed(c, ChannelUnavailable)
handleCommandError(ChannelUnavailable)
relayer ! AddHtlcFailed(c, ChannelUnavailable(d.channelId))
handleCommandError(ChannelUnavailable(d.channelId))
case Event(CMD_CLOSE(_), d: HasCommitments) => handleLocalError(ForcedLocalCommit("can't do a mutual close while syncing"), d)
case Event(CMD_CLOSE(_), d: HasCommitments) => handleLocalError(ForcedLocalCommit(d.channelId, "can't do a mutual close while syncing"), d)
case Event(CurrentBlockCount(count), d: HasCommitments) if d.commitments.hasTimedoutOutgoingHtlcs(count) =>
handleLocalError(HtlcTimedout, d)
handleLocalError(HtlcTimedout(d.channelId), d)
case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx: Transaction), d: HasCommitments) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d)
@ -1061,7 +1061,7 @@ class Channel(val nodeParams: NodeParams, wallet: EclairWallet, remoteNodeId: Pu
whenUnhandled {
case Event(INPUT_PUBLISH_LOCALCOMMIT, d: HasCommitments) => handleLocalError(ForcedLocalCommit("manual unilateral close"), d)
case Event(INPUT_PUBLISH_LOCALCOMMIT, d: HasCommitments) => handleLocalError(ForcedLocalCommit(d.channelId, "manual unilateral close"), d)
case Event(INPUT_DISCONNECTED, _) => goto(OFFLINE)
@ -1363,7 +1363,7 @@ class Channel(val nodeParams: NodeParams, wallet: EclairWallet, remoteNodeId: Pu
nextPerCommitmentPoint = localNextPerCommitmentPoint
)
forwarder ! revocation
} else throw RevocationSyncError
} else throw RevocationSyncError(d.channelId)
}
// re-sending sig/rev (in the right order)
@ -1391,7 +1391,7 @@ class Channel(val nodeParams: NodeParams, wallet: EclairWallet, remoteNodeId: Pu
case Right(_) if commitments1.remoteCommit.index + 1 == channelReestablish.nextLocalCommitmentNumber =>
// there wasn't any sig in-flight when the disconnection occured
resendRevocation
case _ => throw CommitmentSyncError
case _ => throw CommitmentSyncError(d.channelId)
}
// let's now fail all pending htlc for which we are the final payee

View File

@ -1,39 +1,40 @@
package fr.acinq.eclair.channel
import fr.acinq.bitcoin.BinaryData
import fr.acinq.eclair.UInt64
/**
* Created by PM on 11/04/2017.
*/
class ChannelException(message: String) extends RuntimeException(message)
class ChannelException(channelId: BinaryData, message: String) extends RuntimeException(message)
case object DebugTriggeredException extends ChannelException("debug-mode triggered failure")
case object ClosingInProgress extends ChannelException("cannot send new htlcs, closing in progress")
case object ClosingAlreadyInProgress extends ChannelException("closing already in progress")
case object CannotCloseWithPendingChanges extends ChannelException("cannot close when there are pending changes")
case object ChannelUnavailable extends ChannelException("channel is unavailable (offline or closing)")
case object InvalidFinalScript extends ChannelException("invalid final script")
case object HtlcTimedout extends ChannelException(s"one or more htlcs timed out")
case class FeerateTooDifferent(localFeeratePerKw: Long, remoteFeeratePerKw: Long) extends ChannelException(s"local/remote feerates are too different: remoteFeeratePerKw=$remoteFeeratePerKw localFeeratePerKw=$localFeeratePerKw")
case object InvalidCloseSignature extends ChannelException("cannot verify their close signature")
case object InvalidCommitmentSignature extends ChannelException("invalid commitment signature")
case class ForcedLocalCommit(reason: String) extends ChannelException(s"forced local commit: reason")
case class UnexpectedHtlcId(expected: Long, actual: Long) extends ChannelException(s"unexpected htlc id: expected=$expected actual=$actual")
case object InvalidPaymentHash extends ChannelException("invalid payment hash")
case class ExpiryTooSmall(minimum: Long, actual: Long, blockCount: Long) extends ChannelException(s"expiry too small: required=$minimum actual=$actual blockCount=$blockCount")
case class ExpiryCannotBeInThePast(expiry: Long, blockCount: Long) extends ChannelException(s"expiry can't be in the past: expiry=$expiry blockCount=$blockCount")
case class HtlcValueTooSmall(minimum: Long, actual: Long) extends ChannelException(s"htlc value too small: mininmum=$minimum actual=$actual")
case class HtlcValueTooHighInFlight(maximum: UInt64, actual: UInt64) extends ChannelException(s"in-flight htlcs hold too much value: maximum=$maximum actual=$actual")
case class TooManyAcceptedHtlcs(maximum: Long) extends ChannelException(s"too many accepted htlcs: maximum=$maximum")
case class InsufficientFunds(amountMsat: Long, missingSatoshis: Long, reserveSatoshis: Long, feesSatoshis: Long) extends ChannelException(s"insufficient funds: missingSatoshis=$missingSatoshis reserveSatoshis=$reserveSatoshis fees=$feesSatoshis")
case class InvalidHtlcPreimage(id: Long) extends ChannelException(s"invalid htlc preimage for htlc id=$id")
case class UnknownHtlcId(id: Long) extends ChannelException(s"unknown htlc id=$id")
case object FundeeCannotSendUpdateFee extends ChannelException(s"only the funder should send update_fee messages")
case class CannotAffordFees(missingSatoshis: Long, reserveSatoshis: Long, feesSatoshis: Long) extends ChannelException(s"can't pay the fee: missingSatoshis=$missingSatoshis reserveSatoshis=$reserveSatoshis feesSatoshis=$feesSatoshis")
case object CannotSignWithoutChanges extends ChannelException("cannot sign when there are no changes")
case object CannotSignBeforeRevocation extends ChannelException("cannot sign until next revocation hash is received")
case object UnexpectedRevocation extends ChannelException("received unexpected RevokeAndAck message")
case object InvalidRevocation extends ChannelException("invalid revocation")
case object CommitmentSyncError extends ChannelException("commitment sync error")
case object RevocationSyncError extends ChannelException("revocation sync error")
case class DebugTriggeredException (channelId: BinaryData) extends ChannelException(channelId, "debug-mode triggered failure")
case class ClosingInProgress (channelId: BinaryData) extends ChannelException(channelId, "cannot send new htlcs, closing in progress")
case class ClosingAlreadyInProgress (channelId: BinaryData) extends ChannelException(channelId, "closing already in progress")
case class CannotCloseWithPendingChanges(channelId: BinaryData) extends ChannelException(channelId, "cannot close when there are pending changes")
case class ChannelUnavailable (channelId: BinaryData) extends ChannelException(channelId, "channel is unavailable (offline or closing)")
case class InvalidFinalScript (channelId: BinaryData) extends ChannelException(channelId, "invalid final script")
case class HtlcTimedout (channelId: BinaryData) extends ChannelException(channelId, s"one or more htlcs timed out")
case class FeerateTooDifferent (channelId: BinaryData, localFeeratePerKw: Long, remoteFeeratePerKw: Long) extends ChannelException(channelId, s"local/remote feerates are too different: remoteFeeratePerKw=$remoteFeeratePerKw localFeeratePerKw=$localFeeratePerKw")
case class InvalidCloseSignature (channelId: BinaryData) extends ChannelException(channelId, "cannot verify their close signature")
case class InvalidCommitmentSignature (channelId: BinaryData) extends ChannelException(channelId, "invalid commitment signature")
case class ForcedLocalCommit (channelId: BinaryData, reason: String) extends ChannelException(channelId, s"forced local commit: reason")
case class UnexpectedHtlcId (channelId: BinaryData, expected: Long, actual: Long) extends ChannelException(channelId, s"unexpected htlc id: expected=$expected actual=$actual")
case class InvalidPaymentHash (channelId: BinaryData) extends ChannelException(channelId, "invalid payment hash")
case class ExpiryTooSmall (channelId: BinaryData, minimum: Long, actual: Long, blockCount: Long) extends ChannelException(channelId, s"expiry too small: required=$minimum actual=$actual blockCount=$blockCount")
case class ExpiryCannotBeInThePast (channelId: BinaryData, expiry: Long, blockCount: Long) extends ChannelException(channelId, s"expiry can't be in the past: expiry=$expiry blockCount=$blockCount")
case class HtlcValueTooSmall (channelId: BinaryData, minimum: Long, actual: Long) extends ChannelException(channelId, s"htlc value too small: mininmum=$minimum actual=$actual")
case class HtlcValueTooHighInFlight (channelId: BinaryData, maximum: UInt64, actual: UInt64) extends ChannelException(channelId, s"in-flight htlcs hold too much value: maximum=$maximum actual=$actual")
case class TooManyAcceptedHtlcs (channelId: BinaryData, maximum: Long) extends ChannelException(channelId, s"too many accepted htlcs: maximum=$maximum")
case class InsufficientFunds (channelId: BinaryData, amountMsat: Long, missingSatoshis: Long, reserveSatoshis: Long, feesSatoshis: Long) extends ChannelException(channelId, s"insufficient funds: missingSatoshis=$missingSatoshis reserveSatoshis=$reserveSatoshis fees=$feesSatoshis")
case class InvalidHtlcPreimage (channelId: BinaryData, id: Long) extends ChannelException(channelId, s"invalid htlc preimage for htlc id=$id")
case class UnknownHtlcId (channelId: BinaryData, id: Long) extends ChannelException(channelId, s"unknown htlc id=$id")
case class FundeeCannotSendUpdateFee (channelId: BinaryData) extends ChannelException(channelId, s"only the funder should send update_fee messages")
case class CannotAffordFees (channelId: BinaryData, missingSatoshis: Long, reserveSatoshis: Long, feesSatoshis: Long) extends ChannelException(channelId, s"can't pay the fee: missingSatoshis=$missingSatoshis reserveSatoshis=$reserveSatoshis feesSatoshis=$feesSatoshis")
case class CannotSignWithoutChanges (channelId: BinaryData) extends ChannelException(channelId, "cannot sign when there are no changes")
case class CannotSignBeforeRevocation (channelId: BinaryData) extends ChannelException(channelId, "cannot sign until next revocation hash is received")
case class UnexpectedRevocation (channelId: BinaryData) extends ChannelException(channelId, "received unexpected RevokeAndAck message")
case class InvalidRevocation (channelId: BinaryData) extends ChannelException(channelId, "invalid revocation")
case class CommitmentSyncError (channelId: BinaryData) extends ChannelException(channelId, "commitment sync error")
case class RevocationSyncError (channelId: BinaryData) extends ChannelException(channelId, "revocation sync error")

View File

@ -75,16 +75,16 @@ object Commitments extends Logging {
def sendAdd(commitments: Commitments, cmd: CMD_ADD_HTLC): Either[ChannelException, (Commitments, UpdateAddHtlc)] = {
if (cmd.paymentHash.size != 32) {
return Left(InvalidPaymentHash)
return Left(InvalidPaymentHash(commitments.channelId))
}
val blockCount = Globals.blockCount.get()
if (cmd.expiry <= blockCount) {
return Left(ExpiryCannotBeInThePast(cmd.expiry, blockCount))
return Left(ExpiryCannotBeInThePast(commitments.channelId, cmd.expiry, blockCount))
}
if (cmd.amountMsat < commitments.remoteParams.htlcMinimumMsat) {
return Left(HtlcValueTooSmall(minimum = commitments.remoteParams.htlcMinimumMsat, actual = cmd.amountMsat))
return Left(HtlcValueTooSmall(commitments.channelId, minimum = commitments.remoteParams.htlcMinimumMsat, actual = cmd.amountMsat))
}
// let's compute the current commitment *as seen by them* with this change taken into account
@ -95,13 +95,13 @@ object Commitments extends Logging {
val htlcValueInFlight = UInt64(reduced.htlcs.map(_.add.amountMsat).sum)
if (htlcValueInFlight > commitments1.remoteParams.maxHtlcValueInFlightMsat) {
// TODO: this should be a specific UPDATE error
return Left(HtlcValueTooHighInFlight(maximum = commitments1.remoteParams.maxHtlcValueInFlightMsat, actual = htlcValueInFlight))
return Left(HtlcValueTooHighInFlight(commitments.channelId, maximum = commitments1.remoteParams.maxHtlcValueInFlightMsat, actual = htlcValueInFlight))
}
// the HTLC we are about to create is outgoing, but from their point of view it is incoming
val acceptedHtlcs = reduced.htlcs.count(_.direction == IN)
if (acceptedHtlcs > commitments1.remoteParams.maxAcceptedHtlcs) {
return Left(TooManyAcceptedHtlcs(maximum = commitments1.remoteParams.maxAcceptedHtlcs))
return Left(TooManyAcceptedHtlcs(commitments.channelId, maximum = commitments1.remoteParams.maxAcceptedHtlcs))
}
// a node cannot spend pending incoming htlcs, and need to keep funds above the reserve required by the counterparty, after paying the fee
@ -109,7 +109,7 @@ object Commitments extends Logging {
val fees = if (commitments1.localParams.isFunder) Transactions.commitTxFee(Satoshi(commitments1.remoteParams.dustLimitSatoshis), reduced).amount else 0
val missing = reduced.toRemoteMsat / 1000 - commitments1.remoteParams.channelReserveSatoshis - fees
if (missing < 0) {
return Left(InsufficientFunds(amountMsat = cmd.amountMsat, missingSatoshis = -1 * missing, reserveSatoshis = commitments1.remoteParams.channelReserveSatoshis, feesSatoshis = fees))
return Left(InsufficientFunds(commitments.channelId, amountMsat = cmd.amountMsat, missingSatoshis = -1 * missing, reserveSatoshis = commitments1.remoteParams.channelReserveSatoshis, feesSatoshis = fees))
}
Right(commitments1, add)
@ -117,22 +117,22 @@ object Commitments extends Logging {
def receiveAdd(commitments: Commitments, add: UpdateAddHtlc): Commitments = {
if (add.id != commitments.remoteNextHtlcId) {
throw UnexpectedHtlcId(expected = commitments.remoteNextHtlcId, actual = add.id)
throw UnexpectedHtlcId(commitments.channelId, expected = commitments.remoteNextHtlcId, actual = add.id)
}
if (add.paymentHash.size != 32) {
throw InvalidPaymentHash
throw InvalidPaymentHash(commitments.channelId)
}
val blockCount = Globals.blockCount.get()
// we need a reasonable amount of time to pull the funds before the sender can get refunded
val minExpiry = blockCount + 3
if (add.expiry < minExpiry) {
throw ExpiryTooSmall(minimum = minExpiry, actual = add.expiry, blockCount = blockCount)
throw ExpiryTooSmall(commitments.channelId, minimum = minExpiry, actual = add.expiry, blockCount = blockCount)
}
if (add.amountMsat < commitments.localParams.htlcMinimumMsat) {
throw HtlcValueTooSmall(minimum = commitments.localParams.htlcMinimumMsat, actual = add.amountMsat)
throw HtlcValueTooSmall(commitments.channelId, minimum = commitments.localParams.htlcMinimumMsat, actual = add.amountMsat)
}
// let's compute the current commitment *as seen by us* including this change
@ -141,19 +141,19 @@ object Commitments extends Logging {
val htlcValueInFlight = UInt64(reduced.htlcs.map(_.add.amountMsat).sum)
if (htlcValueInFlight > commitments1.localParams.maxHtlcValueInFlightMsat) {
throw HtlcValueTooHighInFlight(maximum = commitments1.localParams.maxHtlcValueInFlightMsat, actual = htlcValueInFlight)
throw HtlcValueTooHighInFlight(commitments.channelId, maximum = commitments1.localParams.maxHtlcValueInFlightMsat, actual = htlcValueInFlight)
}
val acceptedHtlcs = reduced.htlcs.count(_.direction == IN)
if (acceptedHtlcs > commitments1.localParams.maxAcceptedHtlcs) {
throw TooManyAcceptedHtlcs(maximum = commitments1.localParams.maxAcceptedHtlcs)
throw TooManyAcceptedHtlcs(commitments.channelId, maximum = commitments1.localParams.maxAcceptedHtlcs)
}
// a node cannot spend pending incoming htlcs, and need to keep funds above the reserve required by the counterparty, after paying the fee
val fees = if (commitments1.localParams.isFunder) 0 else Transactions.commitTxFee(Satoshi(commitments1.localParams.dustLimitSatoshis), reduced).amount
val missing = reduced.toRemoteMsat / 1000 - commitments1.localParams.channelReserveSatoshis - fees
if (missing < 0) {
throw InsufficientFunds(amountMsat = add.amountMsat, missingSatoshis = -1 * missing, reserveSatoshis = commitments1.localParams.channelReserveSatoshis, feesSatoshis = fees)
throw InsufficientFunds(commitments.channelId, amountMsat = add.amountMsat, missingSatoshis = -1 * missing, reserveSatoshis = commitments1.localParams.channelReserveSatoshis, feesSatoshis = fees)
}
commitments1
@ -178,20 +178,20 @@ object Commitments extends Logging {
case _ => false
} =>
// we have already sent a fail/fulfill for this htlc
throw UnknownHtlcId(cmd.id)
throw UnknownHtlcId(commitments.channelId, cmd.id)
case Some(htlc) if htlc.paymentHash == sha256(cmd.r) =>
val fulfill = UpdateFulfillHtlc(commitments.channelId, cmd.id, cmd.r)
val commitments1 = addLocalProposal(commitments, fulfill)
(commitments1, fulfill)
case Some(htlc) => throw InvalidHtlcPreimage(cmd.id)
case None => throw UnknownHtlcId(cmd.id)
case Some(htlc) => throw InvalidHtlcPreimage(commitments.channelId, cmd.id)
case None => throw UnknownHtlcId(commitments.channelId, cmd.id)
}
def receiveFulfill(commitments: Commitments, fulfill: UpdateFulfillHtlc): Either[Commitments, Commitments] =
getHtlcCrossSigned(commitments, OUT, fulfill.id) match {
case Some(htlc) if htlc.paymentHash == sha256(fulfill.paymentPreimage) => Right(addRemoteProposal(commitments, fulfill))
case Some(htlc) => throw InvalidHtlcPreimage(fulfill.id)
case None => throw UnknownHtlcId(fulfill.id)
case Some(htlc) => throw InvalidHtlcPreimage(commitments.channelId, fulfill.id)
case None => throw UnknownHtlcId(commitments.channelId, fulfill.id)
}
def sendFail(commitments: Commitments, cmd: CMD_FAIL_HTLC, nodeSecret: PrivateKey): (Commitments, UpdateFailHtlc) =
@ -203,7 +203,7 @@ object Commitments extends Logging {
case _ => false
} =>
// we have already sent a fail/fulfill for this htlc
throw UnknownHtlcId(cmd.id)
throw UnknownHtlcId(commitments.channelId, cmd.id)
case Some(htlc) =>
// we need the shared secret to build the error packet
val sharedSecret = Sphinx.parsePacket(nodeSecret, htlc.paymentHash, htlc.onionRoutingPacket).sharedSecret
@ -214,7 +214,7 @@ object Commitments extends Logging {
val fail = UpdateFailHtlc(commitments.channelId, cmd.id, reason)
val commitments1 = addLocalProposal(commitments, fail)
(commitments1, fail)
case None => throw UnknownHtlcId(cmd.id)
case None => throw UnknownHtlcId(commitments.channelId, cmd.id)
}
def sendFailMalformed(commitments: Commitments, cmd: CMD_FAIL_MALFORMED_HTLC): (Commitments, UpdateFailMalformedHtlc) =
@ -226,29 +226,29 @@ object Commitments extends Logging {
case _ => false
} =>
// we have already sent a fail/fulfill for this htlc
throw UnknownHtlcId(cmd.id)
throw UnknownHtlcId(commitments.channelId, cmd.id)
case Some(htlc) =>
val fail = UpdateFailMalformedHtlc(commitments.channelId, cmd.id, cmd.onionHash, cmd.failureCode)
val commitments1 = addLocalProposal(commitments, fail)
(commitments1, fail)
case None => throw UnknownHtlcId(cmd.id)
case None => throw UnknownHtlcId(commitments.channelId, cmd.id)
}
def receiveFail(commitments: Commitments, fail: UpdateFailHtlc): Either[Commitments, Commitments] =
getHtlcCrossSigned(commitments, OUT, fail.id) match {
case Some(htlc) => Right(addRemoteProposal(commitments, fail))
case None => throw UnknownHtlcId(fail.id)
case None => throw UnknownHtlcId(commitments.channelId, fail.id)
}
def receiveFailMalformed(commitments: Commitments, fail: UpdateFailMalformedHtlc): Either[Commitments, Commitments] =
getHtlcCrossSigned(commitments, OUT, fail.id) match {
case Some(htlc) => Right(addRemoteProposal(commitments, fail))
case None => throw UnknownHtlcId(fail.id)
case None => throw UnknownHtlcId(commitments.channelId, fail.id)
}
def sendFee(commitments: Commitments, cmd: CMD_UPDATE_FEE): (Commitments, UpdateFee) = {
if (!commitments.localParams.isFunder) {
throw FundeeCannotSendUpdateFee
throw FundeeCannotSendUpdateFee(commitments.channelId)
}
// let's compute the current commitment *as seen by them* with this change taken into account
val fee = UpdateFee(commitments.channelId, cmd.feeratePerKw)
@ -260,7 +260,7 @@ object Commitments extends Logging {
val fees = Transactions.commitTxFee(Satoshi(commitments1.remoteParams.dustLimitSatoshis), reduced).amount
val missing = reduced.toRemoteMsat / 1000 - commitments1.remoteParams.channelReserveSatoshis - fees
if (missing < 0) {
throw CannotAffordFees(missingSatoshis = -1 * missing, reserveSatoshis = commitments1.localParams.channelReserveSatoshis, feesSatoshis = fees)
throw CannotAffordFees(commitments.channelId, missingSatoshis = -1 * missing, reserveSatoshis = commitments1.localParams.channelReserveSatoshis, feesSatoshis = fees)
}
(commitments1, fee)
@ -268,12 +268,12 @@ object Commitments extends Logging {
def receiveFee(commitments: Commitments, fee: UpdateFee, maxFeerateMismatch: Double): Commitments = {
if (commitments.localParams.isFunder) {
throw FundeeCannotSendUpdateFee
throw FundeeCannotSendUpdateFee(commitments.channelId)
}
val localFeeratePerKw = Globals.feeratePerKw.get()
if (Helpers.isFeeDiffTooHigh(fee.feeratePerKw, localFeeratePerKw, maxFeerateMismatch)) {
throw FeerateTooDifferent(localFeeratePerKw = localFeeratePerKw, remoteFeeratePerKw = fee.feeratePerKw)
throw FeerateTooDifferent(commitments.channelId, localFeeratePerKw = localFeeratePerKw, remoteFeeratePerKw = fee.feeratePerKw)
}
// NB: we check that the funder can afford this new fee even if spec allows to do it at next signature
@ -289,7 +289,7 @@ object Commitments extends Logging {
val fees = Transactions.commitTxFee(Satoshi(commitments1.remoteParams.dustLimitSatoshis), reduced).amount
val missing = reduced.toRemoteMsat / 1000 - commitments1.localParams.channelReserveSatoshis - fees
if (missing < 0) {
throw CannotAffordFees(missingSatoshis = -1 * missing, reserveSatoshis = commitments1.localParams.channelReserveSatoshis, feesSatoshis = fees)
throw CannotAffordFees(commitments.channelId, missingSatoshis = -1 * missing, reserveSatoshis = commitments1.localParams.channelReserveSatoshis, feesSatoshis = fees)
}
commitments1
@ -307,7 +307,7 @@ object Commitments extends Logging {
import commitments._
commitments.remoteNextCommitInfo match {
case Right(_) if !localHasChanges(commitments) =>
throw CannotSignWithoutChanges
throw CannotSignWithoutChanges(commitments.channelId)
case Right(remoteNextPerCommitmentPoint) =>
// remote commitment will includes all local changes + remote acked changes
val spec = CommitmentSpec.reduce(remoteCommit.spec, remoteChanges.acked, localChanges.proposed)
@ -331,7 +331,7 @@ object Commitments extends Logging {
remoteChanges = remoteChanges.copy(acked = Nil, signed = remoteChanges.acked))
(commitments1, commitSig)
case Left(_) =>
throw CannotSignBeforeRevocation
throw CannotSignBeforeRevocation(commitments.channelId)
}
}
@ -347,7 +347,7 @@ object Commitments extends Logging {
// and will increment our index
if (!remoteHasChanges(commitments))
throw CannotSignWithoutChanges
throw CannotSignWithoutChanges(commitments.channelId)
// check that their signature is valid
// signatures are now optional in the commit message, and will be sent only if the other party is actually
@ -363,7 +363,7 @@ object Commitments extends Logging {
// no need to compute htlc sigs if commit sig doesn't check out
val signedCommitTx = Transactions.addSigs(localCommitTx, localParams.fundingPrivKey.publicKey, remoteParams.fundingPubKey, sig, commit.signature)
if (Transactions.checkSpendable(signedCommitTx).isFailure) {
throw InvalidCommitmentSignature
throw InvalidCommitmentSignature(commitments.channelId)
}
val sortedHtlcTxs: Seq[TransactionWithInputInfo] = (htlcTimeoutTxs ++ htlcSuccessTxs).sortBy(_.input.outPoint.index)
@ -410,7 +410,7 @@ object Commitments extends Logging {
// we receive a revocation because we just sent them a sig for their next commit tx
remoteNextCommitInfo match {
case Left(_) if revocation.perCommitmentSecret.toPoint != remoteCommit.remotePerCommitmentPoint =>
throw InvalidRevocation
throw InvalidRevocation(commitments.channelId)
case Left(WaitingForRevocation(theirNextCommit, _, _, _)) =>
val commitments1 = commitments.copy(
localChanges = localChanges.copy(signed = Nil, acked = localChanges.acked ++ localChanges.signed),
@ -421,7 +421,7 @@ object Commitments extends Logging {
commitments1
case Right(_) =>
throw UnexpectedRevocation
throw UnexpectedRevocation(commitments.channelId)
}
}

View File

@ -52,11 +52,11 @@ class PaymentLifecycle(sourceNodeId: PublicKey, router: ActorRef, register: Acto
when(WAITING_FOR_ROUTE) {
case Event(RouteResponse(hops, ignoreNodes, ignoreChannels), WaitingForRoute(s, c, failures)) =>
log.info(s"route found: attempt=${failures.size + 1}/${c.maxAttempts} route=${hops.map(_.nextNodeId).mkString("->")}")
log.info(s"route found: attempt=${failures.size + 1}/${c.maxAttempts} route=${hops.map(_.nextNodeId).mkString("->")} channels=${hops.map(_.lastUpdate.shortChannelId.toHexString).mkString("->")}")
val firstHop = hops.head
val finalExpiry = Globals.blockCount.get().toInt + defaultHtlcExpiry
val (cmd, sharedSecrets) = buildCommand(c.amountMsat, finalExpiry, c.paymentHash, hops)
// TODO: HACK!!!! see Router.scala
// TODO: HACK!!!! see Router.scala (we actually store the first node id in the sig)
if (firstHop.lastUpdate.signature.size == 32) {
register ! Register.Forward(firstHop.lastUpdate.signature, cmd)
} else {

View File

@ -304,7 +304,7 @@ class Router(nodeParams: NodeParams, watcher: ActorRef) extends FSM[State, Data]
val updates2 = updates1.filterKeys(!d.excludedChannels.contains(_))
// we also filter out excluded channels
val updates3 = filterUpdates(updates2, ignoreNodes, ignoreChannels)
log.info(s"finding a route $start->$end with ignoreNodes=${ignoreNodes.map(_.toBin).mkString(",")} ignoreChannels=${ignoreChannels.mkString(",")}")
log.info(s"finding a route $start->$end with ignoreNodes=${ignoreNodes.map(_.toBin).mkString(",")} ignoreChannels=${ignoreChannels.map(_.toHexString).mkString(",")}")
findRoute(start, end, updates3).map(r => RouteResponse(r, ignoreNodes, ignoreChannels)) pipeTo sender
stay
}

View File

@ -142,4 +142,6 @@ trait StateTestsHelperMethods extends TestKitBase {
}
def channelId(a: TestFSMRef[State, Data, Channel]) = Helpers.getChannelId(a.stateData)
}

View File

@ -95,8 +95,8 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
val sender = TestProbe()
val add = CMD_ADD_HTLC(500000000, "11" * 42, expiry = 400144)
sender.send(alice, add)
sender.expectMsg(Failure(InvalidPaymentHash))
relayer.expectMsg(AddHtlcFailed(add, InvalidPaymentHash))
sender.expectMsg(Failure(InvalidPaymentHash(channelId(alice))))
relayer.expectMsg(AddHtlcFailed(add, InvalidPaymentHash(channelId(alice))))
alice2bob.expectNoMsg(200 millis)
}
}
@ -106,7 +106,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
val sender = TestProbe()
val add = CMD_ADD_HTLC(500000000, "11" * 32, expiry = 300000)
sender.send(alice, add)
val error = ExpiryCannotBeInThePast(300000, 400000)
val error = ExpiryCannotBeInThePast(channelId(alice), 300000, 400000)
sender.expectMsg(Failure(error))
relayer.expectMsg(AddHtlcFailed(add, error))
alice2bob.expectNoMsg(200 millis)
@ -118,7 +118,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
val sender = TestProbe()
val add = CMD_ADD_HTLC(50, "11" * 32, 400144)
sender.send(alice, add)
val error = HtlcValueTooSmall(1000, 50)
val error = HtlcValueTooSmall(channelId(alice), 1000, 50)
sender.expectMsg(Failure(error))
relayer.expectMsg(AddHtlcFailed(add, error))
alice2bob.expectNoMsg(200 millis)
@ -130,7 +130,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
val sender = TestProbe()
val add = CMD_ADD_HTLC(Int.MaxValue, "11" * 32, 400144)
sender.send(alice, add)
val error = InsufficientFunds(amountMsat = Int.MaxValue, missingSatoshis = 1376443, reserveSatoshis = 20000, feesSatoshis = 8960)
val error = InsufficientFunds(channelId(alice), amountMsat = Int.MaxValue, missingSatoshis = 1376443, reserveSatoshis = 20000, feesSatoshis = 8960)
sender.expectMsg(Failure(error))
relayer.expectMsg(AddHtlcFailed(add, error))
alice2bob.expectNoMsg(200 millis)
@ -154,7 +154,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
alice2bob.expectMsgType[UpdateAddHtlc]
val add = CMD_ADD_HTLC(1000000, "44" * 32, 400144)
sender.send(alice, add)
val error = InsufficientFunds(amountMsat = 1000000, missingSatoshis = 1000, reserveSatoshis = 20000, feesSatoshis = 12400)
val error = InsufficientFunds(channelId(alice), amountMsat = 1000000, missingSatoshis = 1000, reserveSatoshis = 20000, feesSatoshis = 12400)
sender.expectMsg(Failure(error))
relayer.expectMsg(AddHtlcFailed(add, error))
alice2bob.expectNoMsg(200 millis)
@ -174,7 +174,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
alice2bob.expectMsgType[UpdateAddHtlc]
val add = CMD_ADD_HTLC(500000000, "33" * 32, 400144)
sender.send(alice, add)
val error = InsufficientFunds(amountMsat = 500000000, missingSatoshis = 332400, reserveSatoshis = 20000, feesSatoshis = 12400)
val error = InsufficientFunds(channelId(alice), amountMsat = 500000000, missingSatoshis = 332400, reserveSatoshis = 20000, feesSatoshis = 12400)
sender.expectMsg(Failure(error))
relayer.expectMsg(AddHtlcFailed(add, error))
alice2bob.expectNoMsg(200 millis)
@ -186,7 +186,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
val sender = TestProbe()
val add = CMD_ADD_HTLC(151000000, "11" * 32, 400144)
sender.send(bob, add)
val error = HtlcValueTooHighInFlight(maximum = 150000000, actual = 151000000)
val error = HtlcValueTooHighInFlight(channelId(bob), maximum = 150000000, actual = 151000000)
sender.expectMsg(Failure(error))
relayer.expectMsg(AddHtlcFailed(add, error))
bob2alice.expectNoMsg(200 millis)
@ -205,7 +205,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
}
val add = CMD_ADD_HTLC(10000000, "33" * 32, 400144)
sender.send(alice, add)
val error = TooManyAcceptedHtlcs(maximum = 30)
val error = TooManyAcceptedHtlcs(channelId(alice), maximum = 30)
sender.expectMsg(Failure(error))
relayer.expectMsg(AddHtlcFailed(add, error))
alice2bob.expectNoMsg(200 millis)
@ -222,7 +222,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
// actual test starts here
sender.send(alice, CMD_ADD_HTLC(300000000, "11" * 32, 400144))
sender.expectMsg(Failure(ClosingInProgress))
sender.expectMsg(Failure(ClosingInProgress(channelId(alice))))
}
}
@ -245,7 +245,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
bob ! htlc.copy(id = 3)
bob ! htlc.copy(id = 42)
val error = bob2alice.expectMsgType[Error]
assert(new String(error.data) === UnexpectedHtlcId(expected = 4, actual = 42).getMessage)
assert(new String(error.data) === UnexpectedHtlcId(channelId(bob), expected = 4, actual = 42).getMessage)
awaitCond(bob.stateName == CLOSING)
bob2blockchain.expectMsg(PublishAsap(tx))
bob2blockchain.expectMsgType[WatchConfirmed]
@ -258,7 +258,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
val htlc = UpdateAddHtlc("00" * 32, 0, 150000, "11" * 42, 400144, defaultOnion)
alice2bob.forward(bob, htlc)
val error = bob2alice.expectMsgType[Error]
assert(new String(error.data) === InvalidPaymentHash.getMessage)
assert(new String(error.data) === InvalidPaymentHash(channelId(bob)).getMessage)
awaitCond(bob.stateName == CLOSING)
bob2blockchain.expectMsg(PublishAsap(tx))
bob2blockchain.expectMsgType[WatchConfirmed]
@ -271,7 +271,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
val htlc = UpdateAddHtlc("00" * 32, 0, 150000, BinaryData("42" * 32), expiry = 1, defaultOnion)
alice2bob.forward(bob, htlc)
val error = bob2alice.expectMsgType[Error]
assert(new String(error.data) === ExpiryTooSmall(minimum = 400003, actual = 1, blockCount = 400000).getMessage)
assert(new String(error.data) === ExpiryTooSmall(channelId(bob), minimum = 400003, actual = 1, blockCount = 400000).getMessage)
awaitCond(bob.stateName == CLOSING)
bob2blockchain.expectMsg(PublishAsap(tx))
bob2blockchain.expectMsgType[WatchConfirmed]
@ -284,7 +284,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
val htlc = UpdateAddHtlc("00" * 32, 0, 150, BinaryData("42" * 32), expiry = 400144, defaultOnion)
alice2bob.forward(bob, htlc)
val error = bob2alice.expectMsgType[Error]
assert(new String(error.data) === HtlcValueTooSmall(minimum = 1000, actual = 150).getMessage)
assert(new String(error.data) === HtlcValueTooSmall(channelId(bob), minimum = 1000, actual = 150).getMessage)
awaitCond(bob.stateName == CLOSING)
bob2blockchain.expectMsg(PublishAsap(tx))
bob2blockchain.expectMsgType[WatchConfirmed]
@ -297,7 +297,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
val htlc = UpdateAddHtlc("00" * 32, 0, Long.MaxValue, BinaryData("42" * 32), 400144, defaultOnion)
alice2bob.forward(bob, htlc)
val error = bob2alice.expectMsgType[Error]
assert(new String(error.data) === InsufficientFunds(amountMsat = Long.MaxValue, missingSatoshis = 9223372036083735L, reserveSatoshis = 20000, feesSatoshis = 8960).getMessage)
assert(new String(error.data) === InsufficientFunds(channelId(bob), amountMsat = Long.MaxValue, missingSatoshis = 9223372036083735L, reserveSatoshis = 20000, feesSatoshis = 8960).getMessage)
awaitCond(bob.stateName == CLOSING)
bob2blockchain.expectMsg(PublishAsap(tx))
bob2blockchain.expectMsgType[WatchConfirmed]
@ -312,7 +312,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
alice2bob.forward(bob, UpdateAddHtlc("00" * 32, 2, 167600000, "33" * 32, 400144, defaultOnion))
alice2bob.forward(bob, UpdateAddHtlc("00" * 32, 3, 10000000, "44" * 32, 400144, defaultOnion))
val error = bob2alice.expectMsgType[Error]
assert(new String(error.data) === InsufficientFunds(amountMsat = 10000000, missingSatoshis = 11720, reserveSatoshis = 20000, feesSatoshis = 14120).getMessage)
assert(new String(error.data) === InsufficientFunds(channelId(bob), amountMsat = 10000000, missingSatoshis = 11720, reserveSatoshis = 20000, feesSatoshis = 14120).getMessage)
awaitCond(bob.stateName == CLOSING)
bob2blockchain.expectMsg(PublishAsap(tx))
bob2blockchain.expectMsgType[WatchConfirmed]
@ -326,7 +326,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
alice2bob.forward(bob, UpdateAddHtlc("00" * 32, 1, 300000000, "22" * 32, 400144, defaultOnion))
alice2bob.forward(bob, UpdateAddHtlc("00" * 32, 2, 500000000, "33" * 32, 400144, defaultOnion))
val error = bob2alice.expectMsgType[Error]
assert(new String(error.data) === InsufficientFunds(amountMsat = 500000000, missingSatoshis = 332400, reserveSatoshis = 20000, feesSatoshis = 12400).getMessage)
assert(new String(error.data) === InsufficientFunds(channelId(bob), amountMsat = 500000000, missingSatoshis = 332400, reserveSatoshis = 20000, feesSatoshis = 12400).getMessage)
awaitCond(bob.stateName == CLOSING)
bob2blockchain.expectMsg(PublishAsap(tx))
bob2blockchain.expectMsgType[WatchConfirmed]
@ -338,7 +338,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
val tx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx
alice2bob.forward(alice, UpdateAddHtlc("00" * 32, 0, 151000000, "11" * 32, 400144, defaultOnion))
val error = alice2bob.expectMsgType[Error]
assert(new String(error.data) === HtlcValueTooHighInFlight(maximum = 150000000, actual = 151000000).getMessage)
assert(new String(error.data) === HtlcValueTooHighInFlight(channelId(alice), maximum = 150000000, actual = 151000000).getMessage)
awaitCond(alice.stateName == CLOSING)
alice2blockchain.expectMsg(PublishAsap(tx))
alice2blockchain.expectMsgType[WatchConfirmed]
@ -354,7 +354,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
}
alice2bob.forward(bob, UpdateAddHtlc("00" * 32, 30, 1000000, "11" * 32, 400144, defaultOnion))
val error = bob2alice.expectMsgType[Error]
assert(new String(error.data) === TooManyAcceptedHtlcs(maximum = 30).getMessage)
assert(new String(error.data) === TooManyAcceptedHtlcs(channelId(bob), maximum = 30).getMessage)
awaitCond(bob.stateName == CLOSING)
bob2blockchain.expectMsg(PublishAsap(tx))
bob2blockchain.expectMsgType[WatchConfirmed]
@ -720,7 +720,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
sender.send(bob, CMD_FULFILL_HTLC(42, r))
sender.expectMsg(Failure(UnknownHtlcId(42)))
sender.expectMsg(Failure(UnknownHtlcId(channelId(bob), 42)))
assert(initialState == bob.stateData)
}
}
@ -734,7 +734,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
// actual test begins
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
sender.send(bob, CMD_FULFILL_HTLC(htlc.id, "00" * 32))
sender.expectMsg(Failure(InvalidHtlcPreimage(0)))
sender.expectMsg(Failure(InvalidHtlcPreimage(channelId(bob), 0)))
assert(initialState == bob.stateData)
}
}
@ -836,14 +836,14 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
}
}
test("recv CMD_FAIL_HTLC (unknown htlc id)") { case (alice, bob, alice2bob, bob2alice, _, _, _) =>
test("recv CMD_FAIL_HTLC (unknown htlc id)") { case (_, bob, _, _, _, _, _) =>
within(30 seconds) {
val sender = TestProbe()
val r: BinaryData = "11" * 32
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
sender.send(bob, CMD_FAIL_HTLC(42, Right(PermanentChannelFailure)))
sender.expectMsg(Failure(UnknownHtlcId(42)))
sender.expectMsg(Failure(UnknownHtlcId(channelId(bob), 42)))
assert(initialState == bob.stateData)
}
}
@ -932,7 +932,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
val sender = TestProbe()
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
sender.send(bob, CMD_UPDATE_FEE(20000))
sender.expectMsg(Failure(FundeeCannotSendUpdateFee))
sender.expectMsg(Failure(FundeeCannotSendUpdateFee(channelId(bob))))
assert(initialState == bob.stateData)
}
}
@ -967,7 +967,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
Globals.feeratePerKw.set(fee.feeratePerKw)
sender.send(bob, fee)
val error = bob2alice.expectMsgType[Error]
assert(new String(error.data) === CannotAffordFees(missingSatoshis = 71620000L, reserveSatoshis = 20000L, feesSatoshis=72400000L).getMessage)
assert(new String(error.data) === CannotAffordFees(channelId(bob), missingSatoshis = 71620000L, reserveSatoshis = 20000L, feesSatoshis=72400000L).getMessage)
awaitCond(bob.stateName == CLOSING)
bob2blockchain.expectMsg(PublishAsap(tx))
bob2blockchain.expectMsgType[WatchConfirmed]
@ -1004,15 +1004,15 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
val sender = TestProbe()
val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice)
sender.send(alice, CMD_CLOSE(None))
sender.expectMsg(Failure(CannotCloseWithPendingChanges))
sender.expectMsg(Failure(CannotCloseWithPendingChanges(channelId(bob))))
}
}
test("recv CMD_CLOSE (with invalid final script)") { case (alice, bob, alice2bob, bob2alice, _, _, _) =>
test("recv CMD_CLOSE (with invalid final script)") { case (alice, _, _, _, _, _, _) =>
within(30 seconds) {
val sender = TestProbe()
sender.send(alice, CMD_CLOSE(Some(BinaryData("00112233445566778899"))))
sender.expectMsg(Failure(InvalidFinalScript))
sender.expectMsg(Failure(InvalidFinalScript(channelId(alice))))
}
}
@ -1029,7 +1029,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
}
}
test("recv CMD_CLOSE (two in a row)") { case (alice, _, alice2bob, _, alice2blockchain, _, _) =>
test("recv CMD_CLOSE (two in a row)") { case (alice, _, alice2bob, _, _, _, _) =>
within(30 seconds) {
val sender = TestProbe()
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].localShutdown.isEmpty)
@ -1039,11 +1039,11 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
awaitCond(alice.stateName == NORMAL)
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].localShutdown.isDefined)
sender.send(alice, CMD_CLOSE(None))
sender.expectMsg(Failure(ClosingAlreadyInProgress))
sender.expectMsg(Failure(ClosingAlreadyInProgress(channelId(alice))))
}
}
test("recv CMD_CLOSE (while waiting for a RevokeAndAck)") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, _, _) =>
test("recv CMD_CLOSE (while waiting for a RevokeAndAck)") { case (alice, bob, alice2bob, bob2alice, _, _, _) =>
within(30 seconds) {
val sender = TestProbe()
val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice)
@ -1058,7 +1058,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
}
}
test("recv Shutdown (no pending htlcs)") { case (alice, _, alice2bob, _, alice2blockchain, _, _) =>
test("recv Shutdown (no pending htlcs)") { case (alice, _, alice2bob, _, _, _, _) =>
within(30 seconds) {
val sender = TestProbe()
sender.send(alice, Shutdown("00" * 32, Bob.channelParams.defaultFinalScriptPubKey))
@ -1068,7 +1068,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
}
}
test("recv Shutdown (with unacked sent htlcs)") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, _, _) =>
test("recv Shutdown (with unacked sent htlcs)") { case (alice, bob, alice2bob, bob2alice, _, _, _) =>
within(30 seconds) {
val sender = TestProbe()
val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice)
@ -1122,7 +1122,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
}
}
test("recv Shutdown (with signed htlcs)") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, _, _) =>
test("recv Shutdown (with signed htlcs)") { case (alice, bob, alice2bob, bob2alice, _, _, _) =>
within(30 seconds) {
val sender = TestProbe()
val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice)
@ -1135,7 +1135,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
}
}
test("recv Shutdown (while waiting for a RevokeAndAck)") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, _, _) =>
test("recv Shutdown (while waiting for a RevokeAndAck)") { case (alice, bob, alice2bob, bob2alice, _, _, _) =>
within(30 seconds) {
val sender = TestProbe()
val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice)

View File

@ -88,27 +88,27 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
}
}
test("recv CMD_FULFILL_HTLC (unknown htlc id)") { case (alice, bob, alice2bob, bob2alice, _, _) =>
test("recv CMD_FULFILL_HTLC (unknown htlc id)") { case (_, bob, _, _, _, _) =>
within(30 seconds) {
val sender = TestProbe()
val initialState = bob.stateData.asInstanceOf[DATA_SHUTDOWN]
sender.send(bob, CMD_FULFILL_HTLC(42, "12" * 32))
sender.expectMsg(Failure(UnknownHtlcId(42)))
sender.expectMsg(Failure(UnknownHtlcId(channelId(bob), 42)))
assert(initialState == bob.stateData)
}
}
test("recv CMD_FULFILL_HTLC (invalid preimage)") { case (alice, bob, alice2bob, bob2alice, _, _) =>
test("recv CMD_FULFILL_HTLC (invalid preimage)") { case (_, bob, _, _, _, _) =>
within(30 seconds) {
val sender = TestProbe()
val initialState = bob.stateData.asInstanceOf[DATA_SHUTDOWN]
sender.send(bob, CMD_FULFILL_HTLC(1, "00" * 32))
sender.expectMsg(Failure(InvalidHtlcPreimage(1)))
sender.expectMsg(Failure(InvalidHtlcPreimage(channelId(bob), 1)))
assert(initialState == bob.stateData)
}
}
test("recv UpdateFulfillHtlc") { case (alice, bob, alice2bob, bob2alice, _, _) =>
test("recv UpdateFulfillHtlc") { case (alice, _, _, _, _, _) =>
within(30 seconds) {
val sender = TestProbe()
val initialState = alice.stateData.asInstanceOf[DATA_SHUTDOWN]
@ -118,7 +118,7 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
}
}
test("recv UpdateFulfillHtlc (unknown htlc id)") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, _) =>
test("recv UpdateFulfillHtlc (unknown htlc id)") { case (alice, _, alice2bob, _, alice2blockchain, _) =>
within(30 seconds) {
val tx = alice.stateData.asInstanceOf[DATA_SHUTDOWN].commitments.localCommit.publishableTxs.commitTx.tx
val sender = TestProbe()
@ -131,7 +131,7 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
}
}
test("recv UpdateFulfillHtlc (invalid preimage)") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, _) =>
test("recv UpdateFulfillHtlc (invalid preimage)") { case (alice, _, alice2bob, _, alice2blockchain, _) =>
within(30 seconds) {
val tx = alice.stateData.asInstanceOf[DATA_SHUTDOWN].commitments.localCommit.publishableTxs.commitTx.tx
val sender = TestProbe()
@ -169,17 +169,17 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
}
}
test("recv CMD_FAIL_HTLC (unknown htlc id)") { case (alice, bob, alice2bob, bob2alice, _, _) =>
test("recv CMD_FAIL_HTLC (unknown htlc id)") { case (_, bob, _, _, _, _) =>
within(30 seconds) {
val sender = TestProbe()
val initialState = bob.stateData.asInstanceOf[DATA_SHUTDOWN]
sender.send(bob, CMD_FAIL_HTLC(42, Right(PermanentChannelFailure)))
sender.expectMsg(Failure(UnknownHtlcId(42)))
sender.expectMsg(Failure(UnknownHtlcId(channelId(bob), 42)))
assert(initialState == bob.stateData)
}
}
test("recv UpdateFailHtlc") { case (alice, bob, alice2bob, bob2alice, _, _) =>
test("recv UpdateFailHtlc") { case (alice, _, _, _, _, _) =>
within(30 seconds) {
val sender = TestProbe()
val initialState = alice.stateData.asInstanceOf[DATA_SHUTDOWN]
@ -392,7 +392,7 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
val sender = TestProbe()
val initialState = bob.stateData.asInstanceOf[DATA_SHUTDOWN]
sender.send(bob, CMD_UPDATE_FEE(20000))
sender.expectMsg(Failure(FundeeCannotSendUpdateFee))
sender.expectMsg(Failure(FundeeCannotSendUpdateFee(channelId(bob))))
assert(initialState == bob.stateData)
}
}
@ -427,7 +427,7 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
Globals.feeratePerKw.set(fee.feeratePerKw)
sender.send(bob, fee)
val error = bob2alice.expectMsgType[Error]
assert(new String(error.data) === CannotAffordFees(missingSatoshis = 72120000L, reserveSatoshis = 20000L, feesSatoshis = 72400000L).getMessage)
assert(new String(error.data) === CannotAffordFees(channelId(bob), missingSatoshis = 72120000L, reserveSatoshis = 20000L, feesSatoshis = 72400000L).getMessage)
awaitCond(bob.stateName == CLOSING)
bob2blockchain.expectMsg(PublishAsap(tx))
bob2blockchain.expectMsgType[WatchConfirmed]
@ -635,11 +635,11 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
within(30 seconds) {
val sender = TestProbe()
sender.send(alice, CMD_CLOSE(None))
sender.expectMsg(Failure(ClosingAlreadyInProgress))
sender.expectMsg(Failure(ClosingAlreadyInProgress(channelId(alice))))
}
}
test("recv Error") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, _) =>
test("recv Error") { case (alice, _, _, _, alice2blockchain, _) =>
within(30 seconds) {
val aliceCommitTx = alice.stateData.asInstanceOf[DATA_SHUTDOWN].commitments.localCommit.publishableTxs.commitTx.tx
alice ! Error("00" * 32, "oops".getBytes)

View File

@ -118,11 +118,11 @@ class NegotiatingStateSpec extends TestkitBaseClass with StateTestsHelperMethods
within(30 seconds) {
val sender = TestProbe()
sender.send(alice, CMD_CLOSE(None))
sender.expectMsg(Failure(ClosingAlreadyInProgress))
sender.expectMsg(Failure(ClosingAlreadyInProgress(channelId(alice))))
}
}
test("recv Error") { case (alice, _, alice2bob, bob2alice, alice2blockchain, _) =>
test("recv Error") { case (alice, _, _, _, alice2blockchain, _) =>
within(30 seconds) {
val tx = alice.stateData.asInstanceOf[DATA_NEGOTIATING].commitments.localCommit.publishableTxs.commitTx.tx
alice ! Error("00" * 32, "oops".getBytes())

View File

@ -90,7 +90,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
// actual test starts here
val sender = TestProbe()
sender.send(alice, CMD_FULFILL_HTLC(42, "42" * 32))
sender.expectMsg(Failure(UnknownHtlcId(42)))
sender.expectMsg(Failure(UnknownHtlcId(channelId(alice), 42)))
// NB: nominal case is tested in IntegrationSpec
}
@ -231,7 +231,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
mutualClose(alice, bob, alice2bob, bob2alice, alice2blockchain, bob2blockchain)
val sender = TestProbe()
sender.send(alice, CMD_CLOSE(None))
sender.expectMsg(Failure(ClosingAlreadyInProgress))
sender.expectMsg(Failure(ClosingAlreadyInProgress(channelId(alice))))
}
}