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:
parent
6d16f315fe
commit
ec07cc46ab
43 changed files with 472 additions and 324 deletions
|
@ -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 {
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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-*"
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
})*/
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
}
|
|
@ -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 => {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 _ => {}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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])
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
|
@ -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"]
|
||||
|
|
20
eclair-node/src/test/scala/fr/acinq/eclair/PackageSpec.scala
Normal file
20
eclair-node/src/test/scala/fr/acinq/eclair/PackageSpec.scala
Normal 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))
|
||||
}
|
||||
|
||||
}
|
|
@ -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") {
|
||||
|
|
|
@ -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))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
||||
}
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue