mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
Merge pull request #882 from starius/always-mixed-batches
sweepbatcher: always enabled mixed batches
This commit is contained in:
commit
1f07c375d4
5 changed files with 223 additions and 838 deletions
|
|
@ -59,7 +59,7 @@ func (b *Batcher) greedyAddSweep(ctx context.Context, sweep *sweep) error {
|
|||
// Run the algorithm. Get batchId of possible batches, sorted from best
|
||||
// to worst.
|
||||
batchesIds, err := selectBatches(
|
||||
batches, sweepFeeDetails, newBatchFeeDetails, b.mixedBatch,
|
||||
batches, sweepFeeDetails, newBatchFeeDetails,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("batch selection algorithm failed for sweep "+
|
||||
|
|
@ -133,13 +133,10 @@ func estimateSweepFeeIncrement(s *sweep) (feeDetails, feeDetails, error) {
|
|||
// Create feeDetails for sweep.
|
||||
sweepFeeDetails := feeDetails{
|
||||
FeeRate: s.minFeeRate,
|
||||
NonCoopHint: s.nonCoopHint || s.coopFailed,
|
||||
IsExternalAddr: s.isExternalAddr,
|
||||
|
||||
// Calculate sweep weight as a difference.
|
||||
MixedWeight: fd2.MixedWeight - fd1.MixedWeight,
|
||||
CoopWeight: fd2.CoopWeight - fd1.CoopWeight,
|
||||
NonCoopWeight: fd2.NonCoopWeight - fd1.NonCoopWeight,
|
||||
Weight: fd2.Weight - fd1.Weight,
|
||||
}
|
||||
|
||||
return sweepFeeDetails, fd1, nil
|
||||
|
|
@ -158,14 +155,6 @@ func estimateBatchWeight(batch *batch) (feeDetails, error) {
|
|||
batch.rbfCache.FeeRate)
|
||||
}
|
||||
|
||||
// Find if the batch has at least one non-cooperative sweep.
|
||||
hasNonCoop := false
|
||||
for _, sweep := range batch.sweeps {
|
||||
if sweep.nonCoopHint || sweep.coopFailed {
|
||||
hasNonCoop = true
|
||||
}
|
||||
}
|
||||
|
||||
// Find some sweep of the batch. It is used if there is just one sweep.
|
||||
var theSweep sweep
|
||||
for _, sweep := range batch.sweeps {
|
||||
|
|
@ -186,21 +175,11 @@ func estimateBatchWeight(batch *batch) (feeDetails, error) {
|
|||
destAddr = (*btcutil.AddressTaproot)(nil)
|
||||
}
|
||||
|
||||
// Make three estimators: for mixed, coop and non-coop cases.
|
||||
var mixedWeight, coopWeight, nonCoopWeight input.TxWeightEstimator
|
||||
// Make a weight estimator.
|
||||
var weight input.TxWeightEstimator
|
||||
|
||||
// Add output weight to the estimator.
|
||||
err := sweeppkg.AddOutputEstimate(&mixedWeight, destAddr)
|
||||
if err != nil {
|
||||
return feeDetails{}, fmt.Errorf("sweep.AddOutputEstimate: %w",
|
||||
err)
|
||||
}
|
||||
err = sweeppkg.AddOutputEstimate(&coopWeight, destAddr)
|
||||
if err != nil {
|
||||
return feeDetails{}, fmt.Errorf("sweep.AddOutputEstimate: %w",
|
||||
err)
|
||||
}
|
||||
err = sweeppkg.AddOutputEstimate(&nonCoopWeight, destAddr)
|
||||
err := sweeppkg.AddOutputEstimate(&weight, destAddr)
|
||||
if err != nil {
|
||||
return feeDetails{}, fmt.Errorf("sweep.AddOutputEstimate: %w",
|
||||
err)
|
||||
|
|
@ -209,34 +188,23 @@ func estimateBatchWeight(batch *batch) (feeDetails, error) {
|
|||
// Add inputs.
|
||||
for _, sweep := range batch.sweeps {
|
||||
if sweep.nonCoopHint || sweep.coopFailed {
|
||||
err = sweep.htlcSuccessEstimator(&mixedWeight)
|
||||
err = sweep.htlcSuccessEstimator(&weight)
|
||||
if err != nil {
|
||||
return feeDetails{}, fmt.Errorf(
|
||||
"htlcSuccessEstimator failed: %w", err,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
mixedWeight.AddTaprootKeySpendInput(
|
||||
weight.AddTaprootKeySpendInput(
|
||||
txscript.SigHashDefault,
|
||||
)
|
||||
}
|
||||
|
||||
coopWeight.AddTaprootKeySpendInput(txscript.SigHashDefault)
|
||||
|
||||
err = sweep.htlcSuccessEstimator(&nonCoopWeight)
|
||||
if err != nil {
|
||||
return feeDetails{}, fmt.Errorf("htlcSuccessEstimator "+
|
||||
"failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return feeDetails{
|
||||
BatchId: batch.id,
|
||||
FeeRate: batch.rbfCache.FeeRate,
|
||||
MixedWeight: mixedWeight.Weight(),
|
||||
CoopWeight: coopWeight.Weight(),
|
||||
NonCoopWeight: nonCoopWeight.Weight(),
|
||||
NonCoopHint: hasNonCoop,
|
||||
Weight: weight.Weight(),
|
||||
IsExternalAddr: theSweep.isExternalAddr,
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -250,26 +218,13 @@ const newBatchSignal = -1
|
|||
type feeDetails struct {
|
||||
BatchId int32
|
||||
FeeRate chainfee.SatPerKWeight
|
||||
MixedWeight lntypes.WeightUnit
|
||||
CoopWeight lntypes.WeightUnit
|
||||
NonCoopWeight lntypes.WeightUnit
|
||||
NonCoopHint bool
|
||||
Weight lntypes.WeightUnit
|
||||
IsExternalAddr bool
|
||||
}
|
||||
|
||||
// fee returns fee of onchain transaction representing this instance.
|
||||
func (e feeDetails) fee(mixedBatch bool) btcutil.Amount {
|
||||
var weight lntypes.WeightUnit
|
||||
switch {
|
||||
case mixedBatch:
|
||||
weight = e.MixedWeight
|
||||
case e.NonCoopHint:
|
||||
weight = e.NonCoopWeight
|
||||
default:
|
||||
weight = e.CoopWeight
|
||||
}
|
||||
|
||||
return e.FeeRate.FeeForWeight(weight)
|
||||
func (e feeDetails) fee() btcutil.Amount {
|
||||
return e.FeeRate.FeeForWeight(e.Weight)
|
||||
}
|
||||
|
||||
// combine returns new feeDetails, combining properties.
|
||||
|
|
@ -282,20 +237,15 @@ func (e1 feeDetails) combine(e2 feeDetails) feeDetails {
|
|||
|
||||
return feeDetails{
|
||||
FeeRate: feeRate,
|
||||
MixedWeight: e1.MixedWeight + e2.MixedWeight,
|
||||
CoopWeight: e1.CoopWeight + e2.CoopWeight,
|
||||
NonCoopWeight: e1.NonCoopWeight + e2.NonCoopWeight,
|
||||
NonCoopHint: e1.NonCoopHint || e2.NonCoopHint,
|
||||
Weight: e1.Weight + e2.Weight,
|
||||
IsExternalAddr: e1.IsExternalAddr || e2.IsExternalAddr,
|
||||
}
|
||||
}
|
||||
|
||||
// selectBatches returns the list of id of batches sorted from best to worst.
|
||||
// Creation a new batch is encoded as newBatchSignal. For each batch its fee
|
||||
// rate and a set of weights are provided: weight in case of a mixed batch,
|
||||
// weight in case of cooperative spending and weight in case non-cooperative
|
||||
// spending. Also, a hint is provided to signal what spending path will be used
|
||||
// by the batch.
|
||||
// rate and a weight is provided. Also, a hint is provided to signal which
|
||||
// spending path will be used by the batch.
|
||||
//
|
||||
// The same data is also provided for the sweep for which we are selecting a
|
||||
// batch to add. In case of the sweep weights are weight deltas resulted from
|
||||
|
|
@ -308,10 +258,9 @@ func (e1 feeDetails) combine(e2 feeDetails) feeDetails {
|
|||
//
|
||||
// Each fee details has also IsExternalAddr flag. There is a rule that sweeps
|
||||
// having flag IsExternalAddr must go in individual batches. Cooperative
|
||||
// spending is only available if all the sweeps support cooperative spending
|
||||
// path of in a mixed batch.
|
||||
func selectBatches(batches []feeDetails, sweep, oneSweepBatch feeDetails,
|
||||
mixedBatch bool) ([]int32, error) {
|
||||
// spending may only be available for some sweeps supporting it, not for all.
|
||||
func selectBatches(batches []feeDetails,
|
||||
sweep, oneSweepBatch feeDetails) ([]int32, error) {
|
||||
|
||||
// If the sweep has IsExternalAddr flag, the sweep can't be added to
|
||||
// a batch, so create new batch for it.
|
||||
|
|
@ -332,7 +281,7 @@ func selectBatches(batches []feeDetails, sweep, oneSweepBatch feeDetails,
|
|||
// creation with this sweep only in it. The cost is its full fee.
|
||||
alternatives = append(alternatives, alternative{
|
||||
batchId: newBatchSignal,
|
||||
cost: oneSweepBatch.fee(mixedBatch),
|
||||
cost: oneSweepBatch.fee(),
|
||||
})
|
||||
|
||||
// Try to add the sweep to every batch, calculate the costs and
|
||||
|
|
@ -348,7 +297,7 @@ func selectBatches(batches []feeDetails, sweep, oneSweepBatch feeDetails,
|
|||
combinedBatch := batch.combine(sweep)
|
||||
|
||||
// The cost is the fee increase.
|
||||
cost := combinedBatch.fee(mixedBatch) - batch.fee(mixedBatch)
|
||||
cost := combinedBatch.fee() - batch.fee()
|
||||
|
||||
// The cost must be positive, because we added a sweep.
|
||||
if cost <= 0 {
|
||||
|
|
|
|||
|
|
@ -85,16 +85,12 @@ func TestEstimateSweepFeeIncrement(t *testing.T) {
|
|||
htlcSuccessEstimator: se3,
|
||||
},
|
||||
wantSweepFeeDetails: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopInputWeight,
|
||||
CoopWeight: coopInputWeight,
|
||||
NonCoopWeight: nonCoopInputWeight,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: coopInputWeight,
|
||||
},
|
||||
wantNewBatchFeeDetails: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
},
|
||||
|
||||
|
|
@ -105,16 +101,12 @@ func TestEstimateSweepFeeIncrement(t *testing.T) {
|
|||
htlcSuccessEstimator: se3,
|
||||
},
|
||||
wantSweepFeeDetails: feeDetails{
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: coopInputWeight,
|
||||
CoopWeight: coopInputWeight,
|
||||
NonCoopWeight: nonCoopInputWeight,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: coopInputWeight,
|
||||
},
|
||||
wantNewBatchFeeDetails: feeDetails{
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
},
|
||||
|
||||
|
|
@ -128,16 +120,12 @@ func TestEstimateSweepFeeIncrement(t *testing.T) {
|
|||
},
|
||||
wantSweepFeeDetails: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopInputWeight,
|
||||
CoopWeight: coopInputWeight,
|
||||
NonCoopWeight: nonCoopInputWeight,
|
||||
Weight: coopInputWeight,
|
||||
IsExternalAddr: true,
|
||||
},
|
||||
wantNewBatchFeeDetails: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
Weight: coopNewBatchWeight,
|
||||
IsExternalAddr: true,
|
||||
},
|
||||
},
|
||||
|
|
@ -152,18 +140,12 @@ func TestEstimateSweepFeeIncrement(t *testing.T) {
|
|||
},
|
||||
wantSweepFeeDetails: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopInputWeight,
|
||||
CoopWeight: coopInputWeight,
|
||||
NonCoopWeight: nonCoopInputWeight,
|
||||
Weight: coopInputWeight,
|
||||
IsExternalAddr: true,
|
||||
},
|
||||
wantNewBatchFeeDetails: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopNewBatchWeight -
|
||||
p2pkhDiscount,
|
||||
CoopWeight: coopNewBatchWeight -
|
||||
p2pkhDiscount,
|
||||
NonCoopWeight: nonCoopNewBatchWeight -
|
||||
Weight: coopNewBatchWeight -
|
||||
p2pkhDiscount,
|
||||
IsExternalAddr: true,
|
||||
},
|
||||
|
|
@ -177,18 +159,12 @@ func TestEstimateSweepFeeIncrement(t *testing.T) {
|
|||
nonCoopHint: true,
|
||||
},
|
||||
wantSweepFeeDetails: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: nonCoopInputWeight,
|
||||
CoopWeight: coopInputWeight,
|
||||
NonCoopWeight: nonCoopInputWeight,
|
||||
NonCoopHint: true,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: nonCoopInputWeight,
|
||||
},
|
||||
wantNewBatchFeeDetails: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: nonCoopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
NonCoopHint: true,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: nonCoopNewBatchWeight,
|
||||
},
|
||||
},
|
||||
|
||||
|
|
@ -200,18 +176,12 @@ func TestEstimateSweepFeeIncrement(t *testing.T) {
|
|||
coopFailed: true,
|
||||
},
|
||||
wantSweepFeeDetails: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: nonCoopInputWeight,
|
||||
CoopWeight: coopInputWeight,
|
||||
NonCoopWeight: nonCoopInputWeight,
|
||||
NonCoopHint: true,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: nonCoopInputWeight,
|
||||
},
|
||||
wantNewBatchFeeDetails: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: nonCoopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
NonCoopHint: true,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: nonCoopNewBatchWeight,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -262,11 +232,9 @@ func TestEstimateBatchWeight(t *testing.T) {
|
|||
},
|
||||
},
|
||||
wantBatchFeeDetails: feeDetails{
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
},
|
||||
|
||||
|
|
@ -287,11 +255,9 @@ func TestEstimateBatchWeight(t *testing.T) {
|
|||
},
|
||||
},
|
||||
wantBatchFeeDetails: feeDetails{
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopTwoSweepBatchWeight,
|
||||
CoopWeight: coopTwoSweepBatchWeight,
|
||||
NonCoopWeight: nonCoopTwoSweepBatchWeight,
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: coopTwoSweepBatchWeight,
|
||||
},
|
||||
},
|
||||
|
||||
|
|
@ -312,11 +278,34 @@ func TestEstimateBatchWeight(t *testing.T) {
|
|||
},
|
||||
},
|
||||
wantBatchFeeDetails: feeDetails{
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopTwoSweepBatchWeight,
|
||||
CoopWeight: coopTwoSweepBatchWeight,
|
||||
NonCoopWeight: v2v3BatchWeight,
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: coopTwoSweepBatchWeight,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "v2 and v3 sweeps noncoop",
|
||||
batch: &batch{
|
||||
id: 1,
|
||||
rbfCache: rbfCache{
|
||||
FeeRate: lowFeeRate,
|
||||
},
|
||||
sweeps: map[lntypes.Hash]sweep{
|
||||
swapHash1: {
|
||||
htlcSuccessEstimator: se2,
|
||||
nonCoopHint: true,
|
||||
},
|
||||
swapHash2: {
|
||||
htlcSuccessEstimator: se3,
|
||||
nonCoopHint: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantBatchFeeDetails: feeDetails{
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: v2v3BatchWeight,
|
||||
},
|
||||
},
|
||||
|
||||
|
|
@ -334,11 +323,9 @@ func TestEstimateBatchWeight(t *testing.T) {
|
|||
},
|
||||
},
|
||||
wantBatchFeeDetails: feeDetails{
|
||||
BatchId: 1,
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
BatchId: 1,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
},
|
||||
|
||||
|
|
@ -360,12 +347,9 @@ func TestEstimateBatchWeight(t *testing.T) {
|
|||
},
|
||||
},
|
||||
wantBatchFeeDetails: feeDetails{
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: mixedTwoSweepBatchWeight,
|
||||
CoopWeight: coopTwoSweepBatchWeight,
|
||||
NonCoopWeight: nonCoopTwoSweepBatchWeight,
|
||||
NonCoopHint: true,
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: mixedTwoSweepBatchWeight,
|
||||
},
|
||||
},
|
||||
|
||||
|
|
@ -387,12 +371,9 @@ func TestEstimateBatchWeight(t *testing.T) {
|
|||
},
|
||||
},
|
||||
wantBatchFeeDetails: feeDetails{
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: mixedTwoSweepBatchWeight,
|
||||
CoopWeight: coopTwoSweepBatchWeight,
|
||||
NonCoopWeight: nonCoopTwoSweepBatchWeight,
|
||||
NonCoopHint: true,
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: mixedTwoSweepBatchWeight,
|
||||
},
|
||||
},
|
||||
|
||||
|
|
@ -414,9 +395,7 @@ func TestEstimateBatchWeight(t *testing.T) {
|
|||
wantBatchFeeDetails: feeDetails{
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
Weight: coopNewBatchWeight,
|
||||
IsExternalAddr: true,
|
||||
},
|
||||
},
|
||||
|
|
@ -440,23 +419,18 @@ func TestSelectBatches(t *testing.T) {
|
|||
name string
|
||||
batches []feeDetails
|
||||
sweep, oneSweepBatch feeDetails
|
||||
mixedBatch bool
|
||||
wantBestBatchesIds []int32
|
||||
}{
|
||||
{
|
||||
name: "no existing batches",
|
||||
batches: []feeDetails{},
|
||||
sweep: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopInputWeight,
|
||||
CoopWeight: coopInputWeight,
|
||||
NonCoopWeight: nonCoopInputWeight,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: coopInputWeight,
|
||||
},
|
||||
oneSweepBatch: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
wantBestBatchesIds: []int32{newBatchSignal},
|
||||
},
|
||||
|
|
@ -465,24 +439,18 @@ func TestSelectBatches(t *testing.T) {
|
|||
name: "low fee sweep, low fee existing batch",
|
||||
batches: []feeDetails{
|
||||
{
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
},
|
||||
sweep: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopInputWeight,
|
||||
CoopWeight: coopInputWeight,
|
||||
NonCoopWeight: nonCoopInputWeight,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: coopInputWeight,
|
||||
},
|
||||
oneSweepBatch: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
wantBestBatchesIds: []int32{1, newBatchSignal},
|
||||
},
|
||||
|
|
@ -491,24 +459,18 @@ func TestSelectBatches(t *testing.T) {
|
|||
name: "low fee sweep, high fee existing batch",
|
||||
batches: []feeDetails{
|
||||
{
|
||||
BatchId: 1,
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
BatchId: 1,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
},
|
||||
sweep: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopInputWeight,
|
||||
CoopWeight: coopInputWeight,
|
||||
NonCoopWeight: nonCoopInputWeight,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: coopInputWeight,
|
||||
},
|
||||
oneSweepBatch: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
wantBestBatchesIds: []int32{newBatchSignal, 1},
|
||||
},
|
||||
|
|
@ -517,31 +479,23 @@ func TestSelectBatches(t *testing.T) {
|
|||
name: "low fee sweep, low + high fee existing batches",
|
||||
batches: []feeDetails{
|
||||
{
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
{
|
||||
BatchId: 2,
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
BatchId: 2,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
},
|
||||
sweep: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopInputWeight,
|
||||
CoopWeight: coopInputWeight,
|
||||
NonCoopWeight: nonCoopInputWeight,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: coopInputWeight,
|
||||
},
|
||||
oneSweepBatch: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
wantBestBatchesIds: []int32{1, newBatchSignal, 2},
|
||||
},
|
||||
|
|
@ -550,31 +504,23 @@ func TestSelectBatches(t *testing.T) {
|
|||
name: "high fee sweep, low + high fee existing batches",
|
||||
batches: []feeDetails{
|
||||
{
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
{
|
||||
BatchId: 2,
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
BatchId: 2,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
},
|
||||
sweep: feeDetails{
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: coopInputWeight,
|
||||
CoopWeight: coopInputWeight,
|
||||
NonCoopWeight: nonCoopInputWeight,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: coopInputWeight,
|
||||
},
|
||||
oneSweepBatch: feeDetails{
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
wantBestBatchesIds: []int32{2, newBatchSignal, 1},
|
||||
},
|
||||
|
|
@ -583,105 +529,49 @@ func TestSelectBatches(t *testing.T) {
|
|||
name: "high fee noncoop sweep",
|
||||
batches: []feeDetails{
|
||||
{
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
{
|
||||
BatchId: 2,
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
BatchId: 2,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
},
|
||||
sweep: feeDetails{
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: nonCoopInputWeight,
|
||||
CoopWeight: coopInputWeight,
|
||||
NonCoopWeight: nonCoopInputWeight,
|
||||
NonCoopHint: true,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: nonCoopInputWeight,
|
||||
},
|
||||
oneSweepBatch: feeDetails{
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: nonCoopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
NonCoopHint: true,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: nonCoopNewBatchWeight,
|
||||
},
|
||||
wantBestBatchesIds: []int32{2, newBatchSignal, 1},
|
||||
},
|
||||
|
||||
{
|
||||
name: "high fee noncoop sweep, large batches",
|
||||
batches: []feeDetails{
|
||||
{
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: 10000,
|
||||
CoopWeight: 10000,
|
||||
NonCoopWeight: 15000,
|
||||
},
|
||||
{
|
||||
BatchId: 2,
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: 10000,
|
||||
CoopWeight: 10000,
|
||||
NonCoopWeight: 15000,
|
||||
},
|
||||
},
|
||||
sweep: feeDetails{
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: nonCoopInputWeight,
|
||||
CoopWeight: coopInputWeight,
|
||||
NonCoopWeight: nonCoopInputWeight,
|
||||
NonCoopHint: true,
|
||||
},
|
||||
oneSweepBatch: feeDetails{
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: nonCoopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
NonCoopHint: true,
|
||||
},
|
||||
wantBestBatchesIds: []int32{newBatchSignal, 2, 1},
|
||||
},
|
||||
|
||||
{
|
||||
name: "high fee noncoop sweep, large batches, mixed",
|
||||
batches: []feeDetails{
|
||||
{
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: 10000,
|
||||
CoopWeight: 10000,
|
||||
NonCoopWeight: 15000,
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: 10000,
|
||||
},
|
||||
{
|
||||
BatchId: 2,
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: 10000,
|
||||
CoopWeight: 10000,
|
||||
NonCoopWeight: 15000,
|
||||
BatchId: 2,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: 10000,
|
||||
},
|
||||
},
|
||||
sweep: feeDetails{
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: nonCoopInputWeight,
|
||||
CoopWeight: coopInputWeight,
|
||||
NonCoopWeight: nonCoopInputWeight,
|
||||
NonCoopHint: true,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: nonCoopInputWeight,
|
||||
},
|
||||
oneSweepBatch: feeDetails{
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: nonCoopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
NonCoopHint: true,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: nonCoopNewBatchWeight,
|
||||
},
|
||||
mixedBatch: true,
|
||||
wantBestBatchesIds: []int32{2, newBatchSignal, 1},
|
||||
},
|
||||
|
||||
|
|
@ -689,34 +579,23 @@ func TestSelectBatches(t *testing.T) {
|
|||
name: "high fee noncoop sweep, high batch noncoop",
|
||||
batches: []feeDetails{
|
||||
{
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
{
|
||||
BatchId: 2,
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: nonCoopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
NonCoopHint: true,
|
||||
BatchId: 2,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: nonCoopNewBatchWeight,
|
||||
},
|
||||
},
|
||||
sweep: feeDetails{
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: nonCoopInputWeight,
|
||||
CoopWeight: coopInputWeight,
|
||||
NonCoopWeight: nonCoopInputWeight,
|
||||
NonCoopHint: true,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: nonCoopInputWeight,
|
||||
},
|
||||
oneSweepBatch: feeDetails{
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: nonCoopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
NonCoopHint: true,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: nonCoopNewBatchWeight,
|
||||
},
|
||||
wantBestBatchesIds: []int32{2, newBatchSignal, 1},
|
||||
},
|
||||
|
|
@ -725,105 +604,49 @@ func TestSelectBatches(t *testing.T) {
|
|||
name: "low fee noncoop sweep",
|
||||
batches: []feeDetails{
|
||||
{
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
{
|
||||
BatchId: 2,
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
BatchId: 2,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
},
|
||||
sweep: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: nonCoopInputWeight,
|
||||
CoopWeight: coopInputWeight,
|
||||
NonCoopWeight: nonCoopInputWeight,
|
||||
NonCoopHint: true,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: nonCoopInputWeight,
|
||||
},
|
||||
oneSweepBatch: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: nonCoopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
NonCoopHint: true,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: nonCoopNewBatchWeight,
|
||||
},
|
||||
wantBestBatchesIds: []int32{1, newBatchSignal, 2},
|
||||
},
|
||||
|
||||
{
|
||||
name: "low fee noncoop sweep, large batches",
|
||||
batches: []feeDetails{
|
||||
{
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: 10000,
|
||||
CoopWeight: 10000,
|
||||
NonCoopWeight: 15000,
|
||||
},
|
||||
{
|
||||
BatchId: 2,
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: 10000,
|
||||
CoopWeight: 10000,
|
||||
NonCoopWeight: 15000,
|
||||
},
|
||||
},
|
||||
sweep: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: nonCoopInputWeight,
|
||||
CoopWeight: coopInputWeight,
|
||||
NonCoopWeight: nonCoopInputWeight,
|
||||
NonCoopHint: true,
|
||||
},
|
||||
oneSweepBatch: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: nonCoopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
NonCoopHint: true,
|
||||
},
|
||||
wantBestBatchesIds: []int32{newBatchSignal, 1, 2},
|
||||
},
|
||||
|
||||
{
|
||||
name: "low fee noncoop sweep, large batches, mixed",
|
||||
batches: []feeDetails{
|
||||
{
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: 10000,
|
||||
CoopWeight: 10000,
|
||||
NonCoopWeight: 15000,
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: 10000,
|
||||
},
|
||||
{
|
||||
BatchId: 2,
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: 10000,
|
||||
CoopWeight: 10000,
|
||||
NonCoopWeight: 15000,
|
||||
BatchId: 2,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: 10000,
|
||||
},
|
||||
},
|
||||
sweep: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: nonCoopInputWeight,
|
||||
CoopWeight: coopInputWeight,
|
||||
NonCoopWeight: nonCoopInputWeight,
|
||||
NonCoopHint: true,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: nonCoopInputWeight,
|
||||
},
|
||||
oneSweepBatch: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: nonCoopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
NonCoopHint: true,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: nonCoopNewBatchWeight,
|
||||
},
|
||||
mixedBatch: true,
|
||||
wantBestBatchesIds: []int32{1, newBatchSignal, 2},
|
||||
},
|
||||
|
||||
|
|
@ -831,34 +654,23 @@ func TestSelectBatches(t *testing.T) {
|
|||
name: "low fee noncoop sweep, low batch noncoop",
|
||||
batches: []feeDetails{
|
||||
{
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: nonCoopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
NonCoopHint: true,
|
||||
BatchId: 1,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: nonCoopNewBatchWeight,
|
||||
},
|
||||
{
|
||||
BatchId: 2,
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
BatchId: 2,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
},
|
||||
sweep: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: nonCoopInputWeight,
|
||||
CoopWeight: coopInputWeight,
|
||||
NonCoopWeight: nonCoopInputWeight,
|
||||
NonCoopHint: true,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: nonCoopInputWeight,
|
||||
},
|
||||
oneSweepBatch: feeDetails{
|
||||
FeeRate: lowFeeRate,
|
||||
MixedWeight: nonCoopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
NonCoopHint: true,
|
||||
FeeRate: lowFeeRate,
|
||||
Weight: nonCoopNewBatchWeight,
|
||||
},
|
||||
wantBestBatchesIds: []int32{1, newBatchSignal, 2},
|
||||
},
|
||||
|
|
@ -867,32 +679,24 @@ func TestSelectBatches(t *testing.T) {
|
|||
name: "external address sweep",
|
||||
batches: []feeDetails{
|
||||
{
|
||||
BatchId: 1,
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
BatchId: 1,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
{
|
||||
BatchId: 2,
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
BatchId: 2,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
},
|
||||
sweep: feeDetails{
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: coopInputWeight,
|
||||
CoopWeight: coopInputWeight,
|
||||
NonCoopWeight: nonCoopInputWeight,
|
||||
Weight: coopInputWeight,
|
||||
IsExternalAddr: true,
|
||||
},
|
||||
oneSweepBatch: feeDetails{
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
Weight: coopNewBatchWeight,
|
||||
IsExternalAddr: true,
|
||||
},
|
||||
wantBestBatchesIds: []int32{newBatchSignal},
|
||||
|
|
@ -902,32 +706,24 @@ func TestSelectBatches(t *testing.T) {
|
|||
name: "external address batch",
|
||||
batches: []feeDetails{
|
||||
{
|
||||
BatchId: 1,
|
||||
FeeRate: highFeeRate - 1,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
BatchId: 1,
|
||||
FeeRate: highFeeRate - 1,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
{
|
||||
BatchId: 2,
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
Weight: coopNewBatchWeight,
|
||||
IsExternalAddr: true,
|
||||
},
|
||||
},
|
||||
sweep: feeDetails{
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: coopInputWeight,
|
||||
CoopWeight: coopInputWeight,
|
||||
NonCoopWeight: nonCoopInputWeight,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: coopInputWeight,
|
||||
},
|
||||
oneSweepBatch: feeDetails{
|
||||
FeeRate: highFeeRate,
|
||||
MixedWeight: coopNewBatchWeight,
|
||||
CoopWeight: coopNewBatchWeight,
|
||||
NonCoopWeight: nonCoopNewBatchWeight,
|
||||
FeeRate: highFeeRate,
|
||||
Weight: coopNewBatchWeight,
|
||||
},
|
||||
wantBestBatchesIds: []int32{1, newBatchSignal},
|
||||
},
|
||||
|
|
@ -938,7 +734,6 @@ func TestSelectBatches(t *testing.T) {
|
|||
t.Run(tc.name, func(t *testing.T) {
|
||||
gotBestBatchesIds, err := selectBatches(
|
||||
tc.batches, tc.sweep, tc.oneSweepBatch,
|
||||
tc.mixedBatch,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Equal(
|
||||
|
|
|
|||
|
|
@ -172,16 +172,6 @@ type batchConfig struct {
|
|||
// Note that musig2SignSweep must be nil in this case, however signer
|
||||
// client must still be provided, as it is used for non-coop spendings.
|
||||
customMuSig2Signer SignMuSig2
|
||||
|
||||
// mixedBatch instructs sweepbatcher to create mixed batches with regard
|
||||
// to cooperativeness. Such a batch can include sweeps signed both
|
||||
// cooperatively and non-cooperatively. If cooperative signing fails for
|
||||
// a sweep, transaction is updated to sign that sweep non-cooperatively
|
||||
// and another round of cooperative signing runs on the remaining
|
||||
// sweeps. The remaining sweeps are signed in non-cooperative (more
|
||||
// expensive) way. If the whole procedure fails for whatever reason, the
|
||||
// batch is signed non-cooperatively (the fallback).
|
||||
mixedBatch bool
|
||||
}
|
||||
|
||||
// rbfCache stores data related to our last fee bump.
|
||||
|
|
@ -821,27 +811,23 @@ func (b *batch) publish(ctx context.Context) error {
|
|||
b.publishErrorHandler(err, errMsg, b.log)
|
||||
}
|
||||
|
||||
if b.cfg.mixedBatch {
|
||||
fee, err, signSuccess = b.publishMixedBatch(ctx)
|
||||
if err != nil {
|
||||
logPublishError("mixed batch publish error", err)
|
||||
}
|
||||
} else {
|
||||
fee, err, signSuccess = b.publishBatchCoop(ctx)
|
||||
if err != nil {
|
||||
logPublishError("co-op publish error", err)
|
||||
}
|
||||
}
|
||||
|
||||
if !signSuccess {
|
||||
fee, err = b.publishBatch(ctx)
|
||||
if err != nil {
|
||||
logPublishError("non-coop publish error", err)
|
||||
}
|
||||
}
|
||||
|
||||
fee, err, signSuccess = b.publishMixedBatch(ctx)
|
||||
if err != nil {
|
||||
return nil
|
||||
if signSuccess {
|
||||
logPublishError("publish error", err)
|
||||
|
||||
// Publishing error is expected: "insufficient fee" and
|
||||
// "output already spent". Don't return the error here
|
||||
// not to break the main loop of the sweep batch.
|
||||
return nil
|
||||
} else {
|
||||
logPublishError("signing error", err)
|
||||
|
||||
// Signing error is not expected, because we have
|
||||
// non-cooperative method of signing which should
|
||||
// always succeed.
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
b.log.Infof("published, total sweeps: %v, fees: %v", len(b.sweeps), fee)
|
||||
|
|
@ -853,167 +839,6 @@ func (b *batch) publish(ctx context.Context) error {
|
|||
return b.persist(ctx)
|
||||
}
|
||||
|
||||
// publishBatch creates and publishes the batch transaction. It will consult the
|
||||
// RBFCache to determine the fee rate to use.
|
||||
func (b *batch) publishBatch(ctx context.Context) (btcutil.Amount, error) {
|
||||
// Create the batch transaction.
|
||||
batchTx := wire.NewMsgTx(2)
|
||||
batchTx.LockTime = uint32(b.currentHeight)
|
||||
|
||||
var (
|
||||
batchAmt btcutil.Amount
|
||||
prevOuts = make([]*wire.TxOut, 0, len(b.sweeps))
|
||||
signDescs = make(
|
||||
[]*lndclient.SignDescriptor, 0, len(b.sweeps),
|
||||
)
|
||||
sweeps = make([]sweep, 0, len(b.sweeps))
|
||||
fee btcutil.Amount
|
||||
inputCounter int
|
||||
addrOverride bool
|
||||
)
|
||||
|
||||
var weightEstimate input.TxWeightEstimator
|
||||
|
||||
// Add all the sweeps to the batch transaction.
|
||||
for _, sweep := range b.sweeps {
|
||||
if sweep.isExternalAddr {
|
||||
addrOverride = true
|
||||
}
|
||||
|
||||
batchAmt += sweep.value
|
||||
batchTx.AddTxIn(&wire.TxIn{
|
||||
PreviousOutPoint: sweep.outpoint,
|
||||
Sequence: sweep.htlc.SuccessSequence(),
|
||||
})
|
||||
|
||||
err := sweep.htlcSuccessEstimator(&weightEstimate)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Append this sweep to an array of sweeps. This is needed to
|
||||
// keep the order of sweeps stored, as iterating the sweeps map
|
||||
// does not guarantee same order.
|
||||
sweeps = append(sweeps, sweep)
|
||||
|
||||
// Create and store the previous outpoint for this sweep.
|
||||
prevOuts = append(prevOuts, &wire.TxOut{
|
||||
Value: int64(sweep.value),
|
||||
PkScript: sweep.htlc.PkScript,
|
||||
})
|
||||
|
||||
key, err := btcec.ParsePubKey(
|
||||
sweep.htlcKeys.ReceiverScriptKey[:],
|
||||
)
|
||||
if err != nil {
|
||||
return fee, err
|
||||
}
|
||||
|
||||
// Create and store the sign descriptor for this sweep.
|
||||
signDesc := lndclient.SignDescriptor{
|
||||
WitnessScript: sweep.htlc.SuccessScript(),
|
||||
Output: prevOuts[len(prevOuts)-1],
|
||||
HashType: sweep.htlc.SigHash(),
|
||||
InputIndex: inputCounter,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: key,
|
||||
},
|
||||
}
|
||||
|
||||
inputCounter++
|
||||
|
||||
if sweep.htlc.Version == swap.HtlcV3 {
|
||||
signDesc.SignMethod = input.TaprootScriptSpendSignMethod
|
||||
}
|
||||
|
||||
signDescs = append(signDescs, &signDesc)
|
||||
}
|
||||
|
||||
var address btcutil.Address
|
||||
|
||||
if addrOverride {
|
||||
// Sanity check, there should be exactly 1 sweep in this batch.
|
||||
if len(sweeps) != 1 {
|
||||
return 0, fmt.Errorf("external address sweep batched " +
|
||||
"with other sweeps")
|
||||
}
|
||||
|
||||
address = sweeps[0].destAddr
|
||||
} else {
|
||||
var err error
|
||||
address, err = b.getBatchDestAddr(ctx)
|
||||
if err != nil {
|
||||
return fee, err
|
||||
}
|
||||
}
|
||||
|
||||
batchPkScript, err := txscript.PayToAddrScript(address)
|
||||
if err != nil {
|
||||
return fee, err
|
||||
}
|
||||
|
||||
err = sweeppkg.AddOutputEstimate(&weightEstimate, address)
|
||||
if err != nil {
|
||||
return fee, err
|
||||
}
|
||||
|
||||
weight := weightEstimate.Weight()
|
||||
feeForWeight := b.rbfCache.FeeRate.FeeForWeight(weight)
|
||||
|
||||
// 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.
|
||||
batchTx.AddTxOut(&wire.TxOut{
|
||||
PkScript: batchPkScript,
|
||||
Value: int64(batchAmt - fee),
|
||||
})
|
||||
|
||||
// Collect the signatures for our inputs.
|
||||
rawSigs, err := b.signerClient.SignOutputRaw(
|
||||
ctx, batchTx, signDescs, prevOuts,
|
||||
)
|
||||
if err != nil {
|
||||
return fee, err
|
||||
}
|
||||
|
||||
for i, sweep := range sweeps {
|
||||
// Generate the success witness for the sweep.
|
||||
witness, err := sweep.htlc.GenSuccessWitness(
|
||||
rawSigs[i], sweep.preimage,
|
||||
)
|
||||
if err != nil {
|
||||
return fee, err
|
||||
}
|
||||
|
||||
// Add the success witness to our batch transaction's inputs.
|
||||
batchTx.TxIn[i].Witness = witness
|
||||
}
|
||||
|
||||
b.log.Infof("attempting to publish non-coop tx=%v with feerate=%v, "+
|
||||
"weight=%v, feeForWeight=%v, fee=%v, sweeps=%d, destAddr=%s",
|
||||
batchTx.TxHash(), b.rbfCache.FeeRate, weight, feeForWeight, fee,
|
||||
len(batchTx.TxIn), address)
|
||||
|
||||
b.debugLogTx("serialized non-coop sweep", batchTx)
|
||||
|
||||
err = b.wallet.PublishTransaction(
|
||||
ctx, batchTx, b.cfg.txLabeler(b.id),
|
||||
)
|
||||
if err != nil {
|
||||
return fee, err
|
||||
}
|
||||
|
||||
// Store the batch transaction's txid and pkScript, for monitoring
|
||||
// purposes.
|
||||
txHash := batchTx.TxHash()
|
||||
b.batchTxid = &txHash
|
||||
b.batchPkScript = batchPkScript
|
||||
|
||||
return fee, nil
|
||||
}
|
||||
|
||||
// createPsbt creates serialized PSBT and prevOuts map from unsignedTx and
|
||||
// the list of sweeps.
|
||||
func (b *batch) createPsbt(unsignedTx *wire.MsgTx, sweeps []sweep) ([]byte,
|
||||
|
|
@ -1055,134 +880,6 @@ func (b *batch) createPsbt(unsignedTx *wire.MsgTx, sweeps []sweep) ([]byte,
|
|||
return psbtBuf.Bytes(), prevOuts, nil
|
||||
}
|
||||
|
||||
// publishBatchCoop attempts to construct and publish a batch transaction that
|
||||
// collects all the required signatures interactively from the server. This
|
||||
// helps with collecting the funds immediately without revealing any information
|
||||
// related to the HTLC script.
|
||||
func (b *batch) publishBatchCoop(ctx context.Context) (btcutil.Amount,
|
||||
error, bool) {
|
||||
|
||||
var (
|
||||
batchAmt = btcutil.Amount(0)
|
||||
sweeps = make([]sweep, 0, len(b.sweeps))
|
||||
fee = btcutil.Amount(0)
|
||||
weightEstimate input.TxWeightEstimator
|
||||
addrOverride bool
|
||||
)
|
||||
|
||||
// Sanity check, there should be at least 1 sweep in this batch.
|
||||
if len(b.sweeps) == 0 {
|
||||
return 0, fmt.Errorf("no sweeps in batch"), false
|
||||
}
|
||||
|
||||
// Create the batch transaction.
|
||||
batchTx := &wire.MsgTx{
|
||||
Version: 2,
|
||||
LockTime: uint32(b.currentHeight),
|
||||
}
|
||||
|
||||
for _, sweep := range b.sweeps {
|
||||
// Append this sweep to an array of sweeps. This is needed to
|
||||
// keep the order of sweeps stored, as iterating the sweeps map
|
||||
// does not guarantee same order.
|
||||
sweeps = append(sweeps, sweep)
|
||||
}
|
||||
|
||||
// Add all the sweeps to the batch transaction.
|
||||
for _, sweep := range sweeps {
|
||||
if sweep.isExternalAddr {
|
||||
addrOverride = true
|
||||
}
|
||||
|
||||
// Keep track of the total amount this batch is sweeping back.
|
||||
batchAmt += sweep.value
|
||||
|
||||
// Add this sweep's input to the transaction.
|
||||
batchTx.AddTxIn(&wire.TxIn{
|
||||
PreviousOutPoint: sweep.outpoint,
|
||||
})
|
||||
|
||||
weightEstimate.AddTaprootKeySpendInput(txscript.SigHashDefault)
|
||||
}
|
||||
|
||||
var address btcutil.Address
|
||||
|
||||
if addrOverride {
|
||||
// Sanity check, there should be exactly 1 sweep in this batch.
|
||||
if len(sweeps) != 1 {
|
||||
return 0, fmt.Errorf("external address sweep batched " +
|
||||
"with other sweeps"), false
|
||||
}
|
||||
|
||||
address = sweeps[0].destAddr
|
||||
} else {
|
||||
var err error
|
||||
address, err = b.getBatchDestAddr(ctx)
|
||||
if err != nil {
|
||||
return fee, err, false
|
||||
}
|
||||
}
|
||||
|
||||
batchPkScript, err := txscript.PayToAddrScript(address)
|
||||
if err != nil {
|
||||
return fee, err, false
|
||||
}
|
||||
|
||||
err = sweeppkg.AddOutputEstimate(&weightEstimate, address)
|
||||
if err != nil {
|
||||
return fee, err, false
|
||||
}
|
||||
|
||||
weight := weightEstimate.Weight()
|
||||
feeForWeight := b.rbfCache.FeeRate.FeeForWeight(weight)
|
||||
|
||||
// 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.
|
||||
batchTx.AddTxOut(&wire.TxOut{
|
||||
PkScript: batchPkScript,
|
||||
Value: int64(batchAmt - fee),
|
||||
})
|
||||
|
||||
// Create PSBT and prevOuts.
|
||||
psbtBytes, prevOuts, err := b.createPsbt(batchTx, sweeps)
|
||||
if err != nil {
|
||||
return fee, err, false
|
||||
}
|
||||
|
||||
// Attempt to cooperatively sign the batch tx with the server.
|
||||
err = b.coopSignBatchTx(
|
||||
ctx, batchTx, sweeps, prevOuts, psbtBytes,
|
||||
)
|
||||
if err != nil {
|
||||
return fee, err, false
|
||||
}
|
||||
|
||||
b.log.Infof("attempting to publish coop tx=%v with feerate=%v, "+
|
||||
"weight=%v, feeForWeight=%v, fee=%v, sweeps=%d, destAddr=%s",
|
||||
batchTx.TxHash(), b.rbfCache.FeeRate, weight, feeForWeight, fee,
|
||||
len(batchTx.TxIn), address)
|
||||
|
||||
b.debugLogTx("serialized coop sweep", batchTx)
|
||||
|
||||
err = b.wallet.PublishTransaction(
|
||||
ctx, batchTx, b.cfg.txLabeler(b.id),
|
||||
)
|
||||
if err != nil {
|
||||
return fee, err, true
|
||||
}
|
||||
|
||||
// Store the batch transaction's txid and pkScript, for monitoring
|
||||
// purposes.
|
||||
txHash := batchTx.TxHash()
|
||||
b.batchTxid = &txHash
|
||||
b.batchPkScript = batchPkScript
|
||||
|
||||
return fee, nil, true
|
||||
}
|
||||
|
||||
// constructUnsignedTx creates unsigned tx from the sweeps, paying to the addr.
|
||||
// It also returns absolute fee (from weight and clamped).
|
||||
func (b *batch) constructUnsignedTx(sweeps []sweep,
|
||||
|
|
@ -1504,14 +1201,14 @@ func (b *batch) publishMixedBatch(ctx context.Context) (btcutil.Amount, error,
|
|||
}
|
||||
}
|
||||
txHash := tx.TxHash()
|
||||
b.log.Infof("attempting to publish mixed tx=%v with feerate=%v, "+
|
||||
b.log.Infof("attempting to publish batch tx=%v with feerate=%v, "+
|
||||
"weight=%v, feeForWeight=%v, fee=%v, sweeps=%d, "+
|
||||
"%d cooperative: (%s) and %d non-cooperative (%s), destAddr=%s",
|
||||
txHash, b.rbfCache.FeeRate, weight, feeForWeight, fee,
|
||||
len(tx.TxIn), coopInputs, strings.Join(coopHexs, ", "),
|
||||
nonCoopInputs, strings.Join(nonCoopHexs, ", "), address)
|
||||
|
||||
b.debugLogTx("serialized mixed batch", tx)
|
||||
b.debugLogTx("serialized batch", tx)
|
||||
|
||||
// Make sure tx weight matches the expected value.
|
||||
realWeight := lntypes.WeightUnit(
|
||||
|
|
@ -1549,30 +1246,6 @@ func (b *batch) debugLogTx(msg string, tx *wire.MsgTx) {
|
|||
b.log.Debugf("%s: %s", msg, hex.EncodeToString(buf.Bytes()))
|
||||
}
|
||||
|
||||
// coopSignBatchTx collects the necessary signatures from the server in order
|
||||
// to cooperatively sweep the funds.
|
||||
func (b *batch) coopSignBatchTx(ctx context.Context, tx *wire.MsgTx,
|
||||
sweeps []sweep, prevOuts map[wire.OutPoint]*wire.TxOut,
|
||||
psbt []byte) error {
|
||||
|
||||
for i, sweep := range sweeps {
|
||||
sweep := sweep
|
||||
|
||||
finalSig, err := b.musig2sign(
|
||||
ctx, i, sweep, tx, prevOuts, psbt,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tx.TxIn[i].Witness = wire.TxWitness{
|
||||
finalSig,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// musig2sign signs one sweep using musig2.
|
||||
func (b *batch) musig2sign(ctx context.Context, inputIndex int, sweep sweep,
|
||||
unsignedTx *wire.MsgTx, prevOuts map[wire.OutPoint]*wire.TxOut,
|
||||
|
|
|
|||
|
|
@ -309,16 +309,6 @@ type Batcher struct {
|
|||
// client must still be provided, as it is used for non-coop spendings.
|
||||
customMuSig2Signer SignMuSig2
|
||||
|
||||
// mixedBatch instructs sweepbatcher to create mixed batches with regard
|
||||
// to cooperativeness. Such a batch can include sweeps signed both
|
||||
// cooperatively and non-cooperatively. If cooperative signing fails for
|
||||
// a sweep, transaction is updated to sign that sweep non-cooperatively
|
||||
// and another round of cooperative signing runs on the remaining
|
||||
// sweeps. The remaining sweeps are signed in non-cooperative (more
|
||||
// expensive) way. If the whole procedure fails for whatever reason, the
|
||||
// batch is signed non-cooperatively (the fallback).
|
||||
mixedBatch bool
|
||||
|
||||
// publishErrorHandler is a function that handles transaction publishing
|
||||
// error. By default, it logs all errors as warnings, but "insufficient
|
||||
// fee" as Info.
|
||||
|
|
@ -359,16 +349,6 @@ type BatcherConfig struct {
|
|||
// client must still be provided, as it is used for non-coop spendings.
|
||||
customMuSig2Signer SignMuSig2
|
||||
|
||||
// mixedBatch instructs sweepbatcher to create mixed batches with regard
|
||||
// to cooperativeness. Such a batch can include sweeps signed both
|
||||
// cooperatively and non-cooperatively. If cooperative signing fails for
|
||||
// a sweep, transaction is updated to sign that sweep non-cooperatively
|
||||
// and another round of cooperative signing runs on the remaining
|
||||
// sweeps. The remaining sweeps are signed in non-cooperative (more
|
||||
// expensive) way. If the whole procedure fails for whatever reason, the
|
||||
// batch is signed non-cooperatively (the fallback).
|
||||
mixedBatch bool
|
||||
|
||||
// publishErrorHandler is a function that handles transaction publishing
|
||||
// error. By default, it logs all errors as warnings, but "insufficient
|
||||
// fee" as Info.
|
||||
|
|
@ -438,20 +418,6 @@ func WithCustomSignMuSig2(customMuSig2Signer SignMuSig2) BatcherOption {
|
|||
}
|
||||
}
|
||||
|
||||
// WithMixedBatch instructs sweepbatcher to create mixed batches with
|
||||
// regard to cooperativeness. Such a batch can include both sweeps signed
|
||||
// both cooperatively and non-cooperatively. If cooperative signing fails
|
||||
// for a sweep, transaction is updated to sign that sweep non-cooperatively
|
||||
// and another round of cooperative signing runs on the remaining sweeps.
|
||||
// The remaining sweeps are signed in non-cooperative (more expensive) way.
|
||||
// If the whole procedure fails for whatever reason, the batch is signed
|
||||
// non-cooperatively (the fallback).
|
||||
func WithMixedBatch() BatcherOption {
|
||||
return func(cfg *BatcherConfig) {
|
||||
cfg.mixedBatch = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithPublishErrorHandler sets the callback used to handle publish errors.
|
||||
// It can be used to filter out noisy messages.
|
||||
func WithPublishErrorHandler(handler PublishErrorHandler) BatcherOption {
|
||||
|
|
@ -512,7 +478,6 @@ func NewBatcher(wallet lndclient.WalletKitClient,
|
|||
customFeeRate: cfg.customFeeRate,
|
||||
txLabeler: cfg.txLabeler,
|
||||
customMuSig2Signer: cfg.customMuSig2Signer,
|
||||
mixedBatch: cfg.mixedBatch,
|
||||
publishErrorHandler: cfg.publishErrorHandler,
|
||||
}
|
||||
}
|
||||
|
|
@ -1131,7 +1096,6 @@ func (b *Batcher) newBatchConfig(maxTimeoutDistance int32) batchConfig {
|
|||
txLabeler: b.txLabeler,
|
||||
customMuSig2Signer: b.customMuSig2Signer,
|
||||
clock: b.clock,
|
||||
mixedBatch: b.mixedBatch,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3208,9 +3208,11 @@ func testWithMixedBatch(t *testing.T, store testStore,
|
|||
}
|
||||
|
||||
// Use mixed batches.
|
||||
batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
|
||||
batcher := NewBatcher(
|
||||
lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
|
||||
muSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams,
|
||||
batcherStore, sweepFetcher, WithMixedBatch())
|
||||
batcherStore, sweepFetcher,
|
||||
)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
|
|
@ -3385,9 +3387,11 @@ func testWithMixedBatchCustom(t *testing.T, store testStore,
|
|||
}
|
||||
|
||||
// Use mixed batches.
|
||||
batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
|
||||
batcher := NewBatcher(
|
||||
lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
|
||||
muSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams,
|
||||
batcherStore, sweepFetcher, WithMixedBatch())
|
||||
batcherStore, sweepFetcher,
|
||||
)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue