diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/Logs.scala b/eclair-core/src/main/scala/fr/acinq/eclair/Logs.scala index b259d42ff6..5f83f372c4 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/Logs.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/Logs.scala @@ -20,7 +20,7 @@ import akka.event.DiagnosticLoggingAdapter import akka.io.Tcp import fr.acinq.bitcoin.ByteVector32 import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.eclair.blockchain.ValidateResult +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.ValidateResult import fr.acinq.eclair.channel.{LocalChannelDown, LocalChannelUpdate} import fr.acinq.eclair.crypto.TransportHandler.HandshakeCompleted import fr.acinq.eclair.io.Peer.PeerRoutingMessage diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala b/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala index f154ef9687..53b4f04dd2 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala @@ -17,7 +17,10 @@ package fr.acinq.eclair import akka.Done -import akka.actor.{ActorContext, ActorRef, ActorSystem, Props, SupervisorStrategy} +import akka.actor.typed +import akka.actor.typed.scaladsl.Behaviors +import akka.actor.typed.scaladsl.adapter.ClassicActorSystemOps +import akka.actor.{ActorRef, ActorSystem, Props, SupervisorStrategy} import akka.pattern.after import akka.util.Timeout import com.softwaremill.sttp.okhttp.OkHttpFutureBackend @@ -28,7 +31,7 @@ import fr.acinq.eclair.blockchain.bitcoind.rpc.{BasicBitcoinJsonRPCClient, Batch import fr.acinq.eclair.blockchain.bitcoind.zmq.ZMQActor import fr.acinq.eclair.blockchain.bitcoind.{BitcoinCoreWallet, ZmqWatcher} import fr.acinq.eclair.blockchain.fee._ -import fr.acinq.eclair.channel.{Channel, Register, TxPublisher} +import fr.acinq.eclair.channel.{Channel, Register} import fr.acinq.eclair.crypto.keymanager.{LocalChannelKeyManager, LocalNodeKeyManager} import fr.acinq.eclair.db.Databases.FileBackup import fr.acinq.eclair.db.{Databases, DbEventHandler, FileBackupHandler} @@ -234,7 +237,7 @@ class Setup(datadir: File, watcher = { system.actorOf(SimpleSupervisor.props(Props(new ZMQActor(config.getString("bitcoind.zmqblock"), Some(zmqBlockConnected))), "zmqblock", SupervisorStrategy.Restart)) system.actorOf(SimpleSupervisor.props(Props(new ZMQActor(config.getString("bitcoind.zmqtx"), Some(zmqTxConnected))), "zmqtx", SupervisorStrategy.Restart)) - system.actorOf(SimpleSupervisor.props(ZmqWatcher.props(nodeParams.chainHash, blockCount, extendedBitcoinClient), "watcher", SupervisorStrategy.Resume)) + system.spawn(Behaviors.supervise(ZmqWatcher(nodeParams.chainHash, blockCount, extendedBitcoinClient)).onFailure(typed.SupervisorStrategy.resume), "watcher") } router = system.actorOf(SimpleSupervisor.props(Router.props(nodeParams, watcher, Some(routerInitialized)), "router", SupervisorStrategy.Resume)) @@ -347,7 +350,7 @@ object Setup { case class Kit(nodeParams: NodeParams, system: ActorSystem, - watcher: ActorRef, + watcher: typed.ActorRef[ZmqWatcher.Command], paymentHandler: ActorRef, register: ActorRef, relayer: ActorRef, diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/WatcherTypes.scala b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/WatcherTypes.scala deleted file mode 100644 index 52b8de7391..0000000000 --- a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/WatcherTypes.scala +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2019 ACINQ SAS - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package fr.acinq.eclair.blockchain - -import akka.actor.ActorRef -import fr.acinq.bitcoin.{ByteVector32, Transaction} -import fr.acinq.eclair.channel.BitcoinEvent -import fr.acinq.eclair.wire.protocol.ChannelAnnouncement - -/** - * Created by PM on 19/01/2016. - */ - -sealed trait Watch { - // @formatter:off - def replyTo: ActorRef - def event: BitcoinEvent - // @formatter:on -} - -/** - * Watch for confirmation of a given transaction. - * - * @param replyTo actor to notify once the transaction is confirmed. - * @param txId txid of the transaction to watch. - * @param minDepth number of confirmations. - * @param event channel event related to the transaction. - */ -final case class WatchConfirmed(replyTo: ActorRef, txId: ByteVector32, minDepth: Long, event: BitcoinEvent) extends Watch - -/** - * Watch for transactions spending the given outpoint. - * - * NB: an event will be triggered *every time* a transaction spends the given outpoint. This can be useful when: - * - we see a spending transaction in the mempool, but it is then replaced (RBF) - * - we see a spending transaction in the mempool, but a conflicting transaction "wins" and gets confirmed in a block - * - * @param replyTo actor to notify when the outpoint is spent. - * @param txId txid of the outpoint to watch. - * @param outputIndex index of the outpoint to watch. - * @param event channel event related to the outpoint. - * @param hints txids of potential spending transactions; most of the time we know the txs, and it allows for optimizations. - * This argument can safely be ignored by watcher implementations. - */ -final case class WatchSpent(replyTo: ActorRef, txId: ByteVector32, outputIndex: Int, event: BitcoinEvent, hints: Set[ByteVector32]) extends Watch - -/** - * Watch for the first transaction spending the given outpoint. We assume that txid is already confirmed or in the - * mempool (i.e. the outpoint exists). - * - * NB: an event will be triggered only once when we see a transaction that spends the given outpoint. If you want to - * react to the transaction spending the outpoint, you should use [[WatchSpent]] instead. - * - * @param replyTo actor to notify when the outpoint is spent. - * @param txId txid of the outpoint to watch. - * @param outputIndex index of the outpoint to watch. - * @param event channel event related to the outpoint. - */ -final case class WatchSpentBasic(replyTo: ActorRef, txId: ByteVector32, outputIndex: Int, event: BitcoinEvent) extends Watch - -// TODO: not implemented yet: notify me if confirmation number gets below minDepth? -final case class WatchLost(replyTo: ActorRef, txId: ByteVector32, minDepth: Long, event: BitcoinEvent) extends Watch - -/** Even triggered when a watch condition is met. */ -trait WatchEvent { - def event: BitcoinEvent -} - -/** - * This event is sent when a [[WatchConfirmed]] condition is met. - * - * @param event channel event related to the transaction that has been confirmed. - * @param blockHeight block in which the transaction was confirmed. - * @param txIndex index of the transaction in the block. - * @param tx transaction that has been confirmed. - */ -final case class WatchEventConfirmed(event: BitcoinEvent, blockHeight: Int, txIndex: Int, tx: Transaction) extends WatchEvent - -/** - * This event is sent when a [[WatchSpent]] condition is met. - * - * @param event channel event related to the outpoint that was spent. - * @param tx transaction spending the watched outpoint. - */ -final case class WatchEventSpent(event: BitcoinEvent, tx: Transaction) extends WatchEvent - -/** - * This event is sent when a [[WatchSpentBasic]] condition is met. - * - * @param event channel event related to the outpoint that was spent. - */ -final case class WatchEventSpentBasic(event: BitcoinEvent) extends WatchEvent - -// TODO: not implemented yet. -final case class WatchEventLost(event: BitcoinEvent) extends WatchEvent - -// @formatter:off -sealed trait UtxoStatus -object UtxoStatus { - case object Unspent extends UtxoStatus - case class Spent(spendingTxConfirmed: Boolean) extends UtxoStatus -} - -final case class ValidateRequest(ann: ChannelAnnouncement) -final case class ValidateResult(c: ChannelAnnouncement, fundingTx: Either[Throwable, (Transaction, UtxoStatus)]) - -final case class GetTxWithMeta(txid: ByteVector32) -final case class GetTxWithMetaResponse(txid: ByteVector32, tx_opt: Option[Transaction], lastBlockTimestamp: Long) -// @formatter:on diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/ZmqWatcher.scala b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/ZmqWatcher.scala index 4e33f2ac7e..ce1b30d935 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/ZmqWatcher.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/ZmqWatcher.scala @@ -16,24 +16,22 @@ package fr.acinq.eclair.blockchain.bitcoind -import akka.actor.typed.SupervisorStrategy -import akka.actor.typed.scaladsl.Behaviors -import akka.actor.typed.scaladsl.adapter.ClassicActorContextOps -import akka.actor.{Actor, ActorLogging, Cancellable, Props, Terminated} -import akka.pattern.pipe +import akka.actor.typed.eventstream.EventStream +import akka.actor.typed.scaladsl.{ActorContext, Behaviors, TimerScheduler} +import akka.actor.typed.{ActorRef, Behavior, SupervisorStrategy} import fr.acinq.bitcoin._ -import fr.acinq.eclair.KamonExt import fr.acinq.eclair.blockchain.Monitoring.Metrics import fr.acinq.eclair.blockchain._ import fr.acinq.eclair.blockchain.bitcoind.rpc.ExtendedBitcoinClient import fr.acinq.eclair.blockchain.watchdogs.BlockchainWatchdog +import fr.acinq.eclair.channel.TxPublisher.PublishTx +import fr.acinq.eclair.wire.protocol.ChannelAnnouncement +import fr.acinq.eclair.{KamonExt, ShortChannelId} import org.json4s.JsonAST._ -import scodec.bits.ByteVector import java.util.concurrent.atomic.AtomicLong import scala.concurrent.duration._ import scala.concurrent.{ExecutionContext, Future} -import scala.util.Try /** * Created by PM on 21/02/2016. @@ -44,160 +42,331 @@ import scala.util.Try * - receives bitcoin events (new blocks and new txs) directly from the bitcoin network * - also uses bitcoin-core rpc api, most notably for tx confirmation count and block count (because reorgs) */ -class ZmqWatcher(chainHash: ByteVector32, blockCount: AtomicLong, client: ExtendedBitcoinClient)(implicit ec: ExecutionContext = ExecutionContext.global) extends Actor with ActorLogging { +object ZmqWatcher { - import ZmqWatcher._ + // @formatter:off + sealed trait Command + sealed trait Watch[T <: WatchTriggered] extends Command { + def replyTo: ActorRef[T] + } + type GenericWatch = Watch[_ <: WatchTriggered] + sealed trait WatchTriggered + private case class TriggerEvent[T <: WatchTriggered](replyTo: ActorRef[T], watch: Watch[T], event: T) extends Command + private[bitcoind] case class StopWatching[T <: WatchTriggered](sender: ActorRef[T]) extends Command + case class ListWatches(replyTo: ActorRef[Set[GenericWatch]]) extends Command + + private case object TickNewBlock extends Command + private case class ProcessNewBlock(block: Block) extends Command + private case class ProcessNewTransaction(tx: Transaction) extends Command + + final case class ValidateRequest(replyTo: ActorRef[ValidateResult], ann: ChannelAnnouncement) extends Command + final case class ValidateResult(c: ChannelAnnouncement, fundingTx: Either[Throwable, (Transaction, UtxoStatus)]) + + final case class GetTxWithMeta(replyTo: ActorRef[GetTxWithMetaResponse], txid: ByteVector32) extends Command + final case class GetTxWithMetaResponse(txid: ByteVector32, tx_opt: Option[Transaction], lastBlockTimestamp: Long) + + sealed trait UtxoStatus + object UtxoStatus { + case object Unspent extends UtxoStatus + case class Spent(spendingTxConfirmed: Boolean) extends UtxoStatus + } - context.system.eventStream.subscribe(self, classOf[NewBlock]) - context.system.eventStream.subscribe(self, classOf[NewTransaction]) + /** Watch for confirmation of a given transaction. */ + sealed trait WatchConfirmed[T <: WatchConfirmedTriggered] extends Watch[T] { + /** TxId of the transaction to watch. */ + def txId: ByteVector32 + /** Number of confirmations. */ + def minDepth: Long + } - private val watchdog = context.spawn(Behaviors.supervise(BlockchainWatchdog(chainHash, 150 seconds)).onFailure(SupervisorStrategy.resume), "blockchain-watchdog") + /** + * Watch for transactions spending the given outpoint. + * + * NB: an event will be triggered *every time* a transaction spends the given outpoint. This can be useful when: + * - we see a spending transaction in the mempool, but it is then replaced (RBF) + * - we see a spending transaction in the mempool, but a conflicting transaction "wins" and gets confirmed in a block + */ + sealed trait WatchSpent[T <: WatchSpentTriggered] extends Watch[T] { + /** TxId of the outpoint to watch. */ + def txId: ByteVector32 + /** Index of the outpoint to watch. */ + def outputIndex: Int + /** + * TxIds of potential spending transactions; most of the time we know the txs, and it allows for optimizations. + * This argument can safely be ignored by watcher implementations. + */ + def hints: Set[ByteVector32] + } - // this is to initialize block count - self ! TickNewBlock + /** + * Watch for the first transaction spending the given outpoint. We assume that txid is already confirmed or in the + * mempool (i.e. the outpoint exists). + * + * NB: an event will be triggered only once when we see a transaction that spends the given outpoint. If you want to + * react to the transaction spending the outpoint, you should use [[WatchSpent]] instead. + */ + sealed trait WatchSpentBasic[T <: WatchSpentBasicTriggered] extends Watch[T] { + /** TxId of the outpoint to watch. */ + def txId: ByteVector32 + /** Index of the outpoint to watch. */ + def outputIndex: Int + } - private case class TriggerEvent(w: Watch, e: WatchEvent) + /** This event is sent when a [[WatchConfirmed]] condition is met. */ + sealed trait WatchConfirmedTriggered extends WatchTriggered { + /** Block in which the transaction was confirmed. */ + def blockHeight: Int + /** Index of the transaction in that block. */ + def txIndex: Int + /** Transaction that has been confirmed. */ + def tx: Transaction + } + + /** This event is sent when a [[WatchSpent]] condition is met. */ + sealed trait WatchSpentTriggered extends WatchTriggered { + /** Transaction spending the watched outpoint. */ + def spendingTx: Transaction + } + + /** This event is sent when a [[WatchSpentBasic]] condition is met. */ + sealed trait WatchSpentBasicTriggered extends WatchTriggered + + case class WatchExternalChannelSpent(replyTo: ActorRef[WatchExternalChannelSpentTriggered], txId: ByteVector32, outputIndex: Int, shortChannelId: ShortChannelId) extends WatchSpentBasic[WatchExternalChannelSpentTriggered] + case class WatchExternalChannelSpentTriggered(shortChannelId: ShortChannelId) extends WatchSpentBasicTriggered + + case class WatchFundingSpent(replyTo: ActorRef[WatchFundingSpentTriggered], txId: ByteVector32, outputIndex: Int, hints: Set[ByteVector32]) extends WatchSpent[WatchFundingSpentTriggered] + case class WatchFundingSpentTriggered(spendingTx: Transaction) extends WatchSpentTriggered + + case class WatchOutputSpent(replyTo: ActorRef[WatchOutputSpentTriggered], txId: ByteVector32, outputIndex: Int, hints: Set[ByteVector32]) extends WatchSpent[WatchOutputSpentTriggered] + case class WatchOutputSpentTriggered(spendingTx: Transaction) extends WatchSpentTriggered + + case class WatchFundingConfirmed(replyTo: ActorRef[WatchFundingConfirmedTriggered], txId: ByteVector32, minDepth: Long) extends WatchConfirmed[WatchFundingConfirmedTriggered] + case class WatchFundingConfirmedTriggered(blockHeight: Int, txIndex: Int, tx: Transaction) extends WatchConfirmedTriggered + + case class WatchFundingDeeplyBuried(replyTo: ActorRef[WatchFundingDeeplyBuriedTriggered], txId: ByteVector32, minDepth: Long) extends WatchConfirmed[WatchFundingDeeplyBuriedTriggered] + case class WatchFundingDeeplyBuriedTriggered(blockHeight: Int, txIndex: Int, tx: Transaction) extends WatchConfirmedTriggered + + case class WatchTxConfirmed(replyTo: ActorRef[WatchTxConfirmedTriggered], txId: ByteVector32, minDepth: Long) extends WatchConfirmed[WatchTxConfirmedTriggered] + case class WatchTxConfirmedTriggered(blockHeight: Int, txIndex: Int, tx: Transaction) extends WatchConfirmedTriggered + + case class WatchParentTxConfirmed(replyTo: ActorRef[WatchParentTxConfirmedTriggered], txId: ByteVector32, minDepth: Long, childTx: PublishTx) extends WatchConfirmed[WatchParentTxConfirmedTriggered] + case class WatchParentTxConfirmedTriggered(blockHeight: Int, txIndex: Int, tx: Transaction, childTx: PublishTx) extends WatchConfirmedTriggered + + // TODO: not implemented yet: notify me if confirmation number gets below minDepth? + case class WatchFundingLost(replyTo: ActorRef[WatchFundingLostTriggered], txId: ByteVector32, minDepth: Long) extends Watch[WatchFundingLostTriggered] + case class WatchFundingLostTriggered(txId: ByteVector32) extends WatchTriggered - // @formatter:off private sealed trait AddWatchResult private case object Keep extends AddWatchResult private case object Ignore extends AddWatchResult // @formatter:on - def receive: Receive = watching(Set(), Map(), None) - - def watching(watches: Set[Watch], watchedUtxos: Map[OutPoint, Set[Watch]], nextTick: Option[Cancellable]): Receive = { - - case NewTransaction(tx) => - log.debug("analyzing txid={} tx={}", tx.txid, tx) - tx.txIn - .map(_.outPoint) - .flatMap(watchedUtxos.get) - .flatten // List[Watch] -> Watch - .collect { - case w: WatchSpentBasic => - self ! TriggerEvent(w, WatchEventSpentBasic(w.event)) - case w: WatchSpent => - self ! TriggerEvent(w, WatchEventSpent(w.event, tx)) - } - - case NewBlock(block) => - // using a Try because in tests we generate fake blocks - log.debug("received blockid={}", Try(block.blockId).getOrElse(ByteVector32(ByteVector.empty))) - nextTick.map(_.cancel()) // this may fail or succeed, worse case scenario we will have two ticks in a row (no big deal) - log.debug("scheduling a new task to check on tx confirmations") - // we do this to avoid herd effects in testing when generating a lots of blocks in a row - val task = context.system.scheduler.scheduleOnce(2 seconds, self, TickNewBlock) - context become watching(watches, watchedUtxos, Some(task)) - - case TickNewBlock => - client.getBlockCount.map { - count => - log.debug("setting blockCount={}", count) - blockCount.set(count) - context.system.eventStream.publish(CurrentBlockCount(count)) + def apply(chainHash: ByteVector32, blockCount: AtomicLong, client: ExtendedBitcoinClient): Behavior[Command] = + Behaviors.setup { context => + context.system.eventStream ! EventStream.Subscribe(context.messageAdapter[NewBlock](b => ProcessNewBlock(b.block))) + context.system.eventStream ! EventStream.Subscribe(context.messageAdapter[NewTransaction](t => ProcessNewTransaction(t.tx))) + Behaviors.withTimers { timers => + // we initialize block count + timers.startSingleTimer(TickNewBlock, TickNewBlock, 1 second) + new ZmqWatcher(chainHash, blockCount, client, context, timers).watching(Set.empty[GenericWatch], Map.empty[OutPoint, Set[GenericWatch]]) } - checkUtxos() - // TODO: beware of the herd effect - KamonExt.timeFuture(Metrics.NewBlockCheckConfirmedDuration.withoutTags()) { - Future.sequence(watches.collect { case w: WatchConfirmed => checkConfirmed(w) }) + } + + private def utxo(w: GenericWatch): Option[OutPoint] = { + w match { + case w: WatchSpent[_] => Some(OutPoint(w.txId.reverse, w.outputIndex)) + case w: WatchSpentBasic[_] => Some(OutPoint(w.txId.reverse, w.outputIndex)) + case _ => None + } + } + + /** + * The resulting map allows checking spent txs in constant time wrt number of watchers. + */ + def addWatchedUtxos(m: Map[OutPoint, Set[GenericWatch]], w: GenericWatch): Map[OutPoint, Set[GenericWatch]] = { + utxo(w) match { + case Some(utxo) => m.get(utxo) match { + case Some(watches) => m + (utxo -> (watches + w)) + case None => m + (utxo -> Set(w)) } - context become watching(watches, watchedUtxos, None) - - case TriggerEvent(w, e) if watches.contains(w) => - log.info("triggering {}", w) - w.replyTo ! e - w match { - case _: WatchSpent => - // NB: WatchSpent are permanent because we need to detect multiple spending of the funding tx or the commit tx - // They are never cleaned up but it is not a big deal for now (1 channel == 1 watch) - () - case _ => - context become watching(watches - w, removeWatchedUtxos(watchedUtxos, w), nextTick) + case None => m + } + } + + def removeWatchedUtxos(m: Map[OutPoint, Set[GenericWatch]], w: GenericWatch): Map[OutPoint, Set[GenericWatch]] = { + utxo(w) match { + case Some(utxo) => m.get(utxo) match { + case Some(watches) if watches - w == Set.empty => m - utxo + case Some(watches) => m + (utxo -> (watches - w)) + case None => m } + case None => m + } + } - case w: Watch => +} + +private class ZmqWatcher(chainHash: ByteVector32, blockCount: AtomicLong, client: ExtendedBitcoinClient, context: ActorContext[ZmqWatcher.Command], timers: TimerScheduler[ZmqWatcher.Command])(implicit ec: ExecutionContext = ExecutionContext.global) { + + import ZmqWatcher._ - val result = w match { - case _ if watches.contains(w) => Ignore // we ignore duplicates + private val log = context.log - case WatchSpentBasic(_, txid, outputIndex, _) => - // NB: we assume parent tx was published, we just need to make sure this particular output has not been spent - client.isTransactionOutputSpendable(txid, outputIndex, includeMempool = true).collect { - case false => - log.info(s"output=$outputIndex of txid=$txid has already been spent") - self ! TriggerEvent(w, WatchEventSpentBasic(w.event)) + private val watchdog = context.spawn(Behaviors.supervise(BlockchainWatchdog(chainHash, 150 seconds)).onFailure(SupervisorStrategy.resume), "blockchain-watchdog") + + private def watching(watches: Set[GenericWatch], watchedUtxos: Map[OutPoint, Set[GenericWatch]]): Behavior[Command] = { + Behaviors.receiveMessage { + case ProcessNewTransaction(tx) => + log.debug("analyzing txid={} tx={}", tx.txid, tx) + tx.txIn + .map(_.outPoint) + .flatMap(watchedUtxos.get) + .flatten + .foreach { + case w: WatchExternalChannelSpent => context.self ! TriggerEvent(w.replyTo, w, WatchExternalChannelSpentTriggered(w.shortChannelId)) + case w: WatchFundingSpent => context.self ! TriggerEvent(w.replyTo, w, WatchFundingSpentTriggered(tx)) + case w: WatchOutputSpent => context.self ! TriggerEvent(w.replyTo, w, WatchOutputSpentTriggered(tx)) + case _: WatchConfirmed[_] => // nothing to do + case _: WatchFundingLost => // nothing to do } - Keep - - case WatchSpent(_, txid, outputIndex, _, hints) => - // first let's see if the parent tx was published or not - client.getTxConfirmations(txid).collect { - case Some(_) => - // parent tx was published, we need to make sure this particular output has not been spent - client.isTransactionOutputSpendable(txid, outputIndex, includeMempool = true).collect { - case false => - // the output has been spent, let's find the spending tx - // if we know some potential spending txs, we try to fetch them directly - Future.sequence(hints.map(txid => client.getTransaction(txid).map(Some(_)).recover { case _ => None })) - .map(_ - .flatten // filter out errors - .find(tx => tx.txIn.exists(i => i.outPoint.txid == txid && i.outPoint.index == outputIndex)) match { - case Some(spendingTx) => - // there can be only one spending tx for an utxo - log.info(s"$txid:$outputIndex has already been spent by a tx provided in hints: txid=${spendingTx.txid}") - self ! NewTransaction(spendingTx) - case None => - // no luck, we have to do it the hard way... - log.info(s"$txid:$outputIndex has already been spent, looking for the spending tx in the mempool") - client.getMempool().map { mempoolTxs => - mempoolTxs.filter(tx => tx.txIn.exists(i => i.outPoint.txid == txid && i.outPoint.index == outputIndex)) match { - case Nil => - log.warning(s"$txid:$outputIndex has already been spent, spending tx not in the mempool, looking in the blockchain...") - client.lookForSpendingTx(None, txid, outputIndex).map { tx => - log.warning(s"found the spending tx of $txid:$outputIndex in the blockchain: txid=${tx.txid}") - self ! NewTransaction(tx) - } - case txs => - log.info(s"found ${txs.size} txs spending $txid:$outputIndex in the mempool: txids=${txs.map(_.txid).mkString(",")}") - txs.foreach(tx => self ! NewTransaction(tx)) - } - } - }) - } + Behaviors.same + + case ProcessNewBlock(block) => + log.debug("received blockid={}", block.blockId) + log.debug("scheduling a new task to check on tx confirmations") + // we do this to avoid herd effects in testing when generating a lots of blocks in a row + timers.startSingleTimer(TickNewBlock, TickNewBlock, 2 seconds) + Behaviors.same + + case TickNewBlock => + client.getBlockCount.map { + count => + log.debug("setting blockCount={}", count) + blockCount.set(count) + context.system.eventStream ! EventStream.Publish(CurrentBlockCount(count)) + } + checkUtxos() + // TODO: beware of the herd effect + KamonExt.timeFuture(Metrics.NewBlockCheckConfirmedDuration.withoutTags()) { + Future.sequence(watches.collect { case w: WatchConfirmed[_] => checkConfirmed(w) }) + } + Behaviors.same + + case TriggerEvent(replyTo, watch, event) => + if (watches.contains(watch)) { + log.info("triggering {}", watch) + replyTo ! event + watch match { + case _: WatchSpent[_] => + // NB: WatchSpent are permanent because we need to detect multiple spending of the funding tx or the commit tx + // They are never cleaned up but it is not a big deal for now (1 channel == 1 watch) + Behaviors.same + case _ => + watching(watches - watch, removeWatchedUtxos(watchedUtxos, watch)) } - Keep + } else { + Behaviors.same + } - case w: WatchConfirmed => - checkConfirmed(w) // maybe the tx is already confirmed, in that case the watch will be triggered and removed immediately - Keep + case w: Watch[_] => + // We call check* methods and store the watch unconditionally. + // Maybe the tx is already confirmed or spent, in that case the watch will be triggered and removed immediately. + val result = w match { + case _ if watches.contains(w) => + Ignore // we ignore duplicates + case w: WatchSpentBasic[_] => + checkSpentBasic(w) + Keep + case w: WatchSpent[_] => + checkSpent(w) + Keep + case w: WatchConfirmed[_] => + checkConfirmed(w) + Keep + case _: WatchFundingLost => + // TODO: not implemented, we ignore it silently + Ignore + } + result match { + case Keep => + log.debug("adding watch {}", w) + context.watchWith(w.replyTo, StopWatching(w.replyTo)) + watching(watches + w, addWatchedUtxos(watchedUtxos, w)) + case Ignore => + Behaviors.same + } - case _: WatchLost => Ignore // TODO: not implemented, we ignore it silently - } + case StopWatching(origin) => + // we remove watches associated to dead actors + val deprecatedWatches = watches.filter(_.replyTo == origin) + val watchedUtxos1 = deprecatedWatches.foldLeft(watchedUtxos) { case (m, w) => removeWatchedUtxos(m, w) } + watching(watches -- deprecatedWatches, watchedUtxos1) - result match { - case Keep => - log.debug("adding watch {} for {}", w, sender) - context.watch(w.replyTo) - context become watching(watches + w, addWatchedUtxos(watchedUtxos, w), nextTick) - case Ignore => () - } + case ValidateRequest(replyTo, ann) => + client.validate(ann).map(replyTo ! _) + Behaviors.same - case ValidateRequest(ann) => client.validate(ann).pipeTo(sender) + case GetTxWithMeta(replyTo, txid) => + client.getTransactionMeta(txid).map(replyTo ! _) + Behaviors.same - case GetTxWithMeta(txid) => client.getTransactionMeta(txid).pipeTo(sender) + case r: ListWatches => + r.replyTo ! watches + Behaviors.same - case Terminated(actor) => - // we remove watches associated to dead actor - val deprecatedWatches = watches.filter(_.replyTo == actor) - val watchedUtxos1 = deprecatedWatches.foldLeft(watchedUtxos) { case (m, w) => removeWatchedUtxos(m, w) } - context.become(watching(watches -- deprecatedWatches, watchedUtxos1, nextTick)) + } + } - case Symbol("watches") => sender ! watches + def checkSpentBasic(w: WatchSpentBasic[_ <: WatchSpentBasicTriggered]): Future[Unit] = { + // NB: we assume parent tx was published, we just need to make sure this particular output has not been spent + client.isTransactionOutputSpendable(w.txId, w.outputIndex, includeMempool = true).collect { + case false => + log.info(s"output=${w.txId}:${w.outputIndex} has already been spent") + w match { + case w: WatchExternalChannelSpent => context.self ! TriggerEvent(w.replyTo, w, WatchExternalChannelSpentTriggered(w.shortChannelId)) + } + } + } + def checkSpent(w: WatchSpent[_ <: WatchSpentTriggered]): Future[Unit] = { + // first let's see if the parent tx was published or not + client.getTxConfirmations(w.txId).collect { + case Some(_) => + // parent tx was published, we need to make sure this particular output has not been spent + client.isTransactionOutputSpendable(w.txId, w.outputIndex, includeMempool = true).collect { + case false => + // the output has been spent, let's find the spending tx + // if we know some potential spending txs, we try to fetch them directly + Future.sequence(w.hints.map(txid => client.getTransaction(txid).map(Some(_)).recover { case _ => None })) + .map(_ + .flatten // filter out errors + .find(tx => tx.txIn.exists(i => i.outPoint.txid == w.txId && i.outPoint.index == w.outputIndex)) match { + case Some(spendingTx) => + // there can be only one spending tx for an utxo + log.info(s"${w.txId}:${w.outputIndex} has already been spent by a tx provided in hints: txid=${spendingTx.txid}") + context.self ! ProcessNewTransaction(spendingTx) + case None => + // no luck, we have to do it the hard way... + log.info(s"${w.txId}:${w.outputIndex} has already been spent, looking for the spending tx in the mempool") + client.getMempool().map { mempoolTxs => + mempoolTxs.filter(tx => tx.txIn.exists(i => i.outPoint.txid == w.txId && i.outPoint.index == w.outputIndex)) match { + case Nil => + log.warn(s"${w.txId}:${w.outputIndex} has already been spent, spending tx not in the mempool, looking in the blockchain...") + client.lookForSpendingTx(None, w.txId, w.outputIndex).map { tx => + log.warn(s"found the spending tx of ${w.txId}:${w.outputIndex} in the blockchain: txid=${tx.txid}") + context.self ! ProcessNewTransaction(tx) + } + case txs => + log.info(s"found ${txs.size} txs spending ${w.txId}:${w.outputIndex} in the mempool: txids=${txs.map(_.txid).mkString(",")}") + txs.foreach(tx => context.self ! ProcessNewTransaction(tx)) + } + } + }) + } + } } - def checkConfirmed(w: WatchConfirmed): Future[Unit] = { + def checkConfirmed(w: WatchConfirmed[_ <: WatchConfirmedTriggered]): Future[Unit] = { log.debug("checking confirmations of txid={}", w.txId) // NB: this is very inefficient since internally we call `getrawtransaction` three times, but it doesn't really // matter because this only happens once, when the watched transaction has reached min_depth @@ -205,13 +374,17 @@ class ZmqWatcher(chainHash: ByteVector32, blockCount: AtomicLong, client: Extend case Some(confirmations) if confirmations >= w.minDepth => client.getTransaction(w.txId).flatMap { tx => client.getTransactionShortId(w.txId).map { - case (height, index) => self ! TriggerEvent(w, WatchEventConfirmed(w.event, height, index, tx)) + case (height, index) => w match { + case w: WatchFundingConfirmed => context.self ! TriggerEvent(w.replyTo, w, WatchFundingConfirmedTriggered(height, index, tx)) + case w: WatchFundingDeeplyBuried => context.self ! TriggerEvent(w.replyTo, w, WatchFundingDeeplyBuriedTriggered(height, index, tx)) + case w: WatchTxConfirmed => context.self ! TriggerEvent(w.replyTo, w, WatchTxConfirmedTriggered(height, index, tx)) + case w: WatchParentTxConfirmed => context.self ! TriggerEvent(w.replyTo, w, WatchParentTxConfirmedTriggered(height, index, tx, w.childTx)) + } } } } } - // TODO: move to a separate actor that listens to CurrentBlockCount and manages utxos for RBF def checkUtxos(): Future[Unit] = { case class Utxo(txId: ByteVector32, amount: MilliBtc, confirmations: Long, safe: Boolean) @@ -253,46 +426,7 @@ class ZmqWatcher(chainHash: ByteVector32, blockCount: AtomicLong, client: Extend utxos <- listUnspent() ancestorCount <- getUnconfirmedAncestorCountMap(utxos) } yield recordUtxos(utxos, ancestorCount)).recover { - case ex => log.warning(s"could not check utxos: $ex") - } - } - -} - -object ZmqWatcher { - - def props(chainHash: ByteVector32, blockCount: AtomicLong, client: ExtendedBitcoinClient)(implicit ec: ExecutionContext = ExecutionContext.global) = Props(new ZmqWatcher(chainHash, blockCount, client)(ec)) - - case object TickNewBlock - - private def utxo(w: Watch): Option[OutPoint] = - w match { - case w: WatchSpent => Some(OutPoint(w.txId.reverse, w.outputIndex)) - case w: WatchSpentBasic => Some(OutPoint(w.txId.reverse, w.outputIndex)) - case _ => None - } - - /** - * The resulting map allows checking spent txs in constant time wrt number of watchers - */ - def addWatchedUtxos(m: Map[OutPoint, Set[Watch]], w: Watch): Map[OutPoint, Set[Watch]] = { - utxo(w) match { - case Some(utxo) => m.get(utxo) match { - case Some(watches) => m + (utxo -> (watches + w)) - case None => m + (utxo -> Set(w)) - } - case None => m - } - } - - def removeWatchedUtxos(m: Map[OutPoint, Set[Watch]], w: Watch): Map[OutPoint, Set[Watch]] = { - utxo(w) match { - case Some(utxo) => m.get(utxo) match { - case Some(watches) if watches - w == Set.empty => m - utxo - case Some(watches) => m + (utxo -> (watches - w)) - case None => m - } - case None => m + case ex => log.warn(s"could not check utxos: $ex") } } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/rpc/ExtendedBitcoinClient.scala b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/rpc/ExtendedBitcoinClient.scala index 5eb03079e8..cf6fe53546 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/rpc/ExtendedBitcoinClient.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/rpc/ExtendedBitcoinClient.scala @@ -19,8 +19,8 @@ package fr.acinq.eclair.blockchain.bitcoind.rpc import fr.acinq.bitcoin._ import fr.acinq.eclair.ShortChannelId.coordinates import fr.acinq.eclair.TxCoordinates +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{GetTxWithMetaResponse, UtxoStatus, ValidateResult} import fr.acinq.eclair.blockchain.fee.{FeeratePerKB, FeeratePerKw} -import fr.acinq.eclair.blockchain.{GetTxWithMetaResponse, UtxoStatus, ValidateResult} import fr.acinq.eclair.transactions.Transactions import fr.acinq.eclair.wire.protocol.ChannelAnnouncement import org.json4s.Formats diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Channel.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Channel.scala index dc031d5d97..a1ea6f5b68 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Channel.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Channel.scala @@ -17,8 +17,8 @@ package fr.acinq.eclair.channel import akka.actor.typed.scaladsl.Behaviors -import akka.actor.typed.scaladsl.adapter.ClassicActorContextOps -import akka.actor.{ActorContext, ActorRef, FSM, OneForOneStrategy, Props, Status, SupervisorStrategy} +import akka.actor.typed.scaladsl.adapter.{ClassicActorContextOps, TypedActorRefOps, actorRefAdapter} +import akka.actor.{ActorContext, ActorRef, FSM, OneForOneStrategy, PossiblyHarmful, Props, Status, SupervisorStrategy, typed} import akka.event.Logging.MDC import akka.pattern.pipe import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} @@ -26,6 +26,8 @@ import fr.acinq.bitcoin.{ByteVector32, OutPoint, Satoshi, SatoshiLong, Script, S import fr.acinq.eclair.Logs.LogCategory import fr.acinq.eclair._ import fr.acinq.eclair.blockchain._ +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._ import fr.acinq.eclair.blockchain.bitcoind.rpc.ExtendedBitcoinClient import fr.acinq.eclair.channel.Helpers.{Closing, Funding} import fr.acinq.eclair.channel.Monitoring.{Metrics, Tags} @@ -57,16 +59,16 @@ import scala.util.{Failure, Success, Try} object Channel { trait TxPublisherFactory { - def spawnTxPublisher(context: ActorContext, remoteNodeId: PublicKey): akka.actor.typed.ActorRef[TxPublisher.Command] + def spawnTxPublisher(context: ActorContext, remoteNodeId: PublicKey): typed.ActorRef[TxPublisher.Command] } - case class SimpleTxPublisherFactory(nodeParams: NodeParams, watcher: ActorRef, bitcoinClient: ExtendedBitcoinClient) extends TxPublisherFactory { - override def spawnTxPublisher(context: ActorContext, remoteNodeId: PublicKey): akka.actor.typed.ActorRef[TxPublisher.Command] = { - context.spawn(Behaviors.supervise(TxPublisher(nodeParams, remoteNodeId, watcher, bitcoinClient)).onFailure(akka.actor.typed.SupervisorStrategy.restart), "tx-publisher") + case class SimpleTxPublisherFactory(nodeParams: NodeParams, watcher: typed.ActorRef[ZmqWatcher.Command], bitcoinClient: ExtendedBitcoinClient) extends TxPublisherFactory { + override def spawnTxPublisher(context: ActorContext, remoteNodeId: PublicKey): typed.ActorRef[TxPublisher.Command] = { + context.spawn(Behaviors.supervise(TxPublisher(nodeParams, remoteNodeId, watcher, bitcoinClient)).onFailure(typed.SupervisorStrategy.restart), "tx-publisher") } } - def props(nodeParams: NodeParams, wallet: EclairWallet, remoteNodeId: PublicKey, blockchain: ActorRef, relayer: ActorRef, txPublisherFactory: TxPublisherFactory, origin_opt: Option[ActorRef]): Props = + def props(nodeParams: NodeParams, wallet: EclairWallet, remoteNodeId: PublicKey, blockchain: typed.ActorRef[ZmqWatcher.Command], relayer: ActorRef, txPublisherFactory: TxPublisherFactory, origin_opt: Option[ActorRef]): Props = Props(new Channel(nodeParams, wallet, remoteNodeId, blockchain, relayer, txPublisherFactory, origin_opt)) // see https://github.com/lightningnetwork/lightning-rfc/blob/master/07-routing-gossip.md#requirements @@ -102,6 +104,10 @@ object Channel { case object PeriodicRefresh extends BroadcastReason case object Reconnected extends BroadcastReason case object AboveReserve extends BroadcastReason + + private[channel] sealed trait BitcoinEvent extends PossiblyHarmful + private[channel] case object BITCOIN_FUNDING_PUBLISH_FAILED extends BitcoinEvent + private[channel] case object BITCOIN_FUNDING_TIMEOUT extends BitcoinEvent // @formatter:on case object TickChannelOpenTimeout @@ -120,7 +126,7 @@ object Channel { } -class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId: PublicKey, blockchain: ActorRef, relayer: ActorRef, txPublisherFactory: Channel.TxPublisherFactory, origin_opt: Option[ActorRef] = None)(implicit ec: ExecutionContext = ExecutionContext.Implicits.global) extends FSM[State, Data] with FSMDiagnosticActorLogging[State, Data] { +class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId: PublicKey, blockchain: typed.ActorRef[ZmqWatcher.Command], relayer: ActorRef, txPublisherFactory: Channel.TxPublisherFactory, origin_opt: Option[ActorRef] = None)(implicit ec: ExecutionContext = ExecutionContext.Implicits.global) extends FSM[State, Data] with FSMDiagnosticActorLogging[State, Data] { import Channel._ @@ -257,7 +263,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId // if commitment number is zero, we also need to make sure that the funding tx has been published if (closing.commitments.localCommit.index == 0 && closing.commitments.remoteCommit.index == 0) { - blockchain ! GetTxWithMeta(closing.commitments.commitInput.outPoint.txid) + blockchain ! GetTxWithMeta(self, closing.commitments.commitInput.outPoint.txid) } } // no need to go OFFLINE, we can directly switch to CLOSING @@ -303,7 +309,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId case funding: DATA_WAIT_FOR_FUNDING_CONFIRMED => watchFundingTx(funding.commitments) // we make sure that the funding tx has been published - blockchain ! GetTxWithMeta(funding.commitments.commitInput.outPoint.txid) + blockchain ! GetTxWithMeta(self, funding.commitments.commitInput.outPoint.txid) if (funding.waitingSinceBlock > 1500000) { // we were using timestamps instead of block heights when the channel was created: we reset it *and* we use block heights goto(OFFLINE) using funding.copy(waitingSinceBlock = nodeParams.currentBlockHeight) storing() @@ -502,7 +508,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId log.info(s"waiting for them to publish the funding tx for channelId=$channelId fundingTxid=${commitInput.outPoint.txid}") val fundingMinDepth = Helpers.minDepthForFunding(nodeParams, fundingAmount) watchFundingTx(commitments) - blockchain ! WatchConfirmed(self, commitInput.outPoint.txid, fundingMinDepth, BITCOIN_FUNDING_DEPTHOK) + blockchain ! WatchFundingConfirmed(self, commitInput.outPoint.txid, fundingMinDepth) goto(WAIT_FOR_FUNDING_CONFIRMED) using DATA_WAIT_FOR_FUNDING_CONFIRMED(commitments, None, initialRelayFees_opt, nodeParams.currentBlockHeight, None, Right(fundingSigned)) storing() sending fundingSigned } } @@ -541,7 +547,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId context.system.eventStream.publish(ChannelSignatureReceived(self, commitments)) log.info(s"publishing funding tx for channelId=$channelId fundingTxid=${commitInput.outPoint.txid}") watchFundingTx(commitments) - blockchain ! WatchConfirmed(self, commitInput.outPoint.txid, nodeParams.minDepthBlocks, BITCOIN_FUNDING_DEPTHOK) + blockchain ! WatchFundingConfirmed(self, commitInput.outPoint.txid, nodeParams.minDepthBlocks) log.info(s"committing txid=${fundingTx.txid}") // we will publish the funding tx only after the channel state has been written to disk because we want to @@ -594,11 +600,11 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId log.info(s"received their FundingLocked, deferring message") stay using d.copy(deferred = Some(msg)) // no need to store, they will re-send if we get disconnected - case Event(WatchEventConfirmed(BITCOIN_FUNDING_DEPTHOK, blockHeight, txIndex, fundingTx), d@DATA_WAIT_FOR_FUNDING_CONFIRMED(commitments, _, initialRelayFees_opt, _, deferred, _)) => + case Event(WatchFundingConfirmedTriggered(blockHeight, txIndex, fundingTx), d@DATA_WAIT_FOR_FUNDING_CONFIRMED(commitments, _, initialRelayFees_opt, _, deferred, _)) => Try(Transaction.correctlySpends(commitments.localCommit.publishableTxs.commitTx.tx, Seq(fundingTx), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)) match { case Success(_) => log.info(s"channelId=${commitments.channelId} was confirmed at blockHeight=$blockHeight txIndex=$txIndex") - blockchain ! WatchLost(self, commitments.commitInput.outPoint.txid, nodeParams.minDepthBlocks, BITCOIN_FUNDING_LOST) + blockchain ! WatchFundingLost(self, commitments.commitInput.outPoint.txid, nodeParams.minDepthBlocks) val channelKeyPath = keyManager.keyPath(d.commitments.localParams, commitments.channelVersion) val nextPerCommitmentPoint = keyManager.commitmentPoint(channelKeyPath, 1) val fundingLocked = FundingLocked(commitments.channelId, nextPerCommitmentPoint) @@ -635,9 +641,9 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId case Event(BITCOIN_FUNDING_TIMEOUT, d: DATA_WAIT_FOR_FUNDING_CONFIRMED) => handleFundingTimeout(d) - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: DATA_WAIT_FOR_FUNDING_CONFIRMED) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d) + case Event(WatchFundingSpentTriggered(tx), d: DATA_WAIT_FOR_FUNDING_CONFIRMED) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d) - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: DATA_WAIT_FOR_FUNDING_CONFIRMED) => handleInformationLeak(tx, d) + case Event(WatchFundingSpentTriggered(tx), d: DATA_WAIT_FOR_FUNDING_CONFIRMED) => handleInformationLeak(tx, d) case Event(e: Error, d: DATA_WAIT_FOR_FUNDING_CONFIRMED) => handleRemoteError(e, d) }) @@ -645,7 +651,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId when(WAIT_FOR_FUNDING_LOCKED)(handleExceptions { case Event(FundingLocked(_, nextPerCommitmentPoint), d@DATA_WAIT_FOR_FUNDING_LOCKED(commitments, shortChannelId, _, initialRelayFees_opt)) => // used to get the final shortChannelId, used in announcements (if minDepth >= ANNOUNCEMENTS_MINCONF this event will fire instantly) - blockchain ! WatchConfirmed(self, commitments.commitInput.outPoint.txid, ANNOUNCEMENTS_MINCONF, BITCOIN_FUNDING_DEEPLYBURIED) + blockchain ! WatchFundingDeeplyBuried(self, commitments.commitInput.outPoint.txid, ANNOUNCEMENTS_MINCONF) context.system.eventStream.publish(ShortChannelIdAssigned(self, commitments.channelId, shortChannelId, None)) // we create a channel_update early so that we can use it to send payments through this channel, but it won't be propagated to other nodes since the channel is not yet announced val (feeBase, feeProportionalMillionths) = initialRelayFees_opt.getOrElse((nodeParams.feeBase, nodeParams.feeProportionalMillionth)) @@ -661,9 +667,9 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId context.system.scheduler.scheduleOnce(2 seconds, self, remoteAnnSigs) stay - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: DATA_WAIT_FOR_FUNDING_LOCKED) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d) + case Event(WatchFundingSpentTriggered(tx), d: DATA_WAIT_FOR_FUNDING_LOCKED) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d) - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: DATA_WAIT_FOR_FUNDING_LOCKED) => handleInformationLeak(tx, d) + case Event(WatchFundingSpentTriggered(tx), d: DATA_WAIT_FOR_FUNDING_LOCKED) => handleInformationLeak(tx, d) case Event(e: Error, d: DATA_WAIT_FOR_FUNDING_LOCKED) => handleRemoteError(e, d) }) @@ -938,7 +944,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId case Event(c: CurrentFeerates, d: DATA_NORMAL) => handleCurrentFeerate(c, d) - case Event(WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, blockHeight, txIndex, _), d: DATA_NORMAL) if d.channelAnnouncement.isEmpty => + case Event(WatchFundingDeeplyBuriedTriggered(blockHeight, txIndex, _), d: DATA_NORMAL) if d.channelAnnouncement.isEmpty => val shortChannelId = ShortChannelId(blockHeight, txIndex, d.commitments.commitInput.outPoint.index.toInt) log.info(s"funding tx is deeply buried at blockHeight=$blockHeight txIndex=$txIndex shortChannelId=$shortChannelId") // if final shortChannelId is different from the one we had before, we need to re-announce it @@ -1014,11 +1020,11 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId goto(NORMAL) using d.copy(channelUpdate = channelUpdate1) storing() } - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: DATA_NORMAL) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d) + case Event(WatchFundingSpentTriggered(tx), d: DATA_NORMAL) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d) - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: DATA_NORMAL) if d.commitments.remoteNextCommitInfo.left.toOption.exists(_.nextRemoteCommit.txid == tx.txid) => handleRemoteSpentNext(tx, d) + case Event(WatchFundingSpentTriggered(tx), d: DATA_NORMAL) if d.commitments.remoteNextCommitInfo.left.toOption.exists(_.nextRemoteCommit.txid == tx.txid) => handleRemoteSpentNext(tx, d) - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: DATA_NORMAL) => handleRemoteSpentOther(tx, d) + case Event(WatchFundingSpentTriggered(tx), d: DATA_NORMAL) => handleRemoteSpentOther(tx, d) case Event(INPUT_DISCONNECTED, d: DATA_NORMAL) => // we cancel the timer that would have made us send the enabled update after reconnection (flappy channel protection) @@ -1216,11 +1222,11 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId case Event(c: CurrentFeerates, d: DATA_SHUTDOWN) => handleCurrentFeerate(c, d) - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: DATA_SHUTDOWN) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d) + case Event(WatchFundingSpentTriggered(tx), d: DATA_SHUTDOWN) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d) - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: DATA_SHUTDOWN) if d.commitments.remoteNextCommitInfo.left.toOption.exists(_.nextRemoteCommit.txid == tx.txid) => handleRemoteSpentNext(tx, d) + case Event(WatchFundingSpentTriggered(tx), d: DATA_SHUTDOWN) if d.commitments.remoteNextCommitInfo.left.toOption.exists(_.nextRemoteCommit.txid == tx.txid) => handleRemoteSpentNext(tx, d) - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: DATA_SHUTDOWN) => handleRemoteSpentOther(tx, d) + case Event(WatchFundingSpentTriggered(tx), d: DATA_SHUTDOWN) => handleRemoteSpentOther(tx, d) case Event(c: CMD_CLOSE, d: DATA_SHUTDOWN) => handleCommandError(ClosingAlreadyInProgress(d.channelId), c) @@ -1266,15 +1272,15 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId case Left(cause) => handleLocalError(cause, d, Some(c)) } - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: DATA_NEGOTIATING) if d.closingTxProposed.flatten.exists(_.unsignedTx.tx.txid == tx.txid) => + case Event(WatchFundingSpentTriggered(tx), d: DATA_NEGOTIATING) if d.closingTxProposed.flatten.exists(_.unsignedTx.tx.txid == tx.txid) => // they can publish a closing tx with any sig we sent them, even if we are not done negotiating handleMutualClose(getMutualClosePublished(tx, d.closingTxProposed), Left(d)) - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: DATA_NEGOTIATING) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d) + case Event(WatchFundingSpentTriggered(tx), d: DATA_NEGOTIATING) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d) - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: DATA_NEGOTIATING) if d.commitments.remoteNextCommitInfo.left.toOption.exists(_.nextRemoteCommit.txid == tx.txid) => handleRemoteSpentNext(tx, d) + case Event(WatchFundingSpentTriggered(tx), d: DATA_NEGOTIATING) if d.commitments.remoteNextCommitInfo.left.toOption.exists(_.nextRemoteCommit.txid == tx.txid) => handleRemoteSpentNext(tx, d) - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: DATA_NEGOTIATING) => handleRemoteSpentOther(tx, d) + case Event(WatchFundingSpentTriggered(tx), d: DATA_NEGOTIATING) => handleRemoteSpentOther(tx, d) case Event(c: CMD_CLOSE, d: DATA_NEGOTIATING) => handleCommandError(ClosingAlreadyInProgress(d.channelId), c) @@ -1315,7 +1321,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId case Event(BITCOIN_FUNDING_TIMEOUT, d: DATA_CLOSING) => handleFundingTimeout(d) - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: DATA_CLOSING) => + case Event(WatchFundingSpentTriggered(tx), d: DATA_CLOSING) => if (d.mutualClosePublished.exists(_.tx.txid == tx.txid)) { // we already know about this tx, probably because we have published it ourselves after successful negotiation stay @@ -1346,14 +1352,14 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId handleRemoteSpentOther(tx, d) } - case Event(WatchEventSpent(BITCOIN_OUTPUT_SPENT, tx), d: DATA_CLOSING) => + case Event(WatchOutputSpentTriggered(tx), d: DATA_CLOSING) => // one of the outputs of the local/remote/revoked commit was spent // we just put a watch to be notified when it is confirmed - blockchain ! WatchConfirmed(self, tx.txid, nodeParams.minDepthBlocks, BITCOIN_TX_CONFIRMED(tx)) + blockchain ! WatchTxConfirmed(self, tx.txid, nodeParams.minDepthBlocks) // when a remote or local commitment tx containing outgoing htlcs is published on the network, // we watch it in order to extract payment preimage if funds are pulled by the counterparty // we can then use these preimages to fulfill origin htlcs - log.info(s"processing BITCOIN_OUTPUT_SPENT with txid=${tx.txid} tx=$tx") + log.info(s"processing bitcoin output spent by txid=${tx.txid} tx=$tx") val extracted = Closing.extractPreimages(d.commitments.localCommit, tx) extracted foreach { case (htlc, preimage) => d.commitments.originChannels.get(htlc.id) match { @@ -1369,12 +1375,12 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId val revokedCommitPublished1 = d.revokedCommitPublished.map { rev => val (rev1, penaltyTxs) = Closing.claimRevokedHtlcTxOutputs(keyManager, d.commitments, rev, tx, nodeParams.onChainFeeConf.feeEstimator) penaltyTxs.foreach(claimTx => txPublisher ! PublishRawTx(claimTx)) - penaltyTxs.foreach(claimTx => blockchain ! WatchSpent(self, tx.txid, claimTx.input.outPoint.index.toInt, BITCOIN_OUTPUT_SPENT, hints = Set(claimTx.tx.txid))) + penaltyTxs.foreach(claimTx => blockchain ! WatchOutputSpent(self, tx.txid, claimTx.input.outPoint.index.toInt, hints = Set(claimTx.tx.txid))) rev1 } stay using d.copy(revokedCommitPublished = revokedCommitPublished1) storing() - case Event(WatchEventConfirmed(BITCOIN_TX_CONFIRMED(tx), blockHeight, _, _), d: DATA_CLOSING) => + case Event(WatchTxConfirmedTriggered(blockHeight, _, tx), d: DATA_CLOSING) => log.info(s"txid=${tx.txid} has reached mindepth, updating closing state") // first we check if this tx belongs to one of the current local/remote commits, update it and update the channel data val d1 = d.copy( @@ -1383,7 +1389,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId val (localCommitPublished1, claimHtlcTx_opt) = Closing.claimLocalCommitHtlcTxOutput(localCommitPublished, keyManager, d.commitments, tx, nodeParams.onChainFeeConf.feeEstimator, nodeParams.onChainFeeConf.feeTargets) claimHtlcTx_opt.foreach(claimHtlcTx => { txPublisher ! PublishRawTx(claimHtlcTx) - blockchain ! WatchConfirmed(self, claimHtlcTx.tx.txid, nodeParams.minDepthBlocks, BITCOIN_TX_CONFIRMED(claimHtlcTx.tx)) + blockchain ! WatchTxConfirmed(self, claimHtlcTx.tx.txid, nodeParams.minDepthBlocks) }) Closing.updateLocalCommitPublished(localCommitPublished1, tx) }), @@ -1532,18 +1538,20 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId case Event(BITCOIN_FUNDING_TIMEOUT, d: DATA_WAIT_FOR_FUNDING_CONFIRMED) => handleFundingTimeout(d) // 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 + case Event(WatchFundingConfirmedTriggered(_, _, _), _) => stay + + case Event(WatchFundingDeeplyBuriedTriggered(_, _, _), _) => stay - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: DATA_NEGOTIATING) if d.closingTxProposed.flatten.exists(_.unsignedTx.tx.txid == tx.txid) => + case Event(WatchFundingSpentTriggered(tx), d: DATA_NEGOTIATING) if d.closingTxProposed.flatten.exists(_.unsignedTx.tx.txid == tx.txid) => handleMutualClose(getMutualClosePublished(tx, d.closingTxProposed), Left(d)) - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: HasCommitments) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d) + case Event(WatchFundingSpentTriggered(tx), d: HasCommitments) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d) - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: HasCommitments) if d.commitments.remoteNextCommitInfo.left.toOption.exists(_.nextRemoteCommit.txid == tx.txid) => handleRemoteSpentNext(tx, d) + case Event(WatchFundingSpentTriggered(tx), d: HasCommitments) if d.commitments.remoteNextCommitInfo.left.toOption.exists(_.nextRemoteCommit.txid == tx.txid) => handleRemoteSpentNext(tx, d) - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: DATA_WAIT_FOR_REMOTE_PUBLISH_FUTURE_COMMITMENT) => handleRemoteSpentFuture(tx, d) + case Event(WatchFundingSpentTriggered(tx), d: DATA_WAIT_FOR_REMOTE_PUBLISH_FUTURE_COMMITMENT) => handleRemoteSpentFuture(tx, d) - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: HasCommitments) => handleRemoteSpentOther(tx, d) + case Event(WatchFundingSpentTriggered(tx), d: HasCommitments) => handleRemoteSpentOther(tx, d) }) @@ -1556,7 +1564,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId Helpers.minDepthForFunding(nodeParams, d.commitments.commitInput.txOut.amount) } // we put back the watch (operation is idempotent) because the event may have been fired while we were in OFFLINE - blockchain ! WatchConfirmed(self, d.commitments.commitInput.outPoint.txid, minDepth, BITCOIN_FUNDING_DEPTHOK) + blockchain ! WatchFundingConfirmed(self, d.commitments.commitInput.outPoint.txid, minDepth) goto(WAIT_FOR_FUNDING_CONFIRMED) case Event(_: ChannelReestablish, d: DATA_WAIT_FOR_FUNDING_LOCKED) => @@ -1616,7 +1624,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId if (!d.buried) { // even if we were just disconnected/reconnected, we need to put back the watch because the event may have been // fired while we were in OFFLINE (if not, the operation is idempotent anyway) - blockchain ! WatchConfirmed(self, d.commitments.commitInput.outPoint.txid, ANNOUNCEMENTS_MINCONF, BITCOIN_FUNDING_DEEPLYBURIED) + blockchain ! WatchFundingDeeplyBuried(self, d.commitments.commitInput.outPoint.txid, ANNOUNCEMENTS_MINCONF) } else { // channel has been buried enough, should we (re)send our announcement sigs? d.channelAnnouncement match { @@ -1703,22 +1711,24 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId case Event(BITCOIN_FUNDING_TIMEOUT, d: DATA_WAIT_FOR_FUNDING_CONFIRMED) => handleFundingTimeout(d) // 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 + case Event(WatchFundingConfirmedTriggered(_, _, _), _) => stay + + case Event(WatchFundingDeeplyBuriedTriggered(_, _, _), _) => stay - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: DATA_NEGOTIATING) if d.closingTxProposed.flatten.exists(_.unsignedTx.tx.txid == tx.txid) => + case Event(WatchFundingSpentTriggered(tx), d: DATA_NEGOTIATING) if d.closingTxProposed.flatten.exists(_.unsignedTx.tx.txid == tx.txid) => handleMutualClose(getMutualClosePublished(tx, d.closingTxProposed), Left(d)) - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: HasCommitments) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d) + case Event(WatchFundingSpentTriggered(tx), d: HasCommitments) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d) - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: HasCommitments) if d.commitments.remoteNextCommitInfo.left.toOption.exists(_.nextRemoteCommit.txid == tx.txid) => handleRemoteSpentNext(tx, d) + case Event(WatchFundingSpentTriggered(tx), d: HasCommitments) if d.commitments.remoteNextCommitInfo.left.toOption.exists(_.nextRemoteCommit.txid == tx.txid) => handleRemoteSpentNext(tx, d) - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: HasCommitments) => handleRemoteSpentOther(tx, d) + case Event(WatchFundingSpentTriggered(tx), d: HasCommitments) => handleRemoteSpentOther(tx, d) case Event(e: Error, d: HasCommitments) => handleRemoteError(e, d) }) when(WAIT_FOR_REMOTE_PUBLISH_FUTURE_COMMITMENT)(handleExceptions { - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: DATA_WAIT_FOR_REMOTE_PUBLISH_FUTURE_COMMITMENT) => handleRemoteSpentFuture(tx, d) + case Event(WatchFundingSpentTriggered(tx), d: DATA_WAIT_FOR_REMOTE_PUBLISH_FUTURE_COMMITMENT) => handleRemoteSpentFuture(tx, d) }) private def errorStateHandler: StateFunction = { @@ -1733,7 +1743,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId case Event(INPUT_DISCONNECTED, _) => goto(OFFLINE) - case Event(WatchEventLost(BITCOIN_FUNDING_LOST), _) => goto(ERR_FUNDING_LOST) + case Event(WatchFundingLostTriggered(_), _) => goto(ERR_FUNDING_LOST) case Event(c: CMD_GETSTATE, _) => val replyTo = if (c.replyTo == ActorRef.noSender) sender else c.replyTo @@ -1789,7 +1799,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId // peer doesn't cancel the timer case Event(TickChannelOpenTimeout, _) => stay - case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx), d: HasCommitments) if tx.txid == d.commitments.localCommit.publishableTxs.commitTx.tx.txid => + case Event(WatchFundingSpentTriggered(tx), d: HasCommitments) if tx.txid == d.commitments.localCommit.publishableTxs.commitTx.tx.txid => log.warning(s"processing local commit spent in catch-all handler") spendLocalCurrent(d) } @@ -1954,7 +1964,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId goto(CLOSED) } - private def handleCommandSuccess(c: Command, newData: Data) = { + private def handleCommandSuccess(c: channel.Command, newData: Data) = { val replyTo_opt = c match { case hasOptionalReplyTo: HasOptionalReplyToCommand => hasOptionalReplyTo.replyTo_opt case hasReplyTo: HasReplyToCommand => if (hasReplyTo.replyTo == ActorRef.noSender) Some(sender) else Some(hasReplyTo.replyTo) @@ -1973,7 +1983,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId stay } - private def handleCommandError(cause: ChannelException, c: Command) = { + private def handleCommandError(cause: ChannelException, c: channel.Command) = { log.warning(s"${cause.getMessage} while processing cmd=${c.getClass.getSimpleName} in state=$stateName") val replyTo_opt = c match { case hasOptionalReplyTo: HasOptionalReplyToCommand => hasOptionalReplyTo.replyTo_opt @@ -1987,7 +1997,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId private def watchFundingTx(commitments: Commitments, additionalKnownSpendingTxs: Set[ByteVector32] = Set.empty): Unit = { // TODO: should we wait for an acknowledgment from the watcher? val knownSpendingTxs = Set(commitments.localCommit.publishableTxs.commitTx.tx.txid, commitments.remoteCommit.txid) ++ commitments.remoteNextCommitInfo.left.toSeq.map(_.nextRemoteCommit.txid).toSet ++ additionalKnownSpendingTxs - blockchain ! WatchSpent(self, commitments.commitInput.outPoint.txid, commitments.commitInput.outPoint.index.toInt, BITCOIN_FUNDING_SPENT, knownSpendingTxs) + blockchain ! WatchFundingSpent(self, commitments.commitInput.outPoint.txid, commitments.commitInput.outPoint.index.toInt, knownSpendingTxs) // TODO: implement this? (not needed if we use a reasonable min_depth) //blockchain ! WatchLost(self, commitments.commitInput.outPoint.txid, nodeParams.minDepthBlocks, BITCOIN_FUNDING_LOST) } @@ -2020,7 +2030,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId txPublisher ! PublishRawTx(fundingTx, "funding-tx") // we also check if the funding tx has been double-spent checkDoubleSpent(fundingTx) - context.system.scheduler.scheduleOnce(1 day, blockchain, GetTxWithMeta(txid)) + context.system.scheduler.scheduleOnce(1 day, blockchain.toClassic, GetTxWithMeta(self, txid)) case None if (nodeParams.currentBlockHeight - waitingSinceBlock) > FUNDING_TIMEOUT_FUNDEE => // if we are fundee, we give up after some time log.warning(s"funding tx hasn't been published in ${nodeParams.currentBlockHeight - waitingSinceBlock} blocks") @@ -2028,7 +2038,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId case None => // let's wait a little longer log.info(s"funding tx still hasn't been published in ${nodeParams.currentBlockHeight - waitingSinceBlock} blocks, will wait ${FUNDING_TIMEOUT_FUNDEE - nodeParams.currentBlockHeight + waitingSinceBlock} more blocks...") - context.system.scheduler.scheduleOnce(1 day, blockchain, GetTxWithMeta(txid)) + context.system.scheduler.scheduleOnce(1 day, blockchain.toClassic, GetTxWithMeta(self, txid)) } } stay @@ -2170,7 +2180,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId private def doPublish(closingTx: ClosingTx): Unit = { txPublisher ! PublishRawTx(closingTx) - blockchain ! WatchConfirmed(self, closingTx.tx.txid, nodeParams.minDepthBlocks, BITCOIN_TX_CONFIRMED(closingTx.tx)) + blockchain ! WatchTxConfirmed(self, closingTx.tx.txid, nodeParams.minDepthBlocks) } private def spendLocalCurrent(d: HasCommitments) = { @@ -2212,7 +2222,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId */ private def watchConfirmedIfNeeded(txs: Iterable[Transaction], irrevocablySpent: Map[OutPoint, Transaction]): Unit = { val (skip, process) = txs.partition(Closing.inputsAlreadySpent(_, irrevocablySpent)) - process.foreach(tx => blockchain ! WatchConfirmed(self, tx.txid, nodeParams.minDepthBlocks, BITCOIN_TX_CONFIRMED(tx))) + process.foreach(tx => blockchain ! WatchTxConfirmed(self, tx.txid, nodeParams.minDepthBlocks)) skip.foreach(tx => log.info(s"no need to watch txid=${tx.txid}, it has already been confirmed")) } @@ -2227,7 +2237,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId require(output.txid == parentTx.txid && output.index < parentTx.txOut.size, s"output doesn't belong to the given parentTx: output=${output.txid}:${output.index} (expected txid=${parentTx.txid} index < ${parentTx.txOut.size})") } val (skip, process) = outputs.partition(irrevocablySpent.contains) - process.foreach(output => blockchain ! WatchSpent(self, parentTx.txid, output.index.toInt, BITCOIN_OUTPUT_SPENT, hints = Set.empty)) + process.foreach(output => blockchain ! WatchOutputSpent(self, parentTx.txid, output.index.toInt, Set.empty)) skip.foreach(output => log.info(s"no need to watch output=${output.txid}:${output.index}, it has already been spent by txid=${irrevocablySpent.get(output).map(_.txid)}")) } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelTypes.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelTypes.scala index ac6e12d537..76e0906044 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelTypes.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelTypes.scala @@ -95,18 +95,6 @@ case object INPUT_DISCONNECTED case class INPUT_RECONNECTED(remote: ActorRef, localInit: Init, remoteInit: Init) case class INPUT_RESTORED(data: HasCommitments) -sealed trait BitcoinEvent extends PossiblyHarmful -case object BITCOIN_FUNDING_PUBLISH_FAILED extends BitcoinEvent -case object BITCOIN_FUNDING_DEPTHOK extends BitcoinEvent -case object BITCOIN_FUNDING_DEEPLYBURIED 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_OUTPUT_SPENT extends BitcoinEvent -case class BITCOIN_TX_CONFIRMED(tx: Transaction) extends BitcoinEvent -case class BITCOIN_FUNDING_EXTERNAL_CHANNEL_SPENT(shortChannelId: ShortChannelId) extends BitcoinEvent -case class BITCOIN_PARENT_TX_CONFIRMED(childTx: PublishTx) extends BitcoinEvent - /* .d8888b. .d88888b. 888b d888 888b d888 d8888 888b 888 8888888b. .d8888b. d88P Y88b d88P" "Y88b 8888b d8888 8888b d8888 d88888 8888b 888 888 "Y88b d88P Y88b diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/TxPublisher.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/TxPublisher.scala index c5b330180e..91bf95ae7e 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/TxPublisher.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/TxPublisher.scala @@ -17,15 +17,16 @@ package fr.acinq.eclair.channel import akka.actor.typed.eventstream.EventStream -import akka.actor.typed.scaladsl.adapter.TypedActorRefOps import akka.actor.typed.scaladsl.{ActorContext, Behaviors} import akka.actor.typed.{ActorRef, Behavior} import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Crypto, Satoshi, Script, Transaction, TxOut} +import fr.acinq.eclair.blockchain.CurrentBlockCount +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{WatchParentTxConfirmed, WatchParentTxConfirmedTriggered} import fr.acinq.eclair.blockchain.bitcoind.rpc.ExtendedBitcoinClient import fr.acinq.eclair.blockchain.bitcoind.rpc.ExtendedBitcoinClient.FundTransactionOptions import fr.acinq.eclair.blockchain.fee.FeeratePerKw -import fr.acinq.eclair.blockchain.{CurrentBlockCount, WatchConfirmed, WatchEventConfirmed} import fr.acinq.eclair.transactions.Transactions._ import fr.acinq.eclair.transactions.{Scripts, Transactions} import fr.acinq.eclair.wire.protocol.UpdateFulfillHtlc @@ -74,7 +75,7 @@ object TxPublisher { // CHANGING THIS WILL RESULT IN CONCURRENCY ISSUES WHILE PUBLISHING PARENT AND CHILD TXS! val singleThreadExecutionContext: ExecutionContextExecutor = ExecutionContext.fromExecutor(Executors.newSingleThreadExecutor()) - def apply(nodeParams: NodeParams, remoteNodeId: PublicKey, watcher: akka.actor.ActorRef, client: ExtendedBitcoinClient): Behavior[Command] = + def apply(nodeParams: NodeParams, remoteNodeId: PublicKey, watcher: ActorRef[ZmqWatcher.Command], client: ExtendedBitcoinClient): Behavior[Command] = Behaviors.setup { context => Behaviors.withMdc(Logs.mdc(remoteNodeId_opt = Some(remoteNodeId))) { context.system.eventStream ! EventStream.Subscribe(context.messageAdapter[CurrentBlockCount](cbc => WrappedCurrentBlockCount(cbc.blockCount))) @@ -171,7 +172,7 @@ object TxPublisher { } -private class TxPublisher(nodeParams: NodeParams, watcher: akka.actor.ActorRef, client: ExtendedBitcoinClient, context: ActorContext[TxPublisher.Command])(implicit ec: ExecutionContext = ExecutionContext.Implicits.global) { +private class TxPublisher(nodeParams: NodeParams, watcher: ActorRef[ZmqWatcher.Command], client: ExtendedBitcoinClient, context: ActorContext[TxPublisher.Command])(implicit ec: ExecutionContext = ExecutionContext.Implicits.global) { import TxPublisher._ import nodeParams.onChainFeeConf.{feeEstimator, feeTargets} @@ -181,9 +182,7 @@ private class TxPublisher(nodeParams: NodeParams, watcher: akka.actor.ActorRef, private val log = context.log - private val watchConfirmedResponseMapper: ActorRef[WatchEventConfirmed] = context.messageAdapter(w => w.event match { - case BITCOIN_PARENT_TX_CONFIRMED(childTx) => ParentTxConfirmed(childTx, w.tx.txid) - }) + val watchConfirmedResponseMapper: ActorRef[WatchParentTxConfirmedTriggered] = context.messageAdapter(w => ParentTxConfirmed(w.childTx, w.tx.txid)) /** * @param cltvDelayedTxs when transactions are cltv-delayed, we wait until the target blockchain height is reached. @@ -199,7 +198,7 @@ private class TxPublisher(nodeParams: NodeParams, watcher: akka.actor.ActorRef, csvTimeouts.foreach { case (parentTxId, csvTimeout) => log.info(s"${p.desc} txid=${p.tx.txid} has a relative timeout of $csvTimeout blocks, watching parentTxId=$parentTxId tx={}", p.tx) - watcher ! WatchConfirmed(watchConfirmedResponseMapper.toClassic, parentTxId, minDepth = csvTimeout, BITCOIN_PARENT_TX_CONFIRMED(p)) + watcher ! WatchParentTxConfirmed(watchConfirmedResponseMapper, parentTxId, minDepth = csvTimeout, p) } run(cltvDelayedTxs, csvDelayedTxs + (p.tx.txid -> TxWithRelativeDelay(p, csvTimeouts.keySet))) } else if (cltvTimeout > blockCount) { diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala b/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala index 8efa6189d4..61190678c3 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala @@ -16,6 +16,7 @@ package fr.acinq.eclair.io +import akka.actor.typed import akka.actor.{Actor, ActorContext, ActorRef, ExtendedActorSystem, FSM, OneForOneStrategy, PossiblyHarmful, Props, Status, SupervisorStrategy, Terminated} import akka.event.Logging.MDC import akka.event.{BusLogging, DiagnosticLoggingAdapter} @@ -27,6 +28,7 @@ import fr.acinq.eclair.Features.Wumbo import fr.acinq.eclair.Logs.LogCategory import fr.acinq.eclair._ import fr.acinq.eclair.blockchain.EclairWallet +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher import fr.acinq.eclair.blockchain.fee.FeeratePerKw import fr.acinq.eclair.channel._ import fr.acinq.eclair.io.Monitoring.Metrics @@ -357,7 +359,7 @@ object Peer { def spawn(context: ActorContext, remoteNodeId: PublicKey, origin_opt: Option[ActorRef]): ActorRef } - case class SimpleChannelFactory(nodeParams: NodeParams, watcher: ActorRef, relayer: ActorRef, wallet: EclairWallet, txPublisherFactory: Channel.TxPublisherFactory) extends ChannelFactory { + case class SimpleChannelFactory(nodeParams: NodeParams, watcher: typed.ActorRef[ZmqWatcher.Command], relayer: ActorRef, wallet: EclairWallet, txPublisherFactory: Channel.TxPublisherFactory) extends ChannelFactory { override def spawn(context: ActorContext, remoteNodeId: PublicKey, origin_opt: Option[ActorRef]): ActorRef = context.actorOf(Channel.props(nodeParams, wallet, remoteNodeId, watcher, relayer, txPublisherFactory, origin_opt)) } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala b/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala index 9c443500ac..267e8e1896 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala @@ -17,14 +17,16 @@ package fr.acinq.eclair.router import akka.Done -import akka.actor.{Actor, ActorLogging, ActorRef, Props, Terminated} +import akka.actor.typed.scaladsl.adapter.actorRefAdapter +import akka.actor.{Actor, ActorLogging, ActorRef, Props, Terminated, typed} import akka.event.DiagnosticLoggingAdapter import akka.event.Logging.MDC import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.bitcoin.{ByteVector32, Satoshi} import fr.acinq.eclair.Logs.LogCategory import fr.acinq.eclair._ -import fr.acinq.eclair.blockchain._ +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{ValidateResult, WatchExternalChannelSpent, WatchExternalChannelSpentTriggered} import fr.acinq.eclair.channel._ import fr.acinq.eclair.crypto.TransportHandler import fr.acinq.eclair.db.NetworkDb @@ -46,7 +48,7 @@ import scala.util.{Random, Try} /** * Created by PM on 24/05/2016. */ -class Router(val nodeParams: NodeParams, watcher: ActorRef, initialized: Option[Promise[Done]] = None) extends FSMDiagnosticActorLogging[Router.State, Router.Data] { +class Router(val nodeParams: NodeParams, watcher: typed.ActorRef[ZmqWatcher.Command], initialized: Option[Promise[Done]] = None) extends FSMDiagnosticActorLogging[Router.State, Router.Data] { import Router._ @@ -88,7 +90,7 @@ class Router(val nodeParams: NodeParams, watcher: ActorRef, initialized: Option[ val TxCoordinates(_, _, outputIndex) = ShortChannelId.coordinates(pc.ann.shortChannelId) // avoid herd effect at startup because watch-spent are intensive in terms of rpc calls to bitcoind context.system.scheduler.scheduleOnce(Random.nextLong(nodeParams.watchSpentWindow.toSeconds).seconds) { - watcher ! WatchSpentBasic(self, txid, outputIndex, BITCOIN_FUNDING_EXTERNAL_CHANNEL_SPENT(pc.ann.shortChannelId)) + watcher ! WatchExternalChannelSpent(self, txid, outputIndex, pc.ann.shortChannelId) } } @@ -232,8 +234,8 @@ class Router(val nodeParams: NodeParams, watcher: ActorRef, initialized: Option[ case Event(r: ValidateResult, d) => stay using Validation.handleChannelValidationResponse(d, nodeParams, watcher, r) - case Event(WatchEventSpentBasic(e: BITCOIN_FUNDING_EXTERNAL_CHANNEL_SPENT), d) if d.channels.contains(e.shortChannelId) => - stay using Validation.handleChannelSpent(d, nodeParams.db.network, e) + case Event(WatchExternalChannelSpentTriggered(shortChannelId), d) if d.channels.contains(shortChannelId) => + stay using Validation.handleChannelSpent(d, nodeParams.db.network, shortChannelId) case Event(n: NodeAnnouncement, d: Data) => stay using Validation.handleNodeAnnouncement(d, nodeParams.db.network, Set(LocalGossip), n) @@ -293,7 +295,7 @@ object Router { val shortChannelIdKey = Context.key[ShortChannelId]("shortChannelId", ShortChannelId(0)) val remoteNodeIdKey = Context.key[String]("remoteNodeId", "unknown") - def props(nodeParams: NodeParams, watcher: ActorRef, initialized: Option[Promise[Done]] = None) = Props(new Router(nodeParams, watcher, initialized)) + def props(nodeParams: NodeParams, watcher: typed.ActorRef[ZmqWatcher.Command], initialized: Option[Promise[Done]] = None) = Props(new Router(nodeParams, watcher, initialized)) case class RouterConf(randomizeRouteSelection: Boolean, channelExcludeDuration: FiniteDuration, diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/router/Validation.scala b/eclair-core/src/main/scala/fr/acinq/eclair/router/Validation.scala index e4da951f10..16c343fae2 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/router/Validation.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/router/Validation.scala @@ -16,12 +16,14 @@ package fr.acinq.eclair.router -import akka.actor.{ActorContext, ActorRef} +import akka.actor.typed.scaladsl.adapter.actorRefAdapter +import akka.actor.{ActorContext, ActorRef, typed} import akka.event.{DiagnosticLoggingAdapter, LoggingAdapter} import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.bitcoin.Script.{pay2wsh, write} -import fr.acinq.eclair.blockchain.{UtxoStatus, ValidateRequest, ValidateResult, WatchSpentBasic} -import fr.acinq.eclair.channel.{AvailableBalanceChanged, BITCOIN_FUNDING_EXTERNAL_CHANNEL_SPENT, LocalChannelDown, LocalChannelUpdate} +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{UtxoStatus, ValidateRequest, ValidateResult, WatchExternalChannelSpent} +import fr.acinq.eclair.channel.{AvailableBalanceChanged, LocalChannelDown, LocalChannelUpdate} import fr.acinq.eclair.crypto.TransportHandler import fr.acinq.eclair.db.NetworkDb import fr.acinq.eclair.router.Monitoring.Metrics @@ -41,7 +43,7 @@ object Validation { Metrics.gossipResult(decision).increment() } - def handleChannelAnnouncement(d: Data, db: NetworkDb, watcher: ActorRef, origin: RemoteGossip, c: ChannelAnnouncement)(implicit ctx: ActorContext, log: LoggingAdapter): Data = { + def handleChannelAnnouncement(d: Data, db: NetworkDb, watcher: typed.ActorRef[ZmqWatcher.Command], origin: RemoteGossip, c: ChannelAnnouncement)(implicit ctx: ActorContext, log: LoggingAdapter): Data = { implicit val sender: ActorRef = ctx.self // necessary to preserve origin when sending messages to other actors log.debug("received channel announcement for shortChannelId={} nodeId1={} nodeId2={}", c.shortChannelId, c.nodeId1, c.nodeId2) if (d.channels.contains(c.shortChannelId)) { @@ -68,13 +70,13 @@ object Validation { d } else { log.info("validating shortChannelId={}", c.shortChannelId) - watcher ! ValidateRequest(c) + watcher ! ValidateRequest(ctx.self, c) // we don't acknowledge the message just yet d.copy(awaiting = d.awaiting + (c -> Seq(origin))) } } - def handleChannelValidationResponse(d0: Data, nodeParams: NodeParams, watcher: ActorRef, r: ValidateResult)(implicit ctx: ActorContext, log: DiagnosticLoggingAdapter): Data = { + def handleChannelValidationResponse(d0: Data, nodeParams: NodeParams, watcher: typed.ActorRef[ZmqWatcher.Command], r: ValidateResult)(implicit ctx: ActorContext, log: DiagnosticLoggingAdapter): Data = { implicit val sender: ActorRef = ctx.self // necessary to preserve origin when sending messages to other actors import nodeParams.db.{network => db} import r.c @@ -103,7 +105,7 @@ object Validation { remoteOrigins_opt.foreach(_.foreach(o => sendDecision(o.peerConnection, GossipDecision.InvalidAnnouncement(c)))) None } else { - watcher ! WatchSpentBasic(ctx.self, tx.txid, outputIndex, BITCOIN_FUNDING_EXTERNAL_CHANNEL_SPENT(c.shortChannelId)) + watcher ! WatchExternalChannelSpent(ctx.self, tx.txid, outputIndex, c.shortChannelId) log.debug("added channel channelId={}", c.shortChannelId) remoteOrigins_opt.foreach(_.foreach(o => sendDecision(o.peerConnection, GossipDecision.Accepted(c)))) val capacity = tx.txOut(outputIndex).amount @@ -166,9 +168,8 @@ object Validation { } } - def handleChannelSpent(d: Data, db: NetworkDb, event: BITCOIN_FUNDING_EXTERNAL_CHANNEL_SPENT)(implicit ctx: ActorContext, log: LoggingAdapter): Data = { + def handleChannelSpent(d: Data, db: NetworkDb, shortChannelId: ShortChannelId)(implicit ctx: ActorContext, log: LoggingAdapter): Data = { implicit val sender: ActorRef = ctx.self // necessary to preserve origin when sending messages to other actors - import event.shortChannelId val lostChannel = d.channels(shortChannelId).ann log.info("funding tx of channelId={} has been spent", shortChannelId) // we need to remove nodes that aren't tied to any channels anymore @@ -399,7 +400,7 @@ object Validation { } } - def handleLocalChannelUpdate(d: Data, db: NetworkDb, routerConf: RouterConf, localNodeId: PublicKey, watcher: ActorRef, lcu: LocalChannelUpdate)(implicit ctx: ActorContext, log: LoggingAdapter): Data = { + def handleLocalChannelUpdate(d: Data, db: NetworkDb, routerConf: RouterConf, localNodeId: PublicKey, watcher: typed.ActorRef[ZmqWatcher.Command], lcu: LocalChannelUpdate)(implicit ctx: ActorContext, log: LoggingAdapter): Data = { implicit val sender: ActorRef = ctx.self // necessary to preserve origin when sending messages to other actors d.channels.get(lcu.shortChannelId) match { case Some(_) => @@ -412,7 +413,7 @@ object Validation { handleChannelUpdate(d, db, routerConf, Left(lcu)) case Some(c) => // channel wasn't announced but here is the announcement, we will process it *before* the channel_update - watcher ! ValidateRequest(c) + watcher ! ValidateRequest(ctx.self, c) val d1 = d.copy(awaiting = d.awaiting + (c -> Nil)) // no origin // maybe the local channel was pruned (can happen if we were disconnected for more than 2 weeks) db.removeFromPruned(c.shortChannelId) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/EclairImplSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/EclairImplSpec.scala index 67a493bea6..1c6eb139dc 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/EclairImplSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/EclairImplSpec.scala @@ -16,6 +16,7 @@ package fr.acinq.eclair +import akka.actor.typed.scaladsl.adapter.actorRefAdapter import akka.actor.ActorRef import akka.testkit.TestProbe import akka.util.Timeout diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/ZmqWatcherSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/ZmqWatcherSpec.scala index 1a8c55f00a..05557363ab 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/ZmqWatcherSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/ZmqWatcherSpec.scala @@ -17,23 +17,24 @@ package fr.acinq.eclair.blockchain.bitcoind import akka.Done -import akka.actor.{ActorRef, Props} +import akka.actor.typed.scaladsl.adapter.{ClassicActorSystemOps, TypedActorRefOps, actorRefAdapter} +import akka.actor.{ActorRef, Props, typed} import akka.pattern.pipe -import akka.testkit.{TestActorRef, TestProbe} -import fr.acinq.bitcoin.{Block, Btc, OutPoint, SatoshiLong, Script, Transaction, TxOut} +import akka.testkit.TestProbe +import fr.acinq.bitcoin.{Block, Btc, MilliBtcDouble, OutPoint, SatoshiLong, Script, Transaction, TxOut} import fr.acinq.eclair.blockchain.WatcherSpec._ -import fr.acinq.eclair.blockchain._ import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._ import fr.acinq.eclair.blockchain.bitcoind.rpc.ExtendedBitcoinClient import fr.acinq.eclair.blockchain.bitcoind.rpc.ExtendedBitcoinClient.{FundTransactionResponse, SignTransactionResponse} import fr.acinq.eclair.blockchain.bitcoind.zmq.ZMQActor import fr.acinq.eclair.blockchain.fee.FeeratePerKw -import fr.acinq.eclair.channel._ -import fr.acinq.eclair.{TestKitBaseClass, randomBytes32} +import fr.acinq.eclair.blockchain.{CurrentBlockCount, NewTransaction} +import fr.acinq.eclair.{ShortChannelId, TestKitBaseClass, randomBytes32} import grizzled.slf4j.Logging import org.scalatest.BeforeAndAfterAll import org.scalatest.funsuite.AnyFunSuiteLike +import java.util.UUID import java.util.concurrent.atomic.AtomicLong import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Promise @@ -65,33 +66,35 @@ class ZmqWatcherSpec extends TestKitBaseClass with AnyFunSuiteLike with Bitcoind super.afterAll() } - case class Fixture(blockCount: AtomicLong, bitcoinClient: ExtendedBitcoinClient, bitcoinWallet: BitcoinCoreWallet, watcher: TestActorRef[ZmqWatcher], probe: TestProbe) + case class Fixture(blockCount: AtomicLong, bitcoinClient: ExtendedBitcoinClient, bitcoinWallet: BitcoinCoreWallet, watcher: typed.ActorRef[ZmqWatcher.Command], probe: TestProbe, listener: TestProbe) // NB: we can't use ScalaTest's fixtures, they would see uninitialized bitcoind fields because they sandbox each test. private def withWatcher(testFun: Fixture => Any): Unit = { val blockCount = new AtomicLong() val probe = TestProbe() + val listener = TestProbe() + system.eventStream.subscribe(listener.ref, classOf[CurrentBlockCount]) val bitcoinClient = new ExtendedBitcoinClient(bitcoinrpcclient) val bitcoinWallet = new BitcoinCoreWallet(bitcoinrpcclient) - val watcher = TestActorRef[ZmqWatcher](ZmqWatcher.props(Block.RegtestGenesisBlock.hash, blockCount, bitcoinClient)) + val watcher = system.spawn(ZmqWatcher(Block.RegtestGenesisBlock.hash, blockCount, bitcoinClient), UUID.randomUUID().toString) try { - testFun(Fixture(blockCount, bitcoinClient, bitcoinWallet, watcher, probe)) + testFun(Fixture(blockCount, bitcoinClient, bitcoinWallet, watcher, probe, listener)) } finally { - system.stop(watcher) + system.stop(watcher.ref.toClassic) } } test("add/remove watches from/to utxo map") { - val m0 = Map.empty[OutPoint, Set[Watch]] + val m0 = Map.empty[OutPoint, Set[Watch[_ <: WatchTriggered]]] val txid = randomBytes32 val outputIndex = 42 val utxo = OutPoint(txid.reverse, outputIndex) - val w1 = WatchSpent(TestProbe().ref, txid, outputIndex, BITCOIN_FUNDING_SPENT, hints = Set.empty) - val w2 = WatchSpent(TestProbe().ref, txid, outputIndex, BITCOIN_FUNDING_SPENT, hints = Set.empty) - val w3 = WatchSpentBasic(TestProbe().ref, txid, outputIndex, BITCOIN_FUNDING_SPENT) - val w4 = WatchSpentBasic(TestProbe().ref, randomBytes32, 5, BITCOIN_FUNDING_SPENT) - val w5 = WatchConfirmed(TestProbe().ref, txid, 3, BITCOIN_FUNDING_SPENT) + val w1 = WatchFundingSpent(TestProbe().ref, txid, outputIndex, hints = Set.empty) + val w2 = WatchFundingSpent(TestProbe().ref, txid, outputIndex, hints = Set.empty) + val w3 = WatchExternalChannelSpent(TestProbe().ref, txid, outputIndex, ShortChannelId(1)) + val w4 = WatchExternalChannelSpent(TestProbe().ref, randomBytes32, 5, ShortChannelId(1)) + val w5 = WatchFundingConfirmed(TestProbe().ref, txid, 3) // we test as if the collection was immutable val m1 = addWatchedUtxos(m0, w1) @@ -116,6 +119,27 @@ class ZmqWatcherSpec extends TestKitBaseClass with AnyFunSuiteLike with Bitcoind assert(m10.isEmpty) } + test("send event when new block is found") { + withWatcher(f => { + import f._ + + // When the watcher starts, it broadcasts the current height. + val block1 = listener.expectMsgType[CurrentBlockCount] + assert(blockCount.get() === block1.blockCount) + listener.expectNoMessage(100 millis) + + generateBlocks(1) + assert(listener.expectMsgType[CurrentBlockCount].blockCount === block1.blockCount + 1) + assert(blockCount.get() === block1.blockCount + 1) + listener.expectNoMessage(100 millis) + + generateBlocks(5) + assert(listener.expectMsgType[CurrentBlockCount].blockCount === block1.blockCount + 6) + assert(blockCount.get() === block1.blockCount + 6) + listener.expectNoMessage(100 millis) + }) + } + test("watch for confirmed transactions") { withWatcher(f => { import f._ @@ -124,31 +148,39 @@ class ZmqWatcherSpec extends TestKitBaseClass with AnyFunSuiteLike with Bitcoind val tx = sendToAddress(address, Btc(1), probe) val listener = TestProbe() - probe.send(watcher, WatchConfirmed(listener.ref, tx.txid, 1, BITCOIN_FUNDING_DEPTHOK)) - probe.send(watcher, WatchConfirmed(listener.ref, tx.txid, 4, BITCOIN_FUNDING_DEEPLYBURIED)) - probe.send(watcher, WatchConfirmed(listener.ref, tx.txid, 4, BITCOIN_FUNDING_DEEPLYBURIED)) // setting the watch multiple times should be a no-op + watcher ! WatchFundingConfirmed(listener.ref, tx.txid, 1) + watcher ! WatchFundingDeeplyBuried(listener.ref, tx.txid, 4) + watcher ! WatchFundingDeeplyBuried(listener.ref, tx.txid, 4) // setting the watch multiple times should be a no-op listener.expectNoMsg(1 second) + watcher ! ListWatches(listener.ref) + assert(listener.expectMsgType[Set[Watch[_]]].size === 2) + generateBlocks(1) - val w1 = listener.expectMsgType[WatchEventConfirmed] - assert(w1.tx.txid === tx.txid) - assert(w1.event === BITCOIN_FUNDING_DEPTHOK) + assert(listener.expectMsgType[WatchFundingConfirmedTriggered].tx.txid === tx.txid) listener.expectNoMsg(1 second) + watcher ! ListWatches(listener.ref) + assert(listener.expectMsgType[Set[Watch[_]]].size === 1) + generateBlocks(3) - val w2 = listener.expectMsgType[WatchEventConfirmed] - assert(w2.tx.txid === tx.txid) - assert(w2.event === BITCOIN_FUNDING_DEEPLYBURIED) + assert(listener.expectMsgType[WatchFundingDeeplyBuriedTriggered].tx.txid === tx.txid) listener.expectNoMsg(1 second) + watcher ! ListWatches(listener.ref) + assert(listener.expectMsgType[Set[Watch[_]]].isEmpty) + // If we try to watch a transaction that has already been confirmed, we should immediately receive a WatchEventConfirmed. - probe.send(watcher, WatchConfirmed(listener.ref, tx.txid, 1, BITCOIN_FUNDING_DEPTHOK)) - assert(listener.expectMsgType[WatchEventConfirmed].tx.txid === tx.txid) - probe.send(watcher, WatchConfirmed(listener.ref, tx.txid, 2, BITCOIN_FUNDING_DEPTHOK)) - assert(listener.expectMsgType[WatchEventConfirmed].tx.txid === tx.txid) - probe.send(watcher, WatchConfirmed(listener.ref, tx.txid, 4, BITCOIN_FUNDING_DEEPLYBURIED)) - assert(listener.expectMsgType[WatchEventConfirmed].tx.txid === tx.txid) + watcher ! WatchFundingConfirmed(listener.ref, tx.txid, 1) + assert(listener.expectMsgType[WatchFundingConfirmedTriggered].tx.txid === tx.txid) + watcher ! WatchFundingConfirmed(listener.ref, tx.txid, 2) + assert(listener.expectMsgType[WatchFundingConfirmedTriggered].tx.txid === tx.txid) + watcher ! WatchFundingDeeplyBuried(listener.ref, tx.txid, 4) + assert(listener.expectMsgType[WatchFundingDeeplyBuriedTriggered].tx.txid === tx.txid) listener.expectNoMsg(1 second) + + watcher ! ListWatches(listener.ref) + assert(listener.expectMsgType[Set[Watch[_]]].isEmpty) }) } @@ -163,40 +195,67 @@ class ZmqWatcherSpec extends TestKitBaseClass with AnyFunSuiteLike with Bitcoind val (tx1, tx2) = createUnspentTxChain(tx, priv) val listener = TestProbe() - probe.send(watcher, WatchSpentBasic(listener.ref, tx.txid, outputIndex, BITCOIN_FUNDING_SPENT)) - probe.send(watcher, WatchSpent(listener.ref, tx.txid, outputIndex, BITCOIN_FUNDING_SPENT, hints = Set.empty)) + watcher ! WatchExternalChannelSpent(listener.ref, tx.txid, outputIndex, ShortChannelId(5)) + watcher ! WatchFundingSpent(listener.ref, tx.txid, outputIndex, Set.empty) listener.expectNoMsg(1 second) + + watcher ! ListWatches(listener.ref) + assert(listener.expectMsgType[Set[Watch[_]]].size === 2) + bitcoinClient.publishTransaction(tx1).pipeTo(probe.ref) probe.expectMsg(tx1.txid) // tx and tx1 aren't confirmed yet, but we trigger the WatchEventSpent when we see tx1 in the mempool. listener.expectMsgAllOf( - WatchEventSpentBasic(BITCOIN_FUNDING_SPENT), - WatchEventSpent(BITCOIN_FUNDING_SPENT, tx1) + WatchExternalChannelSpentTriggered(ShortChannelId(5)), + WatchFundingSpentTriggered(tx1) ) // Let's confirm tx and tx1: seeing tx1 in a block should trigger WatchEventSpent again, but not WatchEventSpentBasic // (which only triggers once). - generateBlocks(2) - listener.expectMsg(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx1)) + bitcoinClient.getBlockCount.pipeTo(listener.ref) + val initialBlockCount = listener.expectMsgType[Long] + generateBlocks(1) + listener.expectMsg(WatchFundingSpentTriggered(tx1)) + listener.expectNoMessage(1 second) + + watcher ! ListWatches(listener.ref) + val watches1 = listener.expectMsgType[Set[Watch[_]]] + assert(watches1.size === 1) + assert(watches1.forall(_.isInstanceOf[WatchFundingSpent])) // Let's submit tx2, and set a watch after it has been confirmed this time. bitcoinClient.publishTransaction(tx2).pipeTo(probe.ref) probe.expectMsg(tx2.txid) - listener.expectNoMsg(1 second) + listener.expectNoMessage(1 second) + + system.eventStream.subscribe(probe.ref, classOf[CurrentBlockCount]) generateBlocks(1) - probe.send(watcher, WatchSpentBasic(listener.ref, tx1.txid, 0, BITCOIN_FUNDING_SPENT)) - probe.send(watcher, WatchSpent(listener.ref, tx1.txid, 0, BITCOIN_FUNDING_SPENT, hints = Set.empty)) - listener.expectMsgAllOf( - WatchEventSpentBasic(BITCOIN_FUNDING_SPENT), - WatchEventSpent(BITCOIN_FUNDING_SPENT, tx2) - ) + awaitCond(probe.expectMsgType[CurrentBlockCount].blockCount >= initialBlockCount + 2) + + watcher ! WatchExternalChannelSpent(listener.ref, tx1.txid, 0, ShortChannelId(1)) + listener.expectMsg(WatchExternalChannelSpentTriggered(ShortChannelId(1))) + watcher ! WatchFundingSpent(listener.ref, tx1.txid, 0, Set.empty) + listener.expectMsg(WatchFundingSpentTriggered(tx2)) + listener.expectNoMessage(1 second) + + watcher ! ListWatches(listener.ref) + val watches2 = listener.expectMsgType[Set[Watch[_]]] + assert(watches2.size === 2) + assert(watches2.forall(_.isInstanceOf[WatchFundingSpent])) + watcher ! StopWatching(listener.ref) // We use hints and see if we can find tx2 - probe.send(watcher, WatchSpent(listener.ref, tx1.txid, 0, BITCOIN_FUNDING_SPENT, hints = Set(tx2.txid))) - listener.expectMsg(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx2)) + watcher ! WatchFundingSpent(listener.ref, tx1.txid, 0, Set(tx2.txid)) + listener.expectMsg(WatchFundingSpentTriggered(tx2)) + watcher ! StopWatching(listener.ref) // We should still find tx2 if the provided hint is wrong - probe.send(watcher, WatchSpent(listener.ref, tx1.txid, 0, BITCOIN_FUNDING_SPENT, hints = Set(randomBytes32))) - listener.expectMsg(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx2)) + watcher ! WatchOutputSpent(listener.ref, tx1.txid, 0, Set(randomBytes32)) + listener.expectMsg(WatchOutputSpentTriggered(tx2)) + watcher ! StopWatching(listener.ref) + + // We should find txs that have already been confirmed + watcher ! WatchOutputSpent(listener.ref, tx.txid, outputIndex, Set.empty) + listener.expectMsg(WatchOutputSpentTriggered(tx1)) }) } @@ -216,19 +275,85 @@ class ZmqWatcherSpec extends TestKitBaseClass with AnyFunSuiteLike with Bitcoind val tx2 = createSpendP2WPKH(tx1, priv, priv.publicKey, 10000 sat, 1, 0) // setup watches before we publish transactions - probe.send(watcher, WatchSpent(probe.ref, tx1.txid, outputIndex, BITCOIN_FUNDING_SPENT, hints = Set.empty)) - probe.send(watcher, WatchConfirmed(probe.ref, tx1.txid, 3, BITCOIN_FUNDING_SPENT)) + watcher ! WatchFundingSpent(probe.ref, tx1.txid, outputIndex, Set.empty) + watcher ! WatchFundingConfirmed(probe.ref, tx1.txid, 3) bitcoinClient.publishTransaction(tx1).pipeTo(probe.ref) probe.expectMsg(tx1.txid) generateBlocks(1) probe.expectNoMsg(1 second) bitcoinClient.publishTransaction(tx2).pipeTo(probe.ref) - probe.expectMsgAllOf(tx2.txid, WatchEventSpent(BITCOIN_FUNDING_SPENT, tx2)) + probe.expectMsgAllOf(tx2.txid, WatchFundingSpentTriggered(tx2)) probe.expectNoMsg(1 second) generateBlocks(1) - probe.expectMsg(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx2)) // tx2 is confirmed which triggers WatchEventSpent again + probe.expectMsg(WatchFundingSpentTriggered(tx2)) // tx2 is confirmed which triggers WatchEventSpent again generateBlocks(1) - assert(probe.expectMsgType[WatchEventConfirmed].tx === tx1) // tx1 now has 3 confirmations + assert(probe.expectMsgType[WatchFundingConfirmedTriggered].tx === tx1) // tx1 now has 3 confirmations + }) + } + + test("receive every transaction from new blocks through ZMQ") { + val probe = TestProbe() + val listener = TestProbe() + system.eventStream.subscribe(listener.ref, classOf[NewTransaction]) + + // we receive txs when they enter the mempool + val tx1 = sendToAddress(getNewAddress(probe), 50 millibtc, probe) + listener.expectMsg(NewTransaction(tx1)) + val tx2 = sendToAddress(getNewAddress(probe), 25 millibtc, probe) + listener.expectMsg(NewTransaction(tx2)) + listener.expectNoMessage(100 millis) + + // It may happen that transactions get included in a block without getting into our mempool first (e.g. a miner could + // try to hide a revoked commit tx from the network until it gets confirmed, in an attempt to steal funds). + // When we receive that block, we must send an event for every transaction inside it to analyze them and potentially + // trigger `WatchSpent` / `WatchSpentBasic`. + generateBlocks(1) + // NB: a miner coinbase transaction is also included in the block + val txs = Set( + listener.expectMsgType[NewTransaction], + listener.expectMsgType[NewTransaction], + listener.expectMsgType[NewTransaction] + ).map(_.tx) + listener.expectNoMessage(100 millis) + assert(txs.contains(tx1)) + assert(txs.contains(tx2)) + } + + test("stop watching when requesting actor dies") { + withWatcher(f => { + import f._ + + val actor1 = TestProbe() + val actor2 = TestProbe() + + val txid = randomBytes32 + watcher ! WatchFundingConfirmed(actor1.ref, txid, 2) + watcher ! WatchFundingConfirmed(actor1.ref, txid, 3) + watcher ! WatchFundingDeeplyBuried(actor1.ref, txid, 3) + watcher ! WatchFundingConfirmed(actor1.ref, txid.reverse, 3) + watcher ! WatchOutputSpent(actor1.ref, txid, 0, Set.empty) + watcher ! WatchOutputSpent(actor1.ref, txid, 1, Set.empty) + watcher ! ListWatches(actor1.ref) + val watches1 = actor1.expectMsgType[Set[Watch[_]]] + assert(watches1.size === 6) + + watcher ! WatchFundingConfirmed(actor2.ref, txid, 2) + watcher ! WatchFundingDeeplyBuried(actor2.ref, txid, 3) + watcher ! WatchFundingConfirmed(actor2.ref, txid.reverse, 3) + watcher ! WatchOutputSpent(actor2.ref, txid, 0, Set.empty) + watcher ! WatchOutputSpent(actor2.ref, txid, 1, Set.empty) + watcher ! ListWatches(actor2.ref) + val watches2 = actor2.expectMsgType[Set[Watch[_]]] + assert(watches2.size === 11) + assert(watches1.forall(w => watches2.contains(w))) + + watcher ! StopWatching(actor2.ref) + watcher ! ListWatches(actor1.ref) + actor1.expectMsg(watches1) + + watcher ! StopWatching(actor1.ref) + watcher ! ListWatches(actor1.ref) + assert(actor1.expectMsgType[Set[Watch[_]]].isEmpty) }) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/ChannelTypesSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/ChannelTypesSpec.scala index eadc42da27..b8a34492be 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/ChannelTypesSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/ChannelTypesSpec.scala @@ -2,7 +2,7 @@ package fr.acinq.eclair.channel import akka.testkit.{TestFSMRef, TestProbe} import fr.acinq.bitcoin.{ByteVector32, OutPoint, SatoshiLong, Transaction, TxIn, TxOut} -import fr.acinq.eclair.blockchain.WatchEventSpent +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.WatchFundingSpentTriggered import fr.acinq.eclair.channel.Helpers.Closing import fr.acinq.eclair.channel.states.StateTestsHelperMethods import fr.acinq.eclair.transactions.Transactions @@ -88,7 +88,7 @@ class ChannelTypesSpec extends TestKitBaseClass with AnyFunSuiteLike with StateT awaitCond(alice.stateName == CLOSING) // Bob detects it. - bob ! WatchEventSpent(BITCOIN_FUNDING_SPENT, alice.stateData.asInstanceOf[DATA_CLOSING].localCommitPublished.get.commitTx) + bob ! WatchFundingSpentTriggered(alice.stateData.asInstanceOf[DATA_CLOSING].localCommitPublished.get.commitTx) awaitCond(bob.stateName == CLOSING) Fixture(alice, HtlcWithPreimage(rb2, htlcb2), bob, HtlcWithPreimage(ra2, htlca2), TestProbe()) @@ -371,7 +371,7 @@ class ChannelTypesSpec extends TestKitBaseClass with AnyFunSuiteLike with StateT val lcp = aliceClosing.localCommitPublished.get // Bob detects it. - bob ! WatchEventSpent(BITCOIN_FUNDING_SPENT, alice.stateData.asInstanceOf[DATA_CLOSING].localCommitPublished.get.commitTx) + bob ! WatchFundingSpentTriggered(alice.stateData.asInstanceOf[DATA_CLOSING].localCommitPublished.get.commitTx) awaitCond(bob.stateName == CLOSING) val bobClosing = bob.stateData.asInstanceOf[DATA_CLOSING] @@ -483,7 +483,7 @@ class ChannelTypesSpec extends TestKitBaseClass with AnyFunSuiteLike with StateT fulfillHtlc(htlca1.id, ra1, bob, alice, bob2alice, alice2bob) crossSign(bob, alice, bob2alice, alice2bob) - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, revokedCommitTx) + alice ! WatchFundingSpentTriggered(revokedCommitTx) awaitCond(alice.stateName == CLOSING) val aliceClosing = alice.stateData.asInstanceOf[DATA_CLOSING] assert(aliceClosing.revokedCommitPublished.length === 1) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/FuzzySpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/FuzzySpec.scala index 93d41800d6..79083c8817 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/FuzzySpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/FuzzySpec.scala @@ -16,13 +16,15 @@ package fr.acinq.eclair.channel +import akka.actor.typed.scaladsl.adapter.actorRefAdapter import akka.actor.{Actor, ActorLogging, ActorRef, Props} import akka.testkit.{TestFSMRef, TestProbe} import fr.acinq.bitcoin.ByteVector32 import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.eclair.TestConstants.{Alice, Bob} import fr.acinq.eclair._ -import fr.acinq.eclair.blockchain._ +import fr.acinq.eclair.blockchain.TestWallet +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._ import fr.acinq.eclair.channel.states.StateTestsBase import fr.acinq.eclair.channel.states.StateTestsHelperMethods.FakeTxPublisherFactory import fr.acinq.eclair.payment.OutgoingPacket.Upstream @@ -83,17 +85,17 @@ class FuzzySpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with StateT bob2blockchain.expectMsgType[TxPublisher.SetChannelId] pipe ! (alice, bob) alice2blockchain.expectMsgType[TxPublisher.SetChannelId] - alice2blockchain.expectMsgType[WatchSpent] - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchFundingSpent] + alice2blockchain.expectMsgType[WatchFundingConfirmed] bob2blockchain.expectMsgType[TxPublisher.SetChannelId] - bob2blockchain.expectMsgType[WatchSpent] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchFundingSpent] + bob2blockchain.expectMsgType[WatchFundingConfirmed] awaitCond(alice.stateName == WAIT_FOR_FUNDING_CONFIRMED) val fundingTx = alice.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED].fundingTx.get - alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEPTHOK, 400000, 42, fundingTx) - bob ! WatchEventConfirmed(BITCOIN_FUNDING_DEPTHOK, 400000, 42, fundingTx) - alice2blockchain.expectMsgType[WatchLost] - bob2blockchain.expectMsgType[WatchLost] + alice ! WatchFundingConfirmedTriggered(400000, 42, fundingTx) + bob ! WatchFundingConfirmedTriggered(400000, 42, fundingTx) + alice2blockchain.expectMsgType[WatchFundingLost] + bob2blockchain.expectMsgType[WatchFundingLost] awaitCond(alice.stateName == NORMAL, 1 minute) awaitCond(bob.stateName == NORMAL, 1 minute) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/HelpersSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/HelpersSpec.scala index c77f0cf9c4..fabc2b0ac9 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/HelpersSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/HelpersSpec.scala @@ -20,7 +20,7 @@ import akka.testkit.{TestFSMRef, TestProbe} import fr.acinq.bitcoin.{Btc, OutPoint, SatoshiLong, Transaction, TxOut} import fr.acinq.eclair.TestConstants.Alice.nodeParams import fr.acinq.eclair.TestUtils.NoLoggingDiagnostics -import fr.acinq.eclair.blockchain.WatchEventSpent +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.WatchFundingSpentTriggered import fr.acinq.eclair.channel.Helpers.Closing import fr.acinq.eclair.channel.states.{StateTestsHelperMethods, StateTestsTags} import fr.acinq.eclair.transactions.Transactions._ @@ -91,7 +91,7 @@ class HelpersSpec extends TestKitBaseClass with AnyFunSuiteLike with StateTestsH awaitCond(alice.stateName == CLOSING) // Bob detects it. - bob ! WatchEventSpent(BITCOIN_FUNDING_SPENT, alice.stateData.asInstanceOf[DATA_CLOSING].localCommitPublished.get.commitTx) + bob ! WatchFundingSpentTriggered(alice.stateData.asInstanceOf[DATA_CLOSING].localCommitPublished.get.commitTx) awaitCond(bob.stateName == CLOSING) val lcp = alice.stateData.asInstanceOf[DATA_CLOSING].localCommitPublished.get diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/RecoverySpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/RecoverySpec.scala index 835a8454b5..67d38171f6 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/RecoverySpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/RecoverySpec.scala @@ -4,7 +4,7 @@ import akka.testkit.TestProbe import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.bitcoin._ import fr.acinq.eclair.TestConstants.Alice -import fr.acinq.eclair.blockchain.WatchEventSpent +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.WatchFundingSpentTriggered import fr.acinq.eclair.channel.states.StateTestsBase import fr.acinq.eclair.crypto.Generators import fr.acinq.eclair.crypto.keymanager.ChannelKeyManager @@ -84,7 +84,7 @@ class RecoverySpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with Sta val bobCommitTx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx // actual tests starts here: let's see what we can do with Bob's commit tx - sender.send(alice, WatchEventSpent(BITCOIN_FUNDING_SPENT, bobCommitTx)) + sender.send(alice, WatchFundingSpentTriggered(bobCommitTx)) // from Bob's commit tx we can extract both funding public keys val OP_2 :: OP_PUSHDATA(pub1, _) :: OP_PUSHDATA(pub2, _) :: OP_2 :: OP_CHECKMULTISIG :: Nil = Script.parse(bobCommitTx.txIn(0).witness.stack.last) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/TxPublisherSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/TxPublisherSpec.scala index 6d762c0fbe..b75374e970 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/TxPublisherSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/TxPublisherSpec.scala @@ -16,7 +16,7 @@ package fr.acinq.eclair.channel -import akka.actor.typed.scaladsl.adapter.{ClassicActorSystemOps, TypedActorRefOps} +import akka.actor.typed.scaladsl.adapter.{ClassicActorSystemOps, TypedActorRefOps, actorRefAdapter} import akka.pattern.pipe import akka.testkit.{TestFSMRef, TestProbe} import fr.acinq.bitcoin.{BtcAmount, ByteVector32, MilliBtcDouble, OutPoint, SIGHASH_ALL, SatoshiLong, Script, ScriptFlags, ScriptWitness, SigVersion, Transaction, TxIn, TxOut} @@ -25,8 +25,8 @@ import fr.acinq.eclair.blockchain.WatcherSpec.createSpendP2WPKH import fr.acinq.eclair.blockchain.bitcoind.rpc.ExtendedBitcoinClient import fr.acinq.eclair.blockchain.bitcoind.rpc.ExtendedBitcoinClient.{FundTransactionResponse, MempoolTx, SignTransactionResponse} import fr.acinq.eclair.blockchain.bitcoind.{BitcoinCoreWallet, BitcoindService} +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{WatchOutputSpent, WatchParentTxConfirmed, WatchTxConfirmed} import fr.acinq.eclair.blockchain.fee.{FeeratePerKw, FeeratesPerKw} -import fr.acinq.eclair.blockchain.{WatchConfirmed, WatchSpent} import fr.acinq.eclair.channel.TxPublisher._ import fr.acinq.eclair.channel.states.{StateTestsHelperMethods, StateTestsTags} import fr.acinq.eclair.transactions.Transactions.{ClaimLocalAnchorOutputTx, HtlcSuccessTx, HtlcTimeoutTx, addSigs} @@ -155,22 +155,22 @@ class TxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike with Bitcoin // tx2 has a relative delay but no absolute delay val tx2 = createSpendP2WPKH(tx1, priv, priv.publicKey, 10000 sat, sequence = 2, lockTime = 0) txPublisher ! PublishRawTx(tx2, "child-tx") - val watchParentTx2 = alice2blockchain.expectMsgType[WatchConfirmed] + val watchParentTx2 = alice2blockchain.expectMsgType[WatchParentTxConfirmed] assert(watchParentTx2.txId === tx1.txid) assert(watchParentTx2.minDepth === 2) createBlocks(2) - txPublisher ! ParentTxConfirmed(watchParentTx2.event.asInstanceOf[BITCOIN_PARENT_TX_CONFIRMED].childTx, tx1.txid) + txPublisher ! ParentTxConfirmed(watchParentTx2.childTx, tx1.txid) awaitCond(getMempool.exists(_.txid === tx2.txid), max = 20 seconds, interval = 1 second) // tx3 has both relative and absolute delays val tx3 = createSpendP2WPKH(tx2, priv, priv.publicKey, 10000 sat, sequence = 1, lockTime = blockCount.get + 5) txPublisher ! PublishRawTx(tx3, "grand-child-tx") - val watchParentTx3 = alice2blockchain.expectMsgType[WatchConfirmed] + val watchParentTx3 = alice2blockchain.expectMsgType[WatchParentTxConfirmed] assert(watchParentTx3.txId === tx2.txid) assert(watchParentTx3.minDepth === 1) // after 1 block, the relative delay is elapsed, but not the absolute delay createBlocks(1) - txPublisher ! ParentTxConfirmed(watchParentTx3.event.asInstanceOf[BITCOIN_PARENT_TX_CONFIRMED].childTx, tx2.txid) + txPublisher ! ParentTxConfirmed(watchParentTx3.childTx, tx2.txid) assert(!getMempool.exists(_.txid === tx3.txid)) // after 4 more blocks, the absolute delay is elapsed createBlocks(4) @@ -215,21 +215,20 @@ class TxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike with Bitcoin Transaction.correctlySpends(tx, parentTx1 :: parentTx2 :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS) txPublisher ! PublishRawTx(tx, "child-tx") val watches = Seq( - alice2blockchain.expectMsgType[WatchConfirmed], - alice2blockchain.expectMsgType[WatchConfirmed], + alice2blockchain.expectMsgType[WatchParentTxConfirmed], + alice2blockchain.expectMsgType[WatchParentTxConfirmed], ) - watches.foreach(w => assert(w.event.isInstanceOf[BITCOIN_PARENT_TX_CONFIRMED])) val w1 = watches.find(_.txId == parentTx1.txid).get assert(w1.minDepth === 2) val w2 = watches.find(_.txId == parentTx2.txid).get assert(w2.minDepth === 4) - alice2blockchain.expectNoMsg(1 second) + alice2blockchain.expectNoMessage(1 second) createBlocks(2) - txPublisher ! ParentTxConfirmed(w1.event.asInstanceOf[BITCOIN_PARENT_TX_CONFIRMED].childTx, w1.txId) + txPublisher ! ParentTxConfirmed(w1.childTx, w1.txId) assert(!getMempool.exists(_.txid === tx.txid)) createBlocks(2) - txPublisher ! ParentTxConfirmed(w2.event.asInstanceOf[BITCOIN_PARENT_TX_CONFIRMED].childTx, w2.txId) + txPublisher ! ParentTxConfirmed(w2.childTx, w2.txId) awaitCond(getMempool.exists(_.txid === tx.txid)) }) } @@ -407,10 +406,10 @@ class TxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike with Bitcoin val htlcTimeout = alice2blockchain.expectMsgType[SignAndPublishTx] assert(htlcTimeout.txInfo.isInstanceOf[HtlcTimeoutTx]) - alice2blockchain.expectMsgType[WatchConfirmed] // commit tx - alice2blockchain.expectMsgType[WatchConfirmed] // claim main output - alice2blockchain.expectMsgType[WatchSpent] // htlc-success tx - alice2blockchain.expectMsgType[WatchSpent] // htlc-timeout tx + alice2blockchain.expectMsgType[WatchTxConfirmed] // commit tx + alice2blockchain.expectMsgType[WatchTxConfirmed] // claim main output + alice2blockchain.expectMsgType[WatchOutputSpent] // htlc-success tx + alice2blockchain.expectMsgType[WatchOutputSpent] // htlc-timeout tx alice2blockchain.expectNoMessage(100 millis) (commitTx.tx, htlcSuccess, htlcTimeout) @@ -555,9 +554,9 @@ class TxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike with Bitcoin alice2blockchain.expectMsg(PublishRawTx(commitTx)) val anchorTx = alice2blockchain.expectMsgType[SignAndPublishTx] alice2blockchain.expectMsgType[PublishRawTx] // claim main output - alice2blockchain.expectMsgType[WatchConfirmed] // commit tx - alice2blockchain.expectMsgType[WatchConfirmed] // claim main output - alice2blockchain.expectMsgType[WatchSpent] // alice doesn't have the preimage yet to redeem the htlc but she watches the output + alice2blockchain.expectMsgType[WatchTxConfirmed] // commit tx + alice2blockchain.expectMsgType[WatchTxConfirmed] // claim main output + alice2blockchain.expectMsgType[WatchOutputSpent] // alice doesn't have the preimage yet to redeem the htlc but she watches the output alice2blockchain.expectNoMessage(100 millis) // Publish and confirm the commit tx. @@ -573,13 +572,13 @@ class TxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike with Bitcoin assert(anchorTx2.txInfo === anchorTx.txInfo) alice2blockchain.expectMsgType[PublishRawTx] // claim main output val htlcSuccess = alice2blockchain.expectMsgType[SignAndPublishTx] - alice2blockchain.expectMsgType[WatchConfirmed] // commit tx - alice2blockchain.expectMsgType[WatchConfirmed] // claim main output - alice2blockchain.expectMsgType[WatchSpent] // htlc output + alice2blockchain.expectMsgType[WatchTxConfirmed] // commit tx + alice2blockchain.expectMsgType[WatchTxConfirmed] // claim main output + alice2blockchain.expectMsgType[WatchOutputSpent] // htlc output alice2blockchain.expectNoMessage(100 millis) txPublisher ! htlcSuccess - val w = alice2blockchain.expectMsgType[WatchConfirmed] + val w = alice2blockchain.expectMsgType[WatchParentTxConfirmed] assert(w.txId === commitTx.tx.txid) assert(w.minDepth === 1) txPublisher ! ParentTxConfirmed(htlcSuccess, commitTx.tx.txid) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/StateTestsHelperMethods.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/StateTestsHelperMethods.scala index c04ef45143..4482963e85 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/StateTestsHelperMethods.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/StateTestsHelperMethods.scala @@ -22,8 +22,9 @@ import akka.testkit.{TestFSMRef, TestKitBase, TestProbe} import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.bitcoin.{ByteVector32, Crypto, SatoshiLong, ScriptFlags, Transaction} import fr.acinq.eclair.TestConstants.{Alice, Bob, TestFeeEstimator} -import fr.acinq.eclair.blockchain._ +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._ import fr.acinq.eclair.blockchain.fee.FeeTargets +import fr.acinq.eclair.blockchain.{EclairWallet, TestWallet} import fr.acinq.eclair.channel.TxPublisher.{PublishRawTx, PublishTx, SignAndPublishTx} import fr.acinq.eclair.channel._ import fr.acinq.eclair.channel.states.StateTestsHelperMethods.FakeTxPublisherFactory @@ -147,23 +148,23 @@ trait StateTestsHelperMethods extends TestKitBase { bob2alice.expectMsgType[FundingSigned] bob2alice.forward(alice) assert(alice2blockchain.expectMsgType[TxPublisher.SetChannelId].channelId != ByteVector32.Zeroes) - alice2blockchain.expectMsgType[WatchSpent] - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchFundingSpent] + alice2blockchain.expectMsgType[WatchFundingConfirmed] assert(bob2blockchain.expectMsgType[TxPublisher.SetChannelId].channelId != ByteVector32.Zeroes) - bob2blockchain.expectMsgType[WatchSpent] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchFundingSpent] + bob2blockchain.expectMsgType[WatchFundingConfirmed] awaitCond(alice.stateName == WAIT_FOR_FUNDING_CONFIRMED) val fundingTx = alice.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED].fundingTx.get - alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEPTHOK, 400000, 42, fundingTx) - bob ! WatchEventConfirmed(BITCOIN_FUNDING_DEPTHOK, 400000, 42, fundingTx) - alice2blockchain.expectMsgType[WatchLost] - bob2blockchain.expectMsgType[WatchLost] + alice ! WatchFundingConfirmedTriggered(400000, 42, fundingTx) + bob ! WatchFundingConfirmedTriggered(400000, 42, fundingTx) + alice2blockchain.expectMsgType[WatchFundingLost] + bob2blockchain.expectMsgType[WatchFundingLost] alice2bob.expectMsgType[FundingLocked] alice2bob.forward(bob) bob2alice.expectMsgType[FundingLocked] bob2alice.forward(alice) - alice2blockchain.expectMsgType[WatchConfirmed] // deeply buried - bob2blockchain.expectMsgType[WatchConfirmed] // deeply buried + alice2blockchain.expectMsgType[WatchFundingDeeplyBuried] + bob2blockchain.expectMsgType[WatchFundingDeeplyBuried] awaitCond(alice.stateName == NORMAL) awaitCond(bob.stateName == NORMAL) assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.availableBalanceForSend == (pushMsat - aliceParams.channelReserve).max(0 msat)) @@ -259,9 +260,9 @@ trait StateTestsHelperMethods extends TestKitBase { r2s.forward(s) } while (sCloseFee != rCloseFee) s2blockchain.expectMsgType[PublishTx] - s2blockchain.expectMsgType[WatchConfirmed] + s2blockchain.expectMsgType[WatchTxConfirmed] r2blockchain.expectMsgType[PublishTx] - r2blockchain.expectMsgType[WatchConfirmed] + r2blockchain.expectMsgType[WatchTxConfirmed] awaitCond(s.stateName == CLOSING) awaitCond(r.stateName == CLOSING) // both nodes are now in CLOSING state with a mutual close tx pending for confirmation @@ -297,13 +298,12 @@ trait StateTestsHelperMethods extends TestKitBase { } // we watch the confirmation of the "final" transactions that send funds to our wallets (main delayed output and 2nd stage htlc transactions) - assert(s2blockchain.expectMsgType[WatchConfirmed].event == BITCOIN_TX_CONFIRMED(commitTx)) - localCommitPublished.claimMainDelayedOutputTx.foreach(claimMain => assert(s2blockchain.expectMsgType[WatchConfirmed].event == BITCOIN_TX_CONFIRMED(claimMain.tx))) + assert(s2blockchain.expectMsgType[WatchTxConfirmed].txId === commitTx.txid) + localCommitPublished.claimMainDelayedOutputTx.foreach(claimMain => assert(s2blockchain.expectMsgType[WatchTxConfirmed].txId === claimMain.tx.txid)) // we watch outputs of the commitment tx that both parties may spend val htlcOutputIndexes = localCommitPublished.htlcTxs.keySet.map(_.index) - val spentWatches = htlcOutputIndexes.map(_ => s2blockchain.expectMsgType[WatchSpent]) - spentWatches.foreach(ws => assert(ws.event == BITCOIN_OUTPUT_SPENT)) + val spentWatches = htlcOutputIndexes.map(_ => s2blockchain.expectMsgType[WatchOutputSpent]) spentWatches.foreach(ws => assert(ws.txId == commitTx.txid)) assert(spentWatches.map(_.outputIndex) == htlcOutputIndexes) s2blockchain.expectNoMsg(1 second) @@ -314,7 +314,7 @@ trait StateTestsHelperMethods extends TestKitBase { def remoteClose(rCommitTx: Transaction, s: TestFSMRef[State, Data, Channel], s2blockchain: TestProbe): RemoteCommitPublished = { // we make s believe r unilaterally closed the channel - s ! WatchEventSpent(BITCOIN_FUNDING_SPENT, rCommitTx) + s ! WatchFundingSpentTriggered(rCommitTx) awaitCond(s.stateName == CLOSING) val closingData = s.stateData.asInstanceOf[DATA_CLOSING] val remoteCommitPublished_opt = closingData.remoteCommitPublished.orElse(closingData.nextRemoteCommitPublished).orElse(closingData.futureRemoteCommitPublished) @@ -333,13 +333,12 @@ trait StateTestsHelperMethods extends TestKitBase { s2blockchain.expectMsgAllOf(claimHtlcTxs.map(claimHtlc => PublishRawTx(claimHtlc)): _*) // we watch the confirmation of the "final" transactions that send funds to our wallets (main delayed output and 2nd stage htlc transactions) - assert(s2blockchain.expectMsgType[WatchConfirmed].event == BITCOIN_TX_CONFIRMED(rCommitTx)) - remoteCommitPublished.claimMainOutputTx.foreach(claimMain => assert(s2blockchain.expectMsgType[WatchConfirmed].event == BITCOIN_TX_CONFIRMED(claimMain.tx))) + assert(s2blockchain.expectMsgType[WatchTxConfirmed].txId === rCommitTx.txid) + remoteCommitPublished.claimMainOutputTx.foreach(claimMain => assert(s2blockchain.expectMsgType[WatchTxConfirmed].txId === claimMain.tx.txid)) // we watch outputs of the commitment tx that both parties may spend val htlcOutputIndexes = remoteCommitPublished.claimHtlcTxs.keySet.map(_.index) - val spentWatches = htlcOutputIndexes.map(_ => s2blockchain.expectMsgType[WatchSpent]) - spentWatches.foreach(ws => assert(ws.event == BITCOIN_OUTPUT_SPENT)) + val spentWatches = htlcOutputIndexes.map(_ => s2blockchain.expectMsgType[WatchOutputSpent]) spentWatches.foreach(ws => assert(ws.txId == rCommitTx.txid)) assert(spentWatches.map(_.outputIndex) == htlcOutputIndexes) s2blockchain.expectNoMsg(1 second) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/b/WaitForFundingCreatedStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/b/WaitForFundingCreatedStateSpec.scala index 84b05cbd4a..41bc574c3c 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/b/WaitForFundingCreatedStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/b/WaitForFundingCreatedStateSpec.scala @@ -20,7 +20,7 @@ import akka.actor.ActorRef import akka.testkit.{TestFSMRef, TestProbe} import fr.acinq.bitcoin.{Btc, ByteVector32, SatoshiLong} import fr.acinq.eclair.TestConstants.{Alice, Bob} -import fr.acinq.eclair.blockchain._ +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._ import fr.acinq.eclair.channel._ import fr.acinq.eclair.channel.states.{StateTestsBase, StateTestsTags} import fr.acinq.eclair.transactions.Transactions @@ -81,8 +81,8 @@ class WaitForFundingCreatedStateSpec extends TestKitBaseClass with FixtureAnyFun awaitCond(bob.stateName == WAIT_FOR_FUNDING_CONFIRMED) bob2alice.expectMsgType[FundingSigned] bob2blockchain.expectMsgType[TxPublisher.SetChannelId] - bob2blockchain.expectMsgType[WatchSpent] - val watchConfirmed = bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchFundingSpent] + val watchConfirmed = bob2blockchain.expectMsgType[WatchFundingConfirmed] assert(watchConfirmed.minDepth === Alice.nodeParams.minDepthBlocks) } @@ -93,8 +93,8 @@ class WaitForFundingCreatedStateSpec extends TestKitBaseClass with FixtureAnyFun awaitCond(bob.stateName == WAIT_FOR_FUNDING_CONFIRMED) bob2alice.expectMsgType[FundingSigned] bob2blockchain.expectMsgType[TxPublisher.SetChannelId] - bob2blockchain.expectMsgType[WatchSpent] - val watchConfirmed = bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchFundingSpent] + val watchConfirmed = bob2blockchain.expectMsgType[WatchFundingConfirmed] // when we are fundee, we use a higher min depth for wumbo channels assert(watchConfirmed.minDepth > Bob.nodeParams.minDepthBlocks) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/b/WaitForFundingSignedStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/b/WaitForFundingSignedStateSpec.scala index 8b1029f144..97bb20cceb 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/b/WaitForFundingSignedStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/b/WaitForFundingSignedStateSpec.scala @@ -19,7 +19,8 @@ package fr.acinq.eclair.channel.states.b import akka.testkit.{TestFSMRef, TestProbe} import fr.acinq.bitcoin.{Btc, ByteVector32, ByteVector64} import fr.acinq.eclair.TestConstants.{Alice, Bob} -import fr.acinq.eclair.blockchain._ +import fr.acinq.eclair.blockchain.TestWallet +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._ import fr.acinq.eclair.channel.Channel.TickChannelOpenTimeout import fr.acinq.eclair.channel._ import fr.acinq.eclair.channel.states.{StateTestsBase, StateTestsTags} @@ -79,8 +80,8 @@ class WaitForFundingSignedStateSpec extends TestKitBaseClass with FixtureAnyFunS bob2alice.expectMsgType[FundingSigned] bob2alice.forward(alice) awaitCond(alice.stateName == WAIT_FOR_FUNDING_CONFIRMED) - alice2blockchain.expectMsgType[WatchSpent] - val watchConfirmed = alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchFundingSpent] + val watchConfirmed = alice2blockchain.expectMsgType[WatchFundingConfirmed] assert(watchConfirmed.minDepth === Alice.nodeParams.minDepthBlocks) } @@ -89,8 +90,8 @@ class WaitForFundingSignedStateSpec extends TestKitBaseClass with FixtureAnyFunS bob2alice.expectMsgType[FundingSigned] bob2alice.forward(alice) awaitCond(alice.stateName == WAIT_FOR_FUNDING_CONFIRMED) - alice2blockchain.expectMsgType[WatchSpent] - val watchConfirmed = alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchFundingSpent] + val watchConfirmed = alice2blockchain.expectMsgType[WatchFundingConfirmed] // when we are funder, we keep our regular min depth even for wumbo channels assert(watchConfirmed.minDepth === Alice.nodeParams.minDepthBlocks) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/c/WaitForFundingConfirmedStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/c/WaitForFundingConfirmedStateSpec.scala index a4feca32f5..51f8ceb199 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/c/WaitForFundingConfirmedStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/c/WaitForFundingConfirmedStateSpec.scala @@ -19,7 +19,9 @@ package fr.acinq.eclair.channel.states.c import akka.testkit.{TestFSMRef, TestProbe} import fr.acinq.bitcoin.{ByteVector32, SatoshiLong, Script, Transaction} import fr.acinq.eclair.TestConstants.{Alice, Bob} -import fr.acinq.eclair.blockchain._ +import fr.acinq.eclair.blockchain.CurrentBlockCount +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._ +import fr.acinq.eclair.channel.Channel.{BITCOIN_FUNDING_PUBLISH_FAILED, BITCOIN_FUNDING_TIMEOUT} import fr.acinq.eclair.channel.TxPublisher.{PublishRawTx, PublishTx} import fr.acinq.eclair.channel._ import fr.acinq.eclair.channel.states.StateTestsBase @@ -59,8 +61,8 @@ class WaitForFundingConfirmedStateSpec extends TestKitBaseClass with FixtureAnyF bob2alice.expectMsgType[FundingSigned] bob2alice.forward(alice) alice2blockchain.expectMsgType[TxPublisher.SetChannelId] - alice2blockchain.expectMsgType[WatchSpent] - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchFundingSpent] + alice2blockchain.expectMsgType[WatchFundingConfirmed] awaitCond(alice.stateName == WAIT_FOR_FUNDING_CONFIRMED) withFixture(test.toNoArgTest(FixtureParam(alice, bob, alice2bob, bob2alice, alice2blockchain))) } @@ -70,37 +72,37 @@ class WaitForFundingConfirmedStateSpec extends TestKitBaseClass with FixtureAnyF import f._ // make bob send a FundingLocked msg val fundingTx = alice.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED].fundingTx.get - bob ! WatchEventConfirmed(BITCOIN_FUNDING_DEPTHOK, 42000, 42, fundingTx) + bob ! WatchFundingConfirmedTriggered(42000, 42, fundingTx) val msg = bob2alice.expectMsgType[FundingLocked] bob2alice.forward(alice) awaitCond(alice.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED].deferred.contains(msg)) awaitCond(alice.stateName == WAIT_FOR_FUNDING_CONFIRMED) } - test("recv BITCOIN_FUNDING_DEPTHOK") { f => + test("recv WatchFundingConfirmedTriggered") { f => import f._ val fundingTx = alice.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED].fundingTx.get - alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEPTHOK, 42000, 42, fundingTx) + alice ! WatchFundingConfirmedTriggered(42000, 42, fundingTx) awaitCond(alice.stateName == WAIT_FOR_FUNDING_LOCKED) - alice2blockchain.expectMsgType[WatchLost] + alice2blockchain.expectMsgType[WatchFundingLost] alice2bob.expectMsgType[FundingLocked] } - test("recv BITCOIN_FUNDING_DEPTHOK (bad funding pubkey script)") { f => + test("recv WatchFundingConfirmedTriggered (bad funding pubkey script)") { f => import f._ val fundingTx = alice.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED].fundingTx.get val badOutputScript = fundingTx.txOut.head.copy(publicKeyScript = Script.write(multiSig2of2(randomKey.publicKey, randomKey.publicKey))) val badFundingTx = fundingTx.copy(txOut = Seq(badOutputScript)) - alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEPTHOK, 42000, 42, badFundingTx) + alice ! WatchFundingConfirmedTriggered(42000, 42, badFundingTx) awaitCond(alice.stateName == CLOSED) } - test("recv BITCOIN_FUNDING_DEPTHOK (bad funding amount)") { f => + test("recv WatchFundingConfirmedTriggered (bad funding amount)") { f => import f._ val fundingTx = alice.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED].fundingTx.get val badOutputAmount = fundingTx.txOut.head.copy(amount = 1234567.sat) val badFundingTx = fundingTx.copy(txOut = Seq(badOutputAmount)) - alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEPTHOK, 42000, 42, badFundingTx) + alice ! WatchFundingConfirmedTriggered(42000, 42, badFundingTx) awaitCond(alice.stateName == CLOSED) } @@ -159,20 +161,20 @@ class WaitForFundingConfirmedStateSpec extends TestKitBaseClass with FixtureAnyF assert(bob.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED].waitingSinceBlock === currentBlockHeight) } - test("recv BITCOIN_FUNDING_SPENT (remote commit)") { f => + test("recv WatchFundingSpentTriggered (remote commit)") { f => import f._ // bob publishes his commitment tx val tx = bob.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED].commitments.localCommit.publishableTxs.commitTx.tx - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, tx) + alice ! WatchFundingSpentTriggered(tx) alice2blockchain.expectMsgType[PublishTx] - alice2blockchain.expectMsgType[WatchConfirmed] + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === tx.txid) awaitCond(alice.stateName == CLOSING) } - test("recv BITCOIN_FUNDING_SPENT (other commit)") { f => + test("recv WatchFundingSpentTriggered (other commit)") { f => import f._ val tx = alice.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED].commitments.localCommit.publishableTxs.commitTx.tx - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, Transaction(0, Nil, Nil, 0)) + alice ! WatchFundingSpentTriggered(Transaction(0, Nil, Nil, 0)) alice2bob.expectMsgType[Error] assert(alice2blockchain.expectMsgType[PublishRawTx].tx === tx) awaitCond(alice.stateName == ERR_INFORMATION_LEAK) @@ -185,7 +187,7 @@ class WaitForFundingConfirmedStateSpec extends TestKitBaseClass with FixtureAnyF awaitCond(alice.stateName == CLOSING) assert(alice2blockchain.expectMsgType[PublishRawTx].tx === tx) alice2blockchain.expectMsgType[PublishTx] // claim-main-delayed - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(tx)) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === tx.txid) } test("recv CMD_CLOSE") { f => @@ -204,7 +206,7 @@ class WaitForFundingConfirmedStateSpec extends TestKitBaseClass with FixtureAnyF awaitCond(alice.stateName == CLOSING) assert(alice2blockchain.expectMsgType[PublishRawTx].tx === tx) alice2blockchain.expectMsgType[PublishTx] // claim-main-delayed - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(tx)) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === tx.txid) } } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/c/WaitForFundingLockedStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/c/WaitForFundingLockedStateSpec.scala index bca805655f..ce61db1f10 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/c/WaitForFundingLockedStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/c/WaitForFundingLockedStateSpec.scala @@ -19,7 +19,7 @@ package fr.acinq.eclair.channel.states.c import akka.testkit.{TestFSMRef, TestProbe} import fr.acinq.bitcoin.{ByteVector32, Transaction} import fr.acinq.eclair.TestConstants.{Alice, Bob} -import fr.acinq.eclair.blockchain._ +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._ import fr.acinq.eclair.channel.TxPublisher.{PublishRawTx, PublishTx} import fr.acinq.eclair.channel._ import fr.acinq.eclair.channel.states.StateTestsBase @@ -60,17 +60,17 @@ class WaitForFundingLockedStateSpec extends TestKitBaseClass with FixtureAnyFunS bob2alice.expectMsgType[FundingSigned] bob2alice.forward(alice) alice2blockchain.expectMsgType[TxPublisher.SetChannelId] - alice2blockchain.expectMsgType[WatchSpent] - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchFundingSpent] + alice2blockchain.expectMsgType[WatchFundingConfirmed] bob2blockchain.expectMsgType[TxPublisher.SetChannelId] - bob2blockchain.expectMsgType[WatchSpent] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchFundingSpent] + bob2blockchain.expectMsgType[WatchFundingConfirmed] awaitCond(alice.stateName == WAIT_FOR_FUNDING_CONFIRMED) val fundingTx = alice.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED].fundingTx.get - alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEPTHOK, 400000, 42, fundingTx) - bob ! WatchEventConfirmed(BITCOIN_FUNDING_DEPTHOK, 400000, 42, fundingTx) - alice2blockchain.expectMsgType[WatchLost] - bob2blockchain.expectMsgType[WatchLost] + alice ! WatchFundingConfirmedTriggered(400000, 42, fundingTx) + bob ! WatchFundingConfirmedTriggered(400000, 42, fundingTx) + alice2blockchain.expectMsgType[WatchFundingLost] + bob2blockchain.expectMsgType[WatchFundingLost] alice2bob.expectMsgType[FundingLocked] awaitCond(alice.stateName == WAIT_FOR_FUNDING_LOCKED) awaitCond(bob.stateName == WAIT_FOR_FUNDING_LOCKED) @@ -89,20 +89,20 @@ class WaitForFundingLockedStateSpec extends TestKitBaseClass with FixtureAnyFunS bob2alice.expectNoMsg(200 millis) } - test("recv BITCOIN_FUNDING_SPENT (remote commit)") { f => + test("recv WatchFundingSpentTriggered (remote commit)") { f => import f._ // bob publishes his commitment tx val tx = bob.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_LOCKED].commitments.localCommit.publishableTxs.commitTx.tx - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, tx) + alice ! WatchFundingSpentTriggered(tx) alice2blockchain.expectMsgType[PublishTx] - alice2blockchain.expectMsgType[WatchConfirmed] + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === tx.txid) awaitCond(alice.stateName == CLOSING) } - test("recv BITCOIN_FUNDING_SPENT (other commit)") { f => + test("recv WatchFundingSpentTriggered (other commit)") { f => import f._ val tx = alice.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_LOCKED].commitments.localCommit.publishableTxs.commitTx.tx - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, Transaction(0, Nil, Nil, 0)) + alice ! WatchFundingSpentTriggered(Transaction(0, Nil, Nil, 0)) alice2bob.expectMsgType[Error] assert(alice2blockchain.expectMsgType[PublishRawTx].tx === tx) alice2blockchain.expectMsgType[PublishTx] @@ -116,7 +116,7 @@ class WaitForFundingLockedStateSpec extends TestKitBaseClass with FixtureAnyFunS awaitCond(alice.stateName == CLOSING) assert(alice2blockchain.expectMsgType[PublishRawTx].tx === tx) alice2blockchain.expectMsgType[PublishTx] - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(tx)) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === tx.txid) } test("recv CMD_CLOSE") { f => @@ -135,6 +135,6 @@ class WaitForFundingLockedStateSpec extends TestKitBaseClass with FixtureAnyFunS awaitCond(alice.stateName == CLOSING) assert(alice2blockchain.expectMsgType[PublishRawTx].tx === tx) alice2blockchain.expectMsgType[PublishTx] - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(tx)) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === tx.txid) } } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala index c69c8e9df9..b5bf05f854 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala @@ -24,7 +24,8 @@ import fr.acinq.eclair.Features.StaticRemoteKey import fr.acinq.eclair.TestConstants.{Alice, Bob} import fr.acinq.eclair.UInt64.Conversions._ import fr.acinq.eclair._ -import fr.acinq.eclair.blockchain._ +import fr.acinq.eclair.blockchain.{CurrentBlockCount, CurrentFeerates} +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._ import fr.acinq.eclair.blockchain.fee.{FeeratePerByte, FeeratePerKw, FeeratesPerKw} import fr.acinq.eclair.channel.Channel._ import fr.acinq.eclair.channel.TxPublisher.{PublishRawTx, PublishTx} @@ -446,7 +447,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with awaitCond(bob.stateName == CLOSING) assert(bob2blockchain.expectMsgType[PublishRawTx].tx === tx) bob2blockchain.expectMsgType[PublishTx] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] } test("recv UpdateAddHtlc (value too small)") { f => @@ -461,7 +462,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === bob.stateData.asInstanceOf[DATA_CLOSING].channelId) assert(bob2blockchain.expectMsgType[PublishRawTx].tx === tx) bob2blockchain.expectMsgType[PublishTx] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] } test("recv UpdateAddHtlc (insufficient funds)") { f => @@ -476,7 +477,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === bob.stateData.asInstanceOf[DATA_CLOSING].channelId) assert(bob2blockchain.expectMsgType[PublishRawTx].tx === tx) bob2blockchain.expectMsgType[PublishTx] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] } test("recv UpdateAddHtlc (insufficient funds w/ pending htlcs) (anchor outputs)", Tag(StateTestsTags.AnchorOutputs)) { f => @@ -507,7 +508,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === bob.stateData.asInstanceOf[DATA_CLOSING].channelId) assert(bob2blockchain.expectMsgType[PublishRawTx].tx === tx) bob2blockchain.expectMsgType[PublishTx] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] } test("recv UpdateAddHtlc (insufficient funds w/ pending htlcs 2/2)") { f => @@ -523,7 +524,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === bob.stateData.asInstanceOf[DATA_CLOSING].channelId) assert(bob2blockchain.expectMsgType[PublishRawTx].tx === tx) bob2blockchain.expectMsgType[PublishTx] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] } test("recv UpdateAddHtlc (over max inflight htlc value)") { f => @@ -537,7 +538,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === alice.stateData.asInstanceOf[DATA_CLOSING].channelId) assert(alice2blockchain.expectMsgType[PublishRawTx].tx === tx) alice2blockchain.expectMsgType[PublishTx] - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchTxConfirmed] } test("recv UpdateAddHtlc (over max accepted htlcs)") { f => @@ -555,7 +556,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === bob.stateData.asInstanceOf[DATA_CLOSING].channelId) assert(bob2blockchain.expectMsgType[PublishRawTx].tx === tx) bob2blockchain.expectMsgType[PublishTx] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] } test("recv CMD_SIGN") { f => @@ -869,7 +870,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === bob.stateData.asInstanceOf[DATA_CLOSING].channelId) assert(bob2blockchain.expectMsgType[PublishRawTx].tx === tx) bob2blockchain.expectMsgType[PublishTx] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] } test("recv CommitSig (invalid signature)") { f => @@ -884,7 +885,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with awaitCond(bob.stateName == CLOSING) assert(bob2blockchain.expectMsgType[PublishRawTx].tx === tx) bob2blockchain.expectMsgType[PublishTx] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] } test("recv CommitSig (bad htlc sig count)") { f => @@ -903,7 +904,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(new String(error.data.toArray) === HtlcSigCountMismatch(channelId(bob), expected = 1, actual = 2).getMessage) assert(bob2blockchain.expectMsgType[PublishRawTx].tx === tx) bob2blockchain.expectMsgType[PublishTx] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] } test("recv CommitSig (invalid htlc sig)") { f => @@ -922,7 +923,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(new String(error.data.toArray).startsWith("invalid htlc signature")) assert(bob2blockchain.expectMsgType[PublishRawTx].tx === tx) bob2blockchain.expectMsgType[PublishTx] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] } test("recv RevokeAndAck (one htlc sent)") { f => @@ -1034,7 +1035,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === alice.stateData.asInstanceOf[DATA_CLOSING].channelId) assert(alice2blockchain.expectMsgType[PublishRawTx].tx === tx) alice2blockchain.expectMsgType[PublishTx] - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchTxConfirmed] } test("recv RevokeAndAck (unexpectedly)") { f => @@ -1048,7 +1049,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === alice.stateData.asInstanceOf[DATA_CLOSING].channelId) assert(alice2blockchain.expectMsgType[PublishRawTx].tx === tx) alice2blockchain.expectMsgType[PublishTx] - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchTxConfirmed] } test("recv RevokeAndAck (forward UpdateFailHtlc)") { f => @@ -1288,7 +1289,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === alice.stateData.asInstanceOf[DATA_CLOSING].channelId) assert(alice2blockchain.expectMsgType[PublishRawTx].tx === tx) alice2blockchain.expectMsgType[PublishTx] - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchTxConfirmed] } test("recv UpdateFulfillHtlc (unknown htlc id)") { f => @@ -1301,7 +1302,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === alice.stateData.asInstanceOf[DATA_CLOSING].channelId) assert(alice2blockchain.expectMsgType[PublishRawTx].tx === tx) alice2blockchain.expectMsgType[PublishTx] - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchTxConfirmed] } test("recv UpdateFulfillHtlc (invalid preimage)") { f => @@ -1320,7 +1321,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(alice2blockchain.expectMsgType[PublishRawTx].tx === tx) alice2blockchain.expectMsgType[PublishTx] // main delayed alice2blockchain.expectMsgType[PublishTx] // htlc timeout - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchTxConfirmed] } private def testCmdFailHtlc(f: FixtureParam): Unit = { @@ -1490,7 +1491,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(alice2blockchain.expectMsgType[PublishRawTx].tx === tx) // commit tx alice2blockchain.expectMsgType[PublishTx] // main delayed alice2blockchain.expectMsgType[PublishTx] // htlc timeout - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchTxConfirmed] } test("recv UpdateFailHtlc (sender has not signed htlc)") { f => @@ -1508,7 +1509,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === alice.stateData.asInstanceOf[DATA_CLOSING].channelId) assert(alice2blockchain.expectMsgType[PublishRawTx].tx === tx) alice2blockchain.expectMsgType[PublishTx] - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchTxConfirmed] } test("recv UpdateFailHtlc (unknown htlc id)") { f => @@ -1521,7 +1522,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === alice.stateData.asInstanceOf[DATA_CLOSING].channelId) assert(alice2blockchain.expectMsgType[PublishRawTx].tx === tx) alice2blockchain.expectMsgType[PublishTx] - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchTxConfirmed] } test("recv UpdateFailHtlc (invalid onion error length)") { f => @@ -1613,7 +1614,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === alice.stateData.asInstanceOf[DATA_CLOSING].channelId) assert(alice2blockchain.expectMsgType[PublishRawTx].tx === tx) alice2blockchain.expectMsgType[PublishTx] - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchTxConfirmed] } test("recv UpdateFee (sender can't afford it)") { f => @@ -1630,7 +1631,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === bob.stateData.asInstanceOf[DATA_CLOSING].channelId) assert(bob2blockchain.expectMsgType[PublishRawTx].tx === tx) // commit tx //bob2blockchain.expectMsgType[PublishTx] // main delayed (removed because of the high fees) - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] } test("recv UpdateFee (sender can't afford it, anchor outputs)", Tag(StateTestsTags.AnchorOutputs)) { f => @@ -1664,7 +1665,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === bob.stateData.asInstanceOf[DATA_CLOSING].channelId) assert(bob2blockchain.expectMsgType[PublishRawTx].tx === commitTx) bob2blockchain.expectMsgType[PublishTx] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] } test("recv UpdateFee (remote feerate is too high, anchor outputs)", Tag(StateTestsTags.AnchorOutputs)) { f => @@ -1719,7 +1720,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === bob.stateData.asInstanceOf[DATA_CLOSING].channelId) assert(bob2blockchain.expectMsgType[PublishRawTx].tx === tx) bob2blockchain.expectMsgType[PublishTx] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] } test("recv CMD_UPDATE_RELAY_FEE ") { f => @@ -1895,7 +1896,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with bob2alice.expectMsgType[Error] bob2blockchain.expectMsgType[PublishTx] bob2blockchain.expectMsgType[PublishTx] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] awaitCond(bob.stateName == CLOSING) } @@ -1933,7 +1934,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with bob2alice.expectMsgType[Error] bob2blockchain.expectMsgType[PublishTx] bob2blockchain.expectMsgType[PublishTx] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] awaitCond(bob.stateName == CLOSING) } @@ -1949,7 +1950,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with bob2alice.expectMsgType[Error] bob2blockchain.expectMsgType[PublishTx] bob2blockchain.expectMsgType[PublishTx] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] awaitCond(bob.stateName == CLOSING) } @@ -2051,8 +2052,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with alice2blockchain.expectMsgType[PublishTx] // main delayed alice2blockchain.expectMsgType[PublishTx] // htlc timeout - val watch = alice2blockchain.expectMsgType[WatchConfirmed] - assert(watch.event === BITCOIN_TX_CONFIRMED(aliceCommitTx)) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === aliceCommitTx.txid) } test("recv CurrentBlockCount (fulfilled signed htlc ignored by upstream peer)") { f => @@ -2084,7 +2084,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(bob2blockchain.expectMsgType[PublishRawTx].tx === initialCommitTx) bob2blockchain.expectMsgType[PublishTx] // main delayed assert(bob2blockchain.expectMsgType[PublishTx].tx.txOut === htlcSuccessTx.txOut) - assert(bob2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(initialCommitTx)) + assert(bob2blockchain.expectMsgType[WatchTxConfirmed].txId === initialCommitTx.txid) alice2blockchain.expectNoMsg(500 millis) } @@ -2117,7 +2117,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(bob2blockchain.expectMsgType[PublishRawTx].tx === initialCommitTx) bob2blockchain.expectMsgType[PublishTx] // main delayed assert(bob2blockchain.expectMsgType[PublishTx].tx.txOut === htlcSuccessTx.txOut) - assert(bob2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(initialCommitTx)) + assert(bob2blockchain.expectMsgType[WatchTxConfirmed].txId === initialCommitTx.txid) alice2blockchain.expectNoMsg(500 millis) } @@ -2154,7 +2154,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(bob2blockchain.expectMsgType[PublishRawTx].tx === initialCommitTx) bob2blockchain.expectMsgType[PublishTx] // main delayed assert(bob2blockchain.expectMsgType[PublishTx].tx.txOut === htlcSuccessTx.txOut) - assert(bob2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(initialCommitTx)) + assert(bob2blockchain.expectMsgType[WatchTxConfirmed].txId === initialCommitTx.txid) alice2blockchain.expectNoMsg(500 millis) } @@ -2208,7 +2208,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with bob2alice.expectMsgType[Error] bob2blockchain.expectMsgType[PublishTx] // commit tx bob2blockchain.expectMsgType[PublishTx] // main delayed - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] awaitCond(bob.stateName == CLOSING) } @@ -2247,11 +2247,11 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with bob2alice.expectMsgType[Error] bob2blockchain.expectMsgType[PublishTx] // commit tx bob2blockchain.expectMsgType[PublishTx] // main delayed - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] awaitCond(bob.stateName == CLOSING) } - test("recv BITCOIN_FUNDING_SPENT (their commit w/ htlc)") { f => + test("recv WatchFundingSpentTriggered (their commit w/ htlc)") { f => import f._ val (ra1, htlca1) = addHtlc(250000000 msat, alice, bob, alice2bob, bob2alice) @@ -2277,7 +2277,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // bob publishes his current commit tx val bobCommitTx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx assert(bobCommitTx.txOut.size == 6) // two main outputs and 4 pending htlcs - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobCommitTx) + alice ! WatchFundingSpentTriggered(bobCommitTx) // in response to that, alice publishes its claim txs val claimTxs = for (_ <- 0 until 4) yield alice2blockchain.expectMsgType[PublishTx].tx @@ -2292,12 +2292,12 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // at best we have a little less than 450 000 + 250 000 + 100 000 + 50 000 = 850 000 (because fees) assert(amountClaimed === 814880.sat) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(bobCommitTx)) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(claimMain)) // claim-main - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) // htlc 1 - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) // htlc 2 - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) // htlc 3 - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) // htlc 4 + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === bobCommitTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === claimMain.txid) + alice2blockchain.expectMsgType[WatchOutputSpent] // htlc 1 + alice2blockchain.expectMsgType[WatchOutputSpent] // htlc 2 + alice2blockchain.expectMsgType[WatchOutputSpent] // htlc 3 + alice2blockchain.expectMsgType[WatchOutputSpent] // htlc 4 alice2blockchain.expectNoMsg(1 second) awaitCond(alice.stateName == CLOSING) @@ -2314,7 +2314,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(claimFee == expectedFee) } - test("recv BITCOIN_FUNDING_SPENT (their *next* commit w/ htlc)") { f => + test("recv WatchFundingSpentTriggered (their *next* commit w/ htlc)") { f => import f._ val (ra1, htlca1) = addHtlc(250000000 msat, alice, bob, alice2bob, bob2alice) @@ -2347,7 +2347,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // bob publishes his current commit tx val bobCommitTx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx assert(bobCommitTx.txOut.size == 5) // two main outputs and 3 pending htlcs - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobCommitTx) + alice ! WatchFundingSpentTriggered(bobCommitTx) // in response to that, alice publishes its claim txs val claimTxs = for (i <- 0 until 3) yield alice2blockchain.expectMsgType[PublishTx].tx @@ -2361,11 +2361,11 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // at best we have a little less than 500 000 + 250 000 + 100 000 = 850 000 (because fees) assert(amountClaimed === 822310.sat) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(bobCommitTx)) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(claimTxs(0))) // claim-main - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) // htlc 1 - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) // htlc 2 - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) // htlc 3 + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === bobCommitTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === claimTxs(0).txid) // claim-main + alice2blockchain.expectMsgType[WatchOutputSpent] // htlc 1 + alice2blockchain.expectMsgType[WatchOutputSpent] // htlc 2 + alice2blockchain.expectMsgType[WatchOutputSpent] // htlc 3 alice2blockchain.expectNoMsg(1 second) awaitCond(alice.stateName == CLOSING) @@ -2375,7 +2375,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(getClaimHtlcTimeoutTxs(rcp).length === 2) } - test("recv BITCOIN_FUNDING_SPENT (revoked commit)") { f => + test("recv WatchFundingSpentTriggered (revoked commit)") { f => import f._ // initially we have : // alice = 800 000 @@ -2402,18 +2402,18 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // a->b = 10 000 // two main outputs + 4 htlc assert(revokedTx.txOut.size == 6) - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, revokedTx) + alice ! WatchFundingSpentTriggered(revokedTx) alice2bob.expectMsgType[Error] val mainTx = alice2blockchain.expectMsgType[PublishTx].tx val mainPenaltyTx = alice2blockchain.expectMsgType[PublishTx].tx val htlcPenaltyTxs = for (_ <- 0 until 4) yield alice2blockchain.expectMsgType[PublishTx].tx - assert(alice2blockchain.expectMsgType[WatchConfirmed].event == BITCOIN_TX_CONFIRMED(revokedTx)) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event == BITCOIN_TX_CONFIRMED(mainTx)) - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) // main-penalty + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === revokedTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === mainTx.txid) + alice2blockchain.expectMsgType[WatchOutputSpent] // main-penalty // let's make sure that htlc-penalty txs each spend a different output assert(htlcPenaltyTxs.map(_.txIn.head.outPoint.index).toSet.size === htlcPenaltyTxs.size) - htlcPenaltyTxs.foreach(_ => assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT)) + htlcPenaltyTxs.foreach(_ => alice2blockchain.expectMsgType[WatchOutputSpent]) alice2blockchain.expectNoMsg(1 second) Transaction.correctlySpends(mainTx, Seq(revokedTx), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS) @@ -2432,7 +2432,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(alice.stateData.asInstanceOf[DATA_CLOSING].revokedCommitPublished.size == 1) } - test("recv BITCOIN_FUNDING_SPENT (revoked commit with identical htlcs)") { f => + test("recv WatchFundingSpentTriggered (revoked commit with identical htlcs)") { f => import f._ val sender = TestProbe() @@ -2466,7 +2466,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // a->b = 10 000 // a->b = 10 000 assert(revokedTx.txOut.size == 4) - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, revokedTx) + alice ! WatchFundingSpentTriggered(revokedTx) alice2bob.expectMsgType[Error] val mainTx = alice2blockchain.expectMsgType[PublishTx].tx @@ -2474,10 +2474,10 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with val htlcPenaltyTxs = for (_ <- 0 until 2) yield alice2blockchain.expectMsgType[PublishTx].tx // let's make sure that htlc-penalty txs each spend a different output assert(htlcPenaltyTxs.map(_.txIn.head.outPoint.index).toSet.size === htlcPenaltyTxs.size) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event == BITCOIN_TX_CONFIRMED(revokedTx)) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event == BITCOIN_TX_CONFIRMED(mainTx)) - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) // main-penalty - htlcPenaltyTxs.foreach(_ => assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT)) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === revokedTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === mainTx.txid) + alice2blockchain.expectMsgType[WatchOutputSpent] // main-penalty + htlcPenaltyTxs.foreach(_ => alice2blockchain.expectMsgType[WatchOutputSpent]) alice2blockchain.expectNoMsg(1 second) Transaction.correctlySpends(mainTx, Seq(revokedTx), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS) @@ -2533,22 +2533,22 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // the main delayed output and htlc txs spend the commitment transaction Seq(claimMain, htlcTx1, htlcTx2, htlcTx3).foreach(tx => Transaction.correctlySpends(tx, aliceCommitTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(aliceCommitTx)) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(claimMain)) // main-delayed - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) // htlc 1 - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) // htlc 2 - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) // htlc 3 - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) // htlc 4 + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === aliceCommitTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === claimMain.txid) // main-delayed + alice2blockchain.expectMsgType[WatchOutputSpent] // htlc 1 + alice2blockchain.expectMsgType[WatchOutputSpent] // htlc 2 + alice2blockchain.expectMsgType[WatchOutputSpent] // htlc 3 + alice2blockchain.expectMsgType[WatchOutputSpent] // htlc 4 alice2blockchain.expectNoMsg(1 second) // 3rd-stage txs are published when htlc txs confirm Seq(htlcTx1, htlcTx2, htlcTx3).foreach(htlcTimeoutTx => { - alice ! WatchEventSpent(BITCOIN_OUTPUT_SPENT, htlcTimeoutTx) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(htlcTimeoutTx)) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(htlcTimeoutTx), 2701, 3, htlcTimeoutTx) + alice ! WatchOutputSpentTriggered(htlcTimeoutTx) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === htlcTimeoutTx.txid) + alice ! WatchTxConfirmedTriggered(2701, 3, htlcTimeoutTx) val claimHtlcDelayedTx = alice2blockchain.expectMsgType[PublishTx].tx Transaction.correctlySpends(claimHtlcDelayedTx, htlcTimeoutTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(claimHtlcDelayedTx)) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === claimHtlcDelayedTx.txid) }) awaitCond(alice.stateData.asInstanceOf[DATA_CLOSING].localCommitPublished.get.claimHtlcDelayedTxs.length == 3) alice2blockchain.expectNoMsg(1 second) @@ -2573,9 +2573,9 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(localCommitPublished.commitTx == bobCommitTx) } - test("recv BITCOIN_FUNDING_DEEPLYBURIED", Tag(StateTestsTags.ChannelsPublic)) { f => + test("recv WatchFundingDeeplyBuriedTriggered", Tag(StateTestsTags.ChannelsPublic)) { f => import f._ - alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400000, 42, null) + alice ! WatchFundingDeeplyBuriedTriggered(400000, 42, null) val annSigs = alice2bob.expectMsgType[AnnouncementSignatures] // public channel: we don't send the channel_update directly to the peer alice2bob.expectNoMsg(1 second) @@ -2584,9 +2584,9 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with channelUpdateListener.expectNoMsg(1 second) } - test("recv BITCOIN_FUNDING_DEEPLYBURIED (short channel id changed)", Tag(StateTestsTags.ChannelsPublic)) { f => + test("recv WatchFundingDeeplyBuriedTriggered (short channel id changed)", Tag(StateTestsTags.ChannelsPublic)) { f => import f._ - alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400001, 22, null) + alice ! WatchFundingDeeplyBuriedTriggered(400001, 22, null) val annSigs = alice2bob.expectMsgType[AnnouncementSignatures] // public channel: we don't send the channel_update directly to the peer alice2bob.expectNoMsg(1 second) @@ -2595,9 +2595,9 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with channelUpdateListener.expectNoMsg(1 second) } - test("recv BITCOIN_FUNDING_DEEPLYBURIED (private channel)") { f => + test("recv WatchFundingDeeplyBuriedTriggered (private channel)") { f => import f._ - alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400000, 42, null) + alice ! WatchFundingDeeplyBuriedTriggered(400000, 42, null) // private channel: we send the channel_update directly to the peer val channelUpdate = alice2bob.expectMsgType[ChannelUpdate] awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].shortChannelId == channelUpdate.shortChannelId && alice.stateData.asInstanceOf[DATA_NORMAL].buried) @@ -2605,9 +2605,9 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with channelUpdateListener.expectNoMsg(1 second) } - test("recv BITCOIN_FUNDING_DEEPLYBURIED (private channel, short channel id changed)") { f => + test("recv WatchFundingDeeplyBuriedTriggered (private channel, short channel id changed)") { f => import f._ - alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400001, 22, null) + alice ! WatchFundingDeeplyBuriedTriggered(400001, 22, null) // private channel: we send the channel_update directly to the peer val channelUpdate = alice2bob.expectMsgType[ChannelUpdate] awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].shortChannelId == channelUpdate.shortChannelId && alice.stateData.asInstanceOf[DATA_NORMAL].buried) @@ -2619,9 +2619,9 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with test("recv AnnouncementSignatures", Tag(StateTestsTags.ChannelsPublic)) { f => import f._ val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] - alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400000, 42, null) + alice ! WatchFundingDeeplyBuriedTriggered(400000, 42, null) val annSigsA = alice2bob.expectMsgType[AnnouncementSignatures] - bob ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400000, 42, null) + bob ! WatchFundingDeeplyBuriedTriggered(400000, 42, null) val annSigsB = bob2alice.expectMsgType[AnnouncementSignatures] import initialState.commitments.{localParams, remoteParams} val channelAnn = Announcements.makeChannelAnnouncement(Alice.nodeParams.chainHash, annSigsA.shortChannelId, Alice.nodeParams.nodeId, remoteParams.nodeId, Alice.channelKeyManager.fundingPublicKey(localParams.fundingKeyPath).publicKey, remoteParams.fundingPubKey, annSigsA.nodeSignature, annSigsB.nodeSignature, annSigsA.bitcoinSignature, annSigsB.bitcoinSignature) @@ -2637,9 +2637,9 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with test("recv AnnouncementSignatures (re-send)", Tag(StateTestsTags.ChannelsPublic)) { f => import f._ val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] - alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 42, 10, null) + alice ! WatchFundingDeeplyBuriedTriggered(42, 10, null) val annSigsA = alice2bob.expectMsgType[AnnouncementSignatures] - bob ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 42, 10, null) + bob ! WatchFundingDeeplyBuriedTriggered(42, 10, null) val annSigsB = bob2alice.expectMsgType[AnnouncementSignatures] import initialState.commitments.{localParams, remoteParams} val channelAnn = Announcements.makeChannelAnnouncement(Alice.nodeParams.chainHash, annSigsA.shortChannelId, Alice.nodeParams.nodeId, remoteParams.nodeId, Alice.channelKeyManager.fundingPublicKey(localParams.fundingKeyPath).publicKey, remoteParams.fundingPubKey, annSigsA.nodeSignature, annSigsB.nodeSignature, annSigsA.bitcoinSignature, annSigsB.bitcoinSignature) @@ -2656,9 +2656,9 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with test("recv AnnouncementSignatures (invalid)", Tag(StateTestsTags.ChannelsPublic)) { f => import f._ val channelId = alice.stateData.asInstanceOf[DATA_NORMAL].channelId - alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400000, 42, null) + alice ! WatchFundingDeeplyBuriedTriggered(400000, 42, null) alice2bob.expectMsgType[AnnouncementSignatures] - bob ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400000, 42, null) + bob ! WatchFundingDeeplyBuriedTriggered(400000, 42, null) val annSigsB = bob2alice.expectMsgType[AnnouncementSignatures] // actual test starts here - Bob sends an invalid signature val annSigsB_invalid = annSigsB.copy(bitcoinSignature = annSigsB.nodeSignature, nodeSignature = annSigsB.bitcoinSignature) @@ -2671,8 +2671,8 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with test("recv BroadcastChannelUpdate", Tag(StateTestsTags.ChannelsPublic)) { f => import f._ - alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400000, 42, null) - bob ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400000, 42, null) + alice ! WatchFundingDeeplyBuriedTriggered(400000, 42, null) + bob ! WatchFundingDeeplyBuriedTriggered(400000, 42, null) bob2alice.expectMsgType[AnnouncementSignatures] bob2alice.forward(alice) val update1 = channelUpdateListener.expectMsgType[LocalChannelUpdate] @@ -2686,8 +2686,8 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with test("recv BroadcastChannelUpdate (no changes)", Tag(StateTestsTags.ChannelsPublic)) { f => import f._ - alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400000, 42, null) - bob ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400000, 42, null) + alice ! WatchFundingDeeplyBuriedTriggered(400000, 42, null) + bob ! WatchFundingDeeplyBuriedTriggered(400000, 42, null) bob2alice.expectMsgType[AnnouncementSignatures] bob2alice.forward(alice) channelUpdateListener.expectMsgType[LocalChannelUpdate] @@ -2700,7 +2700,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with test("recv INPUT_DISCONNECTED") { f => import f._ - alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400000, 42, null) + alice ! WatchFundingDeeplyBuriedTriggered(400000, 42, null) val update1a = alice2bob.expectMsgType[ChannelUpdate] assert(Announcements.isEnabled(update1a.channelFlags)) @@ -2714,7 +2714,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with test("recv INPUT_DISCONNECTED (with pending unsigned htlcs)") { f => import f._ val sender = TestProbe() - alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400000, 42, null) + alice ! WatchFundingDeeplyBuriedTriggered(400000, 42, null) val update1a = alice2bob.expectMsgType[ChannelUpdate] assert(Announcements.isEnabled(update1a.channelFlags)) val (_, htlc1) = addHtlc(10000 msat, alice, bob, alice2bob, bob2alice, sender.ref) @@ -2735,8 +2735,8 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with test("recv INPUT_DISCONNECTED (public channel)", Tag(StateTestsTags.ChannelsPublic)) { f => import f._ - alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400000, 42, null) - bob ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400000, 42, null) + alice ! WatchFundingDeeplyBuriedTriggered(400000, 42, null) + bob ! WatchFundingDeeplyBuriedTriggered(400000, 42, null) bob2alice.expectMsgType[AnnouncementSignatures] bob2alice.forward(alice) val update1 = channelUpdateListener.expectMsgType[LocalChannelUpdate] @@ -2751,8 +2751,8 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with test("recv INPUT_DISCONNECTED (public channel, with pending unsigned htlcs)", Tag(StateTestsTags.ChannelsPublic)) { f => import f._ val sender = TestProbe() - alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400000, 42, null) - bob ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400000, 42, null) + alice ! WatchFundingDeeplyBuriedTriggered(400000, 42, null) + bob ! WatchFundingDeeplyBuriedTriggered(400000, 42, null) bob2alice.expectMsgType[AnnouncementSignatures] bob2alice.forward(alice) alice2bob.expectMsgType[AnnouncementSignatures] diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/OfflineStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/OfflineStateSpec.scala index 8dae3f9f88..c9be28c037 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/OfflineStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/OfflineStateSpec.scala @@ -21,7 +21,8 @@ import akka.testkit.{TestFSMRef, TestProbe} import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} import fr.acinq.bitcoin.{ByteVector32, ScriptFlags, Transaction} import fr.acinq.eclair.TestConstants.{Alice, Bob, TestFeeEstimator} -import fr.acinq.eclair.blockchain._ +import fr.acinq.eclair.blockchain.{CurrentBlockCount, CurrentFeerates} +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._ import fr.acinq.eclair.blockchain.fee.FeeratesPerKw import fr.acinq.eclair.channel.TxPublisher.{PublishRawTx, PublishTx} import fr.acinq.eclair.channel._ @@ -271,7 +272,7 @@ class OfflineStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // bob is nice and publishes its commitment val bobCommitTx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobCommitTx) + alice ! WatchFundingSpentTriggered(bobCommitTx) // alice is able to claim its main output val claimMainOutput = alice2blockchain.expectMsgType[PublishTx].tx @@ -316,7 +317,7 @@ class OfflineStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // bob is nice and publishes its commitment val bobCommitTx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobCommitTx) + alice ! WatchFundingSpentTriggered(bobCommitTx) // alice is able to claim its main output val claimMainOutput = alice2blockchain.expectMsgType[PublishTx].tx @@ -485,16 +486,16 @@ class OfflineStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(bob2blockchain.expectMsgType[PublishRawTx].tx === initialCommitTx) bob2blockchain.expectMsgType[PublishTx] // main delayed - assert(bob2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(initialCommitTx)) - bob2blockchain.expectMsgType[WatchConfirmed] // main delayed - bob2blockchain.expectMsgType[WatchSpent] // htlc + assert(bob2blockchain.expectMsgType[WatchTxConfirmed].txId === initialCommitTx.txid) + bob2blockchain.expectMsgType[WatchTxConfirmed] // main delayed + bob2blockchain.expectMsgType[WatchOutputSpent] // htlc assert(bob2blockchain.expectMsgType[PublishRawTx].tx === initialCommitTx) bob2blockchain.expectMsgType[PublishTx] // main delayed assert(bob2blockchain.expectMsgType[PublishTx].tx.txOut === htlcSuccessTx.txOut) - assert(bob2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(initialCommitTx)) - bob2blockchain.expectMsgType[WatchConfirmed] // main delayed - bob2blockchain.expectMsgType[WatchSpent] // htlc + assert(bob2blockchain.expectMsgType[WatchTxConfirmed].txId === initialCommitTx.txid) + bob2blockchain.expectMsgType[WatchTxConfirmed] // main delayed + bob2blockchain.expectMsgType[WatchOutputSpent] // htlc bob2blockchain.expectNoMsg(500 millis) } @@ -695,8 +696,8 @@ class OfflineStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with bob2alice.expectNoMsg() // funding tx gets 6 confirmations, channel is private so there is no announcement sigs - alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400000, 42, null) - bob ! WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400000, 42, null) + alice ! WatchFundingDeeplyBuriedTriggered(400000, 42, null) + bob ! WatchFundingDeeplyBuriedTriggered(400000, 42, null) alice2bob.expectMsgType[ChannelUpdate] bob2alice.expectMsgType[ChannelUpdate] diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala index acb29237bb..1cfe596047 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala @@ -19,7 +19,8 @@ package fr.acinq.eclair.channel.states.f import akka.testkit.TestProbe import fr.acinq.bitcoin.Crypto.PrivateKey import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Crypto, SatoshiLong, ScriptFlags, Transaction} -import fr.acinq.eclair.blockchain._ +import fr.acinq.eclair.blockchain.{CurrentBlockCount, CurrentFeerates} +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._ import fr.acinq.eclair.blockchain.fee.{FeeratePerKw, FeeratesPerKw} import fr.acinq.eclair.channel.TxPublisher.{PublishRawTx, PublishTx} import fr.acinq.eclair.channel._ @@ -188,7 +189,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit alice2blockchain.expectMsgType[PublishTx] // main delayed alice2blockchain.expectMsgType[PublishTx] // htlc timeout 1 alice2blockchain.expectMsgType[PublishTx] // htlc timeout 2 - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchTxConfirmed] } test("recv UpdateFulfillHtlc (invalid preimage)") { f => @@ -201,7 +202,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit alice2blockchain.expectMsgType[PublishTx] // main delayed alice2blockchain.expectMsgType[PublishTx] // htlc timeout 1 alice2blockchain.expectMsgType[PublishTx] // htlc timeout 2 - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchTxConfirmed] } test("recv CMD_FAIL_HTLC") { f => @@ -292,7 +293,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit alice2blockchain.expectMsgType[PublishTx] // main delayed alice2blockchain.expectMsgType[PublishTx] // htlc timeout 1 alice2blockchain.expectMsgType[PublishTx] // htlc timeout 2 - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchTxConfirmed] } test("recv UpdateFailMalformedHtlc") { f => @@ -315,7 +316,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit alice2blockchain.expectMsgType[PublishTx] // main delayed alice2blockchain.expectMsgType[PublishTx] // htlc timeout 1 alice2blockchain.expectMsgType[PublishTx] // htlc timeout 2 - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchTxConfirmed] } test("recv CMD_SIGN") { f => @@ -381,7 +382,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit awaitCond(bob.stateName == CLOSING) assert(bob2blockchain.expectMsgType[PublishRawTx].tx === tx) // commit tx bob2blockchain.expectMsgType[PublishTx] // main delayed - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] } test("recv CommitSig (invalid signature)") { f => @@ -392,7 +393,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit awaitCond(bob.stateName == CLOSING) assert(bob2blockchain.expectMsgType[PublishRawTx].tx === tx) // commit tx bob2blockchain.expectMsgType[PublishTx] // main delayed - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] } test("recv RevokeAndAck (with remaining htlcs on both sides)") { f => @@ -449,7 +450,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit assert(bob2blockchain.expectMsgType[PublishRawTx].tx === tx) // commit tx bob2blockchain.expectMsgType[PublishTx] // main delayed bob2blockchain.expectMsgType[PublishTx] // htlc success - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] } test("recv RevokeAndAck (unexpectedly)") { f => @@ -463,7 +464,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit alice2blockchain.expectMsgType[PublishTx] // main delayed alice2blockchain.expectMsgType[PublishTx] // htlc timeout 1 alice2blockchain.expectMsgType[PublishTx] // htlc timeout 2 - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchTxConfirmed] } test("recv RevokeAndAck (forward UpdateFailHtlc)") { f => @@ -551,7 +552,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit alice2blockchain.expectMsgType[PublishTx] // main delayed alice2blockchain.expectMsgType[PublishTx] // htlc timeout 1 alice2blockchain.expectMsgType[PublishTx] // htlc timeout 2 - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchTxConfirmed] } test("recv UpdateFee (sender can't afford it)") { f => @@ -566,7 +567,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit awaitCond(bob.stateName == CLOSING) assert(bob2blockchain.expectMsgType[PublishRawTx].tx === tx) // commit tx //bob2blockchain.expectMsgType[PublishTx] // main delayed (removed because of the high fees) - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] } test("recv UpdateFee (local/remote feerates are too different)") { f => @@ -578,7 +579,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit awaitCond(bob.stateName == CLOSING) assert(bob2blockchain.expectMsgType[PublishRawTx].tx === tx) // commit tx bob2blockchain.expectMsgType[PublishTx] // main delayed - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] } test("recv UpdateFee (remote feerate is too small)") { f => @@ -590,7 +591,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit awaitCond(bob.stateName == CLOSING) assert(bob2blockchain.expectMsgType[PublishRawTx].tx === tx) // commit tx bob2blockchain.expectMsgType[PublishTx] // main delayed - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] } test("recv CMD_UPDATE_RELAY_FEE ") { f => @@ -619,8 +620,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit alice2blockchain.expectMsgType[PublishTx] // main delayed alice2blockchain.expectMsgType[PublishTx] // htlc timeout 1 alice2blockchain.expectMsgType[PublishTx] // htlc timeout 2 - val watch = alice2blockchain.expectMsgType[WatchConfirmed] - assert(watch.event === BITCOIN_TX_CONFIRMED(aliceCommitTx)) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === aliceCommitTx.txid) } test("recv CurrentFeerate (when funder, triggers an UpdateFee)") { f => @@ -668,16 +668,16 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit bob2alice.expectMsgType[Error] bob2blockchain.expectMsgType[PublishTx] // commit tx bob2blockchain.expectMsgType[PublishTx] // main delayed - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] awaitCond(bob.stateName == CLOSING) } - test("recv BITCOIN_FUNDING_SPENT (their commit)") { f => + test("recv WatchFundingSpentTriggered (their commit)") { f => import f._ // bob publishes his current commit tx, which contains two pending htlcs alice->bob val bobCommitTx = bob.stateData.asInstanceOf[DATA_SHUTDOWN].commitments.localCommit.publishableTxs.commitTx.tx assert(bobCommitTx.txOut.size == 4) // two main outputs and 2 pending htlcs - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobCommitTx) + alice ! WatchFundingSpentTriggered(bobCommitTx) // in response to that, alice publishes its claim txs val claimTxs = for (_ <- 0 until 3) yield alice2blockchain.expectMsgType[PublishTx].tx @@ -691,10 +691,10 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit // htlc will timeout and be eventually refunded so we have a little less than fundingSatoshis - pushMsat = 1000000 - 200000 = 800000 (because fees) assert(amountClaimed === 774040.sat) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(bobCommitTx)) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(claimTxs(0))) - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === bobCommitTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === claimTxs(0).txid) + alice2blockchain.expectMsgType[WatchOutputSpent] + alice2blockchain.expectMsgType[WatchOutputSpent] alice2blockchain.expectNoMsg(1 second) awaitCond(alice.stateName == CLOSING) @@ -705,7 +705,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit assert(getClaimHtlcTimeoutTxs(rcp).length === 2) } - test("recv BITCOIN_FUNDING_SPENT (their next commit)") { f => + test("recv WatchFundingSpentTriggered (their next commit)") { f => import f._ // bob fulfills the first htlc fulfillHtlc(0, r1, bob, alice, bob2alice, alice2bob) @@ -724,7 +724,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit // bob publishes his current commit tx, which contains one pending htlc alice->bob val bobCommitTx = bob.stateData.asInstanceOf[DATA_SHUTDOWN].commitments.localCommit.publishableTxs.commitTx.tx assert(bobCommitTx.txOut.size == 3) // two main outputs and 1 pending htlc - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobCommitTx) + alice ! WatchFundingSpentTriggered(bobCommitTx) // in response to that, alice publishes its claim txs val claimTxs = for (_ <- 0 until 2) yield alice2blockchain.expectMsgType[PublishTx].tx @@ -738,9 +738,9 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit // htlc will timeout and be eventually refunded so we have a little less than fundingSatoshis - pushMsat - htlc1 = 1000000 - 200000 - 300 000 = 500000 (because fees) assert(amountClaimed === 481210.sat) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(bobCommitTx)) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(claimTxs(0))) - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === bobCommitTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === claimTxs(0).txid) + alice2blockchain.expectMsgType[WatchOutputSpent] alice2blockchain.expectNoMsg(1 second) awaitCond(alice.stateName == CLOSING) @@ -751,7 +751,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit assert(getClaimHtlcTimeoutTxs(rcp).length === 1) } - test("recv BITCOIN_FUNDING_SPENT (revoked tx)") { f => + test("recv WatchFundingSpentTriggered (revoked tx)") { f => import f._ val revokedTx = bob.stateData.asInstanceOf[DATA_SHUTDOWN].commitments.localCommit.publishableTxs.commitTx.tx // two main outputs + 2 htlc @@ -764,18 +764,18 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit // bob now has a new commitment tx // bob published the revoked tx - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, revokedTx) + alice ! WatchFundingSpentTriggered(revokedTx) alice2bob.expectMsgType[Error] val mainTx = alice2blockchain.expectMsgType[PublishTx].tx val mainPenaltyTx = alice2blockchain.expectMsgType[PublishTx].tx val htlc1PenaltyTx = alice2blockchain.expectMsgType[PublishTx].tx val htlc2PenaltyTx = alice2blockchain.expectMsgType[PublishTx].tx - assert(alice2blockchain.expectMsgType[WatchConfirmed].event == BITCOIN_TX_CONFIRMED(revokedTx)) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event == BITCOIN_TX_CONFIRMED(mainTx)) - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) // main-penalty - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) // htlc1-penalty - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) // htlc2-penalty + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === revokedTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === mainTx.txid) + alice2blockchain.expectMsgType[WatchOutputSpent] // main-penalty + alice2blockchain.expectMsgType[WatchOutputSpent] // htlc1-penalty + alice2blockchain.expectMsgType[WatchOutputSpent] // htlc2-penalty alice2blockchain.expectNoMsg(1 second) Transaction.correctlySpends(mainTx, Seq(revokedTx), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS) @@ -793,7 +793,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit assert(alice.stateData.asInstanceOf[DATA_CLOSING].revokedCommitPublished.size == 1) } - test("recv BITCOIN_FUNDING_SPENT (revoked tx with updated commitment)") { f => + test("recv WatchFundingSpentTriggered (revoked tx with updated commitment)") { f => import f._ val initialCommitTx = bob.stateData.asInstanceOf[DATA_SHUTDOWN].commitments.localCommit.publishableTxs.commitTx.tx assert(initialCommitTx.txOut.size === 4) // two main outputs + 2 htlc @@ -810,16 +810,16 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit alice2bob.expectMsgType[ClosingSigned] // no more htlcs in the commitment // bob published the revoked tx - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, revokedTx) + alice ! WatchFundingSpentTriggered(revokedTx) alice2bob.expectMsgType[Error] val mainTx = alice2blockchain.expectMsgType[PublishTx].tx val mainPenaltyTx = alice2blockchain.expectMsgType[PublishTx].tx val htlcPenaltyTx = alice2blockchain.expectMsgType[PublishTx].tx - assert(alice2blockchain.expectMsgType[WatchConfirmed].event == BITCOIN_TX_CONFIRMED(revokedTx)) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event == BITCOIN_TX_CONFIRMED(mainTx)) - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) // main-penalty - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) // htlc-penalty + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === revokedTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === mainTx.txid) + alice2blockchain.expectMsgType[WatchOutputSpent] // main-penalty + alice2blockchain.expectMsgType[WatchOutputSpent] // htlc-penalty alice2blockchain.expectNoMsg(1 second) Transaction.correctlySpends(mainTx, Seq(revokedTx), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS) @@ -862,20 +862,20 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit val htlc1 = alice2blockchain.expectMsgType[PublishTx].tx val htlc2 = alice2blockchain.expectMsgType[PublishTx].tx Seq(claimMain, htlc1, htlc2).foreach(tx => Transaction.correctlySpends(tx, aliceCommitTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(aliceCommitTx)) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(claimMain)) - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === aliceCommitTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === claimMain.txid) + alice2blockchain.expectMsgType[WatchOutputSpent] + alice2blockchain.expectMsgType[WatchOutputSpent] alice2blockchain.expectNoMsg(1 second) // 3rd-stage txs are published when htlc txs confirm Seq(htlc1, htlc2).foreach(htlcTimeoutTx => { - alice ! WatchEventSpent(BITCOIN_OUTPUT_SPENT, htlcTimeoutTx) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(htlcTimeoutTx)) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(htlcTimeoutTx), 2701, 3, htlcTimeoutTx) + alice ! WatchOutputSpentTriggered(htlcTimeoutTx) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === htlcTimeoutTx.txid) + alice ! WatchTxConfirmedTriggered(2701, 3, htlcTimeoutTx) val claimHtlcDelayedTx = alice2blockchain.expectMsgType[PublishTx].tx Transaction.correctlySpends(claimHtlcDelayedTx, htlcTimeoutTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(claimHtlcDelayedTx)) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === claimHtlcDelayedTx.txid) }) awaitCond(alice.stateData.asInstanceOf[DATA_CLOSING].localCommitPublished.get.claimHtlcDelayedTxs.length == 2) alice2blockchain.expectNoMsg(1 second) @@ -897,10 +897,10 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit val claimTxs = for (_ <- 0 until 3) yield alice2blockchain.expectMsgType[PublishTx].tx // the main delayed output and htlc txs spend the commitment transaction claimTxs.foreach(tx => Transaction.correctlySpends(tx, aliceCommitTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(aliceCommitTx)) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(claimTxs(0))) // main-delayed - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_OUTPUT_SPENT) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === aliceCommitTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === claimTxs(0).txid) // main-delayed + alice2blockchain.expectMsgType[WatchOutputSpent] + alice2blockchain.expectMsgType[WatchOutputSpent] alice2blockchain.expectNoMsg(1 second) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/g/NegotiatingStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/g/NegotiatingStateSpec.scala index f5704626aa..35853cfaa0 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/g/NegotiatingStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/g/NegotiatingStateSpec.scala @@ -20,7 +20,7 @@ import akka.event.LoggingAdapter import akka.testkit.TestProbe import fr.acinq.bitcoin.{ByteVector32, ByteVector64, SatoshiLong} import fr.acinq.eclair.TestConstants.Bob -import fr.acinq.eclair.blockchain._ +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._ import fr.acinq.eclair.blockchain.fee.{FeeratePerKw, FeeratesPerKw} import fr.acinq.eclair.channel.Helpers.Closing import fr.acinq.eclair.channel.TxPublisher.{PublishRawTx, PublishTx} @@ -149,8 +149,8 @@ class NegotiatingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike val mutualCloseTxAlice = alice2blockchain.expectMsgType[PublishTx].tx val mutualCloseTxBob = bob2blockchain.expectMsgType[PublishTx].tx assert(mutualCloseTxAlice === mutualCloseTxBob) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(mutualCloseTxAlice)) - assert(bob2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(mutualCloseTxBob)) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === mutualCloseTxAlice.txid) + assert(bob2blockchain.expectMsgType[WatchTxConfirmed].txId === mutualCloseTxBob.txid) assert(alice.stateData.asInstanceOf[DATA_CLOSING].mutualClosePublished.map(_.tx) == List(mutualCloseTxAlice)) assert(bob.stateData.asInstanceOf[DATA_CLOSING].mutualClosePublished.map(_.tx) == List(mutualCloseTxBob)) } @@ -165,7 +165,7 @@ class NegotiatingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike assert(new String(error.data.toArray).startsWith("invalid close fee: fee_satoshis=Satoshi(99000)")) assert(bob2blockchain.expectMsgType[PublishRawTx].tx === tx) bob2blockchain.expectMsgType[PublishTx] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] } test("recv ClosingSigned (invalid sig)") { f => @@ -177,10 +177,10 @@ class NegotiatingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike assert(new String(error.data.toArray).startsWith("invalid close signature")) assert(bob2blockchain.expectMsgType[PublishRawTx].tx === tx) bob2blockchain.expectMsgType[PublishTx] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchTxConfirmed] } - test("recv BITCOIN_FUNDING_SPENT (counterparty's mutual close)") { f => + test("recv WatchFundingSpentTriggered (counterparty's mutual close)") { f => import f._ var aliceCloseFee, bobCloseFee = 0.sat do { @@ -198,15 +198,15 @@ class NegotiatingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike // actual test starts here assert(alice.stateName == NEGOTIATING) val mutualCloseTx = bob2blockchain.expectMsgType[PublishTx].tx - assert(bob2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(mutualCloseTx)) - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, mutualCloseTx) + assert(bob2blockchain.expectMsgType[WatchTxConfirmed].txId === mutualCloseTx.txid) + alice ! WatchFundingSpentTriggered(mutualCloseTx) assert(alice2blockchain.expectMsgType[PublishRawTx].tx === mutualCloseTx) - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === mutualCloseTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === mutualCloseTx.txid) alice2blockchain.expectNoMsg(100 millis) assert(alice.stateName == CLOSING) } - test("recv BITCOIN_FUNDING_SPENT (an older mutual close)") { f => + test("recv WatchFundingSpentTriggered (an older mutual close)") { f => import f._ val aliceClose1 = alice2bob.expectMsgType[ClosingSigned] alice2bob.forward(bob) @@ -219,9 +219,9 @@ class NegotiatingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike implicit val log: LoggingAdapter = bob.underlyingActor.implicitLog val Right(bobClosingTx) = Closing.checkClosingSignature(Bob.channelKeyManager, d.commitments, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, aliceClose1.feeSatoshis, aliceClose1.signature) - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobClosingTx.tx) + alice ! WatchFundingSpentTriggered(bobClosingTx.tx) assert(alice2blockchain.expectMsgType[PublishRawTx].tx === bobClosingTx.tx) - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === bobClosingTx.tx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === bobClosingTx.tx.txid) alice2blockchain.expectNoMsg(100 millis) assert(alice.stateName == CLOSING) } @@ -240,7 +240,7 @@ class NegotiatingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike awaitCond(alice.stateName == CLOSING) assert(alice2blockchain.expectMsgType[PublishRawTx].tx === tx) alice2blockchain.expectMsgType[PublishTx] - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(tx)) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === tx.txid) } } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/h/ClosingStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/h/ClosingStateSpec.scala index 263038b963..2eb47e02c9 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/h/ClosingStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/h/ClosingStateSpec.scala @@ -20,8 +20,9 @@ import akka.testkit.{TestFSMRef, TestProbe} import fr.acinq.bitcoin.Crypto.PrivateKey import fr.acinq.bitcoin.{ByteVector32, Crypto, OutPoint, SatoshiLong, Script, ScriptFlags, Transaction, TxIn, TxOut} import fr.acinq.eclair.TestConstants.{Alice, Bob} -import fr.acinq.eclair.blockchain._ +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._ import fr.acinq.eclair.blockchain.fee.{FeeratePerKw, FeeratesPerKw} +import fr.acinq.eclair.channel.Channel.{BITCOIN_FUNDING_PUBLISH_FAILED, BITCOIN_FUNDING_TIMEOUT} import fr.acinq.eclair.channel.Helpers.Closing import fr.acinq.eclair.channel.TxPublisher.{PublishRawTx, PublishTx} import fr.acinq.eclair.channel._ @@ -79,11 +80,11 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with bob2alice.expectMsgType[FundingSigned] bob2alice.forward(alice) alice2blockchain.expectMsgType[TxPublisher.SetChannelId] - alice2blockchain.expectMsgType[WatchSpent] - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchFundingSpent] + alice2blockchain.expectMsgType[WatchFundingConfirmed] bob2blockchain.expectMsgType[TxPublisher.SetChannelId] - bob2blockchain.expectMsgType[WatchSpent] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchFundingSpent] + bob2blockchain.expectMsgType[WatchFundingConfirmed] awaitCond(alice.stateName == WAIT_FOR_FUNDING_CONFIRMED) awaitCond(bob.stateName == WAIT_FOR_FUNDING_CONFIRMED) withFixture(test.toNoArgTest(FixtureParam(alice, bob, alice2bob, bob2alice, alice2blockchain, bob2blockchain, relayerA, relayerB, channelUpdateListener, Nil))) @@ -170,8 +171,8 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with alice2bob.expectMsgType[Error] alice2blockchain.expectMsgType[PublishTx] alice2blockchain.expectMsgType[PublishTx] // claim-main-delayed - alice2blockchain.expectMsgType[WatchConfirmed] // commitment - alice2blockchain.expectMsgType[WatchConfirmed] // claim-main-delayed + alice2blockchain.expectMsgType[WatchTxConfirmed] // commitment + alice2blockchain.expectMsgType[WatchTxConfirmed] // claim-main-delayed // test starts here alice ! GetTxWithMetaResponse(fundingTx.txid, Some(fundingTx), System.currentTimeMillis.milliseconds.toSeconds) @@ -189,8 +190,8 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with alice2bob.expectMsgType[Error] alice2blockchain.expectMsgType[PublishTx] alice2blockchain.expectMsgType[PublishTx] // claim-main-delayed - alice2blockchain.expectMsgType[WatchConfirmed] // commitment - alice2blockchain.expectMsgType[WatchConfirmed] // claim-main-delayed + alice2blockchain.expectMsgType[WatchTxConfirmed] // commitment + alice2blockchain.expectMsgType[WatchTxConfirmed] // claim-main-delayed // test starts here alice ! GetTxWithMetaResponse(fundingTx.txid, None, System.currentTimeMillis.milliseconds.toSeconds) @@ -208,8 +209,8 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with bob2alice.expectMsgType[Error] bob2blockchain.expectMsgType[PublishTx] bob2blockchain.expectMsgType[PublishTx] // claim-main-delayed - bob2blockchain.expectMsgType[WatchConfirmed] // commitment - bob2blockchain.expectMsgType[WatchConfirmed] // claim-main-delayed + bob2blockchain.expectMsgType[WatchTxConfirmed] // commitment + bob2blockchain.expectMsgType[WatchTxConfirmed] // claim-main-delayed // test starts here bob ! GetTxWithMetaResponse(fundingTx.txid, Some(fundingTx), System.currentTimeMillis.milliseconds.toSeconds) @@ -227,8 +228,8 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with bob2alice.expectMsgType[Error] bob2blockchain.expectMsgType[PublishTx] bob2blockchain.expectMsgType[PublishTx] // claim-main-delayed - bob2blockchain.expectMsgType[WatchConfirmed] // commitment - bob2blockchain.expectMsgType[WatchConfirmed] // claim-main-delayed + bob2blockchain.expectMsgType[WatchTxConfirmed] // commitment + bob2blockchain.expectMsgType[WatchTxConfirmed] // claim-main-delayed // test starts here bob ! GetTxWithMetaResponse(fundingTx.txid, None, System.currentTimeMillis.milliseconds.toSeconds) @@ -246,8 +247,8 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with bob2alice.expectMsgType[Error] bob2blockchain.expectMsgType[PublishTx] bob2blockchain.expectMsgType[PublishTx] // claim-main-delayed - bob2blockchain.expectMsgType[WatchConfirmed] // commitment - bob2blockchain.expectMsgType[WatchConfirmed] // claim-main-delayed + bob2blockchain.expectMsgType[WatchTxConfirmed] // commitment + bob2blockchain.expectMsgType[WatchTxConfirmed] // claim-main-delayed // test starts here bob.setState(stateData = bob.stateData.asInstanceOf[DATA_CLOSING].copy(waitingSinceBlock = bob.underlyingActor.nodeParams.currentBlockHeight - Channel.FUNDING_TIMEOUT_FUNDEE - 1)) @@ -309,30 +310,30 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(mutualCloseTx === alice.stateData.asInstanceOf[DATA_CLOSING].mutualClosePublished.last) // actual test starts here - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, mutualCloseTx.tx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(mutualCloseTx.tx), 0, 0, mutualCloseTx.tx) + alice ! WatchFundingSpentTriggered(mutualCloseTx.tx) + alice ! WatchTxConfirmedTriggered(0, 0, mutualCloseTx.tx) awaitCond(alice.stateName == CLOSED) } - test("recv BITCOIN_FUNDING_SPENT (mutual close before converging)") { f => + test("recv WatchFundingSpentTriggered (mutual close before converging)") { f => testMutualCloseBeforeConverge(f, ChannelVersion.STANDARD) } - test("recv BITCOIN_FUNDING_SPENT (mutual close before converging, anchor outputs)", Tag(StateTestsTags.AnchorOutputs)) { f => + test("recv WatchFundingSpentTriggered (mutual close before converging, anchor outputs)", Tag(StateTestsTags.AnchorOutputs)) { f => testMutualCloseBeforeConverge(f, ChannelVersion.ANCHOR_OUTPUTS) } - test("recv BITCOIN_TX_CONFIRMED (mutual close)") { f => + test("recv WatchTxConfirmedTriggered (mutual close)") { f => import f._ mutualClose(alice, bob, alice2bob, bob2alice, alice2blockchain, bob2blockchain) val mutualCloseTx = alice.stateData.asInstanceOf[DATA_CLOSING].mutualClosePublished.last // actual test starts here - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(mutualCloseTx.tx), 0, 0, mutualCloseTx.tx) + alice ! WatchTxConfirmedTriggered(0, 0, mutualCloseTx.tx) awaitCond(alice.stateName == CLOSED) } - test("recv BITCOIN_FUNDING_SPENT (local commit)") { f => + test("recv WatchFundingSpentTriggered (local commit)") { f => import f._ // an error occurs and alice publishes her commit tx val aliceCommitTx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx @@ -342,11 +343,11 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // actual test starts here // we are notified afterwards from our watcher about the tx that we just published - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, aliceCommitTx) + alice ! WatchFundingSpentTriggered(aliceCommitTx) assert(alice.stateData == initialState) // this was a no-op } - test("recv BITCOIN_OUTPUT_SPENT") { f => + test("recv WatchOutputSpentTriggered") { f => import f._ // alice sends an htlc to bob val (ra1, htlca1) = addHtlc(50000000 msat, alice, bob, alice2bob, bob2alice) @@ -361,14 +362,14 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // scenario 1: bob claims the htlc output from the commit tx using its preimage val claimHtlcSuccessFromCommitTx = Transaction(version = 0, txIn = TxIn(outPoint = OutPoint(randomBytes32, 0), signatureScript = ByteVector.empty, sequence = 0, witness = Scripts.witnessClaimHtlcSuccessFromCommitTx(Transactions.PlaceHolderSig, ra1, ByteVector.fill(130)(33))) :: Nil, txOut = Nil, lockTime = 0) - alice ! WatchEventSpent(BITCOIN_OUTPUT_SPENT, claimHtlcSuccessFromCommitTx) + alice ! WatchOutputSpentTriggered(claimHtlcSuccessFromCommitTx) val fulfill1 = relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFulfill]] assert(fulfill1.htlc === htlca1) assert(fulfill1.result.paymentPreimage === ra1) // scenario 2: bob claims the htlc output from his own commit tx using its preimage (let's assume both parties had published their commitment tx) val claimHtlcSuccessTx = Transaction(version = 0, txIn = TxIn(outPoint = OutPoint(randomBytes32, 0), signatureScript = ByteVector.empty, sequence = 0, witness = Scripts.witnessHtlcSuccess(Transactions.PlaceHolderSig, Transactions.PlaceHolderSig, ra1, ByteVector.fill(130)(33), Transactions.DefaultCommitmentFormat)) :: Nil, txOut = Nil, lockTime = 0) - alice ! WatchEventSpent(BITCOIN_OUTPUT_SPENT, claimHtlcSuccessTx) + alice ! WatchOutputSpentTriggered(claimHtlcSuccessTx) val fulfill2 = relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFulfill]] assert(fulfill2.htlc === htlca1) assert(fulfill2.result.paymentPreimage === ra1) @@ -400,14 +401,14 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(getHtlcTimeoutTxs(closingState).length === 1) val htlcTimeoutTx = getHtlcTimeoutTxs(closingState).head.tx assert(closingState.claimHtlcDelayedTxs.length === 0) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(closingState.commitTx), 42, 0, closingState.commitTx) + alice ! WatchTxConfirmedTriggered(42, 0, closingState.commitTx) assert(listener.expectMsgType[LocalCommitConfirmed].refundAtBlock == 42 + TestConstants.Bob.channelParams.toSelfDelay.toInt) assert(listener.expectMsgType[PaymentSettlingOnChain].paymentHash == htlca1.paymentHash) // htlcs below dust will never reach the chain, once the commit tx is confirmed we can consider them failed assert(relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFail]].htlc === htlca2) relayerA.expectNoMsg(100 millis) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(closingState.claimMainDelayedOutputTx.get.tx), 200, 0, closingState.claimMainDelayedOutputTx.get.tx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(htlcTimeoutTx), 201, 0, htlcTimeoutTx) + alice ! WatchTxConfirmedTriggered(200, 0, closingState.claimMainDelayedOutputTx.get.tx) + alice ! WatchTxConfirmedTriggered(201, 0, htlcTimeoutTx) assert(alice.stateData.asInstanceOf[DATA_CLOSING].localCommitPublished.get.irrevocablySpent.values.toSet === Set(closingState.commitTx, closingState.claimMainDelayedOutputTx.get.tx, htlcTimeoutTx)) assert(relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFail]].htlc === htlca1) relayerA.expectNoMsg(100 millis) @@ -417,20 +418,20 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(claimHtlcDelayedTx.isInstanceOf[PublishRawTx]) Transaction.correctlySpends(claimHtlcDelayedTx.tx, Seq(htlcTimeoutTx), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS) awaitCond(alice.stateData.asInstanceOf[DATA_CLOSING].localCommitPublished.get.claimHtlcDelayedTxs.length === 1) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcDelayedTx.tx), 202, 0, claimHtlcDelayedTx.tx) + alice ! WatchTxConfirmedTriggered(202, 0, claimHtlcDelayedTx.tx) awaitCond(alice.stateName == CLOSED) } - test("recv BITCOIN_TX_CONFIRMED (local commit)") { f => + test("recv WatchTxConfirmedTriggered (local commit)") { f => testLocalCommitTxConfirmed(f, ChannelVersion.STANDARD) } - test("recv BITCOIN_TX_CONFIRMED (local commit, anchor outputs)", Tag(StateTestsTags.AnchorOutputs)) { f => + test("recv WatchTxConfirmedTriggered (local commit, anchor outputs)", Tag(StateTestsTags.AnchorOutputs)) { f => testLocalCommitTxConfirmed(f, ChannelVersion.ANCHOR_OUTPUTS) } - test("recv BITCOIN_TX_CONFIRMED (local commit with multiple htlcs for the same payment)") { f => + test("recv WatchTxConfirmedTriggered (local commit with multiple htlcs for the same payment)") { f => import f._ // alice sends a first htlc to bob @@ -457,34 +458,34 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(closingState.claimHtlcDelayedTxs.length === 0) // if commit tx and htlc-timeout txs end up in the same block, we may receive the htlc-timeout confirmation before the commit tx confirmation - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(htlcTimeoutTxs(0)), 42, 0, htlcTimeoutTxs(0)) + alice ! WatchTxConfirmedTriggered(42, 0, htlcTimeoutTxs(0)) val forwardedFail1 = relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFail]].htlc relayerA.expectNoMsg(250 millis) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(closingState.commitTx), 42, 1, closingState.commitTx) + alice ! WatchTxConfirmedTriggered(42, 1, closingState.commitTx) assert(relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFail]].htlc === dust) relayerA.expectNoMsg(250 millis) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(closingState.claimMainDelayedOutputTx.get.tx), 200, 0, closingState.claimMainDelayedOutputTx.get.tx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(htlcTimeoutTxs(1)), 202, 0, htlcTimeoutTxs(1)) + alice ! WatchTxConfirmedTriggered(200, 0, closingState.claimMainDelayedOutputTx.get.tx) + alice ! WatchTxConfirmedTriggered(202, 0, htlcTimeoutTxs(1)) val forwardedFail2 = relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFail]].htlc relayerA.expectNoMsg(250 millis) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(htlcTimeoutTxs(2)), 202, 1, htlcTimeoutTxs(2)) + alice ! WatchTxConfirmedTriggered(202, 1, htlcTimeoutTxs(2)) val forwardedFail3 = relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFail]].htlc relayerA.expectNoMsg(250 millis) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(htlcTimeoutTxs(3)), 203, 0, htlcTimeoutTxs(3)) + alice ! WatchTxConfirmedTriggered(203, 0, htlcTimeoutTxs(3)) val forwardedFail4 = relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFail]].htlc assert(Set(forwardedFail1, forwardedFail2, forwardedFail3, forwardedFail4) === Set(htlca1, htlca2, htlca3, htlca4)) relayerA.expectNoMsg(250 millis) awaitCond(alice.stateData.asInstanceOf[DATA_CLOSING].localCommitPublished.get.claimHtlcDelayedTxs.length === 4) val claimHtlcDelayedTxs = alice.stateData.asInstanceOf[DATA_CLOSING].localCommitPublished.get.claimHtlcDelayedTxs - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcDelayedTxs(0).tx), 203, 0, claimHtlcDelayedTxs(0).tx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcDelayedTxs(1).tx), 203, 1, claimHtlcDelayedTxs(1).tx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcDelayedTxs(2).tx), 203, 2, claimHtlcDelayedTxs(2).tx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcDelayedTxs(3).tx), 203, 3, claimHtlcDelayedTxs(3).tx) + alice ! WatchTxConfirmedTriggered(203, 0, claimHtlcDelayedTxs(0).tx) + alice ! WatchTxConfirmedTriggered(203, 1, claimHtlcDelayedTxs(1).tx) + alice ! WatchTxConfirmedTriggered(203, 2, claimHtlcDelayedTxs(2).tx) + alice ! WatchTxConfirmedTriggered(203, 3, claimHtlcDelayedTxs(3).tx) awaitCond(alice.stateName == CLOSED) } - test("recv BITCOIN_TX_CONFIRMED (local commit with htlcs only signed by local)") { f => + test("recv WatchTxConfirmedTriggered (local commit with htlcs only signed by local)") { f => import f._ val listener = TestProbe() system.eventStream.subscribe(listener.ref, classOf[PaymentSettlingOnChain]) @@ -502,7 +503,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with channelUpdateListener.expectMsgType[LocalChannelDown] assert(closingState.htlcTxs.isEmpty && closingState.claimHtlcDelayedTxs.isEmpty) // when the commit tx is confirmed, alice knows that the htlc she sent right before the unilateral close will never reach the chain - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(aliceCommitTx), 0, 0, aliceCommitTx) + alice ! WatchTxConfirmedTriggered(0, 0, aliceCommitTx) // so she fails it val origin = alice.stateData.asInstanceOf[DATA_CLOSING].commitments.originChannels(htlc.id) relayerA.expectMsg(RES_ADD_SETTLED(origin, htlc, HtlcResult.OnChainFail(HtlcOverriddenByLocalCommit(channelId(alice), htlc)))) @@ -511,7 +512,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with relayerA.expectNoMsg(100 millis) } - test("recv BITCOIN_TX_CONFIRMED (local commit with fulfill only signed by local)") { f => + test("recv WatchTxConfirmedTriggered (local commit with fulfill only signed by local)") { f => import f._ // bob sends an htlc val (r, htlc) = addHtlc(110000000 msat, bob, alice, bob2alice, alice2bob) @@ -533,7 +534,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(getHtlcSuccessTxs(closingState).length === 1) } - test("recv BITCOIN_TX_CONFIRMED (local commit with fail not acked by remote)") { f => + test("recv WatchTxConfirmedTriggered (local commit with fail not acked by remote)") { f => import f._ val listener = TestProbe() system.eventStream.subscribe(listener.ref, classOf[PaymentSettlingOnChain]) @@ -557,7 +558,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with channelUpdateListener.expectMsgType[LocalChannelDown] assert(closingState.htlcTxs.isEmpty && closingState.claimHtlcDelayedTxs.isEmpty) // when the commit tx is confirmed, alice knows that the htlc will never reach the chain - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(closingState.commitTx), 0, 0, closingState.commitTx) + alice ! WatchTxConfirmedTriggered(0, 0, closingState.commitTx) // so she fails it val origin = alice.stateData.asInstanceOf[DATA_CLOSING].commitments.originChannels(htlc.id) relayerA.expectMsg(RES_ADD_SETTLED(origin, htlc, HtlcResult.OnChainFail(HtlcOverriddenByLocalCommit(channelId(alice), htlc)))) @@ -583,23 +584,23 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with awaitCond(alice.stateName == CLOSING) // the commit tx hasn't been confirmed yet, so we watch the funding output first - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_FUNDING_SPENT) + alice2blockchain.expectMsgType[WatchFundingSpent] // then we should re-publish unconfirmed transactions assert(alice2blockchain.expectMsgType[PublishTx].tx === closingState.commitTx) closingState.claimMainDelayedOutputTx.foreach(claimMain => assert(alice2blockchain.expectMsgType[PublishTx].tx === claimMain.tx)) assert(alice2blockchain.expectMsgType[PublishTx].tx === htlcTimeoutTx.tx) - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === closingState.commitTx.txid) - closingState.claimMainDelayedOutputTx.foreach(claimMain => assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === claimMain.tx.txid)) - assert(alice2blockchain.expectMsgType[WatchSpent].outputIndex === htlcTimeoutTx.input.outPoint.index) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === closingState.commitTx.txid) + closingState.claimMainDelayedOutputTx.foreach(claimMain => assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === claimMain.tx.txid)) + assert(alice2blockchain.expectMsgType[WatchOutputSpent].outputIndex === htlcTimeoutTx.input.outPoint.index) // the htlc transaction confirms, so we publish a 3rd-stage transaction - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(closingState.commitTx), 2701, 1, closingState.commitTx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(htlcTimeoutTx.tx), 2702, 0, htlcTimeoutTx.tx) + alice ! WatchTxConfirmedTriggered(2701, 1, closingState.commitTx) + alice ! WatchTxConfirmedTriggered(2702, 0, htlcTimeoutTx.tx) awaitCond(alice.stateData.asInstanceOf[DATA_CLOSING].localCommitPublished.get.claimHtlcDelayedTxs.nonEmpty) val beforeSecondRestart = alice.stateData.asInstanceOf[DATA_CLOSING] val claimHtlcTimeoutTx = beforeSecondRestart.localCommitPublished.get.claimHtlcDelayedTxs.head assert(alice2blockchain.expectMsgType[PublishTx].tx === claimHtlcTimeoutTx.tx) - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === claimHtlcTimeoutTx.tx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === claimHtlcTimeoutTx.tx.txid) // simulate another node restart alice.setState(WAIT_FOR_INIT_INTERNAL, Nothing) @@ -610,11 +611,11 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // we should re-publish unconfirmed transactions closingState.claimMainDelayedOutputTx.foreach(claimMain => assert(alice2blockchain.expectMsgType[PublishTx].tx === claimMain.tx)) assert(alice2blockchain.expectMsgType[PublishTx].tx === claimHtlcTimeoutTx.tx) - closingState.claimMainDelayedOutputTx.foreach(claimMain => assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === claimMain.tx.txid)) - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === claimHtlcTimeoutTx.tx.txid) + closingState.claimMainDelayedOutputTx.foreach(claimMain => assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === claimMain.tx.txid)) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === claimHtlcTimeoutTx.tx.txid) } - test("recv BITCOIN_TX_CONFIRMED (remote commit with htlcs only signed by local in next remote commit)") { f => + test("recv WatchTxConfirmedTriggered (remote commit with htlcs only signed by local in next remote commit)") { f => import f._ val listener = TestProbe() system.eventStream.subscribe(listener.ref, classOf[PaymentSettlingOnChain]) @@ -631,7 +632,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(closingState.claimMainOutputTx.nonEmpty) assert(closingState.claimHtlcTxs.isEmpty) // when the commit tx is signed, alice knows that the htlc she sent right before the unilateral close will never reach the chain - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(bobCommitTx), 0, 0, bobCommitTx) + alice ! WatchTxConfirmedTriggered(0, 0, bobCommitTx) // so she fails it val origin = alice.stateData.asInstanceOf[DATA_CLOSING].commitments.originChannels(htlc.id) relayerA.expectMsg(RES_ADD_SETTLED(origin, htlc, HtlcResult.OnChainFail(HtlcOverriddenByLocalCommit(channelId(alice), htlc)))) @@ -639,7 +640,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with listener.expectNoMsg(2 seconds) } - test("recv BITCOIN_FUNDING_SPENT (remote commit)") { f => + test("recv WatchFundingSpentTriggered (remote commit)") { f => import f._ mutualClose(alice, bob, alice2bob, bob2alice, alice2blockchain, bob2blockchain) val initialState = alice.stateData.asInstanceOf[DATA_CLOSING] @@ -652,7 +653,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(alice.stateData.asInstanceOf[DATA_CLOSING].copy(remoteCommitPublished = None) == initialState) } - test("recv BITCOIN_TX_CONFIRMED (remote commit)") { f => + test("recv WatchTxConfirmedTriggered (remote commit)") { f => import f._ mutualClose(alice, bob, alice2bob, bob2alice, alice2blockchain, bob2blockchain) val initialState = alice.stateData.asInstanceOf[DATA_CLOSING] @@ -666,29 +667,29 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(closingState.claimMainOutputTx.nonEmpty) assert(closingState.claimHtlcTxs.isEmpty) assert(alice.stateData.asInstanceOf[DATA_CLOSING].copy(remoteCommitPublished = None) == initialState) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(bobCommitTx), 0, 0, bobCommitTx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(closingState.claimMainOutputTx.get.tx), 0, 0, closingState.claimMainOutputTx.get.tx) + alice ! WatchTxConfirmedTriggered(0, 0, bobCommitTx) + alice ! WatchTxConfirmedTriggered(0, 0, closingState.claimMainOutputTx.get.tx) awaitCond(alice.stateName == CLOSED) } - test("recv BITCOIN_TX_CONFIRMED (remote commit, option_static_remotekey)", Tag(StateTestsTags.StaticRemoteKey)) { f => + test("recv WatchTxConfirmedTriggered (remote commit, option_static_remotekey)", Tag(StateTestsTags.StaticRemoteKey)) { f => import f._ mutualClose(alice, bob, alice2bob, bob2alice, alice2blockchain, bob2blockchain) assert(alice.stateData.asInstanceOf[DATA_CLOSING].commitments.channelVersion === ChannelVersion.STATIC_REMOTEKEY) // bob publishes his last current commit tx, the one it had when entering NEGOTIATING state val bobCommitTx = bobCommitTxs.last.commitTx.tx assert(bobCommitTx.txOut.size == 2) // two main outputs - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobCommitTx) + alice ! WatchFundingSpentTriggered(bobCommitTx) // alice won't create a claimMainOutputTx because her main output is already spendable by the wallet awaitCond(alice.stateData.asInstanceOf[DATA_CLOSING].remoteCommitPublished.get.claimMainOutputTx.isEmpty) assert(alice.stateName == CLOSING) // once the remote commit is confirmed the channel is definitively closed - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(bobCommitTx), 0, 0, bobCommitTx) + alice ! WatchTxConfirmedTriggered(0, 0, bobCommitTx) awaitCond(alice.stateName == CLOSED) } - test("recv BITCOIN_TX_CONFIRMED (remote commit, anchor outputs)", Tag(StateTestsTags.AnchorOutputs)) { f => + test("recv WatchTxConfirmedTriggered (remote commit, anchor outputs)", Tag(StateTestsTags.AnchorOutputs)) { f => import f._ mutualClose(alice, bob, alice2bob, bob2alice, alice2blockchain, bob2blockchain) val initialState = alice.stateData.asInstanceOf[DATA_CLOSING] @@ -702,8 +703,8 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(closingState.claimMainOutputTx.nonEmpty) assert(closingState.claimHtlcTxs.isEmpty) assert(alice.stateData.asInstanceOf[DATA_CLOSING].copy(remoteCommitPublished = None) == initialState) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(bobCommitTx), 0, 0, bobCommitTx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(closingState.claimMainOutputTx.get.tx), 0, 0, closingState.claimMainOutputTx.get.tx) + alice ! WatchTxConfirmedTriggered(0, 0, bobCommitTx) + alice ! WatchTxConfirmedTriggered(0, 0, closingState.claimMainOutputTx.get.tx) awaitCond(alice.stateName == CLOSED) } @@ -733,31 +734,31 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with val claimHtlcTimeoutTxs = getClaimHtlcTimeoutTxs(closingState).map(_.tx) assert(claimHtlcTimeoutTxs.length === 3) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(bobCommitTx), 42, 0, bobCommitTx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(closingState.claimMainOutputTx.get.tx), 45, 0, closingState.claimMainOutputTx.get.tx) + alice ! WatchTxConfirmedTriggered(42, 0, bobCommitTx) + alice ! WatchTxConfirmedTriggered(45, 0, closingState.claimMainOutputTx.get.tx) relayerA.expectNoMsg(100 millis) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcTimeoutTxs(0)), 201, 0, claimHtlcTimeoutTxs(0)) + alice ! WatchTxConfirmedTriggered(201, 0, claimHtlcTimeoutTxs(0)) val forwardedFail1 = relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFail]].htlc relayerA.expectNoMsg(250 millis) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcTimeoutTxs(1)), 202, 0, claimHtlcTimeoutTxs(1)) + alice ! WatchTxConfirmedTriggered(202, 0, claimHtlcTimeoutTxs(1)) val forwardedFail2 = relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFail]].htlc relayerA.expectNoMsg(250 millis) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcTimeoutTxs(2)), 203, 1, claimHtlcTimeoutTxs(2)) + alice ! WatchTxConfirmedTriggered(203, 1, claimHtlcTimeoutTxs(2)) val forwardedFail3 = relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFail]].htlc assert(Set(forwardedFail1, forwardedFail2, forwardedFail3) === Set(htlca1, htlca2, htlca3)) relayerA.expectNoMsg(250 millis) awaitCond(alice.stateName == CLOSED) } - test("recv BITCOIN_TX_CONFIRMED (remote commit with multiple htlcs for the same payment)") { f => + test("recv WatchTxConfirmedTriggered (remote commit with multiple htlcs for the same payment)") { f => testRemoteCommitTxWithHtlcsConfirmed(f, ChannelVersion.STANDARD) } - test("recv BITCOIN_TX_CONFIRMED (remote commit with multiple htlcs for the same payment, anchor outputs)", Tag(StateTestsTags.AnchorOutputs)) { f => + test("recv WatchTxConfirmedTriggered (remote commit with multiple htlcs for the same payment, anchor outputs)", Tag(StateTestsTags.AnchorOutputs)) { f => testRemoteCommitTxWithHtlcsConfirmed(f, ChannelVersion.ANCHOR_OUTPUTS) } - test("recv BITCOIN_TX_CONFIRMED (remote commit) followed by CMD_FULFILL_HTLC") { f => + test("recv WatchTxConfirmedTriggered (remote commit) followed by CMD_FULFILL_HTLC") { f => import f._ // An HTLC Bob -> Alice is cross-signed that will be fulfilled later. val (r1, htlc1) = addHtlc(110000000 msat, bob, alice, bob2alice, alice2bob) @@ -786,19 +787,18 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(alice2blockchain.expectMsgType[PublishRawTx].tx === claimHtlcSuccessTx) // Alice resets watches on all relevant transactions. - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(bobCommitTx)) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(closingState.claimMainOutputTx.get.tx)) - val watchHtlcSuccess = alice2blockchain.expectMsgType[WatchSpent] - assert(watchHtlcSuccess.event === BITCOIN_OUTPUT_SPENT) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === bobCommitTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === closingState.claimMainOutputTx.get.tx.txid) + val watchHtlcSuccess = alice2blockchain.expectMsgType[WatchOutputSpent] assert(watchHtlcSuccess.txId === bobCommitTx.txid) assert(watchHtlcSuccess.outputIndex === claimHtlcSuccessTx.txIn.head.outPoint.index) alice2blockchain.expectNoMsg(100 millis) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(bobCommitTx), 0, 0, bobCommitTx) + alice ! WatchTxConfirmedTriggered(0, 0, bobCommitTx) // The second htlc was not included in the commit tx published on-chain, so we can consider it failed assert(relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFail]].htlc === htlc2) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(closingState.claimMainOutputTx.get.tx), 0, 0, closingState.claimMainOutputTx.get.tx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcSuccessTx), 0, 0, claimHtlcSuccessTx) + alice ! WatchTxConfirmedTriggered(0, 0, closingState.claimMainOutputTx.get.tx) + alice ! WatchTxConfirmedTriggered(0, 0, claimHtlcSuccessTx) assert(alice.stateData.asInstanceOf[DATA_CLOSING].remoteCommitPublished.get.irrevocablySpent.values.toSet === Set(bobCommitTx, closingState.claimMainOutputTx.get.tx, claimHtlcSuccessTx)) awaitCond(alice.stateName == CLOSED) alice2blockchain.expectNoMsg(100 millis) @@ -814,7 +814,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with val bobCommitTx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx val closingState = remoteClose(bobCommitTx, alice, alice2blockchain) val htlcTimeoutTx = getClaimHtlcTimeoutTxs(closingState).head - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(bobCommitTx), 0, 0, bobCommitTx) + alice ! WatchTxConfirmedTriggered(0, 0, bobCommitTx) // simulate a node restart val beforeRestart = alice.stateData.asInstanceOf[DATA_CLOSING] @@ -826,8 +826,8 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // we should re-publish unconfirmed transactions closingState.claimMainOutputTx.foreach(claimMain => assert(alice2blockchain.expectMsgType[PublishTx].tx === claimMain.tx)) assert(alice2blockchain.expectMsgType[PublishTx].tx === htlcTimeoutTx.tx) - closingState.claimMainOutputTx.foreach(claimMain => assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === claimMain.tx.txid)) - assert(alice2blockchain.expectMsgType[WatchSpent].outputIndex === htlcTimeoutTx.input.outPoint.index) + closingState.claimMainOutputTx.foreach(claimMain => assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === claimMain.tx.txid)) + assert(alice2blockchain.expectMsgType[WatchOutputSpent].outputIndex === htlcTimeoutTx.input.outPoint.index) } private def testNextRemoteCommitTxConfirmed(f: FixtureParam, channelVersion: ChannelVersion): (Transaction, RemoteCommitPublished, Set[UpdateAddHtlc]) = { @@ -862,67 +862,67 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with (bobCommitTx, closingState, Set(htlca1, htlca2, htlca3)) } - test("recv BITCOIN_TX_CONFIRMED (next remote commit)") { f => + test("recv WatchTxConfirmedTriggered (next remote commit)") { f => import f._ val (bobCommitTx, closingState, htlcs) = testNextRemoteCommitTxConfirmed(f, ChannelVersion.STANDARD) val claimHtlcTimeoutTxs = getClaimHtlcTimeoutTxs(closingState).map(_.tx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(bobCommitTx), 42, 0, bobCommitTx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(closingState.claimMainOutputTx.get.tx), 45, 0, closingState.claimMainOutputTx.get.tx) + alice ! WatchTxConfirmedTriggered(42, 0, bobCommitTx) + alice ! WatchTxConfirmedTriggered(45, 0, closingState.claimMainOutputTx.get.tx) relayerA.expectNoMsg(100 millis) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcTimeoutTxs(0)), 201, 0, claimHtlcTimeoutTxs(0)) + alice ! WatchTxConfirmedTriggered(201, 0, claimHtlcTimeoutTxs(0)) val forwardedFail1 = relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFail]].htlc relayerA.expectNoMsg(250 millis) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcTimeoutTxs(1)), 202, 0, claimHtlcTimeoutTxs(1)) + alice ! WatchTxConfirmedTriggered(202, 0, claimHtlcTimeoutTxs(1)) val forwardedFail2 = relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFail]].htlc relayerA.expectNoMsg(250 millis) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcTimeoutTxs(2)), 203, 1, claimHtlcTimeoutTxs(2)) + alice ! WatchTxConfirmedTriggered(203, 1, claimHtlcTimeoutTxs(2)) val forwardedFail3 = relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFail]].htlc assert(Set(forwardedFail1, forwardedFail2, forwardedFail3) === htlcs) relayerA.expectNoMsg(250 millis) awaitCond(alice.stateName == CLOSED) } - test("recv BITCOIN_TX_CONFIRMED (next remote commit, static_remotekey)", Tag(StateTestsTags.StaticRemoteKey)) { f => + test("recv WatchTxConfirmedTriggered (next remote commit, static_remotekey)", Tag(StateTestsTags.StaticRemoteKey)) { f => import f._ val (bobCommitTx, closingState, htlcs) = testNextRemoteCommitTxConfirmed(f, ChannelVersion.STATIC_REMOTEKEY) val claimHtlcTimeoutTxs = getClaimHtlcTimeoutTxs(closingState).map(_.tx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(bobCommitTx), 42, 0, bobCommitTx) + alice ! WatchTxConfirmedTriggered(42, 0, bobCommitTx) assert(closingState.claimMainOutputTx.isEmpty) // with static_remotekey we don't claim out main output relayerA.expectNoMsg(100 millis) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcTimeoutTxs(0)), 201, 0, claimHtlcTimeoutTxs(0)) + alice ! WatchTxConfirmedTriggered(201, 0, claimHtlcTimeoutTxs(0)) val forwardedFail1 = relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFail]].htlc relayerA.expectNoMsg(250 millis) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcTimeoutTxs(1)), 202, 0, claimHtlcTimeoutTxs(1)) + alice ! WatchTxConfirmedTriggered(202, 0, claimHtlcTimeoutTxs(1)) val forwardedFail2 = relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFail]].htlc relayerA.expectNoMsg(250 millis) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcTimeoutTxs(2)), 203, 1, claimHtlcTimeoutTxs(2)) + alice ! WatchTxConfirmedTriggered(203, 1, claimHtlcTimeoutTxs(2)) val forwardedFail3 = relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFail]].htlc assert(Set(forwardedFail1, forwardedFail2, forwardedFail3) === htlcs) relayerA.expectNoMsg(250 millis) awaitCond(alice.stateName == CLOSED) } - test("recv BITCOIN_TX_CONFIRMED (next remote commit, anchor outputs)", Tag(StateTestsTags.AnchorOutputs)) { f => + test("recv WatchTxConfirmedTriggered (next remote commit, anchor outputs)", Tag(StateTestsTags.AnchorOutputs)) { f => import f._ val (bobCommitTx, closingState, htlcs) = testNextRemoteCommitTxConfirmed(f, ChannelVersion.ANCHOR_OUTPUTS) val claimHtlcTimeoutTxs = getClaimHtlcTimeoutTxs(closingState).map(_.tx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(bobCommitTx), 42, 0, bobCommitTx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(closingState.claimMainOutputTx.get.tx), 45, 0, closingState.claimMainOutputTx.get.tx) + alice ! WatchTxConfirmedTriggered(42, 0, bobCommitTx) + alice ! WatchTxConfirmedTriggered(45, 0, closingState.claimMainOutputTx.get.tx) relayerA.expectNoMsg(100 millis) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcTimeoutTxs(0)), 201, 0, claimHtlcTimeoutTxs(0)) + alice ! WatchTxConfirmedTriggered(201, 0, claimHtlcTimeoutTxs(0)) val forwardedFail1 = relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFail]].htlc relayerA.expectNoMsg(250 millis) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcTimeoutTxs(1)), 202, 0, claimHtlcTimeoutTxs(1)) + alice ! WatchTxConfirmedTriggered(202, 0, claimHtlcTimeoutTxs(1)) val forwardedFail2 = relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFail]].htlc relayerA.expectNoMsg(250 millis) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcTimeoutTxs(2)), 203, 1, claimHtlcTimeoutTxs(2)) + alice ! WatchTxConfirmedTriggered(203, 1, claimHtlcTimeoutTxs(2)) val forwardedFail3 = relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFail]].htlc assert(Set(forwardedFail1, forwardedFail2, forwardedFail3) === htlcs) relayerA.expectNoMsg(250 millis) awaitCond(alice.stateName == CLOSED) } - test("recv BITCOIN_TX_CONFIRMED (next remote commit) followed by CMD_FULFILL_HTLC") { f => + test("recv WatchTxConfirmedTriggered (next remote commit) followed by CMD_FULFILL_HTLC") { f => import f._ // An HTLC Bob -> Alice is cross-signed that will be fulfilled later. val (r1, htlc1) = addHtlc(110000000 msat, bob, alice, bob2alice, alice2bob) @@ -955,18 +955,17 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(alice2blockchain.expectMsgType[PublishRawTx].tx === claimHtlcSuccessTx) assert(alice2blockchain.expectMsgType[PublishRawTx].tx === claimHtlcTimeoutTx) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(bobCommitTx)) - assert(alice2blockchain.expectMsgType[WatchConfirmed].event === BITCOIN_TX_CONFIRMED(closingState.claimMainOutputTx.get.tx)) - val watchHtlcs = alice2blockchain.expectMsgType[WatchSpent] :: alice2blockchain.expectMsgType[WatchSpent] :: Nil - watchHtlcs.foreach(ws => assert(ws.event === BITCOIN_OUTPUT_SPENT)) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === bobCommitTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === closingState.claimMainOutputTx.get.tx.txid) + val watchHtlcs = alice2blockchain.expectMsgType[WatchOutputSpent] :: alice2blockchain.expectMsgType[WatchOutputSpent] :: Nil watchHtlcs.foreach(ws => assert(ws.txId === bobCommitTx.txid)) assert(watchHtlcs.map(_.outputIndex).toSet === Set(claimHtlcSuccessTx, claimHtlcTimeoutTx).map(_.txIn.head.outPoint.index)) alice2blockchain.expectNoMsg(100 millis) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(bobCommitTx), 0, 0, bobCommitTx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(closingState.claimMainOutputTx.get.tx), 0, 0, closingState.claimMainOutputTx.get.tx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcSuccessTx), 0, 0, claimHtlcSuccessTx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcTimeoutTx), 0, 0, claimHtlcTimeoutTx) + alice ! WatchTxConfirmedTriggered(0, 0, bobCommitTx) + alice ! WatchTxConfirmedTriggered(0, 0, closingState.claimMainOutputTx.get.tx) + alice ! WatchTxConfirmedTriggered(0, 0, claimHtlcSuccessTx) + alice ! WatchTxConfirmedTriggered(0, 0, claimHtlcTimeoutTx) assert(relayerA.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFail]].htlc === htlc2) awaitCond(alice.stateName == CLOSED) alice2blockchain.expectNoMsg(100 millis) @@ -987,13 +986,13 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with awaitCond(alice.stateName == CLOSING) // the commit tx hasn't been confirmed yet, so we watch the funding output first - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_FUNDING_SPENT) + alice2blockchain.expectMsgType[WatchFundingSpent] // then we should re-publish unconfirmed transactions closingState.claimMainOutputTx.foreach(claimMain => assert(alice2blockchain.expectMsgType[PublishTx].tx === claimMain.tx)) claimHtlcTimeoutTxs.foreach(claimHtlcTimeout => assert(alice2blockchain.expectMsgType[PublishTx].tx === claimHtlcTimeout.tx)) - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === bobCommitTx.txid) - closingState.claimMainOutputTx.foreach(claimMain => assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === claimMain.tx.txid)) - claimHtlcTimeoutTxs.foreach(claimHtlcTimeout => assert(alice2blockchain.expectMsgType[WatchSpent].outputIndex === claimHtlcTimeout.input.outPoint.index)) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === bobCommitTx.txid) + closingState.claimMainOutputTx.foreach(claimMain => assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === claimMain.tx.txid)) + claimHtlcTimeoutTxs.foreach(claimHtlcTimeout => assert(alice2blockchain.expectMsgType[WatchOutputSpent].outputIndex === claimHtlcTimeout.input.outPoint.index)) } private def testFutureRemoteCommitTxConfirmed(f: FixtureParam, channelVersion: ChannelVersion): Transaction = { @@ -1038,51 +1037,51 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with } else { assert(bobCommitTx.txOut.length === 4) // two main outputs + 2 HTLCs } - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobCommitTx) + alice ! WatchFundingSpentTriggered(bobCommitTx) bobCommitTx } - test("recv BITCOIN_TX_CONFIRMED (future remote commit)") { f => + test("recv WatchTxConfirmedTriggered (future remote commit)") { f => import f._ val bobCommitTx = testFutureRemoteCommitTxConfirmed(f, ChannelVersion.STANDARD) // alice is able to claim its main output val claimMainTx = alice2blockchain.expectMsgType[PublishTx].tx Transaction.correctlySpends(claimMainTx, bobCommitTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS) - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === bobCommitTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === bobCommitTx.txid) awaitCond(alice.stateData.asInstanceOf[DATA_CLOSING].futureRemoteCommitPublished.isDefined) - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === claimMainTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === claimMainTx.txid) alice2blockchain.expectNoMsg(250 millis) // alice ignores the htlc-timeout // actual test starts here - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(bobCommitTx), 0, 0, bobCommitTx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimMainTx), 0, 0, claimMainTx) + alice ! WatchTxConfirmedTriggered(0, 0, bobCommitTx) + alice ! WatchTxConfirmedTriggered(0, 0, claimMainTx) awaitCond(alice.stateName == CLOSED) } - test("recv BITCOIN_TX_CONFIRMED (future remote commit, option_static_remotekey)", Tag(StateTestsTags.StaticRemoteKey)) { f => + test("recv WatchTxConfirmedTriggered (future remote commit, option_static_remotekey)", Tag(StateTestsTags.StaticRemoteKey)) { f => import f._ val bobCommitTx = testFutureRemoteCommitTxConfirmed(f, ChannelVersion.STATIC_REMOTEKEY) // using option_static_remotekey alice doesn't need to sweep her output awaitCond(alice.stateName == CLOSING, 10 seconds) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(bobCommitTx), 0, 0, bobCommitTx) + alice ! WatchTxConfirmedTriggered(0, 0, bobCommitTx) // after the commit tx is confirmed the channel is closed, no claim transactions needed awaitCond(alice.stateName == CLOSED, 10 seconds) } - test("recv BITCOIN_TX_CONFIRMED (future remote commit, anchor outputs)", Tag(StateTestsTags.AnchorOutputs)) { f => + test("recv WatchTxConfirmedTriggered (future remote commit, anchor outputs)", Tag(StateTestsTags.AnchorOutputs)) { f => import f._ val bobCommitTx = testFutureRemoteCommitTxConfirmed(f, ChannelVersion.ANCHOR_OUTPUTS) // alice is able to claim its main output val claimMainTx = alice2blockchain.expectMsgType[PublishTx].tx Transaction.correctlySpends(claimMainTx, bobCommitTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS) - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === bobCommitTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === bobCommitTx.txid) awaitCond(alice.stateData.asInstanceOf[DATA_CLOSING].futureRemoteCommitPublished.isDefined) - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === claimMainTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === claimMainTx.txid) alice2blockchain.expectNoMsg(250 millis) // alice ignores the htlc-timeout // actual test starts here - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(bobCommitTx), 0, 0, bobCommitTx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimMainTx), 0, 0, claimMainTx) + alice ! WatchTxConfirmedTriggered(0, 0, bobCommitTx) + alice ! WatchTxConfirmedTriggered(0, 0, claimMainTx) awaitCond(alice.stateName == CLOSED) } @@ -1100,8 +1099,8 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // then we should claim our main output val claimMainTx = alice2blockchain.expectMsgType[PublishTx].tx Transaction.correctlySpends(claimMainTx, bobCommitTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS) - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === bobCommitTx.txid) - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === claimMainTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === bobCommitTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === claimMainTx.txid) } case class RevokedCloseFixture(bobRevokedTxs: Seq[PublishableTxs], htlcsAlice: Seq[(UpdateAddHtlc, ByteVector32)], htlcsBob: Seq[(UpdateAddHtlc, ByteVector32)]) @@ -1177,7 +1176,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // bob publishes one of his revoked txs val bobRevokedTx = revokedCloseFixture.bobRevokedTxs(1).commitTx.tx - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobRevokedTx) + alice ! WatchFundingSpentTriggered(bobRevokedTx) awaitCond(alice.stateData.isInstanceOf[DATA_CLOSING]) awaitCond(alice.stateData.asInstanceOf[DATA_CLOSING].revokedCommitPublished.size == 1) @@ -1214,13 +1213,13 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with } // alice watches confirmation for the outputs only her can claim - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === bobRevokedTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === bobRevokedTx.txid) if (!channelVersion.paysDirectlyToWallet) { - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === rvk.claimMainOutputTx.get.tx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === rvk.claimMainOutputTx.get.tx.txid) } // alice watches outputs that can be spent by both parties - val watchedOutpoints = Seq(alice2blockchain.expectMsgType[WatchSpent], alice2blockchain.expectMsgType[WatchSpent], alice2blockchain.expectMsgType[WatchSpent]).map(_.outputIndex).toSet + val watchedOutpoints = Seq(alice2blockchain.expectMsgType[WatchOutputSpent], alice2blockchain.expectMsgType[WatchOutputSpent], alice2blockchain.expectMsgType[WatchOutputSpent]).map(_.outputIndex).toSet assert(watchedOutpoints === (rvk.mainPenaltyTx.get :: rvk.htlcPenaltyTxs).map(_.input.outPoint.index).toSet) alice2blockchain.expectNoMsg(1 second) @@ -1233,36 +1232,36 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with val (bobRevokedTx, rvk) = setupFundingSpentRevokedTx(f, channelVersion) // once all txs are confirmed, alice can move to the closed state - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(bobRevokedTx), 100, 3, bobRevokedTx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(rvk.mainPenaltyTx.get.tx), 110, 1, rvk.mainPenaltyTx.get.tx) + alice ! WatchTxConfirmedTriggered(100, 3, bobRevokedTx) + alice ! WatchTxConfirmedTriggered(110, 1, rvk.mainPenaltyTx.get.tx) if (!channelVersion.paysDirectlyToWallet) { - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(rvk.claimMainOutputTx.get.tx), 110, 2, rvk.claimMainOutputTx.get.tx) + alice ! WatchTxConfirmedTriggered(110, 2, rvk.claimMainOutputTx.get.tx) } - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(rvk.htlcPenaltyTxs(0).tx), 115, 0, rvk.htlcPenaltyTxs(0).tx) + alice ! WatchTxConfirmedTriggered(115, 0, rvk.htlcPenaltyTxs(0).tx) assert(alice.stateName === CLOSING) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(rvk.htlcPenaltyTxs(1).tx), 115, 2, rvk.htlcPenaltyTxs(1).tx) + alice ! WatchTxConfirmedTriggered(115, 2, rvk.htlcPenaltyTxs(1).tx) awaitCond(alice.stateName === CLOSED) } - test("recv BITCOIN_FUNDING_SPENT (one revoked tx)") { f => + test("recv WatchFundingSpentTriggered (one revoked tx)") { f => testFundingSpentRevokedTx(f, ChannelVersion.STANDARD) } - test("recv BITCOIN_FUNDING_SPENT (one revoked tx, option_static_remotekey)", Tag(StateTestsTags.StaticRemoteKey)) { f => + test("recv WatchFundingSpentTriggered (one revoked tx, option_static_remotekey)", Tag(StateTestsTags.StaticRemoteKey)) { f => testFundingSpentRevokedTx(f, ChannelVersion.STATIC_REMOTEKEY) } - test("recv BITCOIN_FUNDING_SPENT (one revoked tx, anchor outputs)", Tag(StateTestsTags.AnchorOutputs)) { f => + test("recv WatchFundingSpentTriggered (one revoked tx, anchor outputs)", Tag(StateTestsTags.AnchorOutputs)) { f => testFundingSpentRevokedTx(f, ChannelVersion.ANCHOR_OUTPUTS) } - test("recv BITCOIN_FUNDING_SPENT (multiple revoked tx)") { f => + test("recv WatchFundingSpentTriggered (multiple revoked tx)") { f => import f._ val revokedCloseFixture = prepareRevokedClose(f, ChannelVersion.STANDARD) assert(revokedCloseFixture.bobRevokedTxs.map(_.commitTx.tx.txid).toSet.size === revokedCloseFixture.bobRevokedTxs.size) // all commit txs are distinct def broadcastBobRevokedTx(revokedTx: Transaction, htlcCount: Int, revokedCount: Int): RevokedCommitPublished = { - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, revokedTx) + alice ! WatchFundingSpentTriggered(revokedTx) awaitCond(alice.stateData.isInstanceOf[DATA_CLOSING]) awaitCond(alice.stateData.asInstanceOf[DATA_CLOSING].revokedCommitPublished.size == revokedCount) assert(alice.stateData.asInstanceOf[DATA_CLOSING].revokedCommitPublished.last.commitTx === revokedTx) @@ -1274,12 +1273,12 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with (claimMain +: mainPenalty +: htlcPenaltyTxs).foreach(tx => Transaction.correctlySpends(tx, revokedTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)) // alice watches confirmation for the outputs only her can claim - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === revokedTx.txid) - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === claimMain.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === revokedTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === claimMain.txid) // alice watches outputs that can be spent by both parties - assert(alice2blockchain.expectMsgType[WatchSpent].outputIndex === mainPenalty.txIn.head.outPoint.index) - val htlcOutpoints = (1 to htlcCount).map(_ => alice2blockchain.expectMsgType[WatchSpent].outputIndex).toSet + assert(alice2blockchain.expectMsgType[WatchOutputSpent].outputIndex === mainPenalty.txIn.head.outPoint.index) + val htlcOutpoints = (1 to htlcCount).map(_ => alice2blockchain.expectMsgType[WatchOutputSpent].outputIndex).toSet assert(htlcOutpoints === htlcPenaltyTxs.flatMap(_.txIn.map(_.outPoint.index)).toSet) alice2blockchain.expectNoMsg(1 second) @@ -1295,12 +1294,12 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // bob's second revoked tx confirms: once all penalty txs are confirmed, alice can move to the closed state // NB: if multiple txs confirm in the same block, we may receive the events in any order - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(rvk2.mainPenaltyTx.get.tx), 100, 1, rvk2.mainPenaltyTx.get.tx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(rvk2.claimMainOutputTx.get.tx), 100, 2, rvk2.claimMainOutputTx.get.tx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(rvk2.commitTx), 100, 3, rvk2.commitTx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(rvk2.htlcPenaltyTxs(0).tx), 115, 0, rvk2.htlcPenaltyTxs(0).tx) + alice ! WatchTxConfirmedTriggered(100, 1, rvk2.mainPenaltyTx.get.tx) + alice ! WatchTxConfirmedTriggered(100, 2, rvk2.claimMainOutputTx.get.tx) + alice ! WatchTxConfirmedTriggered(100, 3, rvk2.commitTx) + alice ! WatchTxConfirmedTriggered(115, 0, rvk2.htlcPenaltyTxs(0).tx) assert(alice.stateName === CLOSING) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(rvk2.htlcPenaltyTxs(1).tx), 115, 2, rvk2.htlcPenaltyTxs(1).tx) + alice ! WatchTxConfirmedTriggered(115, 2, rvk2.htlcPenaltyTxs(1).tx) awaitCond(alice.stateName === CLOSED) } @@ -1317,15 +1316,15 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with awaitCond(alice.stateName == CLOSING) // the commit tx hasn't been confirmed yet, so we watch the funding output first - assert(alice2blockchain.expectMsgType[WatchSpent].event === BITCOIN_FUNDING_SPENT) + alice2blockchain.expectMsgType[WatchFundingSpent] // then we should re-publish unconfirmed transactions rvk.claimMainOutputTx.foreach(claimMain => assert(alice2blockchain.expectMsgType[PublishTx].tx === claimMain.tx)) assert(alice2blockchain.expectMsgType[PublishTx].tx === rvk.mainPenaltyTx.get.tx) rvk.htlcPenaltyTxs.foreach(htlcPenalty => assert(alice2blockchain.expectMsgType[PublishTx].tx === htlcPenalty.tx)) - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === bobRevokedTx.txid) - rvk.claimMainOutputTx.foreach(claimMain => assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === claimMain.tx.txid)) - assert(alice2blockchain.expectMsgType[WatchSpent].outputIndex === rvk.mainPenaltyTx.get.input.outPoint.index) - rvk.htlcPenaltyTxs.foreach(htlcPenalty => assert(alice2blockchain.expectMsgType[WatchSpent].outputIndex === htlcPenalty.input.outPoint.index)) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === bobRevokedTx.txid) + rvk.claimMainOutputTx.foreach(claimMain => assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === claimMain.tx.txid)) + assert(alice2blockchain.expectMsgType[WatchOutputSpent].outputIndex === rvk.mainPenaltyTx.get.input.outPoint.index) + rvk.htlcPenaltyTxs.foreach(htlcPenalty => assert(alice2blockchain.expectMsgType[WatchOutputSpent].outputIndex === htlcPenalty.input.outPoint.index)) } test("recv INPUT_RESTORED (one revoked tx)") { f => @@ -1344,7 +1343,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // bob publishes one of his revoked txs val bobRevokedTxs = revokedCloseFixture.bobRevokedTxs(2) - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobRevokedTxs.commitTx.tx) + alice ! WatchFundingSpentTriggered(bobRevokedTxs.commitTx.tx) awaitCond(alice.stateData.isInstanceOf[DATA_CLOSING]) awaitCond(alice.stateData.asInstanceOf[DATA_CLOSING].revokedCommitPublished.size == 1) @@ -1362,11 +1361,11 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // alice publishes the penalty txs and watches outputs val claimTxsCount = if (channelVersion.paysDirectlyToWallet) 5 else 6 // 2 main outputs and 4 htlcs (1 to claimTxsCount).foreach(_ => alice2blockchain.expectMsgType[PublishTx]) - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === rvk.commitTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === rvk.commitTx.txid) if (!channelVersion.paysDirectlyToWallet) { - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === rvk.claimMainOutputTx.get.tx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === rvk.claimMainOutputTx.get.tx.txid) } - (1 to 5).foreach(_ => alice2blockchain.expectMsgType[WatchSpent]) // main output penalty and 4 htlc penalties + (1 to 5).foreach(_ => alice2blockchain.expectMsgType[WatchOutputSpent]) // main output penalty and 4 htlc penalties alice2blockchain.expectNoMsg(1 second) // bob manages to claim 2 htlc outputs before alice can penalize him: 1 htlc-success and 1 htlc-timeout. @@ -1385,24 +1384,24 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(bobOutpoints.size === 2) // alice reacts by publishing penalty txs that spend bob's htlc transactions - alice ! WatchEventSpent(BITCOIN_OUTPUT_SPENT, bobHtlcSuccessTx1.tx) + alice ! WatchOutputSpentTriggered(bobHtlcSuccessTx1.tx) awaitCond(alice.stateData.asInstanceOf[DATA_CLOSING].revokedCommitPublished.head.claimHtlcDelayedPenaltyTxs.size == 1) val claimHtlcSuccessPenalty1 = alice.stateData.asInstanceOf[DATA_CLOSING].revokedCommitPublished.head.claimHtlcDelayedPenaltyTxs.last Transaction.correctlySpends(claimHtlcSuccessPenalty1.tx, bobHtlcSuccessTx1.tx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS) - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === bobHtlcSuccessTx1.tx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === bobHtlcSuccessTx1.tx.txid) assert(alice2blockchain.expectMsgType[PublishTx].tx === claimHtlcSuccessPenalty1.tx) - val watchSpent1 = alice2blockchain.expectMsgType[WatchSpent] + val watchSpent1 = alice2blockchain.expectMsgType[WatchOutputSpent] assert(watchSpent1.txId === bobHtlcSuccessTx1.tx.txid) assert(watchSpent1.outputIndex === claimHtlcSuccessPenalty1.input.outPoint.index) alice2blockchain.expectNoMsg(1 second) - alice ! WatchEventSpent(BITCOIN_OUTPUT_SPENT, bobHtlcTimeoutTx.tx) + alice ! WatchOutputSpentTriggered(bobHtlcTimeoutTx.tx) awaitCond(alice.stateData.asInstanceOf[DATA_CLOSING].revokedCommitPublished.head.claimHtlcDelayedPenaltyTxs.size == 2) val claimHtlcTimeoutPenalty = alice.stateData.asInstanceOf[DATA_CLOSING].revokedCommitPublished.head.claimHtlcDelayedPenaltyTxs.last Transaction.correctlySpends(claimHtlcTimeoutPenalty.tx, bobHtlcTimeoutTx.tx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS) - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === bobHtlcTimeoutTx.tx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === bobHtlcTimeoutTx.tx.txid) assert(alice2blockchain.expectMsgType[PublishTx].tx === claimHtlcTimeoutPenalty.tx) - val watchSpent2 = alice2blockchain.expectMsgType[WatchSpent] + val watchSpent2 = alice2blockchain.expectMsgType[WatchOutputSpent] assert(watchSpent2.txId === bobHtlcTimeoutTx.tx.txid) assert(watchSpent2.outputIndex === claimHtlcTimeoutPenalty.input.outPoint.index) alice2blockchain.expectNoMsg(1 second) @@ -1410,14 +1409,14 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // bob RBFs his htlc-success with a different transaction val bobHtlcSuccessTx2 = bobHtlcSuccessTx1.tx.copy(txIn = TxIn(OutPoint(randomBytes32, 0), Nil, 0) +: bobHtlcSuccessTx1.tx.txIn) assert(bobHtlcSuccessTx2.txid !== bobHtlcSuccessTx1.tx.txid) - alice ! WatchEventSpent(BITCOIN_OUTPUT_SPENT, bobHtlcSuccessTx2) + alice ! WatchOutputSpentTriggered(bobHtlcSuccessTx2) awaitCond(alice.stateData.asInstanceOf[DATA_CLOSING].revokedCommitPublished.head.claimHtlcDelayedPenaltyTxs.size == 3) val claimHtlcSuccessPenalty2 = alice.stateData.asInstanceOf[DATA_CLOSING].revokedCommitPublished.head.claimHtlcDelayedPenaltyTxs.last assert(claimHtlcSuccessPenalty1.tx.txid != claimHtlcSuccessPenalty2.tx.txid) Transaction.correctlySpends(claimHtlcSuccessPenalty2.tx, bobHtlcSuccessTx2 :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS) - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === bobHtlcSuccessTx2.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === bobHtlcSuccessTx2.txid) assert(alice2blockchain.expectMsgType[PublishTx].tx === claimHtlcSuccessPenalty2.tx) - val watchSpent3 = alice2blockchain.expectMsgType[WatchSpent] + val watchSpent3 = alice2blockchain.expectMsgType[WatchOutputSpent] assert(watchSpent3.txId === bobHtlcSuccessTx2.txid) assert(watchSpent3.outputIndex === claimHtlcSuccessPenalty2.input.outPoint.index) alice2blockchain.expectNoMsg(1 second) @@ -1425,41 +1424,41 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // transactions confirm: alice can move to the closed state val remainingHtlcPenaltyTxs = rvk.htlcPenaltyTxs.filterNot(htlcPenalty => bobOutpoints.contains(htlcPenalty.input.outPoint)) assert(remainingHtlcPenaltyTxs.size === 2) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(rvk.commitTx), 100, 3, rvk.commitTx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(rvk.mainPenaltyTx.get.tx), 110, 0, rvk.mainPenaltyTx.get.tx) + alice ! WatchTxConfirmedTriggered(100, 3, rvk.commitTx) + alice ! WatchTxConfirmedTriggered(110, 0, rvk.mainPenaltyTx.get.tx) if (!channelVersion.paysDirectlyToWallet) { - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(rvk.claimMainOutputTx.get.tx), 110, 1, rvk.claimMainOutputTx.get.tx) + alice ! WatchTxConfirmedTriggered(110, 1, rvk.claimMainOutputTx.get.tx) } - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(remainingHtlcPenaltyTxs.head.tx), 110, 2, remainingHtlcPenaltyTxs.head.tx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(remainingHtlcPenaltyTxs.last.tx), 115, 2, remainingHtlcPenaltyTxs.last.tx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(bobHtlcTimeoutTx.tx), 115, 0, bobHtlcTimeoutTx.tx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(bobHtlcSuccessTx2), 115, 1, bobHtlcSuccessTx2) + alice ! WatchTxConfirmedTriggered(110, 2, remainingHtlcPenaltyTxs.head.tx) + alice ! WatchTxConfirmedTriggered(115, 2, remainingHtlcPenaltyTxs.last.tx) + alice ! WatchTxConfirmedTriggered(115, 0, bobHtlcTimeoutTx.tx) + alice ! WatchTxConfirmedTriggered(115, 1, bobHtlcSuccessTx2) assert(alice.stateName === CLOSING) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcTimeoutPenalty.tx), 120, 0, claimHtlcTimeoutPenalty.tx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(claimHtlcSuccessPenalty2.tx), 121, 0, claimHtlcSuccessPenalty2.tx) + alice ! WatchTxConfirmedTriggered(120, 0, claimHtlcTimeoutPenalty.tx) + alice ! WatchTxConfirmedTriggered(121, 0, claimHtlcSuccessPenalty2.tx) awaitCond(alice.stateName === CLOSED) } - test("recv BITCOIN_OUTPUT_SPENT (one revoked tx, counterparty published htlc-success tx)") { f => + test("recv WatchOutputSpentTriggered (one revoked tx, counterparty published htlc-success tx)") { f => testOutputSpentRevokedTx(f, ChannelVersion.STANDARD) } - test("recv BITCOIN_OUTPUT_SPENT (one revoked tx, counterparty published htlc-success tx, option_static_remotekey)", Tag(StateTestsTags.StaticRemoteKey)) { f => + test("recv WatchOutputSpentTriggered (one revoked tx, counterparty published htlc-success tx, option_static_remotekey)", Tag(StateTestsTags.StaticRemoteKey)) { f => testOutputSpentRevokedTx(f, ChannelVersion.STATIC_REMOTEKEY) } - test("recv BITCOIN_OUTPUT_SPENT (one revoked tx, counterparty published htlc-success tx, anchor outputs)", Tag(StateTestsTags.AnchorOutputs)) { f => + test("recv WatchOutputSpentTriggered (one revoked tx, counterparty published htlc-success tx, anchor outputs)", Tag(StateTestsTags.AnchorOutputs)) { f => testOutputSpentRevokedTx(f, ChannelVersion.ANCHOR_OUTPUTS) } - test("recv BITCOIN_OUTPUT_SPENT (one revoked tx, counterparty published aggregated htlc tx)", Tag(StateTestsTags.AnchorOutputs)) { f => + test("recv WatchOutputSpentTriggered (one revoked tx, counterparty published aggregated htlc tx)", Tag(StateTestsTags.AnchorOutputs)) { f => import f._ // bob publishes one of his revoked txs val revokedCloseFixture = prepareRevokedClose(f, ChannelVersion.ANCHOR_OUTPUTS) val bobRevokedTxs = revokedCloseFixture.bobRevokedTxs(2) - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobRevokedTxs.commitTx.tx) + alice ! WatchFundingSpentTriggered(bobRevokedTxs.commitTx.tx) awaitCond(alice.stateData.isInstanceOf[DATA_CLOSING]) assert(alice.stateData.asInstanceOf[DATA_CLOSING].commitments.commitmentFormat === AnchorOutputsCommitmentFormat) awaitCond(alice.stateData.asInstanceOf[DATA_CLOSING].revokedCommitPublished.size == 1) @@ -1470,9 +1469,9 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // alice publishes the penalty txs and watches outputs (1 to 6).foreach(_ => alice2blockchain.expectMsgType[PublishTx]) // 2 main outputs and 4 htlcs - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === rvk.commitTx.txid) - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === rvk.claimMainOutputTx.get.tx.txid) - (1 to 5).foreach(_ => alice2blockchain.expectMsgType[WatchSpent]) // main output penalty and 4 htlc penalties + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === rvk.commitTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === rvk.claimMainOutputTx.get.tx.txid) + (1 to 5).foreach(_ => alice2blockchain.expectMsgType[WatchOutputSpent]) // main output penalty and 4 htlc penalties alice2blockchain.expectNoMsg(1 second) // bob claims multiple htlc outputs in a single transaction (this is possible with anchor outputs because signatures @@ -1506,13 +1505,13 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with ) // alice reacts by publishing penalty txs that spend bob's htlc transaction - alice ! WatchEventSpent(BITCOIN_OUTPUT_SPENT, bobHtlcTx) + alice ! WatchOutputSpentTriggered(bobHtlcTx) awaitCond(alice.stateData.asInstanceOf[DATA_CLOSING].revokedCommitPublished.head.claimHtlcDelayedPenaltyTxs.size == 4) val claimHtlcDelayedPenaltyTxs = alice.stateData.asInstanceOf[DATA_CLOSING].revokedCommitPublished.head.claimHtlcDelayedPenaltyTxs val spentOutpoints = Set(OutPoint(bobHtlcTx, 1), OutPoint(bobHtlcTx, 2), OutPoint(bobHtlcTx, 3), OutPoint(bobHtlcTx, 4)) assert(claimHtlcDelayedPenaltyTxs.map(_.input.outPoint).toSet === spentOutpoints) claimHtlcDelayedPenaltyTxs.foreach(claimHtlcPenalty => Transaction.correctlySpends(claimHtlcPenalty.tx, bobHtlcTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)) - assert(alice2blockchain.expectMsgType[WatchConfirmed].txId === bobHtlcTx.txid) + assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId === bobHtlcTx.txid) val publishedPenaltyTxs = Set( alice2blockchain.expectMsgType[PublishTx], alice2blockchain.expectMsgType[PublishTx], @@ -1521,10 +1520,10 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with ) assert(publishedPenaltyTxs.map(_.tx) === claimHtlcDelayedPenaltyTxs.map(_.tx).toSet) val watchedOutpoints = Seq( - alice2blockchain.expectMsgType[WatchSpent], - alice2blockchain.expectMsgType[WatchSpent], - alice2blockchain.expectMsgType[WatchSpent], - alice2blockchain.expectMsgType[WatchSpent] + alice2blockchain.expectMsgType[WatchOutputSpent], + alice2blockchain.expectMsgType[WatchOutputSpent], + alice2blockchain.expectMsgType[WatchOutputSpent], + alice2blockchain.expectMsgType[WatchOutputSpent] ).map(w => OutPoint(w.txId.reverse, w.outputIndex)).toSet assert(watchedOutpoints === spentOutpoints) alice2blockchain.expectNoMsg(1 second) @@ -1561,8 +1560,8 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with relayerA.expectNoMsg(1 second) // bob publishes one of his revoked txs which quickly confirms - alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobRevokedTx) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(bobRevokedTx), 100, 1, bobRevokedTx) + alice ! WatchFundingSpentTriggered(bobRevokedTx) + alice ! WatchTxConfirmedTriggered(100, 1, bobRevokedTx) awaitCond(alice.stateName === CLOSING) // alice should fail all pending htlcs @@ -1575,11 +1574,11 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with relayerA.expectNoMsg(1 second) } - test("recv BITCOIN_TX_CONFIRMED (one revoked tx, pending htlcs)") { f => + test("recv WatchTxConfirmedTriggered (one revoked tx, pending htlcs)") { f => testRevokedTxConfirmed(f, ChannelVersion.STANDARD) } - test("recv BITCOIN_TX_CONFIRMED (one revoked tx, pending htlcs, anchor outputs)", Tag(StateTestsTags.AnchorOutputs)) { f => + test("recv WatchTxConfirmedTriggered (one revoked tx, pending htlcs, anchor outputs)", Tag(StateTestsTags.AnchorOutputs)) { f => testRevokedTxConfirmed(f, ChannelVersion.ANCHOR_OUTPUTS) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/integration/PaymentIntegrationSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/integration/PaymentIntegrationSpec.scala index c1d87cc84e..126ff7f4ac 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/integration/PaymentIntegrationSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/integration/PaymentIntegrationSpec.scala @@ -16,14 +16,16 @@ package fr.acinq.eclair.integration +import akka.actor.typed.scaladsl.adapter.actorRefAdapter import akka.actor.ActorRef import akka.testkit.TestProbe import com.typesafe.config.ConfigFactory import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} import fr.acinq.bitcoin.{Block, ByteVector32, SatoshiLong} import fr.acinq.eclair.blockchain.bitcoind.BitcoindService.BitcoinReq +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher import fr.acinq.eclair.blockchain.bitcoind.rpc.ExtendedBitcoinClient -import fr.acinq.eclair.blockchain.{Watch, WatchConfirmed} +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{Watch, WatchFundingConfirmed} import fr.acinq.eclair.channel.Channel.{BroadcastChannelUpdate, PeriodicRefresh} import fr.acinq.eclair.channel._ import fr.acinq.eclair.crypto.Sphinx.DecryptedFailurePacket @@ -91,12 +93,12 @@ class PaymentIntegrationSpec extends IntegrationSpec { // we make sure all channels have set up their WatchConfirmed for the funding tx awaitCond({ - val watches = nodes.values.foldLeft(Set.empty[Watch]) { + val watches = nodes.values.foldLeft(Set.empty[Watch[_]]) { case (watches, setup) => - sender.send(setup.watcher, Symbol("watches")) - watches ++ sender.expectMsgType[Set[Watch]] + setup.watcher ! ZmqWatcher.ListWatches(sender.ref) + watches ++ sender.expectMsgType[Set[Watch[_]]] } - watches.count(_.isInstanceOf[WatchConfirmed]) == channelEndpointsCount + watches.count(_.isInstanceOf[WatchFundingConfirmed]) == channelEndpointsCount }, max = 20 seconds, interval = 1 second) // confirming the funding tx diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/RustyTestsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/RustyTestsSpec.scala index 03669d4bf4..4dd5aefdee 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/RustyTestsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/RustyTestsSpec.scala @@ -16,11 +16,13 @@ package fr.acinq.eclair.interop.rustytests +import akka.actor.typed.scaladsl.adapter.actorRefAdapter import akka.actor.{ActorRef, Props} import akka.testkit.{TestFSMRef, TestKit, TestProbe} import fr.acinq.bitcoin.{ByteVector32, SatoshiLong} import fr.acinq.eclair.TestConstants.{Alice, Bob, TestFeeEstimator} -import fr.acinq.eclair.blockchain._ +import fr.acinq.eclair.blockchain.TestWallet +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._ import fr.acinq.eclair.blockchain.fee.{FeeratePerKw, FeeratesPerKw} import fr.acinq.eclair.channel._ import fr.acinq.eclair.channel.states.StateTestsHelperMethods.FakeTxPublisherFactory @@ -77,17 +79,17 @@ class RustyTestsSpec extends TestKitBaseClass with Matchers with FixtureAnyFunSu pipe ! (alice, bob) within(30 seconds) { alice2blockchain.expectMsgType[TxPublisher.SetChannelId] - alice2blockchain.expectMsgType[WatchSpent] - alice2blockchain.expectMsgType[WatchConfirmed] + alice2blockchain.expectMsgType[WatchFundingSpent] + alice2blockchain.expectMsgType[WatchFundingConfirmed] bob2blockchain.expectMsgType[TxPublisher.SetChannelId] - bob2blockchain.expectMsgType[WatchSpent] - bob2blockchain.expectMsgType[WatchConfirmed] + bob2blockchain.expectMsgType[WatchFundingSpent] + bob2blockchain.expectMsgType[WatchFundingConfirmed] awaitCond(alice.stateName == WAIT_FOR_FUNDING_CONFIRMED) val fundingTx = alice.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_CONFIRMED].fundingTx.get - alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEPTHOK, 400000, 42, fundingTx) - bob ! WatchEventConfirmed(BITCOIN_FUNDING_DEPTHOK, 400000, 42, fundingTx) - alice2blockchain.expectMsgType[WatchLost] - bob2blockchain.expectMsgType[WatchLost] + alice ! WatchFundingConfirmedTriggered(400000, 42, fundingTx) + bob ! WatchFundingConfirmedTriggered(400000, 42, fundingTx) + alice2blockchain.expectMsgType[WatchFundingLost] + bob2blockchain.expectMsgType[WatchFundingLost] awaitCond(alice.stateName == NORMAL) awaitCond(bob.stateName == NORMAL) pipe ! new File(getClass.getResource(s"/scenarii/${test.name}.script").getFile) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala index c4049966db..7b6b6bda9f 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala @@ -23,7 +23,7 @@ import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.bitcoin.Script.{pay2wsh, write} import fr.acinq.bitcoin.{Block, ByteVector32, Crypto, SatoshiLong, Transaction, TxOut} import fr.acinq.eclair._ -import fr.acinq.eclair.blockchain.{UtxoStatus, ValidateRequest, ValidateResult, WatchSpentBasic} +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{UtxoStatus, ValidateRequest, ValidateResult, WatchExternalChannelSpent} import fr.acinq.eclair.channel.Register.{ForwardShortId, ForwardShortIdFailure} import fr.acinq.eclair.channel._ import fr.acinq.eclair.crypto.Sphinx @@ -628,9 +628,9 @@ class PaymentLifecycleSpec extends BaseRouterSpec { router ! PeerRoutingMessage(peerConnection.ref, remoteNodeId, chan_bh) router ! PeerRoutingMessage(peerConnection.ref, remoteNodeId, channelUpdate_bh) router ! PeerRoutingMessage(peerConnection.ref, remoteNodeId, channelUpdate_hb) - watcher.expectMsg(ValidateRequest(chan_bh)) + assert(watcher.expectMsgType[ValidateRequest].ann === chan_bh) watcher.send(router, ValidateResult(chan_bh, Right((Transaction(version = 0, txIn = Nil, txOut = TxOut(1000000 sat, write(pay2wsh(Scripts.multiSig2of2(funding_b, funding_h)))) :: Nil, lockTime = 0), UtxoStatus.Unspent)))) - watcher.expectMsgType[WatchSpentBasic] + watcher.expectMsgType[WatchExternalChannelSpent] val payFixture = createPaymentLifecycle() import payFixture._ diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PostRestartHtlcCleanerSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PostRestartHtlcCleanerSpec.scala index be8329f1ab..d6438ccf37 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PostRestartHtlcCleanerSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PostRestartHtlcCleanerSpec.scala @@ -22,7 +22,7 @@ import akka.event.LoggingAdapter import akka.testkit.TestProbe import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.bitcoin.{Block, ByteVector32, Crypto, Satoshi, SatoshiLong} -import fr.acinq.eclair.blockchain.WatchEventConfirmed +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.WatchTxConfirmedTriggered import fr.acinq.eclair.channel._ import fr.acinq.eclair.channel.states.StateTestsHelperMethods import fr.acinq.eclair.db.{OutgoingPayment, OutgoingPaymentStatus, PaymentType} @@ -375,13 +375,13 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit } val closingState = localClose(alice, alice2blockchain) - alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(closingState.commitTx), 42, 0, closingState.commitTx) + alice ! WatchTxConfirmedTriggered(42, 0, closingState.commitTx) // All committed htlcs timed out except the last two; one will be fulfilled later and the other will timeout later. assert(closingState.htlcTxs.size === 4) assert(getHtlcTimeoutTxs(closingState).length === 4) val htlcTxs = getHtlcTimeoutTxs(closingState).sortBy(_.tx.txOut.map(_.amount).sum) htlcTxs.reverse.drop(2).zipWithIndex.foreach { - case (htlcTx, i) => alice ! WatchEventConfirmed(BITCOIN_TX_CONFIRMED(htlcTx.tx), 201, i, htlcTx.tx) + case (htlcTx, i) => alice ! WatchTxConfirmedTriggered(201, i, htlcTx.tx) } (alice.stateData.asInstanceOf[DATA_CLOSING], htlc_2_2) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsBatchValidationSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsBatchValidationSpec.scala index 3d8d789744..ace58ec3e9 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsBatchValidationSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsBatchValidationSpec.scala @@ -22,7 +22,7 @@ import akka.testkit.TestProbe import com.softwaremill.sttp.okhttp.OkHttpFutureBackend import fr.acinq.bitcoin.Crypto.PrivateKey import fr.acinq.bitcoin.{Block, Satoshi, SatoshiLong, Script, Transaction} -import fr.acinq.eclair.blockchain.ValidateResult +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.ValidateResult import fr.acinq.eclair.blockchain.bitcoind.BitcoinCoreWallet import fr.acinq.eclair.blockchain.bitcoind.rpc.{BasicBitcoinJsonRPCClient, ExtendedBitcoinClient} import fr.acinq.eclair.blockchain.fee.FeeratePerKw diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/BaseRouterSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/BaseRouterSpec.scala index 9e0e0ea271..c0d99379e9 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/BaseRouterSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/BaseRouterSpec.scala @@ -17,12 +17,13 @@ package fr.acinq.eclair.router import akka.actor.ActorRef +import akka.actor.typed.scaladsl.adapter.actorRefAdapter import akka.testkit.TestProbe import fr.acinq.bitcoin.Crypto.PrivateKey import fr.acinq.bitcoin.Script.{pay2wsh, write} import fr.acinq.bitcoin.{Block, ByteVector32, SatoshiLong, Transaction, TxOut} import fr.acinq.eclair.TestConstants.Alice -import fr.acinq.eclair.blockchain.{UtxoStatus, ValidateRequest, ValidateResult, WatchSpentBasic} +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{UtxoStatus, ValidateRequest, ValidateResult, WatchExternalChannelSpent} import fr.acinq.eclair.channel.{CommitmentsSpec, LocalChannelUpdate} import fr.acinq.eclair.crypto.TransportHandler import fr.acinq.eclair.crypto.keymanager.{LocalChannelKeyManager, LocalNodeKeyManager} @@ -151,11 +152,11 @@ abstract class BaseRouterSpec extends TestKitBaseClass with FixtureAnyFunSuiteLi // then private channels sender.send(router, LocalChannelUpdate(sender.ref, randomBytes32, channelId_ag, g, None, update_ag, CommitmentsSpec.makeCommitments(30000000 msat, 8000000 msat, a, g, announceChannel = false))) // watcher receives the get tx requests - watcher.expectMsg(ValidateRequest(chan_ab)) - watcher.expectMsg(ValidateRequest(chan_bc)) - watcher.expectMsg(ValidateRequest(chan_cd)) - watcher.expectMsg(ValidateRequest(chan_ef)) - watcher.expectMsg(ValidateRequest(chan_gh)) + assert(watcher.expectMsgType[ValidateRequest].ann === chan_ab) + assert(watcher.expectMsgType[ValidateRequest].ann === chan_bc) + assert(watcher.expectMsgType[ValidateRequest].ann === chan_cd) + assert(watcher.expectMsgType[ValidateRequest].ann === chan_ef) + assert(watcher.expectMsgType[ValidateRequest].ann === chan_gh) // and answers with valid scripts watcher.send(router, ValidateResult(chan_ab, Right((Transaction(version = 0, txIn = Nil, txOut = TxOut(publicChannelCapacity, write(pay2wsh(Scripts.multiSig2of2(funding_a, funding_b)))) :: Nil, lockTime = 0), UtxoStatus.Unspent)))) watcher.send(router, ValidateResult(chan_bc, Right((Transaction(version = 0, txIn = Nil, txOut = TxOut(publicChannelCapacity, write(pay2wsh(Scripts.multiSig2of2(funding_b, funding_c)))) :: Nil, lockTime = 0), UtxoStatus.Unspent)))) @@ -163,11 +164,14 @@ abstract class BaseRouterSpec extends TestKitBaseClass with FixtureAnyFunSuiteLi watcher.send(router, ValidateResult(chan_ef, Right((Transaction(version = 0, txIn = Nil, txOut = TxOut(publicChannelCapacity, write(pay2wsh(Scripts.multiSig2of2(funding_e, funding_f)))) :: Nil, lockTime = 0), UtxoStatus.Unspent)))) watcher.send(router, ValidateResult(chan_gh, Right((Transaction(version = 0, txIn = Nil, txOut = TxOut(publicChannelCapacity, write(pay2wsh(Scripts.multiSig2of2(funding_g, funding_h)))) :: Nil, lockTime = 0), UtxoStatus.Unspent)))) // watcher receives watch-spent request - watcher.expectMsgType[WatchSpentBasic] - watcher.expectMsgType[WatchSpentBasic] - watcher.expectMsgType[WatchSpentBasic] - watcher.expectMsgType[WatchSpentBasic] - watcher.expectMsgType[WatchSpentBasic] + val watchedShortChannelIds = Set( + watcher.expectMsgType[WatchExternalChannelSpent].shortChannelId, + watcher.expectMsgType[WatchExternalChannelSpent].shortChannelId, + watcher.expectMsgType[WatchExternalChannelSpent].shortChannelId, + watcher.expectMsgType[WatchExternalChannelSpent].shortChannelId, + watcher.expectMsgType[WatchExternalChannelSpent].shortChannelId, + ) + assert(watchedShortChannelIds === Set(channelId_ab, channelId_bc, channelId_cd, channelId_ef, channelId_gh)) // all messages are acked peerConnection.expectMsgAllOf( GossipDecision.Accepted(chan_ab), diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/RouterSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/RouterSpec.scala index d7210c48d3..0449833478 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/RouterSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/RouterSpec.scala @@ -22,8 +22,8 @@ import akka.testkit.TestProbe import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.bitcoin.Script.{pay2wsh, write} import fr.acinq.bitcoin.{Block, SatoshiLong, Transaction, TxOut} -import fr.acinq.eclair.blockchain._ -import fr.acinq.eclair.channel.{AvailableBalanceChanged, BITCOIN_FUNDING_EXTERNAL_CHANNEL_SPENT, CommitmentsSpec, LocalChannelUpdate} +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._ +import fr.acinq.eclair.channel.{AvailableBalanceChanged, CommitmentsSpec, LocalChannelUpdate} import fr.acinq.eclair.crypto.TransportHandler import fr.acinq.eclair.io.Peer.PeerRoutingMessage import fr.acinq.eclair.payment.PaymentRequest.ExtraHop @@ -58,12 +58,12 @@ class RouterSpec extends BaseRouterSpec { val node_c = makeNodeAnnouncement(priv_c, "node-C", Color(123, 100, -40), Nil, TestConstants.Bob.nodeParams.features, timestamp = System.currentTimeMillis.milliseconds.toSeconds + 1) peerConnection.send(router, PeerRoutingMessage(peerConnection.ref, remoteNodeId, chan_ac)) peerConnection.expectNoMsg(100 millis) // we don't immediately acknowledge the announcement (back pressure) - watcher.expectMsg(ValidateRequest(chan_ac)) + assert(watcher.expectMsgType[ValidateRequest].ann === chan_ac) watcher.send(router, ValidateResult(chan_ac, Right(Transaction(version = 0, txIn = Nil, txOut = TxOut(1000000 sat, write(pay2wsh(Scripts.multiSig2of2(funding_a, funding_c)))) :: Nil, lockTime = 0), UtxoStatus.Unspent))) peerConnection.expectMsg(TransportHandler.ReadAck(chan_ac)) peerConnection.expectMsg(GossipDecision.Accepted(chan_ac)) assert(peerConnection.sender() == router) - watcher.expectMsgType[WatchSpentBasic] + assert(watcher.expectMsgType[WatchExternalChannelSpent].shortChannelId === chan_ac.shortChannelId) peerConnection.send(router, PeerRoutingMessage(peerConnection.ref, remoteNodeId, update_ac)) peerConnection.expectMsg(TransportHandler.ReadAck(update_ac)) peerConnection.expectMsg(GossipDecision.Accepted(update_ac)) @@ -88,7 +88,7 @@ class RouterSpec extends BaseRouterSpec { val node_u = makeNodeAnnouncement(priv_u, "node-U", Color(-120, -20, 60), Nil, Features.empty) peerConnection.send(router, PeerRoutingMessage(peerConnection.ref, remoteNodeId, chan_uc)) peerConnection.expectNoMsg(200 millis) // we don't immediately acknowledge the announcement (back pressure) - watcher.expectMsg(ValidateRequest(chan_uc)) + assert(watcher.expectMsgType[ValidateRequest].ann === chan_uc) peerConnection.send(router, PeerRoutingMessage(peerConnection.ref, remoteNodeId, update_uc)) peerConnection.expectMsg(TransportHandler.ReadAck(update_uc)) peerConnection.send(router, PeerRoutingMessage(peerConnection.ref, remoteNodeId, node_u)) @@ -97,7 +97,7 @@ class RouterSpec extends BaseRouterSpec { peerConnection.expectMsg(TransportHandler.ReadAck(chan_uc)) peerConnection.expectMsg(GossipDecision.Accepted(chan_uc)) assert(peerConnection.sender() == router) - watcher.expectMsgType[WatchSpentBasic] + assert(watcher.expectMsgType[WatchExternalChannelSpent].shortChannelId === chan_uc.shortChannelId) peerConnection.expectMsg(GossipDecision.Accepted(update_uc)) peerConnection.expectMsg(GossipDecision.Accepted(node_u)) eventListener.expectMsg(ChannelsDiscovered(SingleChannelDiscovered(chan_uc, 2000000 sat, None, None) :: Nil)) @@ -193,7 +193,7 @@ class RouterSpec extends BaseRouterSpec { val update_ay = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, priv_y.publicKey, chan_ay.shortChannelId, CltvExpiryDelta(7), 0 msat, 766000 msat, 10, htlcMaximum) val node_y = makeNodeAnnouncement(priv_y, "node-Y", Color(123, 100, -40), Nil, TestConstants.Bob.nodeParams.features) peerConnection.send(router, PeerRoutingMessage(peerConnection.ref, remoteNodeId, chan_ay)) - watcher.expectMsg(ValidateRequest(chan_ay)) + assert(watcher.expectMsgType[ValidateRequest].ann === chan_ay) peerConnection.send(router, PeerRoutingMessage(peerConnection.ref, remoteNodeId, update_ay)) peerConnection.expectMsg(TransportHandler.ReadAck(update_ay)) peerConnection.send(router, PeerRoutingMessage(peerConnection.ref, remoteNodeId, node_y)) @@ -213,7 +213,7 @@ class RouterSpec extends BaseRouterSpec { val priv_x = randomKey val chan_ax = channelAnnouncement(ShortChannelId(42001), priv_a, priv_x, priv_funding_a, randomKey) peerConnection.send(router, PeerRoutingMessage(peerConnection.ref, remoteNodeId, chan_ax)) - watcher.expectMsg(ValidateRequest(chan_ax)) + assert(watcher.expectMsgType[ValidateRequest].ann === chan_ax) watcher.send(router, ValidateResult(chan_ax, Left(new RuntimeException("funding tx not found")))) peerConnection.expectMsg(TransportHandler.ReadAck(chan_ax)) peerConnection.expectMsg(GossipDecision.ValidationFailure(chan_ax)) @@ -228,7 +228,7 @@ class RouterSpec extends BaseRouterSpec { val priv_funding_z = randomKey val chan_az = channelAnnouncement(ShortChannelId(42003), priv_a, priv_z, priv_funding_a, priv_funding_z) peerConnection.send(router, PeerRoutingMessage(peerConnection.ref, remoteNodeId, chan_az)) - watcher.expectMsg(ValidateRequest(chan_az)) + assert(watcher.expectMsgType[ValidateRequest].ann === chan_az) watcher.send(router, ValidateResult(chan_az, Right(Transaction(version = 0, txIn = Nil, txOut = TxOut(1000000 sat, write(pay2wsh(Scripts.multiSig2of2(funding_a, priv_funding_z.publicKey)))) :: Nil, lockTime = 0), UtxoStatus.Spent(spendingTxConfirmed = false)))) peerConnection.expectMsg(TransportHandler.ReadAck(chan_az)) peerConnection.expectMsg(GossipDecision.ChannelClosing(chan_az)) @@ -243,7 +243,7 @@ class RouterSpec extends BaseRouterSpec { val priv_funding_z = randomKey val chan_az = channelAnnouncement(ShortChannelId(42003), priv_a, priv_z, priv_funding_a, priv_funding_z) peerConnection.send(router, PeerRoutingMessage(peerConnection.ref, remoteNodeId, chan_az)) - watcher.expectMsg(ValidateRequest(chan_az)) + assert(watcher.expectMsgType[ValidateRequest].ann === chan_az) watcher.send(router, ValidateResult(chan_az, Right(Transaction(version = 0, txIn = Nil, txOut = TxOut(1000000 sat, write(pay2wsh(Scripts.multiSig2of2(funding_a, priv_funding_z.publicKey)))) :: Nil, lockTime = 0), UtxoStatus.Spent(spendingTxConfirmed = true)))) peerConnection.expectMsg(TransportHandler.ReadAck(chan_az)) peerConnection.expectMsg(GossipDecision.ChannelClosed(chan_az)) @@ -261,19 +261,19 @@ class RouterSpec extends BaseRouterSpec { val eventListener = TestProbe() system.eventStream.subscribe(eventListener.ref, classOf[NetworkEvent]) - router ! WatchEventSpentBasic(BITCOIN_FUNDING_EXTERNAL_CHANNEL_SPENT(channelId_ab)) + router ! WatchExternalChannelSpentTriggered(channelId_ab) eventListener.expectMsg(ChannelLost(channelId_ab)) // a doesn't have any channels, b still has one with c eventListener.expectMsg(NodeLost(a)) eventListener.expectNoMsg(200 milliseconds) - router ! WatchEventSpentBasic(BITCOIN_FUNDING_EXTERNAL_CHANNEL_SPENT(channelId_cd)) + router ! WatchExternalChannelSpentTriggered(channelId_cd) eventListener.expectMsg(ChannelLost(channelId_cd)) // d doesn't have any channels, c still has one with b eventListener.expectMsg(NodeLost(d)) eventListener.expectNoMsg(200 milliseconds) - router ! WatchEventSpentBasic(BITCOIN_FUNDING_EXTERNAL_CHANNEL_SPENT(channelId_bc)) + router ! WatchExternalChannelSpentTriggered(channelId_bc) eventListener.expectMsg(ChannelLost(channelId_bc)) // now b and c do not have any channels eventListener.expectMsgAllOf(NodeLost(b), NodeLost(c)) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/RoutingSyncSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/RoutingSyncSpec.scala index 20311a2d5f..8c06931d3f 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/RoutingSyncSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/RoutingSyncSpec.scala @@ -16,13 +16,14 @@ package fr.acinq.eclair.router +import akka.actor.typed.scaladsl.adapter.actorRefAdapter import akka.actor.{Actor, Props} import akka.testkit.{TestFSMRef, TestProbe} import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} import fr.acinq.bitcoin.{Block, ByteVector32, Satoshi, Script, Transaction, TxIn, TxOut} import fr.acinq.eclair.TestConstants.{Alice, Bob} import fr.acinq.eclair._ -import fr.acinq.eclair.blockchain.{UtxoStatus, ValidateRequest, ValidateResult} +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{UtxoStatus, ValidateRequest, ValidateResult} import fr.acinq.eclair.crypto.TransportHandler import fr.acinq.eclair.io.Peer.PeerRoutingMessage import fr.acinq.eclair.router.Announcements.{makeChannelUpdate, makeNodeAnnouncement} @@ -53,7 +54,7 @@ class RoutingSyncSpec extends TestKitBaseClass with AnyFunSuiteLike with Paralle class YesWatcher extends Actor { override def receive: Receive = { - case ValidateRequest(c) => + case ValidateRequest(replyTo, c) => val pubkeyScript = Script.write(Script.pay2wsh(Scripts.multiSig2of2(c.bitcoinKey1, c.bitcoinKey2))) val TxCoordinates(_, _, outputIndex) = ShortChannelId.coordinates(c.shortChannelId) val fakeFundingTx = Transaction( @@ -61,7 +62,7 @@ class RoutingSyncSpec extends TestKitBaseClass with AnyFunSuiteLike with Paralle txIn = Seq.empty[TxIn], txOut = List.fill(outputIndex + 1)(TxOut(Satoshi(0), pubkeyScript)), // quick and dirty way to be sure that the outputIndex'th output is of the expected format lockTime = 0) - sender ! ValidateResult(c, Right(fakeFundingTx, UtxoStatus.Unspent)) + replyTo ! ValidateResult(c, Right(fakeFundingTx, UtxoStatus.Unspent)) } } diff --git a/eclair-front/src/test/scala/fr/acinq/eclair/router/FrontRouterSpec.scala b/eclair-front/src/test/scala/fr/acinq/eclair/router/FrontRouterSpec.scala index f6b7080422..3752ff89d1 100644 --- a/eclair-front/src/test/scala/fr/acinq/eclair/router/FrontRouterSpec.scala +++ b/eclair-front/src/test/scala/fr/acinq/eclair/router/FrontRouterSpec.scala @@ -16,6 +16,7 @@ package fr.acinq.eclair.router +import akka.actor.typed.scaladsl.adapter.actorRefAdapter import akka.actor.ActorSystem import akka.testkit.{TestKit, TestProbe} import fr.acinq.bitcoin.Crypto.PrivateKey @@ -23,7 +24,7 @@ import fr.acinq.bitcoin.Script.{pay2wsh, write} import fr.acinq.bitcoin.{Block, SatoshiLong, Transaction, TxOut} import fr.acinq.eclair.TestConstants.Alice import fr.acinq.eclair._ -import fr.acinq.eclair.blockchain.{UtxoStatus, ValidateRequest, ValidateResult} +import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{UtxoStatus, ValidateRequest, ValidateResult} import fr.acinq.eclair.crypto.TransportHandler import fr.acinq.eclair.io.Peer.PeerRoutingMessage import fr.acinq.eclair.router.Announcements.{makeChannelAnnouncement, makeChannelUpdate, makeNodeAnnouncement} @@ -91,7 +92,7 @@ class FrontRouterSpec extends TestKit(ActorSystem("test")) with AnyFunSuiteLike pipe1.expectMsg(PeerRoutingMessage(front1, origin1a.nodeId, chan_ab)) pipe1.send(router, PeerRoutingMessage(pipe1.ref, origin1a.nodeId, chan_ab)) - watcher.expectMsg(ValidateRequest(chan_ab)) + assert(watcher.expectMsgType[ValidateRequest].ann === chan_ab) peerConnection1b.send(front1, PeerRoutingMessage(peerConnection1b.ref, origin1b.nodeId, chan_ab)) pipe1.expectNoMessage() @@ -168,7 +169,7 @@ class FrontRouterSpec extends TestKit(ActorSystem("test")) with AnyFunSuiteLike val origin3a = RemoteGossip(peerConnection3a.ref, randomKey.publicKey) peerConnection1a.send(front1, PeerRoutingMessage(peerConnection1a.ref, origin1a.nodeId, chan_ab)) - watcher.expectMsg(ValidateRequest(chan_ab)) + assert(watcher.expectMsgType[ValidateRequest].ann === chan_ab) peerConnection1b.send(front1, PeerRoutingMessage(peerConnection1b.ref, origin1b.nodeId, chan_ab)) peerConnection2a.send(front2, PeerRoutingMessage(peerConnection2a.ref, origin2a.nodeId, chan_ab))