@@ -169,9 +169,10 @@ type batchConfig struct {
169
169
// initial delay completion and publishing the batch transaction.
170
170
batchPublishDelay time.Duration
171
171
172
- // noBumping instructs sweepbatcher not to fee bump itself and rely on
173
- // external source of fee rates (FeeRateProvider).
174
- noBumping bool
172
+ // customFeeRate provides custom min fee rate per swap. The batch uses
173
+ // max of the fee rates of its swaps. In this mode confTarget is
174
+ // ignored and fee bumping by sweepbatcher is disabled.
175
+ customFeeRate FeeRateProvider
175
176
176
177
// txLabeler is a function generating a transaction label. It is called
177
178
// before publishing a batch transaction. Batch ID is passed to it.
@@ -232,6 +233,10 @@ type batch struct {
232
233
// spendChan is the channel over which spend notifications are received.
233
234
spendChan chan * chainntnfs.SpendDetail
234
235
236
+ // spendErrChan is the channel over which spend notifier errors are
237
+ // received.
238
+ spendErrChan chan error
239
+
235
240
// confChan is the channel over which confirmation notifications are
236
241
// received.
237
242
confChan chan * chainntnfs.TxConfirmation
@@ -378,9 +383,7 @@ func NewBatch(cfg batchConfig, bk batchKit) *batch {
378
383
id : - 1 ,
379
384
state : Open ,
380
385
sweeps : make (map [wire.OutPoint ]sweep ),
381
- spendChan : make (chan * chainntnfs.SpendDetail ),
382
386
confChan : make (chan * chainntnfs.TxConfirmation , 1 ),
383
- reorgChan : make (chan struct {}, 1 ),
384
387
testReqs : make (chan * testRequest ),
385
388
errChan : make (chan error , 1 ),
386
389
callEnter : make (chan struct {}),
@@ -423,9 +426,7 @@ func NewBatchFromDB(cfg batchConfig, bk batchKit) (*batch, error) {
423
426
state : bk .state ,
424
427
primarySweepID : bk .primaryID ,
425
428
sweeps : bk .sweeps ,
426
- spendChan : make (chan * chainntnfs.SpendDetail ),
427
429
confChan : make (chan * chainntnfs.TxConfirmation , 1 ),
428
- reorgChan : make (chan struct {}, 1 ),
429
430
testReqs : make (chan * testRequest ),
430
431
errChan : make (chan error , 1 ),
431
432
callEnter : make (chan struct {}),
@@ -723,6 +724,9 @@ func (b *batch) addSweeps(ctx context.Context, sweeps []*sweep) (bool, error) {
723
724
// lower that minFeeRate of other sweeps (so it is
724
725
// applied).
725
726
if b .rbfCache .FeeRate < s .minFeeRate {
727
+ b .Infof ("Increasing feerate of the batch " +
728
+ "from %v to %v" , b .rbfCache .FeeRate ,
729
+ s .minFeeRate )
726
730
b .rbfCache .FeeRate = s .minFeeRate
727
731
}
728
732
}
@@ -769,6 +773,9 @@ func (b *batch) addSweeps(ctx context.Context, sweeps []*sweep) (bool, error) {
769
773
// Update FeeRate. Max(s.minFeeRate) for all the sweeps of
770
774
// the batch is the basis for fee bumps.
771
775
if b .rbfCache .FeeRate < s .minFeeRate {
776
+ b .Infof ("Increasing feerate of the batch " +
777
+ "from %v to %v" , b .rbfCache .FeeRate ,
778
+ s .minFeeRate )
772
779
b .rbfCache .FeeRate = s .minFeeRate
773
780
b .rbfCache .SkipNextBump = true
774
781
}
@@ -968,6 +975,12 @@ func (b *batch) Run(ctx context.Context) error {
968
975
continue
969
976
}
970
977
978
+ // Update feerate of sweeps. This is normally done by
979
+ // AddSweep, but it may not be called after the sweep
980
+ // is confirmed, but fresh feerate is still needed to
981
+ // keep publishing in case of reorg.
982
+ b .updateFeeRate (ctx )
983
+
971
984
err := b .publish (ctx )
972
985
if err != nil {
973
986
return fmt .Errorf ("publish error: %w" , err )
@@ -979,23 +992,26 @@ func (b *batch) Run(ctx context.Context) error {
979
992
return fmt .Errorf ("handleSpend error: %w" , err )
980
993
}
981
994
995
+ case err := <- b .spendErrChan :
996
+ b .writeToSpendErrChan (ctx , err )
997
+
998
+ return fmt .Errorf ("spend notifier failed: %w" , err )
999
+
982
1000
case conf := <- b .confChan :
983
1001
if err := b .handleConf (runCtx , conf ); err != nil {
984
1002
return fmt .Errorf ("handleConf error: %w" , err )
985
1003
}
986
1004
987
1005
return nil
988
1006
1007
+ // A re-org has been detected. We set the batch state back to
1008
+ // open since our batch transaction is no longer present in any
1009
+ // block. We can accept more sweeps and try to publish.
989
1010
case <- b .reorgChan :
990
1011
b .state = Open
991
1012
b .Warnf ("reorg detected, batch is able to " +
992
1013
"accept new sweeps" )
993
1014
994
- err := b .monitorSpend (ctx , b .sweeps [b .primarySweepID ])
995
- if err != nil {
996
- return fmt .Errorf ("monitorSpend error: %w" , err )
997
- }
998
-
999
1015
case testReq := <- b .testReqs :
1000
1016
testReq .handler ()
1001
1017
close (testReq .quit )
@@ -1013,6 +1029,41 @@ func (b *batch) Run(ctx context.Context) error {
1013
1029
}
1014
1030
}
1015
1031
1032
+ // updateFeeRate gets fresh values of minFeeRate for sweeps and updates the
1033
+ // feerate of the batch if needed. This method must be called from event loop.
1034
+ func (b * batch ) updateFeeRate (ctx context.Context ) {
1035
+ for outpoint , s := range b .sweeps {
1036
+ minFeeRate , err := minimumSweepFeeRate (
1037
+ ctx , b .cfg .customFeeRate , b .wallet ,
1038
+ s .swapHash , s .outpoint , s .confTarget ,
1039
+ )
1040
+ if err != nil {
1041
+ b .Warnf ("failed to determine feerate for sweep %v of " +
1042
+ "swap %x, confTarget %d: %w" , s .outpoint ,
1043
+ s .swapHash [:6 ], s .confTarget , err )
1044
+ continue
1045
+ }
1046
+
1047
+ if minFeeRate <= s .minFeeRate {
1048
+ continue
1049
+ }
1050
+
1051
+ b .Infof ("Increasing feerate of sweep %v of swap %x from %v " +
1052
+ "to %v" , s .outpoint , s .swapHash [:6 ], s .minFeeRate ,
1053
+ minFeeRate )
1054
+ s .minFeeRate = minFeeRate
1055
+ b .sweeps [outpoint ] = s
1056
+
1057
+ if s .minFeeRate <= b .rbfCache .FeeRate {
1058
+ continue
1059
+ }
1060
+
1061
+ b .Infof ("Increasing feerate of the batch from %v to %v" ,
1062
+ b .rbfCache .FeeRate , s .minFeeRate )
1063
+ b .rbfCache .FeeRate = s .minFeeRate
1064
+ }
1065
+ }
1066
+
1016
1067
// testRunInEventLoop runs a function in the event loop blocking until
1017
1068
// the function returns. For unit tests only!
1018
1069
func (b * batch ) testRunInEventLoop (ctx context.Context , handler func ()) {
@@ -1790,7 +1841,7 @@ func (b *batch) updateRbfRate(ctx context.Context) error {
1790
1841
1791
1842
// Set the initial value for our fee rate.
1792
1843
b .rbfCache .FeeRate = rate
1793
- } else if ! b .cfg .noBumping {
1844
+ } else if noBumping := b .cfg .customFeeRate != nil ; ! noBumping {
1794
1845
if b .rbfCache .SkipNextBump {
1795
1846
// Skip fee bumping, unset the flag, to bump next time.
1796
1847
b .rbfCache .SkipNextBump = false
@@ -1812,44 +1863,33 @@ func (b *batch) updateRbfRate(ctx context.Context) error {
1812
1863
// of the batch transaction gets confirmed, due to the uncertainty of RBF
1813
1864
// replacements and network propagation, we can always detect the transaction.
1814
1865
func (b * batch ) monitorSpend (ctx context.Context , primarySweep sweep ) error {
1815
- spendCtx , cancel := context .WithCancel (ctx )
1866
+ if b .spendChan != nil || b .spendErrChan != nil || b .reorgChan != nil {
1867
+ return fmt .Errorf ("an attempt to run monitorSpend multiple " +
1868
+ "times per batch" )
1869
+ }
1870
+
1871
+ reorgChan := make (chan struct {}, 1 )
1816
1872
1817
- spendChan , spendErr , err := b .chainNotifier .RegisterSpendNtfn (
1818
- spendCtx , & primarySweep .outpoint , primarySweep .htlc .PkScript ,
1873
+ spendChan , spendErrChan , err := b .chainNotifier .RegisterSpendNtfn (
1874
+ ctx , & primarySweep .outpoint , primarySweep .htlc .PkScript ,
1819
1875
primarySweep .initiationHeight ,
1876
+ lndclient .WithReOrgChan (reorgChan ),
1820
1877
)
1821
1878
if err != nil {
1822
- cancel ()
1823
-
1824
- return err
1879
+ return fmt .Errorf ("failed to register spend notifier for " +
1880
+ "primary sweep %v, pkscript %x, height %d: %w" ,
1881
+ primarySweep .outpoint , primarySweep .htlc .PkScript ,
1882
+ primarySweep .initiationHeight , err )
1825
1883
}
1826
1884
1827
- b .wg .Add (1 )
1828
- go func () {
1829
- defer cancel ()
1830
- defer b .wg .Done ()
1831
-
1832
- b .Infof ("monitoring spend for outpoint %s" ,
1833
- primarySweep .outpoint .String ())
1885
+ b .Infof ("monitoring spend for outpoint %s" ,
1886
+ primarySweep .outpoint .String ())
1834
1887
1835
- select {
1836
- case spend := <- spendChan :
1837
- select {
1838
- case b .spendChan <- spend :
1839
-
1840
- case <- ctx .Done ():
1841
- }
1842
-
1843
- case err := <- spendErr :
1844
- b .writeToSpendErrChan (ctx , err )
1845
-
1846
- b .writeToErrChan (
1847
- fmt .Errorf ("spend error: %w" , err ),
1848
- )
1849
-
1850
- case <- ctx .Done ():
1851
- }
1852
- }()
1888
+ // This is safe to do as we always call monitorSpend from the event
1889
+ // loop's goroutine.
1890
+ b .spendChan = spendChan
1891
+ b .spendErrChan = spendErrChan
1892
+ b .reorgChan = reorgChan
1853
1893
1854
1894
return nil
1855
1895
}
@@ -1862,14 +1902,11 @@ func (b *batch) monitorConfirmations(ctx context.Context) error {
1862
1902
return fmt .Errorf ("can't find primarySweep" )
1863
1903
}
1864
1904
1865
- reorgChan := make (chan struct {})
1866
-
1867
1905
confCtx , cancel := context .WithCancel (ctx )
1868
1906
1869
1907
confChan , errChan , err := b .chainNotifier .RegisterConfirmationsNtfn (
1870
1908
confCtx , b .batchTxid , b .batchPkScript , batchConfHeight ,
1871
1909
primarySweep .initiationHeight ,
1872
- lndclient .WithReOrgChan (reorgChan ),
1873
1910
)
1874
1911
if err != nil {
1875
1912
cancel ()
@@ -1895,18 +1932,6 @@ func (b *batch) monitorConfirmations(ctx context.Context) error {
1895
1932
b .writeToErrChan (fmt .Errorf ("confirmations " +
1896
1933
"monitoring error: %w" , err ))
1897
1934
1898
- case <- reorgChan :
1899
- // A re-org has been detected. We set the batch
1900
- // state back to open since our batch
1901
- // transaction is no longer present in any
1902
- // block. We can accept more sweeps and try to
1903
- // publish new transactions, at this point we
1904
- // need to monitor again for a new spend.
1905
- select {
1906
- case b .reorgChan <- struct {}{}:
1907
- case <- ctx .Done ():
1908
- }
1909
-
1910
1935
case <- ctx .Done ():
1911
1936
}
1912
1937
}()
@@ -2395,12 +2420,6 @@ func (b *batch) writeToErrChan(err error) {
2395
2420
2396
2421
// writeToSpendErrChan sends an error to spend error channels of all the sweeps.
2397
2422
func (b * batch ) writeToSpendErrChan (ctx context.Context , spendErr error ) {
2398
- done , err := b .scheduleNextCall ()
2399
- if err != nil {
2400
- done ()
2401
-
2402
- return
2403
- }
2404
2423
notifiers := make ([]* SpendNotifier , 0 , len (b .sweeps ))
2405
2424
for _ , s := range b .sweeps {
2406
2425
// If the sweep's notifier is empty then this means that a swap
@@ -2412,7 +2431,6 @@ func (b *batch) writeToSpendErrChan(ctx context.Context, spendErr error) {
2412
2431
2413
2432
notifiers = append (notifiers , s .notifier )
2414
2433
}
2415
- done ()
2416
2434
2417
2435
for _ , notifier := range notifiers {
2418
2436
select {
0 commit comments