lnwallet: add anchor size back to balance on coop close

To be spec compliant, we require the initiator to not pay the anchor
values into fees on coop close. We extract the balance calculation into
commitment.go, and add back the value of the anchors to the initiator's
balance.
This commit is contained in:
Johan T. Halseth 2020-08-21 13:38:36 +02:00
parent 07a57ae778
commit 09a126b29f
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26
2 changed files with 67 additions and 7 deletions

@ -683,14 +683,37 @@ func testCommitHTLCSigTieBreak(t *testing.T, restart bool) {
} }
} }
// TestCooperativeChannelClosure checks that the coop close process finishes
// with an agreement from both parties, and that the final balances of the
// close tx check out.
func TestCooperativeChannelClosure(t *testing.T) { func TestCooperativeChannelClosure(t *testing.T) {
t.Run("tweakless", func(t *testing.T) {
testCoopClose(t, &coopCloseTestCase{
chanType: channeldb.SingleFunderTweaklessBit,
})
})
t.Run("anchors", func(t *testing.T) {
testCoopClose(t, &coopCloseTestCase{
chanType: channeldb.SingleFunderTweaklessBit |
channeldb.AnchorOutputsBit,
anchorAmt: anchorSize * 2,
})
})
}
type coopCloseTestCase struct {
chanType channeldb.ChannelType
anchorAmt btcutil.Amount
}
func testCoopClose(t *testing.T, testCase *coopCloseTestCase) {
t.Parallel() t.Parallel()
// Create a test channel which will be used for the duration of this // Create a test channel which will be used for the duration of this
// unittest. The channel will be funded evenly with Alice having 5 BTC, // unittest. The channel will be funded evenly with Alice having 5 BTC,
// and Bob having 5 BTC. // and Bob having 5 BTC.
aliceChannel, bobChannel, cleanUp, err := CreateTestChannels( aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(
channeldb.SingleFunderTweaklessBit, testCase.chanType,
) )
if err != nil { if err != nil {
t.Fatalf("unable to create test channels: %v", err) t.Fatalf("unable to create test channels: %v", err)
@ -707,7 +730,7 @@ func TestCooperativeChannelClosure(t *testing.T) {
bobChannel.channelState.LocalCommitment.FeePerKw, bobChannel.channelState.LocalCommitment.FeePerKw,
) )
// We'll store with both Alice and Bob creating a new close proposal // We'll start with both Alice and Bob creating a new close proposal
// with the same fee. // with the same fee.
aliceFee := aliceChannel.CalcFee(aliceFeeRate) aliceFee := aliceChannel.CalcFee(aliceFeeRate)
aliceSig, _, _, err := aliceChannel.CreateCloseProposal( aliceSig, _, _, err := aliceChannel.CreateCloseProposal(
@ -728,7 +751,7 @@ func TestCooperativeChannelClosure(t *testing.T) {
// With the proposals created, both sides should be able to properly // With the proposals created, both sides should be able to properly
// process the other party's signature. This indicates that the // process the other party's signature. This indicates that the
// transaction is well formed, and the signatures verify. // transaction is well formed, and the signatures verify.
aliceCloseTx, _, err := bobChannel.CompleteCooperativeClose( aliceCloseTx, bobTxBalance, err := bobChannel.CompleteCooperativeClose(
bobSig, aliceSig, bobDeliveryScript, aliceDeliveryScript, bobSig, aliceSig, bobDeliveryScript, aliceDeliveryScript,
bobFee, bobFee,
) )
@ -737,7 +760,7 @@ func TestCooperativeChannelClosure(t *testing.T) {
} }
bobCloseSha := aliceCloseTx.TxHash() bobCloseSha := aliceCloseTx.TxHash()
bobCloseTx, _, err := aliceChannel.CompleteCooperativeClose( bobCloseTx, aliceTxBalance, err := aliceChannel.CompleteCooperativeClose(
aliceSig, bobSig, aliceDeliveryScript, bobDeliveryScript, aliceSig, bobSig, aliceDeliveryScript, bobDeliveryScript,
aliceFee, aliceFee,
) )
@ -749,6 +772,32 @@ func TestCooperativeChannelClosure(t *testing.T) {
if bobCloseSha != aliceCloseSha { if bobCloseSha != aliceCloseSha {
t.Fatalf("alice and bob close transactions don't match: %v", err) t.Fatalf("alice and bob close transactions don't match: %v", err)
} }
// Finally, make sure the final balances are correct from both's
// perspective.
aliceBalance := aliceChannel.channelState.LocalCommitment.
LocalBalance.ToSatoshis()
// The commit balance have had the initiator's (Alice) commitfee and
// any anchors subtracted, so add that back to the final expected
// balance. Alice also pays the coop close fee, so that must be
// subtracted.
commitFee := aliceChannel.channelState.LocalCommitment.CommitFee
expBalanceAlice := aliceBalance + commitFee +
testCase.anchorAmt - bobFee
if aliceTxBalance != expBalanceAlice {
t.Fatalf("expected balance %v got %v", expBalanceAlice,
aliceTxBalance)
}
// Bob is not the initiator, so his final balance should simply be
// equal to the latest commitment balance.
expBalanceBob := bobChannel.channelState.LocalCommitment.
LocalBalance.ToSatoshis()
if bobTxBalance != expBalanceBob {
t.Fatalf("expected bob's balance to be %v got %v",
expBalanceBob, bobTxBalance)
}
} }
// TestForceClose checks that the resulting ForceCloseSummary is correct when a // TestForceClose checks that the resulting ForceCloseSummary is correct when a

@ -675,11 +675,22 @@ func CoopCloseBalance(chanType channeldb.ChannelType, isInitiator bool,
// We'll make sure we account for the complete balance by adding the // We'll make sure we account for the complete balance by adding the
// current dangling commitment fee to the balance of the initiator. // current dangling commitment fee to the balance of the initiator.
commitFee := localCommit.CommitFee initiatorDelta := localCommit.CommitFee
// Since the initiator's balance also is stored after subtracting the
// anchor values, add that back in case this was an anchor commitment.
if chanType.HasAnchors() {
initiatorDelta += 2 * anchorSize
}
// The initiator will pay the full coop close fee, subtract that value
// from their balance.
initiatorDelta -= coopCloseFee
if isInitiator { if isInitiator {
ourBalance = ourBalance - coopCloseFee + commitFee ourBalance += initiatorDelta
} else { } else {
theirBalance = theirBalance - coopCloseFee + commitFee theirBalance += initiatorDelta
} }
return ourBalance, theirBalance return ourBalance, theirBalance