Skip to content

Commit e3cc4d7

Browse files
authored
Merge pull request #8600 from GeorgeTsagk/chanfunding-coinselect-nofeecheck
Add `maxFeeRatio` parameter to sanityCheckFee in psbt coin selection
2 parents 8517581 + c1f5908 commit e3cc4d7

File tree

10 files changed

+520
-334
lines changed

10 files changed

+520
-334
lines changed

cmd/commands/walletrpc_active.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,6 +1252,12 @@ var fundPsbtCommand = cli.Command{
12521252
Value: defaultUtxoMinConf,
12531253
},
12541254
coinSelectionStrategyFlag,
1255+
cli.Float64Flag{
1256+
Name: "max_fee_ratio",
1257+
Usage: "the maximum fee to total output amount ratio " +
1258+
"that this psbt should adhere to",
1259+
Value: chanfunding.DefaultMaxFeeRatio,
1260+
},
12551261
},
12561262
Action: actionDecorator(fundPsbt),
12571263
}
@@ -1390,6 +1396,8 @@ func fundPsbt(ctx *cli.Context) error {
13901396
}
13911397
}
13921398

1399+
req.MaxFeeRatio = ctx.Float64("max_fee_ratio")
1400+
13931401
walletClient, cleanUp := getWalletClient(ctx)
13941402
defer cleanUp()
13951403

