From 1754bf0933a8928a480354dc0e59644c87fa3807 Mon Sep 17 00:00:00 2001 From: pm47 Date: Fri, 16 Mar 2018 17:05:06 +0100 Subject: [PATCH 1/3] back to SNAPSHOT --- eclair-core/pom.xml | 2 +- eclair-node-gui/pom.xml | 2 +- eclair-node/pom.xml | 2 +- pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/eclair-core/pom.xml b/eclair-core/pom.xml index e176798c0..179158b5d 100644 --- a/eclair-core/pom.xml +++ b/eclair-core/pom.xml @@ -5,7 +5,7 @@ fr.acinq.eclair eclair_2.11 - 0.2-alpha11 + 0.2-SNAPSHOT eclair-core_2.11 diff --git a/eclair-node-gui/pom.xml b/eclair-node-gui/pom.xml index 12873daff..5341e019c 100644 --- a/eclair-node-gui/pom.xml +++ b/eclair-node-gui/pom.xml @@ -5,7 +5,7 @@ fr.acinq.eclair eclair_2.11 - 0.2-alpha11 + 0.2-SNAPSHOT eclair-node-gui_2.11 diff --git a/eclair-node/pom.xml b/eclair-node/pom.xml index 211883f86..0f0a70721 100644 --- a/eclair-node/pom.xml +++ b/eclair-node/pom.xml @@ -5,7 +5,7 @@ fr.acinq.eclair eclair_2.11 - 0.2-alpha11 + 0.2-SNAPSHOT eclair-node_2.11 diff --git a/pom.xml b/pom.xml index e5b93d61a..86c44fd14 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ fr.acinq.eclair eclair_2.11 - 0.2-alpha11 + 0.2-SNAPSHOT pom From 8721eebc4f56323081531cff3f4cf74dac2ff792 Mon Sep 17 00:00:00 2001 From: pm47 Date: Fri, 16 Mar 2018 17:12:44 +0100 Subject: [PATCH 2/3] removed lit from Readme.MD (they don't follow the BOLTs) --- README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 76793a79d..ebf175cc7 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ **Eclair** (french for Lightning) is a scala implementation of the Lightning Network. It can run with or without a GUI, and a JSON-RPC API is also available. -This software follows the [Lightning Network Specifications (BOLTs)](https://github.com/lightningnetwork/lightning-rfc). Other implementations include [lightning-c], [lit], and [lnd]. +This software follows the [Lightning Network Specifications (BOLTs)](https://github.com/lightningnetwork/lightning-rfc). Other implementations include [c-lightning] and [lnd]. --- @@ -179,9 +179,6 @@ docker run -ti --rm -v "/path_on_host:/data" -e "JAVA_OPTS=-Declair.printToConso - [2] [Reaching The Ground With Lightning](https://github.com/ElementsProject/lightning/raw/master/doc/deployable-lightning.pdf) by Rusty Russell - [3] [Lightning Network Explorer](https://explorer.acinq.co) - Explore testnet LN nodes you can connect to -[Amiko-Pay]: https://github.com/cornwarecjp/amiko-pay -[lightning-c]: https://github.com/ElementsProject/lightning +[c-lightning]: https://github.com/ElementsProject/lightning [lnd]: https://github.com/LightningNetwork/lnd -[lit]: https://github.com/mit-dci/lit -[Thunder]: https://github.com/blockchain/thunder From 4a91c1cdf8b6e01ae804c15086830e5e8b2edd4b Mon Sep 17 00:00:00 2001 From: Pierre-Marie Padiou Date: Mon, 19 Mar 2018 12:55:40 +0100 Subject: [PATCH 3/3] Proper clean up of private channels (#488) Private channels need specific handling because we can't rely on `WatchEventSpentBasic` event for tracking whether they are still up. --- .../scala/fr/acinq/eclair/router/Router.scala | 27 +++++++++++++---- .../eclair/payment/PaymentLifecycleSpec.scala | 30 +++++++++++++++++-- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala b/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala index 6f072a022..57336cbe9 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala @@ -140,9 +140,25 @@ class Router(nodeParams: NodeParams, watcher: ActorRef) extends FSM[State, Data] } } - case Event(LocalChannelDown(_, channelId, shortChannelId, _), d: Data) => - log.debug("removed local channel_update for channelId={} shortChannelId={}", channelId, shortChannelId) - stay using d.copy(privateChannels = d.privateChannels - shortChannelId, privateUpdates = d.privateUpdates.filterKeys(_.shortChannelId != shortChannelId)) + case Event(LocalChannelDown(_, channelId, shortChannelId, remoteNodeId), d: Data) => + // a local channel has permanently gone down + if (d.channels.contains(shortChannelId)) { + // the channel was public, we will receive (or have already received) a WatchEventSpentBasic event, that will trigger a clean up of the channel + // so let's not do anything here + stay + } else if (d.privateChannels.contains(shortChannelId)) { + // the channel was private or public-but-not-yet-announced, let's do the clean up + log.debug("removing private local channel and channel_update for channelId={} shortChannelId={}", channelId, shortChannelId) + val desc1 = ChannelDesc(shortChannelId, nodeParams.nodeId, remoteNodeId) + val desc2 = ChannelDesc(shortChannelId, remoteNodeId, nodeParams.nodeId) + // we remove the corresponding updates from the graph + removeEdge(d.graph, desc1) + removeEdge(d.graph, desc2) + // and we remove the channel and channel_update from our state + stay using d.copy(privateChannels = d.privateChannels - shortChannelId, privateUpdates = d.privateUpdates - desc1 - desc2) + } else { + stay + } case Event(GetRoutingState, d: Data) => log.info(s"getting valid announcements for $sender") @@ -233,6 +249,8 @@ class Router(nodeParams: NodeParams, watcher: ActorRef) extends FSM[State, Data] // we remove channel from awaiting map val awaiting1 = d0.awaiting - c if (success) { + // note: if the channel is graduating from private to public, the implementation (in the LocalChannelUpdate handler) guarantees that we will process a new channel_update + // right after the channel_announcement, channel_updates will be moved from private to public at that time val d1 = d0.copy( channels = d0.channels + (c.shortChannelId -> c), privateChannels = d0.privateChannels - c.shortChannelId, // we remove fake announcements that we may have made before @@ -261,8 +279,7 @@ class Router(nodeParams: NodeParams, watcher: ActorRef) extends FSM[State, Data] log.debug("received channel update for shortChannelId={} from {}", u.shortChannelId, sender) stay using handle(u, sender, d) - case Event(WatchEventSpentBasic(BITCOIN_FUNDING_EXTERNAL_CHANNEL_SPENT(shortChannelId)), d) - if d.channels.contains(shortChannelId) => + case Event(WatchEventSpentBasic(BITCOIN_FUNDING_EXTERNAL_CHANNEL_SPENT(shortChannelId)), d) if d.channels.contains(shortChannelId) => val lostChannel = d.channels(shortChannelId) log.info("funding tx of channelId={} has been spent", shortChannelId) // we need to remove nodes that aren't tied to any channels anymore diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala index 201090674..67e8cd545 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala @@ -1,9 +1,11 @@ package fr.acinq.eclair.payment import akka.actor.FSM.{CurrentState, SubscribeTransitionCallBack, Transition} +import akka.actor.Status import akka.testkit.{TestFSMRef, TestProbe} import fr.acinq.bitcoin.MilliSatoshi import fr.acinq.eclair.Globals +import fr.acinq.eclair.channel.{AddHtlcFailed, ChannelUnavailable} import fr.acinq.eclair.channel.Register.ForwardShortId import fr.acinq.eclair.crypto.Sphinx import fr.acinq.eclair.crypto.Sphinx.ErrorPacket @@ -71,7 +73,33 @@ class PaymentLifecycleSpec extends BaseRouterSpec { // we allow 2 tries, so we send a 2nd request to the router sender.expectMsg(PaymentFailed(request.paymentHash, UnreadableRemoteFailure(hops) :: UnreadableRemoteFailure(hops) :: Nil)) + } + test("payment failed (local error)") { case (router, _) => + val relayer = TestProbe() + val routerForwarder = TestProbe() + val paymentFSM = TestFSMRef(new PaymentLifecycle(a, routerForwarder.ref, relayer.ref)) + val monitor = TestProbe() + val sender = TestProbe() + + paymentFSM ! SubscribeTransitionCallBack(monitor.ref) + val CurrentState(_, WAITING_FOR_REQUEST) = monitor.expectMsgClass(classOf[CurrentState[_]]) + + val request = SendPayment(142000L, "42" * 32, d, maxAttempts = 2) + sender.send(paymentFSM, request) + awaitCond(paymentFSM.stateName == WAITING_FOR_ROUTE) + val WaitingForRoute(_, _, Nil) = paymentFSM.stateData + routerForwarder.expectMsg(RouteRequest(a, d, assistedRoutes = Nil, ignoreNodes = Set.empty, ignoreChannels = Set.empty)) + routerForwarder.forward(router) + awaitCond(paymentFSM.stateName == WAITING_FOR_PAYMENT_COMPLETE) + val WaitingForComplete(_, _, cmd1, Nil, _, _, _, hops) = paymentFSM.stateData + + relayer.expectMsg(ForwardShortId(channelId_ab, cmd1)) + sender.send(paymentFSM, Status.Failure(AddHtlcFailed("00" * 32, request.paymentHash, ChannelUnavailable("00" * 32), Local(Some(paymentFSM.underlying.self)), None))) + + // then the payment lifecycle will ask for a new route excluding the channel + routerForwarder.expectMsg(RouteRequest(a, d, assistedRoutes = Nil, ignoreNodes = Set.empty, ignoreChannels = Set(channelId_ab))) + awaitCond(paymentFSM.stateName == WAITING_FOR_ROUTE) } test("payment failed (first hop returns an UpdateFailMalformedHtlc)") { case (router, _) => @@ -99,7 +127,6 @@ class PaymentLifecycleSpec extends BaseRouterSpec { // then the payment lifecycle will ask for a new route excluding the channel routerForwarder.expectMsg(RouteRequest(a, d, assistedRoutes = Nil, ignoreNodes = Set.empty, ignoreChannels = Set(channelId_ab))) awaitCond(paymentFSM.stateName == WAITING_FOR_ROUTE) - } test("payment failed (TemporaryChannelFailure)") { case (router, _) => @@ -192,7 +219,6 @@ class PaymentLifecycleSpec extends BaseRouterSpec { assert(paymentOK.amountMsat > request.amountMsat) val PaymentSent(MilliSatoshi(request.amountMsat), feesPaid, request.paymentHash, paymentOK.paymentPreimage) = eventListener.expectMsgType[PaymentSent] assert(feesPaid.amount > 0) - } }