Skip to content

Commit

Permalink
Merge branch 'master' into payment_request_read_faster
Browse files Browse the repository at this point in the history
  • Loading branch information
sstone committed Nov 13, 2019
2 parents 5a0d191 + e5060d9 commit 28b678e
Show file tree
Hide file tree
Showing 64 changed files with 3,418 additions and 1,062 deletions.
2 changes: 1 addition & 1 deletion eclair-core/src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ eclair {
initial-random-reconnect-delay = 5 seconds // we add a random delay before the first reconnection attempt, capped by this value
max-reconnect-interval = 1 hour // max interval between two reconnection attempts, after the exponential backoff period

payment-handler = "local"
payment-request-expiry = 1 hour // default expiry for payment requests generated by this node
multi-part-payment-expiry = 60 seconds // default expiry for receiving all parts of a multi-part payment
min-funding-satoshis = 100000
max-payment-attempts = 5

Expand Down
24 changes: 13 additions & 11 deletions eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ import fr.acinq.eclair.channel._
import fr.acinq.eclair.db.{IncomingPayment, NetworkFee, OutgoingPayment, Stats}
import fr.acinq.eclair.io.Peer.{GetPeerInfo, PeerInfo}
import fr.acinq.eclair.io.{NodeURI, Peer}
import fr.acinq.eclair.payment.PaymentInitiator.SendPaymentRequest
import fr.acinq.eclair.payment.PaymentLifecycle.ReceivePayment
import fr.acinq.eclair.payment.send.PaymentInitiator.SendPaymentRequest
import fr.acinq.eclair.payment.Relayer.{GetOutgoingChannels, OutgoingChannels, UsableBalance}
import fr.acinq.eclair.payment._
import fr.acinq.eclair.payment.receive.MultiPartHandler.ReceivePayment
import fr.acinq.eclair.router.{ChannelDesc, RouteRequest, RouteResponse, Router}
import fr.acinq.eclair.wire.{ChannelAnnouncement, ChannelUpdate, NodeAddress, NodeAnnouncement}
import scodec.bits.ByteVector
Expand Down Expand Up @@ -79,7 +80,7 @@ trait Eclair {

def peersInfo()(implicit timeout: Timeout): Future[Iterable[PeerInfo]]

def receive(description: String, amount_opt: Option[MilliSatoshi], expire_opt: Option[Long], fallbackAddress_opt: Option[String], paymentPreimage_opt: Option[ByteVector32])(implicit timeout: Timeout): Future[PaymentRequest]
def receive(description: String, amount_opt: Option[MilliSatoshi], expire_opt: Option[Long], fallbackAddress_opt: Option[String], paymentPreimage_opt: Option[ByteVector32], allowMultiPart: Boolean)(implicit timeout: Timeout): Future[PaymentRequest]

def newAddress(): Future[String]

Expand All @@ -91,7 +92,7 @@ trait Eclair {

def findRoute(targetNodeId: PublicKey, amount: MilliSatoshi, assistedRoutes: Seq[Seq[PaymentRequest.ExtraHop]] = Seq.empty)(implicit timeout: Timeout): Future[RouteResponse]

def sendToRoute(externalId_opt: Option[String], route: Seq[PublicKey], amount: MilliSatoshi, paymentHash: ByteVector32, finalCltvExpiryDelta: CltvExpiryDelta)(implicit timeout: Timeout): Future[UUID]
def sendToRoute(externalId_opt: Option[String], route: Seq[PublicKey], amount: MilliSatoshi, paymentHash: ByteVector32, finalCltvExpiryDelta: CltvExpiryDelta, invoice_opt: Option[PaymentRequest] = None)(implicit timeout: Timeout): Future[UUID]

def audit(from_opt: Option[Long], to_opt: Option[Long])(implicit timeout: Timeout): Future[AuditResponse]

Expand All @@ -113,7 +114,7 @@ trait Eclair {

def getInfoResponse()(implicit timeout: Timeout): Future[GetInfoResponse]

def usableBalances()(implicit timeout: Timeout): Future[Iterable[UsableBalances]]
def usableBalances()(implicit timeout: Timeout): Future[Iterable[UsableBalance]]
}

class EclairImpl(appKit: Kit) extends Eclair {
Expand Down Expand Up @@ -187,9 +188,9 @@ class EclairImpl(appKit: Kit) extends Eclair {
case Some(pk) => (appKit.router ? 'updatesMap).mapTo[Map[ChannelDesc, ChannelUpdate]].map(_.filter(e => e._1.a == pk || e._1.b == pk).values)
}

override def receive(description: String, amount_opt: Option[MilliSatoshi], expire_opt: Option[Long], fallbackAddress_opt: Option[String], paymentPreimage_opt: Option[ByteVector32])(implicit timeout: Timeout): Future[PaymentRequest] = {
override def receive(description: String, amount_opt: Option[MilliSatoshi], expire_opt: Option[Long], fallbackAddress_opt: Option[String], paymentPreimage_opt: Option[ByteVector32], allowMultiPart: Boolean)(implicit timeout: Timeout): Future[PaymentRequest] = {
fallbackAddress_opt.map { fa => fr.acinq.eclair.addressToPublicKeyScript(fa, appKit.nodeParams.chainHash) } // if it's not a bitcoin address throws an exception
(appKit.paymentHandler ? ReceivePayment(description = description, amount_opt = amount_opt, expirySeconds_opt = expire_opt, fallbackAddress = fallbackAddress_opt, paymentPreimage = paymentPreimage_opt)).mapTo[PaymentRequest]
(appKit.paymentHandler ? ReceivePayment(amount_opt, description, expire_opt, fallbackAddress = fallbackAddress_opt, paymentPreimage = paymentPreimage_opt, allowMultiPart = allowMultiPart)).mapTo[PaymentRequest]
}

override def newAddress(): Future[String] = {
Expand All @@ -203,10 +204,10 @@ class EclairImpl(appKit: Kit) extends Eclair {
(appKit.router ? RouteRequest(appKit.nodeParams.nodeId, targetNodeId, amount, assistedRoutes)).mapTo[RouteResponse]
}

override def sendToRoute(externalId_opt: Option[String], route: Seq[PublicKey], amount: MilliSatoshi, paymentHash: ByteVector32, finalCltvExpiryDelta: CltvExpiryDelta)(implicit timeout: Timeout): Future[UUID] = {
override def sendToRoute(externalId_opt: Option[String], route: Seq[PublicKey], amount: MilliSatoshi, paymentHash: ByteVector32, finalCltvExpiryDelta: CltvExpiryDelta, invoice_opt: Option[PaymentRequest] = None)(implicit timeout: Timeout): Future[UUID] = {
externalId_opt match {
case Some(externalId) if externalId.length > externalIdMaxLength => Future.failed(new IllegalArgumentException("externalId is too long: cannot exceed 66 characters"))
case _ => (appKit.paymentInitiator ? SendPaymentRequest(amount, paymentHash, route.last, 1, finalCltvExpiryDelta, None, externalId_opt, route)).mapTo[UUID]
case _ => (appKit.paymentInitiator ? SendPaymentRequest(amount, paymentHash, route.last, 1, finalCltvExpiryDelta, invoice_opt, externalId_opt, route)).mapTo[UUID]
}
}

Expand Down Expand Up @@ -237,7 +238,7 @@ class EclairImpl(appKit: Kit) extends Eclair {

override def sentInfo(id: Either[UUID, ByteVector32])(implicit timeout: Timeout): Future[Seq[OutgoingPayment]] = Future {
id match {
case Left(uuid) => appKit.nodeParams.db.payments.getOutgoingPayment(uuid).toSeq
case Left(uuid) => appKit.nodeParams.db.payments.listOutgoingPayments(uuid)
case Right(paymentHash) => appKit.nodeParams.db.payments.listOutgoingPayments(paymentHash)
}
}
Expand Down Expand Up @@ -298,5 +299,6 @@ class EclairImpl(appKit: Kit) extends Eclair {
publicAddresses = appKit.nodeParams.publicAddresses)
)

override def usableBalances()(implicit timeout: Timeout): Future[Iterable[UsableBalances]] = (appKit.relayer ? GetUsableBalances).mapTo[Iterable[UsableBalances]]
override def usableBalances()(implicit timeout: Timeout): Future[Iterable[UsableBalance]] =
(appKit.relayer ? GetOutgoingChannels()).mapTo[OutgoingChannels].map(_.channels.map(_.toUsableBalance))
}
18 changes: 15 additions & 3 deletions eclair-core/src/main/scala/fr/acinq/eclair/Features.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,17 @@ object Features {
val CHANNEL_RANGE_QUERIES_BIT_MANDATORY = 6
val CHANNEL_RANGE_QUERIES_BIT_OPTIONAL = 7

val VARIABLE_LENGTH_ONION_MANDATORY = 8
val VARIABLE_LENGTH_ONION_OPTIONAL = 9

val CHANNEL_RANGE_QUERIES_EX_BIT_MANDATORY = 10
val CHANNEL_RANGE_QUERIES_EX_BIT_OPTIONAL = 11

val VARIABLE_LENGTH_ONION_MANDATORY = 8
val VARIABLE_LENGTH_ONION_OPTIONAL = 9
val PAYMENT_SECRET_MANDATORY = 14
val PAYMENT_SECRET_OPTIONAL = 15

val BASIC_MULTI_PART_PAYMENT_MANDATORY = 16
val BASIC_MULTI_PART_PAYMENT_OPTIONAL = 17

// Note that BitVector indexes from left to right whereas the specification indexes from right to left.
// This is why we have to reverse the bits to check if a feature is set.
Expand All @@ -57,7 +63,13 @@ object Features {
* we don't understand (even bits).
*/
def areSupported(features: BitVector): Boolean = {
val supportedMandatoryFeatures = Set[Long](OPTION_DATA_LOSS_PROTECT_MANDATORY, VARIABLE_LENGTH_ONION_MANDATORY)
val supportedMandatoryFeatures = Set[Long](
OPTION_DATA_LOSS_PROTECT_MANDATORY,
CHANNEL_RANGE_QUERIES_BIT_MANDATORY,
VARIABLE_LENGTH_ONION_MANDATORY,
CHANNEL_RANGE_QUERIES_EX_BIT_MANDATORY,
PAYMENT_SECRET_MANDATORY,
BASIC_MULTI_PART_PAYMENT_MANDATORY)
val reversed = features.reverse
for (i <- 0L until reversed.length by 2) {
if (reversed.get(i) && !supportedMandatoryFeatures.contains(i)) return false
Expand Down
10 changes: 7 additions & 3 deletions eclair-core/src/main/scala/fr/acinq/eclair/Logs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,21 @@

package fr.acinq.eclair

import java.util.UUID

import akka.event.DiagnosticLoggingAdapter
import akka.event.Logging.MDC
import fr.acinq.bitcoin.ByteVector32
import fr.acinq.bitcoin.Crypto.PublicKey

object Logs {

def mdc(remoteNodeId_opt: Option[PublicKey] = None, channelId_opt: Option[ByteVector32] = None): MDC =
def mdc(remoteNodeId_opt: Option[PublicKey] = None, channelId_opt: Option[ByteVector32] = None, parentPaymentId_opt: Option[UUID] = None, paymentId_opt: Option[UUID] = None): MDC =
Seq(
remoteNodeId_opt.map(n => "nodeId" -> s" n:$n"), // nb: we preformat MDC values so that there is no white spaces in logs
channelId_opt.map(c => "channelId" -> s" c:$c")
remoteNodeId_opt.map(n => "nodeId" -> s" n:$n"), // nb: we preformat MDC values so that there is no white spaces in logs when they are not defined
channelId_opt.map(c => "channelId" -> s" c:$c"),
parentPaymentId_opt.map(p => "parentPaymentId" -> s" p:$p"),
paymentId_opt.map(i => "paymentId" -> s" i:$i")
).flatten.toMap

def withMdc(mdc: MDC)(f: => Any)(implicit log: DiagnosticLoggingAdapter) = {
Expand Down
2 changes: 2 additions & 0 deletions eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ case class NodeParams(keyManager: KeyManager,
channelFlags: Byte,
watcherType: WatcherType,
paymentRequestExpiry: FiniteDuration,
multiPartPaymentExpiry: FiniteDuration,
minFundingSatoshis: Satoshi,
routerConf: RouterConf,
socksProxy_opt: Option[Socks5ProxyParams],
Expand Down Expand Up @@ -253,6 +254,7 @@ object NodeParams {
channelFlags = config.getInt("channel-flags").toByte,
watcherType = watcherType,
paymentRequestExpiry = FiniteDuration(config.getDuration("payment-request-expiry").getSeconds, TimeUnit.SECONDS),
multiPartPaymentExpiry = FiniteDuration(config.getDuration("multi-part-payment-expiry").getSeconds, TimeUnit.SECONDS),
minFundingSatoshis = Satoshi(config.getLong("min-funding-satoshis")),
routerConf = RouterConf(
channelExcludeDuration = FiniteDuration(config.getDuration("router.channel-exclude-duration").getSeconds, TimeUnit.SECONDS),
Expand Down
31 changes: 15 additions & 16 deletions eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ import fr.acinq.eclair.channel.Register
import fr.acinq.eclair.crypto.LocalKeyManager
import fr.acinq.eclair.db.{BackupHandler, Databases}
import fr.acinq.eclair.io.{Authenticator, Server, Switchboard}
import fr.acinq.eclair.payment._
import fr.acinq.eclair.payment.receive.PaymentHandler
import fr.acinq.eclair.payment.send.{Autoprobe, PaymentInitiator}
import fr.acinq.eclair.payment.{Auditor, Relayer}
import fr.acinq.eclair.router._
import fr.acinq.eclair.tor.TorProtocolHandler.OnionServiceVersion
import fr.acinq.eclair.tor.{Controller, TorProtocolHandler}
Expand All @@ -56,14 +58,14 @@ import scala.concurrent._
import scala.concurrent.duration._

/**
* Setup eclair from a data directory.
*
* Created by PM on 25/01/2016.
*
* @param datadir directory where eclair-core will write/read its data.
* @param overrideDefaults use this parameter to programmatically override the node configuration .
* @param seed_opt optional seed, if set eclair will use it instead of generating one and won't create a seed.dat file.
*/
* Setup eclair from a data directory.
*
* Created by PM on 25/01/2016.
*
* @param datadir directory where eclair-core will write/read its data.
* @param overrideDefaults use this parameter to programmatically override the node configuration .
* @param seed_opt optional seed, if set eclair will use it instead of generating one and won't create a seed.dat file.
*/
class Setup(datadir: File,
overrideDefaults: Config = ConfigFactory.empty(),
seed_opt: Option[ByteVector] = None,
Expand Down Expand Up @@ -275,18 +277,15 @@ class Setup(datadir: File,
nodeParams.db,
new File(chaindir, "eclair.sqlite.bak"),
if (config.hasPath("backup-notify-script")) Some(config.getString("backup-notify-script")) else None
),"backuphandler", SupervisorStrategy.Resume))
), "backuphandler", SupervisorStrategy.Resume))
audit = system.actorOf(SimpleSupervisor.props(Auditor.props(nodeParams), "auditor", SupervisorStrategy.Resume))
paymentHandler = system.actorOf(SimpleSupervisor.props(config.getString("payment-handler") match {
case "local" => LocalPaymentHandler.props(nodeParams)
case "noop" => Props[NoopPaymentHandler]
}, "payment-handler", SupervisorStrategy.Resume))
paymentHandler = system.actorOf(SimpleSupervisor.props(PaymentHandler.props(nodeParams), "payment-handler", SupervisorStrategy.Resume))
register = system.actorOf(SimpleSupervisor.props(Props(new Register), "register", SupervisorStrategy.Resume))
relayer = system.actorOf(SimpleSupervisor.props(Relayer.props(nodeParams, register, paymentHandler), "relayer", SupervisorStrategy.Resume))
authenticator = system.actorOf(SimpleSupervisor.props(Authenticator.props(nodeParams), "authenticator", SupervisorStrategy.Resume))
switchboard = system.actorOf(SimpleSupervisor.props(Switchboard.props(nodeParams, authenticator, watcher, router, relayer, wallet), "switchboard", SupervisorStrategy.Resume))
switchboard = system.actorOf(SimpleSupervisor.props(Switchboard.props(nodeParams, authenticator, watcher, router, relayer, paymentHandler, wallet), "switchboard", SupervisorStrategy.Resume))
server = system.actorOf(SimpleSupervisor.props(Server.props(nodeParams, authenticator, serverBindingAddress, Some(tcpBound)), "server", SupervisorStrategy.Restart))
paymentInitiator = system.actorOf(SimpleSupervisor.props(PaymentInitiator.props(nodeParams, router, register), "payment-initiator", SupervisorStrategy.Restart))
paymentInitiator = system.actorOf(SimpleSupervisor.props(PaymentInitiator.props(nodeParams, router, relayer, register), "payment-initiator", SupervisorStrategy.Restart))
_ = for (i <- 0 until config.getInt("autoprobe-count")) yield system.actorOf(SimpleSupervisor.props(Autoprobe.props(nodeParams, router, paymentInitiator), s"payment-autoprobe-$i", SupervisorStrategy.Restart))

kit = Kit(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
Try(Commitments.receiveFulfill(d.commitments, fulfill)) match {
case Success(Right((commitments1, origin, htlc))) =>
// we forward preimages as soon as possible to the upstream channel because it allows us to pull funds
relayer ! ForwardFulfill(fulfill, origin, htlc)
relayer ! Relayer.ForwardFulfill(fulfill, origin, htlc)
stay using d.copy(commitments = commitments1)
case Success(Left(_)) => stay
case Failure(cause) => handleLocalError(cause, d, Some(fulfill))
Expand Down Expand Up @@ -989,7 +989,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
Try(Commitments.receiveFulfill(d.commitments, fulfill)) match {
case Success(Right((commitments1, origin, htlc))) =>
// we forward preimages as soon as possible to the upstream channel because it allows us to pull funds
relayer ! ForwardFulfill(fulfill, origin, htlc)
relayer ! Relayer.ForwardFulfill(fulfill, origin, htlc)
stay using d.copy(commitments = commitments1)
case Success(Left(_)) => stay
case Failure(cause) => handleLocalError(cause, d, Some(fulfill))
Expand Down Expand Up @@ -1105,7 +1105,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
cancelTimer(RevocationTimeout.toString)
log.debug(s"received a new rev, spec:\n${Commitments.specs2String(commitments1)}")
forwards.foreach {
case forwardAdd: ForwardAdd =>
case forwardAdd: Relayer.ForwardAdd =>
// BOLT 2: A sending node SHOULD fail to route any HTLC added after it sent shutdown.
log.debug(s"closing in progress: failing ${forwardAdd.add}")
self ! CMD_FAIL_HTLC(forwardAdd.add.id, Right(PermanentChannelFailure), commit = true)
Expand Down Expand Up @@ -1268,7 +1268,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
d.commitments.originChannels.get(fulfill.id) match {
case Some(origin) =>
log.info(s"fulfilling htlc #${fulfill.id} paymentHash=${sha256(fulfill.paymentPreimage)} origin=$origin")
relayer ! ForwardFulfill(fulfill, origin, htlc)
relayer ! Relayer.ForwardFulfill(fulfill, origin, htlc)
case None =>
// if we don't have the origin, it means that we already have forwarded the fulfill so that's not a big deal.
// this can happen if they send a signature containing the fulfill, then fail the channel before we have time to sign it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ object Commitments {
(commitments1, revocation)
}

def receiveRevocation(commitments: Commitments, revocation: RevokeAndAck): (Commitments, Seq[ForwardMessage]) = {
def receiveRevocation(commitments: Commitments, revocation: RevokeAndAck): (Commitments, Seq[Relayer.ForwardMessage]) = {
import commitments._
// we receive a revocation because we just sent them a sig for their next commit tx
remoteNextCommitInfo match {
Expand All @@ -535,17 +535,17 @@ object Commitments {
val forwards = commitments.remoteChanges.signed collect {
// we forward adds downstream only when they have been committed by both sides
// it always happen when we receive a revocation, because they send the add, then they sign it, then we sign it
case add: UpdateAddHtlc => ForwardAdd(add)
case add: UpdateAddHtlc => Relayer.ForwardAdd(add)
// same for fails: we need to make sure that they are in neither commitment before propagating the fail upstream
case fail: UpdateFailHtlc =>
val origin = commitments.originChannels(fail.id)
val add = commitments.remoteCommit.spec.htlcs.find(p => p.direction == IN && p.add.id == fail.id).map(_.add).get
ForwardFail(fail, origin, add)
Relayer.ForwardFail(fail, origin, add)
// same as above
case fail: UpdateFailMalformedHtlc =>
val origin = commitments.originChannels(fail.id)
val add = commitments.remoteCommit.spec.htlcs.find(p => p.direction == IN && p.add.id == fail.id).map(_.add).get
ForwardFailMalformed(fail, origin, add)
Relayer.ForwardFailMalformed(fail, origin, add)
}
// the outgoing following htlcs have been completed (fulfilled or failed) when we received this revocation
// they have been removed from both local and remote commitment
Expand Down
Loading

0 comments on commit 28b678e

Please sign in to comment.