docs/release-notes/release-notes-0.19.0.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@
5252
`sat_per_kw` which allows for more precise
5353
fees](https://github.com/lightningnetwork/lnd/pull/9013).
5454

55+
* [The `walletrpc.FundPsbt` method now has a new option to specify the maximum
56+
fee to output amounts ratio.](https://github.com/lightningnetwork/lnd/pull/8600)
57+
5558
## lncli Additions
5659

5760
* [A pre-generated macaroon root key can now be specified in `lncli create` and
@@ -62,6 +65,9 @@
6265
specify more precise fee
6366
rates](https://github.com/lightningnetwork/lnd/pull/9013).
6467

68+
* The `lncli wallet fundpsbt` command now has a [`--max_fee_ratio` argument to
69+
specify the max fees to output amounts ratio.](https://github.com/lightningnetwork/lnd/pull/8600)
70+
6571
# Improvements
6672
## Functional Updates
6773

@@ -151,6 +157,7 @@
151157
* Boris Nagaev
152158
* CharlieZKSmith
153159
* Elle Mouton
160+
* George Tsagkarelis
154161
* Pins
155162
* Viktor Tigerström
156163
* Ziggie

lnrpc/walletrpc/walletkit.pb.go

Lines changed: 314 additions & 302 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lnrpc/walletrpc/walletkit.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,6 +1417,9 @@ message FundPsbtRequest {
14171417

14181418
// The strategy to use for selecting coins during funding the PSBT.
14191419
lnrpc.CoinSelectionStrategy coin_selection_strategy = 10;
1420+
1421+
// The max fee to total output amount ratio that this psbt should adhere to.
1422+
double max_fee_ratio = 12;
14201423
}
14211424
message FundPsbtResponse {
14221425
/*

lnrpc/walletrpc/walletkit.swagger.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,6 +1589,11 @@
15891589
"coin_selection_strategy": {
15901590
"$ref": "#/definitions/lnrpcCoinSelectionStrategy",
15911591
"description": "The strategy to use for selecting coins during funding the PSBT."
1592+
},
1593+
"max_fee_ratio": {
1594+
"type": "number",
1595+
"format": "double",
1596+
"description": "The max fee to total output amount ratio that this psbt should adhere to."
15921597
}
15931598
}
15941599
},

lnrpc/walletrpc/walletkit_server.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1626,11 +1626,17 @@ func (w *WalletKit) FundPsbt(_ context.Context,
16261626
return nil, fmt.Errorf("unknown change output type")
16271627
}
16281628

1629+
maxFeeRatio := chanfunding.DefaultMaxFeeRatio
1630+
1631+
if req.MaxFeeRatio != 0 {
1632+
maxFeeRatio = req.MaxFeeRatio
1633+
}
1634+
16291635
// Run the actual funding process now, using the channel funding
16301636
// coin selection algorithm.
16311637
return w.fundPsbtCoinSelect(
16321638
account, changeIndex, packet, minConfs, changeType,
1633-
feeSatPerKW, coinSelectionStrategy,
1639+
feeSatPerKW, coinSelectionStrategy, maxFeeRatio,
16341640
)
16351641

16361642
// The template is specified as a RPC message. We need to create a new
@@ -1833,8 +1839,8 @@ func (w *WalletKit) fundPsbtInternalWallet(account string,
18331839
func (w *WalletKit) fundPsbtCoinSelect(account string, changeIndex int32,
18341840
packet *psbt.Packet, minConfs int32,
18351841
changeType chanfunding.ChangeAddressType,
1836-
feeRate chainfee.SatPerKWeight, strategy base.CoinSelectionStrategy) (
1837-
*FundPsbtResponse, error) {
1842+
feeRate chainfee.SatPerKWeight, strategy base.CoinSelectionStrategy,
1843+
maxFeeRatio float64) (*FundPsbtResponse, error) {
18381844

18391845
// We want to make sure we don't select any inputs that are already
18401846
// specified in the template. To do that, we require those inputs to
@@ -1922,6 +1928,7 @@ func (w *WalletKit) fundPsbtCoinSelect(account string, changeIndex int32,
19221928
changeAmt, needMore, err := chanfunding.CalculateChangeAmount(
19231929
inputSum, outputSum, packetFeeNoChange,
19241930
packetFeeWithChange, changeDustLimit, changeType,
1931+
maxFeeRatio,
19251932
)
19261933
if err != nil {
19271934
return nil, fmt.Errorf("error calculating change "+
@@ -1978,7 +1985,7 @@ func (w *WalletKit) fundPsbtCoinSelect(account string, changeIndex int32,
19781985

19791986
selectedCoins, changeAmount, err := chanfunding.CoinSelect(
19801987
feeRate, fundingAmount, changeDustLimit, coins,
1981-
strategy, estimator, changeType,
1988+
strategy, estimator, changeType, maxFeeRatio,
19821989
)
19831990
if err != nil {
19841991
return fmt.Errorf("error selecting coins: %w", err)

lnrpc/walletrpc/walletkit_server_test.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,18 @@ func TestFundPsbtCoinSelect(t *testing.T) {
162162
// packet in bytes.
163163
expectedFee btcutil.Amount
164164

165+
// maxFeeRatio is the maximum fee to total output amount ratio
166+
// that we consider valid.
167+
maxFeeRatio float64
168+
165169
// expectedErr is the expected concrete error. If not nil, then
166170
// the error must match exactly.
167171
expectedErr error
168172

173+
// expectedContainedErrStr is the expected string to be
174+
// contained in the returned error.
175+
expectedContainedErrStr string
176+
169177
// expectedErrType is the expected error type. If not nil, then
170178
// the error must be of this type.
171179
expectedErrType error
@@ -178,6 +186,7 @@ func TestFundPsbtCoinSelect(t *testing.T) {
178186
}),
179187
changeIndex: -1,
180188
feeRate: chainfee.FeePerKwFloor,
189+
maxFeeRatio: chanfunding.DefaultMaxFeeRatio,
181190
expectedErrType: &chanfunding.ErrInsufficientFunds{},
182191
}, {
183192
name: "1 p2wpkh utxo, add p2wkh change",
@@ -193,6 +202,7 @@ func TestFundPsbtCoinSelect(t *testing.T) {
193202
}),
194203
changeIndex: -1,
195204
feeRate: chainfee.FeePerKwFloor,
205+
maxFeeRatio: chanfunding.DefaultMaxFeeRatio,
196206
expectedUtxoIndexes: []int{0},
197207
expectChangeOutputIndex: 1,
198208
expectedFee: calcFee(0, 1, 1, 1, 0),
@@ -210,6 +220,7 @@ func TestFundPsbtCoinSelect(t *testing.T) {
210220
}),
211221
changeIndex: -1,
212222
feeRate: chainfee.FeePerKwFloor,
223+
maxFeeRatio: chanfunding.DefaultMaxFeeRatio,
213224
changeType: chanfunding.P2TRChangeAddress,
214225
expectedUtxoIndexes: []int{0},
215226
expectChangeOutputIndex: 1,
@@ -228,6 +239,7 @@ func TestFundPsbtCoinSelect(t *testing.T) {
228239
}),
229240
changeIndex: -1,
230241
feeRate: chainfee.FeePerKwFloor,
242+
maxFeeRatio: chanfunding.DefaultMaxFeeRatio,
231243
expectedUtxoIndexes: []int{0},
232244
expectChangeOutputIndex: -1,
233245
expectedFee: calcFee(0, 1, 1, 0, 0),
@@ -247,6 +259,7 @@ func TestFundPsbtCoinSelect(t *testing.T) {
247259
}),
248260
changeIndex: -1,
249261
feeRate: chainfee.FeePerKwFloor,
262+
maxFeeRatio: chanfunding.DefaultMaxFeeRatio,
250263
changeType: chanfunding.P2WKHChangeAddress,
251264
expectedUtxoIndexes: []int{0},
252265
expectChangeOutputIndex: -1,
@@ -267,6 +280,7 @@ func TestFundPsbtCoinSelect(t *testing.T) {
267280
}),
268281
changeIndex: -1,
269282
feeRate: chainfee.FeePerKwFloor,
283+
maxFeeRatio: chanfunding.DefaultMaxFeeRatio,
270284
changeType: chanfunding.P2TRChangeAddress,
271285
expectedUtxoIndexes: []int{0},
272286
expectChangeOutputIndex: -1,
@@ -285,6 +299,7 @@ func TestFundPsbtCoinSelect(t *testing.T) {
285299
}),
286300
changeIndex: 0,
287301
feeRate: chainfee.FeePerKwFloor,
302+
maxFeeRatio: chanfunding.DefaultMaxFeeRatio,
288303
changeType: chanfunding.ExistingChangeAddress,
289304
expectedUtxoIndexes: []int{0},
290305
expectChangeOutputIndex: 0,
@@ -303,6 +318,7 @@ func TestFundPsbtCoinSelect(t *testing.T) {
303318
}),
304319
changeIndex: 0,
305320
feeRate: chainfee.FeePerKwFloor,
321+
maxFeeRatio: chanfunding.DefaultMaxFeeRatio,
306322
changeType: chanfunding.ExistingChangeAddress,
307323
expectedUtxoIndexes: []int{0},
308324
expectChangeOutputIndex: 0,
@@ -321,6 +337,7 @@ func TestFundPsbtCoinSelect(t *testing.T) {
321337
}),
322338
changeIndex: 0,
323339
feeRate: chainfee.FeePerKwFloor,
340+
maxFeeRatio: chanfunding.DefaultMaxFeeRatio,
324341
changeType: chanfunding.ExistingChangeAddress,
325342
expectedUtxoIndexes: []int{0},
326343
expectChangeOutputIndex: 0,
@@ -369,6 +386,7 @@ func TestFundPsbtCoinSelect(t *testing.T) {
369386
}),
370387
changeIndex: 0,
371388
feeRate: chainfee.FeePerKwFloor,
389+
maxFeeRatio: chanfunding.DefaultMaxFeeRatio,
372390
changeType: chanfunding.ExistingChangeAddress,
373391
expectedUtxoIndexes: []int{0, 1},
374392
expectChangeOutputIndex: 0,
@@ -417,6 +435,7 @@ func TestFundPsbtCoinSelect(t *testing.T) {
417435
}),
418436
changeIndex: -1,
419437
feeRate: chainfee.FeePerKwFloor,
438+
maxFeeRatio: chanfunding.DefaultMaxFeeRatio,
420439
changeType: chanfunding.P2TRChangeAddress,
421440
expectedUtxoIndexes: []int{0, 1},
422441
expectChangeOutputIndex: 1,
@@ -456,6 +475,7 @@ func TestFundPsbtCoinSelect(t *testing.T) {
456475
}),
457476
changeIndex: -1,
458477
feeRate: chainfee.FeePerKwFloor,
478+
maxFeeRatio: chanfunding.DefaultMaxFeeRatio,
459479
changeType: chanfunding.P2WKHChangeAddress,
460480
expectedUtxoIndexes: []int{},
461481
expectChangeOutputIndex: 1,
@@ -497,10 +517,74 @@ func TestFundPsbtCoinSelect(t *testing.T) {
497517
}),
498518
changeIndex: -1,
499519
feeRate: chainfee.FeePerKwFloor,
520+
maxFeeRatio: chanfunding.DefaultMaxFeeRatio,
500521
changeType: chanfunding.P2TRChangeAddress,
501522
expectedUtxoIndexes: []int{},
502523
expectChangeOutputIndex: -1,
503524
expectedFee: calcFee(1, 0, 1, 0, 0),
525+
}, {
526+
name: "1 p2wpkh utxo, existing p2wkh change, invalid fee ratio",
527+
utxos: []*lnwallet.Utxo{
528+
{
529+
Value: 250,
530+
PkScript: p2wkhScript,
531+
},
532+
},
533+
packet: makePacket(&wire.TxOut{
534+
Value: 50,
535+
PkScript: p2wkhScript,
536+
}),
537+
changeIndex: 0,
538+
feeRate: chainfee.FeePerKwFloor,
539+
maxFeeRatio: chanfunding.DefaultMaxFeeRatio,
540+
changeType: chanfunding.ExistingChangeAddress,
541+
expectedUtxoIndexes: []int{0},
542+
expectChangeOutputIndex: 0,
543+
expectedFee: calcFee(0, 1, 0, 1, 0),
544+
545+
expectedContainedErrStr: "fee 0.00000111 BTC exceeds max fee " +
546+
"(0.00000027 BTC) on total output value",
547+
}, {
548+
name: "1 p2wpkh utxo, existing p2wkh change, negative feeratio",
549+
utxos: []*lnwallet.Utxo{
550+
{
551+
Value: 250,
552+
PkScript: p2wkhScript,
553+
},
554+
},
555+
packet: makePacket(&wire.TxOut{
556+
Value: 50,
557+
PkScript: p2wkhScript,
558+
}),
559+
changeIndex: 0,
560+
feeRate: chainfee.FeePerKwFloor,
561+
maxFeeRatio: chanfunding.DefaultMaxFeeRatio * (-1),
562+
changeType: chanfunding.ExistingChangeAddress,
563+
expectedUtxoIndexes: []int{0},
564+
expectChangeOutputIndex: 0,
565+
expectedFee: calcFee(0, 1, 0, 1, 0),
566+
567+
expectedContainedErrStr: "maxFeeRatio must be between 0.00 " +
568+
"and 1.00 got -0.20",
569+
}, {
570+
name: "1 p2wpkh utxo, existing p2wkh change, big fee ratio",
571+
utxos: []*lnwallet.Utxo{
572+
{
573+
Value: 250,
574+
PkScript: p2wkhScript,
575+
},
576+
},
577+
packet: makePacket(&wire.TxOut{
578+
Value: 50,
579+
PkScript: p2wkhScript,
580+
}),
581+
changeIndex: 0,
582+
feeRate: chainfee.FeePerKwFloor,
583+
maxFeeRatio: 0.85,
584+
changeType: chanfunding.ExistingChangeAddress,
585+
expectedUtxoIndexes: []int{0},
586+
expectChangeOutputIndex: 0,
587+
expectedFee: calcFee(0, 1, 0, 1, 0),
504588
}, {
505589
name: "large existing p2tr input, fee estimation existing " +
506590
"change output",
@@ -536,6 +620,7 @@ func TestFundPsbtCoinSelect(t *testing.T) {
536620
}),
537621
changeIndex: 0,
538622
feeRate: chainfee.FeePerKwFloor,
623+
maxFeeRatio: chanfunding.DefaultMaxFeeRatio,
539624
changeType: chanfunding.ExistingChangeAddress,
540625
expectedUtxoIndexes: []int{},
541626
expectChangeOutputIndex: 0,
@@ -575,6 +660,7 @@ func TestFundPsbtCoinSelect(t *testing.T) {
575660
"", tc.changeIndex, copiedPacket, 0,
576661
tc.changeType, tc.feeRate,
577662
rpcServer.cfg.CoinSelectionStrategy,
663+
tc.maxFeeRatio,
578664
)
579665

580666
switch {
@@ -588,6 +674,12 @@ func TestFundPsbtCoinSelect(t *testing.T) {
588674
require.Error(tt, err)
589675
require.ErrorAs(tt, err, &tc.expectedErr)
590676

677+
return
678+
case tc.expectedContainedErrStr != "":
679+
require.ErrorContains(
680+
tt, err, tc.expectedContainedErrStr,
681+
)
682+
591683
return
592684
}
593685

0 commit comments

Comments
 (0)