1
0
mirror of https://github.com/ACINQ/eclair.git synced 2024-11-20 02:27:32 +01:00

updated api

This commit is contained in:
pm47 2017-02-23 18:46:19 +01:00
parent 3d08c7e391
commit c9d6a20954
17 changed files with 153 additions and 105 deletions

View File

@ -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)

View File

@ -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())
}
))

View File

@ -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"))
}

View File

@ -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 {

View File

@ -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

View File

@ -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,

View File

@ -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}"
}

View File

@ -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)

View File

@ -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)

View File

@ -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,

View File

@ -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)

View File

@ -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, _)) =>

View File

@ -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))

View File

@ -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 = "",

View File

@ -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)
}

View File

@ -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"
})

View File

@ -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)