@@ -3698,6 +3698,179 @@ func testWithMixedBatchCoopFailedOnly(t *testing.T, store testStore,
36983698 wantWeight , wantWitnessSizes )
36993699}
37003700
3701+ // testFeeRateGrows tests that fee rate of a batch does not decrease and is at
3702+ // least as high as the highest fee rate of sweeps.
3703+ func testFeeRateGrows (t * testing.T , store testStore ,
3704+ batcherStore testBatcherStore ) {
3705+
3706+ defer test .Guard (t )()
3707+
3708+ lnd := test .NewMockLnd ()
3709+ ctx , cancel := context .WithCancel (context .Background ())
3710+ defer cancel ()
3711+
3712+ sweepStore , err := NewSweepFetcherFromSwapStore (store , lnd .ChainParams )
3713+ require .NoError (t , err )
3714+
3715+ // Create a map to store fee rates.
3716+ swap2feeRate := map [lntypes.Hash ]chainfee.SatPerKWeight {}
3717+ var swap2feeRateMu sync.Mutex
3718+ setFeeRate := func (swapHash lntypes.Hash , rate chainfee.SatPerKWeight ) {
3719+ swap2feeRateMu .Lock ()
3720+ defer swap2feeRateMu .Unlock ()
3721+
3722+ swap2feeRate [swapHash ] = rate
3723+ }
3724+
3725+ customFeeRate := func (ctx context.Context ,
3726+ swapHash lntypes.Hash ) (chainfee.SatPerKWeight , error ) {
3727+
3728+ swap2feeRateMu .Lock ()
3729+ defer swap2feeRateMu .Unlock ()
3730+
3731+ return swap2feeRate [swapHash ], nil
3732+ }
3733+
3734+ const (
3735+ feeRateLow = chainfee .SatPerKWeight (10_000 )
3736+ feeRateMedium = chainfee .SatPerKWeight (30_000 )
3737+ feeRateHigh = chainfee .SatPerKWeight (50_000 )
3738+ )
3739+
3740+ batcher := NewBatcher (lnd .WalletKit , lnd .ChainNotifier , lnd .Signer ,
3741+ testMuSig2SignSweep , testVerifySchnorrSig , lnd .ChainParams ,
3742+ batcherStore , sweepStore , WithCustomFeeRate (customFeeRate ))
3743+ go func () {
3744+ err := batcher .Run (ctx )
3745+ checkBatcherError (t , err )
3746+ }()
3747+
3748+ // Create the first sweep.
3749+ swapHash1 := lntypes.Hash {1 , 1 , 1 }
3750+ setFeeRate (swapHash1 , feeRateMedium )
3751+ sweepReq1 := SweepRequest {
3752+ SwapHash : swapHash1 ,
3753+ Value : 1_000_000 ,
3754+ Outpoint : wire.OutPoint {
3755+ Hash : chainhash.Hash {1 , 1 },
3756+ Index : 1 ,
3757+ },
3758+ Notifier : & dummyNotifier ,
3759+ }
3760+
3761+ swap1 := & loopdb.LoopOutContract {
3762+ SwapContract : loopdb.SwapContract {
3763+ CltvExpiry : 111 ,
3764+ AmountRequested : 1_000_000 ,
3765+ ProtocolVersion : loopdb .ProtocolVersionMuSig2 ,
3766+ HtlcKeys : htlcKeys ,
3767+
3768+ // Make preimage unique to pass SQL constraints.
3769+ Preimage : lntypes.Preimage {1 },
3770+ },
3771+
3772+ DestAddr : destAddr ,
3773+ SwapInvoice : swapInvoice ,
3774+ SweepConfTarget : 111 ,
3775+ }
3776+
3777+ err = store .CreateLoopOut (ctx , swapHash1 , swap1 )
3778+ require .NoError (t , err )
3779+ store .AssertLoopOutStored ()
3780+
3781+ // Deliver sweep request to batcher.
3782+ require .NoError (t , batcher .AddSweep (& sweepReq1 ))
3783+
3784+ // Since a batch was created we check that it registered for its primary
3785+ // sweep's spend.
3786+ <- lnd .RegisterSpendChannel
3787+
3788+ // Wait for tx to be published.
3789+ <- lnd .TxPublishChannel
3790+
3791+ // Make sure the fee rate is feeRateMedium.
3792+ batch := getOnlyBatch (batcher )
3793+ require .Len (t , batch .sweeps , 1 )
3794+ require .Equal (t , feeRateMedium , batch .rbfCache .FeeRate )
3795+
3796+ // Now decreate the fee of sweep1.
3797+ setFeeRate (swapHash1 , feeRateLow )
3798+ require .NoError (t , batcher .AddSweep (& sweepReq1 ))
3799+
3800+ // Tick tock next block.
3801+ err = lnd .NotifyHeight (601 )
3802+ require .NoError (t , err )
3803+
3804+ // Wait for tx to be published.
3805+ <- lnd .TxPublishChannel
3806+
3807+ // Make sure the fee rate is still feeRateMedium.
3808+ require .Equal (t , feeRateMedium , batch .rbfCache .FeeRate )
3809+
3810+ // Add sweep2, with feeRateMedium.
3811+ swapHash2 := lntypes.Hash {2 , 2 , 2 }
3812+ setFeeRate (swapHash2 , feeRateMedium )
3813+ sweepReq2 := SweepRequest {
3814+ SwapHash : swapHash2 ,
3815+ Value : 1_000_000 ,
3816+ Outpoint : wire.OutPoint {
3817+ Hash : chainhash.Hash {2 , 2 },
3818+ Index : 1 ,
3819+ },
3820+ Notifier : & dummyNotifier ,
3821+ }
3822+
3823+ swap2 := & loopdb.LoopOutContract {
3824+ SwapContract : loopdb.SwapContract {
3825+ CltvExpiry : 111 ,
3826+ AmountRequested : 1_000_000 ,
3827+ ProtocolVersion : loopdb .ProtocolVersionMuSig2 ,
3828+ HtlcKeys : htlcKeys ,
3829+
3830+ // Make preimage unique to pass SQL constraints.
3831+ Preimage : lntypes.Preimage {2 },
3832+ },
3833+
3834+ DestAddr : destAddr ,
3835+ SwapInvoice : swapInvoice ,
3836+ SweepConfTarget : 111 ,
3837+ }
3838+
3839+ err = store .CreateLoopOut (ctx , swapHash2 , swap2 )
3840+ require .NoError (t , err )
3841+ store .AssertLoopOutStored ()
3842+
3843+ // Deliver sweep request to batcher.
3844+ require .NoError (t , batcher .AddSweep (& sweepReq2 ))
3845+
3846+ // Tick tock next block.
3847+ err = lnd .NotifyHeight (602 )
3848+ require .NoError (t , err )
3849+
3850+ // Wait for tx to be published.
3851+ <- lnd .TxPublishChannel
3852+
3853+ // Make sure the fee rate is still feeRateMedium.
3854+ require .Len (t , batch .sweeps , 2 )
3855+ require .Equal (t , feeRateMedium , batch .rbfCache .FeeRate )
3856+
3857+ // Now update fee rate of second sweep (which is not promary) to
3858+ // feeRateHigh. Fee rate of sweep 1 is still feeRateLow.
3859+ setFeeRate (swapHash2 , feeRateHigh )
3860+ require .NoError (t , batcher .AddSweep (& sweepReq1 ))
3861+ require .NoError (t , batcher .AddSweep (& sweepReq2 ))
3862+
3863+ // Tick tock next block.
3864+ err = lnd .NotifyHeight (603 )
3865+ require .NoError (t , err )
3866+
3867+ // Wait for tx to be published.
3868+ <- lnd .TxPublishChannel
3869+
3870+ // Make sure the fee rate increased to feeRateHigh.
3871+ require .Equal (t , feeRateHigh , batch .rbfCache .FeeRate )
3872+ }
3873+
37013874// TestSweepBatcherBatchCreation tests that sweep requests enter the expected
37023875// batch based on their timeout distance.
37033876func TestSweepBatcherBatchCreation (t * testing.T ) {
@@ -3839,6 +4012,12 @@ func TestWithMixedBatchCoopFailedOnly(t *testing.T) {
38394012 runTests (t , testWithMixedBatchCoopFailedOnly )
38404013}
38414014
4015+ // TestFeeRateGrows tests that fee rate of a batch does not decrease and is at
4016+ // least as high as the highest fee rate of sweeps.
4017+ func TestFeeRateGrows (t * testing.T ) {
4018+ runTests (t , testFeeRateGrows )
4019+ }
4020+
38424021// testBatcherStore is BatcherStore used in tests.
38434022type testBatcherStore interface {
38444023 BatcherStore
0 commit comments