|
1 | 1 | package sweepbatcher |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "bytes" |
4 | 5 | "context" |
5 | 6 | "fmt" |
6 | 7 | "os" |
@@ -1268,6 +1269,147 @@ func testPresigned_presigned_group_with_change(t *testing.T, |
1268 | 1269 | require.NoError(t, lnd.NotifyHeight(601)) |
1269 | 1270 | } |
1270 | 1271 |
|
| 1272 | +// testPresigned_fee_portion_with_change ensures that the fee portion reported |
| 1273 | +// to clients accounts for change outputs in the presigned transaction. |
| 1274 | +func testPresigned_fee_portion_with_change(t *testing.T, |
| 1275 | + batcherStore testBatcherStore) { |
| 1276 | + |
| 1277 | + defer test.Guard(t)() |
| 1278 | + |
| 1279 | + lnd := test.NewMockLnd() |
| 1280 | + |
| 1281 | + ctx, cancel := context.WithCancel(context.Background()) |
| 1282 | + defer cancel() |
| 1283 | + |
| 1284 | + customFeeRate := func(_ context.Context, _ lntypes.Hash, |
| 1285 | + _ wire.OutPoint) (chainfee.SatPerKWeight, error) { |
| 1286 | + |
| 1287 | + return chainfee.SatPerKWeight(10_000), nil |
| 1288 | + } |
| 1289 | + |
| 1290 | + presignedHelper := newMockPresignedHelper() |
| 1291 | + |
| 1292 | + batcher := NewBatcher( |
| 1293 | + lnd.WalletKit, lnd.ChainNotifier, lnd.Signer, |
| 1294 | + testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams, |
| 1295 | + batcherStore, presignedHelper, |
| 1296 | + WithCustomFeeRate(customFeeRate), |
| 1297 | + WithPresignedHelper(presignedHelper), |
| 1298 | + ) |
| 1299 | + |
| 1300 | + go func() { |
| 1301 | + err := batcher.Run(ctx) |
| 1302 | + checkBatcherError(t, err) |
| 1303 | + }() |
| 1304 | + |
| 1305 | + swapHash := lntypes.Hash{2, 2, 2} |
| 1306 | + op := wire.OutPoint{ |
| 1307 | + Hash: chainhash.Hash{2, 2}, |
| 1308 | + Index: 2, |
| 1309 | + } |
| 1310 | + group := []Input{ |
| 1311 | + { |
| 1312 | + Outpoint: op, |
| 1313 | + Value: 1_000_000, |
| 1314 | + }, |
| 1315 | + } |
| 1316 | + change := &wire.TxOut{ |
| 1317 | + Value: 250_000, |
| 1318 | + PkScript: []byte{0xca, 0xfe}, |
| 1319 | + } |
| 1320 | + |
| 1321 | + presignedHelper.setChangeForPrimaryDeposit(op, change) |
| 1322 | + presignedHelper.SetOutpointOnline(op, true) |
| 1323 | + |
| 1324 | + require.NoError(t, batcher.PresignSweepsGroup( |
| 1325 | + ctx, group, sweepTimeout, destAddr, change, |
| 1326 | + )) |
| 1327 | + |
| 1328 | + spendChan := make(chan *SpendDetail, 1) |
| 1329 | + confChan := make(chan *ConfDetail, 1) |
| 1330 | + notifier := &SpendNotifier{ |
| 1331 | + SpendChan: spendChan, |
| 1332 | + SpendErrChan: make(chan error, 1), |
| 1333 | + ConfChan: confChan, |
| 1334 | + ConfErrChan: make(chan error, 1), |
| 1335 | + QuitChan: make(chan bool, 1), |
| 1336 | + } |
| 1337 | + |
| 1338 | + require.NoError(t, batcher.AddSweep(ctx, &SweepRequest{ |
| 1339 | + SwapHash: swapHash, |
| 1340 | + Inputs: group, |
| 1341 | + Notifier: notifier, |
| 1342 | + })) |
| 1343 | + |
| 1344 | + spendReg := <-lnd.RegisterSpendChannel |
| 1345 | + require.NotNil(t, spendReg) |
| 1346 | + require.NotNil(t, spendReg.Outpoint) |
| 1347 | + require.Equal(t, op, *spendReg.Outpoint) |
| 1348 | + |
| 1349 | + tx := <-lnd.TxPublishChannel |
| 1350 | + require.Len(t, tx.TxIn, 1) |
| 1351 | + require.Len(t, tx.TxOut, 2) |
| 1352 | + |
| 1353 | + var ( |
| 1354 | + outputSum int64 |
| 1355 | + foundChange bool |
| 1356 | + ) |
| 1357 | + for _, txOut := range tx.TxOut { |
| 1358 | + outputSum += txOut.Value |
| 1359 | + if txOut.Value != change.Value { |
| 1360 | + continue |
| 1361 | + } |
| 1362 | + |
| 1363 | + if !bytes.Equal(txOut.PkScript, change.PkScript) { |
| 1364 | + continue |
| 1365 | + } |
| 1366 | + |
| 1367 | + foundChange = true |
| 1368 | + } |
| 1369 | + |
| 1370 | + require.True(t, foundChange) |
| 1371 | + |
| 1372 | + totalInput := int64(group[0].Value) |
| 1373 | + require.LessOrEqual(t, outputSum, totalInput) |
| 1374 | + |
| 1375 | + expectedFee := btcutil.Amount(totalInput - outputSum) |
| 1376 | + require.Greater(t, expectedFee, btcutil.Amount(0)) |
| 1377 | + |
| 1378 | + txHash := tx.TxHash() |
| 1379 | + spendDetail := &chainntnfs.SpendDetail{ |
| 1380 | + SpentOutPoint: &op, |
| 1381 | + SpendingTx: tx, |
| 1382 | + SpenderTxHash: &txHash, |
| 1383 | + SpenderInputIndex: 0, |
| 1384 | + SpendingHeight: spendReg.HeightHint + 1, |
| 1385 | + } |
| 1386 | + lnd.SpendChannel <- spendDetail |
| 1387 | + |
| 1388 | + spend := <-spendChan |
| 1389 | + require.Equal(t, expectedFee, spend.OnChainFeePortion) |
| 1390 | + |
| 1391 | + confReg := <-lnd.RegisterConfChannel |
| 1392 | + require.True(t, bytes.Equal(tx.TxOut[0].PkScript, confReg.PkScript) || |
| 1393 | + bytes.Equal(tx.TxOut[1].PkScript, confReg.PkScript)) |
| 1394 | + |
| 1395 | + require.NoError( |
| 1396 | + t, lnd.NotifyHeight(spendReg.HeightHint+batchConfHeight+1), |
| 1397 | + ) |
| 1398 | + lnd.ConfChannel <- &chainntnfs.TxConfirmation{Tx: tx} |
| 1399 | + |
| 1400 | + require.Eventually(t, func() bool { |
| 1401 | + select { |
| 1402 | + case <-presignedHelper.cleanupCalled: |
| 1403 | + return true |
| 1404 | + default: |
| 1405 | + return false |
| 1406 | + } |
| 1407 | + }, test.Timeout, eventuallyCheckFrequency) |
| 1408 | + |
| 1409 | + conf := <-confChan |
| 1410 | + require.Equal(t, expectedFee, conf.OnChainFeePortion) |
| 1411 | +} |
| 1412 | + |
1271 | 1413 | // testPresigned_presigned_group_with_identical_change_pkscript tests passing multiple sweeps to |
1272 | 1414 | // the method PresignSweepsGroup. It tests that a change output of a primary |
1273 | 1415 | // deposit sweep is properly added to the presigned transaction. |
@@ -2356,6 +2498,10 @@ func TestPresigned(t *testing.T) { |
2356 | 2498 | testPresigned_presigned_group_with_change(t, NewStoreMock()) |
2357 | 2499 | }) |
2358 | 2500 |
|
| 2501 | + t.Run("fee_portion_change", func(t *testing.T) { |
| 2502 | + testPresigned_fee_portion_with_change(t, NewStoreMock()) |
| 2503 | + }) |
| 2504 | + |
2359 | 2505 | t.Run("identical change pkscript", func(t *testing.T) { |
2360 | 2506 | testPresigned_presigned_group_with_identical_change_pkscript(t, NewStoreMock()) |
2361 | 2507 | }) |
|
0 commit comments