Skip to content

Commit 85eeade

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 98a872e commit 85eeade

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
@@ -840,7 +840,7 @@ func (b *Batcher) handleSweeps(ctx context.Context, sweeps []*sweep,
840840
// Instead we directly detect and return the spend here.
841841
if completed && parentBatch.Confirmed {
842842
return b.monitorSpendAndNotify(
843-
ctx, sweep, parentBatch.ID, notifier,
843+
ctx, sweeps, parentBatch.ID, notifier,
844844
)
845845
}
846846

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

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

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

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

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

11841202
select {

sweepbatcher/sweep_batcher_presigned_test.go

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

1571+
// Calculate the expected on-chain fee of the swap.
1572+
wantFee := make([]btcutil.Amount, numConfirmedSwaps)
1573+
for i := range numConfirmedSwaps {
1574+
batchAmount := swapAmount * btcutil.Amount(numConfirmedSwaps)
1575+
txFee := batchAmount - btcutil.Amount(tx.TxOut[0].Value)
1576+
numConfirmedSweeps := numConfirmedSwaps * sweepsPerSwap
1577+
feePerSweep := txFee / btcutil.Amount(numConfirmedSweeps)
1578+
roundingDiff := txFee - feePerSweep*btcutil.Amount(
1579+
numConfirmedSweeps,
1580+
)
1581+
swapFee := feePerSweep * 2
1582+
1583+
// Add rounding difference to the first swap.
1584+
if i == 0 {
1585+
swapFee += roundingDiff
1586+
}
1587+
1588+
wantFee[i] = swapFee
1589+
}
1590+
15711591
// Make sure that notifiers of confirmed sweeps received notifications.
15721592
for i := range numConfirmedSwaps {
15731593
spend := <-spendChans[i]
15741594
require.Equal(t, txHash, spend.Tx.TxHash())
1595+
require.Equal(t, wantFee[i], spend.OnChainFeePortion)
15751596
}
15761597

15771598
<-lnd.RegisterConfChannel
@@ -1631,6 +1652,7 @@ func testPresigned_purging(t *testing.T, numSwaps, numConfirmedSwaps int,
16311652

16321653
spend := <-spendChan
16331654
require.Equal(t, txHash, spend.Tx.TxHash())
1655+
require.Equal(t, wantFee[i], spend.OnChainFeePortion)
16341656

16351657
<-lnd.RegisterConfChannel
16361658
lnd.ConfChannel <- &chainntnfs.TxConfirmation{

0 commit comments

Comments
 (0)