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

Correctly set new channel balance (#1431)

When we announce a new public channel, make sure we don't override the
balance information with None.

Clean up IntegrationSpec warnings.

Fix PaymentLifecycle test: this test was broken by the recent changes in
BaseRouterSpec.
This commit is contained in:
Bastien Teinturier 2020-05-26 16:04:10 +02:00 committed by GitHub
parent e0320da383
commit 2e79ccaf3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 50 additions and 42 deletions

View File

@ -125,7 +125,9 @@ object Validation {
val nodeAnn = Announcements.makeNodeAnnouncement(nodeParams.privateKey, nodeParams.alias, nodeParams.color, nodeParams.publicAddresses, nodeParams.features) val nodeAnn = Announcements.makeNodeAnnouncement(nodeParams.privateKey, nodeParams.alias, nodeParams.color, nodeParams.publicAddresses, nodeParams.features)
ctx.self ! nodeAnn ctx.self ! nodeAnn
} }
Some(PublicChannel(c, tx.txid, capacity, None, None, None)) // public channels that haven't yet been announced are considered as private channels
val channelMeta_opt = d0.privateChannels.get(c.shortChannelId).map(_.meta)
Some(PublicChannel(c, tx.txid, capacity, None, None, channelMeta_opt))
} }
case ValidateResult(c, Right((tx, fundingTxStatus: UtxoStatus.Spent))) => case ValidateResult(c, Right((tx, fundingTxStatus: UtxoStatus.Spent))) =>
if (fundingTxStatus.spendingTxConfirmed) { if (fundingTxStatus.spendingTxConfirmed) {

View File

@ -50,7 +50,7 @@ import fr.acinq.eclair.payment.send.PaymentLifecycle.{State => _}
import fr.acinq.eclair.router.Graph.WeightRatios import fr.acinq.eclair.router.Graph.WeightRatios
import fr.acinq.eclair.router.RouteCalculation.ROUTE_MAX_LENGTH import fr.acinq.eclair.router.RouteCalculation.ROUTE_MAX_LENGTH
import fr.acinq.eclair.router.Router.{GossipDecision, PublicChannel, RouteParams, NORMAL => _, State => _} import fr.acinq.eclair.router.Router.{GossipDecision, PublicChannel, RouteParams, NORMAL => _, State => _}
import fr.acinq.eclair.router.{Announcements, AnnouncementsBatchValidationSpec} import fr.acinq.eclair.router.{Announcements, AnnouncementsBatchValidationSpec, Router}
import fr.acinq.eclair.transactions.Transactions import fr.acinq.eclair.transactions.Transactions
import fr.acinq.eclair.transactions.Transactions.{HtlcSuccessTx, HtlcTimeoutTx} import fr.acinq.eclair.transactions.Transactions.{HtlcSuccessTx, HtlcTimeoutTx}
import fr.acinq.eclair.wire._ import fr.acinq.eclair.wire._
@ -62,7 +62,6 @@ import org.scalatest.BeforeAndAfterAll
import org.scalatest.funsuite.AnyFunSuiteLike import org.scalatest.funsuite.AnyFunSuiteLike
import scodec.bits.ByteVector import scodec.bits.ByteVector
import scala.compat.Platform
import scala.concurrent.Await import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._ import scala.concurrent.duration._
@ -122,11 +121,11 @@ class IntegrationSpec extends TestKitBaseClass with BitcoindService with AnyFunS
implicit val formats = DefaultFormats implicit val formats = DefaultFormats
override def beforeAll(): Unit = { override def beforeAll: Unit = {
startBitcoind() startBitcoind()
} }
override def afterAll(): Unit = { override def afterAll: Unit = {
// gracefully stopping bitcoin will make it store its state cleanly to disk, which is good for later debugging // gracefully stopping bitcoin will make it store its state cleanly to disk, which is good for later debugging
logger.info(s"stopping bitcoind") logger.info(s"stopping bitcoind")
stopBitcoind() stopBitcoind()
@ -157,7 +156,7 @@ class IntegrationSpec extends TestKitBaseClass with BitcoindService with AnyFunS
nodes = nodes + (name -> kit) nodes = nodes + (name -> kit)
} }
def javaProps(props: Seq[(String, String)]) = { def javaProps(props: Seq[(String, String)]): Properties = {
val properties = new Properties() val properties = new Properties()
props.foreach(p => properties.setProperty(p._1, p._2)) props.foreach(p => properties.setProperty(p._1, p._2))
properties properties
@ -231,7 +230,7 @@ class IntegrationSpec extends TestKitBaseClass with BitcoindService with AnyFunS
awaitCond({ awaitCond({
val watches = nodes.values.foldLeft(Set.empty[Watch]) { val watches = nodes.values.foldLeft(Set.empty[Watch]) {
case (watches, setup) => case (watches, setup) =>
sender.send(setup.watcher, 'watches) sender.send(setup.watcher, Symbol("watches"))
watches ++ sender.expectMsgType[Set[Watch]] watches ++ sender.expectMsgType[Set[Watch]]
} }
watches.count(_.isInstanceOf[WatchConfirmed]) == channelEndpointsCount watches.count(_.isInstanceOf[WatchConfirmed]) == channelEndpointsCount
@ -253,15 +252,15 @@ class IntegrationSpec extends TestKitBaseClass with BitcoindService with AnyFunS
subset.foreach { subset.foreach {
case (_, setup) => case (_, setup) =>
awaitCond({ awaitCond({
sender.send(setup.router, 'nodes) sender.send(setup.router, Symbol("nodes"))
sender.expectMsgType[Iterable[NodeAnnouncement]](20 seconds).size == nodes sender.expectMsgType[Iterable[NodeAnnouncement]](20 seconds).size == nodes
}, max = 60 seconds, interval = 1 second) }, max = 60 seconds, interval = 1 second)
awaitCond({ awaitCond({
sender.send(setup.router, 'channels) sender.send(setup.router, Symbol("channels"))
sender.expectMsgType[Iterable[ChannelAnnouncement]](20 seconds).size == channels sender.expectMsgType[Iterable[ChannelAnnouncement]](20 seconds).size == channels
}, max = 60 seconds, interval = 1 second) }, max = 60 seconds, interval = 1 second)
awaitCond({ awaitCond({
sender.send(setup.router, 'updates) sender.send(setup.router, Symbol("updates"))
sender.expectMsgType[Iterable[ChannelUpdate]](20 seconds).size == updates sender.expectMsgType[Iterable[ChannelUpdate]](20 seconds).size == updates
}, max = 60 seconds, interval = 1 second) }, max = 60 seconds, interval = 1 second)
} }
@ -277,6 +276,17 @@ class IntegrationSpec extends TestKitBaseClass with BitcoindService with AnyFunS
awaitAnnouncements(nodes.filterKeys(key => !List("A", "B").contains(key)).toMap, 10, 12, 24) awaitAnnouncements(nodes.filterKeys(key => !List("A", "B").contains(key)).toMap, 10, 12, 24)
} }
test("wait for channels balance") {
// Channels balance should now be available in the router
val sender = TestProbe()
val nodeId = nodes("C").nodeParams.nodeId
sender.send(nodes("C").router, Router.GetRoutingState)
val routingState = sender.expectMsgType[Router.RoutingState]
val publicChannels = routingState.channels.filter(pc => Set(pc.ann.nodeId1, pc.ann.nodeId2).contains(nodeId))
assert(publicChannels.nonEmpty)
publicChannels.foreach(pc => assert(pc.meta_opt.map(m => m.balance1 > 0.msat || m.balance2 > 0.msat) === Some(true), pc))
}
test("open a wumbo channel and wait for longer than the default min_depth") { test("open a wumbo channel and wait for longer than the default min_depth") {
// we open a 5BTC channel and check that we scale `min_depth` up to 13 confirmations // we open a 5BTC channel and check that we scale `min_depth` up to 13 confirmations
val funder = nodes("C") val funder = nodes("C")
@ -295,7 +305,7 @@ class IntegrationSpec extends TestKitBaseClass with BitcoindService with AnyFunS
generateBlocks(bitcoincli, 2) generateBlocks(bitcoincli, 2)
// get the channelId // get the channelId
sender.send(fundee.register, 'channels) sender.send(fundee.register, Symbol("channels"))
val Some((_, fundeeChannel)) = sender.expectMsgType[Map[ByteVector32, ActorRef]].find(_._1 == tempChannelId) val Some((_, fundeeChannel)) = sender.expectMsgType[Map[ByteVector32, ActorRef]].find(_._1 == tempChannelId)
sender.send(fundeeChannel, CMD_GETSTATEDATA) sender.send(fundeeChannel, CMD_GETSTATEDATA)
val channelId = sender.expectMsgType[HasCommitments].channelId val channelId = sender.expectMsgType[HasCommitments].channelId
@ -374,7 +384,7 @@ class IntegrationSpec extends TestKitBaseClass with BitcoindService with AnyFunS
val sender = TestProbe() val sender = TestProbe()
// to simulate this, we will update B's relay params // to simulate this, we will update B's relay params
// first we find out the short channel id for channel B-C // first we find out the short channel id for channel B-C
sender.send(nodes("B").router, 'channels) sender.send(nodes("B").router, Symbol("channels"))
val shortIdBC = sender.expectMsgType[Iterable[ChannelAnnouncement]].find(c => Set(c.nodeId1, c.nodeId2) == Set(nodes("B").nodeParams.nodeId, nodes("C").nodeParams.nodeId)).get.shortChannelId val shortIdBC = sender.expectMsgType[Iterable[ChannelAnnouncement]].find(c => Set(c.nodeId1, c.nodeId2) == Set(nodes("B").nodeParams.nodeId, nodes("C").nodeParams.nodeId)).get.shortChannelId
// we also need the full commitment // we also need the full commitment
sender.send(nodes("B").register, ForwardShortId(shortIdBC, CMD_GETINFO)) sender.send(nodes("B").register, ForwardShortId(shortIdBC, CMD_GETINFO))
@ -399,7 +409,7 @@ class IntegrationSpec extends TestKitBaseClass with BitcoindService with AnyFunS
awaitCond({ awaitCond({
// in the meantime, the router will have updated its state // in the meantime, the router will have updated its state
sender.send(nodes("A").router, 'channelsMap) sender.send(nodes("A").router, Symbol("channelsMap"))
// we then put everything back like before by asking B to refresh its channel update (this will override the one we created) // we then put everything back like before by asking B to refresh its channel update (this will override the one we created)
val u_opt = updateFor(nodes("B").nodeParams.nodeId, sender.expectMsgType[Map[ShortChannelId, PublicChannel]](10 seconds).apply(channelUpdateBC.shortChannelId)) val u_opt = updateFor(nodes("B").nodeParams.nodeId, sender.expectMsgType[Map[ShortChannelId, PublicChannel]](10 seconds).apply(channelUpdateBC.shortChannelId))
u_opt.contains(channelUpdateBC) u_opt.contains(channelUpdateBC)
@ -415,7 +425,7 @@ class IntegrationSpec extends TestKitBaseClass with BitcoindService with AnyFunS
assert(channelUpdateBC_new.timestamp > channelUpdateBC.timestamp) assert(channelUpdateBC_new.timestamp > channelUpdateBC.timestamp)
assert(channelUpdateBC_new.cltvExpiryDelta == nodes("B").nodeParams.expiryDeltaBlocks) assert(channelUpdateBC_new.cltvExpiryDelta == nodes("B").nodeParams.expiryDeltaBlocks)
awaitCond({ awaitCond({
sender.send(nodes("A").router, 'channelsMap) sender.send(nodes("A").router, Symbol("channelsMap"))
val u = updateFor(nodes("B").nodeParams.nodeId, sender.expectMsgType[Map[ShortChannelId, PublicChannel]](10 seconds).apply(channelUpdateBC.shortChannelId)).get val u = updateFor(nodes("B").nodeParams.nodeId, sender.expectMsgType[Map[ShortChannelId, PublicChannel]](10 seconds).apply(channelUpdateBC.shortChannelId)).get
u.cltvExpiryDelta == nodes("B").nodeParams.expiryDeltaBlocks u.cltvExpiryDelta == nodes("B").nodeParams.expiryDeltaBlocks
}, max = 30 seconds, interval = 1 second) }, max = 30 seconds, interval = 1 second)
@ -881,7 +891,7 @@ class IntegrationSpec extends TestKitBaseClass with BitcoindService with AnyFunS
val res = sender.expectMsgType[JValue](10 seconds) val res = sender.expectMsgType[JValue](10 seconds)
val previouslyReceivedByC = res.filter(_ \ "address" == JString(finalAddressC)).flatMap(_ \ "txids" \\ classOf[JString]) val previouslyReceivedByC = res.filter(_ \ "address" == JString(finalAddressC)).flatMap(_ \ "txids" \\ classOf[JString])
// we then kill the connection between C and F // we then kill the connection between C and F
sender.send(nodes("F1").switchboard, 'peers) sender.send(nodes("F1").switchboard, Symbol("peers"))
val peers = sender.expectMsgType[Iterable[ActorRef]] val peers = sender.expectMsgType[Iterable[ActorRef]]
// F's only node is C // F's only node is C
peers.head ! Peer.Disconnect(nodes("C").nodeParams.nodeId) peers.head ! Peer.Disconnect(nodes("C").nodeParams.nodeId)
@ -962,7 +972,7 @@ class IntegrationSpec extends TestKitBaseClass with BitcoindService with AnyFunS
val res = sender.expectMsgType[JValue](10 seconds) val res = sender.expectMsgType[JValue](10 seconds)
val previouslyReceivedByC = res.filter(_ \ "address" == JString(finalAddressC)).flatMap(_ \ "txids" \\ classOf[JString]) val previouslyReceivedByC = res.filter(_ \ "address" == JString(finalAddressC)).flatMap(_ \ "txids" \\ classOf[JString])
// we then kill the connection between C and F // we then kill the connection between C and F
sender.send(nodes("F2").switchboard, 'peers) sender.send(nodes("F2").switchboard, Symbol("peers"))
val peers = sender.expectMsgType[Iterable[ActorRef]] val peers = sender.expectMsgType[Iterable[ActorRef]]
// F's only node is C // F's only node is C
peers.head ! Disconnect(nodes("C").nodeParams.nodeId) peers.head ! Disconnect(nodes("C").nodeParams.nodeId)
@ -1274,7 +1284,7 @@ class IntegrationSpec extends TestKitBaseClass with BitcoindService with AnyFunS
sender.expectMsg(GossipDecision.Accepted(ann)) sender.expectMsg(GossipDecision.Accepted(ann))
} }
awaitCond({ awaitCond({
sender.send(nodes("D").router, 'channels) sender.send(nodes("D").router, Symbol("channels"))
sender.expectMsgType[Iterable[ChannelAnnouncement]](5 seconds).size == channels.size + 8 // 8 remaining channels because D->F{1-5} have disappeared sender.expectMsgType[Iterable[ChannelAnnouncement]](5 seconds).size == channels.size + 8 // 8 remaining channels because D->F{1-5} have disappeared
}, max = 120 seconds, interval = 1 second) }, max = 120 seconds, interval = 1 second)
} }

View File

@ -38,13 +38,12 @@ import fr.acinq.eclair.payment.relay.{Origin, Relayer}
import fr.acinq.eclair.payment.send.PaymentInitiator.{SendPaymentConfig, SendPaymentRequest} import fr.acinq.eclair.payment.send.PaymentInitiator.{SendPaymentConfig, SendPaymentRequest}
import fr.acinq.eclair.payment.send.PaymentLifecycle import fr.acinq.eclair.payment.send.PaymentLifecycle
import fr.acinq.eclair.payment.send.PaymentLifecycle._ import fr.acinq.eclair.payment.send.PaymentLifecycle._
import fr.acinq.eclair.router.Announcements.{makeChannelUpdate, makeNodeAnnouncement} import fr.acinq.eclair.router.Announcements.makeChannelUpdate
import fr.acinq.eclair.router.Router._ import fr.acinq.eclair.router.Router._
import fr.acinq.eclair.router._ import fr.acinq.eclair.router._
import fr.acinq.eclair.transactions.Scripts import fr.acinq.eclair.transactions.Scripts
import fr.acinq.eclair.wire.Onion.FinalLegacyPayload import fr.acinq.eclair.wire.Onion.FinalLegacyPayload
import fr.acinq.eclair.wire._ import fr.acinq.eclair.wire._
import scodec.bits.HexStringSyntax
import scala.concurrent.duration._ import scala.concurrent.duration._
@ -487,38 +486,36 @@ class PaymentLifecycleSpec extends BaseRouterSpec {
} }
test("payment succeeded to a channel with fees=0") { routerFixture => test("payment succeeded to a channel with fees=0") { routerFixture =>
import fr.acinq.eclair.randomKey
import routerFixture._ import routerFixture._
// the network will be a --(1)--> b ---(2)--> c --(3)--> d and e --(4)--> f (we are a) and b -> g has fees=0 // the network will be a --(1)--> b ---(2)--> c --(3)--> d
// \ // | |
// \--(5)--> g // | +---(100)---+
val (priv_g, priv_funding_g) = (randomKey, randomKey) // | | and b -> h has fees = 0
val (g, funding_g) = (priv_g.publicKey, priv_funding_g.publicKey) // +---(5)--> g ---(6)--> h
val ann_g = makeNodeAnnouncement(priv_g, "node-G", Color(-30, 10, -50), Nil, TestConstants.Bob.nodeParams.features) // and e --(4)--> f (we are a)
val channelId_bg = ShortChannelId(420000, 5, 0) val channelId_bh = ShortChannelId(420000, 100, 0)
val chan_bg = channelAnnouncement(channelId_bg, priv_b, priv_g, priv_funding_b, priv_funding_g) val chan_bh = channelAnnouncement(channelId_bh, priv_b, priv_h, priv_funding_b, priv_funding_h)
val channelUpdate_bg = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, g, channelId_bg, CltvExpiryDelta(9), htlcMinimumMsat = 0 msat, feeBaseMsat = 0 msat, feeProportionalMillionths = 0, htlcMaximumMsat = 500000000 msat) val channelUpdate_bh = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, h, channelId_bh, CltvExpiryDelta(9), htlcMinimumMsat = 0 msat, feeBaseMsat = 0 msat, feeProportionalMillionths = 0, htlcMaximumMsat = 500000000 msat)
val channelUpdate_gb = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_g, b, channelId_bg, CltvExpiryDelta(9), htlcMinimumMsat = 0 msat, feeBaseMsat = 10 msat, feeProportionalMillionths = 8, htlcMaximumMsat = 500000000 msat) val channelUpdate_hb = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_h, b, channelId_bh, CltvExpiryDelta(9), htlcMinimumMsat = 0 msat, feeBaseMsat = 10 msat, feeProportionalMillionths = 8, htlcMaximumMsat = 500000000 msat)
assert(Router.getDesc(channelUpdate_bg, chan_bg) === ChannelDesc(chan_bg.shortChannelId, priv_b.publicKey, priv_g.publicKey)) assert(Router.getDesc(channelUpdate_bh, chan_bh) === ChannelDesc(channelId_bh, priv_b.publicKey, priv_h.publicKey))
val peerConnection = TestProbe() val peerConnection = TestProbe()
router ! PeerRoutingMessage(peerConnection.ref, remoteNodeId, chan_bg) router ! PeerRoutingMessage(peerConnection.ref, remoteNodeId, chan_bh)
router ! PeerRoutingMessage(peerConnection.ref, remoteNodeId, ann_g) router ! PeerRoutingMessage(peerConnection.ref, remoteNodeId, channelUpdate_bh)
router ! PeerRoutingMessage(peerConnection.ref, remoteNodeId, channelUpdate_bg) router ! PeerRoutingMessage(peerConnection.ref, remoteNodeId, channelUpdate_hb)
router ! PeerRoutingMessage(peerConnection.ref, remoteNodeId, channelUpdate_gb) watcher.expectMsg(ValidateRequest(chan_bh))
watcher.expectMsg(ValidateRequest(chan_bg)) watcher.send(router, ValidateResult(chan_bh, Right((Transaction(version = 0, txIn = Nil, txOut = TxOut(1000000 sat, write(pay2wsh(Scripts.multiSig2of2(funding_b, funding_h)))) :: Nil, lockTime = 0), UtxoStatus.Unspent))))
watcher.send(router, ValidateResult(chan_bg, Right((Transaction(version = 0, txIn = Nil, txOut = TxOut(1000000 sat, write(pay2wsh(Scripts.multiSig2of2(funding_b, funding_g)))) :: Nil, lockTime = 0), UtxoStatus.Unspent))))
watcher.expectMsgType[WatchSpentBasic] watcher.expectMsgType[WatchSpentBasic]
val payFixture = createPaymentLifecycle() val payFixture = createPaymentLifecycle()
import payFixture._ import payFixture._
// we send a payment to G // we send a payment to H
val request = SendPayment(g, FinalLegacyPayload(defaultAmountMsat, defaultExpiry), 5) val request = SendPayment(h, FinalLegacyPayload(defaultAmountMsat, defaultExpiry), 5)
sender.send(paymentFSM, request) sender.send(paymentFSM, request)
routerForwarder.expectMsgType[RouteRequest] routerForwarder.expectMsgType[RouteRequest]
// the route will be A -> B -> G where B -> G has a channel_update with fees=0 // the route will be A -> B -> H where B -> H has a channel_update with fees=0
val Transition(_, WAITING_FOR_REQUEST, WAITING_FOR_ROUTE) = monitor.expectMsgClass(classOf[Transition[_]]) val Transition(_, WAITING_FOR_REQUEST, WAITING_FOR_ROUTE) = monitor.expectMsgClass(classOf[Transition[_]])
routerForwarder.forward(router) routerForwarder.forward(router)
val Transition(_, WAITING_FOR_ROUTE, WAITING_FOR_PAYMENT_COMPLETE) = monitor.expectMsgClass(classOf[Transition[_]]) val Transition(_, WAITING_FOR_ROUTE, WAITING_FOR_PAYMENT_COMPLETE) = monitor.expectMsgClass(classOf[Transition[_]])
@ -528,9 +525,8 @@ class PaymentLifecycleSpec extends BaseRouterSpec {
val PaymentSent(_, _, paymentOK.paymentPreimage, finalAmount, _, PartialPayment(_, request.finalPayload.amount, fee, ByteVector32.Zeroes, _, _) :: Nil) = eventListener.expectMsgType[PaymentSent] val PaymentSent(_, _, paymentOK.paymentPreimage, finalAmount, _, PartialPayment(_, request.finalPayload.amount, fee, ByteVector32.Zeroes, _, _) :: Nil) = eventListener.expectMsgType[PaymentSent]
assert(finalAmount === defaultAmountMsat) assert(finalAmount === defaultAmountMsat)
// during the route computation the fees were treated as if they were 1msat but when sending the onion we actually put zero
// NB: A -> B doesn't pay fees because it's our direct neighbor // NB: A -> B doesn't pay fees because it's our direct neighbor
// NB: B -> G doesn't asks for fees at all // NB: B -> H doesn't asks for fees at all
assert(fee === 0.msat) assert(fee === 0.msat)
assert(paymentOK.recipientAmount === request.finalPayload.amount) assert(paymentOK.recipientAmount === request.finalPayload.amount)
} }