Handle update relay fee in OFFLINE state (#719)
Previously it was only possible to update relay fee in NORMAL state, which is not very convenient because most of the time there are always some channels in OFFLINE state. This works like the NORMAL case, except that the new `channel_update` won't be broadcast immediately. It will be sent out next time the channel goes back to NORMAL, in the same `channel_update` that sets the `enable` flag to true. Also added a default handler that properly rejects the CMD_UPDATE_RELAY_FEE command in all other states.
This commit is contained in:
parent
83b00e37f4
commit
7a4f175f2f
@ -1296,6 +1296,12 @@ class Channel(val nodeParams: NodeParams, wallet: EclairWallet, remoteNodeId: Pu
|
||||
// -> in CLOSING we either have mutual closed (so no more htlcs), or already have unilaterally closed (so no action required), and we can't be in OFFLINE state anyway
|
||||
handleLocalError(HtlcTimedout(d.channelId), d, Some(c))
|
||||
|
||||
case Event(CMD_UPDATE_RELAY_FEE(feeBaseMsat, feeProportionalMillionths), d: DATA_NORMAL) =>
|
||||
log.info(s"updating relay fees: prevFeeBaseMsat={} nextFeeBaseMsat={} prevFeeProportionalMillionths={} nextFeeProportionalMillionths={}", d.channelUpdate.feeBaseMsat, feeBaseMsat, d.channelUpdate.feeProportionalMillionths, feeProportionalMillionths)
|
||||
val channelUpdate = Announcements.makeChannelUpdate(nodeParams.chainHash, nodeParams.privateKey, remoteNodeId, d.shortChannelId, d.channelUpdate.cltvExpiryDelta, d.channelUpdate.htlcMinimumMsat, feeBaseMsat, feeProportionalMillionths, enable = true)
|
||||
// we're in OFFLINE state, we don't broadcast the new update right away, we will do that when next time we go to NORMAL state
|
||||
stay using store(d.copy(channelUpdate = channelUpdate)) replying "ok"
|
||||
|
||||
// just ignore this, we will put a new watch when we reconnect, and we'll be notified again
|
||||
case Event(WatchEventConfirmed(BITCOIN_FUNDING_DEPTHOK | BITCOIN_FUNDING_DEEPLYBURIED, _, _), _) => stay
|
||||
|
||||
@ -1469,14 +1475,16 @@ class Channel(val nodeParams: NodeParams, wallet: EclairWallet, remoteNodeId: Pu
|
||||
case _ => handleCommandError(AddHtlcFailed(d.channelId, c.paymentHash, error, origin(c), None, Some(c)), c) // we don't provide a channel_update: this will be a permanent channel failure
|
||||
}
|
||||
|
||||
case Event(c: CMD_CLOSE, d) => handleCommandError(CannotCloseInThisState(Helpers.getChannelId(d), stateName), c)
|
||||
case Event(c: CMD_CLOSE, d) => handleCommandError(CommandUnavailableInThisState(Helpers.getChannelId(d), "close", stateName), c)
|
||||
|
||||
case Event(c@CMD_FORCECLOSE, d) =>
|
||||
d match {
|
||||
case data: HasCommitments => handleLocalError(ForcedLocalCommit(data.channelId), data, Some(c)) replying "ok"
|
||||
case _ => handleCommandError(CannotCloseInThisState(Helpers.getChannelId(d), stateName), c)
|
||||
case _ => handleCommandError(CommandUnavailableInThisState(Helpers.getChannelId(d), "forceclose", stateName), c)
|
||||
}
|
||||
|
||||
case Event(c: CMD_UPDATE_RELAY_FEE, d) => handleCommandError(CommandUnavailableInThisState(Helpers.getChannelId(d), "updaterelayfee", stateName), c)
|
||||
|
||||
// we only care about this event in NORMAL and SHUTDOWN state, and we never unregister to the event stream
|
||||
case Event(CurrentBlockCount(_), _) => stay
|
||||
|
||||
|
||||
@ -43,7 +43,6 @@ case class ChannelReserveNotMet (override val channelId: BinaryDa
|
||||
case class ChannelFundingError (override val channelId: BinaryData) extends ChannelException(channelId, "channel funding error")
|
||||
case class NoMoreHtlcsClosingInProgress (override val channelId: BinaryData) extends ChannelException(channelId, "cannot send new htlcs, closing in progress")
|
||||
case class ClosingAlreadyInProgress (override val channelId: BinaryData) extends ChannelException(channelId, "closing already in progress")
|
||||
case class CannotCloseInThisState (override val channelId: BinaryData, state: State) extends ChannelException(channelId, s"cannot close in state=$state")
|
||||
case class CannotCloseWithUnsignedOutgoingHtlcs(override val channelId: BinaryData) extends ChannelException(channelId, "cannot close when there are unsigned outgoing htlcs")
|
||||
case class ChannelUnavailable (override val channelId: BinaryData) extends ChannelException(channelId, "channel is unavailable (offline or closing)")
|
||||
case class InvalidFinalScript (override val channelId: BinaryData) extends ChannelException(channelId, "invalid final script")
|
||||
@ -82,4 +81,5 @@ case class RevocationSyncError (override val channelId: BinaryDa
|
||||
case class InvalidFailureCode (override val channelId: BinaryData) extends ChannelException(channelId, "UpdateFailMalformedHtlc message doesn't have BADONION bit set")
|
||||
case class PleasePublishYourCommitment (override val channelId: BinaryData) extends ChannelException(channelId, "please publish your local commitment")
|
||||
case class AddHtlcFailed (override val channelId: BinaryData, paymentHash: BinaryData, t: Throwable, origin: Origin, channelUpdate: Option[ChannelUpdate], originalCommand: Option[CMD_ADD_HTLC]) extends ChannelException(channelId, s"cannot add htlc with origin=$origin reason=${t.getMessage}")
|
||||
case class CommandUnavailableInThisState (override val channelId: BinaryData, command: String, state: State) extends ChannelException(channelId, s"cannot execute command=$command in state=$state")
|
||||
// @formatter:on
|
||||
@ -133,7 +133,7 @@ class WaitForFundingConfirmedStateSpec extends TestkitBaseClass with StateTestsH
|
||||
within(30 seconds) {
|
||||
val sender = TestProbe()
|
||||
sender.send(alice, CMD_CLOSE(None))
|
||||
sender.expectMsg(Failure(CannotCloseInThisState(channelId(alice), WAIT_FOR_FUNDING_CONFIRMED)))
|
||||
sender.expectMsg(Failure(CommandUnavailableInThisState(channelId(alice), "close", WAIT_FOR_FUNDING_CONFIRMED)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -116,7 +116,7 @@ class WaitForFundingLockedStateSpec extends TestkitBaseClass with StateTestsHelp
|
||||
within(30 seconds) {
|
||||
val sender = TestProbe()
|
||||
sender.send(alice, CMD_CLOSE(None))
|
||||
sender.expectMsg(Failure(CannotCloseInThisState(channelId(alice), WAIT_FOR_FUNDING_LOCKED)))
|
||||
sender.expectMsg(Failure(CommandUnavailableInThisState(channelId(alice), "close", WAIT_FOR_FUNDING_LOCKED)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ import fr.acinq.eclair.blockchain.{PublishAsap, WatchEventSpent}
|
||||
import fr.acinq.eclair.channel.states.StateTestsHelperMethods
|
||||
import fr.acinq.eclair.channel.{Data, State, _}
|
||||
import fr.acinq.eclair.crypto.Sphinx
|
||||
import fr.acinq.eclair.router.Announcements
|
||||
import fr.acinq.eclair.wire._
|
||||
import fr.acinq.eclair.{TestConstants, TestkitBaseClass}
|
||||
import org.junit.runner.RunWith
|
||||
@ -318,4 +319,44 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
||||
assert(new String(error.data) === InvalidRevokedCommitProof(channelId(alice), 0, 42, ba_reestablish_forged.yourLastPerCommitmentSecret.get).getMessage)
|
||||
}
|
||||
|
||||
test("change relay fee while offline") { case (alice, bob, alice2bob, bob2alice, _, _, relayer) =>
|
||||
|
||||
val sender = TestProbe()
|
||||
|
||||
// we simulate a disconnection
|
||||
sender.send(alice, INPUT_DISCONNECTED)
|
||||
sender.send(bob, INPUT_DISCONNECTED)
|
||||
awaitCond(alice.stateName == OFFLINE)
|
||||
awaitCond(bob.stateName == OFFLINE)
|
||||
|
||||
// alice and bob announce that their channel is OFFLINE
|
||||
assert(Announcements.isEnabled(relayer.expectMsgType[LocalChannelUpdate].channelUpdate.flags) == false)
|
||||
assert(Announcements.isEnabled(relayer.expectMsgType[LocalChannelUpdate].channelUpdate.flags) == false)
|
||||
|
||||
// we make alice update here relay fee
|
||||
sender.send(alice, CMD_UPDATE_RELAY_FEE(4200, 123456))
|
||||
sender.expectMsg("ok")
|
||||
|
||||
// alice doesn't broadcast the new channel_update yet
|
||||
relayer.expectNoMsg(300 millis)
|
||||
|
||||
// then we reconnect them
|
||||
sender.send(alice, INPUT_RECONNECTED(alice2bob.ref))
|
||||
sender.send(bob, INPUT_RECONNECTED(bob2alice.ref))
|
||||
|
||||
// peers exchange channel_reestablish messages
|
||||
alice2bob.expectMsgType[ChannelReestablish]
|
||||
bob2alice.expectMsgType[ChannelReestablish]
|
||||
// note that we don't forward the channel_reestablish so that only alice reaches NORMAL state, it facilitates the test below
|
||||
bob2alice.forward(alice)
|
||||
|
||||
// then alice reaches NORMAL state, and during the transition she broadcasts the channel_update
|
||||
val channelUpdate = relayer.expectMsgType[LocalChannelUpdate](10 seconds).channelUpdate
|
||||
assert(channelUpdate.feeBaseMsat === 4200)
|
||||
assert(channelUpdate.feeProportionalMillionths === 123456)
|
||||
|
||||
// no more messages
|
||||
relayer.expectNoMsg(300 millis)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user