@@ -19,6 +19,7 @@ import (
1919 "github.com/lightninglabs/loop/loopdb"
2020 "github.com/lightninglabs/loop/test"
2121 "github.com/lightninglabs/loop/utils"
22+ "github.com/lightningnetwork/lnd/build"
2223 "github.com/lightningnetwork/lnd/chainntnfs"
2324 "github.com/lightningnetwork/lnd/clock"
2425 "github.com/lightningnetwork/lnd/input"
@@ -1176,6 +1177,161 @@ func testDelays(t *testing.T, store testStore, batcherStore testBatcherStore) {
11761177 checkBatcherError (t , runErr )
11771178}
11781179
1180+ // testMaxSweepsPerBatch tests the limit on max number of sweeps per batch.
1181+ func testMaxSweepsPerBatch (t * testing.T , store testStore ,
1182+ batcherStore testBatcherStore ) {
1183+
1184+ // Disable logging, because this test is very noisy.
1185+ oldLogger := log
1186+ UseLogger (build .NewSubLogger ("SWEEP" , nil ))
1187+ defer UseLogger (oldLogger )
1188+
1189+ defer test .Guard (t , test .WithGuardTimeout (5 * time .Minute ))()
1190+
1191+ lnd := test .NewMockLnd ()
1192+ ctx , cancel := context .WithCancel (context .Background ())
1193+
1194+ sweepStore , err := NewSweepFetcherFromSwapStore (store , lnd .ChainParams )
1195+ require .NoError (t , err )
1196+
1197+ startTime := time .Date (2018 , 11 , 1 , 0 , 0 , 0 , 0 , time .UTC )
1198+ testClock := clock .NewTestClock (startTime )
1199+
1200+ // Create muSig2SignSweep failing all sweeps to force non-cooperative
1201+ // scenario (it increases transaction size).
1202+ muSig2SignSweep := func (ctx context.Context ,
1203+ protocolVersion loopdb.ProtocolVersion , swapHash lntypes.Hash ,
1204+ paymentAddr [32 ]byte , nonce []byte , sweepTxPsbt []byte ,
1205+ prevoutMap map [wire.OutPoint ]* wire.TxOut ) (
1206+ []byte , []byte , error ) {
1207+
1208+ return nil , nil , fmt .Errorf ("test error" )
1209+ }
1210+
1211+ // Set publish delay.
1212+ const publishDelay = 3 * time .Second
1213+
1214+ batcher := NewBatcher (
1215+ lnd .WalletKit , lnd .ChainNotifier , lnd .Signer ,
1216+ muSig2SignSweep , testVerifySchnorrSig , lnd .ChainParams ,
1217+ batcherStore , sweepStore , WithPublishDelay (publishDelay ),
1218+ WithClock (testClock ),
1219+ )
1220+
1221+ var wg sync.WaitGroup
1222+ wg .Add (1 )
1223+
1224+ var runErr error
1225+ go func () {
1226+ defer wg .Done ()
1227+ runErr = batcher .Run (ctx )
1228+ }()
1229+
1230+ // Wait for the batcher to be initialized.
1231+ <- batcher .initDone
1232+
1233+ const swapsNum = MaxSweepsPerBatch + 1
1234+
1235+ // Expect 2 batches to be registered.
1236+ expectedBatches := (swapsNum + MaxSweepsPerBatch - 1 ) /
1237+ MaxSweepsPerBatch
1238+
1239+ for i := 0 ; i < swapsNum ; i ++ {
1240+ preimage := lntypes.Preimage {2 , byte (i % 256 ), byte (i / 256 )}
1241+ swapHash := preimage .Hash ()
1242+
1243+ // Create a sweep request.
1244+ sweepReq := SweepRequest {
1245+ SwapHash : swapHash ,
1246+ Value : 111 ,
1247+ Outpoint : wire.OutPoint {
1248+ Hash : chainhash.Hash {1 , 1 },
1249+ Index : 1 ,
1250+ },
1251+ Notifier : & dummyNotifier ,
1252+ }
1253+
1254+ swap := & loopdb.LoopOutContract {
1255+ SwapContract : loopdb.SwapContract {
1256+ CltvExpiry : 1000 ,
1257+ AmountRequested : 111 ,
1258+ ProtocolVersion : loopdb .ProtocolVersionMuSig2 ,
1259+ HtlcKeys : htlcKeys ,
1260+
1261+ // Make preimage unique to pass SQL constraints.
1262+ Preimage : preimage ,
1263+ },
1264+
1265+ DestAddr : destAddr ,
1266+ SwapInvoice : swapInvoice ,
1267+ SweepConfTarget : 123 ,
1268+ }
1269+
1270+ err = store .CreateLoopOut (ctx , swapHash , swap )
1271+ require .NoError (t , err )
1272+ store .AssertLoopOutStored ()
1273+
1274+ // Deliver sweep request to batcher.
1275+ require .NoError (t , batcher .AddSweep (& sweepReq ))
1276+
1277+ // If this is new batch, expect a spend registration.
1278+ if i % MaxSweepsPerBatch == 0 {
1279+ <- lnd .RegisterSpendChannel
1280+ }
1281+ }
1282+
1283+ // Eventually the batches are launched and all the sweeps are added.
1284+ require .Eventually (t , func () bool {
1285+ // Make sure all the batches have started.
1286+ if len (batcher .batches ) != expectedBatches {
1287+ return false
1288+ }
1289+
1290+ // Make sure all the sweeps were added.
1291+ sweepsNum := 0
1292+ for _ , batch := range batcher .batches {
1293+ sweepsNum += len (batch .sweeps )
1294+ }
1295+ return sweepsNum == swapsNum
1296+ }, test .Timeout , eventuallyCheckFrequency )
1297+
1298+ // Advance the clock to publishDelay, so batches are published.
1299+ now := startTime .Add (publishDelay )
1300+ testClock .SetTime (now )
1301+
1302+ // Expect mockSigner.SignOutputRaw calls to sign non-cooperative
1303+ // sweeps.
1304+ for i := 0 ; i < expectedBatches ; i ++ {
1305+ <- lnd .SignOutputRawChannel
1306+ }
1307+
1308+ // Wait for txs to be published.
1309+ inputsNum := 0
1310+ const maxWeight = lntypes .WeightUnit (400_000 )
1311+ for i := 0 ; i < expectedBatches ; i ++ {
1312+ tx := <- lnd .TxPublishChannel
1313+ inputsNum += len (tx .TxIn )
1314+
1315+ // Make sure the transaction size is standard.
1316+ weight := lntypes .WeightUnit (
1317+ blockchain .GetTransactionWeight (btcutil .NewTx (tx )),
1318+ )
1319+ require .Less (t , weight , maxWeight )
1320+ t .Logf ("tx weight: %v" , weight )
1321+ }
1322+
1323+ // Make sure the number of inputs in batch transactions is equal
1324+ // to the number of swaps.
1325+ require .Equal (t , swapsNum , inputsNum )
1326+
1327+ // Now make the batcher quit by canceling the context.
1328+ cancel ()
1329+ wg .Wait ()
1330+
1331+ // Make sure the batcher exited without an error.
1332+ checkBatcherError (t , runErr )
1333+ }
1334+
11791335// testSweepBatcherSweepReentry tests that when an old version of the batch tx
11801336// gets confirmed the sweep leftovers are sent back to the batcher.
11811337func testSweepBatcherSweepReentry (t * testing.T , store testStore ,
@@ -3468,6 +3624,11 @@ func TestDelays(t *testing.T) {
34683624 runTests (t , testDelays )
34693625}
34703626
3627+ // TestMaxSweepsPerBatch tests the limit on max number of sweeps per batch.
3628+ func TestMaxSweepsPerBatch (t * testing.T ) {
3629+ runTests (t , testMaxSweepsPerBatch )
3630+ }
3631+
34713632// TestSweepBatcherSweepReentry tests that when an old version of the batch tx
34723633// gets confirmed the sweep leftovers are sent back to the batcher.
34733634func TestSweepBatcherSweepReentry (t * testing.T ) {
0 commit comments