1
0
mirror of https://github.com/ACINQ/eclair.git synced 2024-11-19 01:43:22 +01:00

Separate code into modules (#91) (closes #88)

This commit is contained in:
Pierre-Marie Padiou 2017-06-06 18:37:34 +02:00 committed by GitHub
parent 7f747d55fd
commit d86dd72d78
182 changed files with 714 additions and 357 deletions

0
eclair-node/eclair-cli → eclair-core/eclair-cli Executable file → Normal file
View File

198
eclair-core/pom.xml Normal file
View File

@ -0,0 +1,198 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>fr.acinq.eclair</groupId>
<artifactId>eclair_2.11</artifactId>
<version>0.2-SNAPSHOT</version>
</parent>
<artifactId>eclair-core_2.11</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<build>
<plugins>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>revision</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.googlecode.maven-download-plugin</groupId>
<artifactId>download-maven-plugin</artifactId>
<version>1.3.0</version>
<executions>
<execution>
<id>download-bitcoind</id>
<phase>generate-test-resources</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>${bitcoind.url}</url>
<unpack>true</unpack>
<outputDirectory>${project.build.directory}</outputDirectory>
<md5>${bitcoind.md5}</md5>
<sha1>${bitcoind.sha1}</sha1>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
<manifestEntries>
<!-- we hide the git commit in the Specification-Version standard field-->
<Specification-Version>${git.commit.id}</Specification-Version>
<Url>${project.parent.url}</Url>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>default</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<bitcoind.url>https://bitcoin.org/bin/bitcoin-core-0.14.0/bitcoin-0.14.0-x86_64-linux-gnu.tar.gz
</bitcoind.url>
<bitcoind.md5>c811c157d4d618f7d7f4b9f24834551c</bitcoind.md5>
<bitcoind.sha1>3ab7e537bd00bf35e6a78fca108d0d886f8289c1</bitcoind.sha1>
</properties>
</profile>
<profile>
<id>Windows</id>
<activation>
<os>
<family>Windows</family>
</os>
</activation>
<properties>
<bitcoind.url>https://bitcoin.org/bin/bitcoin-core-0.14.0/bitcoin-0.14.0-win64.zip</bitcoind.url>
<bitcoind.md5>e84bc3a81ad3d1776299419eb7a04935</bitcoind.md5>
<bitcoind.sha1>d2e64fcabf6f85d56d64a52c76e007b6defc32ef</bitcoind.sha1>
</properties>
</profile>
</profiles>
<dependencies>
<!-- AKKA -->
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_${scala.version.short}</artifactId>
<version>${akka.version}</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-slf4j_${scala.version.short}</artifactId>
<version>${akka.version}</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-http-core_${scala.version.short}</artifactId>
<version>10.0.7</version>
</dependency>
<!-- JSON -->
<dependency>
<groupId>org.json4s</groupId>
<artifactId>json4s-jackson_${scala.version.short}</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>de.heikoseeberger</groupId>
<artifactId>akka-http-json4s_${scala.version.short}</artifactId>
<version>1.16.1</version>
</dependency>
<!-- BITCOIN -->
<dependency>
<groupId>fr.acinq</groupId>
<artifactId>bitcoin-lib_${scala.version.short}</artifactId>
<version>${bitcoinlib.version}</version>
<exclusions>
<exclusion>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.zeromq</groupId>
<artifactId>jeromq</artifactId>
<version>0.4.0</version>
</dependency>
<!-- SERIALIZATION -->
<dependency>
<groupId>org.scodec</groupId>
<artifactId>scodec-core_${scala.version.short}</artifactId>
<version>1.10.3</version>
</dependency>
<!-- LOGGING -->
<dependency>
<groupId>org.clapper</groupId>
<artifactId>grizzled-slf4j_${scala.version.short}</artifactId>
<version>1.3.1</version>
</dependency>
<!-- OTHER -->
<dependency>
<groupId>org.jgrapht</groupId>
<artifactId>jgrapht-core</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>org.jgrapht</groupId>
<artifactId>jgrapht-ext</artifactId>
<version>1.0.1</version>
<exclusions>
<exclusion>
<groupId>org.tinyjee.jgraphx</groupId>
<artifactId>jgraphx</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<!-- This is to get rid of '[WARNING] warning: Class javax.annotation.Nonnull not found - continuing with a stub.' compile errors -->
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>3.0.2</version>
</dependency>
<!-- TESTS -->
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-testkit_${scala.version.short}</artifactId>
<version>${akka.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,73 @@
eclair {
server {
public-ip = "127.0.0.1"
binding-ip = "0.0.0.0"
port = 9735
}
api {
binding-ip = "127.0.0.1"
port = 8080
}
bitcoind {
host = "localhost"
rpcport = 18332
rpcuser = "foo"
rpcpassword = "bar"
zmq = "tcp://127.0.0.1:29000"
}
node-alias = "eclair"
node-color = "49daaa"
global-features = ""
local-features = "03" // channels_public and initial_routing_sync
dust-limit-satoshis = 542
default-feerate-perkw = 10000 # corresponds to bitcoind's default value of feerate-perkB=20000 for a standard commit tx
max-htlc-value-in-flight-msat = 100000000000 // 1 BTC ~= unlimited
htlc-minimum-msat = 1000000
max-accepted-htlcs = 30
reserve-to-funding-ratio = 0.01 // recommended by BOLT #2
max-reserve-to-funding-ratio = 0.05 // channel reserve can't be more than 5% of the funding amount (recommended: 1%)
delay-blocks = 144
mindepth-blocks = 2
expiry-delta-blocks = 144
fee-base-msat = 546000
fee-proportional-millionth = 10
// maximum local vs remote feerate mismatch; 1.0 means 100%
// actual check is abs((local feerate - remote fee rate) / (local fee rate + remote fee rate)/2) > fee rate mismatch
max-feerate-mismatch = 1.5
// funder will send an UpdateFee message if the difference between current commitment fee and actual current network fee is greater
// than this ratio.
update-fee_min-diff-ratio = 0.1
router-broadcast-interval = 10 seconds // this should be 60 seconds on mainnet
router-validate-interval = 2 seconds // this should be high enough to have a decent level of parallelism
ping-interval = 30 seconds
auto-reconnect = true
payment-handler = "local"
}
akka {
loggers = ["akka.event.slf4j.Slf4jLogger"]
loglevel = "DEBUG"
actor {
debug {
# enable DEBUG logging of all LoggingFSMs for events, transitions and timers
fsm = on
}
}
http {
host-connection-pool {
max-open-requests = 64
}
}
}

View File

@ -55,7 +55,7 @@ object NodeParams {
* Order of precedence for the configuration parameters:
* 1) Java environment variables (-D...)
* 2) Configuration file eclair.conf
* 3) default values in application.conf
* 3) default values in reference.conf
*/
def loadConfiguration(datadir: File) =
ConfigFactory.parseProperties(System.getProperties)

