mirror of
https://github.com/ACINQ/eclair.git
synced 2025-02-23 06:35:11 +01:00
fixed bug when remote close during opening of the channel
This commit is contained in:
parent
1b74f1c6cb
commit
3a3b8c6eb1
9 changed files with 44 additions and 29 deletions
|
@ -75,9 +75,10 @@ class PeerWatcher(client: ExtendedBitcoinClient, blockCount: Long)(implicit ec:
|
|||
// absolute timeout in blocks
|
||||
val timeout = Math.max(cltvTimeout, csvTimeout)
|
||||
if (timeout <= currentBlockCount) {
|
||||
log.info(s"publishing tx ${Transaction.write(tx)}")
|
||||
publish(tx)
|
||||
} else {
|
||||
log.info(s"delaying publication of tx $tx until block=$timeout (curblock=$currentBlockCount)")
|
||||
log.info(s"delaying publication of tx ${Transaction.write(tx)} until block=$timeout (curblock=$currentBlockCount)")
|
||||
val block2tx1 = block2tx.updated(timeout, tx +: block2tx.getOrElse(timeout, Seq.empty[Transaction]))
|
||||
context.become(watching(watches, block2tx1, currentBlockCount))
|
||||
}
|
||||
|
|
|
@ -310,8 +310,9 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, router: ActorRef, re
|
|||
|
||||
case Event(e: Error, d: DATA_WAIT_FOR_FUNDING_LOCKED_INTERNAL) =>
|
||||
log.error(s"peer sent $e, closing connection") // see bolt #2: A node MUST fail the connection if it receives an err message
|
||||
blockchain ! PublishAsap(d.commitments.localCommit.publishableTxs.commitTx.tx)
|
||||
blockchain ! WatchConfirmed(self, d.commitments.localCommit.publishableTxs.commitTx.tx.txid, d.params.minimumDepth, BITCOIN_CLOSE_DONE)
|
||||
val localCommitTx = d.commitments.localCommit.publishableTxs.commitTx.tx
|
||||
blockchain ! PublishAsap(localCommitTx)
|
||||
blockchain ! WatchConfirmed(self, localCommitTx.txid, d.params.minimumDepth, BITCOIN_LOCALCOMMIT_DONE)
|
||||
// there can't be htlcs at this stage
|
||||
// TODO: LocalCommitPublished.claimDelayedOutputTx should be defined
|
||||
val localCommitPublished = LocalCommitPublished(d.commitments.localCommit.publishableTxs.commitTx.tx, None, Nil, Nil, Nil)
|
||||
|
@ -672,9 +673,9 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, router: ActorRef, re
|
|||
|
||||
case Event(WatchEventConfirmed(BITCOIN_CLOSE_DONE, _, _), d: DATA_CLOSING) if d.mutualClosePublished.isDefined => goto(CLOSED)
|
||||
|
||||
case Event(WatchEventConfirmed(BITCOIN_SPEND_OURS_DONE, _, _), d: DATA_CLOSING) if d.localCommitPublished.isDefined => goto(CLOSED)
|
||||
case Event(WatchEventConfirmed(BITCOIN_LOCALCOMMIT_DONE, _, _), d: DATA_CLOSING) if d.localCommitPublished.isDefined => goto(CLOSED)
|
||||
|
||||
case Event(WatchEventConfirmed(BITCOIN_SPEND_THEIRS_DONE, _, _), d: DATA_CLOSING) if d.remoteCommitPublished.isDefined => goto(CLOSED)
|
||||
case Event(WatchEventConfirmed(BITCOIN_REMOTECOMMIT_DONE, _, _), d: DATA_CLOSING) if d.remoteCommitPublished.isDefined => goto(CLOSED)
|
||||
|
||||
case Event(WatchEventConfirmed(BITCOIN_PUNISHMENT_DONE, _, _), d: DATA_CLOSING) if d.revokedCommitPublished.size > 0 => goto(CLOSED)
|
||||
|
||||
|
@ -778,7 +779,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, router: ActorRef, re
|
|||
|
||||
blockchain ! PublishAsap(tx)
|
||||
// TODO hardcoded mindepth + shouldn't we watch the claim tx instead?
|
||||
blockchain ! WatchConfirmed(self, tx.txid, 3, BITCOIN_SPEND_OURS_DONE)
|
||||
blockchain ! WatchConfirmed(self, tx.txid, 3, BITCOIN_LOCALCOMMIT_DONE)
|
||||
|
||||
val localCommitPublished = Helpers.Closing.claimCurrentLocalCommitTxOutputs(d.commitments, tx)
|
||||
localCommitPublished.claimMainDelayedOutputTx.foreach(tx => blockchain ! PublishAsap(tx))
|
||||
|
@ -799,7 +800,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, router: ActorRef, re
|
|||
require(tx.txid == d.commitments.remoteCommit.txid, "txid mismatch")
|
||||
|
||||
// TODO hardcoded mindepth + shouldn't we watch the claim tx instead?
|
||||
blockchain ! WatchConfirmed(self, tx.txid, 3, BITCOIN_SPEND_THEIRS_DONE)
|
||||
blockchain ! WatchConfirmed(self, tx.txid, 3, BITCOIN_REMOTECOMMIT_DONE)
|
||||
|
||||
val remoteCommitPublished = Helpers.Closing.claimCurrentRemoteCommitTxOutputs(d.commitments, tx)
|
||||
remoteCommitPublished.claimMainOutputTx.foreach(tx => blockchain ! PublishAsap(tx))
|
||||
|
|
|
@ -66,9 +66,8 @@ case object BITCOIN_FUNDING_DEPTHOK extends BitcoinEvent
|
|||
case object BITCOIN_FUNDING_LOST extends BitcoinEvent
|
||||
case object BITCOIN_FUNDING_TIMEOUT extends BitcoinEvent
|
||||
case object BITCOIN_FUNDING_SPENT extends BitcoinEvent
|
||||
case object BITCOIN_FUNDING_OURCOMMIT_DELAYPASSED extends BitcoinEvent
|
||||
case object BITCOIN_SPEND_THEIRS_DONE extends BitcoinEvent
|
||||
case object BITCOIN_SPEND_OURS_DONE extends BitcoinEvent
|
||||
case object BITCOIN_LOCALCOMMIT_DONE extends BitcoinEvent
|
||||
case object BITCOIN_REMOTECOMMIT_DONE extends BitcoinEvent
|
||||
case object BITCOIN_PUNISHMENT_DONE extends BitcoinEvent
|
||||
case object BITCOIN_CLOSE_DONE extends BitcoinEvent
|
||||
case class BITCOIN_FUNDING_OTHER_CHANNEL_SPENT(channelId: Long) extends BitcoinEvent
|
||||
|
|
|
@ -105,7 +105,7 @@ class WaitForFundingLockedInternalStateSpec extends TestkitBaseClass {
|
|||
alice ! Error(0, "oops".getBytes)
|
||||
awaitCond(alice.stateName == CLOSING)
|
||||
alice2blockchain.expectMsg(PublishAsap(tx))
|
||||
alice2blockchain.expectMsgType[WatchConfirmed]
|
||||
assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_LOCALCOMMIT_DONE)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,7 @@ class WaitForFundingLockedInternalStateSpec extends TestkitBaseClass {
|
|||
alice ! CMD_CLOSE(None)
|
||||
awaitCond(alice.stateName == CLOSING)
|
||||
alice2blockchain.expectMsg(PublishAsap(tx))
|
||||
alice2blockchain.expectMsgType[WatchConfirmed]
|
||||
assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_CLOSE_DONE)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -115,7 +115,7 @@ class WaitForFundingLockedStateSpec extends TestkitBaseClass {
|
|||
alice ! Error(0, "oops".getBytes)
|
||||
awaitCond(alice.stateName == CLOSING)
|
||||
alice2blockchain.expectMsg(PublishAsap(tx))
|
||||
alice2blockchain.expectMsgType[WatchConfirmed]
|
||||
assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_LOCALCOMMIT_DONE)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,7 +125,7 @@ class WaitForFundingLockedStateSpec extends TestkitBaseClass {
|
|||
alice ! CMD_CLOSE(None)
|
||||
awaitCond(alice.stateName == CLOSING)
|
||||
alice2blockchain.expectMsg(PublishAsap(tx))
|
||||
alice2blockchain.expectMsgType[WatchConfirmed]
|
||||
assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_CLOSE_DONE)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ package fr.acinq.eclair.channel.states.e
|
|||
|
||||
import akka.actor.Props
|
||||
import akka.testkit.{TestFSMRef, TestProbe}
|
||||
import fr.acinq.bitcoin.Crypto.{Point, Scalar}
|
||||
import fr.acinq.bitcoin.Crypto.Scalar
|
||||
import fr.acinq.bitcoin.{BinaryData, Crypto, Satoshi, Script, ScriptFlags, Transaction}
|
||||
import fr.acinq.eclair.TestConstants.{Alice, Bob}
|
||||
import fr.acinq.eclair.blockchain._
|
||||
|
@ -820,7 +820,9 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
assert(bobCommitTx.txOut.size == 6) // two main outputs and 4 pending htlcs
|
||||
alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobCommitTx)
|
||||
|
||||
alice2blockchain.expectMsgType[WatchConfirmed].txId == bobCommitTx.txid
|
||||
val watch = alice2blockchain.expectMsgType[WatchConfirmed]
|
||||
assert(watch.txId === bobCommitTx.txid)
|
||||
assert(watch.event === BITCOIN_REMOTECOMMIT_DONE)
|
||||
|
||||
// in addition to its main output, alice can only claim 3 out of 4 htlcs, she can't do anything regarding the htlc sent by bob for which she does not have the preimage
|
||||
val amountClaimed = (for (i <- 0 until 4) yield {
|
||||
|
@ -872,7 +874,10 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
assert(revokedTx.txOut.size == 6)
|
||||
alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, revokedTx)
|
||||
alice2bob.expectMsgType[Error]
|
||||
alice2blockchain.expectMsgType[WatchConfirmed]
|
||||
|
||||
val watch = alice2blockchain.expectMsgType[WatchConfirmed]
|
||||
assert(watch.txId === revokedTx.txid)
|
||||
assert(watch.event === BITCOIN_PUNISHMENT_DONE)
|
||||
|
||||
val mainTx = alice2blockchain.expectMsgType[PublishAsap].tx
|
||||
val punishTx = alice2blockchain.expectMsgType[PublishAsap].tx
|
||||
|
@ -919,7 +924,9 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
alice2blockchain.expectMsg(PublishAsap(aliceCommitTx))
|
||||
assert(aliceCommitTx.txOut.size == 6) // two main outputs and 4 pending htlcs
|
||||
|
||||
alice2blockchain.expectMsgType[WatchConfirmed].txId == aliceCommitTx.txid
|
||||
val watch = alice2blockchain.expectMsgType[WatchConfirmed]
|
||||
assert(watch.txId === aliceCommitTx.txid)
|
||||
assert(watch.event === BITCOIN_LOCALCOMMIT_DONE)
|
||||
|
||||
// alice can only claim 3 out of 4 htlcs, she can't do anything regarding the htlc sent by bob for which she does not have the htlc
|
||||
// so we expect 7 transactions:
|
||||
|
|
|
@ -359,7 +359,9 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
assert(bobCommitTx.txOut.size == 4) // two main outputs and 2 pending htlcs
|
||||
alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobCommitTx)
|
||||
|
||||
alice2blockchain.expectMsgType[WatchConfirmed].txId == bobCommitTx.txid
|
||||
val watch = alice2blockchain.expectMsgType[WatchConfirmed]
|
||||
assert(watch.txId === bobCommitTx.txid)
|
||||
assert(watch.event === BITCOIN_REMOTECOMMIT_DONE)
|
||||
|
||||
val amountClaimed = (for (i <- 0 until 3) yield {
|
||||
val claimHtlcTx = alice2blockchain.expectMsgType[PublishAsap].tx
|
||||
|
@ -395,7 +397,10 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
// bob published the revoked tx
|
||||
alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, revokedTx)
|
||||
alice2bob.expectMsgType[Error]
|
||||
alice2blockchain.expectMsgType[WatchConfirmed]
|
||||
|
||||
val watch = alice2blockchain.expectMsgType[WatchConfirmed]
|
||||
assert(watch.txId === revokedTx.txid)
|
||||
assert(watch.event === BITCOIN_PUNISHMENT_DONE)
|
||||
|
||||
val mainTx = alice2blockchain.expectMsgType[PublishAsap].tx
|
||||
val punishTx = alice2blockchain.expectMsgType[PublishAsap].tx
|
||||
|
@ -420,7 +425,9 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
alice2blockchain.expectMsg(PublishAsap(aliceCommitTx))
|
||||
assert(aliceCommitTx.txOut.size == 4) // two main outputs and two htlcs
|
||||
|
||||
alice2blockchain.expectMsgType[WatchConfirmed].txId == aliceCommitTx.txid
|
||||
val watch = alice2blockchain.expectMsgType[WatchConfirmed]
|
||||
assert(watch.txId === aliceCommitTx.txid)
|
||||
assert(watch.event === BITCOIN_LOCALCOMMIT_DONE)
|
||||
|
||||
// alice can claim both htlc after a timeout
|
||||
// so we expect 5 transactions:
|
||||
|
|
|
@ -67,9 +67,9 @@ class NegotiatingStateSpec extends TestkitBaseClass with StateTestsHelperMethods
|
|||
bob2alice.forward(alice)
|
||||
} while (aliceCloseFee != bobCloseFee)
|
||||
alice2blockchain.expectMsgType[PublishAsap]
|
||||
alice2blockchain.expectMsgType[WatchConfirmed]
|
||||
assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_CLOSE_DONE)
|
||||
bob2blockchain.expectMsgType[PublishAsap]
|
||||
bob2blockchain.expectMsgType[WatchConfirmed]
|
||||
assert(bob2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_CLOSE_DONE)
|
||||
awaitCond(alice.stateName == CLOSING)
|
||||
awaitCond(bob.stateName == CLOSING)
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ class NegotiatingStateSpec extends TestkitBaseClass with StateTestsHelperMethods
|
|||
// actual test starts here
|
||||
assert(alice.stateName == NEGOTIATING)
|
||||
val mutualCloseTx = bob2blockchain.expectMsgType[PublishAsap].tx
|
||||
bob2blockchain.expectMsgType[WatchConfirmed]
|
||||
assert(bob2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_CLOSE_DONE)
|
||||
alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, mutualCloseTx)
|
||||
alice2blockchain.expectNoMsg(1 second)
|
||||
assert(alice.stateName == NEGOTIATING)
|
||||
|
@ -104,7 +104,7 @@ class NegotiatingStateSpec extends TestkitBaseClass with StateTestsHelperMethods
|
|||
alice ! Error(0, "oops".getBytes())
|
||||
awaitCond(alice.stateName == CLOSING)
|
||||
alice2blockchain.expectMsg(PublishAsap(tx))
|
||||
alice2blockchain.expectMsgType[WatchConfirmed]
|
||||
assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_LOCALCOMMIT_DONE)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
}
|
||||
}
|
||||
|
||||
test("recv BITCOIN_SPEND_OURS_DONE") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, bob2blockchain, _) =>
|
||||
test("recv BITCOIN_LOCALCOMMIT_DONE") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, bob2blockchain, _) =>
|
||||
within(30 seconds) {
|
||||
// an error occurs and alice publishes her commit tx
|
||||
val aliceCommitTx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx
|
||||
|
@ -131,7 +131,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
assert(alice.stateData.asInstanceOf[DATA_CLOSING].localCommitPublished.isDefined)
|
||||
|
||||
// actual test starts here
|
||||
alice ! WatchEventConfirmed(BITCOIN_SPEND_OURS_DONE, 0, 0)
|
||||
alice ! WatchEventConfirmed(BITCOIN_LOCALCOMMIT_DONE, 0, 0)
|
||||
awaitCond(alice.stateName == CLOSED)
|
||||
}
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
}
|
||||
}
|
||||
|
||||
test("recv BITCOIN_SPEND_THEIRS_DONE") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, bob2blockchain, bobCommitTxes) =>
|
||||
test("recv BITCOIN_REMOTECOMMIT_DONE") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, bob2blockchain, bobCommitTxes) =>
|
||||
within(30 seconds) {
|
||||
mutualClose(alice, bob, alice2bob, bob2alice, alice2blockchain, bob2blockchain)
|
||||
val initialState = alice.stateData.asInstanceOf[DATA_CLOSING]
|
||||
|
@ -166,7 +166,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
assert(alice.stateData.asInstanceOf[DATA_CLOSING].copy(remoteCommitPublished = None) == initialState)
|
||||
|
||||
// actual test starts here
|
||||
alice ! WatchEventConfirmed(BITCOIN_SPEND_THEIRS_DONE, 0, 0)
|
||||
alice ! WatchEventConfirmed(BITCOIN_REMOTECOMMIT_DONE, 0, 0)
|
||||
awaitCond(alice.stateName == CLOSED)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue