sweepbatcher: consider change in presigning and batch tx

Presigning sweeps takes change outputs into account.
Each primary deposit id of a sweep group points to
an optional change output.
sweepbatcher.presign scans all passed sweeps for
change outputs and passes them to constructUnsignedTx.
Optional change of a swap is encoded in its sweeps
as a pointer to the same change output. This change
is taken into account when constructing the unsigned
batch transaction when it comes to tx weight and
outputs.
This commit is contained in:
Slyghtning 2025-07-18 11:07:40 +02:00
parent 8399a63091
commit bc7d155e69
No known key found for this signature in database
GPG key ID: F82D456EA023C9BF
7 changed files with 901 additions and 106 deletions

View file

@ -51,6 +51,7 @@ func ensurePresigned(ctx context.Context, newSweeps []*sweep,
outpoint: s.outpoint,
value: s.value,
presigned: s.presigned,
change: s.change,
}
}
@ -493,10 +494,12 @@ func (b *batch) publishPresigned(ctx context.Context) (btcutil.Amount, error,
signedFeeRate := chainfee.NewSatPerKWeight(fee, realWeight)
numSweeps := len(tx.TxIn)
numChange := len(tx.TxOut) - 1
b.Infof("attempting to publish custom signed tx=%v, desiredFeerate=%v,"+
" signedFeeRate=%v, weight=%v, fee=%v, sweeps=%d, destAddr=%s",
" signedFeeRate=%v, weight=%v, fee=%v, sweeps=%d, "+
"changeOutputs=%d, destAddr=%s",
txHash, feeRate, signedFeeRate, realWeight, fee, numSweeps,
address)
numChange, address)
b.debugLogTx("serialized batch", tx)
// Publish the transaction.
@ -593,23 +596,31 @@ func CheckSignedTx(unsignedTx, signedTx *wire.MsgTx, inputAmt btcutil.Amount,
}
// Compare outputs.
if len(unsignedTx.TxOut) != 1 {
return fmt.Errorf("unsigned tx has %d outputs, want 1",
len(unsignedTx.TxOut))
}
if len(signedTx.TxOut) != 1 {
return fmt.Errorf("the signed tx has %d outputs, want 1",
if len(unsignedTx.TxOut) != len(signedTx.TxOut) {
return fmt.Errorf("unsigned tx has %d outputs, signed tx has "+
"%d outputs, should be equal", len(unsignedTx.TxOut),
len(signedTx.TxOut))
}
unsignedOut := unsignedTx.TxOut[0]
signedOut := signedTx.TxOut[0]
if !bytes.Equal(unsignedOut.PkScript, signedOut.PkScript) {
return fmt.Errorf("mismatch of output pkScript: %x, %x",
unsignedOut.PkScript, signedOut.PkScript)
for i, o := range unsignedTx.TxOut {
if !bytes.Equal(o.PkScript, signedTx.TxOut[i].PkScript) {
return fmt.Errorf("mismatch of output pkScript: %x, %x",
o.PkScript, signedTx.TxOut[i].PkScript)
}
if i != 0 && o.Value != signedTx.TxOut[i].Value {
return fmt.Errorf("mismatch of output value: %d, %d",
o.Value, signedTx.TxOut[i].Value)
}
}
// Calculate the total value of all outputs to help determine the
// transaction fee.
totalOutputValue := btcutil.Amount(0)
for _, o := range signedTx.TxOut {
totalOutputValue += btcutil.Amount(o.Value)
}
// Find the feerate of signedTx.
fee := inputAmt - btcutil.Amount(signedOut.Value)
fee := inputAmt - totalOutputValue
weight := lntypes.WeightUnit(
blockchain.GetTransactionWeight(btcutil.NewTx(signedTx)),
)

View file

@ -1460,7 +1460,8 @@ func TestCheckSignedTx(t *testing.T) {
},
inputAmt: 3_000_000,
minRelayFee: 253,
wantErr: "unsigned tx has 2 outputs, want 1",
wantErr: "unsigned tx has 2 outputs, signed tx " +
"has 1 outputs, should be equal",
},
{
@ -1517,7 +1518,153 @@ func TestCheckSignedTx(t *testing.T) {
},
inputAmt: 3_000_000,
minRelayFee: 253,
wantErr: "the signed tx has 2 outputs, want 1",
wantErr: "unsigned tx has 1 outputs, signed tx " +
"has 2 outputs, should be equal",
},
{
name: "pkscript mismatch",
unsignedTx: &wire.MsgTx{
Version: 2,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: op2,
Sequence: 2,
},
},
TxOut: []*wire.TxOut{
{
Value: 2999374,
PkScript: batchPkScript,
},
},
LockTime: 800_000,
},
signedTx: &wire.MsgTx{
Version: 2,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: op2,
Sequence: 2,
Witness: wire.TxWitness{
[]byte("test"),
},
},
},
TxOut: []*wire.TxOut{
{
Value: 2999374,
PkScript: []byte{0xaf, 0xfe}, // Just to make it different.
},
},
LockTime: 799_999,
},
inputAmt: 3_000_000,
minRelayFee: 253,
wantErr: "mismatch of output pkScript",
},
{
name: "value mismatch, first output",
unsignedTx: &wire.MsgTx{
Version: 2,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: op2,
Sequence: 2,
},
},
TxOut: []*wire.TxOut{
{
Value: 2999374,
PkScript: batchPkScript,
},
},
LockTime: 800_000,
},
signedTx: &wire.MsgTx{
Version: 2,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: op2,
Sequence: 2,
Witness: wire.TxWitness{
[]byte("test"),
},
},
},
TxOut: []*wire.TxOut{
{
Value: 1_337_000, // Just to make it different.
PkScript: batchPkScript,
},
},
LockTime: 799_999,
},
inputAmt: 3_000_000,
minRelayFee: 253,
wantErr: "",
},
{
name: "value mismatch, change output",
unsignedTx: &wire.MsgTx{
Version: 2,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: op2,
Sequence: 2,
},
{
PreviousOutPoint: op1,
Sequence: 2,
},
},
TxOut: []*wire.TxOut{
{
Value: 2999374,
PkScript: batchPkScript,
},
{
Value: 1_337_000,
PkScript: batchPkScript,
},
},
LockTime: 800_000,
},
signedTx: &wire.MsgTx{
Version: 2,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: op2,
Sequence: 2,
Witness: wire.TxWitness{
[]byte("test"),
},
},
{
PreviousOutPoint: op1,
Sequence: 2,
Witness: wire.TxWitness{
[]byte("test"),
},
},
},
TxOut: []*wire.TxOut{
{
Value: 2_493_300,
PkScript: batchPkScript,
},
{
Value: 1_338, // Just to make it different.
PkScript: batchPkScript,
},
},
LockTime: 799_999,
},
inputAmt: 3_000_000,
minRelayFee: 253,
wantErr: "mismatch of output value",
},
{

View file

@ -26,6 +26,7 @@ import (
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/swap"
sweeppkg "github.com/lightninglabs/loop/sweep"
"github.com/lightninglabs/loop/utils"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/clock"
"github.com/lightningnetwork/lnd/input"
@ -1290,10 +1291,14 @@ func (b *batch) createPsbt(unsignedTx *wire.MsgTx, sweeps []sweep) ([]byte,
}
// constructUnsignedTx creates unsigned tx from the sweeps, paying to the addr.
// It also returns absolute fee (from weight and clamped).
// It also returns absolute fee (from weight and clamped). The main output is
// the first output of the transaction, followed by an optional list of change
// outputs. If the main output value is below dust limit this function will
// return an error.
func constructUnsignedTx(sweeps []sweep, address btcutil.Address,
currentHeight int32, feeRate chainfee.SatPerKWeight) (*wire.MsgTx,
lntypes.WeightUnit, btcutil.Amount, btcutil.Amount, error) {
currentHeight int32, feeRate chainfee.SatPerKWeight) (
*wire.MsgTx, lntypes.WeightUnit, btcutil.Amount, btcutil.Amount,
error) {
// Sanity check, there should be at least 1 sweep in this batch.
if len(sweeps) == 0 {
@ -1306,6 +1311,13 @@ func constructUnsignedTx(sweeps []sweep, address btcutil.Address,
LockTime: uint32(currentHeight),
}
var changeOutputs []*wire.TxOut
for _, sweep := range sweeps {
if sweep.change != nil {
changeOutputs = append(changeOutputs, sweep.change)
}
}
// Add transaction inputs and estimate its weight.
var weightEstimate input.TxWeightEstimator
for _, sweep := range sweeps {
@ -1351,6 +1363,11 @@ func constructUnsignedTx(sweeps []sweep, address btcutil.Address,
"failed: %w", err)
}
// Add the optional change outputs to weight estimates.
for _, o := range changeOutputs {
weightEstimate.AddOutput(o.PkScript)
}
// Keep track of the total amount this batch is sweeping back.
batchAmt := btcutil.Amount(0)
for _, sweep := range sweeps {
@ -1368,15 +1385,78 @@ func constructUnsignedTx(sweeps []sweep, address btcutil.Address,
feeForWeight++
}
// Clamp the calculated fee to the max allowed fee amount for the batch.
fee := clampBatchFee(feeForWeight, batchAmt)
// Add the batch transaction output, which excludes the fees paid to
// miners.
// miners. Reduce the amount by the sum of change outputs, if any.
var sumChange int64
for _, change := range changeOutputs {
sumChange += change.Value
}
// Ensure that the batch amount is greater than the sum of change.
if batchAmt <= btcutil.Amount(sumChange) {
return nil, 0, 0, 0, fmt.Errorf("batch amount %v is <= the "+
"sum of change outputs %v", batchAmt,
btcutil.Amount(sumChange))
}
// Clamp the calculated fee to the max allowed fee amount for the batch.
fee := clampBatchFee(feeForWeight, batchAmt-btcutil.Amount(sumChange))
// Ensure that batch amount exceeds the sum of change outputs and the
// fee, and that it is also greater than dust limit for the main
// output.
dustLimit := utils.DustLimitForPkScript(batchPkScript)
if fee+btcutil.Amount(sumChange)+dustLimit > batchAmt {
return nil, 0, 0, 0, fmt.Errorf("batch amount %v is <= the "+
"sum of change outputs %v plus fee %v and dust "+
"limit %v", batchAmt, btcutil.Amount(sumChange),
fee, dustLimit)
}
// Add the main output first.
batchTx.AddTxOut(&wire.TxOut{
PkScript: batchPkScript,
Value: int64(batchAmt - fee),
Value: int64(batchAmt-fee) - sumChange,
})
// Then add change outputs.
for _, txOut := range changeOutputs {
batchTx.AddTxOut(&wire.TxOut{
PkScript: txOut.PkScript,
Value: txOut.Value,
})
}
// Check that for each swap, inputs exceed the change outputs.
if len(changeOutputs) != 0 {
swap2Inputs := make(map[lntypes.Hash]btcutil.Amount)
swap2Change := make(map[lntypes.Hash]btcutil.Amount)
for _, sweep := range sweeps {
swap2Inputs[sweep.swapHash] += sweep.value
if sweep.change != nil {
swap2Change[sweep.swapHash] +=
btcutil.Amount(sweep.change.Value)
}
}
for swapHash, inputs := range swap2Inputs {
change := swap2Change[swapHash]
if inputs <= change {
return nil, 0, 0, 0, fmt.Errorf(""+
"inputs %v <= change %v for swap %x",
inputs, change, swapHash[:6])
}
}
}
// Ensure that each output is above dust limit.
for _, txOut := range batchTx.TxOut {
dustLimit = utils.DustLimitForPkScript(txOut.PkScript)
if btcutil.Amount(txOut.Value) < dustLimit {
return nil, 0, 0, 0, fmt.Errorf("output %v is below "+
"dust limit %v", btcutil.Amount(txOut.Value),
dustLimit)
}
}
return batchTx, weight, feeForWeight, fee, nil
}

View file

@ -29,6 +29,10 @@ func TestConstructUnsignedTx(t *testing.T) {
Hash: chainhash.Hash{2, 2, 2},
Index: 2,
}
op3 := wire.OutPoint{
Hash: chainhash.Hash{3, 3, 3},
Index: 3,
}
batchPkScript, err := txscript.PayToAddrScript(destAddr)
require.NoError(t, err)
@ -40,6 +44,28 @@ func TestConstructUnsignedTx(t *testing.T) {
p2trPkScript, err := txscript.PayToAddrScript(p2trAddress)
require.NoError(t, err)
change1Addr := "bc1pdx9ggvtjjcpaqfqk375qhdmzx9xu8dcu7w94lqfcxhh0rj" +
"lwyyeq5ryn6r"
change1Address, err := btcutil.DecodeAddress(change1Addr, nil)
require.NoError(t, err)
change1Pkscript, err := txscript.PayToAddrScript(change1Address)
require.NoError(t, err)
change1 := &wire.TxOut{
Value: 100_000,
PkScript: change1Pkscript,
}
change2Addr := "bc1psw0nrrulq4pgyuyk09a3wsutygltys4gxjjw3zl2uz4ep8pa" +
"r2vsvntfe0"
change2Address, err := btcutil.DecodeAddress(change2Addr, nil)
require.NoError(t, err)
change2Pkscript, err := txscript.PayToAddrScript(change2Address)
require.NoError(t, err)
change2 := &wire.TxOut{
Value: 200_000,
PkScript: change2Pkscript,
}
serializedPubKey := []byte{
0x02, 0x19, 0x2d, 0x74, 0xd0, 0xcb, 0x94, 0x34, 0x4c, 0x95,
0x69, 0xc2, 0xe7, 0x79, 0x01, 0x57, 0x3d, 0x8d, 0x79, 0x03,
@ -70,12 +96,15 @@ func TestConstructUnsignedTx(t *testing.T) {
return fmt.Errorf("weight estimator test failure")
}
dustLimit := utils.DustLimitForPkScript(batchPkScript)
cases := []struct {
name string
sweeps []sweep
address btcutil.Address
currentHeight int32
feeRate chainfee.SatPerKWeight
minRelayFeeRate chainfee.SatPerKWeight
wantErr string
wantTx *wire.MsgTx
wantWeight lntypes.WeightUnit
@ -223,7 +252,7 @@ func TestConstructUnsignedTx(t *testing.T) {
},
TxOut: []*wire.TxOut{
{
Value: 2400000,
Value: 2_400_000,
PkScript: batchPkScript,
},
},
@ -265,7 +294,7 @@ func TestConstructUnsignedTx(t *testing.T) {
},
TxOut: []*wire.TxOut{
{
Value: 2999211,
Value: 2_999_211,
PkScript: batchPkScript,
},
},
@ -275,6 +304,208 @@ func TestConstructUnsignedTx(t *testing.T) {
wantFee: 789,
},
{
name: "single sweep with change",
sweeps: []sweep{
{
outpoint: op1,
value: 1_000_000,
change: change1,
},
},
address: p2trAddress,
currentHeight: 800_000,
feeRate: 1000,
wantTx: &wire.MsgTx{
Version: 2,
LockTime: 800_000,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: op1,
},
},
TxOut: []*wire.TxOut{
{
Value: 899_384,
PkScript: p2trPkScript,
},
{
Value: change1.Value,
PkScript: change1.PkScript,
},
},
},
wantWeight: 616,
wantFeeForWeight: 616,
wantFee: 616,
},
{
name: "all sweeps different change outputs",
sweeps: []sweep{
{
outpoint: op1,
value: 1_000_000,
},
{
outpoint: op2,
value: 2_000_000,
change: change1,
},
{
outpoint: op3,
value: 3_000_000,
change: change2,
},
},
address: p2trAddress,
currentHeight: 800_000,
feeRate: 1000,
wantTx: &wire.MsgTx{
Version: 2,
LockTime: 800_000,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: op1,
},
{
PreviousOutPoint: op2,
},
{
PreviousOutPoint: op3,
},
},
TxOut: []*wire.TxOut{
{
Value: 5_698_752,
PkScript: p2trPkScript,
},
{
Value: change1.Value,
PkScript: change1.PkScript,
},
{
Value: change2.Value,
PkScript: change2.PkScript,
},
},
},
wantWeight: 1248,
wantFeeForWeight: 1248,
wantFee: 1248,
},
{
name: "change exceeds input value",
sweeps: []sweep{
{
outpoint: op2,
value: btcutil.Amount(change1.Value - 1),
change: change1,
},
},
address: p2trAddress,
currentHeight: 800_000,
feeRate: 1000,
wantTx: &wire.MsgTx{
Version: 2,
LockTime: 800_000,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: op2,
},
},
TxOut: []*wire.TxOut{
{
Value: change1.Value,
PkScript: change1.PkScript,
},
},
},
wantErr: "batch amount 0.00099999 BTC is <= the sum " +
"of change outputs 0.00100000 BTC",
},
{
name: "main output dust, batch amount less than " +
"change+fee+dust",
sweeps: []sweep{
{
outpoint: op1,
value: dustLimit,
},
{
outpoint: op2,
value: btcutil.Amount(change1.Value),
change: change1,
},
},
address: p2trAddress,
currentHeight: 800_000,
feeRate: 1,
wantErr: "batch amount 0.00100294 BTC is <= the sum " +
"of change outputs 0.00100000 BTC plus fee " +
"0.00000001 BTC and dust limit 0.00000330 BTC",
},
{
name: "change output is dust",
sweeps: []sweep{
{
outpoint: op1,
value: 1_000_000,
change: &wire.TxOut{
Value: int64(dustLimit - 1),
PkScript: []byte{0xaf, 0xfe},
},
},
},
address: p2trAddress,
currentHeight: 800_000,
feeRate: 1000,
wantErr: "output 0.00000293 BTC is below dust limit " +
"0.00000477 BTC",
},
{
name: "clamp fee to max fee to swap amount ratio",
sweeps: []sweep{
{
outpoint: op1,
value: btcutil.SatoshiPerBitcoin,
change: &wire.TxOut{
Value: btcutil.SatoshiPerBitcoin * 0.9,
PkScript: change1Pkscript,
},
},
},
address: p2trAddress,
currentHeight: 800_000,
feeRate: 10000000,
wantTx: &wire.MsgTx{
Version: 2,
LockTime: 800_000,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: op1,
},
},
TxOut: []*wire.TxOut{
{
Value: btcutil.SatoshiPerBitcoin * 0.08,
PkScript: p2trPkScript,
},
{
Value: btcutil.SatoshiPerBitcoin * 0.9,
PkScript: change1.PkScript,
},
},
},
wantWeight: 616,
wantFeeForWeight: 6_160_000,
wantFee: 2_000_000,
},
{
name: "weight estimator fails",
sweeps: []sweep{
@ -338,9 +569,13 @@ func TestConstructUnsignedTx(t *testing.T) {
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
relayFeeRate := minRelayFeeRate
if tc.minRelayFeeRate != 0 {
relayFeeRate = tc.minRelayFeeRate
}
tx, weight, feeForW, fee, err := constructUnsignedTx(
tc.sweeps, tc.address, tc.currentHeight,
tc.feeRate,
tc.feeRate, relayFeeRate,
)
if tc.wantErr != "" {
require.Error(t, err)

View file

@ -125,6 +125,9 @@ type SweepInfo struct {
// value should be stable for a sweep. Currently presigned and
// non-presigned sweeps never appear in the same batch.
IsPresigned bool
// Change is an optional change output of the sweep.
Change *wire.TxOut
}
// SweepFetcher is used to get details of a sweep.
@ -168,7 +171,10 @@ type PresignedHelper interface {
// SignTx signs an unsigned transaction or returns a pre-signed tx.
// It must satisfy the following invariants:
// - the set of inputs is the same, though the order may change;
// - the output is the same, but its amount may be different;
// - the main output is the same, but its amount may be different;
// - the main output is the first output in the transaction;
// - an optional set of change outputs may be added, the values and
// pkscripts must be preserved.
// - feerate is higher or equal to minRelayFee;
// - LockTime may be decreased;
// - transaction version must be the same;
@ -177,6 +183,7 @@ type PresignedHelper interface {
// When choosing a presigned transaction, a transaction with fee rate
// closer to the fee rate passed is selected. If loadOnly is set, it
// doesn't try to sign the transaction and only loads a presigned tx.
// These rules are enforced by CheckSignedTx function.
SignTx(ctx context.Context, primarySweepID wire.OutPoint,
tx *wire.MsgTx, inputAmt btcutil.Amount,
minRelayFee, feeRate chainfee.SatPerKWeight,
@ -711,9 +718,11 @@ func (b *Batcher) Run(ctx context.Context) error {
// group. This method must be called prior to AddSweep if presigned mode is
// enabled, otherwise AddSweep will fail. All the sweeps must belong to the same
// swap. The order of sweeps is important. The first sweep serves as
// primarySweepID if the group starts a new batch.
// primarySweepID if the group starts a new batch. The change output may be nil
// to indicate that the sweep group does not create a change output.
func (b *Batcher) PresignSweepsGroup(ctx context.Context, inputs []Input,
sweepTimeout int32, destAddress btcutil.Address) error {
sweepTimeout int32, destAddress btcutil.Address,
changeOutput *wire.TxOut) error {
if len(inputs) == 0 {
return fmt.Errorf("no inputs passed to PresignSweepsGroup")
@ -745,6 +754,9 @@ func (b *Batcher) PresignSweepsGroup(ctx context.Context, inputs []Input,
}
}
// Set the change output on the primary group sweep.
sweeps[0].change = changeOutput
// The sweeps are ordered inside the group, the first one is the primary
// outpoint in the batch.
primarySweepID := sweeps[0].outpoint
@ -1548,6 +1560,7 @@ func (b *Batcher) loadSweep(ctx context.Context, swapHash lntypes.Hash,
minFeeRate: minFeeRate,
nonCoopHint: s.NonCoopHint,
presigned: s.IsPresigned,
change: s.Change,
}, nil
}

View file

@ -17,7 +17,9 @@ import (
"github.com/lightninglabs/loop/swap"
"github.com/lightninglabs/loop/test"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -30,6 +32,9 @@ type mockPresignedHelper struct {
// participating in presigning.
onlineOutpoints map[wire.OutPoint]bool
// changeOutputs is a map of change outputs for a given primary deposit.
changeOutputs map[wire.OutPoint]*wire.TxOut
// presignedBatches is the collection of presigned batches. The key is
// primarySweepID.
presignedBatches map[wire.OutPoint][]*wire.MsgTx
@ -46,6 +51,7 @@ type mockPresignedHelper struct {
func newMockPresignedHelper() *mockPresignedHelper {
return &mockPresignedHelper{
onlineOutpoints: make(map[wire.OutPoint]bool),
changeOutputs: make(map[wire.OutPoint]*wire.TxOut),
presignedBatches: make(map[wire.OutPoint][]*wire.MsgTx),
cleanupCalled: make(chan struct{}),
}
@ -59,6 +65,16 @@ func (h *mockPresignedHelper) SetOutpointOnline(op wire.OutPoint, online bool) {
h.onlineOutpoints[op] = online
}
// setChangeForPrimaryDeposit sets the change output of a primary deposit sweep.
func (h *mockPresignedHelper) setChangeForPrimaryDeposit(op wire.OutPoint,
change *wire.TxOut) {
h.mu.Lock()
defer h.mu.Unlock()
h.changeOutputs[op] = change
}
// offlineInputs returns inputs of a tx which are offline.
func (h *mockPresignedHelper) offlineInputs(tx *wire.MsgTx) []wire.OutPoint {
offline := make([]wire.OutPoint, 0, len(tx.TxIn))
@ -113,7 +129,7 @@ func (h *mockPresignedHelper) DestPkScript(ctx context.Context,
}
// SignTx tries to sign the transaction. If all the inputs are online, it signs
// the exact transaction passed and adds it to presignedBatches. Otherwise it
// the exact transaction passed and adds it to presignedBatches. Otherwise, it
// looks for a transaction in presignedBatches satisfying the criteria.
func (h *mockPresignedHelper) SignTx(ctx context.Context,
primarySweepID wire.OutPoint, tx *wire.MsgTx, inputAmt btcutil.Amount,
@ -211,6 +227,9 @@ func (h *mockPresignedHelper) FetchSweep(_ context.Context,
// Find IsPresigned.
_, isPresigned := h.onlineOutpoints[utxo]
// Find change.
change := h.changeOutputs[utxo]
return &SweepInfo{
// Set Timeout to prevent warning messages about timeout=0.
Timeout: sweepTimeout,
@ -220,6 +239,7 @@ func (h *mockPresignedHelper) FetchSweep(_ context.Context,
HTLC: swap.Htlc{
PkScript: []byte{10, 11, 12},
},
Change: change,
}, nil
}
@ -273,7 +293,7 @@ func testPresigned_forgotten_presign(t *testing.T,
presignedHelper.SetOutpointOnline(op1, false)
err := batcher.PresignSweepsGroup(
ctx, []Input{{Outpoint: op1, Value: 1_000_000}},
sweepTimeout, destAddr,
sweepTimeout, destAddr, nil,
)
require.Error(t, err)
require.ErrorContains(t, err, "offline")
@ -350,7 +370,7 @@ func testPresigned_input1_offline_then_input2(t *testing.T,
presignedHelper.SetOutpointOnline(op1, true)
err = batcher.PresignSweepsGroup(
ctx, []Input{{Outpoint: op1, Value: 1_000_000}},
sweepTimeout, destAddr,
sweepTimeout, destAddr, nil,
)
require.NoError(t, err)
@ -413,7 +433,7 @@ func testPresigned_input1_offline_then_input2(t *testing.T,
presignedHelper.SetOutpointOnline(op2, true)
err = batcher.PresignSweepsGroup(
ctx, []Input{{Outpoint: op2, Value: 2_000_000}},
sweepTimeout, destAddr,
sweepTimeout, destAddr, nil,
)
require.NoError(t, err)
@ -520,7 +540,7 @@ func testPresigned_min_relay_fee(t *testing.T,
presignedHelper.SetOutpointOnline(op1, true)
err := batcher.PresignSweepsGroup(
ctx, []Input{{Outpoint: op1, Value: inputAmt}},
sweepTimeout, destAddr,
sweepTimeout, destAddr, nil,
)
require.NoError(t, err)
@ -644,7 +664,7 @@ func testPresigned_two_inputs_one_goes_offline(t *testing.T,
presignedHelper.SetOutpointOnline(op1, true)
err = batcher.PresignSweepsGroup(
ctx, []Input{{Outpoint: op1, Value: 1_000_000}},
sweepTimeout, destAddr,
sweepTimeout, destAddr, nil,
)
require.NoError(t, err)
require.NoError(t, batcher.AddSweep(ctx, &sweepReq1))
@ -670,7 +690,7 @@ func testPresigned_two_inputs_one_goes_offline(t *testing.T,
presignedHelper.SetOutpointOnline(op2, true)
err = batcher.PresignSweepsGroup(
ctx, []Input{{Outpoint: op2, Value: 2_000_000}},
sweepTimeout, destAddr,
sweepTimeout, destAddr, nil,
)
require.NoError(t, err)
require.NoError(t, batcher.AddSweep(ctx, &sweepReq2))
@ -780,7 +800,7 @@ func testPresigned_first_publish_fails(t *testing.T,
presignedHelper.SetOutpointOnline(op1, true)
err = batcher.PresignSweepsGroup(
ctx, []Input{{Outpoint: op1, Value: 1_000_000}},
sweepTimeout, destAddr,
sweepTimeout, destAddr, nil,
)
require.NoError(t, err)
presignedHelper.SetOutpointOnline(op1, false)
@ -903,7 +923,7 @@ func testPresigned_locktime(t *testing.T,
presignedHelper.SetOutpointOnline(op1, true)
err = batcher.PresignSweepsGroup(
ctx, []Input{{Outpoint: op1, Value: 1_000_000}},
sweepTimeout, destAddr,
sweepTimeout, destAddr, nil,
)
require.NoError(t, err)
presignedHelper.SetOutpointOnline(op1, false)
@ -994,14 +1014,18 @@ func testPresigned_presigned_group(t *testing.T,
presignedHelper.SetOutpointOnline(op2, false)
// An attempt to presign must fail.
err = batcher.PresignSweepsGroup(ctx, group1, sweepTimeout, destAddr)
err = batcher.PresignSweepsGroup(
ctx, group1, sweepTimeout, destAddr, nil,
)
require.ErrorContains(t, err, "some outpoint is offline")
// Enable both outpoints.
presignedHelper.SetOutpointOnline(op2, true)
// An attempt to presign must succeed.
err = batcher.PresignSweepsGroup(ctx, group1, sweepTimeout, destAddr)
err = batcher.PresignSweepsGroup(
ctx, group1, sweepTimeout, destAddr, nil,
)
require.NoError(t, err)
// Add the sweep, triggering the publish attempt.
@ -1053,7 +1077,9 @@ func testPresigned_presigned_group(t *testing.T,
presignedHelper.SetOutpointOnline(op4, true)
// An attempt to presign must succeed.
err = batcher.PresignSweepsGroup(ctx, group2, sweepTimeout, destAddr)
err = batcher.PresignSweepsGroup(
ctx, group2, sweepTimeout, destAddr, nil,
)
require.NoError(t, err)
// Add the sweep. It should go to the same batch.
@ -1107,7 +1133,9 @@ func testPresigned_presigned_group(t *testing.T,
presignedHelper.SetOutpointOnline(op6, true)
// An attempt to presign must succeed.
err = batcher.PresignSweepsGroup(ctx, group3, sweepTimeout, destAddr)
err = batcher.PresignSweepsGroup(
ctx, group3, sweepTimeout, destAddr, nil,
)
require.NoError(t, err)
// Add the sweep. It should go to the same batch.
@ -1135,6 +1163,271 @@ func testPresigned_presigned_group(t *testing.T,
require.Equal(t, batchPkScript, tx.TxOut[0].PkScript)
}
// testPresigned_presigned_group_with_change tests passing multiple sweeps to
// the method PresignSweepsGroup. It tests that a change output of a primary
// deposit sweep is properly added to the presigned transaction.
func testPresigned_presigned_group_with_change(t *testing.T,
batcherStore testBatcherStore) {
defer test.Guard(t)()
batchPkScript, err := txscript.PayToAddrScript(destAddr)
require.NoError(t, err)
lnd := test.NewMockLnd()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
customFeeRate := func(_ context.Context, _ lntypes.Hash,
_ wire.OutPoint) (chainfee.SatPerKWeight, error) {
return chainfee.SatPerKWeight(10_000), nil
}
presignedHelper := newMockPresignedHelper()
batcher := NewBatcher(
lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams,
batcherStore, presignedHelper,
WithCustomFeeRate(customFeeRate),
WithPresignedHelper(presignedHelper),
)
go func() {
err := batcher.Run(ctx)
checkBatcherError(t, err)
}()
// Create a swap of two sweeps.
swapHash1 := lntypes.Hash{1, 1, 1}
op1 := wire.OutPoint{
Hash: chainhash.Hash{1, 1},
Index: 1,
}
op2 := wire.OutPoint{
Hash: chainhash.Hash{2, 2},
Index: 2,
}
group1 := []Input{
{
Outpoint: op1,
Value: 1_000_000,
},
{
Outpoint: op2,
Value: 2_000_000,
},
}
change := &wire.TxOut{
Value: 500_000,
PkScript: []byte{0xaf, 0xfe},
}
presignedHelper.setChangeForPrimaryDeposit(op1, change)
// Enable only one of the sweeps.
presignedHelper.SetOutpointOnline(op1, true)
presignedHelper.SetOutpointOnline(op2, true)
// An attempt to presign must fail.
err = batcher.PresignSweepsGroup(
ctx, group1, sweepTimeout, destAddr, change,
)
require.NoError(t, err)
// Add the sweep, triggering the publishing attempt.
err = batcher.AddSweep(ctx, &SweepRequest{
SwapHash: swapHash1,
Inputs: group1,
Notifier: &dummyNotifier,
})
require.NoError(t, err)
// Since a batch was created we check that it registered for its primary
// sweep's spend.
<-lnd.RegisterSpendChannel
// Wait for a transactions to be published.
tx := <-lnd.TxPublishChannel
require.Len(t, tx.TxIn, 2)
require.Len(t, tx.TxOut, 2)
require.ElementsMatch(
t, []wire.OutPoint{op1, op2},
[]wire.OutPoint{
tx.TxIn[0].PreviousOutPoint,
tx.TxIn[1].PreviousOutPoint,
},
)
require.Equal(t, int64(2_493_300), tx.TxOut[0].Value)
require.Equal(t, change.Value, tx.TxOut[1].Value)
require.Equal(t, batchPkScript, tx.TxOut[0].PkScript)
require.Equal(t, change.PkScript, tx.TxOut[1].PkScript)
// Mine a blocks to trigger republishing.
require.NoError(t, lnd.NotifyHeight(601))
}
// testPresigned_presigned_group_with_dust_main_output tests passing multiple
// sweeps to the method PresignSweepsGroup. It tests that a dust change output of
// a primary deposit sweep is rejected by PresignSweepsGroup and AddSweep.
func testPresigned_presigned_group_with_dust_main_output(t *testing.T,
batcherStore testBatcherStore) {
defer test.Guard(t)()
lnd := test.NewMockLnd()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
customFeeRate := func(_ context.Context, _ lntypes.Hash,
_ wire.OutPoint) (chainfee.SatPerKWeight, error) {
return chainfee.SatPerKWeight(10_000), nil
}
presignedHelper := newMockPresignedHelper()
batcher := NewBatcher(
lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams,
batcherStore, presignedHelper,
WithCustomFeeRate(customFeeRate),
WithPresignedHelper(presignedHelper),
)
go func() {
err := batcher.Run(ctx)
checkBatcherError(t, err)
}()
// Create a swap of two sweeps.
swapHash1 := lntypes.Hash{1, 1, 1}
op1 := wire.OutPoint{
Hash: chainhash.Hash{1, 1},
Index: 1,
}
inputValue := int64(1_000_000)
group1 := []Input{
{
Outpoint: op1,
Value: 1_000_000,
},
}
dustLimit := int64(lnwallet.DustLimitForSize(input.P2TRSize))
change := &wire.TxOut{
Value: inputValue - dustLimit + 1,
PkScript: []byte{0xaf, 0xfe},
}
presignedHelper.setChangeForPrimaryDeposit(op1, change)
// Enable only one of the sweeps.
presignedHelper.SetOutpointOnline(op1, true)
// An attempt to presign must fail.
err := batcher.PresignSweepsGroup(
ctx, group1, sweepTimeout, destAddr, change,
)
require.EqualError(t, err, "failed to construct unsigned tx for "+
"feeRate 253 sat/kw: batch amount 0.01000000 BTC is <= the "+
"sum of change outputs 0.00999671 BTC plus fee "+
"0.00000065 BTC and dust limit 0.00000294 BTC")
// Add the sweep, triggering the publishing attempt.
err = batcher.AddSweep(ctx, &SweepRequest{
SwapHash: swapHash1,
Inputs: group1,
Notifier: &dummyNotifier,
})
require.ErrorContains(t, err, "were not presigned")
}
// testPresigned_presigned_group_with_dust_change tests passing multiple sweeps
// to the method PresignSweepsGroup. It tests that a dust change output of a
// primary deposit sweep is rejected by PresignSweepsGroup and AddSweep.
func testPresigned_presigned_group_with_dust_change(t *testing.T,
batcherStore testBatcherStore) {
defer test.Guard(t)()
lnd := test.NewMockLnd()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
customFeeRate := func(_ context.Context, _ lntypes.Hash,
_ wire.OutPoint) (chainfee.SatPerKWeight, error) {
return chainfee.SatPerKWeight(10_000), nil
}
presignedHelper := newMockPresignedHelper()
batcher := NewBatcher(
lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams,
batcherStore, presignedHelper,
WithCustomFeeRate(customFeeRate),
WithPresignedHelper(presignedHelper),
)
go func() {
err := batcher.Run(ctx)
checkBatcherError(t, err)
}()
// Create a swap of two sweeps.
swapHash1 := lntypes.Hash{1, 1, 1}
op1 := wire.OutPoint{
Hash: chainhash.Hash{1, 1},
Index: 1,
}
op2 := wire.OutPoint{
Hash: chainhash.Hash{2, 2},
Index: 2,
}
group1 := []Input{
{
Outpoint: op1,
Value: 1_000_000,
},
{
Outpoint: op2,
Value: 2_000_000,
},
}
dustLimit := lnwallet.DustLimitForSize(input.P2TRSize)
change := &wire.TxOut{
Value: int64(dustLimit - 1),
PkScript: []byte{0xaf, 0xfe},
}
presignedHelper.setChangeForPrimaryDeposit(op1, change)
// Enable only one of the sweeps.
presignedHelper.SetOutpointOnline(op1, true)
presignedHelper.SetOutpointOnline(op2, true)
// An attempt to presign must fail.
err := batcher.PresignSweepsGroup(
ctx, group1, sweepTimeout, destAddr, change,
)
require.EqualError(t, err, "failed to construct unsigned tx for "+
"feeRate 253 sat/kw: output 0.00000329 BTC is below dust "+
"limit 0.00000477 BTC")
// Add the sweep, triggering the publishing attempt.
err = batcher.AddSweep(ctx, &SweepRequest{
SwapHash: swapHash1,
Inputs: group1,
Notifier: &dummyNotifier,
})
require.ErrorContains(t, err, "were not presigned")
}
// wrappedStoreWithPresignedFlag wraps a SweepFetcher store adding IsPresigned
// flag to the returned sweeps, taking it from mockPresignedHelper.
type wrappedStoreWithPresignedFlag struct {
@ -1304,7 +1597,7 @@ func testPresigned_presigned_and_regular_sweeps(t *testing.T, store testStore,
presignedHelper.SetOutpointOnline(op2, true)
err = batcher.PresignSweepsGroup(
ctx, []Input{{Outpoint: op2, Value: 2_000_000}},
sweepTimeout, destAddr,
sweepTimeout, destAddr, nil,
)
require.NoError(t, err)
require.NoError(t, batcher.AddSweep(ctx, &sweepReq2))
@ -1399,7 +1692,7 @@ func testPresigned_presigned_and_regular_sweeps(t *testing.T, store testStore,
presignedHelper.SetOutpointOnline(op4, true)
err = batcher.PresignSweepsGroup(
ctx, []Input{{Outpoint: op4, Value: 3_000_000}},
sweepTimeout, destAddr,
sweepTimeout, destAddr, nil,
)
require.NoError(t, err)
require.NoError(t, batcher.AddSweep(ctx, &sweepReq4))
@ -1520,7 +1813,7 @@ func testPresigned_purging(t *testing.T, numSwaps, numConfirmedSwaps int,
// An attempt to presign must succeed.
err := batcher.PresignSweepsGroup(
ctx, group, sweepTimeout, destAddr,
ctx, group, sweepTimeout, destAddr, nil,
)
require.NoError(t, err)
@ -1584,11 +1877,11 @@ func testPresigned_purging(t *testing.T, numSwaps, numConfirmedSwaps int,
// An attempt to presign must succeed.
err := batcher.PresignSweepsGroup(
ctx, group, sweepTimeout, destAddr,
ctx, group, sweepTimeout, destAddr, nil,
)
require.NoError(t, err)
// Add the sweep, triggering the publish attempt.
// Add the sweep, triggering the publishing attempt.
require.NoError(t, batcher.AddSweep(ctx, &SweepRequest{
SwapHash: swapHash,
Inputs: group,
@ -1799,6 +2092,20 @@ func TestPresigned(t *testing.T) {
testPresigned_presigned_group(t, NewStoreMock())
})
t.Run("change", func(t *testing.T) {
testPresigned_presigned_group_with_change(t, NewStoreMock())
})
t.Run("dust_main_output", func(t *testing.T) {
testPresigned_presigned_group_with_dust_main_output(
t, NewStoreMock(),
)
})
t.Run("dust_change", func(t *testing.T) {
testPresigned_presigned_group_with_dust_change(t, NewStoreMock())
})
t.Run("presigned_and_regular_sweeps", func(t *testing.T) {
runTests(t, testPresigned_presigned_and_regular_sweeps)
})

View file

@ -229,7 +229,7 @@ func testSweepBatcherBatchCreation(t *testing.T, store testStore,
sweepReq1 := SweepRequest{
SwapHash: lntypes.Hash{1, 1, 1},
Inputs: []Input{{
Value: 111,
Value: 1111,
Outpoint: op1,
}},
Notifier: &dummyNotifier,
@ -238,7 +238,7 @@ func testSweepBatcherBatchCreation(t *testing.T, store testStore,
swap1 := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111,
AmountRequested: 111,
AmountRequested: 1111,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
},
@ -276,7 +276,7 @@ func testSweepBatcherBatchCreation(t *testing.T, store testStore,
sweepReq2 := SweepRequest{
SwapHash: lntypes.Hash{2, 2, 2},
Inputs: []Input{{
Value: 222,
Value: 2222,
Outpoint: op2,
}},
Notifier: &dummyNotifier,
@ -285,7 +285,7 @@ func testSweepBatcherBatchCreation(t *testing.T, store testStore,
swap2 := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111 + defaultMaxTimeoutDistance - 1,
AmountRequested: 222,
AmountRequested: 2222,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
@ -322,7 +322,7 @@ func testSweepBatcherBatchCreation(t *testing.T, store testStore,
sweepReq3 := SweepRequest{
SwapHash: lntypes.Hash{3, 3, 3},
Inputs: []Input{{
Value: 333,
Value: 3333,
Outpoint: op3,
}},
Notifier: &dummyNotifier,
@ -331,7 +331,7 @@ func testSweepBatcherBatchCreation(t *testing.T, store testStore,
swap3 := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111 + defaultMaxTimeoutDistance + 1,
AmountRequested: 333,
AmountRequested: 3333,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
@ -539,7 +539,7 @@ func testTxLabeler(t *testing.T, store testStore,
sweepReq1 := SweepRequest{
SwapHash: lntypes.Hash{1, 1, 1},
Inputs: []Input{{
Value: 111,
Value: 1111,
Outpoint: op1,
}},
Notifier: &dummyNotifier,
@ -548,7 +548,7 @@ func testTxLabeler(t *testing.T, store testStore,
swap1 := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111,
AmountRequested: 111,
AmountRequested: 1111,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
},
@ -692,7 +692,7 @@ func testPublishErrorHandler(t *testing.T, store testStore,
sweepReq1 := SweepRequest{
SwapHash: lntypes.Hash{1, 1, 1},
Inputs: []Input{{
Value: 111,
Value: 1111,
Outpoint: wire.OutPoint{
Hash: chainhash.Hash{1, 1},
Index: 1,
@ -704,7 +704,7 @@ func testPublishErrorHandler(t *testing.T, store testStore,
swap1 := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111,
AmountRequested: 111,
AmountRequested: 1111,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
},
@ -774,7 +774,7 @@ func testSweepBatcherSimpleLifecycle(t *testing.T, store testStore,
Index: 1,
}
const (
inputValue = 111
inputValue = 1111
outputValue = 50
fee = inputValue - outputValue
)
@ -1209,7 +1209,7 @@ func testSweepBatcherSkippedTxns(t *testing.T, store testStore,
}
swapHash := lntypes.Hash{1, 1, 1}
const (
inputValue = 111
inputValue = 1111
initiationHeight = 550
)
@ -1419,7 +1419,7 @@ func testDelays(t *testing.T, store testStore, batcherStore testBatcherStore) {
sweepReq := SweepRequest{
SwapHash: lntypes.Hash{1, 1, 1},
Inputs: []Input{{
Value: 111,
Value: 1111,
Outpoint: op1,
}},
Notifier: &dummyNotifier,
@ -1428,7 +1428,7 @@ func testDelays(t *testing.T, store testStore, batcherStore testBatcherStore) {
swap := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 1000,
AmountRequested: 111,
AmountRequested: 1111,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
},
@ -1712,7 +1712,7 @@ func testDelays(t *testing.T, store testStore, batcherStore testBatcherStore) {
sweepReq2 := SweepRequest{
SwapHash: lntypes.Hash{2, 2, 2},
Inputs: []Input{{
Value: 111,
Value: 1111,
Outpoint: wire.OutPoint{
Hash: chainhash.Hash{2, 2},
Index: 2,
@ -1727,7 +1727,7 @@ func testDelays(t *testing.T, store testStore, batcherStore testBatcherStore) {
// CltvExpiry is not urgent, but close.
CltvExpiry: 600 + blocksInDelay*2 + 5,
AmountRequested: 111,
AmountRequested: 1111,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
@ -1795,7 +1795,7 @@ func testDelays(t *testing.T, store testStore, batcherStore testBatcherStore) {
sweepReq3 := SweepRequest{
SwapHash: lntypes.Hash{3, 3, 3},
Inputs: []Input{{
Value: 111,
Value: 1111,
Outpoint: wire.OutPoint{
Hash: chainhash.Hash{3, 3},
Index: 3,
@ -1808,7 +1808,7 @@ func testDelays(t *testing.T, store testStore, batcherStore testBatcherStore) {
// CltvExpiry is urgent.
CltvExpiry: 600 + blocksInDelay*2 - 5,
AmountRequested: 111,
AmountRequested: 1111,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
@ -1871,8 +1871,8 @@ func testCustomDelays(t *testing.T, store testStore,
swapHash2 := lntypes.Hash{2, 2, 2}
const (
swapSize1 = 111
swapSize2 = 222
swapSize1 = 1111
swapSize2 = 2222
)
// initialDelay returns initialDelay depending of batch size (sats).
@ -1945,7 +1945,7 @@ func testCustomDelays(t *testing.T, store testStore,
swap1 := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 1000,
AmountRequested: 111,
AmountRequested: 1111,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
},
@ -2013,7 +2013,7 @@ func testCustomDelays(t *testing.T, store testStore,
swap2 := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 1000,
AmountRequested: 111,
AmountRequested: 1111,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
@ -2152,7 +2152,7 @@ func testMaxSweepsPerBatch(t *testing.T, store testStore,
sweepReq := SweepRequest{
SwapHash: swapHash,
Inputs: []Input{{
Value: 111,
Value: 1111,
Outpoint: outpoint,
}},
Notifier: &dummyNotifier,
@ -2161,7 +2161,7 @@ func testMaxSweepsPerBatch(t *testing.T, store testStore,
swap := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 1000,
AmountRequested: 111,
AmountRequested: 1111,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
@ -2269,7 +2269,7 @@ func testSweepBatcherSweepReentry(t *testing.T, store testStore,
Hash: chainhash.Hash{1, 1},
Index: 1,
}
value1 := btcutil.Amount(111)
value1 := btcutil.Amount(1111)
sweepReq1 := SweepRequest{
SwapHash: lntypes.Hash{1, 1, 1},
Inputs: []Input{{
@ -2282,7 +2282,7 @@ func testSweepBatcherSweepReentry(t *testing.T, store testStore,
swap1 := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111,
AmountRequested: 111,
AmountRequested: 1111,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
},
@ -2298,7 +2298,7 @@ func testSweepBatcherSweepReentry(t *testing.T, store testStore,
sweepReq2 := SweepRequest{
SwapHash: lntypes.Hash{2, 2, 2},
Inputs: []Input{{
Value: 222,
Value: 2222,
Outpoint: wire.OutPoint{
Hash: chainhash.Hash{2, 2},
Index: 2,
@ -2310,7 +2310,7 @@ func testSweepBatcherSweepReentry(t *testing.T, store testStore,
swap2 := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111,
AmountRequested: 222,
AmountRequested: 2222,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
@ -2329,7 +2329,7 @@ func testSweepBatcherSweepReentry(t *testing.T, store testStore,
sweepReq3 := SweepRequest{
SwapHash: lntypes.Hash{3, 3, 3},
Inputs: []Input{{
Value: 333,
Value: 3333,
Outpoint: wire.OutPoint{
Hash: chainhash.Hash{3, 3},
Index: 3,
@ -2341,7 +2341,7 @@ func testSweepBatcherSweepReentry(t *testing.T, store testStore,
swap3 := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111,
AmountRequested: 333,
AmountRequested: 3333,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
@ -2536,7 +2536,7 @@ func testSweepBatcherGroup(t *testing.T, store testStore,
swap1 := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111,
AmountRequested: 111,
AmountRequested: 1111,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
},
@ -2555,11 +2555,11 @@ func testSweepBatcherGroup(t *testing.T, store testStore,
Inputs: []Input{
{
Outpoint: outpoint1,
Value: 111,
Value: 1111,
},
{
Outpoint: outpoint2,
Value: 222,
Value: 2222,
},
},
Notifier: &dummyNotifier,
@ -2621,7 +2621,7 @@ func testSweepBatcherNonWalletAddr(t *testing.T, store testStore,
sweepReq1 := SweepRequest{
SwapHash: lntypes.Hash{1, 1, 1},
Inputs: []Input{{
Value: 111,
Value: 1111,
Outpoint: op1,
}},
Notifier: &dummyNotifier,
@ -2630,7 +2630,7 @@ func testSweepBatcherNonWalletAddr(t *testing.T, store testStore,
swap1 := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111,
AmountRequested: 111,
AmountRequested: 1111,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
},
@ -2668,7 +2668,7 @@ func testSweepBatcherNonWalletAddr(t *testing.T, store testStore,
sweepReq2 := SweepRequest{
SwapHash: lntypes.Hash{2, 2, 2},
Inputs: []Input{{
Value: 222,
Value: 2222,
Outpoint: op2,
}},
Notifier: &dummyNotifier,
@ -2677,7 +2677,7 @@ func testSweepBatcherNonWalletAddr(t *testing.T, store testStore,
swap2 := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111 + defaultMaxTimeoutDistance - 1,
AmountRequested: 222,
AmountRequested: 2222,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
@ -2714,7 +2714,7 @@ func testSweepBatcherNonWalletAddr(t *testing.T, store testStore,
sweepReq3 := SweepRequest{
SwapHash: lntypes.Hash{3, 3, 3},
Inputs: []Input{{
Value: 333,
Value: 3333,
Outpoint: op3,
}},
Notifier: &dummyNotifier,
@ -2723,7 +2723,7 @@ func testSweepBatcherNonWalletAddr(t *testing.T, store testStore,
swap3 := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111 + defaultMaxTimeoutDistance + 1,
AmountRequested: 222,
AmountRequested: 2222,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
@ -2799,6 +2799,8 @@ func testSweepBatcherComposite(t *testing.T, store testStore,
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
lnd.SetMinRelayFee(200)
sweepStore, err := NewSweepFetcherFromSwapStore(store, lnd.ChainParams)
require.NoError(t, err)
@ -2839,7 +2841,7 @@ func testSweepBatcherComposite(t *testing.T, store testStore,
sweepReq1 := SweepRequest{
SwapHash: lntypes.Hash{1, 1, 1},
Inputs: []Input{{
Value: 111,
Value: 1111,
Outpoint: op1,
}},
Notifier: &dummyNotifier,
@ -2848,7 +2850,7 @@ func testSweepBatcherComposite(t *testing.T, store testStore,
swap1 := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111,
AmountRequested: 111,
AmountRequested: 1111,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
},
@ -2866,7 +2868,7 @@ func testSweepBatcherComposite(t *testing.T, store testStore,
sweepReq2 := SweepRequest{
SwapHash: lntypes.Hash{2, 2, 2},
Inputs: []Input{{
Value: 222,
Value: 2222,
Outpoint: op2,
}},
Notifier: &dummyNotifier,
@ -2875,7 +2877,7 @@ func testSweepBatcherComposite(t *testing.T, store testStore,
swap2 := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111 + defaultMaxTimeoutDistance - 1,
AmountRequested: 222,
AmountRequested: 2222,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
@ -2896,7 +2898,7 @@ func testSweepBatcherComposite(t *testing.T, store testStore,
sweepReq3 := SweepRequest{
SwapHash: lntypes.Hash{3, 3, 3},
Inputs: []Input{{
Value: 333,
Value: 3333,
Outpoint: op3,
}},
Notifier: &dummyNotifier,
@ -2905,7 +2907,7 @@ func testSweepBatcherComposite(t *testing.T, store testStore,
swap3 := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111 + defaultMaxTimeoutDistance - 3,
AmountRequested: 333,
AmountRequested: 3333,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
@ -3242,7 +3244,7 @@ func testRestoringEmptyBatch(t *testing.T, store testStore,
sweepReq := SweepRequest{
SwapHash: lntypes.Hash{1, 1, 1},
Inputs: []Input{{
Value: 111,
Value: 1111,
Outpoint: op,
}},
Notifier: &dummyNotifier,
@ -3251,7 +3253,7 @@ func testRestoringEmptyBatch(t *testing.T, store testStore,
swap := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111,
AmountRequested: 111,
AmountRequested: 1111,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
},
@ -3426,7 +3428,7 @@ func testHandleSweepTwice(t *testing.T, backend testStore,
sweepReq1 := SweepRequest{
SwapHash: lntypes.Hash{1, 1, 1},
Inputs: []Input{{
Value: 111,
Value: 1111,
Outpoint: op1,
}},
Notifier: &dummyNotifier,
@ -3439,7 +3441,7 @@ func testHandleSweepTwice(t *testing.T, backend testStore,
Contract: &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: shortCltv,
AmountRequested: 111,
AmountRequested: 1111,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
},
@ -3456,7 +3458,7 @@ func testHandleSweepTwice(t *testing.T, backend testStore,
sweepReq2 := SweepRequest{
SwapHash: lntypes.Hash{2, 2, 2},
Inputs: []Input{{
Value: 222,
Value: 2222,
Outpoint: op2,
}},
Notifier: &dummyNotifier,
@ -3469,7 +3471,7 @@ func testHandleSweepTwice(t *testing.T, backend testStore,
Contract: &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: longCltv,
AmountRequested: 222,
AmountRequested: 2222,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
},
@ -3524,7 +3526,7 @@ func testHandleSweepTwice(t *testing.T, backend testStore,
Contract: &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: shortCltv,
AmountRequested: 222,
AmountRequested: 2222,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
},
@ -3628,7 +3630,7 @@ func testRestoringPreservesConfTarget(t *testing.T, store testStore,
sweepReq := SweepRequest{
SwapHash: lntypes.Hash{1, 1, 1},
Inputs: []Input{{
Value: 111,
Value: 1111,
Outpoint: op,
}},
Notifier: &dummyNotifier,
@ -3637,7 +3639,7 @@ func testRestoringPreservesConfTarget(t *testing.T, store testStore,
swap := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111,
AmountRequested: 111,
AmountRequested: 1111,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
},
@ -3955,7 +3957,7 @@ func testSweepBatcherCloseDuringAdding(t *testing.T, store testStore,
swap := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111,
AmountRequested: 111,
AmountRequested: 1111,
// Make preimage unique to pass SQL constraints.
Preimage: lntypes.Preimage{i},
@ -3981,7 +3983,7 @@ func testSweepBatcherCloseDuringAdding(t *testing.T, store testStore,
sweepReq := SweepRequest{
SwapHash: lntypes.Hash{i, i, i},
Inputs: []Input{{
Value: 111,
Value: 1111,
Outpoint: wire.OutPoint{
Hash: chainhash.Hash{i, i},
Index: 1,
@ -4066,7 +4068,7 @@ func testCustomSignMuSig2(t *testing.T, store testStore,
sweepReq := SweepRequest{
SwapHash: lntypes.Hash{1, 1, 1},
Inputs: []Input{{
Value: 111,
Value: 1111,
Outpoint: wire.OutPoint{
Hash: chainhash.Hash{1, 1},
Index: 1,
@ -4078,7 +4080,7 @@ func testCustomSignMuSig2(t *testing.T, store testStore,
swap := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111,
AmountRequested: 111,
AmountRequested: 1111,
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
HtlcKeys: htlcKeys,
},