Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update funding timeout fundee #1692

Merged
merged 3 commits into from
Feb 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,10 @@ import scala.util.{Failure, Success}
*
* Created by PM on 25/01/2016.
*
* @param datadir directory where eclair-core will write/read its data.
* @param pluginParams parameters for all configured plugins.
* @param seeds_opt optional seeds, if set eclair will use them instead of generating them and won't create a node_seed.dat and channel_seed.dat files.
* @param db optional databases to use, if not set eclair will create the necessary databases
* @param datadir directory where eclair-core will write/read its data.
* @param pluginParams parameters for all configured plugins.
* @param seeds_opt optional seeds, if set eclair will use them instead of generating them and won't create a node_seed.dat and channel_seed.dat files.
* @param db optional databases to use, if not set eclair will create the necessary databases
*/
class Setup(datadir: File,
pluginParams: Seq[PluginParams],
Expand Down Expand Up @@ -185,6 +185,8 @@ class Setup(datadir: File,
assert(!initialBlockDownload, s"bitcoind should be synchronized (initialblockdownload=$initialBlockDownload)")
assert(progress > 0.999, s"bitcoind should be synchronized (progress=$progress)")
assert(headers - blocks <= 1, s"bitcoind should be synchronized (headers=$headers blocks=$blocks)")
logger.info(s"current blockchain height=$blocks")
blockCount.set(blocks)
Bitcoind(bitcoinClient)
case ELECTRUM =>
val addresses = config.hasPath("electrum") match {
Expand Down
85 changes: 52 additions & 33 deletions eclair-core/src/main/scala/fr/acinq/eclair/channel/Channel.scala

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ final case class DATA_WAIT_FOR_FUNDING_SIGNED(channelId: ByteVector32,
final case class DATA_WAIT_FOR_FUNDING_CONFIRMED(commitments: Commitments,
fundingTx: Option[Transaction],
initialRelayFees_opt: Option[(MilliSatoshi, Int)],
waitingSince: Long, // how long have we been waiting for the funding tx to confirm
waitingSinceBlock: Long, // how long have we been waiting for the funding tx to confirm
deferred: Option[FundingLocked],
lastSent: Either[FundingCreated, FundingSigned]) extends Data with HasCommitments
final case class DATA_WAIT_FOR_FUNDING_LOCKED(commitments: Commitments, shortChannelId: ShortChannelId, lastSent: FundingLocked, initialRelayFees_opt: Option[(MilliSatoshi, Int)]) extends Data with HasCommitments
Expand All @@ -353,11 +353,11 @@ final case class DATA_NEGOTIATING(commitments: Commitments,
closingTxProposed: List[List[ClosingTxProposed]], // one list for every negotiation (there can be several in case of disconnection)
bestUnpublishedClosingTx_opt: Option[Transaction]) extends Data with HasCommitments {
require(closingTxProposed.nonEmpty, "there must always be a list for the current negotiation")
require(!commitments.localParams.isFunder || closingTxProposed.forall(_.nonEmpty), "funder must have at least one closing signature for every negotation attempt because it initiates the closing")
require(!commitments.localParams.isFunder || closingTxProposed.forall(_.nonEmpty), "funder must have at least one closing signature for every negotiation attempt because it initiates the closing")
}
final case class DATA_CLOSING(commitments: Commitments,
fundingTx: Option[Transaction], // this will be non-empty if we are funder and we got in closing while waiting for our own tx to be published
waitingSince: Long, // how long since we initiated the closing
waitingSinceBlock: Long, // how long since we initiated the closing
mutualCloseProposed: List[Transaction], // all exchanged closing sigs are flattened, we use this only to keep track of what publishable tx they have
mutualClosePublished: List[Transaction] = Nil,
localCommitPublished: Option[LocalCommitPublished] = None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class HelpersSpec extends AnyFunSuite {
DATA_CLOSING(
commitments = commitments,
fundingTx = None,
waitingSince = 0,
waitingSinceBlock = 0,
mutualCloseProposed = tx1 :: tx2 :: tx3 :: Nil,
mutualClosePublished = tx2 :: tx3 :: Nil,
localCommitPublished = None,
Expand All @@ -75,7 +75,7 @@ class HelpersSpec extends AnyFunSuite {
DATA_CLOSING(
commitments = commitments,
fundingTx = None,
waitingSince = 0,
waitingSinceBlock = 0,
mutualCloseProposed = tx1 :: Nil,
mutualClosePublished = tx1 :: Nil,
localCommitPublished = Some(LocalCommitPublished(
Expand All @@ -97,7 +97,7 @@ class HelpersSpec extends AnyFunSuite {
DATA_CLOSING(
commitments = commitments,
fundingTx = None,
waitingSince = 0,
waitingSinceBlock = 0,
mutualCloseProposed = tx1 :: Nil,
mutualClosePublished = tx1 :: Nil,
localCommitPublished = Some(LocalCommitPublished(
Expand All @@ -121,7 +121,7 @@ class HelpersSpec extends AnyFunSuite {
DATA_CLOSING(
commitments = commitments,
fundingTx = None,
waitingSince = 0,
waitingSinceBlock = 0,
mutualCloseProposed = Nil,
mutualClosePublished = Nil,
localCommitPublished = Some(LocalCommitPublished(
Expand Down Expand Up @@ -149,7 +149,7 @@ class HelpersSpec extends AnyFunSuite {
DATA_CLOSING(
commitments = commitments,
fundingTx = None,
waitingSince = 0,
waitingSinceBlock = 0,
mutualCloseProposed = tx1 :: Nil,
mutualClosePublished = tx1 :: Nil,
localCommitPublished = Some(LocalCommitPublished(
Expand Down Expand Up @@ -179,7 +179,7 @@ class HelpersSpec extends AnyFunSuite {
DATA_CLOSING(
commitments = commitments.copy(remoteNextCommitInfo = Left(WaitingForRevocation(commitments.remoteCommit, null, 7L))),
fundingTx = None,
waitingSince = 0,
waitingSinceBlock = 0,
mutualCloseProposed = tx1 :: Nil,
mutualClosePublished = tx1 :: Nil,
localCommitPublished = Some(LocalCommitPublished(
Expand Down Expand Up @@ -215,7 +215,7 @@ class HelpersSpec extends AnyFunSuite {
DATA_CLOSING(
commitments = commitments,
fundingTx = None,
waitingSince = 0,
waitingSinceBlock = 0,
mutualCloseProposed = Nil,
mutualClosePublished = Nil,
localCommitPublished = None,
Expand All @@ -236,7 +236,7 @@ class HelpersSpec extends AnyFunSuite {
DATA_CLOSING(
commitments = commitments,
fundingTx = None,
waitingSince = 0,
waitingSinceBlock = 0,
mutualCloseProposed = Nil,
mutualClosePublished = Nil,
localCommitPublished = None,
Expand All @@ -259,7 +259,7 @@ class HelpersSpec extends AnyFunSuite {
DATA_CLOSING(
commitments = commitments,
fundingTx = None,
waitingSince = 0,
waitingSinceBlock = 0,
mutualCloseProposed = Nil,
mutualClosePublished = Nil,
localCommitPublished = Some(LocalCommitPublished(
Expand Down Expand Up @@ -306,7 +306,7 @@ class HelpersSpec extends AnyFunSuite {
DATA_CLOSING(
commitments = commitments,
fundingTx = None,
waitingSince = 0,
waitingSinceBlock = 0,
mutualCloseProposed = Nil,
mutualClosePublished = Nil,
localCommitPublished = Some(LocalCommitPublished(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,54 @@ class WaitForFundingConfirmedStateSpec extends TestKitBaseClass with FixtureAnyF
awaitCond(alice.stateName == CLOSED)
}

test("recv BITCOIN_FUNDING_TIMEOUT") { f =>
test("recv BITCOIN_FUNDING_TIMEOUT (funder)") { f =>
import f._
alice ! BITCOIN_FUNDING_TIMEOUT
alice2bob.expectMsgType[Error]
awaitCond(alice.stateName == CLOSED)
}

test("recv BITCOIN_FUNDING_TIMEOUT (fundee)") { f =>
import f._
bob ! BITCOIN_FUNDING_TIMEOUT
bob2alice.expectMsgType[Error]
awaitCond(bob.stateName == CLOSED)
}

test("recv CurrentBlockCount (funder)") { f =>
import f._
val initialState = alice.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED]
alice ! CurrentBlockCount(initialState.waitingSinceBlock + Channel.FUNDING_TIMEOUT_FUNDEE + 1)
alice2bob.expectNoMsg(100 millis)
}

test("recv CurrentBlockCount (funding timeout not reached)") { f =>
import f._
val initialState = bob.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED]
bob ! CurrentBlockCount(initialState.waitingSinceBlock + Channel.FUNDING_TIMEOUT_FUNDEE - 1)
bob2alice.expectNoMsg(100 millis)
}

test("recv CurrentBlockCount (funding timeout reached)") { f =>
import f._
val initialState = bob.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED]
bob ! CurrentBlockCount(initialState.waitingSinceBlock + Channel.FUNDING_TIMEOUT_FUNDEE + 1)
bob2alice.expectMsgType[Error]
awaitCond(bob.stateName == CLOSED)
}

test("migrate waitingSince to waitingSinceBlocks") { f =>
import f._
// Before version 0.5.1, eclair used an absolute timestamp instead of a block height for funding timeouts.
val beforeMigration = bob.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED].copy(waitingSinceBlock = System.currentTimeMillis().milliseconds.toSeconds)
bob.setState(WAIT_FOR_INIT_INTERNAL, Nothing)
bob ! INPUT_RESTORED(beforeMigration)
awaitCond(bob.stateName == OFFLINE)
// We reset the waiting period to the current block height when starting up after updating eclair.
val currentBlockHeight = bob.underlyingActor.nodeParams.currentBlockHeight
assert(bob.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED].waitingSinceBlock === currentBlockHeight)
}

test("recv BITCOIN_FUNDING_SPENT (remote commit)") { f =>
import f._
// bob publishes his commitment tx
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,32 +243,13 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
bob2blockchain.expectMsgType[WatchConfirmed] // claim-main-delayed

// test starts here
bob.setState(stateData = bob.stateData.asInstanceOf[DATA_CLOSING].copy(waitingSince = System.currentTimeMillis.milliseconds.toSeconds - 15.days.toSeconds))
bob.setState(stateData = bob.stateData.asInstanceOf[DATA_CLOSING].copy(waitingSinceBlock = bob.underlyingActor.nodeParams.currentBlockHeight - Channel.FUNDING_TIMEOUT_FUNDEE - 1))
bob ! GetTxWithMetaResponse(fundingTx.txid, None, System.currentTimeMillis.milliseconds.toSeconds)
bob2alice.expectMsgType[Error]
bob2blockchain.expectNoMsg(200 millis)
assert(bob.stateName == CLOSED)
}

test("recv GetTxResponse (fundee, tx not found, timeout, blockchain lags)", Tag("funding_unconfirmed")) { f =>
import f._
val sender = TestProbe()
val fundingTx = alice.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED].fundingTx.get
bob ! CMD_FORCECLOSE(sender.ref)
awaitCond(bob.stateName == CLOSING)
bob2alice.expectMsgType[Error]
bob2blockchain.expectMsgType[PublishAsap]
bob2blockchain.expectMsgType[PublishAsap] // claim-main-delayed
bob2blockchain.expectMsgType[WatchConfirmed] // commitment
bob2blockchain.expectMsgType[WatchConfirmed] // claim-main-delayed

// test starts here
bob ! GetTxWithMetaResponse(fundingTx.txid, None, System.currentTimeMillis.milliseconds.toSeconds - 3.hours.toSeconds)
bob2alice.expectNoMsg(200 millis)
bob2blockchain.expectNoMsg(200 millis)
assert(bob.stateName == CLOSING) // the above expectNoMsg will make us wait, so this checks that we are still in CLOSING
}

test("recv CMD_ADD_HTLC") { f =>
import f._
mutualClose(alice, bob, alice2bob, bob2alice, alice2blockchain, bob2blockchain)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ class ChannelCodecsSpec extends AnyFunSuite {
// let's decode the old data (this will use the old codec that provides default values for new fields)
val data_new = stateDataCodec.decode(bin_old.toBitVector).require.value
assert(data_new.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED].fundingTx === None)
assert(System.currentTimeMillis.milliseconds.toSeconds - data_new.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED].waitingSince < 3600) // we just set this timestamp to current time
assert(System.currentTimeMillis.milliseconds.toSeconds - data_new.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED].waitingSinceBlock < 3600) // we just set this timestamp to current time
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comparison still valid ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will still do that "trick" for upgrades from old eclair nodes, but it will then be changed again in Channel.scala to use a block height. I thought about directly using currentBlockHeight in the legacy codec, but it requires changing a val to a def to pipe the blockCount which was messy...

// and re-encode it with the new codec
val bin_new = ByteVector(stateDataCodec.encode(data_new).require.toByteVector.toArray)
// data should now be encoded under the new format
Expand Down