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

implemented basic announcement broadcast

This commit is contained in:
pm47 2017-01-27 17:04:42 +01:00
parent 6d16f315fe
commit ec07cc46ab
43 changed files with 472 additions and 324 deletions

View file

@ -17,11 +17,19 @@ eclair {
}
node {
seed = 0102030405060708010203040506070801020304050607080102030405060708
alias = "eclair"
color {
r = 73
g = 218
b = 170
}
}
delay-blocks = 144
mindepth-blocks = 3
base-fee = 546000
proportional-fee = 10
expiry-delta-blocks = 144
htlc-minimum-msat = 1000000
fee-base-msat = 546000
fee-proportional-msat = 10
payment-handler = "local"
}
akka {

View file

@ -4,13 +4,14 @@
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<target>System.out</target>
<encoder>
<pattern>${HOSTNAME} %d %-5level %logger{36} %X{akkaSource} - %msg%ex{12}%n</pattern>
<pattern>${HOSTNAME} %d %-5level %logger{36} %X{akkaSource} - %msg%ex{24}%n</pattern>
</encoder>
</appender>
<logger name="fr.acinq.eclair.channel" level="DEBUG"/>
<logger name="fr.acinq.eclair.channel.Register" level="DEBUG"/>
<logger name="fr.acinq.eclair.crypto.TransportHandler" level="INFO"/>
<logger name="fr.acinq.eclair.router" level="DEBUG"/>
<root level="INFO">
<appender-ref ref="CONSOLE"/>

View file

@ -1,5 +1,6 @@
package fr.acinq.eclair
import java.net.InetSocketAddress
import javafx.application.{Application, Platform}
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
@ -42,7 +43,7 @@ object Boot extends App with Logging {
class Setup() extends Logging {
logger.info(s"hello!")
logger.info(s"nodeid=${Globals.Node.publicKey.toBin}")
logger.info(s"nodeid=${Globals.Node.publicKey.toBin} alias=${Globals.Node.alias}")
val config = ConfigFactory.load()
implicit lazy val system = ActorSystem()
@ -66,6 +67,7 @@ class Setup() extends Logging {
// TODO: we should use p2wpkh instead of p2pkh as soon as bitcoind supports it
//val finalScriptPubKey = OP_0 :: OP_PUSHDATA(Base58Check.decode(finalAddress)._2) :: Nil
val finalScriptPubKey = OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Base58Check.decode(finalAddress)._2) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil
val socket = new InetSocketAddress(config.getString("eclair.server.host"), config.getInt("eclair.server.port"))
val fatalEventPromise = Promise[FatalEvent]()
system.actorOf(Props(new Actor {
@ -84,11 +86,10 @@ class Setup() extends Logging {
case "local" => system.actorOf(Props[LocalPaymentHandler], name = "payment-handler")
case "noop" => system.actorOf(Props[NoopPaymentHandler], name = "payment-handler")
}
val register = system.actorOf(Register.props(watcher, paymentHandler, finalScriptPubKey), name = "register")
val selector = system.actorOf(Props[ChannelSelector], name = "selector")
val router = system.actorOf(Props[Router], name = "router")
val ircWatcher = system.actorOf(Props[IRCWatcher], "irc")
val router = system.actorOf(Router.props(watcher, Globals.Node.announcement), name = "router")
val paymentInitiator = system.actorOf(PaymentInitiator.props(router, selector, blockCount), "payment-spawner")
val register = system.actorOf(Register.props(watcher, router, paymentHandler, finalScriptPubKey), name = "register")
val server = system.actorOf(Server.props(config.getString("eclair.server.host"), config.getInt("eclair.server.port"), register), "server")
val _setup = this

View file

@ -1,8 +1,12 @@
package fr.acinq.eclair
import java.net.InetSocketAddress
import com.typesafe.config.ConfigFactory
import fr.acinq.bitcoin.{BinaryData, DeterministicWallet}
import fr.acinq.eclair.router.Router
import scala.compat.Platform
import scala.concurrent.duration._
@ -20,13 +24,20 @@ object Globals {
val extendedPublicKey = DeterministicWallet.publicKey(extendedPrivateKey)
val publicKey = extendedPublicKey.publicKey
val id = publicKey.toBin.toString()
val alias = config.getString("node.alias").take(32)
val color: (Byte, Byte, Byte) = (config.getInt("node.color.r").toByte, config.getInt("node.color.g").toByte, config.getInt("node.color.b").toByte)
val address = new InetSocketAddress(config.getString("server.host"), config.getInt("server.port"))
val announcement = Router.makeNodeAnnouncement(privateKey, alias, color, address :: Nil, Platform.currentTime / 1000)
}
val default_delay_blocks = config.getInt("delay-blocks")
val default_mindepth_blocks = config.getInt("mindepth-blocks")
val default_feeratePerKw = 10000
val base_fee = config.getInt("base-fee")
val proportional_fee = config.getInt("proportional-fee")
val expiry_delta_blocks = config.getInt("expiry-delta-blocks")
val htlc_minimum_msat = config.getInt("htlc-minimum-msat")
val delay_blocks = config.getInt("delay-blocks")
val mindepth_blocks = config.getInt("mindepth-blocks")
val feeratePerKw = 10000
val fee_base_msat = config.getInt("fee-base-msat")
val fee_proportional_msat = config.getInt("fee-proportional-msat")
val default_anchor_amount = 1000000
val autosign_interval = 300 milliseconds
}

View file

@ -84,14 +84,14 @@ trait Service extends Logging {
(paymentInitiator ? CreatePayment(amount.toInt, BinaryData(rhash), BinaryData(nodeId))).mapTo[ChannelEvent]
case JsonRPCBody(_, _, "genh", _) =>
(paymentHandler ? 'genh).mapTo[BinaryData]
case JsonRPCBody(_, _, "sign", JString(channel) :: Nil) =>
(register ? SendCommand(channel, CMD_SIGN)).mapTo[ActorRef].map(_ => "ok")
case JsonRPCBody(_, _, "sign", JInt(channel) :: Nil) =>
(register ? SendCommand(channel.toLong, CMD_SIGN)).mapTo[ActorRef].map(_ => "ok")
case JsonRPCBody(_, _, "fulfillhtlc", JString(channel) :: JDouble(id) :: JString(r) :: Nil) =>
(register ? SendCommand(channel, CMD_FULFILL_HTLC(id.toLong, BinaryData(r), commit = true))).mapTo[ActorRef].map(_ => "ok")
(register ? SendCommand(channel.toLong, CMD_FULFILL_HTLC(id.toLong, BinaryData(r), commit = true))).mapTo[ActorRef].map(_ => "ok")
case JsonRPCBody(_, _, "close", JString(channel) :: JString(scriptPubKey) :: Nil) =>
(register ? SendCommand(channel, CMD_CLOSE(Some(scriptPubKey)))).mapTo[ActorRef].map(_ => "ok")
(register ? SendCommand(channel.toLong, CMD_CLOSE(Some(scriptPubKey)))).mapTo[ActorRef].map(_ => "ok")
case JsonRPCBody(_, _, "close", JString(channel) :: Nil) =>
(register ? SendCommand(channel, CMD_CLOSE(None))).mapTo[ActorRef].map(_ => "ok")
(register ? SendCommand(channel.toLong, CMD_CLOSE(None))).mapTo[ActorRef].map(_ => "ok")
case JsonRPCBody(_, _, "help", _) =>
Future.successful(List(
"info: display basic node information",

View file

@ -28,7 +28,7 @@ class PeerWatcher(client: ExtendedBitcoinClient, blockCount: Long)(implicit ec:
val triggeredWatches = watches.collect {
case w@WatchSpent(channel, txid, outputIndex, minDepth, event)
if tx.txIn.exists(i => i.outPoint.txid == txid && i.outPoint.index == outputIndex) =>
channel ! (BITCOIN_FUNDING_SPENT, tx)
channel ! WatchEventSpent(BITCOIN_FUNDING_SPENT, tx)
w
}
context.become(watching(watches -- triggeredWatches, block2tx, currentBlockCount))
@ -38,19 +38,23 @@ class PeerWatcher(client: ExtendedBitcoinClient, blockCount: Long)(implicit ec:
// TODO: beware of the herd effect
watches.collect {
case w@WatchConfirmed(channel, txId, minDepth, event) =>
client.getTxConfirmations(txId.toString).collect {
// TODO: this is a workaround to not have WatchConfirmed triggered multiple times in testing
// the reason is that we cannot fo a become(watches - w, ...) because it happens in the future callback
case Some(confirmations) if confirmations >= minDepth => self ! ('trigger, w)
client.getTxConfirmations(txId.toString).map {
case Some(confirmations) if confirmations >= minDepth =>
client.getTransactionShortId(txId.toString).map {
// TODO: this is a workaround to not have WatchConfirmed triggered multiple times in testing
// the reason is that we cannot do a become(watches - w, ...) because it happens in the future callback
case (height, index) => self ! ('trigger, w, WatchEventConfirmed(w.event, height, index))
}
}
}
case ('trigger, w: WatchConfirmed) if watches.contains(w) =>
case ('trigger, w: WatchConfirmed, e: WatchEvent) if watches.contains(w) =>
log.info(s"triggering $w")
w.channel ! w.event
w.channel ! e
context.become(watching(watches - w, block2tx, currentBlockCount))
case ('trigger, w: WatchConfirmed) if !watches.contains(w) => {}
case ('trigger, w: WatchConfirmed, e: WatchEvent) if !watches.contains(w) => {}
case CurrentBlockCount(count) => {
val toPublish = block2tx.filterKeys(_ <= count)

View file

@ -16,9 +16,16 @@ trait Watch {
}
final case class WatchConfirmed(channel: ActorRef, txId: BinaryData, minDepth: Long, event: BitcoinEvent) extends Watch
final case class WatchSpent(channel: ActorRef, txId: BinaryData, outputIndex: Int, minDepth: Int, event: BitcoinEvent) extends Watch
// notify me if confirmation number gets below minDepth
// TODO: notify me if confirmation number gets below minDepth?
final case class WatchLost(channel: ActorRef, txId: BinaryData, minDepth: Long, event: BitcoinEvent) extends Watch
trait WatchEvent {
def event: BitcoinEvent
}
final case class WatchEventConfirmed(event: BitcoinEvent, blockHeight: Int, txIndex: Int) extends WatchEvent
final case class WatchEventSpent(event: BitcoinEvent, tx: Transaction) extends WatchEvent
final case class WatchEventLost(event: BitcoinEvent) extends WatchEvent
/**
* Publish the provided tx as soon as possible depending on locktime and csv
*/

View file

@ -1,7 +1,6 @@
package fr.acinq.eclair.channel
import akka.actor.{ActorRef, FSM, LoggingFSM, Props}
import fr.acinq.bitcoin.Crypto.Point
import fr.acinq.bitcoin._
import fr.acinq.eclair._
import fr.acinq.eclair.blockchain._
@ -21,10 +20,10 @@ import scala.util.{Failure, Success, Try}
*/
object Channel {
def props(them: ActorRef, blockchain: ActorRef, paymentHandler: ActorRef, localParams: LocalParams, theirNodeId: String, autoSignInterval: Option[FiniteDuration] = None) = Props(new Channel(them, blockchain, paymentHandler, localParams, theirNodeId, autoSignInterval))
def props(them: ActorRef, blockchain: ActorRef, router: ActorRef, paymentHandler: ActorRef, localParams: LocalParams, theirNodeId: String, autoSignInterval: Option[FiniteDuration] = None) = Props(new Channel(them, blockchain, router, paymentHandler, localParams, theirNodeId, autoSignInterval))
}
class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: ActorRef, val localParams: LocalParams, theirNodeId: String, autoSignInterval: Option[FiniteDuration] = None)(implicit ec: ExecutionContext = ExecutionContext.Implicits.global) extends LoggingFSM[State, Data] {
class Channel(val them: ActorRef, val blockchain: ActorRef, router: ActorRef, paymentHandler: ActorRef, val localParams: LocalParams, theirNodeId: String, autoSignInterval: Option[FiniteDuration] = None)(implicit ec: ExecutionContext = ExecutionContext.Implicits.global) extends LoggingFSM[State, Data] {
context.system.eventStream.publish(ChannelCreated(self, localParams, theirNodeId))
@ -95,7 +94,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
case Event(open: OpenChannel, DATA_WAIT_FOR_OPEN_CHANNEL(localParams, autoSignInterval)) =>
// TODO: here we should check if remote parameters suit us
// TODO: maybe also check uniqueness of temporary channel id
val minimumDepth = Globals.default_mindepth_blocks
val minimumDepth = Globals.mindepth_blocks
val firstPerCommitmentPoint = Generators.perCommitPoint(localParams.shaSeed, 0)
them ! AcceptChannel(temporaryChannelId = Platform.currentTime,
dustLimitSatoshis = localParams.dustLimitSatoshis,
@ -229,7 +228,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
LocalChanges(Nil, Nil, Nil), RemoteChanges(Nil, Nil),
localCurrentHtlcId = 0L,
remoteNextCommitInfo = Right(null), // TODO: we will receive their next per-commitment point in the next message, so we temporarily put an empty byte array
commitInput, ShaChain.init, channelId = 0)
commitInput, ShaChain.init, channelId = 0) // TODO: we will compute the channelId at the next step, so we temporarily put 0
context.system.eventStream.publish(ChannelIdAssigned(self, commitments.anchorId, Satoshi(params.fundingSatoshis)))
goto(WAIT_FOR_FUNDING_LOCKED_INTERNAL) using DATA_WAIT_FOR_FUNDING_LOCKED_INTERNAL(temporaryChannelId, params, commitments, None)
}
@ -280,23 +279,23 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
log.info(s"received their FundingLocked, deferring message")
stay using d.copy(deferred = Some(msg))
case Event(BITCOIN_FUNDING_DEPTHOK, d@DATA_WAIT_FOR_FUNDING_LOCKED_INTERNAL(temporaryChannelId, params, commitments, deferred)) =>
// TODO: set channelId
val channelId = 0L
case Event(WatchEventConfirmed(BITCOIN_FUNDING_DEPTHOK, blockHeight, txIndex), d@DATA_WAIT_FOR_FUNDING_LOCKED_INTERNAL(temporaryChannelId, params, commitments, deferred)) =>
val channelId = toShortId(blockHeight, txIndex, commitments.commitInput.outPoint.index.toInt)
blockchain ! WatchLost(self, commitments.anchorId, params.minimumDepth, BITCOIN_FUNDING_LOST)
val nextPerCommitmentPoint = Generators.perCommitPoint(localParams.shaSeed, 1)
them ! FundingLocked(channelId, 0L, None, None, nextPerCommitmentPoint) // TODO: routing announcements disabled
them ! FundingLocked(temporaryChannelId, channelId, None, None, nextPerCommitmentPoint) // TODO: routing announcements disabled
deferred.map(self ! _)
// TODO: htlcIdx should not be 0 when resuming connection
goto(WAIT_FOR_FUNDING_LOCKED) using DATA_NORMAL(channelId, params, commitments.copy(channelId = channelId), None, Map())
goto(WAIT_FOR_FUNDING_LOCKED) using DATA_NORMAL(params, commitments.copy(channelId = channelId), None, Map())
// TODO: not implemented, maybe should be done with a state timer and not a blockchain watch?
case Event(BITCOIN_FUNDING_TIMEOUT, _) =>
them ! Error(0, "Funding tx timed out".getBytes)
goto(CLOSED)
case Event((BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_WAIT_FOR_FUNDING_LOCKED_INTERNAL) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d)
case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_WAIT_FOR_FUNDING_LOCKED_INTERNAL) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d)
case Event((BITCOIN_FUNDING_SPENT, _), d: DATA_WAIT_FOR_FUNDING_LOCKED_INTERNAL) => handleInformationLeak(d)
case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, _), d: DATA_WAIT_FOR_FUNDING_LOCKED_INTERNAL) => handleInformationLeak(d)
case Event(cmd: CMD_CLOSE, d: DATA_WAIT_FOR_FUNDING_LOCKED_INTERNAL) =>
blockchain ! PublishAsap(d.commitments.localCommit.publishableTxs.commitTx.tx)
@ -316,14 +315,18 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
})
when(WAIT_FOR_FUNDING_LOCKED)(handleExceptions {
case Event(FundingLocked(temporaryChannelId, channelId, _, _, nextPerCommitmentPoint), d: DATA_NORMAL) =>
// TODO: check channelId matches ours
Register.create_alias(theirNodeId, d.commitments.anchorId)
case Event(FundingLocked(_, remoteChannelId, _, _, nextPerCommitmentPoint), d: DATA_NORMAL) if remoteChannelId != d.channelId =>
// TODO: channel id mismatch, can happen if minDepth is to low, negotiation not suported yet
handleLocalError(new RuntimeException(s"channel id mismatch local=${d.channelId} remote=$remoteChannelId"), d)
case Event(FundingLocked(_, remoteChannelId, _, _, nextPerCommitmentPoint), d: DATA_NORMAL) =>
log.info(s"channel ready with channelId=${java.lang.Long.toUnsignedString(d.channelId)}")
Register.create_alias(theirNodeId, d.channelId)
goto(NORMAL) using d.copy(commitments = d.commitments.copy(remoteNextCommitInfo = Right(nextPerCommitmentPoint)))
case Event((BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_NORMAL) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d)
case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_NORMAL) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d)
case Event((BITCOIN_FUNDING_SPENT, _), d: DATA_NORMAL) => handleInformationLeak(d)
case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, _), d: DATA_NORMAL) => handleInformationLeak(d)
case Event(cmd: CMD_CLOSE, d: DATA_NORMAL) =>
blockchain ! PublishAsap(d.commitments.localCommit.publishableTxs.commitTx.tx)
@ -353,7 +356,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
case Event(c: CMD_ADD_HTLC, d: DATA_NORMAL) if d.localShutdown.isDefined =>
handleCommandError(sender, new RuntimeException("cannot send new htlcs, closing in progress"))
case Event(c@CMD_ADD_HTLC(amountMsat, rHash, expiry, route, origin, id_opt, do_commit), d@DATA_NORMAL(channelId, params, commitments, _, downstreams)) =>
case Event(c@CMD_ADD_HTLC(amountMsat, rHash, expiry, route, origin, id_opt, do_commit), d@DATA_NORMAL(params, commitments, _, downstreams)) =>
Try(Commitments.sendAdd(commitments, c)) match {
case Success((commitments1, add)) =>
if (do_commit) self ! CMD_SIGN
@ -361,7 +364,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
case Failure(cause) => handleCommandError(sender, cause)
}
case Event(add: UpdateAddHtlc, d@DATA_NORMAL(_, params, commitments, _, _)) =>
case Event(add: UpdateAddHtlc, d@DATA_NORMAL(params, commitments, _, _)) =>
Try(Commitments.receiveAdd(commitments, add)) match {
case Success(commitments1) =>
import scala.concurrent.ExecutionContext.Implicits.global
@ -378,7 +381,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
case Failure(cause) => handleCommandError(sender, cause)
}
case Event(fulfill@UpdateFulfillHtlc(_, id, r), d@DATA_NORMAL(channelId, params, commitments, _, downstreams)) =>
case Event(fulfill@UpdateFulfillHtlc(_, id, r), d@DATA_NORMAL(params, commitments, _, downstreams)) =>
Try(Commitments.receiveFulfill(d.commitments, fulfill)) match {
case Success((commitments1, htlc)) =>
propagateDownstream(htlc, Right(fulfill), downstreams(id))
@ -396,7 +399,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
case Failure(cause) => handleCommandError(sender, cause)
}
case Event(fail@UpdateFailHtlc(_, id, reason), d@DATA_NORMAL(channelId, params, commitments, _, downstreams)) =>
case Event(fail@UpdateFailHtlc(_, id, reason), d@DATA_NORMAL(params, commitments, _, downstreams)) =>
Try(Commitments.receiveFail(d.commitments, fail)) match {
case Success((commitments1, htlc)) =>
propagateDownstream(htlc, Left(fail), downstreams(id))
@ -460,10 +463,10 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
case _ => handleCommandError(sender, new RuntimeException("invalid final script"))
}
case Event(remoteShutdown@Shutdown(_, remoteScriptPubKey), d@DATA_NORMAL(channelId, params, commitments, ourShutdownOpt, downstreams)) if commitments.remoteChanges.proposed.size > 0 =>
case Event(remoteShutdown@Shutdown(_, remoteScriptPubKey), d@DATA_NORMAL(params, commitments, ourShutdownOpt, downstreams)) if commitments.remoteChanges.proposed.size > 0 =>
handleLocalError(new RuntimeException("it is illegal to send a shutdown while having unsigned changes"), d)
case Event(remoteShutdown@Shutdown(_, remoteScriptPubKey), d@DATA_NORMAL(channelId, params, commitments, ourShutdownOpt, downstreams)) =>
case Event(remoteShutdown@Shutdown(_, remoteScriptPubKey), d@DATA_NORMAL(params, commitments, ourShutdownOpt, downstreams)) =>
Try(ourShutdownOpt.map(s => (s, commitments)).getOrElse {
require(Closing.isValidFinalScriptPubkey(remoteScriptPubKey), "invalid final script")
// first if we have pending changes, we need to commit them
@ -472,7 +475,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
them ! commit
commitments1
} else commitments
val shutdown = Shutdown(channelId, Script.write(params.localParams.defaultFinalScriptPubKey))
val shutdown = Shutdown(d.channelId, Script.write(params.localParams.defaultFinalScriptPubKey))
them ! shutdown
(shutdown, commitments2)
}) match {
@ -481,15 +484,15 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
|| (commitments3.remoteNextCommitInfo.isLeft && commitments3.localCommit.spec.htlcs.size == 0 && commitments3.remoteNextCommitInfo.left.get.spec.htlcs.size == 0) =>
val closingSigned = Closing.makeFirstClosingTx(params, commitments3, localShutdown.scriptPubKey, remoteShutdown.scriptPubKey)
them ! closingSigned
goto(NEGOTIATING) using DATA_NEGOTIATING(channelId, params, commitments3, localShutdown, remoteShutdown, closingSigned)
goto(NEGOTIATING) using DATA_NEGOTIATING(params, commitments3, localShutdown, remoteShutdown, closingSigned)
case Success((localShutdown, commitments3)) =>
goto(SHUTDOWN) using DATA_SHUTDOWN(channelId, params, commitments3, localShutdown, remoteShutdown, downstreams)
goto(SHUTDOWN) using DATA_SHUTDOWN(params, commitments3, localShutdown, remoteShutdown, downstreams)
case Failure(cause) => handleLocalError(cause, d)
}
case Event((BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_NORMAL) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d)
case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_NORMAL) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d)
case Event((BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_NORMAL) => handleRemoteSpentOther(tx, d)
case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_NORMAL) => handleRemoteSpentOther(tx, d)
case Event(e: Error, d: DATA_NORMAL) => handleRemoteError(e, d)
@ -552,36 +555,36 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
case Failure(cause) => handleCommandError(sender, cause)
}
case Event(msg@CommitSig(_, theirSig, theirHtlcSigs), d@DATA_SHUTDOWN(channelId, params, commitments, localShutdown, remoteShutdown, _)) =>
case Event(msg@CommitSig(_, theirSig, theirHtlcSigs), d@DATA_SHUTDOWN(params, commitments, localShutdown, remoteShutdown, _)) =>
// TODO: we might have to propagate htlcs upstream depending on the outcome of https://github.com/ElementsProject/lightning/issues/29
Try(Commitments.receiveCommit(d.commitments, msg)) match {
case Success((commitments1, revocation)) if commitments1.hasNoPendingHtlcs =>
them ! revocation
val closingSigned = Closing.makeFirstClosingTx(params, commitments1, localShutdown.scriptPubKey, remoteShutdown.scriptPubKey)
them ! closingSigned
goto(NEGOTIATING) using DATA_NEGOTIATING(channelId, params, commitments1, localShutdown, remoteShutdown, closingSigned)
goto(NEGOTIATING) using DATA_NEGOTIATING(params, commitments1, localShutdown, remoteShutdown, closingSigned)
case Success((commitments1, revocation)) =>
them ! revocation
stay using d.copy(commitments = commitments1)
case Failure(cause) => handleLocalError(cause, d)
}
case Event(msg: RevokeAndAck, d@DATA_SHUTDOWN(channelId, params, commitments, localShutdown, remoteShutdown, _)) =>
case Event(msg: RevokeAndAck, d@DATA_SHUTDOWN(params, commitments, localShutdown, remoteShutdown, _)) =>
// we received a revocation because we sent a signature
// => all our changes have been acked
Try(Commitments.receiveRevocation(d.commitments, msg)) match {
case Success(commitments1) if commitments1.hasNoPendingHtlcs =>
val closingSigned = Closing.makeFirstClosingTx(params, commitments, localShutdown.scriptPubKey, remoteShutdown.scriptPubKey)
them ! closingSigned
goto(NEGOTIATING) using DATA_NEGOTIATING(channelId, params, commitments1, localShutdown, remoteShutdown, closingSigned)
goto(NEGOTIATING) using DATA_NEGOTIATING(params, commitments1, localShutdown, remoteShutdown, closingSigned)
case Success(commitments1) =>
stay using d.copy(commitments = commitments1)
case Failure(cause) => handleLocalError(cause, d)
}
case Event((BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_SHUTDOWN) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d)
case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_SHUTDOWN) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d)
case Event((BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_SHUTDOWN) => handleRemoteSpentOther(tx, d)
case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_SHUTDOWN) => handleRemoteSpentOther(tx, d)
case Event(e: Error, d: DATA_SHUTDOWN) => handleRemoteError(e, d)
}
@ -614,13 +617,13 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
throw new RuntimeException("cannot verify their close signature", cause)
}
case Event((BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_NEGOTIATING) if tx.txid == Closing.makeClosingTx(d.params, d.commitments, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, Satoshi(d.localClosingSigned.feeSatoshis))._1.tx.txid =>
case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_NEGOTIATING) if tx.txid == Closing.makeClosingTx(d.params, d.commitments, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, Satoshi(d.localClosingSigned.feeSatoshis))._1.tx.txid =>
// happens when we agreed on a closeSig, but we don't know it yet: we receive the watcher notification before their ClosingSigned (which will match ours)
stay()
case Event((BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_NEGOTIATING) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d)
case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_NEGOTIATING) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d)
case Event((BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_NEGOTIATING) => handleRemoteSpentOther(tx, d)
case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_NEGOTIATING) => handleRemoteSpentOther(tx, d)
case Event(e: Error, d: DATA_NEGOTIATING) => handleRemoteError(e, d)
@ -628,29 +631,29 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
when(CLOSING) {
case Event((BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_CLOSING) if tx.txid == d.commitments.localCommit.publishableTxs.commitTx.tx.txid =>
case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_CLOSING) if tx.txid == d.commitments.localCommit.publishableTxs.commitTx.tx.txid =>
// we just initiated a uniclose moments ago and are now receiving the blockchain notification, there is nothing to do
stay()
case Event((BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_CLOSING) if Some(tx.txid) == d.mutualClosePublished.map(_.txid) =>
case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_CLOSING) if Some(tx.txid) == d.mutualClosePublished.map(_.txid) =>
// we just published a mutual close tx, we are notified but it's alright
stay()
case Event((BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_CLOSING) if tx.txid == d.commitments.remoteCommit.txid =>
case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_CLOSING) if tx.txid == d.commitments.remoteCommit.txid =>
// counterparty may attempt to spend its last commit tx at any time
handleRemoteSpentCurrent(tx, d)
case Event((BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_CLOSING) =>
case Event(WatchEventSpent(BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_CLOSING) =>
// counterparty may attempt to spend a revoked commit tx at any time
handleRemoteSpentOther(tx, d)
case Event(BITCOIN_CLOSE_DONE, d: DATA_CLOSING) if d.mutualClosePublished.isDefined => goto(CLOSED)
case Event(WatchEventConfirmed(BITCOIN_CLOSE_DONE, _, _), d: DATA_CLOSING) if d.mutualClosePublished.isDefined => goto(CLOSED)
case Event(BITCOIN_SPEND_OURS_DONE, d: DATA_CLOSING) if d.localCommitPublished.isDefined => goto(CLOSED)
case Event(WatchEventConfirmed(BITCOIN_SPEND_OURS_DONE, _, _), d: DATA_CLOSING) if d.localCommitPublished.isDefined => goto(CLOSED)
case Event(BITCOIN_SPEND_THEIRS_DONE, d: DATA_CLOSING) if d.remoteCommitPublished.isDefined => goto(CLOSED)
case Event(WatchEventConfirmed(BITCOIN_SPEND_THEIRS_DONE, _, _), d: DATA_CLOSING) if d.remoteCommitPublished.isDefined => goto(CLOSED)
case Event(BITCOIN_PUNISHMENT_DONE, d: DATA_CLOSING) if d.revokedCommitPublished.size > 0 => goto(CLOSED)
case Event(WatchEventConfirmed(BITCOIN_PUNISHMENT_DONE, _, _), d: DATA_CLOSING) if d.revokedCommitPublished.size > 0 => goto(CLOSED)
case Event(e: Error, d: DATA_CLOSING) => stay // nothing to do, there is already a spending tx published
}
@ -669,7 +672,11 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
whenUnhandled {
case Event(BITCOIN_FUNDING_LOST, _) => goto(ERR_FUNDING_LOST)
case Event(msg: RoutingMessage, _) =>
router forward msg
stay
case Event(WatchEventLost(BITCOIN_FUNDING_LOST), _) => goto(ERR_FUNDING_LOST)
case Event(CMD_GETSTATE, _) =>
sender ! stateName
@ -686,7 +693,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
case c: DATA_WAIT_FOR_ACCEPT_CHANNEL => c.temporaryChannelId
case c: DATA_WAIT_FOR_FUNDING_CREATED => c.temporaryChannelId
case c: DATA_WAIT_FOR_FUNDING_LOCKED_INTERNAL => c.temporaryChannelId
case c: DATA_NORMAL => c.channelId
case c: DATA_NORMAL => c.commitments.channelId
case c: DATA_SHUTDOWN => c.channelId
case c: DATA_NEGOTIATING => c.channelId
case c: DATA_CLOSING => 0L
@ -699,7 +706,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
}
onTransition {
case previousState -> currentState => context.system.eventStream.publish(ChannelChangedState(self, theirNodeId, previousState, currentState, stateData))
case previousState -> currentState => context.system.eventStream.publish(ChannelChangedState(self, context.parent, theirNodeId, previousState, currentState, stateData))
}
/*

View file

@ -13,7 +13,7 @@ case class ChannelCreated(channel: ActorRef, params: LocalParams, theirNodeId: S
case class ChannelIdAssigned(channel: ActorRef, channelId: BinaryData, amount: Satoshi) extends ChannelEvent
case class ChannelChangedState(channel: ActorRef, theirNodeId: BinaryData, previousState: State, currentState: State, currentData: Data) extends ChannelEvent
case class ChannelChangedState(channel: ActorRef, transport: ActorRef, remoteNodeId: BinaryData, previousState: State, currentState: State, currentData: Data) extends ChannelEvent
case class ChannelSignatureReceived(channel: ActorRef, Commitments: Commitments) extends ChannelEvent

View file

@ -119,6 +119,7 @@ case object Nothing extends Data
trait HasCommitments extends Data {
def commitments: Commitments
def channelId = commitments.channelId
}
case class LocalCommitPublished(commitTx: Transaction, claimMainDelayedOutputTx: Option[Transaction], htlcSuccessTxs: Seq[Transaction], htlcTimeoutTxs: Seq[Transaction], claimHtlcDelayedTx: Seq[Transaction])
@ -131,11 +132,11 @@ final case class DATA_WAIT_FOR_FUNDING_INTERNAL(temporaryChannelId: Long, params
final case class DATA_WAIT_FOR_FUNDING_CREATED(temporaryChannelId: Long, params: ChannelParams, pushMsat: Long, remoteFirstPerCommitmentPoint: Point) extends Data
final case class DATA_WAIT_FOR_FUNDING_SIGNED(temporaryChannelId: Long, params: ChannelParams, fundingTx: Transaction, localSpec: CommitmentSpec, localCommitTx: CommitTx, remoteCommit: RemoteCommit) extends Data
final case class DATA_WAIT_FOR_FUNDING_LOCKED_INTERNAL(temporaryChannelId: Long, params: ChannelParams, commitments: Commitments, deferred: Option[FundingLocked]) extends Data with HasCommitments
final case class DATA_NORMAL(channelId: Long, params: ChannelParams, commitments: Commitments, localShutdown: Option[Shutdown], downstreams: Map[Long, Option[Origin]]) extends Data with HasCommitments
final case class DATA_SHUTDOWN(channelId: Long, params: ChannelParams, commitments: Commitments,
final case class DATA_NORMAL(params: ChannelParams, commitments: Commitments, localShutdown: Option[Shutdown], downstreams: Map[Long, Option[Origin]]) extends Data with HasCommitments
final case class DATA_SHUTDOWN(params: ChannelParams, commitments: Commitments,
localShutdown: Shutdown, remoteShutdown: Shutdown,
downstreams: Map[Long, Option[Origin]]) extends Data with HasCommitments
final case class DATA_NEGOTIATING(channelId: Long, params: ChannelParams, commitments: Commitments,
final case class DATA_NEGOTIATING(params: ChannelParams, commitments: Commitments,
localShutdown: Shutdown, remoteShutdown: Shutdown, localClosingSigned: ClosingSigned) extends Data with HasCommitments
final case class DATA_CLOSING(commitments: Commitments,
ourSignature: Option[ClosingSigned] = None,

View file

@ -32,7 +32,7 @@ import scala.concurrent.duration._
* ├── client (0..m, transient)
* └── api
*/
class Register(blockchain: ActorRef, paymentHandler: ActorRef, defaultFinalScriptPubKey: Seq[ScriptElt]) extends Actor with ActorLogging {
class Register(watcher: ActorRef, router: ActorRef, paymentHandler: ActorRef, defaultFinalScriptPubKey: Seq[ScriptElt]) extends Actor with ActorLogging {
import Register._
@ -47,8 +47,8 @@ class Register(blockchain: ActorRef, paymentHandler: ActorRef, defaultFinalScrip
maxHtlcValueInFlightMsat = Long.MaxValue,
channelReserveSatoshis = 0,
htlcMinimumMsat = 0,
feeratePerKw = Globals.default_feeratePerKw,
toSelfDelay = Globals.default_delay_blocks,
feeratePerKw = Globals.feeratePerKw,
toSelfDelay = Globals.delay_blocks,
maxAcceptedHtlcs = 100,
fundingPrivKey = generateKey(0),
revocationSecret = generateKey(1),
@ -59,8 +59,9 @@ class Register(blockchain: ActorRef, paymentHandler: ActorRef, defaultFinalScrip
isFunder = amount_opt.isDefined
)
def makeChannel(conn: ActorRef, publicKey: BinaryData): ActorRef = {
val channel = context.actorOf(Channel.props(conn, blockchain, paymentHandler, localParams, publicKey.toString(), Some(Globals.autosign_interval)), s"channel-$counter")
def makeChannel(conn: ActorRef, publicKey: BinaryData, ctx: ActorContext): ActorRef = {
// note that we use transport's context and not register's context
val channel = ctx.actorOf(Channel.props(conn, watcher, router, paymentHandler, localParams, publicKey.toString(), Some(Globals.autosign_interval)), s"channel-$counter")
amount_opt match {
case Some(amount) => channel ! INPUT_INIT_FUNDER(amount.amount, 0)
case None => channel ! INPUT_INIT_FUNDEE()
@ -95,14 +96,14 @@ class Register(blockchain: ActorRef, paymentHandler: ActorRef, defaultFinalScrip
object Register {
def props(blockchain: ActorRef, paymentHandler: ActorRef, defaultFinalScriptPubKey: Seq[ScriptElt]) = Props(classOf[Register], blockchain, paymentHandler, defaultFinalScriptPubKey)
def props(blockchain: ActorRef, router: ActorRef, paymentHandler: ActorRef, defaultFinalScriptPubKey: Seq[ScriptElt]) = Props(classOf[Register], blockchain, router, paymentHandler, defaultFinalScriptPubKey)
// @formatter:off
case class CreateChannel(connection: ActorRef, pubkey: Option[BinaryData], anchorAmount: Option[Satoshi])
case class ListChannels()
case class SendCommand(channelId: String, cmd: Command)
case class SendCommand(channelId: Long, cmd: Command)
// @formatter:on
@ -110,22 +111,22 @@ object Register {
* Once it reaches NORMAL state, channel creates a [[fr.acinq.eclair.channel.AliasActor]]
* which name is counterparty_id-anchor_id
*/
def create_alias(node_id: BinaryData, anchor_id: BinaryData)(implicit context: ActorContext) =
context.actorOf(Props(new AliasActor(context.self)), name = s"$node_id-$anchor_id")
def create_alias(node_id: BinaryData, channel_id: Long)(implicit context: ActorContext) =
context.actorOf(Props(new AliasActor(context.self)), name = s"$node_id-${java.lang.Long.toUnsignedString(channel_id)}")
def actorPathToNodeId(system: ActorSystem, nodeId: BinaryData): ActorPath =
system / "register" / "auth-handler-*" / "channel" / s"${nodeId}-*"
system / "register" / "transport-handler-*" / "channel" / s"${nodeId}-*"
def actorPathToNodeId(nodeId: BinaryData)(implicit context: ActorContext): ActorPath = actorPathToNodeId(context.system, nodeId)
def actorPathToChannelId(system: ActorSystem, channelId: BinaryData): ActorPath =
system / "register" / "auth-handler-*" / "channel" / s"*-${channelId}"
def actorPathToChannelId(system: ActorSystem, channelId: Long): ActorPath =
system / "register" / "transport-handler-*" / "channel" / s"*-${channelId}"
def actorPathToChannelId(channelId: BinaryData)(implicit context: ActorContext): ActorPath = actorPathToChannelId(context.system, channelId)
def actorPathToChannelId(channelId: Long)(implicit context: ActorContext): ActorPath = actorPathToChannelId(context.system, channelId)
def actorPathToChannels()(implicit context: ActorContext): ActorPath =
context.system / "register" / "auth-handler-*" / "channel"
context.system / "register" / "transport-handler-*" / "channel"
def actorPathToHandlers()(implicit context: ActorContext): ActorPath =
context.system / "register" / "auth-handler-*"
def actorPathToTransportHandlers()(implicit context: ActorContext): ActorPath =
context.system / "register" / "transport-handler-*"
}

View file

@ -2,7 +2,7 @@ package fr.acinq.eclair.crypto
import java.nio.ByteOrder
import akka.actor.{Actor, ActorRef, LoggingFSM, Terminated}
import akka.actor.{Actor, ActorContext, ActorRef, LoggingFSM, Terminated}
import akka.io.Tcp.{PeerClosed, _}
import akka.util.ByteString
import fr.acinq.bitcoin.{BinaryData, Protocol}
@ -29,7 +29,7 @@ import scala.util.{Failure, Success, Try}
* @param listenerFactory factory that will be used to create the listener that will receive decrypted messages once the
* handshake phase as been completed. Its parameters are a tuple (transport handler, remote public key)
*/
class TransportHandler[T: ClassTag](keyPair: KeyPair, rs: Option[BinaryData], them: ActorRef, isWriter: Boolean, listenerFactory: (ActorRef, BinaryData) => ActorRef, serializer: TransportHandler.Serializer[T]) extends Actor with LoggingFSM[TransportHandler.State, TransportHandler.Data] {
class TransportHandler[T: ClassTag](keyPair: KeyPair, rs: Option[BinaryData], them: ActorRef, isWriter: Boolean, listenerFactory: (ActorRef, BinaryData, ActorContext) => ActorRef, serializer: TransportHandler.Serializer[T]) extends Actor with LoggingFSM[TransportHandler.State, TransportHandler.Data] {
import TransportHandler._
@ -70,7 +70,7 @@ class TransportHandler[T: ClassTag](keyPair: KeyPair, rs: Option[BinaryData], th
reader.read(payload) match {
case (writer, _, Some((dec, enc, ck))) =>
val listener = listenerFactory(self, writer.rs)
val listener = listenerFactory(self, writer.rs, context)
context.watch(listener)
val (nextStateData, plaintextMessages) = WaitingForCyphertextData(ExtendedCipherState(enc, ck), ExtendedCipherState(dec, ck), None, remainder, listener).decrypt
sendToListener(listener, plaintextMessages)
@ -87,7 +87,7 @@ class TransportHandler[T: ClassTag](keyPair: KeyPair, rs: Option[BinaryData], th
}
case (_, message, Some((enc, dec, ck))) => {
them ! Write(TransportHandler.prefix +: message)
val listener = listenerFactory(self, writer.rs)
val listener = listenerFactory(self, writer.rs, context)
context.watch(listener)
val (nextStateData, plaintextMessages) = WaitingForCyphertextData(ExtendedCipherState(enc, ck), ExtendedCipherState(dec, ck), None, remainder, listener).decrypt
sendToListener(listener, plaintextMessages)

View file

@ -15,7 +15,6 @@ import fr.acinq.eclair.Setup
import fr.acinq.eclair.channel.ChannelEvent
import fr.acinq.eclair.gui.controllers.MainController
import fr.acinq.eclair.gui.stages.SplashStage
import fr.acinq.eclair.router.NetworkEvent
import grizzled.slf4j.Logging
/**
@ -40,7 +39,6 @@ class FxApp extends Application with Logging {
val controller = new MainController(handlers, primaryStage, setup)
val guiUpdater = setup.system.actorOf(Props(classOf[GUIUpdater], primaryStage, controller, setup), "gui-updater")
setup.system.eventStream.subscribe(guiUpdater, classOf[ChannelEvent])
setup.system.eventStream.subscribe(guiUpdater, classOf[NetworkEvent])
import scala.concurrent.ExecutionContext.Implicits.global
setup.fatalEventFuture onSuccess {

View file

@ -12,7 +12,7 @@ import com.mxgraph.swing.mxGraphComponent
import fr.acinq.bitcoin._
import fr.acinq.eclair.channel._
import fr.acinq.eclair.gui.controllers.{ChannelPaneController, MainController}
import fr.acinq.eclair.router.{ChannelDesc, ChannelDiscovered}
import fr.acinq.eclair.router.{ChannelDesc}
import fr.acinq.eclair.{Globals, Setup}
import org.jgrapht.ext.JGraphXAdapter
import org.jgrapht.graph.{DefaultEdge, SimpleGraph}
@ -69,7 +69,7 @@ class GUIUpdater(primaryStage: Stage, mainController: MainController, setup: Set
}
})
case ChannelChangedState(channel, _, previousState, currentState, currentData) =>
case ChannelChangedState(channel, _, _, previousState, currentState, currentData) =>
val channelPane = m(channel)
Platform.runLater(new Runnable() {
override def run(): Unit = {
@ -87,7 +87,8 @@ class GUIUpdater(primaryStage: Stage, mainController: MainController, setup: Set
}
})
case ChannelDiscovered(ChannelDesc(id, a, b)) =>
// TODO
/*case ChannelDiscovered(ChannelDesc(id, a, b)) =>
graph.addVertex(BinaryData(a))
graph.addVertex(BinaryData(b))
graph.addEdge(a, b, new NamedEdge(id))
@ -100,7 +101,7 @@ class GUIUpdater(primaryStage: Stage, mainController: MainController, setup: Set
lay.execute(jgxAdapter.getDefaultParent())
mainController.swingNode.setContent(component)
}
})
})*/
}
}

View file

@ -24,4 +24,11 @@ package object eclair {
* @return the fee (in msat) that a node should be paid to forward an HTLC of 'amount' millisatoshis
*/
def nodeFee(base: Long, proportional: Long, msat: Long): Long = base + (proportional * msat) / 1000000
def toShortId(blockHeight: Int, txIndex: Int, outputIndex: Int): Long =
((blockHeight & 0xFFFFFFL) << 40) | ((txIndex & 0xFFFFFFL) << 16) | (outputIndex & 0xFFFFL)
def fromShortId(id: Long): (Int, Int, Int) =
(((id >> 40) & 0xFFFFFF).toInt, ((id >> 16) & 0xFFFFFF).toInt, (id & 0xFFFF).toInt)
}

View file

@ -101,7 +101,7 @@ object PaymentLifecycle {
def buildRoute(finalAmountMsat: Int, nodeIds: Seq[BinaryData]): lightning.route = {
// TODO: use actual fee parameters that are specific to each node
def fee(amountMsat: Int) = nodeFee(Globals.base_fee, Globals.proportional_fee, amountMsat).toInt
def fee(amountMsat: Int) = nodeFee(Globals.fee_base_msat, Globals.fee_proportional_msat, amountMsat).toInt
var amountMsat = finalAmountMsat
val steps = nodeIds.reverse.map(nodeId => {

View file

@ -17,7 +17,7 @@ class ChannelSelector extends Actor with ActorLogging {
def main(node2channels: Map[BinaryData, Set[ActorRef]], channel2balance: Map[ActorRef, Long]): Receive = {
case ChannelChangedState(channel, theirNodeId, _, NORMAL, d: DATA_NORMAL) =>
case ChannelChangedState(channel, _, theirNodeId, _, NORMAL, d: DATA_NORMAL) =>
val bal = d.commitments.remoteCommit.spec.toRemoteMsat
log.info(s"new channel to $theirNodeId with availableMsat=$bal")
val channels = node2channels.get(theirNodeId).getOrElse(Set()) + channel

View file

@ -1,111 +0,0 @@
package fr.acinq.eclair.router
import akka.actor.{Actor, ActorContext, ActorLogging, ActorRef}
import fr.acinq.eclair.Globals
import fr.acinq.eclair.channel.{ChannelChangedState, DATA_NORMAL, NORMAL}
import grizzled.slf4j.Logging
import org.kitteh.irc.client.library.Client
import org.kitteh.irc.client.library.event.channel.ChannelUsersUpdatedEvent
import org.kitteh.irc.client.library.event.client.ClientConnectedEvent
import org.kitteh.irc.client.library.event.helper.ChannelUserListChangeEvent
import org.kitteh.irc.client.library.event.helper.ChannelUserListChangeEvent.Change
import org.kitteh.irc.client.library.event.user.PrivateMessageEvent
import org.kitteh.irc.lib.net.engio.mbassy.listener.Handler
import scala.collection.JavaConversions._
import scala.util.Random
/**
* Created by PM on 25/08/2016.
*/
class IRCWatcher extends Actor with ActorLogging {
val ircChannel = "#eclair-gossip"
context.system.eventStream.subscribe(self, classOf[ChannelChangedState])
val client = Client.builder().nick(s"node-${Globals.Node.id.take(8)}").serverHost("irc.freenode.net").build()
client.getEventManager().registerEventListener(new NodeIRCListener())
client.addChannel(ircChannel)
// we can't use channel id as nickname because there would be conflicts (a channel has two ends)
val rand = new Random()
override def receive: Receive = main(Map(), Map())
def main(channels: Map[String, ChannelDesc], localChannels: Map[ActorRef, Client]): Receive = {
case ChannelChangedState(channel, theirNodeId, _, NORMAL, d: DATA_NORMAL) =>
val channelDesc = ChannelDesc(d.commitments.anchorId, Globals.Node.publicKey, theirNodeId)
val channelClient = Client.builder().nick(f"chan-${rand.nextInt(1000000)}%06d").realName(channelDesc.id.toString()).serverHost("irc.freenode.net").build()
channelClient.getEventManager().registerEventListener(new ChannelIRCListener(channelDesc))
channelClient.addChannel(ircChannel)
context become main(channels, localChannels + (channel -> channelClient))
case ChannelChangedState(channel, theirNodeId, NORMAL, _, _) if localChannels.contains(channel) =>
localChannels(channel).shutdown()
context become main(channels, localChannels - channel)
case ('add, nick: String, desc: ChannelDesc) if !channels.contains(nick) && !channels.values.map(_.id).contains(desc.id) =>
context.system.eventStream.publish(ChannelDiscovered(desc))
context become main(channels + (nick -> desc), localChannels)
case ('remove, nick: String) if channels.contains(nick) =>
context.system.eventStream.publish(ChannelLost(channels(nick)))
context become main(channels - nick, localChannels)
}
}
class NodeIRCListener(implicit context: ActorContext) extends Logging {
@Handler
def onClientConnected(event: ClientConnectedEvent) {
logger.info(s"connected to IRC: ${event.getServerInfo}")
}
@Handler
def onChannelUsersUpdated(event: ChannelUsersUpdatedEvent) {
logger.debug(s"users updated: $event")
event.getChannel.getUsers
.filter(_.getNick.startsWith("chan"))
.map(chanUser => chanUser.sendMessage("desc"))
}
@Handler
def onChannelUserListChangeEvent(event: ChannelUserListChangeEvent) {
logger.debug(s"${event.getChange} ${event.getUser}")
event.getChange match {
case Change.JOIN if event.getUser.getNick.startsWith("chan") => event.getUser.sendMessage("desc")
case Change.LEAVE if event.getUser.getNick.startsWith("chan") => context.self ! ('remove, event.getUser.getNick)
case _ => {}
}
}
val r = """([0-9a-f]{64}): ([0-9a-f]{66})-([0-9a-f]{66})""".r
@Handler
def onPrivateMessage(event: PrivateMessageEvent) {
logger.debug(s"got private message: ${event.getMessage}")
event.getMessage match {
case r(id, a, b) => context.self ! ('add, event.getActor.getNick, ChannelDesc(id, a, b))
case _ => {}
}
}
}
class ChannelIRCListener(channelDesc: ChannelDesc) extends Logging {
@Handler
def onClientConnected(event: ClientConnectedEvent) {
logger.info(s"channel=${channelDesc.id} connected to IRC: ${event.getServerInfo}")
}
@Handler
def onPrivateMessage(event: PrivateMessageEvent) {
logger.debug(s"got private message: ${event.getMessage}")
event.getMessage match {
case "desc" => event.sendReply(s"${channelDesc.id}: ${channelDesc.a}-${channelDesc.b}")
case "kill" => event.getClient.shutdown()
case _ => {}
}
}
}

View file

@ -1,11 +0,0 @@
package fr.acinq.eclair.router
/**
* Created by PM on 26/08/2016.
*/
trait NetworkEvent
case class ChannelDiscovered(c: ChannelDesc) extends NetworkEvent
case class ChannelLost(c: ChannelDesc) extends NetworkEvent

View file

@ -1,42 +1,202 @@
package fr.acinq.eclair.router
import akka.actor.{Actor, ActorLogging}
import akka.pattern.pipe
import fr.acinq.bitcoin.BinaryData
import java.net.InetSocketAddress
import akka.actor.{Actor, ActorLogging, ActorRef, Props}
import fr.acinq.bitcoin.Crypto.PrivateKey
import fr.acinq.bitcoin.{BinaryData, LexicographicalOrdering}
import fr.acinq.eclair.Globals
import fr.acinq.eclair.channel.{ChannelChangedState, DATA_NORMAL, NORMAL, Register}
import fr.acinq.eclair.wire._
import org.jgrapht.alg.DijkstraShortestPath
import org.jgrapht.graph.{DefaultEdge, SimpleGraph}
import scala.collection.JavaConversions._
import scala.compat.Platform
import scala.concurrent.duration._
import scala.concurrent.{ExecutionContext, Future}
/**
* Created by PM on 24/05/2016.
*/
class Router extends Actor with ActorLogging {
context.system.eventStream.subscribe(self, classOf[NetworkEvent])
class Router(watcher: ActorRef, announcement: NodeAnnouncement) extends Actor with ActorLogging {
import Router._
import ExecutionContext.Implicits.global
def receive: Receive = main(Map())
context.system.eventStream.subscribe(self, classOf[ChannelChangedState])
context.system.scheduler.schedule(10 seconds, 60 seconds, self, 'tick_broadcast)
def receive: Receive = main(myself = announcement, nodes = Map(announcement.nodeId -> announcement), channels = Map(), updates = Map(), rebroadcast = Nil)
def main(
myself: NodeAnnouncement,
nodes: Map[BinaryData, NodeAnnouncement],
channels: Map[Long, ChannelAnnouncement],
updates: Map[(Long, BinaryData), ChannelUpdate],
rebroadcast: Seq[RoutingMessage]): Receive = {
case ChannelChangedState(channel, transport, remoteNodeId, _, NORMAL, d: DATA_NORMAL) =>
val (c, u) = if (LexicographicalOrdering.isLessThan(myself.nodeId, remoteNodeId)) {
(
makeChannelAnnouncement(d.commitments.channelId, myself.nodeId, remoteNodeId, d.params.localParams.fundingPrivKey.publicKey.toBin, d.params.remoteParams.fundingPubKey.toBin),
makeChannelUpdate(Globals.Node.privateKey, d.commitments.channelId, true, Platform.currentTime / 1000)
)
} else {
(
makeChannelAnnouncement(d.commitments.channelId, remoteNodeId, myself.nodeId, d.params.remoteParams.fundingPubKey.toBin, d.params.localParams.fundingPrivKey.publicKey.toBin),
makeChannelUpdate(Globals.Node.privateKey, d.commitments.channelId, false, Platform.currentTime / 1000)
)
}
// we send all known announcements to the new peer
channels.values.foreach(transport ! _)
nodes.values.foreach(transport ! _)
updates.values.foreach(transport ! _)
// and we queue the new announcements for everybody
log.debug(s"queueing channel announcement $c")
log.debug(s"queueing node announcement $myself")
// let's trigger the broadcast immediately so that we don't wait for 60 seconds to announce our newly created channel
self ! 'tick_broadcast
context become main(myself, nodes, channels + (c.channelId -> c), updates, rebroadcast :+ c :+ myself :+ u)
case s: ChannelChangedState =>
// other channel changed state messages are ignored
case c: ChannelAnnouncement if channels.containsKey(c.channelId) =>
log.debug(s"ignoring $c (duplicate)")
case c: ChannelAnnouncement =>
// TODO: check channel output = P2WSH(nodeid1, nodeid2)
// TODO: check sigs
// TODO: blacklist if already received same channel id and different node ids
// TODO: check feature bit set
// TODO: forget channel once funding tx spent (add watch)
//watcher ! WatchSpent(self, txId: BinaryData, outputIndex: Int, minDepth: Int, event: BitcoinEvent)
log.info(s"added channel channelId=${c.channelId} (nodes=${nodes.size} channels=${channels.size + 1}")
context become main(myself, nodes, channels + (c.channelId -> c), updates, rebroadcast :+ c)
case n: NodeAnnouncement if !checkSig(n) =>
// TODO: fail connection (should probably be done in the auth handler or channel)
case n: NodeAnnouncement if !channels.values.exists(c => c.nodeId1 == n.nodeId || c.nodeId2 == n.nodeId) =>
log.debug(s"ignoring $n (no related channel found)")
case n: NodeAnnouncement if nodes.containsKey(n.nodeId) && nodes(n.nodeId).timestamp >= n.timestamp =>
log.debug(s"ignoring announcement $n (old timestamp or duplicate)")
case n: NodeAnnouncement =>
log.info(s"added/replaced node nodeId=${n.nodeId} (nodes=${nodes.size + 1} channels=${channels.size}")
context become main(myself, nodes + (n.nodeId -> n), channels, updates, rebroadcast :+ n)
case u: ChannelUpdate if !channels.contains(u.channelId) =>
log.debug(s"ignoring $u (no related channel found)")
case u: ChannelUpdate if !checkSig(u, getNodeId(u, channels(u.channelId))) =>
// TODO: fail connection (should probably be done in the auth handler or channel)
case u: ChannelUpdate =>
val channel = channels(u.channelId)
val nodeId = getNodeId(u, channel)
if (updates.contains((u.channelId, nodeId)) && updates((u.channelId, nodeId)).timestamp >= u.timestamp) {
log.debug(s"ignoring $u (old timestamp or duplicate)")
} else {
context become main(myself, nodes, channels, updates + ((u.channelId, nodeId) -> u), rebroadcast :+ u)
}
case 'tick_broadcast if rebroadcast.size ==0 =>
// no-op
case 'tick_broadcast =>
log.info(s"broadcasting ${rebroadcast.size} routing messages")
rebroadcast.foreach(context.actorSelection(Register.actorPathToTransportHandlers) ! _)
context become main(myself, nodes, channels, updates, Nil)
def main(channels: Map[BinaryData, ChannelDesc]): Receive = {
case ChannelDiscovered(c) =>
log.info(s"added channel ${c.id} to available routes")
context become main(channels + (c.id -> c))
case ChannelLost(c) =>
log.info(s"removed channel ${c.id} from available routes")
context become main(channels - c.id)
case 'network => sender ! channels.values
case RouteRequest(start, end) => findRoute(start, end, channels) map (RouteResponse(_)) pipeTo sender
case other => log.warning(s"unhandled message $other")
//case RouteRequest(start, end) => findRoute(start, end, channels) map (RouteResponse(_)) pipeTo sender
}
}
object Router {
def props(watcher: ActorRef, announcement: NodeAnnouncement) = Props(classOf[Router], watcher, announcement)
// TODO: placeholder for signatures, we don't actually sign for now
val DUMMY_SIG = BinaryData("3045022100e0a180fdd0fe38037cc878c03832861b40a29d32bd7b40b10c9e1efc8c1468a002205ae06d1624896d0d29f4b31e32772ea3cb1b4d7ed4e077e5da28dcc33c0e781201")
def makeChannelAnnouncement(channelId: Long, nodeId1: BinaryData, nodeId2: BinaryData, fundingKey1: BinaryData, fundingKey2: BinaryData): ChannelAnnouncement = {
val unsigned = ChannelAnnouncement(
nodeSignature1 = DUMMY_SIG,
nodeSignature2 = DUMMY_SIG,
channelId = channelId,
bitcoinSignature1 = DUMMY_SIG,
bitcoinSignature2 = DUMMY_SIG,
nodeId1 = nodeId1,
nodeId2 = nodeId2,
bitcoinKey1 = fundingKey1,
bitcoinKey2 = fundingKey2
)
unsigned
}
def makeNodeAnnouncement(secret: PrivateKey, alias: String, color: (Byte, Byte, Byte), addresses: List[InetSocketAddress], timestamp: Long): NodeAnnouncement = {
require(alias.size <= 32)
val unsigned = NodeAnnouncement(
signature = DUMMY_SIG,
timestamp = timestamp,
nodeId = secret.publicKey.toBin,
rgbColor = color,
alias = alias,
features = "",
addresses = addresses
)
unsigned
/*val bin = Codecs.nodeAnnouncementCodec.encode(unsigned).toOption.map(_.toByteArray).getOrElse(throw new RuntimeException(s"cannot encode $unsigned"))
val hash = sha256(sha256(bin.drop(64)))
val sig = encodeSignature(sign(hash, secret))
unsigned.copy(signature = sig)*/
}
def makeChannelUpdate(secret: PrivateKey, channelId: Long, isNodeId1: Boolean, timestamp: Long): ChannelUpdate = {
val unsigned = ChannelUpdate(
signature = DUMMY_SIG,
channelId = channelId,
timestamp = timestamp,
flags = if (isNodeId1) "0000" else "0001",
cltvExpiryDelta = Globals.expiry_delta_blocks,
htlcMinimumMsat = Globals.htlc_minimum_msat,
feeBaseMsat = Globals.fee_base_msat,
feeProportionalMillionths = Globals.fee_proportional_msat
)
unsigned
/*val bin = Codecs.channelUpdateCodec.encode(unsigned).toOption.map(_.toByteArray).getOrElse(throw new RuntimeException(s"cannot encode $unsigned"))
val hash = sha256(sha256(bin.drop(64)))
val sig = encodeSignature(sign(hash, secret))
unsigned.copy(signature = sig)*/
}
def checkSig(ann: NodeAnnouncement): Boolean = true /*{
val bin = Codecs.nodeAnnouncementCodec.encode(ann).toOption.map(_.toByteArray).getOrElse(throw new RuntimeException(s"cannot encode $ann"))
val hash = sha256(sha256(bin.drop(64)))
verifySignature(hash, ann.signature, PublicKey(ann.nodeId))
}*/
def checkSig(ann: ChannelUpdate, nodeId: BinaryData): Boolean = true /*{
val bin = Codecs.channelUpdateCodec.encode(ann).toOption.map(_.toByteArray).getOrElse(throw new RuntimeException(s"cannot encode $ann"))
val hash = sha256(sha256(bin.drop(64)))
verifySignature(hash, ann.signature, PublicKey(nodeId))
}*/
def getNodeId(u: ChannelUpdate, channel: ChannelAnnouncement): BinaryData = {
require(u.flags.data.size == 2, s"invalid flags length ${u.flags.data.size} != 2")
// the least significant bit tells us if it is node1 or node2
if (u.flags.data(1) % 2 == 0) channel.nodeId1 else channel.nodeId2
}
def findRouteDijkstra(myNodeId: BinaryData, targetNodeId: BinaryData, channels: Map[BinaryData, ChannelDesc]): Seq[BinaryData] = {
class NamedEdge(val id: BinaryData) extends DefaultEdge
val g = new SimpleGraph[BinaryData, NamedEdge](classOf[NamedEdge])

View file

@ -130,7 +130,7 @@ object Codecs {
val fundingCreatedCodec: Codec[FundingCreated] = (
("temporaryChannelId" | int64) ::
("txid" | binarydata(32)) ::
("outputIndex" | uint16) ::
("outputIndex" | uint8) ::
("signature" | signature)).as[FundingCreated]
val fundingSignedCodec: Codec[FundingSigned] = (
@ -213,7 +213,7 @@ object Codecs {
("channelId" | int64) ::
("timestamp" | uint32) ::
("flags" | binarydata(2)) ::
("expiry" | uint16) ::
("cltvExpiryDelta" | uint16) ::
("htlcMinimumMsat" | uint32) ::
("feeBaseMsat" | uint32) ::
("feeProportionalMillionths" | uint32)).as[ChannelUpdate]

View file

@ -118,13 +118,14 @@ case class NodeAnnouncement(signature: BinaryData,
rgbColor: (Byte, Byte, Byte),
alias: String,
features: BinaryData,
// TODO: check address order + support padding data (type 0)
addresses: List[InetSocketAddress]) extends RoutingMessage
case class ChannelUpdate(signature: BinaryData,
channelId: Long,
timestamp: Long,
flags: BinaryData,
expiry: Int,
cltvExpiryDelta: Int,
htlcMinimumMsat: Long,
feeBaseMsat: Long,
feeProportionalMillionths: Long) extends RoutingMessage

View file

@ -1,31 +1,36 @@
eclair {
server {
address = "localhost"
port = 45000
host = "localhost"
port = 9735
}
api {
address = "localhost"
host = "localhost"
port = 8080
}
bitcoind {
address = "localhost"
port = 18444 # regtest
network = "regtest"
host = "localhost"
port = 18444
rpcport = 18332
rpcuser = "foo"
rpcpassword = "bar"
}
node {
seed = 0102030405060708010203040506070801020304050607080102030405060708
alias = "eclair"
color {
r = 73
g = 218
b = 170
}
}
delay-blocks = 144
mindepth-blocks = 3
commit-fee = 80000
closing-fee = 10000
payment-handler = "noop"
}
interop-test {
bitcoin-path = ""
lightning-path = ""
expiry-delta-blocks = 144
htlc-minimum-msat = 1000000
fee-base-msat = 546000
fee-proportional-msat = 10
payment-handler = "local"
}
akka {
loggers = ["akka.event.slf4j.Slf4jLogger"]

View file

@ -0,0 +1,20 @@
package fr.acinq.eclair
import org.junit.runner.RunWith
import org.scalatest.FunSuite
import org.scalatest.junit.JUnitRunner
/**
* Created by PM on 27/01/2017.
*/
@RunWith(classOf[JUnitRunner])
class PackageSpec extends FunSuite {
test("calculate simple route") {
val blockHeight = 42000
val txIndex = 27
val outputIndex = 3
assert(fromShortId(toShortId(blockHeight, txIndex, outputIndex)) === (blockHeight, txIndex, outputIndex))
}
}

View file

@ -21,7 +21,8 @@ class PaymentFSMSpec extends TestKit(ActorSystem("test")) with FunSuiteLike with
TestKit.shutdownActorSystem(system)
}
test("route not available") {
// TODO: reenable
/*test("route not available") {
val router = system.actorOf(Props[Router])
val selector = system.actorOf(Props[ChannelSelector])
val channel00 = TestProbe()
@ -47,7 +48,7 @@ class PaymentFSMSpec extends TestKit(ActorSystem("test")) with FunSuiteLike with
sender.send(paymentFsm, CreatePayment(42000000, BinaryData("00112233445566778899aabbccddeeff"), node_c))
val Transition(_, WAITING_FOR_REQUEST, WAITING_FOR_ROUTE) = monitor.expectMsgClass(classOf[Transition[_]])
sender.expectMsgType[Status.Failure]
}
}*/
//TODO re-enable
/*test("payment succeeded") {

View file

@ -1,6 +1,7 @@
package fr.acinq.eclair
import fr.acinq.bitcoin.BinaryData
import fr.acinq.bitcoin.Crypto.PrivateKey
import fr.acinq.bitcoin.{BinaryData, Crypto}
import fr.acinq.eclair.payment.PaymentLifecycle
import fr.acinq.eclair.router.{ChannelDesc, Router}
import lightning.route_step
@ -86,7 +87,7 @@ class RouterSpec extends FunSuite {
assert(route.steps.length == 4 && route.steps.last == route_step(0, next = route_step.Next.End(true)))
assert(route.steps(2).amount == amountMsat)
assert(route.steps.dropRight(1).map(_.next.bitcoin.get.key).map(bytestring2bin) == nodeIds)
assert(route.steps(0).amount - route.steps(1).amount == nodeFee(Globals.base_fee, Globals.proportional_fee, route.steps(1).amount))
assert(route.steps(0).amount - route.steps(1).amount == nodeFee(Globals.fee_base_msat, Globals.fee_proportional_msat, route.steps(1).amount))
}
test("route to neighbor") {
@ -103,7 +104,15 @@ class RouterSpec extends FunSuite {
assert(route.steps.length == 3 && route.steps.last == route_step(0, next = route_step.Next.End(true)))
assert(route.steps(1).amount == amountMsat)
assert(route.steps.dropRight(1).map(_.next.bitcoin.get.key).map(bytestring2bin) == nodeIds)
assert(route.steps(0).amount - route.steps(1).amount == nodeFee(Globals.base_fee, Globals.proportional_fee, route.steps(1).amount))
assert(route.steps(0).amount - route.steps(1).amount == nodeFee(Globals.fee_base_msat, Globals.fee_proportional_msat, route.steps(1).amount))
}
test("compute example sig") {
val data = BinaryData("00" * 32)
val key = PrivateKey(BinaryData("11" * 32))
val sig = Crypto.encodeSignature(Crypto.sign(data, key))
assert(Crypto.isDERSignature(sig :+ 1.toByte))
}
}

View file

@ -44,4 +44,6 @@ class TestBitcoinClient()(implicit system: ActorSystem) extends ExtendedBitcoinC
override def signTransaction(tx: Transaction)(implicit ec: ExecutionContext): Future[SignTransactionResponse] = ???
override def getTransactionShortId(txId: String)(implicit ec: ExecutionContext): Future[(Int, Int)] = Future.successful((42000, 42))
}

View file

@ -51,13 +51,14 @@ class ThroughputSpec extends FunSuite {
context.become(run(h2r - htlc.paymentHash))
}
}), "payment-handler")
val alice = system.actorOf(Channel.props(pipe, blockchain, paymentHandler, Alice.channelParams, "B"), "a")
val bob = system.actorOf(Channel.props(pipe, blockchain, paymentHandler, Bob.channelParams, "A"), "b")
val router: ActorRef = ???
val alice = system.actorOf(Channel.props(pipe, blockchain, ???, paymentHandler, Alice.channelParams, "0B"), "a")
val bob = system.actorOf(Channel.props(pipe, blockchain, ???, paymentHandler, Bob.channelParams, "0A"), "b")
val latch = new CountDownLatch(2)
val listener = system.actorOf(Props(new Actor {
override def receive: Receive = {
case ChannelChangedState(_, _, _, NORMAL, _) => latch.countDown()
case ChannelChangedState(_, _, _, _, NORMAL, _) => latch.countDown()
}
}), "listener")
system.eventStream.subscribe(listener, classOf[ChannelEvent])

View file

@ -41,7 +41,7 @@ trait StateTestsHelperMethods extends TestKitBase {
alice2blockchain.forward(blockchainA)
bob2blockchain.expectMsgType[WatchSpent]
bob2blockchain.expectMsgType[WatchConfirmed]
bob ! BITCOIN_FUNDING_DEPTHOK
bob ! WatchEventConfirmed(BITCOIN_FUNDING_DEPTHOK, 42000, 42)
alice2blockchain.expectMsgType[WatchLost]
bob2blockchain.expectMsgType[WatchLost]
alice2bob.expectMsgType[FundingLocked]

View file

@ -28,8 +28,9 @@ class WaitForAcceptChannelStateSpec extends StateSpecBaseClass {
val blockchainA = system.actorOf(Props(new PeerWatcher(new TestBitcoinClient(), 300)))
val bob2blockchain = TestProbe()
val paymentHandler = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, alice2blockchain.ref, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, paymentHandler.ref, Bob.channelParams, "0A"))
val router = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, alice2blockchain.ref, router.ref, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, router.ref, paymentHandler.ref, Bob.channelParams, "0A"))
alice ! INPUT_INIT_FUNDER(TestConstants.fundingSatoshis, TestConstants.pushMsat)
bob ! INPUT_INIT_FUNDEE()
within(30 seconds) {

View file

@ -25,8 +25,9 @@ class WaitForOpenChannelStateSpec extends StateSpecBaseClass {
val alice2blockchain = TestProbe()
val bob2blockchain = TestProbe()
val paymentHandler = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, alice2blockchain.ref, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, paymentHandler.ref, Bob.channelParams, "0A"))
val router = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, alice2blockchain.ref, router.ref, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, router.ref, paymentHandler.ref, Bob.channelParams, "0A"))
alice ! INPUT_INIT_FUNDER(TestConstants.fundingSatoshis, TestConstants.pushMsat)
bob ! INPUT_INIT_FUNDEE()
within(30 seconds) {

View file

@ -28,8 +28,9 @@ class WaitForFundingCreatedInternalStateSpec extends StateSpecBaseClass {
val blockchainA = system.actorOf(Props(new PeerWatcher(new TestBitcoinClient(), 300)))
val bob2blockchain = TestProbe()
val paymentHandler = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, alice2blockchain.ref, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, paymentHandler.ref, Bob.channelParams, "0A"))
val router = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, alice2blockchain.ref, router.ref, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, router.ref, paymentHandler.ref, Bob.channelParams, "0A"))
alice ! INPUT_INIT_FUNDER(TestConstants.fundingSatoshis, TestConstants.pushMsat)
bob ! INPUT_INIT_FUNDEE()
within(30 seconds) {

View file

@ -27,8 +27,9 @@ class WaitForFundingCreatedStateSpec extends StateSpecBaseClass {
val blockchainA = system.actorOf(Props(new PeerWatcher(new TestBitcoinClient(), 300)))
val bob2blockchain = TestProbe()
val paymentHandler = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, blockchainA, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, paymentHandler.ref, Bob.channelParams, "0A"))
val router = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, blockchainA, router.ref, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, router.ref, paymentHandler.ref, Bob.channelParams, "0A"))
alice ! INPUT_INIT_FUNDER(TestConstants.fundingSatoshis, TestConstants.pushMsat)
bob ! INPUT_INIT_FUNDEE()
within(30 seconds) {

View file

@ -29,8 +29,9 @@ class WaitForFundingSignedStateSpec extends StateSpecBaseClass {
val blockchainA = system.actorOf(Props(new PeerWatcher(new TestBitcoinClient(), 300)))
val bob2blockchain = TestProbe()
val paymentHandler = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, alice2blockchain.ref, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, paymentHandler.ref, Bob.channelParams, "0A"))
val router = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, alice2blockchain.ref, router.ref, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, router.ref, paymentHandler.ref, Bob.channelParams, "0A"))
alice ! INPUT_INIT_FUNDER(TestConstants.fundingSatoshis, TestConstants.pushMsat)
bob ! INPUT_INIT_FUNDEE()
within(30 seconds) {

View file

@ -28,8 +28,9 @@ class WaitForFundingLockedInternalStateSpec extends StateSpecBaseClass {
val blockchainA = system.actorOf(Props(new PeerWatcher(new TestBitcoinClient(), 300)))
val bob2blockchain = TestProbe()
val paymentHandler = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, alice2blockchain.ref, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, paymentHandler.ref, Bob.channelParams, "0A"))
val router = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, alice2blockchain.ref, router.ref, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, router.ref, paymentHandler.ref, Bob.channelParams, "0A"))
alice ! INPUT_INIT_FUNDER(TestConstants.fundingSatoshis, TestConstants.pushMsat)
bob ! INPUT_INIT_FUNDEE()
within(30 seconds) {
@ -54,7 +55,7 @@ class WaitForFundingLockedInternalStateSpec extends StateSpecBaseClass {
test("recv FundingLocked") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, _) =>
within(30 seconds) {
// make bob send a FundingLocked msg
bob ! BITCOIN_FUNDING_DEPTHOK
bob ! WatchEventConfirmed(BITCOIN_FUNDING_DEPTHOK, 42000, 42)
val msg = bob2alice.expectMsgType[FundingLocked]
bob2alice.forward(alice)
awaitCond(alice.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_LOCKED_INTERNAL].deferred == Some(msg))
@ -64,7 +65,7 @@ class WaitForFundingLockedInternalStateSpec extends StateSpecBaseClass {
test("recv BITCOIN_FUNDING_DEPTHOK") { case (alice, _, alice2bob, bob2alice, alice2blockchain, _) =>
within(30 seconds) {
alice ! BITCOIN_FUNDING_DEPTHOK
alice ! WatchEventConfirmed(BITCOIN_FUNDING_DEPTHOK, 42000, 42)
awaitCond(alice.stateName == WAIT_FOR_FUNDING_LOCKED)
alice2blockchain.expectMsgType[WatchLost]
alice2bob.expectMsgType[FundingLocked]
@ -83,7 +84,7 @@ class WaitForFundingLockedInternalStateSpec extends StateSpecBaseClass {
within(30 seconds) {
// bob publishes his commitment tx
val tx = bob.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_LOCKED_INTERNAL].commitments.localCommit.publishableTxs.commitTx.tx
alice ! (BITCOIN_FUNDING_SPENT, tx)
alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, tx)
alice2blockchain.expectMsgType[WatchConfirmed]
awaitCond(alice.stateName == CLOSING)
}
@ -92,7 +93,7 @@ class WaitForFundingLockedInternalStateSpec extends StateSpecBaseClass {
test("recv BITCOIN_FUNDING_SPENT (other commit)") { case (alice, _, alice2bob, bob2alice, alice2blockchain, _) =>
within(30 seconds) {
val tx = alice.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_LOCKED_INTERNAL].commitments.localCommit.publishableTxs.commitTx.tx
alice ! (BITCOIN_FUNDING_SPENT, null)
alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, null)
alice2bob.expectMsgType[Error]
alice2blockchain.expectMsg(PublishAsap(tx))
awaitCond(alice.stateName == ERR_INFORMATION_LEAK)

View file

@ -28,8 +28,9 @@ class WaitForFundingLockedStateSpec extends StateSpecBaseClass {
val blockchainA = system.actorOf(Props(new PeerWatcher(new TestBitcoinClient(), 300)))
val bob2blockchain = TestProbe()
val paymentHandler = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, alice2blockchain.ref, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, paymentHandler.ref, Bob.channelParams, "0A"))
val router = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, alice2blockchain.ref, router.ref, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, router.ref, paymentHandler.ref, Bob.channelParams, "0A"))
alice ! INPUT_INIT_FUNDER(TestConstants.fundingSatoshis, TestConstants.pushMsat)
bob ! INPUT_INIT_FUNDEE()
within(30 seconds) {
@ -50,7 +51,7 @@ class WaitForFundingLockedStateSpec extends StateSpecBaseClass {
alice2blockchain.forward(blockchainA)
bob2blockchain.expectMsgType[WatchSpent]
bob2blockchain.expectMsgType[WatchConfirmed]
bob ! BITCOIN_FUNDING_DEPTHOK
bob ! WatchEventConfirmed(BITCOIN_FUNDING_DEPTHOK, 42000, 42)
alice2blockchain.expectMsgType[WatchLost]
bob2blockchain.expectMsgType[WatchLost]
alice2bob.expectMsgType[FundingLocked]
@ -68,11 +69,23 @@ class WaitForFundingLockedStateSpec extends StateSpecBaseClass {
}
}
test("recv FundingLocked (channel id mismatch") { case (alice, _, alice2bob, bob2alice, alice2blockchain, _) =>
within(30 seconds) {
val tx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx
val fundingLocked = bob2alice.expectMsgType[FundingLocked]
alice ! fundingLocked.copy(channelId = 42)
alice2bob.expectMsgType[Error]
awaitCond(alice.stateName == CLOSING)
alice2blockchain.expectMsg(PublishAsap(tx))
alice2blockchain.expectMsgType[WatchConfirmed]
}
}
test("recv BITCOIN_FUNDING_SPENT (remote commit)") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, _) =>
within(30 seconds) {
// bob publishes his commitment tx
val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx
alice ! (BITCOIN_FUNDING_SPENT, tx)
alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, tx)
alice2blockchain.expectMsgType[WatchConfirmed]
awaitCond(alice.stateName == CLOSING)
}
@ -81,7 +94,7 @@ class WaitForFundingLockedStateSpec extends StateSpecBaseClass {
test("recv BITCOIN_FUNDING_SPENT (other commit)") { case (alice, _, alice2bob, bob2alice, alice2blockchain, _) =>
within(30 seconds) {
val tx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx
alice ! (BITCOIN_FUNDING_SPENT, null)
alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, null)
alice2bob.expectMsgType[Error]
alice2blockchain.expectMsg(PublishAsap(tx))
awaitCond(alice.stateName == ERR_INFORMATION_LEAK)

View file

@ -31,8 +31,9 @@ class NormalStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
val blockchainA = system.actorOf(Props(new PeerWatcher(new TestBitcoinClient(), 300)))
val bob2blockchain = TestProbe()
val paymentHandler = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, alice2blockchain.ref, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, paymentHandler.ref, Bob.channelParams, "0A"))
val router = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, alice2blockchain.ref, router.ref, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, router.ref, paymentHandler.ref, Bob.channelParams, "0A"))
within(30 seconds) {
reachNormal(alice, bob, alice2bob, bob2alice, blockchainA, alice2blockchain, bob2blockchain)
awaitCond(alice.stateName == NORMAL)
@ -803,7 +804,7 @@ class NormalStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
// 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 ! (BITCOIN_FUNDING_SPENT, bobCommitTx)
alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobCommitTx)
alice2blockchain.expectMsgType[WatchConfirmed].txId == bobCommitTx.txid
@ -855,7 +856,7 @@ class NormalStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
// a->b = 10 000
// two main outputs + 4 htlc
assert(revokedTx.txOut.size == 6)
alice ! (BITCOIN_FUNDING_SPENT, revokedTx)
alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, revokedTx)
alice2bob.expectMsgType[Error]
alice2blockchain.expectMsgType[WatchConfirmed]

View file

@ -30,8 +30,9 @@ class ShutdownStateSpec extends StateSpecBaseClass with StateTestsHelperMethods
val blockchainA = system.actorOf(Props(new PeerWatcher(new TestBitcoinClient(), 300)))
val bob2blockchain = TestProbe()
val paymentHandler = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, alice2blockchain.ref, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, paymentHandler.ref, Bob.channelParams, "0A"))
val router = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, alice2blockchain.ref, router.ref, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, router.ref, paymentHandler.ref, Bob.channelParams, "0A"))
within(30 seconds) {
reachNormal(alice, bob, alice2bob, bob2alice, blockchainA, alice2blockchain, bob2blockchain)
val sender = TestProbe()
@ -356,7 +357,7 @@ class ShutdownStateSpec extends StateSpecBaseClass with StateTestsHelperMethods
// 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 ! (BITCOIN_FUNDING_SPENT, bobCommitTx)
alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobCommitTx)
alice2blockchain.expectMsgType[WatchConfirmed].txId == bobCommitTx.txid
@ -392,7 +393,7 @@ class ShutdownStateSpec extends StateSpecBaseClass with StateTestsHelperMethods
// bob now has a new commitment tx
// bob published the revoked tx
alice ! (BITCOIN_FUNDING_SPENT, revokedTx)
alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, revokedTx)
alice2bob.expectMsgType[Error]
alice2blockchain.expectMsgType[WatchConfirmed]

View file

@ -29,8 +29,9 @@ class NegotiatingStateSpec extends StateSpecBaseClass with StateTestsHelperMetho
val bob2blockchain = TestProbe()
val paymentHandler = TestProbe()
// note that alice.initialFeeRate != bob.initialFeeRate
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, alice2blockchain.ref, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, paymentHandler.ref, Bob.channelParams, "0A"))
val router = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, alice2blockchain.ref, router.ref, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, router.ref, paymentHandler.ref, Bob.channelParams, "0A"))
within(30 seconds) {
reachNormal(alice, bob, alice2bob, bob2alice, blockchainA, alice2blockchain, bob2blockchain)
val sender = TestProbe()
@ -91,7 +92,7 @@ class NegotiatingStateSpec extends StateSpecBaseClass with StateTestsHelperMetho
assert(alice.stateName == NEGOTIATING)
val mutualCloseTx = bob2blockchain.expectMsgType[PublishAsap].tx
bob2blockchain.expectMsgType[WatchConfirmed]
alice ! (BITCOIN_FUNDING_SPENT, mutualCloseTx)
alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, mutualCloseTx)
alice2blockchain.expectNoMsg(1 second)
assert(alice.stateName == NEGOTIATING)
}

View file

@ -29,8 +29,9 @@ class ClosingStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
val blockchainA = system.actorOf(Props(new PeerWatcher(new TestBitcoinClient(), 300)))
val bob2blockchain = TestProbe()
val paymentHandler = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, alice2blockchain.ref, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, paymentHandler.ref, Bob.channelParams, "0A"))
val router = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, alice2blockchain.ref, router.ref, paymentHandler.ref, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, router.ref, paymentHandler.ref, Bob.channelParams, "0A"))
within(30 seconds) {
reachNormal(alice, bob, alice2bob, bob2alice, blockchainA, alice2blockchain, bob2blockchain)
@ -96,7 +97,7 @@ class ClosingStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
mutualClose(alice, bob, alice2bob, bob2alice, alice2blockchain, bob2blockchain)
// actual test starts here
alice ! BITCOIN_CLOSE_DONE
alice ! WatchEventConfirmed(BITCOIN_CLOSE_DONE, 0, 0)
awaitCond(alice.stateName == CLOSED)
}
}
@ -114,7 +115,7 @@ class ClosingStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
// actual test starts here
// we are notified afterwards from our watcher about the tx that we just published
alice ! (BITCOIN_FUNDING_SPENT, aliceCommitTx)
alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, aliceCommitTx)
assert(alice.stateData == initialState) // this was a no-op
}
}
@ -130,7 +131,7 @@ class ClosingStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
assert(alice.stateData.asInstanceOf[DATA_CLOSING].localCommitPublished.isDefined)
// actual test starts here
alice ! BITCOIN_SPEND_OURS_DONE
alice ! WatchEventConfirmed(BITCOIN_SPEND_OURS_DONE, 0, 0)
awaitCond(alice.stateName == CLOSED)
}
}
@ -142,7 +143,7 @@ class ClosingStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
// bob publishes his last current commit tx, the one it had when entering NEGOTIATING state
val bobCommitTx = bobCommitTxes.last
assert(bobCommitTx.txOut.size == 2) // two main outputs
alice ! (BITCOIN_FUNDING_SPENT, bobCommitTx)
alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobCommitTx)
alice2blockchain.expectMsgType[WatchConfirmed].txId == bobCommitTx.txid
@ -158,14 +159,14 @@ class ClosingStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
// bob publishes his last current commit tx, the one it had when entering NEGOTIATING state
val bobCommitTx = bobCommitTxes.last
assert(bobCommitTx.txOut.size == 2) // two main outputs
alice ! (BITCOIN_FUNDING_SPENT, bobCommitTx)
alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobCommitTx)
alice2blockchain.expectMsgType[WatchConfirmed].txId == bobCommitTx.txid
awaitCond(alice.stateData.asInstanceOf[DATA_CLOSING].remoteCommitPublished.isDefined)
assert(alice.stateData.asInstanceOf[DATA_CLOSING].copy(remoteCommitPublished = None) == initialState)
// actual test starts here
alice ! BITCOIN_SPEND_THEIRS_DONE
alice ! WatchEventConfirmed(BITCOIN_SPEND_THEIRS_DONE, 0, 0)
awaitCond(alice.stateName == CLOSED)
}
}
@ -176,7 +177,7 @@ class ClosingStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
val initialState = alice.stateData.asInstanceOf[DATA_CLOSING]
// bob publishes one of his revoked txes
val bobRevokedTx = bobCommitTxes.head
alice ! (BITCOIN_FUNDING_SPENT, bobRevokedTx)
alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobRevokedTx)
// alice publishes and watches the punishment tx
alice2blockchain.expectMsgType[WatchConfirmed]
@ -192,7 +193,7 @@ class ClosingStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
mutualClose(alice, bob, alice2bob, bob2alice, alice2blockchain, bob2blockchain)
// bob publishes multiple revoked txes (last one isn't revoked)
for (bobRevokedTx <- bobCommitTxes.dropRight(1)) {
alice ! (BITCOIN_FUNDING_SPENT, bobRevokedTx)
alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobRevokedTx)
// alice publishes and watches the punishment tx
alice2blockchain.expectMsgType[WatchConfirmed]
alice2blockchain.expectMsgType[PublishAsap]
@ -208,7 +209,7 @@ class ClosingStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
val initialState = alice.stateData.asInstanceOf[DATA_CLOSING]
// bob publishes one of his revoked txes
val bobRevokedTx = bobCommitTxes.head
alice ! (BITCOIN_FUNDING_SPENT, bobRevokedTx)
alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobRevokedTx)
// alice publishes and watches the punishment tx
alice2blockchain.expectMsgType[WatchConfirmed]
alice2blockchain.expectMsgType[PublishAsap]
@ -216,7 +217,7 @@ class ClosingStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
// awaitCond(alice.stateData.asInstanceOf[DATA_CLOSING] == initialState.copy(revokedCommitPublished = Seq(RevokedCommitPublished(bobRevokedTx))))
// actual test starts here
alice ! BITCOIN_PUNISHMENT_DONE
alice ! WatchEventConfirmed(BITCOIN_PUNISHMENT_DONE, 0, 0)
awaitCond(alice.stateName == CLOSED)
}
}

View file

@ -29,8 +29,8 @@ class TransportHandlerSpec extends TestKit(ActorSystem("test")) with FunSuiteLik
val pipe = system.actorOf(Props[MyPipe])
val probe1 = TestProbe()
val probe2 = TestProbe()
val initiator = TestFSMRef(new TransportHandler(Initiator.s, Some(Responder.s.pub), pipe, true, (conn, _) => probe1.ref, TransportHandler.Noop))
val responder = TestFSMRef(new TransportHandler(Responder.s, None, pipe, false, (conn, _) => probe2.ref, TransportHandler.Noop))
val initiator = TestFSMRef(new TransportHandler(Initiator.s, Some(Responder.s.pub), pipe, true, (conn, _, _) => probe1.ref, TransportHandler.Noop))
val responder = TestFSMRef(new TransportHandler(Responder.s, None, pipe, false, (conn, _, _) => probe2.ref, TransportHandler.Noop))
pipe ! (initiator, responder)
awaitCond(initiator.stateName == TransportHandler.WaitingForCyphertext)
@ -65,8 +65,8 @@ class TransportHandlerSpec extends TestKit(ActorSystem("test")) with FunSuiteLik
val pipe = system.actorOf(Props[MyPipe])
val probe1 = TestProbe()
val probe2 = TestProbe()
val initiator = TestFSMRef(new TransportHandler(Initiator.s, Some(Responder.s.pub), pipe, true, (conn, _) => probe1.ref, mySerializer))
val responder = TestFSMRef(new TransportHandler(Responder.s, None, pipe, false, (conn, _) => probe2.ref, mySerializer))
val initiator = TestFSMRef(new TransportHandler(Initiator.s, Some(Responder.s.pub), pipe, true, (conn, _, _) => probe1.ref, mySerializer))
val responder = TestFSMRef(new TransportHandler(Responder.s, None, pipe, false, (conn, _, _) => probe2.ref, mySerializer))
pipe ! (initiator, responder)
awaitCond(initiator.stateName == TransportHandler.WaitingForCyphertext)
@ -89,8 +89,8 @@ class TransportHandlerSpec extends TestKit(ActorSystem("test")) with FunSuiteLik
val pipe = system.actorOf(Props[MyPipeSplitter])
val probe1 = TestProbe()
val probe2 = TestProbe()
val initiator = TestFSMRef(new TransportHandler(Initiator.s, Some(Responder.s.pub), pipe, true, (conn, _) => probe1.ref, TransportHandler.Noop))
val responder = TestFSMRef(new TransportHandler(Responder.s, None, pipe, false, (conn, _) => probe2.ref, TransportHandler.Noop))
val initiator = TestFSMRef(new TransportHandler(Initiator.s, Some(Responder.s.pub), pipe, true, (conn, _, _) => probe1.ref, TransportHandler.Noop))
val responder = TestFSMRef(new TransportHandler(Responder.s, None, pipe, false, (conn, _, _) => probe2.ref, TransportHandler.Noop))
pipe ! (initiator, responder)
awaitCond(initiator.stateName == TransportHandler.WaitingForCyphertext)
@ -113,8 +113,8 @@ class TransportHandlerSpec extends TestKit(ActorSystem("test")) with FunSuiteLik
val pipe = system.actorOf(Props[MyPipe])
val probe1 = TestProbe()
val probe2 = TestProbe()
val initiator = TestFSMRef(new TransportHandler(Initiator.s, Some(Initiator.s.pub), pipe, true, (conn, _) => probe1.ref, TransportHandler.Noop))
val responder = TestFSMRef(new TransportHandler(Responder.s, None, pipe, false, (conn, _) => probe2.ref, TransportHandler.Noop))
val initiator = TestFSMRef(new TransportHandler(Initiator.s, Some(Initiator.s.pub), pipe, true, (conn, _, _) => probe1.ref, TransportHandler.Noop))
val responder = TestFSMRef(new TransportHandler(Responder.s, None, pipe, false, (conn, _, _) => probe2.ref, TransportHandler.Noop))
pipe ! (initiator, responder)
Thread.sleep(1000)

View file

@ -90,7 +90,7 @@ class InteroperabilitySpec extends FunSuite with BeforeAndAfterAll {
super.afterAll()
}
def sendCommand(channelId: String, cmd: Command): Future[String] = {
def sendCommand(channelId: Long, cmd: Command): Future[String] = {
system.actorSelection(Register.actorPathToChannelId(system, channelId)).resolveOne().map(actor => {
actor ! cmd
"ok"
@ -145,7 +145,7 @@ class InteroperabilitySpec extends FunSuite with BeforeAndAfterAll {
def now: Int = (System.currentTimeMillis() / 1000).toInt
val future = for {
channelId <- listChannels.map(_.head).map(_.channelId.toString)
channelId <- listChannels.map(_.head).map(_.channelId)
peer = lncli.getPeers.head
// lightningd sends us a htlc
blockcount <- btccli.getBlockCount

View file

@ -4,7 +4,7 @@ import java.io.File
import java.util.concurrent.{CountDownLatch, TimeUnit}
import akka.actor.{ActorRef, ActorSystem, Props}
import akka.testkit.{TestFSMRef, TestKit}
import akka.testkit.{TestFSMRef, TestKit, TestProbe}
import fr.acinq.eclair.TestConstants.{Alice, Bob}
import fr.acinq.eclair.blockchain.PeerWatcher
import fr.acinq.eclair.channel._
@ -31,8 +31,9 @@ class RustyTestsSpec extends TestKit(ActorSystem("test")) with Matchers with fix
val blockchainA = system.actorOf(Props(new PeerWatcher(new TestBitcoinClient(), 300)))
val blockchainB = system.actorOf(Props(new PeerWatcher(new TestBitcoinClient(), 300)))
val paymentHandler = system.actorOf(Props(new NoopPaymentHandler()))
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(pipe, blockchainA, paymentHandler, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(pipe, blockchainB, paymentHandler, Bob.channelParams, "0A"))
val router = TestProbe()
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(pipe, blockchainA, router.ref, paymentHandler, Alice.channelParams, "0B"))
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(pipe, blockchainB, router.ref, paymentHandler, Bob.channelParams, "0A"))
alice ! INPUT_INIT_FUNDER(TestConstants.fundingSatoshis, 0)
bob ! INPUT_INIT_FUNDEE()
pipe ! (alice, bob)