View File

@ -0,0 +1,139 @@
package fr.acinq.eclair
import java.io.File
import java.net.InetSocketAddress
import akka.actor.{ActorRef, ActorSystem, Props, SupervisorStrategy}
import akka.http.scaladsl.Http
import akka.stream.ActorMaterializer
import akka.util.Timeout
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.rpc.BitcoinJsonRPCClient
import fr.acinq.eclair.blockchain.zmq.ZMQActor
import fr.acinq.eclair.blockchain.{ExtendedBitcoinClient, PeerWatcher}
import fr.acinq.eclair.channel.Register
import fr.acinq.eclair.io.{Server, Switchboard}
import fr.acinq.eclair.payment._
import fr.acinq.eclair.router._
import fr.acinq.eclair.wire.{ChannelAnnouncement, ChannelUpdate, NodeAnnouncement}
import grizzled.slf4j.Logging
import org.json4s.JsonAST.JString
import scala.compat.Platform
import scala.concurrent.duration._
import scala.concurrent.{Await, ExecutionContext, Promise}
import scala.util.Try
/**
* Created by PM on 25/01/2016.
*/
class Setup(datadir: String, actorSystemName: String = "default") extends Logging {
logger.info(s"hello!")
logger.info(s"version=${getClass.getPackage.getImplementationVersion} commit=${getClass.getPackage.getSpecificationVersion}")
val config = NodeParams.loadConfiguration(new File(datadir))
logger.info(s"initializing secure random generator")
// this will force the secure random instance to initialize itself right now, making sure it doesn't hang later (see comment in package.scala)
secureRandom.nextInt()
implicit lazy val system = ActorSystem(actorSystemName)
implicit val materializer = ActorMaterializer()
implicit val timeout = Timeout(30 seconds)
val bitcoinClient = new ExtendedBitcoinClient(new BitcoinJsonRPCClient(
user = config.getString("bitcoind.rpcuser"),
password = config.getString("bitcoind.rpcpassword"),
host = config.getString("bitcoind.host"),
port = config.getInt("bitcoind.rpcport")))
implicit val formats = org.json4s.DefaultFormats
implicit val ec = ExecutionContext.Implicits.global
val future = for {
json <- bitcoinClient.client.invoke("getblockchaininfo")
chain = (json \ "chain").extract[String]
blockCount = (json \ "blocks").extract[Long]
progress = (json \ "verificationprogress").extract[Double]
chainHash <- bitcoinClient.client.invoke("getblockhash", 0).map(_.extract[String])
} yield (chain, blockCount, progress, chainHash)
val (chain, blockCount, progress, chainHash) = Try(Await.result(future, 10 seconds)).recover { case _ => throw BitcoinRPCConnectionException }.get
logger.info(s"using chain=$chain chainHash=$chainHash")
chain match {
case "test" | "regtest" => ()
case _ => throw new RuntimeException("only regtest and testnet are supported for now")
}
val nodeParams = NodeParams.makeNodeParams(new File(datadir), config, chainHash)
logger.info(s"nodeid=${nodeParams.privateKey.publicKey.toBin} alias=${nodeParams.alias}")
assert(progress > 0.99, "bitcoind should be synchronized")
Globals.blockCount.set(blockCount)
val defaultFeeratePerKw = config.getLong("default-feerate-perkw")
val feeratePerKw = if (chain == "regtest") defaultFeeratePerKw else {
val feeratePerKB = Await.result(bitcoinClient.estimateSmartFee(nodeParams.smartfeeNBlocks), 10 seconds)
if (feeratePerKB < 0) defaultFeeratePerKw else feerateKB2Kw(feeratePerKB)
}
logger.info(s"initial feeratePerKw=$feeratePerKw")
Globals.feeratePerKw.set(feeratePerKw)
val bitcoinVersion = Await.result(bitcoinClient.client.invoke("getinfo").map(json => (json \ "version").extract[String]), 10 seconds)
// we use it as final payment address, so that funds are moved to the bitcoind wallet upon channel termination
val JString(finalAddress) = Await.result(bitcoinClient.client.invoke("getnewaddress"), 10 seconds)
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 = Script.write(OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Base58Check.decode(finalAddress)._2) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil)
val zmqConnected = Promise[Boolean]()
val zmq = system.actorOf(SimpleSupervisor.props(Props(new ZMQActor(config.getString("bitcoind.zmq"), Some(zmqConnected))), "zmq", SupervisorStrategy.Restart))
val watcher = system.actorOf(SimpleSupervisor.props(PeerWatcher.props(nodeParams, bitcoinClient), "watcher", SupervisorStrategy.Resume))
val paymentHandler = system.actorOf(SimpleSupervisor.props(config.getString("payment-handler") match {
case "local" => LocalPaymentHandler.props(nodeParams)
case "noop" => Props[NoopPaymentHandler]
}, "payment-handler", SupervisorStrategy.Resume))
val register = system.actorOf(SimpleSupervisor.props(Props(new Register), "register", SupervisorStrategy.Resume))
val relayer = system.actorOf(SimpleSupervisor.props(Relayer.props(nodeParams.privateKey, paymentHandler), "relayer", SupervisorStrategy.Resume))
val router = system.actorOf(SimpleSupervisor.props(Router.props(nodeParams, watcher), "router", SupervisorStrategy.Resume))
val switchboard = system.actorOf(SimpleSupervisor.props(Switchboard.props(nodeParams, watcher, router, relayer, finalScriptPubKey), "switchboard", SupervisorStrategy.Resume))
val paymentInitiator = system.actorOf(SimpleSupervisor.props(PaymentInitiator.props(nodeParams.privateKey.publicKey, router, register), "payment-initiator", SupervisorStrategy.Restart))
val tcpBound = Promise[Unit]()
val server = system.actorOf(SimpleSupervisor.props(Server.props(nodeParams, switchboard, new InetSocketAddress(config.getString("server.binding-ip"), config.getInt("server.port")), Some(tcpBound)), "server", SupervisorStrategy.Restart))
val _setup = this
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
}
val httpBound = Http().bindAndHandle(api.route, config.getString("api.binding-ip"), config.getInt("api.port"))
Try(Await.result(zmqConnected.future, 5 seconds)).recover { case _ => throw BitcoinZMQConnectionTimeoutException }.get
Try(Await.result(tcpBound.future, 5 seconds)).recover { case _ => throw new TCPBindException(config.getInt("server.port")) }.get
Try(Await.result(httpBound, 5 seconds)).recover { case _ => throw new TCPBindException(config.getInt("api.port")) }.get
val tasks = new Thread(new Runnable() {
override def run(): Unit = {
nodeParams.peersDb.values.foreach(rec => switchboard ! rec)
nodeParams.channelsDb.values.foreach(rec => switchboard ! rec)
nodeParams.announcementsDb.values.collect { case ann: ChannelAnnouncement => router ! ann }
nodeParams.announcementsDb.values.collect { case ann: NodeAnnouncement => router ! ann }
nodeParams.announcementsDb.values.collect { case ann: ChannelUpdate => router ! ann }
if (nodeParams.channelsDb.values.size > 0) {
val nodeAnn = Announcements.makeNodeAnnouncement(nodeParams.privateKey, nodeParams.alias, nodeParams.color, nodeParams.address :: Nil, Platform.currentTime / 1000)
router ! nodeAnn
}
}
})
def boostrap: Unit = tasks.start()
}
case class TCPBindException(port: Int) extends RuntimeException
case object BitcoinZMQConnectionTimeoutException extends RuntimeException("could not connect to bitcoind using zeromq")
case object BitcoinRPCConnectionException extends RuntimeException("could not connect to bitcoind using json-rpc")

