Skip to content

Commit bed3c1c

Browse files
committed
sweepbatcher: fix OnChainFeePortion values
There were two mistakes. In case of a swap with multiple sweeps only the fee of the first sweep of a swap was accounted. Rounding diff (the remainder) was attributed to all the sweeps rather than to the first (primary) sweep of the batch. The sweep to attribute the remainder was chosen by comparing SignatureScript which is always empty. New approach is to find the primary sweep and to compare its outpoint directly.
1 parent 78e90b3 commit bed3c1c

File tree

3 files changed

+78
-18
lines changed

3 files changed

+78
-18
lines changed

sweepbatcher/sweep_batch.go

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1923,12 +1923,12 @@ func getFeePortionForSweep(spendTx *wire.MsgTx, numSweeps int,
19231923
}
19241924

19251925
// getFeePortionPaidBySweep returns the fee portion that the sweep should pay
1926-
// for the batch transaction. If the sweep is the first sweep in the batch, it
1926+
// for the batch transaction. If the sweep is the primary sweep in the batch, it
19271927
// pays the rounding difference.
1928-
func getFeePortionPaidBySweep(spendTx *wire.MsgTx, feePortionPerSweep,
1929-
roundingDiff btcutil.Amount, sweep *sweep) btcutil.Amount {
1928+
func getFeePortionPaidBySweep(feePortionPerSweep, roundingDiff btcutil.Amount,
1929+
primary bool) btcutil.Amount {
19301930

1931-
if bytes.Equal(spendTx.TxIn[0].SignatureScript, sweep.htlc.SigScript) {
1931+
if primary {
19321932
return feePortionPerSweep + roundingDiff
19331933
}
19341934

@@ -1980,22 +1980,42 @@ func (b *batch) handleSpend(ctx context.Context, spendTx *wire.MsgTx) error {
19801980
spendTx, len(notifyList), totalSweptAmt,
19811981
)
19821982

1983+
// Calculate fees per swaps. Only the first sweep in a swap has a
1984+
// notifier, so we calculate total fee per swap and send it to a sweep
1985+
// having that swap and a notifier.
1986+
swap2fee := make(map[lntypes.Hash]btcutil.Amount)
1987+
for _, sweep := range notifyList {
1988+
primary := sweep.outpoint == b.primarySweepID
1989+
1990+
swap2fee[sweep.swapHash] += getFeePortionPaidBySweep(
1991+
feePortionPaidPerSweep, roundingDifference, primary,
1992+
)
1993+
}
1994+
1995+
// Now send notifications to notifiers.
19831996
for _, sweep := range notifyList {
19841997
// If the sweep's notifier is empty then this means that a swap
1985-
// is not waiting to read an update from it, so we can skip
1986-
// the notification part.
1998+
// is not waiting to read an update from it or this is not the
1999+
// first sweep in a swap, so we can skip the notification part.
19872000
if sweep.notifier == nil ||
19882001
*sweep.notifier == (SpendNotifier{}) {
19892002

19902003
continue
19912004
}
19922005

2006+
// Make sure there is only one sweep with a notifier per swap
2007+
// hash, otherwise our fee calculation is incorrect.
2008+
fee, has := swap2fee[sweep.swapHash]
2009+
if !has {
2010+
return fmt.Errorf("no fee for swap %v; maybe "+
2011+
"multiple sweeps with a notifier per swap?",
2012+
sweep.swapHash)
2013+
}
2014+
delete(swap2fee, sweep.swapHash)
2015+
19932016
spendDetail := SpendDetail{
1994-
Tx: spendTx,
1995-
OnChainFeePortion: getFeePortionPaidBySweep(
1996-
spendTx, feePortionPaidPerSweep,
1997-
roundingDifference, &sweep,
1998-
),
2017+
Tx: spendTx,
2018+
OnChainFeePortion: fee,
19992019
}
20002020

20012021
// Dispatch the sweep notifier, we don't care about the outcome

sweepbatcher/sweep_batcher.go

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,7 @@ func (b *Batcher) handleSweeps(ctx context.Context, sweeps []*sweep,
841841
// Instead we directly detect and return the spend here.
842842
if completed && parentBatch.Confirmed {
843843
return b.monitorSpendAndNotify(
844-
ctx, sweep, parentBatch.ID, notifier,
844+
ctx, sweeps, parentBatch.ID, notifier,
845845
)
846846
}
847847

@@ -1123,7 +1123,7 @@ func (b *Batcher) FetchUnconfirmedBatches(ctx context.Context) ([]*batch,
11231123
// the response back to the response channel. It is called if the batch is fully
11241124
// confirmed and we just need to deliver the data back to the caller though
11251125
// SpendNotifier.
1126-
func (b *Batcher) monitorSpendAndNotify(ctx context.Context, sweep *sweep,
1126+
func (b *Batcher) monitorSpendAndNotify(ctx context.Context, sweeps []*sweep,
11271127
parentBatchID int32, notifier *SpendNotifier) error {
11281128

11291129
// If the caller has not provided a notifier, stop.
@@ -1141,6 +1141,17 @@ func (b *Batcher) monitorSpendAndNotify(ctx context.Context, sweep *sweep,
11411141
return err
11421142
}
11431143

1144+
// Find the primarySweepID.
1145+
dbSweeps, err := b.store.FetchBatchSweeps(ctx, parentBatchID)
1146+
if err != nil {
1147+
cancel()
1148+
1149+
return err
1150+
}
1151+
primarySweepID := dbSweeps[0].Outpoint
1152+
1153+
sweep := sweeps[0]
1154+
11441155
spendChan, spendErr, err := b.chainNotifier.RegisterSpendNtfn(
11451156
spendCtx, &sweep.outpoint, sweep.htlc.PkScript,
11461157
sweep.initiationHeight,
@@ -1161,6 +1172,7 @@ func (b *Batcher) monitorSpendAndNotify(ctx context.Context, sweep *sweep,
11611172
select {
11621173
case spend := <-spendChan:
11631174
spendTx := spend.SpendingTx
1175+
11641176
// Calculate the fee portion that each sweep should pay
11651177
// for the batch.
11661178
feePortionPerSweep, roundingDifference :=
@@ -1169,17 +1181,23 @@ func (b *Batcher) monitorSpendAndNotify(ctx context.Context, sweep *sweep,
11691181
totalSwept,
11701182
)
11711183

1172-
onChainFeePortion := getFeePortionPaidBySweep(
1173-
spendTx, feePortionPerSweep,
1174-
roundingDifference, sweep,
1175-
)
1184+
// Sum onchain fee across all the sweeps of the swap.
1185+
var fee btcutil.Amount
1186+
for _, s := range sweeps {
1187+
isFirst := s.outpoint == primarySweepID
1188+
1189+
fee += getFeePortionPaidBySweep(
1190+
feePortionPerSweep, roundingDifference,
1191+
isFirst,
1192+
)
1193+
}
11761194

11771195
// Notify the requester of the spend with the spend
11781196
// details, including the fee portion for this
11791197
// particular sweep.
11801198
spendDetail := &SpendDetail{
11811199
Tx: spendTx,
1182-
OnChainFeePortion: onChainFeePortion,
1200+
OnChainFeePortion: fee,
11831201
}
11841202

11851203
select {

sweepbatcher/sweep_batcher_presigned_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1486,10 +1486,31 @@ func testPresigned_purging(t *testing.T, numSwaps, numConfirmedSwaps int,
14861486
}
14871487
lnd.SpendChannel <- spendDetail
14881488

1489+
// Calculate the expected on-chain fee of the swap.
1490+
wantFee := make([]btcutil.Amount, numConfirmedSwaps)
1491+
for i := range numConfirmedSwaps {
1492+
batchAmount := swapAmount * btcutil.Amount(numConfirmedSwaps)
1493+
txFee := batchAmount - btcutil.Amount(tx.TxOut[0].Value)
1494+
numConfirmedSweeps := numConfirmedSwaps * sweepsPerSwap
1495+
feePerSweep := txFee / btcutil.Amount(numConfirmedSweeps)
1496+
roundingDiff := txFee - feePerSweep*btcutil.Amount(
1497+
numConfirmedSweeps,
1498+
)
1499+
swapFee := feePerSweep * 2
1500+
1501+
// Add rounding difference to the first swap.
1502+
if i == 0 {
1503+
swapFee += roundingDiff
1504+
}
1505+
1506+
wantFee[i] = swapFee
1507+
}
1508+
14891509
// Make sure that notifiers of confirmed sweeps received notifications.
14901510
for i := range numConfirmedSwaps {
14911511
spend := <-spendChans[i]
14921512
require.Equal(t, txHash, spend.Tx.TxHash())
1513+
require.Equal(t, wantFee[i], spend.OnChainFeePortion)
14931514
}
14941515

14951516
<-lnd.RegisterConfChannel
@@ -1543,6 +1564,7 @@ func testPresigned_purging(t *testing.T, numSwaps, numConfirmedSwaps int,
15431564

15441565
spend := <-spendChan
15451566
require.Equal(t, txHash, spend.Tx.TxHash())
1567+
require.Equal(t, wantFee[i], spend.OnChainFeePortion)
15461568

15471569
<-lnd.RegisterConfChannel
15481570
lnd.ConfChannel <- &chainntnfs.TxConfirmation{

0 commit comments

Comments
 (0)