1
0
Fork 0
mirror of https://github.com/ACINQ/eclair.git synced 2025-02-23 22:46:44 +01:00

implement uniclose before NORMAL state has been reached

This commit is contained in:
sstone 2016-06-17 14:25:37 +02:00
parent 7e1f7236f8
commit 2e61f8afa6
4 changed files with 39 additions and 26 deletions

View file

@ -1,10 +1,10 @@
package fr.acinq.eclair.blockchain
import akka.actor.{Actor, ActorLogging, Cancellable}
import akka.actor.{Actor, ActorLogging, Cancellable, Terminated}
import akka.pattern.pipe
import fr.acinq.bitcoin._
import fr.acinq.eclair.channel.{BITCOIN_ANCHOR_SPENT}
import fr.acinq.eclair.channel.BITCOIN_ANCHOR_SPENT
import org.bouncycastle.util.encoders.Hex
import scala.concurrent.{Await, ExecutionContext, Future, Promise}
@ -27,6 +27,7 @@ class PollingWatcher(client: ExtendedBitcoinClient)(implicit ec: ExecutionContex
case w: Watch if !watches.contains(w) =>
log.info(s"adding watch $w for $sender")
context.watch(w.channel)
val cancellable = context.system.scheduler.schedule(100 milliseconds, 10 seconds)(w match {
case w@WatchConfirmed(channel, txId, minDepth, event) =>
client.getTxConfirmations(txId.toString).map(_ match {
@ -42,7 +43,7 @@ class PollingWatcher(client: ExtendedBitcoinClient)(implicit ec: ExecutionContex
} yield {
if (conf.isDefined && !unspent) {
// NOTE : isSpent=!isUnspent only works if the parent transaction actually exists (which we assume to be true)
client.findSpendingTransaction(txId.toString(), outputIndex).map(tx => channel ! (BITCOIN_ANCHOR_SPENT, tx))
client.findSpendingTransaction(txId.toString(), outputIndex).map(tx => channel !(BITCOIN_ANCHOR_SPENT, tx))
self !('remove, w)
} else {}
}
@ -61,5 +62,10 @@ class PollingWatcher(client: ExtendedBitcoinClient)(implicit ec: ExecutionContex
case MakeAnchor(ourCommitPub, theirCommitPub, amount) =>
client.makeAnchorTx(ourCommitPub, theirCommitPub, amount).pipeTo(sender)
case Terminated(subject) =>
val deadWatches = watches.keys.filter(_.channel == subject)
deadWatches.map(w => watches(w).cancel())
context.become(watching(watches -- deadWatches))
}
}

View file

@ -10,7 +10,9 @@ import fr.acinq.eclair.channel.BlockchainEvent
// @formatter:off
trait Watch
trait Watch {
def channel: ActorRef
}
final case class WatchConfirmed(channel: ActorRef, txId: BinaryData, minDepth: Int, event: BlockchainEvent) extends Watch
final case class WatchSpent(channel: ActorRef, txId: BinaryData, outputIndex: Int, minDepth: Int, event: BlockchainEvent) extends Watch
final case class WatchLost(channel: ActorRef, txId: BinaryData, minDepth: Int, event: BlockchainEvent) extends Watch // notify me if confirmation number gets below minDepth

View file

@ -109,6 +109,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
case Failure(cause) =>
log.error(cause, "their open_commit_sig message contains an invalid signature")
them ! error(Some("Bad signature"))
// we haven't published anything yet, we can just stop
context stop self
goto(CLOSED)
case Success(_) =>
@ -137,13 +138,15 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
case Event(BITCOIN_ANCHOR_TIMEOUT, _) =>
them ! error(Some("Anchor timed out"))
goto(ERR_ANCHOR_TIMEOUT)
context stop self
goto(CLOSED)
case Event((BITCOIN_ANCHOR_SPENT, tx: Transaction), d: DATA_OPEN_WAITING) if (isTheirCommit(tx, d.commitments.ourParams, d.commitments.theirParams, d.commitments.theirCommit)) =>
goto(CLOSING) using DATA_CLOSING(d.commitments, d.shaChain, theirCommitPublished = Some(tx))
case Event(BITCOIN_ANCHOR_SPENT, _) =>
goto(ERR_INFORMATION_LEAK)
case Event((BITCOIN_ANCHOR_SPENT, _), d: DATA_OPEN_WAITING) =>
// they are funding the anchor, we have nothing at stake
log.warning(s"their anchor ${d.commitments.anchorId} was spent, sending error and closing")
them ! error(Some(s"your anchor ${d.commitments.anchorId} was spent"))
context stop self
goto(CLOSED)
}
when(OPEN_WAITING_OURANCHOR) {
@ -175,12 +178,12 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
Register.create_alias(theirNodeId, d.commitments.anchorId)
goto(NORMAL)
case Event((BITCOIN_ANCHOR_SPENT, tx: Transaction), d: DATA_NORMAL) if (isTheirCommit(tx, d.commitments.ourParams, d.commitments.theirParams, d.commitments.theirCommit)) =>
them ! handle_theircommit(tx, d.commitments.ourParams, d.commitments.theirParams, d.shaChain, d.commitments.theirCommit)
goto(CLOSING) using DATA_CLOSING(d.commitments, d.shaChain, theirCommitPublished = Some(tx))
case Event((BITCOIN_ANCHOR_SPENT, _), _) =>
goto(ERR_INFORMATION_LEAK)
case Event((BITCOIN_ANCHOR_SPENT, _), d: DATA_OPEN_WAITING) =>
// they are funding the anchor, we have nothing at stake
log.warning(s"their anchor ${d.commitments.anchorId} was spent, sending error and closing")
them ! error(Some(s"your anchor ${d.commitments.anchorId} was spent"))
context stop self
goto(CLOSED)
}
when(OPEN_WAIT_FOR_COMPLETE_OURANCHOR) {
@ -188,14 +191,14 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
Register.create_alias(theirNodeId, d.commitments.anchorId)
goto(NORMAL)
case Event((BITCOIN_ANCHOR_SPENT, tx: Transaction), d: DATA_NORMAL) if (isTheirCommit(tx, d.commitments.ourParams, d.commitments.theirParams, d.commitments.theirCommit)) =>
them ! handle_theircommit(tx, d.commitments.ourParams, d.commitments.theirParams, d.shaChain, d.commitments.theirCommit)
goto(CLOSING) using DATA_CLOSING(d.commitments, d.shaChain, theirCommitPublished = Some(tx))
case Event((BITCOIN_ANCHOR_SPENT, _), _) =>
case Event((BITCOIN_ANCHOR_SPENT, _), d: DATA_NORMAL) =>
// this is never supposed to happen !!
log.error(s"our anchor ${d.commitments.anchorId} was spent while we were waiting for their open_complete message !!")
blockchain ! Publish(d.commitments.ourCommit.publishableTx)
goto(ERR_INFORMATION_LEAK)
case Event(e@error(problem), d: DATA_OPEN_WAITING) =>
log.error(s"received error message: $e")
blockchain ! Publish(d.commitments.ourCommit.publishableTx)
goto(CLOSING) using DATA_CLOSING(d.commitments, d.shaChain, ourCommitPublished = Some(d.commitments.ourCommit.publishableTx))
@ -297,15 +300,15 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
stay using d.copy(ourClearing = Some(ourCloseClearing))
case Event(e@error(problem), d: DATA_NORMAL) =>
log.error(s"peer send $e")
blockchain ! Publish(d.commitments.ourCommit.publishableTx)
goto(CLOSING) using DATA_CLOSING(d.commitments, d.shaChain, ourCommitPublished = Some(d.commitments.ourCommit.publishableTx))
/*case Event(pkt: close_channel, d: CurrentCommitment) =>
val (finalTx, res) = handle_pkt_close(pkt, d.ourParams, d.theirParams, d.commitment)
blockchain ! Publish(finalTx)
them ! res
goto(WAIT_FOR_CLOSE_ACK) using DATA_WAIT_FOR_CLOSE_ACK(d.ourParams, d.theirParams, d.shaChain, d.commitment, finalTx)
case Event((BITCOIN_ANCHOR_SPENT, tx: Transaction), d: DATA_NORMAL) =>
log.warning(s"anchor spent in ${tx.txid}")
stay()
/*
case Event((BITCOIN_ANCHOR_SPENT, tx: Transaction), d: DATA_NORMAL) if (isTheirCommit(tx, d.ourParams, d.theirParams, d.commitment)) =>
them ! handle_theircommit(tx, d.ourParams, d.theirParams, d.shaChain, d.commitment)
goto(CLOSING) using DATA_CLOSING(d.ourParams, d.theirParams, d.shaChain, d.commitment, theirCommitPublished = Some(tx))

View file

@ -17,12 +17,14 @@ object TestConstants {
val (Base58.Prefix.SecretKeyTestnet, commitPrivKey) = Base58Check.decode("cQPmcNr6pwBQPyGfab3SksE9nTCtx9ism9T4dkS9dETNU2KKtJHk")
val (Base58.Prefix.SecretKeyTestnet, finalPrivKey) = Base58Check.decode("cUrAtLtV7GGddqdkhUxnbZVDWGJBTducpPoon3eKp9Vnr1zxs6BG")
val channelParams = OurChannelParams(locktime(Blocks(10)), commitPrivKey, finalPrivKey, 1, 10000, "alice-seed".getBytes(), Some(anchorAmount))
val finalPubKey = channelParams.finalPubKey
}
object Bob {
val (Base58.Prefix.SecretKeyTestnet, commitPrivKey) = Base58Check.decode("cSUwLtdZ2tht9ZmHhdQue48pfe7tY2GT2TGWJDtjoZgo6FHrubGk")
val (Base58.Prefix.SecretKeyTestnet, finalPrivKey) = Base58Check.decode("cPR7ZgXpUaDPA3GwGceMDS5pfnSm955yvks3yELf3wMJwegsdGTg")
val channelParams = OurChannelParams(locktime(Blocks(10)), commitPrivKey, finalPrivKey, 2, 10000, "bob-seed".getBytes(), None)
val finalPubKey = channelParams.finalPubKey
}
}