View File

@ -47,7 +47,7 @@ trait Service extends Logging {
implicit val timeout = Timeout(30 seconds)
implicit val shouldWritePretty: ShouldWritePretty = ShouldWritePretty.True
import Json4sSupport.{json4sMarshaller, json4sUnmarshaller}
import Json4sSupport.{marshaller, unmarshaller}
def switchboard: ActorRef

View File

@ -186,7 +186,7 @@ class PeerWatcher(nodeParams: NodeParams, client: ExtendedBitcoinClient)(implici
import akka.pattern.after
import scala.concurrent.duration._
after(3 seconds, context.system.scheduler)(Future.successful()).map(x => publish(tx, isRetry = true))
after(3 seconds, context.system.scheduler)(Future.successful(Unit)).map(x => publish(tx, isRetry = true))
case t: Throwable => log.error(s"cannot publish tx: reason=${t.getMessage} txid=${tx.txid} tx=${BinaryData(Transaction.write(tx))}")
}
}

View File

@ -25,7 +25,7 @@ class Server(nodeParams: NodeParams, switchboard: ActorRef, address: InetSocketA
def receive() = {
case Bound(localAddress) =>
bound.map(_.success())
bound.map(_.success(Unit))
log.info(s"bound on $localAddress")
case CommandFailed(_: Bind) =>

View File

@ -72,7 +72,7 @@ object LightningMessageCodecs {
def rgb: Codec[(Byte, Byte, Byte)] = bytes(3).xmap(buf => (buf(0), buf(1), buf(2)), t => ByteVector(t._1, t._2, t._3))
def zeropaddedstring(size: Int): Codec[String] = fixedSizeBytes(32, utf8).xmap(s => s.takeWhile(_ != '\0'), s => s)
def zeropaddedstring(size: Int): Codec[String] = fixedSizeBytes(32, utf8).xmap(s => s.takeWhile(_ != '\u0000'), s => s)
def der2wire(signature: BinaryData): BinaryData = {
require(Crypto.isDERSignature(signature), s"invalid DER signature $signature")

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" debug="false">
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<target>System.out</target>
<encoder>
<pattern>%date{HH:mm:ss.SSS} %highlight(%-5level) %X{akkaSource} - %msg%ex{12}%n</pattern>
</encoder>
</appender>
<!--appender name="CONSOLEWARN" class="ch.qos.logback.core.ConsoleAppender">
<target>System.out</target>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>
<encoder>
<pattern>%-5level %X{akkaSource} - %msg%ex{12}%n</pattern>
</encoder>
</appender-->
<!--appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>eclair.log</file>
<append>false</append>
<encoder>
<pattern>%-5level %X{akkaSource} - %msg%ex{12}%n</pattern>
</encoder>
</appender-->
<logger name="fr.acinq.eclair.Pipe" level="DEBUG" />
<logger name="fr.acinq.eclair.crypto.TransportHandler" level="DEBUG" />
<root level="INFO">
<!--appender-ref ref="FILE"/>
<appender-ref ref="CONSOLEWARN"/-->
<appender-ref ref="CONSOLE"/>
</root>
</configuration>

Some files were not shown because too many files have changed in this diff Show More