Skip to content

Commit

Permalink
reproduce bug in Commitments.availableForSend
Browse files Browse the repository at this point in the history
We must consider `nextRemoteCommit` when applicable.

This is a regression caused in #784. The core bug only exists when we
have a pending unacked `commit_sig`, but since we only send the
`AvailableBalanceChanged` event when sending a signature (not when
receiving a revocation), actors relying on this event to know the
current available balance (e.g. the `Relayer`) will have a wrong
value in-between two outgoing sigs.
  • Loading branch information
pm47 committed Aug 22, 2019
1 parent 290ac3d commit c18e36a
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2019 ACINQ SAS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package fr.acinq.eclair.channel

import java.util.UUID

import fr.acinq.bitcoin.Satoshi
import fr.acinq.eclair.{TestkitBaseClass, _}
import fr.acinq.eclair.channel.Commitments._
import fr.acinq.eclair.channel.states.StateTestsHelperMethods
import fr.acinq.eclair.payment.Local
import org.scalatest.Outcome

import scala.concurrent.duration._

class CommitmentsSpec extends TestkitBaseClass with StateTestsHelperMethods {

type FixtureParam = SetupFixture

implicit val log: akka.event.LoggingAdapter = akka.event.NoLogging

override def withFixture(test: OneArgTest): Outcome = {
val setup = init()
import setup._
within(30 seconds) {
reachNormal(setup, test.tags)
awaitCond(alice.stateName == NORMAL)
awaitCond(bob.stateName == NORMAL)
withFixture(test.toNoArgTest(setup))
}
}

test("availableForSend") { f =>
import f._

val c0 = alice.stateData.asInstanceOf[DATA_NORMAL].commitments
val sendAmount = Satoshi(420000).toMilliSatoshi
assert(c0.availableBalanceForSend > sendAmount)

val cmdAdd = makeCmdAdd(sendAmount, bob.underlyingActor.nodeParams.nodeId)._2
val Right((c1, _)) = sendAdd(c0, cmdAdd, Local(UUID.randomUUID, None))
assert(c1.availableBalanceForSend < c0.availableBalanceForSend)

val (c2, commit_sig) = sendCommit(c1, alice.underlyingActor.nodeParams.keyManager)
assert(c2.availableBalanceForSend == c1.availableBalanceForSend)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package fr.acinq.eclair.channel.states
import java.util.UUID

import akka.testkit.{TestFSMRef, TestKitBase, TestProbe}
import fr.acinq.bitcoin.Crypto.PublicKey
import fr.acinq.bitcoin.{ByteVector32, Crypto}
import fr.acinq.eclair
import fr.acinq.eclair.TestConstants.{Alice, Bob, TestFeeEstimator}
Expand Down Expand Up @@ -106,19 +107,23 @@ trait StateTestsHelperMethods extends TestKitBase {
channelUpdateListener.expectMsgType[LocalChannelUpdate]
}

def makeCmdAdd(amount: MilliSatoshi, destination: PublicKey): (ByteVector32, CMD_ADD_HTLC) = {
val payment_preimage: ByteVector32 = randomBytes32
val payment_hash: ByteVector32 = Crypto.sha256(payment_preimage)
val expiry = 400144
val cmd = PaymentLifecycle.buildCommand(UUID.randomUUID, amount, expiry, payment_hash, Hop(null, destination, null) :: Nil)._1.copy(commit = false)
(payment_preimage, cmd)
}

def addHtlc(amount: MilliSatoshi, s: TestFSMRef[State, Data, Channel], r: TestFSMRef[State, Data, Channel], s2r: TestProbe, r2s: TestProbe): (ByteVector32, UpdateAddHtlc) = {
val R: ByteVector32 = randomBytes32
val H: ByteVector32 = Crypto.sha256(R)
val sender = TestProbe()
val receiverPubkey = r.underlyingActor.nodeParams.nodeId
val expiry = 400144
val cmd = PaymentLifecycle.buildCommand(UUID.randomUUID, amount, expiry, H, Hop(null, receiverPubkey, null) :: Nil)._1.copy(commit = false)
val (payment_preimage, cmd) = makeCmdAdd(amount, r.underlyingActor.nodeParams.nodeId)
sender.send(s, cmd)
sender.expectMsg("ok")
val htlc = s2r.expectMsgType[UpdateAddHtlc]
s2r.forward(r)
awaitCond(r.stateData.asInstanceOf[HasCommitments].commitments.remoteChanges.proposed.contains(htlc))
(R, htlc)
(payment_preimage, htlc)
}

def fulfillHtlc(id: Long, R: ByteVector32, s: TestFSMRef[State, Data, Channel], r: TestFSMRef[State, Data, Channel], s2r: TestProbe, r2s: TestProbe) = {
Expand Down

0 comments on commit c18e36a

Please sign in to comment.