mirror of
https://github.com/ACINQ/eclair.git
synced 2024-11-20 02:27:32 +01:00
updated api
This commit is contained in:
parent
3d08c7e391
commit
c9d6a20954
@ -8,11 +8,12 @@ import akka.http.scaladsl.Http
|
||||
import akka.stream.ActorMaterializer
|
||||
import akka.util.Timeout
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import fr.acinq.bitcoin.{Base58Check, OP_CHECKSIG, OP_DUP, OP_EQUALVERIFY, OP_HASH160, OP_PUSHDATA}
|
||||
import fr.acinq.bitcoin.{Base58Check, OP_CHECKSIG, OP_DUP, OP_EQUALVERIFY, OP_HASH160, OP_PUSHDATA, Script}
|
||||
import fr.acinq.eclair.api.Service
|
||||
import fr.acinq.eclair.blockchain.peer.PeerClient
|
||||
import fr.acinq.eclair.blockchain.rpc.BitcoinJsonRPCClient
|
||||
import fr.acinq.eclair.blockchain.{ExtendedBitcoinClient, PeerWatcher}
|
||||
import fr.acinq.eclair.channel.Register
|
||||
import fr.acinq.eclair.gui.FxApp
|
||||
import fr.acinq.eclair.io.{Server, Switchboard}
|
||||
import fr.acinq.eclair.payment._
|
||||
@ -67,7 +68,7 @@ class Setup() extends Logging {
|
||||
logger.info(s"finaladdress=$finalAddress")
|
||||
// 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 finalScriptPubKey = Script.write(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]()
|
||||
@ -87,6 +88,7 @@ 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(Props(new Register), name = "register")
|
||||
val relayer = system.actorOf(Relayer.props(Globals.Node.privateKey, paymentHandler), name = "relayer")
|
||||
val router = system.actorOf(Router.props(watcher), name = "router")
|
||||
val paymentInitiator = system.actorOf(PaymentInitiator.props(Globals.Node.publicKey, router), "payment-initiator")
|
||||
@ -97,8 +99,10 @@ class Setup() extends Logging {
|
||||
val api = new Service {
|
||||
override val switchboard: ActorRef = _setup.switchboard
|
||||
override val router: ActorRef = _setup.router
|
||||
override val register: ActorRef = _setup.register
|
||||
override val paymentHandler: ActorRef = _setup.paymentHandler
|
||||
override val paymentInitiator: ActorRef = _setup.paymentInitiator
|
||||
override val system: ActorSystem = _setup.system
|
||||
}
|
||||
Http().bindAndHandle(api.route, config.getString("eclair.api.host"), config.getInt("eclair.api.port")) onFailure {
|
||||
case t: Throwable => system.eventStream.publish(HTTPBindError)
|
||||
|
@ -1,8 +1,10 @@
|
||||
package fr.acinq.eclair.api
|
||||
|
||||
import fr.acinq.bitcoin.BinaryData
|
||||
import fr.acinq.bitcoin.{BinaryData, Script, ScriptElt, Transaction}
|
||||
import fr.acinq.bitcoin.Crypto.{Point, PrivateKey, PublicKey, Scalar}
|
||||
import fr.acinq.eclair.channel.State
|
||||
import fr.acinq.eclair.crypto.ShaChain
|
||||
import fr.acinq.eclair.transactions.Transactions.TransactionWithInputInfo
|
||||
import org.json4s.CustomSerializer
|
||||
import org.json4s.JsonAST.{JNull, JString}
|
||||
|
||||
@ -32,3 +34,43 @@ class ShaChainSerializer extends CustomSerializer[ShaChain](format => ( {
|
||||
case x: ShaChain => JNull
|
||||
}
|
||||
))
|
||||
|
||||
class PublicKeySerializer extends CustomSerializer[PublicKey](format => ( {
|
||||
case JString(x) if (false) => // NOT IMPLEMENTED
|
||||
???
|
||||
}, {
|
||||
case x: PublicKey => JString(x.toString())
|
||||
}
|
||||
))
|
||||
|
||||
class PrivateKeySerializer extends CustomSerializer[PrivateKey](format => ( {
|
||||
case JString(x) if (false) => // NOT IMPLEMENTED
|
||||
???
|
||||
}, {
|
||||
case x: PrivateKey => JString("XXX")
|
||||
}
|
||||
))
|
||||
|
||||
class PointSerializer extends CustomSerializer[Point](format => ( {
|
||||
case JString(x) if (false) => // NOT IMPLEMENTED
|
||||
???
|
||||
}, {
|
||||
case x: Point => JString(x.toString())
|
||||
}
|
||||
))
|
||||
|
||||
class ScalarSerializer extends CustomSerializer[Scalar](format => ( {
|
||||
case JString(x) if (false) => // NOT IMPLEMENTED
|
||||
???
|
||||
}, {
|
||||
case x: Scalar => JString("XXX")
|
||||
}
|
||||
))
|
||||
|
||||
class TransactionWithInputInfoSerializer extends CustomSerializer[TransactionWithInputInfo](format => ( {
|
||||
case JString(x) if (false) => // NOT IMPLEMENTED
|
||||
???
|
||||
}, {
|
||||
case x: TransactionWithInputInfo => JString(Transaction.write(x.tx).toString())
|
||||
}
|
||||
))
|
||||
|
@ -2,7 +2,7 @@ package fr.acinq.eclair.api
|
||||
|
||||
import java.net.InetSocketAddress
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import akka.actor.{ActorRef, ActorSystem}
|
||||
import akka.http.scaladsl.model.HttpMethods._
|
||||
import akka.http.scaladsl.model.StatusCodes
|
||||
import akka.http.scaladsl.model.headers.CacheDirectives.{`max-age`, `no-store`, public}
|
||||
@ -15,11 +15,10 @@ import de.heikoseeberger.akkahttpjson4s.Json4sSupport
|
||||
import de.heikoseeberger.akkahttpjson4s.Json4sSupport.ShouldWritePretty
|
||||
import fr.acinq.bitcoin.Crypto.PublicKey
|
||||
import fr.acinq.bitcoin.{BinaryData, MilliSatoshi, Satoshi}
|
||||
import fr.acinq.eclair._
|
||||
import fr.acinq.eclair.channel._
|
||||
import fr.acinq.eclair.io.Switchboard.{NewChannel, NewConnection}
|
||||
import fr.acinq.eclair.payment.CreatePayment
|
||||
import fr.acinq.eclair.router.ChannelDesc
|
||||
import fr.acinq.eclair.wire.NodeAnnouncement
|
||||
import grizzled.slf4j.Logging
|
||||
import org.json4s.JsonAST.{JInt, JString}
|
||||
import org.json4s.{JValue, jackson}
|
||||
@ -44,7 +43,7 @@ trait Service extends Logging {
|
||||
implicit def ec: ExecutionContext = ExecutionContext.Implicits.global
|
||||
|
||||
implicit val serialization = jackson.Serialization
|
||||
implicit val formats = org.json4s.DefaultFormats + new BinaryDataSerializer + new StateSerializer + new ShaChainSerializer
|
||||
implicit val formats = org.json4s.DefaultFormats + new BinaryDataSerializer + new StateSerializer + new ShaChainSerializer + new PublicKeySerializer + new PrivateKeySerializer + new ScalarSerializer + new PointSerializer + new TransactionWithInputInfoSerializer
|
||||
implicit val timeout = Timeout(30 seconds)
|
||||
implicit val shouldWritePretty: ShouldWritePretty = ShouldWritePretty.True
|
||||
|
||||
@ -54,16 +53,25 @@ trait Service extends Logging {
|
||||
|
||||
def router: ActorRef
|
||||
|
||||
def register: ActorRef
|
||||
|
||||
def paymentInitiator: ActorRef
|
||||
|
||||
def paymentHandler: ActorRef
|
||||
|
||||
def system: ActorSystem
|
||||
|
||||
val customHeaders = `Access-Control-Allow-Origin`(*) ::
|
||||
`Access-Control-Allow-Headers`("Content-Type, Authorization") ::
|
||||
`Access-Control-Allow-Methods`(PUT, GET, POST, DELETE, OPTIONS) ::
|
||||
`Cache-Control`(public, `no-store`, `max-age`(0)) ::
|
||||
`Access-Control-Allow-Headers`("x-requested-with") :: Nil
|
||||
|
||||
def getChannel(channelIdHex: String): Future[ActorRef] =
|
||||
for {
|
||||
channels <- (register ? 'channels).mapTo[Map[String, ActorRef]]
|
||||
} yield channels.get(channelIdHex).getOrElse(throw new RuntimeException("unknown channel"))
|
||||
|
||||
val route =
|
||||
respondWithDefaultHeaders(customHeaders) {
|
||||
pathSingleSlash {
|
||||
@ -72,39 +80,36 @@ trait Service extends Logging {
|
||||
req =>
|
||||
val f_res: Future[AnyRef] = req match {
|
||||
case JsonRPCBody(_, _, "connect", JString(host) :: JInt(port) :: JString(pubkey) :: Nil) =>
|
||||
switchboard ! NewConnection(PublicKey(pubkey), new InetSocketAddress(host, port.toInt), None)
|
||||
Future.successful("ok")
|
||||
case JsonRPCBody(_, _, "open", JString(host) :: JInt(port) :: JString(pubkey) :: JInt(funding_amount) :: Nil) =>
|
||||
switchboard ! NewConnection(PublicKey(pubkey), new InetSocketAddress(host, port.toInt), Some(NewChannel(Satoshi(funding_amount.toLong), MilliSatoshi(0))))
|
||||
Future.successful("ok")
|
||||
case JsonRPCBody(_, _, "info", _) =>
|
||||
Future.successful(Status(Globals.Node.id))
|
||||
/*case JsonRPCBody(_, _, "list", _) =>
|
||||
(register ? ListChannels).mapTo[Iterable[ActorRef]]
|
||||
.flatMap(l => Future.sequence(l.map(c => c ? CMD_GETINFO)))*/
|
||||
(switchboard ? NewConnection(PublicKey(pubkey), new InetSocketAddress(host, port.toInt), None)).mapTo[String]
|
||||
case JsonRPCBody(_, _, "open", JString(host) :: JInt(port) :: JString(pubkey) :: JInt(fundingSatoshi) :: JInt(pushMsat) :: Nil) =>
|
||||
(switchboard ? NewConnection(PublicKey(pubkey), new InetSocketAddress(host, port.toInt), Some(NewChannel(Satoshi(fundingSatoshi.toLong), MilliSatoshi(pushMsat.toLong))))).mapTo[String]
|
||||
case JsonRPCBody(_, _, "peers", _) =>
|
||||
(switchboard ? 'peers).mapTo[Iterable[PublicKey]].map(_.map(_.toBin))
|
||||
case JsonRPCBody(_, _, "channels", _) =>
|
||||
(register ? 'channels).mapTo[Map[Long, ActorRef]].map(_.keys)
|
||||
case JsonRPCBody(_, _, "channel", JString(channelIdHex) :: Nil) =>
|
||||
getChannel(channelIdHex).flatMap(_ ? CMD_GETINFO).mapTo[RES_GETINFO]
|
||||
case JsonRPCBody(_, _, "network", _) =>
|
||||
(router ? 'channels).mapTo[Iterable[ChannelDesc]]
|
||||
case JsonRPCBody(_, _, "addhtlc", JInt(amount) :: JString(rhash) :: JString(nodeId) :: Nil) =>
|
||||
(paymentInitiator ? CreatePayment(amount.toLong, BinaryData(rhash), BinaryData(nodeId))).mapTo[ChannelEvent]
|
||||
(router ? 'nodes).mapTo[Iterable[NodeAnnouncement]].map(_.map(_.nodeId))
|
||||
case JsonRPCBody(_, _, "genh", _) =>
|
||||
(paymentHandler ? 'genh).mapTo[BinaryData]
|
||||
/*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.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.toLong, CMD_CLOSE(Some(scriptPubKey)))).mapTo[ActorRef].map(_ => "ok")
|
||||
case JsonRPCBody(_, _, "close", JString(channel) :: Nil) =>
|
||||
(register ? SendCommand(channel.toLong, CMD_CLOSE(None))).mapTo[ActorRef].map(_ => "ok")*/
|
||||
case JsonRPCBody(_, _, "send", JInt(amountMsat) :: JString(paymentHash) :: JString(nodeId) :: Nil) =>
|
||||
(paymentInitiator ? CreatePayment(amountMsat.toLong, paymentHash, PublicKey(nodeId))).mapTo[String]
|
||||
case JsonRPCBody(_, _, "close", JString(channelIdHex) :: JString(scriptPubKey) :: Nil) =>
|
||||
getChannel(channelIdHex).flatMap(_ ? CMD_CLOSE(scriptPubKey = Some(scriptPubKey))).mapTo[String]
|
||||
case JsonRPCBody(_, _, "close", JString(channelIdHex) :: Nil) =>
|
||||
getChannel(channelIdHex).flatMap(_ ? CMD_CLOSE(scriptPubKey = None)).mapTo[String]
|
||||
case JsonRPCBody(_, _, "help", _) =>
|
||||
Future.successful(List(
|
||||
"info: display basic node information",
|
||||
"connect (host, port, anchor_amount): open a channel with another eclair or lightningd instance",
|
||||
"list: list existing channels",
|
||||
"addhtlc (amount, rhash, nodeId): send an htlc",
|
||||
"sign (channel_id): update the commitment transaction",
|
||||
"fulfillhtlc (channel_id, htlc_id, r): fulfill an htlc",
|
||||
"connect (host, port, nodeId): opens a secure connection with another lightning node",
|
||||
"connect (host, port, nodeId, fundingSat, pushMsat): open a channel with another lightning node",
|
||||
"peers: list existing peers",
|
||||
"channels: list existing channels",
|
||||
"channel (channelId): retrieve detailed information about a given channel",
|
||||
"send (amount, paymentHash, nodeId): send a payment to a lightning node",
|
||||
"close (channel_id): close a channel",
|
||||
"close (channel_id, scriptPubKey): close a channel and send the funds to the given scriptPubKey",
|
||||
"help: display this message"))
|
||||
case _ => Future.failed(new RuntimeException("method not found"))
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ class Channel(val r: ActorRef, val blockchain: ActorRef, router: ActorRef, relay
|
||||
when(WAIT_FOR_INIT_INTERNAL)(handleExceptions {
|
||||
case Event(initFunder@INPUT_INIT_FUNDER(remoteNodeId, temporaryChannelId, fundingSatoshis, pushMsat, localParams, remoteInit), Nothing) =>
|
||||
this.remoteNodeId = remoteNodeId
|
||||
context.system.eventStream.publish(ChannelCreated(context.parent, self, localParams, remoteNodeId))
|
||||
context.system.eventStream.publish(ChannelCreated(temporaryChannelId, context.parent, self, localParams, remoteNodeId))
|
||||
val firstPerCommitmentPoint = Generators.perCommitPoint(localParams.shaSeed, 0)
|
||||
val open = OpenChannel(temporaryChannelId = temporaryChannelId,
|
||||
fundingSatoshis = fundingSatoshis,
|
||||
@ -94,7 +94,6 @@ class Channel(val r: ActorRef, val blockchain: ActorRef, router: ActorRef, relay
|
||||
|
||||
case Event(inputFundee@INPUT_INIT_FUNDEE(remoteNodeId, _, localParams, _), Nothing) if !localParams.isFunder =>
|
||||
this.remoteNodeId = remoteNodeId
|
||||
context.system.eventStream.publish(ChannelCreated(context.parent, self, localParams, remoteNodeId))
|
||||
goto(WAIT_FOR_OPEN_CHANNEL) using DATA_WAIT_FOR_OPEN_CHANNEL(inputFundee)
|
||||
})
|
||||
|
||||
@ -106,6 +105,7 @@ class Channel(val r: ActorRef, val blockchain: ActorRef, router: ActorRef, relay
|
||||
remote ! Error(open.temporaryChannelId, t.getMessage.getBytes)
|
||||
goto(CLOSED)
|
||||
case Success(_) =>
|
||||
context.system.eventStream.publish(ChannelCreated(open.temporaryChannelId, context.parent, self, localParams, remoteNodeId))
|
||||
// TODO: maybe also check uniqueness of temporary channel id
|
||||
val minimumDepth = Globals.mindepth_blocks
|
||||
val firstPerCommitmentPoint = Generators.perCommitPoint(localParams.shaSeed, 0)
|
||||
@ -329,7 +329,7 @@ class Channel(val r: ActorRef, val blockchain: ActorRef, router: ActorRef, relay
|
||||
|
||||
case Event(FundingLocked(_, _, nextPerCommitmentPoint), d@DATA_WAIT_FOR_FUNDING_LOCKED(params, commitments, _)) =>
|
||||
log.info(s"channelId=${java.lang.Long.toUnsignedString(d.channelId)}")
|
||||
Register.createAlias(remoteNodeId.hash160, d.channelId)
|
||||
Register.createAlias(remoteNodeId, d.channelId)
|
||||
// this clock will be used to detect htlc timeouts
|
||||
context.system.eventStream.subscribe(self, classOf[CurrentBlockCount])
|
||||
if (Features.isChannelPublic(params.localParams.localFeatures) && Features.isChannelPublic(params.remoteParams.localFeatures)) {
|
||||
@ -507,7 +507,7 @@ class Channel(val r: ActorRef, val blockchain: ActorRef, router: ActorRef, relay
|
||||
handleCommandError(sender, new RuntimeException("cannot close when there are pending changes"))
|
||||
|
||||
case Event(CMD_CLOSE(ourScriptPubKey_opt), d: DATA_NORMAL) =>
|
||||
ourScriptPubKey_opt.getOrElse(Script.write(d.params.localParams.defaultFinalScriptPubKey)) match {
|
||||
ourScriptPubKey_opt.getOrElse(d.params.localParams.defaultFinalScriptPubKey) match {
|
||||
case finalScriptPubKey if Closing.isValidFinalScriptPubkey(finalScriptPubKey) =>
|
||||
val localShutdown = Shutdown(d.channelId, finalScriptPubKey)
|
||||
handleCommandSuccess(sender, localShutdown, d.copy(unackedShutdown = Some(localShutdown)))
|
||||
@ -526,7 +526,7 @@ class Channel(val r: ActorRef, val blockchain: ActorRef, router: ActorRef, relay
|
||||
remote ! commit
|
||||
commitments1
|
||||
} else commitments
|
||||
val shutdown = Shutdown(d.channelId, Script.write(params.localParams.defaultFinalScriptPubKey))
|
||||
val shutdown = Shutdown(d.channelId, params.localParams.defaultFinalScriptPubKey)
|
||||
remote ! shutdown
|
||||
(shutdown, commitments2)
|
||||
}) match {
|
||||
|
@ -10,7 +10,7 @@ import fr.acinq.bitcoin.{BinaryData, Satoshi}
|
||||
|
||||
trait ChannelEvent
|
||||
|
||||
case class ChannelCreated(peer: ActorRef, channel: ActorRef, params: LocalParams, remoteNodeId: PublicKey) extends ChannelEvent
|
||||
case class ChannelCreated(temporaryChannelId: Long, peer: ActorRef, channel: ActorRef, params: LocalParams, remoteNodeId: PublicKey) extends ChannelEvent
|
||||
|
||||
case class ChannelIdAssigned(channel: ActorRef, channelId: BinaryData, amount: Satoshi) extends ChannelEvent
|
||||
|
||||
|
@ -163,7 +163,7 @@ final case class LocalParams(dustLimitSatoshis: Long,
|
||||
revocationSecret: Scalar,
|
||||
paymentKey: PrivateKey,
|
||||
delayedPaymentKey: Scalar,
|
||||
defaultFinalScriptPubKey: Seq[ScriptElt],
|
||||
defaultFinalScriptPubKey: BinaryData,
|
||||
shaSeed: BinaryData,
|
||||
isFunder: Boolean,
|
||||
globalFeatures: BinaryData,
|
||||
|
@ -1,7 +1,7 @@
|
||||
package fr.acinq.eclair.channel
|
||||
|
||||
import akka.actor.{ActorContext, ActorPath, ActorSystem, Props}
|
||||
import fr.acinq.bitcoin.BinaryData
|
||||
import akka.actor.{Actor, ActorContext, ActorLogging, ActorPath, ActorRef, ActorSystem, Props, Terminated}
|
||||
import fr.acinq.bitcoin.Crypto.PublicKey
|
||||
|
||||
/**
|
||||
* Created by PM on 26/01/2016.
|
||||
@ -23,61 +23,54 @@ import fr.acinq.bitcoin.BinaryData
|
||||
* ├── client (0..m, transient)
|
||||
* └── api
|
||||
*/
|
||||
/*class Register(watcher: ActorRef, router: ActorRef, relayer: ActorRef, defaultFinalScriptPubKey: Seq[ScriptElt]) extends Actor with ActorLogging {
|
||||
|
||||
import Register._
|
||||
class Register extends Actor with ActorLogging {
|
||||
|
||||
def receive: Receive = main(0L)
|
||||
context.system.eventStream.subscribe(self, classOf[ChannelCreated])
|
||||
context.system.eventStream.subscribe(self, classOf[ChannelChangedState])
|
||||
|
||||
def main(counter: Long): Receive = {
|
||||
override def receive: Receive = main(Map())
|
||||
|
||||
case ListChannels => sender ! context.children
|
||||
def main(channels: Map[String, ActorRef]): Receive = {
|
||||
case ChannelCreated(temporaryChannelId, _, channel, _, _) =>
|
||||
context.watch(channel)
|
||||
context become main(channels + (java.lang.Long.toHexString(temporaryChannelId) -> channel))
|
||||
|
||||
case SendCommand(channelId, cmd) =>
|
||||
val s = sender
|
||||
implicit val timeout = Timeout(30 seconds)
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
context.system.actorSelection(actorPathToChannelId(channelId)).resolveOne().map(actor => {
|
||||
actor ! cmd
|
||||
actor
|
||||
})
|
||||
case ChannelChangedState(channel, _, _, _, _, d: DATA_WAIT_FOR_FUNDING_LOCKED) =>
|
||||
import d.commitments.channelId
|
||||
context.watch(channel)
|
||||
// channel id switch
|
||||
context become main(channels + (java.lang.Long.toHexString(channelId) -> channel) - java.lang.Long.toHexString(d.lastSent.temporaryChannelId))
|
||||
|
||||
case Terminated(actor) if channels.values.toSet.contains(actor) =>
|
||||
val channelId = channels.find(_._2 == actor).get._1
|
||||
context become main(channels - channelId)
|
||||
|
||||
case 'channels => sender ! channels
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
object Register {
|
||||
|
||||
/*def props(blockchain: ActorRef, router: ActorRef, relayer: ActorRef, defaultFinalScriptPubKey: Seq[ScriptElt]) = Props(classOf[Register], blockchain, router, relayer, defaultFinalScriptPubKey)
|
||||
|
||||
// @formatter:off
|
||||
case class CreateChannel(remoteNodeId: PublicKey, fundingSatoshis: Satoshi, pushMsat: MilliSatoshi)
|
||||
case class ConnectionEstablished(connection: ActorRef, createChannel_opt: Option[CreateChannel])
|
||||
case class HandshakeCompleted(transport: ActorRef, remoteNodeId: PublicKey)
|
||||
case class ListChannels()
|
||||
case class SendCommand(channelId: Long, cmd: Command)
|
||||
// @formatter:on
|
||||
*/
|
||||
/**
|
||||
* Once it reaches NORMAL state, channel creates a [[fr.acinq.eclair.channel.AliasActor]]
|
||||
* which name is counterparty_id-anchor_id
|
||||
*/
|
||||
def createAlias(nodeAddress: BinaryData, channelId: Long)(implicit context: ActorContext) =
|
||||
context.actorOf(Props(new AliasActor(context.self)), name = s"$nodeAddress-${java.lang.Long.toUnsignedString(channelId)}")
|
||||
def createAlias(nodeId: PublicKey, channelId: Long)(implicit context: ActorContext) =
|
||||
context.actorOf(Props(new AliasActor(context.self)), name = s"${nodeId.toBin}-${java.lang.Long.toHexString(channelId)}")
|
||||
|
||||
/*def actorPathToNodeAddress(system: ActorSystem, nodeAddress: BinaryData): ActorPath =
|
||||
system / "register" / "transport-handler-*" / "channel" / s"$nodeAddress-*"
|
||||
def actorPathToChannels(system: ActorSystem): ActorPath =
|
||||
system / "switchboard" / "peer-*" / "*"
|
||||
|
||||
def actorPathToNodeAddress(nodeAddress: BinaryData)(implicit context: ActorContext): ActorPath = actorPathToNodeAddress(context.system, nodeAddress)
|
||||
*/
|
||||
def actorPathToChannelId(system: ActorSystem, channelId: Long): ActorPath =
|
||||
system / "switchboard" / "peer-*" / "*" / s"*-${channelId}"
|
||||
def actorPathToChannel(system: ActorSystem, channelId: Long): ActorPath =
|
||||
system / "switchboard" / "peer-*" / "*" / s"*-${java.lang.Long.toHexString(channelId)}"
|
||||
|
||||
def actorPathToChannelId(channelId: Long)(implicit context: ActorContext): ActorPath = actorPathToChannelId(context.system, channelId)
|
||||
|
||||
/*def actorPathToChannels()(implicit context: ActorContext): ActorPath =
|
||||
context.system / "register" / "transport-handler-*" / "channel"*/
|
||||
def actorPathToChannel(channelId: Long)(implicit context: ActorContext): ActorPath = actorPathToChannel(context.system, channelId)
|
||||
|
||||
def actorPathToPeers()(implicit context: ActorContext): ActorPath =
|
||||
context.system / "switchboard" / "peer-*"
|
||||
|
||||
def actorPathToPeer(system: ActorSystem, nodeId: PublicKey): ActorPath =
|
||||
system / "switchboard" / s"peer-${nodeId.toBin}"
|
||||
|
||||
}
|
@ -33,7 +33,7 @@ class GUIUpdater(primaryStage: Stage, mainController: MainController, setup: Set
|
||||
|
||||
def main(m: Map[ActorRef, ChannelPaneController]): Receive = {
|
||||
|
||||
case ChannelCreated(peer, channel, params, theirNodeId) =>
|
||||
case ChannelCreated(_, peer, channel, params, theirNodeId) =>
|
||||
log.info(s"new channel: $channel")
|
||||
|
||||
val loader = new FXMLLoader(getClass.getResource("/gui/main/channelPane.fxml"))
|
||||
@ -68,7 +68,7 @@ class GUIUpdater(primaryStage: Stage, mainController: MainController, setup: Set
|
||||
}
|
||||
})
|
||||
|
||||
case ChannelChangedState(channel, _, _, previousState, currentState, currentData) =>
|
||||
case ChannelChangedState(channel, _, _, previousState, currentState, currentData) if m.contains(channel) =>
|
||||
val channelPane = m(channel)
|
||||
Platform.runLater(new Runnable() {
|
||||
override def run = channelPane.state.setText(currentState.toString)
|
||||
|
@ -54,7 +54,7 @@ class Handlers(setup: Setup) extends Logging {
|
||||
|
||||
def send(nodeId: String, rhash: String, amountMsat: String) = {
|
||||
logger.info(s"sending $amountMsat to $rhash @ $nodeId")
|
||||
(paymentInitiator ? CreatePayment(amountMsat.toLong, BinaryData(rhash), BinaryData(nodeId))).mapTo[String].onComplete {
|
||||
(paymentInitiator ? CreatePayment(amountMsat.toLong, rhash, PublicKey(nodeId))).mapTo[String].onComplete {
|
||||
case Success(s) =>
|
||||
val message = s"Amount (mSat): $amountMsat\nH: $rhash"
|
||||
notification("Payment Successful", message, NOTIFICATION_SUCCESS)
|
||||
|
@ -4,7 +4,7 @@ import java.net.InetSocketAddress
|
||||
|
||||
import akka.actor.{ActorRef, LoggingFSM, PoisonPill, Props, Terminated}
|
||||
import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey}
|
||||
import fr.acinq.bitcoin.{DeterministicWallet, ScriptElt}
|
||||
import fr.acinq.bitcoin.{BinaryData, DeterministicWallet, ScriptElt}
|
||||
import fr.acinq.eclair.channel._
|
||||
import fr.acinq.eclair.crypto.TransportHandler.{HandshakeCompleted, Listener}
|
||||
import fr.acinq.eclair.io.Switchboard.{NewChannel, NewConnection}
|
||||
@ -39,7 +39,7 @@ case object CONNECTED extends State
|
||||
/**
|
||||
* Created by PM on 26/08/2016.
|
||||
*/
|
||||
class Peer(remoteNodeId: PublicKey, address_opt: Option[InetSocketAddress], watcher: ActorRef, router: ActorRef, relayer: ActorRef, defaultFinalScriptPubKey: Seq[ScriptElt]) extends LoggingFSM[State, Data] {
|
||||
class Peer(remoteNodeId: PublicKey, address_opt: Option[InetSocketAddress], watcher: ActorRef, router: ActorRef, relayer: ActorRef, defaultFinalScriptPubKey: BinaryData) extends LoggingFSM[State, Data] {
|
||||
|
||||
import Peer._
|
||||
|
||||
@ -154,11 +154,11 @@ class Peer(remoteNodeId: PublicKey, address_opt: Option[InetSocketAddress], watc
|
||||
|
||||
object Peer {
|
||||
|
||||
def props(remoteNodeId: PublicKey, address_opt: Option[InetSocketAddress], watcher: ActorRef, router: ActorRef, relayer: ActorRef, defaultFinalScriptPubKey: Seq[ScriptElt]) = Props(classOf[Peer], remoteNodeId, address_opt, watcher, router, relayer, defaultFinalScriptPubKey)
|
||||
def props(remoteNodeId: PublicKey, address_opt: Option[InetSocketAddress], watcher: ActorRef, router: ActorRef, relayer: ActorRef, defaultFinalScriptPubKey: BinaryData) = Props(classOf[Peer], remoteNodeId, address_opt, watcher, router, relayer, defaultFinalScriptPubKey)
|
||||
|
||||
def generateKey(keyPath: Seq[Long]): PrivateKey = DeterministicWallet.derivePrivateKey(Globals.Node.extendedPrivateKey, keyPath).privateKey
|
||||
|
||||
def makeChannelParams(keyIndex: Long, defaultFinalScriptPubKey: Seq[ScriptElt], isFunder: Boolean): LocalParams =
|
||||
def makeChannelParams(keyIndex: Long, defaultFinalScriptPubKey: BinaryData, isFunder: Boolean): LocalParams =
|
||||
LocalParams(
|
||||
dustLimitSatoshis = 542,
|
||||
maxHtlcValueInFlightMsat = Long.MaxValue,
|
||||
|
@ -4,7 +4,7 @@ import java.net.InetSocketAddress
|
||||
|
||||
import akka.actor.{Actor, ActorLogging, ActorRef, Props, Status, Terminated}
|
||||
import fr.acinq.bitcoin.Crypto.PublicKey
|
||||
import fr.acinq.bitcoin.{MilliSatoshi, Satoshi, ScriptElt}
|
||||
import fr.acinq.bitcoin.{BinaryData, MilliSatoshi, Satoshi, ScriptElt}
|
||||
import fr.acinq.eclair.Globals
|
||||
import fr.acinq.eclair.crypto.TransportHandler.HandshakeCompleted
|
||||
|
||||
@ -12,7 +12,7 @@ import fr.acinq.eclair.crypto.TransportHandler.HandshakeCompleted
|
||||
* Ties network connections to peers.
|
||||
* Created by PM on 14/02/2017.
|
||||
*/
|
||||
class Switchboard(watcher: ActorRef, router: ActorRef, relayer: ActorRef, defaultFinalScriptPubKey: Seq[ScriptElt]) extends Actor with ActorLogging {
|
||||
class Switchboard(watcher: ActorRef, router: ActorRef, relayer: ActorRef, defaultFinalScriptPubKey: BinaryData) extends Actor with ActorLogging {
|
||||
|
||||
import Switchboard._
|
||||
|
||||
@ -26,7 +26,8 @@ class Switchboard(watcher: ActorRef, router: ActorRef, relayer: ActorRef, defaul
|
||||
case NewConnection(remoteNodeId, address, newChannel_opt) =>
|
||||
val connection = connections.get(remoteNodeId) match {
|
||||
case Some(connection) =>
|
||||
log.info(s"connection to $remoteNodeId is already in progress")
|
||||
log.info(s"already connected to nodeId=$remoteNodeId")
|
||||
sender ! s"already connected to nodeId=$remoteNodeId"
|
||||
connection
|
||||
case None =>
|
||||
log.info(s"connecting to $remoteNodeId @ $address")
|
||||
@ -38,7 +39,7 @@ class Switchboard(watcher: ActorRef, router: ActorRef, relayer: ActorRef, defaul
|
||||
case Some(peer) => peer
|
||||
case None => createPeer(remoteNodeId, Some(address))
|
||||
}
|
||||
newChannel_opt.map (peer forward _)
|
||||
newChannel_opt.foreach(peer forward _)
|
||||
context become main(peers + (remoteNodeId -> peer), connections + (remoteNodeId -> connection))
|
||||
|
||||
case Terminated(actor) if connections.values.toSet.contains(actor) =>
|
||||
@ -51,6 +52,9 @@ class Switchboard(watcher: ActorRef, router: ActorRef, relayer: ActorRef, defaul
|
||||
peer forward h
|
||||
context become main(peers + (remoteNodeId -> peer), connections)
|
||||
|
||||
case 'peers =>
|
||||
sender ! peers.keys
|
||||
|
||||
}
|
||||
|
||||
def createPeer(remoteNodeId: PublicKey, address_opt: Option[InetSocketAddress]) = context.actorOf(Peer.props(remoteNodeId, address_opt, watcher, router, relayer, defaultFinalScriptPubKey), name = s"peer-$remoteNodeId")
|
||||
@ -58,7 +62,7 @@ class Switchboard(watcher: ActorRef, router: ActorRef, relayer: ActorRef, defaul
|
||||
|
||||
object Switchboard {
|
||||
|
||||
def props(watcher: ActorRef, router: ActorRef, relayer: ActorRef, defaultFinalScriptPubKey: Seq[ScriptElt]) = Props(classOf[Switchboard], watcher, router, relayer, defaultFinalScriptPubKey)
|
||||
def props(watcher: ActorRef, router: ActorRef, relayer: ActorRef, defaultFinalScriptPubKey: BinaryData) = Props(classOf[Switchboard], watcher, router, relayer, defaultFinalScriptPubKey)
|
||||
|
||||
// @formatter:off
|
||||
case class NewChannel(fundingSatoshis: Satoshi, pushMsat: MilliSatoshi)
|
||||
|
@ -13,7 +13,7 @@ import scodec.Attempt
|
||||
|
||||
// @formatter:off
|
||||
|
||||
case class CreatePayment(amountMsat: Long, paymentHash: BinaryData, targetNodeId: BinaryData)
|
||||
case class CreatePayment(amountMsat: Long, paymentHash: BinaryData, targetNodeId: PublicKey)
|
||||
|
||||
sealed trait Data
|
||||
case object WaitingForRequest extends Data
|
||||
@ -46,7 +46,7 @@ class PaymentLifecycle(sourceNodeId: BinaryData, router: ActorRef) extends Loggi
|
||||
case Event(RouteResponse(hops), WaitingForRoute(s, c)) =>
|
||||
val firstHop = hops.head
|
||||
val cmd = buildCommand(c.amountMsat, c.paymentHash, hops, Globals.blockCount.get().toInt)
|
||||
context.actorSelection(Register.actorPathToChannelId(firstHop.lastUpdate.channelId)) ! cmd
|
||||
context.actorSelection(Register.actorPathToChannel(firstHop.lastUpdate.channelId)) ! cmd
|
||||
goto(WAITING_FOR_PAYMENT_COMPLETE) using WaitingForComplete(s, cmd)
|
||||
|
||||
case Event(f@Failure(t), WaitingForRoute(s, _)) =>
|
||||
|
@ -200,7 +200,7 @@ object Transactions {
|
||||
(htlcTimeoutTxs, htlcSuccessTxs)
|
||||
}
|
||||
|
||||
def makeClaimHtlcSuccessTx(commitTx: Transaction, localPubkey: PublicKey, remotePubkey: PublicKey, localFinalScriptPubKey: Seq[ScriptElt], htlc: UpdateAddHtlc, feeRatePerKw: Long): ClaimHtlcSuccessTx = {
|
||||
def makeClaimHtlcSuccessTx(commitTx: Transaction, localPubkey: PublicKey, remotePubkey: PublicKey, localFinalScriptPubKey: BinaryData, htlc: UpdateAddHtlc, feeRatePerKw: Long): ClaimHtlcSuccessTx = {
|
||||
val fee = weight2fee(feeRatePerKw, claimHtlcSuccessWeight)
|
||||
val redeemScript = htlcOffered(remotePubkey, localPubkey, ripemd160(htlc.paymentHash))
|
||||
val pubkeyScript = write(pay2wsh(redeemScript))
|
||||
@ -214,7 +214,7 @@ object Transactions {
|
||||
lockTime = 0))
|
||||
}
|
||||
|
||||
def makeClaimHtlcTimeoutTx(commitTx: Transaction, localPubkey: PublicKey, remotePubkey: PublicKey, localFinalScriptPubKey: Seq[ScriptElt], htlc: UpdateAddHtlc, feeRatePerKw: Long): ClaimHtlcTimeoutTx = {
|
||||
def makeClaimHtlcTimeoutTx(commitTx: Transaction, localPubkey: PublicKey, remotePubkey: PublicKey, localFinalScriptPubKey: BinaryData, htlc: UpdateAddHtlc, feeRatePerKw: Long): ClaimHtlcTimeoutTx = {
|
||||
val fee = weight2fee(feeRatePerKw, claimHtlcTimeoutWeight)
|
||||
val redeemScript = htlcReceived(remotePubkey, localPubkey, ripemd160(htlc.paymentHash), htlc.expiry)
|
||||
val pubkeyScript = write(pay2wsh(redeemScript))
|
||||
@ -228,7 +228,7 @@ object Transactions {
|
||||
lockTime = htlc.expiry))
|
||||
}
|
||||
|
||||
def makeClaimP2WPKHOutputTx(delayedOutputTx: Transaction, localPubkey: PublicKey, localFinalScriptPubKey: Seq[ScriptElt], feeRatePerKw: Long): ClaimP2WPKHOutputTx = {
|
||||
def makeClaimP2WPKHOutputTx(delayedOutputTx: Transaction, localPubkey: PublicKey, localFinalScriptPubKey: BinaryData, feeRatePerKw: Long): ClaimP2WPKHOutputTx = {
|
||||
val fee = weight2fee(feeRatePerKw, claimP2WPKHOutputWeight)
|
||||
val redeemScript = Script.pay2pkh(localPubkey)
|
||||
val pubkeyScript = write(pay2wpkh(localPubkey))
|
||||
@ -242,7 +242,7 @@ object Transactions {
|
||||
lockTime = 0))
|
||||
}
|
||||
|
||||
def makeClaimDelayedOutputTx(delayedOutputTx: Transaction, localRevocationPubkey: PublicKey, toLocalDelay: Int, localDelayedPubkey: PublicKey, localFinalScriptPubKey: Seq[ScriptElt], feeRatePerKw: Long): ClaimDelayedOutputTx = {
|
||||
def makeClaimDelayedOutputTx(delayedOutputTx: Transaction, localRevocationPubkey: PublicKey, toLocalDelay: Int, localDelayedPubkey: PublicKey, localFinalScriptPubKey: BinaryData, feeRatePerKw: Long): ClaimDelayedOutputTx = {
|
||||
val fee = weight2fee(feeRatePerKw, claimHtlcDelayedWeight)
|
||||
val redeemScript = toLocalDelayed(localRevocationPubkey, toLocalDelay, localDelayedPubkey)
|
||||
val pubkeyScript = write(pay2wsh(redeemScript))
|
||||
@ -256,7 +256,7 @@ object Transactions {
|
||||
lockTime = 0))
|
||||
}
|
||||
|
||||
def makeMainPenaltyTx(commitTx: Transaction, remoteRevocationPubkey: PublicKey, localFinalScriptPubKey: Seq[ScriptElt], toRemoteDelay: Int, remoteDelayedPubkey: PublicKey, feeRatePerKw: Long): MainPenaltyTx = {
|
||||
def makeMainPenaltyTx(commitTx: Transaction, remoteRevocationPubkey: PublicKey, localFinalScriptPubKey: BinaryData, toRemoteDelay: Int, remoteDelayedPubkey: PublicKey, feeRatePerKw: Long): MainPenaltyTx = {
|
||||
val fee = weight2fee(feeRatePerKw, mainPenaltyWeight)
|
||||
val redeemScript = toLocalDelayed(remoteRevocationPubkey, toRemoteDelay, remoteDelayedPubkey)
|
||||
val pubkeyScript = write(pay2wsh(redeemScript))
|
||||
|
@ -26,7 +26,7 @@ object TestConstants {
|
||||
revocationSecret = PrivateKey(Array.fill[Byte](32)(2), compressed = true),
|
||||
paymentKey = PrivateKey(Array.fill[Byte](32)(3), compressed = true),
|
||||
delayedPaymentKey = PrivateKey(Array.fill[Byte](32)(4), compressed = true),
|
||||
defaultFinalScriptPubKey = Script.pay2wpkh(PrivateKey(Array.fill[Byte](32)(5), compressed = true).publicKey),
|
||||
defaultFinalScriptPubKey = Script.write(Script.pay2wpkh(PrivateKey(Array.fill[Byte](32)(5), compressed = true).publicKey)),
|
||||
shaSeed = Crypto.sha256("alice-seed".getBytes()),
|
||||
isFunder = true,
|
||||
globalFeatures = "",
|
||||
@ -48,7 +48,7 @@ object TestConstants {
|
||||
revocationSecret = PrivateKey(Array.fill[Byte](32)(12), compressed = true),
|
||||
paymentKey = PrivateKey(Array.fill[Byte](32)(13), compressed = true),
|
||||
delayedPaymentKey = PrivateKey(Array.fill[Byte](32)(14), compressed = true),
|
||||
defaultFinalScriptPubKey = Script.pay2wpkh(PrivateKey(Array.fill[Byte](32)(15), compressed = true).publicKey),
|
||||
defaultFinalScriptPubKey = Script.write(Script.pay2wpkh(PrivateKey(Array.fill[Byte](32)(15), compressed = true).publicKey)),
|
||||
shaSeed = Crypto.sha256("alice-seed".getBytes()),
|
||||
isFunder = false,
|
||||
globalFeatures = "",
|
||||
|
@ -911,7 +911,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
||||
test("recv Shutdown (no pending htlcs)") { case (alice, _, alice2bob, _, alice2blockchain, _, _) =>
|
||||
within(30 seconds) {
|
||||
val sender = TestProbe()
|
||||
sender.send(alice, Shutdown(0, Script.write(Bob.channelParams.defaultFinalScriptPubKey)))
|
||||
sender.send(alice, Shutdown(0, Bob.channelParams.defaultFinalScriptPubKey))
|
||||
alice2bob.expectMsgType[Shutdown]
|
||||
alice2bob.expectMsgType[ClosingSigned]
|
||||
awaitCond(alice.stateName == NEGOTIATING)
|
||||
@ -937,7 +937,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
||||
val sender = TestProbe()
|
||||
val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice)
|
||||
// actual test begins
|
||||
sender.send(bob, Shutdown(0, Script.write(TestConstants.Alice.channelParams.defaultFinalScriptPubKey)))
|
||||
sender.send(bob, Shutdown(0, TestConstants.Alice.channelParams.defaultFinalScriptPubKey))
|
||||
bob2alice.expectMsgType[Error]
|
||||
bob2blockchain.expectMsgType[PublishAsap]
|
||||
bob2blockchain.expectMsgType[WatchConfirmed]
|
||||
@ -963,7 +963,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
||||
crossSign(alice, bob, alice2bob, bob2alice)
|
||||
|
||||
// actual test begins
|
||||
sender.send(bob, Shutdown(0, Script.write(TestConstants.Alice.channelParams.defaultFinalScriptPubKey)))
|
||||
sender.send(bob, Shutdown(0, TestConstants.Alice.channelParams.defaultFinalScriptPubKey))
|
||||
bob2alice.expectMsgType[Shutdown]
|
||||
awaitCond(bob.stateName == SHUTDOWN)
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ class InteroperabilitySpec extends FunSuite with BeforeAndAfterAll {
|
||||
}
|
||||
|
||||
def sendCommand(channelId: Long, cmd: Command): Future[String] = {
|
||||
system.actorSelection(Register.actorPathToChannelId(system, channelId)).resolveOne().map(actor => {
|
||||
system.actorSelection(Register.actorPathToChannel(system, channelId)).resolveOne().map(actor => {
|
||||
actor ! cmd
|
||||
"ok"
|
||||
})
|
||||
|
@ -64,7 +64,7 @@ class TransactionsSpec extends FunSuite {
|
||||
val localPaymentPriv = PrivateKey(BinaryData("dd" * 32), compressed = true)
|
||||
val remotePaymentPriv = PrivateKey(BinaryData("ee" * 32), compressed = true)
|
||||
val localFinalPriv = PrivateKey(BinaryData("ff" * 32), compressed = true)
|
||||
val finalPubKeyScript = Script.pay2wpkh(PrivateKey(BinaryData("ff" * 32), compressed = true).publicKey)
|
||||
val finalPubKeyScript = Script.write(Script.pay2wpkh(PrivateKey(BinaryData("ff" * 32), compressed = true).publicKey))
|
||||
val toLocalDelay = 144
|
||||
val feeRatePerKw = 1000
|
||||
|
||||
@ -134,7 +134,7 @@ class TransactionsSpec extends FunSuite {
|
||||
val localRevocationPriv = PrivateKey(BinaryData("cc" * 32) :+ 1.toByte)
|
||||
val localPaymentPriv = PrivateKey(BinaryData("dd" * 32) :+ 1.toByte)
|
||||
val remotePaymentPriv = PrivateKey(BinaryData("ee" * 32) :+ 1.toByte)
|
||||
val finalPubKeyScript = Script.pay2wpkh(PrivateKey(BinaryData("ee" * 32), true).publicKey)
|
||||
val finalPubKeyScript = Script.write(Script.pay2wpkh(PrivateKey(BinaryData("ee" * 32), true).publicKey))
|
||||
val commitInput = Funding.makeFundingInputInfo(BinaryData("12" * 32), 0, Btc(1), localFundingPriv.publicKey, remoteFundingPriv.publicKey)
|
||||
val toLocalDelay = 144
|
||||
val localDustLimit = Satoshi(542)
|
||||
|
Loading…
Reference in New Issue
Block a user