Skip to content

Commit

Permalink
Fix flaky coin selection tests (#2749)
Browse files Browse the repository at this point in the history
Some tests related to bitcoind's funding behavior have been randomly
failing since we updated to bitcoind 24.1. They now have more sensibility
over the exact set of available utxos, so we needed to fine-tune them
accordingly.

I also imported some tests that were added to feature branches but never
backported to `master`.
  • Loading branch information
t-bast authored Sep 22, 2023
1 parent 96ebbfe commit 59c612e
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -513,18 +513,25 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
}

test("initiator uses unconfirmed inputs") {
withFixture(100_000 sat, Seq(250_000 sat), 0 sat, Nil, FeeratePerKw(2500 sat), 660 sat, 0, RequireConfirmedInputs(forLocal = false, forRemote = false)) { f =>
withFixture(100_000 sat, Seq(170_000 sat), 0 sat, Nil, FeeratePerKw(2500 sat), 660 sat, 0, RequireConfirmedInputs(forLocal = false, forRemote = false)) { f =>
import f._

// Alice's inputs are all unconfirmed.
// Alice's inputs are all unconfirmed: we spent her only confirmed input to create two unconfirmed outputs.
val probe = TestProbe()
val tx = sendToAddress(getNewAddress(probe, rpcClientA), 75_000 sat, probe, rpcClientA)
walletA.listUnspent().pipeTo(probe.ref)
probe.expectMsgType[Seq[Utxo]].foreach(utxo => assert(utxo.confirmations == 0))
val utxosA = probe.expectMsgType[Seq[Utxo]]
assert(utxosA.size == 2)
utxosA.foreach(utxo => assert(utxo.confirmations == 0))
utxosA.foreach(utxo => assert(utxo.amount < 100_000.sat)) // Alice will need both inputs to fund the channel.

alice ! Start(alice2bob.ref)
bob ! Start(bob2alice.ref)

// Alice --- tx_add_input --> Bob
fwd.forwardAlice2Bob[TxAddInput]
// Alice <-- tx_complete --- Bob
fwd.forwardBob2Alice[TxComplete]
// Alice --- tx_add_input --> Bob
fwd.forwardAlice2Bob[TxAddInput]
// Alice <-- tx_complete --- Bob
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ class ReplaceableTxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike w
}

test("commit tx not confirming, adding other wallet inputs") {
withFixture(Seq(10.4 millibtc, 5 millibtc), ChannelTypes.AnchorOutputsZeroFeeHtlcTx()) { f =>
withFixture(Seq(10.2 millibtc, 0.15 millibtc, 0.15 millibtc), ChannelTypes.AnchorOutputsZeroFeeHtlcTx()) { f =>
import f._

val (commitTx, anchorTx) = closeChannelWithoutHtlcs(f, aliceBlockHeight() + 40)
Expand All @@ -563,8 +563,14 @@ class ReplaceableTxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike w
val listener = TestProbe()
system.eventStream.subscribe(listener.ref, classOf[TransactionPublished])

// We have 3 small utxos: one is enough to fund the first anchor tx, but we'll need several at a higher feerate.
wallet.listUnspent().pipeTo(probe.ref)
val utxos = probe.expectMsgType[Seq[BitcoinCoreClient.Utxo]]
assert(utxos.size == 3)
utxos.foreach(utxo => assert(utxo.amount <= 0.15.millibtc))

// The feerate is (much) higher for higher block targets
val targetFeerate = FeeratePerKw(20_000 sat)
val targetFeerate = FeeratePerKw(10_000 sat)
setFeerate(FeeratePerKw(3000 sat))
setFeerate(targetFeerate, blockTarget = 6)
publisher ! Publish(probe.ref, anchorTx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ import fr.acinq.eclair.blockchain.fee.FeeratesPerKw
import fr.acinq.eclair.blockchain.{CurrentBlockHeight, CurrentFeerates}
import fr.acinq.eclair.channel._
import fr.acinq.eclair.channel.fsm.Channel
import fr.acinq.eclair.channel.publish.TxPublisher.{PublishFinalTx, PublishTx}
import fr.acinq.eclair.channel.publish.TxPublisher.{PublishFinalTx, PublishReplaceableTx, PublishTx}
import fr.acinq.eclair.channel.states.ChannelStateTestsBase
import fr.acinq.eclair.channel.states.ChannelStateTestsBase.PimpTestFSM
import fr.acinq.eclair.transactions.Transactions.HtlcSuccessTx
import fr.acinq.eclair.transactions.Transactions.{ClaimHtlcTimeoutTx, HtlcSuccessTx}
import fr.acinq.eclair.wire.protocol._
import fr.acinq.eclair.{BlockHeight, CltvExpiry, CltvExpiryDelta, MilliSatoshiLong, TestConstants, TestKitBaseClass, TestUtils, randomBytes32}
import org.scalatest.funsuite.FixtureAnyFunSuiteLike
Expand Down Expand Up @@ -394,24 +394,73 @@ class OfflineStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
Transaction.correctlySpends(claimMainOutput, bobCommitTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
}

test("counterparty lies about having a more recent commitment", Tag(IgnoreChannelUpdates)) { f =>
test("counterparty lies about having a more recent commitment and publishes current commitment", Tag(IgnoreChannelUpdates)) { f =>
import f._
val aliceCommitTx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx

// the current state contains a pending htlc
addHtlc(250_000_000 msat, alice, bob, alice2bob, bob2alice)
crossSign(alice, bob, alice2bob, bob2alice)
val bobCommitTx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx

// we simulate a disconnection followed by a reconnection
disconnect(alice, bob)
reconnect(alice, bob, alice2bob, bob2alice)

// peers exchange channel_reestablish messages
alice2bob.expectMsgType[ChannelReestablish]
// bob sends an invalid channel_reestablish
alice2bob.expectMsgType[ChannelReestablish]
val invalidReestablish = bob2alice.expectMsgType[ChannelReestablish].copy(nextRemoteRevocationNumber = 42)

// alice then finds out bob is lying
// alice then asks bob to publish its commitment to find out if bob is lying
bob2alice.send(alice, invalidReestablish)
val error = alice2bob.expectMsgType[Error]
assert(error == Error(channelId(alice), PleasePublishYourCommitment(channelId(alice)).getMessage))
// alice now waits for bob to publish its commitment
alice2blockchain.expectNoMessage(100 millis)
awaitCond(alice.stateName == WAIT_FOR_REMOTE_PUBLISH_FUTURE_COMMITMENT)

// bob publishes the latest commitment
alice ! WatchFundingSpentTriggered(bobCommitTx)

// alice is able to claim her main output and the htlc (once it times out)
val claimMainOutput = alice2blockchain.expectMsgType[PublishFinalTx].tx
Transaction.correctlySpends(claimMainOutput, bobCommitTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
val claimHtlc = alice2blockchain.expectMsgType[PublishReplaceableTx]
assert(claimHtlc.txInfo.isInstanceOf[ClaimHtlcTimeoutTx])
Transaction.correctlySpends(claimHtlc.txInfo.tx, bobCommitTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
}

test("counterparty lies about having a more recent commitment and publishes revoked commitment", Tag(IgnoreChannelUpdates)) { f =>
import f._

// we sign a new commitment to make sure the first one is revoked
val bobRevokedCommitTx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx
addHtlc(250_000_000 msat, alice, bob, alice2bob, bob2alice)
crossSign(alice, bob, alice2bob, bob2alice)

// we simulate a disconnection followed by a reconnection
disconnect(alice, bob)
reconnect(alice, bob, alice2bob, bob2alice)
// bob sends an invalid channel_reestablish
alice2bob.expectMsgType[ChannelReestablish]
val invalidReestablish = bob2alice.expectMsgType[ChannelReestablish].copy(nextLocalCommitmentNumber = 42)

// alice then asks bob to publish its commitment to find out if bob is lying
bob2alice.send(alice, invalidReestablish)
val error = alice2bob.expectMsgType[Error]
assert(error == Error(channelId(alice), PleasePublishYourCommitment(channelId(alice)).getMessage))
// alice now waits for bob to publish its commitment
alice2blockchain.expectNoMessage(100 millis)
awaitCond(alice.stateName == WAIT_FOR_REMOTE_PUBLISH_FUTURE_COMMITMENT)

// bob publishes the revoked commitment
alice ! WatchFundingSpentTriggered(bobRevokedCommitTx)

// alice is able to claim all outputs
assert(bobRevokedCommitTx.txOut.length == 2)
val claimMainOutput = alice2blockchain.expectMsgType[PublishFinalTx].tx
Transaction.correctlySpends(claimMainOutput, bobRevokedCommitTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
val claimRevokedOutput = alice2blockchain.expectMsgType[PublishFinalTx].tx
Transaction.correctlySpends(claimRevokedOutput, bobRevokedCommitTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
assert(claimRevokedOutput.txIn.head.outPoint.index != claimMainOutput.txIn.head.outPoint.index)
}

test("change relay fee while offline", Tag(IgnoreChannelUpdates)) { f =>
Expand Down

0 comments on commit 59c612e

Please sign in to comment.