@@ -159,6 +159,11 @@ func (h *mockPresignedHelper) SignTx(ctx context.Context,
159159 h .mu .Lock ()
160160 defer h .mu .Unlock ()
161161
162+ if feeRate < minRelayFee {
163+ return nil , fmt .Errorf ("feeRate (%v) is below minRelayFee (%v)" ,
164+ feeRate , minRelayFee )
165+ }
166+
162167 // If all the inputs are online and loadOnly is not set, sign this exact
163168 // transaction.
164169 if offline := h .offlineInputs (tx ); len (offline ) == 0 && ! loadOnly {
@@ -492,6 +497,118 @@ func testPresigned_input1_offline_then_input2(t *testing.T,
492497 require .NoError (t , err )
493498}
494499
500+ // testPresigned_min_relay_fee tests that online and presigned transactions
501+ // comply with min_relay_fee.
502+ func testPresigned_min_relay_fee (t * testing.T ,
503+ batcherStore testBatcherStore ) {
504+
505+ defer test .Guard (t )()
506+
507+ lnd := test .NewMockLnd ()
508+ ctx , cancel := context .WithCancel (context .Background ())
509+ defer cancel ()
510+
511+ const inputAmt = 1_000_000
512+
513+ customFeeRate := func (_ context.Context , _ lntypes.Hash ,
514+ _ wire.OutPoint ) (chainfee.SatPerKWeight , error ) {
515+
516+ return chainfee .FeePerKwFloor , nil
517+ }
518+
519+ presignedHelper := newMockPresignedHelper ()
520+
521+ batcher := NewBatcher (lnd .WalletKit , lnd .ChainNotifier , lnd .Signer ,
522+ testMuSig2SignSweep , testVerifySchnorrSig , lnd .ChainParams ,
523+ batcherStore , presignedHelper ,
524+ WithCustomFeeRate (customFeeRate ),
525+ WithPresignedHelper (presignedHelper ))
526+ go func () {
527+ err := batcher .Run (ctx )
528+ checkBatcherError (t , err )
529+ }()
530+
531+ // Set high min_relay_fee.
532+ lnd .SetMinRelayFee (400 )
533+
534+ // Create the first sweep.
535+ swapHash1 := lntypes.Hash {1 , 1 , 1 }
536+ op1 := wire.OutPoint {
537+ Hash : chainhash.Hash {1 , 1 },
538+ Index : 1 ,
539+ }
540+ sweepReq1 := SweepRequest {
541+ SwapHash : swapHash1 ,
542+ Inputs : []Input {{
543+ Value : inputAmt ,
544+ Outpoint : op1 ,
545+ }},
546+ Notifier : & dummyNotifier ,
547+ }
548+
549+ // Enable the input and presign.
550+ presignedHelper .SetOutpointOnline (op1 , true )
551+ err := batcher .PresignSweepsGroup (
552+ ctx , []Input {{Outpoint : op1 , Value : inputAmt }},
553+ sweepTimeout , destAddr ,
554+ )
555+ require .NoError (t , err )
556+
557+ // Deliver sweep request to batcher.
558+ require .NoError (t , batcher .AddSweep (ctx , & sweepReq1 ))
559+
560+ // Since a batch was created we check that it registered for its primary
561+ // sweep's spend.
562+ <- lnd .RegisterSpendChannel
563+
564+ // Wait for a transactions to be published.
565+ tx := <- lnd .TxPublishChannel
566+ gotFeeRate := presignedHelper .getTxFeerate (tx , inputAmt )
567+ require .Equal (t , chainfee .SatPerKWeight (402 ), gotFeeRate )
568+
569+ // Now decrease min_relay_fee and make sure fee rate doesn't decrease.
570+ // The only difference of tx2 is a higher lock_time.
571+ lnd .SetMinRelayFee (300 )
572+ require .NoError (t , lnd .NotifyHeight (601 ))
573+ tx2 := <- lnd .TxPublishChannel
574+ require .Equal (t , tx .TxOut [0 ].Value , tx2 .TxOut [0 ].Value )
575+ gotFeeRate = presignedHelper .getTxFeerate (tx2 , inputAmt )
576+ require .Equal (t , chainfee .SatPerKWeight (402 ), gotFeeRate )
577+ require .Equal (t , uint32 (601 ), tx2 .LockTime )
578+
579+ // Set a higher min_relay_fee, turn off the client and try presigned tx.
580+ lnd .SetMinRelayFee (500 )
581+ presignedHelper .SetOutpointOnline (op1 , false )
582+
583+ // Check fee rate of the presigned tx broadcasted.
584+ require .NoError (t , lnd .NotifyHeight (602 ))
585+ tx = <- lnd .TxPublishChannel
586+ gotFeeRate = presignedHelper .getTxFeerate (tx , inputAmt )
587+ require .Equal (t , chainfee .SatPerKWeight (523 ), gotFeeRate )
588+ // LockTime of a presigned tx is 0.
589+ require .Equal (t , uint32 (0 ), tx .LockTime )
590+
591+ // Now decrease min_relay_fee and make sure fee rate doesn't decrease.
592+ // It should re-broadcast the same presigned tx.
593+ lnd .SetMinRelayFee (450 )
594+ require .NoError (t , lnd .NotifyHeight (603 ))
595+ tx2 = <- lnd .TxPublishChannel
596+ require .Equal (t , tx .TxHash (), tx2 .TxHash ())
597+ gotFeeRate = presignedHelper .getTxFeerate (tx2 , inputAmt )
598+ require .Equal (t , chainfee .SatPerKWeight (523 ), gotFeeRate )
599+ // LockTime of a presigned tx is 0.
600+ require .Equal (t , uint32 (0 ), tx2 .LockTime )
601+
602+ // Even if the client is back online, fee rate doesn't decrease.
603+ presignedHelper .SetOutpointOnline (op1 , true )
604+ require .NoError (t , lnd .NotifyHeight (604 ))
605+ tx3 := <- lnd .TxPublishChannel
606+ require .Equal (t , tx2 .TxOut [0 ].Value , tx3 .TxOut [0 ].Value )
607+ gotFeeRate = presignedHelper .getTxFeerate (tx3 , inputAmt )
608+ require .Equal (t , chainfee .SatPerKWeight (523 ), gotFeeRate )
609+ require .Equal (t , uint32 (604 ), tx3 .LockTime )
610+ }
611+
495612// testPresigned_two_inputs_one_goes_offline tests presigned mode for the
496613// following scenario: two online inputs are added, then one of them goes
497614// offline, then feerate grows and a presigned transaction is used.
@@ -1692,6 +1809,10 @@ func TestPresigned(t *testing.T) {
16921809 testPresigned_input1_offline_then_input2 (t , NewStoreMock ())
16931810 })
16941811
1812+ t .Run ("min_relay_fee" , func (t * testing.T ) {
1813+ testPresigned_min_relay_fee (t , NewStoreMock ())
1814+ })
1815+
16951816 t .Run ("two_inputs_one_goes_offline" , func (t * testing.T ) {
16961817 testPresigned_two_inputs_one_goes_offline (t , NewStoreMock ())
16971818 })
0 commit comments