Skip to content

Commit de7b910

Browse files
committed
staticaddr: high fee versions of the htlc tx
In this commit we add signings of higher fee htlc txns. The server requires the client to sign higher fee versions to cover for high fee environments where pending transactions take a long time to confirm.
1 parent 6814b3d commit de7b910

File tree

4 files changed

+148
-21
lines changed

4 files changed

+148
-21
lines changed

staticaddr/loopin/actions.go

Lines changed: 93 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ const (
3131
defaultConfTarget = 3
3232
)
3333

34+
var (
35+
// ErrFeeTooHigh is returned if the server sets a fee rate for the htlc
36+
// tx that is too high. We prevent here against a low htlc timeout sweep
37+
// amount.
38+
ErrFeeTooHigh = errors.New("fee is higher than 20% of sweep value")
39+
)
40+
3441
// InitHtlcAction is executed if all loop-in information has been validated. We
3542
// assemble a loop-in request and send it to the server.
3643
func (f *FSM) InitHtlcAction(_ fsm.EventContext) fsm.EventType {
@@ -132,22 +139,53 @@ func (f *FSM) InitHtlcAction(_ fsm.EventContext) fsm.EventType {
132139
}
133140

134141
f.loopIn.HtlcCltvExpiry = loopInResp.HtlcExpiry
142+
143+
// Retrieve htlc tx server nonces.
135144
f.htlcServerNonces, err = toNonces(loopInResp.HtlcServerNonces)
136145
if err != nil {
137146
err = fmt.Errorf("unable to convert server nonces: %w", err)
138147
return f.HandleError(err)
139148
}
149+
f.htlcServerNoncesHighFee, err = toNonces(
150+
loopInResp.HtlcServerNoncesHighFee,
151+
)
152+
if err != nil {
153+
return f.HandleError(err)
154+
}
155+
f.htlcServerNoncesExtremelyHighFee, err = toNonces(
156+
loopInResp.HtlcServerNoncesExtremelyHighFee,
157+
)
158+
if err != nil {
159+
return f.HandleError(err)
160+
}
140161

141162
// We need to defend against the server setting high fees for the htlc
142163
// tx since we might have to sweep the timeout path. We maximally allow
143164
// 20% of the swap value to be spent on fees.
144-
f.loopIn.HtlcTxFeeRate = chainfee.SatPerKWeight(loopInResp.HtlcFeeRate)
145-
fee := f.loopIn.HtlcTxFeeRate.FeeForWeight(f.loopIn.htlcWeight())
165+
maxFee := f.loopIn.TotalDepositAmount() / 5
146166

147-
if fee > f.loopIn.TotalDepositAmount()/5 {
148-
return f.HandleError(errors.New("fee is higher than 20% of " +
149-
"sweep value"))
167+
feeRate := chainfee.SatPerKWeight(loopInResp.HtlcFeeRate)
168+
fee := feeRate.FeeForWeight(f.loopIn.htlcWeight())
169+
if fee > maxFee {
170+
return f.HandleError(ErrFeeTooHigh)
150171
}
172+
f.loopIn.HtlcTxFeeRate = feeRate
173+
174+
highFeeRate := chainfee.SatPerKWeight(loopInResp.HighHtlcFeeRate)
175+
fee = highFeeRate.FeeForWeight(f.loopIn.htlcWeight())
176+
if fee > maxFee {
177+
return f.HandleError(ErrFeeTooHigh)
178+
}
179+
f.loopIn.HtlcTxHighFeeRate = highFeeRate
180+
181+
extremelyHighFeeRate := chainfee.SatPerKWeight(
182+
loopInResp.ExtremelyHighHtlcFeeRate,
183+
)
184+
fee = extremelyHighFeeRate.FeeForWeight(f.loopIn.htlcWeight())
185+
if fee > maxFee {
186+
return f.HandleError(ErrFeeTooHigh)
187+
}
188+
f.loopIn.HtlcTxExtremelyHighFeeRate = extremelyHighFeeRate
151189

152190
// Derive the sweep address for the htlc timeout sweep tx.
153191
sweepAddress, err := f.cfg.WalletKit.NextAddr(
@@ -195,26 +233,51 @@ func (f *FSM) SignHtlcTxAction(_ fsm.EventContext) fsm.EventType {
195233
return f.HandleError(err)
196234
}
197235

198-
// Create a musig2 session for each deposit.
236+
// Create a musig2 session for each deposit and different htlc tx fee
237+
// rates.
199238
htlcSessions, clientHtlcNonces, err := f.loopIn.createMusig2Sessions(
200239
f.ctx, f.cfg.Signer,
201240
)
202241
if err != nil {
203242
err = fmt.Errorf("unable to create musig2 sessions: %w", err)
204243
return f.HandleError(err)
205244
}
245+
htlcSessionsHighFee, highFeeNonces, err := f.loopIn.createMusig2Sessions(
246+
f.ctx, f.cfg.Signer,
247+
)
248+
if err != nil {
249+
return f.HandleError(err)
250+
}
251+
htlcSessionsExtremelyHighFee, extremelyHighNonces, err := f.loopIn.createMusig2Sessions( //nolint:lll
252+
f.ctx, f.cfg.Signer,
253+
)
206254
if err != nil {
207255
err = fmt.Errorf("unable to convert nonces: %w", err)
208256
return f.HandleError(err)
209257
}
210258

211-
htlcTx, err := f.loopIn.createHtlcTx(f.cfg.ChainParams)
259+
// Create the htlc txns for different fee rates.
260+
htlcTx, err := f.loopIn.createHtlcTx(
261+
f.cfg.ChainParams, f.loopIn.HtlcTxFeeRate,
262+
)
263+
if err != nil {
264+
return f.HandleError(err)
265+
}
266+
htlcTxHighFee, err := f.loopIn.createHtlcTx(
267+
f.cfg.ChainParams, f.loopIn.HtlcTxHighFeeRate,
268+
)
269+
if err != nil {
270+
return f.HandleError(err)
271+
}
272+
htlcTxExtremelyHighFee, err := f.loopIn.createHtlcTx(
273+
f.cfg.ChainParams, f.loopIn.HtlcTxExtremelyHighFeeRate,
274+
)
212275
if err != nil {
213276
err = fmt.Errorf("unable to create the htlc tx: %w", err)
214277
return f.HandleError(err)
215278
}
216279

217-
// Next we'll get our htlc tx signatures.
280+
// Next we'll get our htlc tx signatures for different fee rates.
218281
htlcSigs, err := f.loopIn.signMusig2Tx(
219282
f.ctx, htlcTx, f.cfg.Signer, htlcSessions, f.htlcServerNonces,
220283
)
@@ -223,11 +286,30 @@ func (f *FSM) SignHtlcTxAction(_ fsm.EventContext) fsm.EventType {
223286
return f.HandleError(err)
224287
}
225288

289+
htlcSigsHighFee, err := f.loopIn.signMusig2Tx(
290+
f.ctx, htlcTxHighFee, f.cfg.Signer, htlcSessionsHighFee,
291+
f.htlcServerNoncesHighFee,
292+
)
293+
if err != nil {
294+
return f.HandleError(err)
295+
}
296+
htlcSigsExtremelyHighFee, err := f.loopIn.signMusig2Tx(
297+
f.ctx, htlcTxExtremelyHighFee, f.cfg.Signer,
298+
htlcSessionsExtremelyHighFee, f.htlcServerNoncesExtremelyHighFee,
299+
)
300+
if err != nil {
301+
return f.HandleError(err)
302+
}
303+
226304
// Push htlc tx sigs to server.
227305
pushHtlcReq := &looprpc.PushStaticAddressHtlcSigsRequest{
228-
SwapHash: f.loopIn.SwapHash[:],
229-
HtlcClientNonces: clientHtlcNonces,
230-
HtlcClientSigs: htlcSigs,
306+
SwapHash: f.loopIn.SwapHash[:],
307+
HtlcClientNonces: clientHtlcNonces,
308+
HtlcClientNoncesHighFee: highFeeNonces,
309+
HtlcClientNoncesExtremelyHighFee: extremelyHighNonces,
310+
HtlcClientSigs: htlcSigs,
311+
HtlcClientSigsHighFee: htlcSigsHighFee,
312+
HtlcClientSigsExtremelyHighFee: htlcSigsExtremelyHighFee,
231313
}
232314
_, err = f.cfg.Server.PushStaticAddressHtlcSigs(f.ctx, pushHtlcReq)
233315
if err != nil {

staticaddr/loopin/fsm.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ type FSM struct {
2828
// htlcServerNonces contains all the nonces that the server generated
2929
// for the htlc musig2 sessions.
3030
htlcServerNonces [][musig2.PubNonceSize]byte
31+
32+
// htlcServerNoncesHighFee contains all the high fee nonces that the
33+
// server generated for the htlc musig2 sessions.
34+
htlcServerNoncesHighFee [][musig2.PubNonceSize]byte
35+
36+
// htlcServerNoncesExtremelyHighFee contains all the extremely high fee
37+
// nonces that the server generated for the htlc musig2 sessions.
38+
htlcServerNoncesExtremelyHighFee [][musig2.PubNonceSize]byte
3139
}
3240

3341
// NewFSM creates a new loop-in state machine.

staticaddr/loopin/loopin.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,14 @@ type StaticAddressLoopIn struct {
102102
// HtlcTxFeeRate is the fee rate that is used for the htlc transaction.
103103
HtlcTxFeeRate chainfee.SatPerKWeight
104104

105+
// HtlcTxHighFeeRate is the fee rate that is used for the htlc
106+
// transaction.
107+
HtlcTxHighFeeRate chainfee.SatPerKWeight
108+
109+
// HtlcTxExtremelyHighFeeRate is the fee rate that is used for the htlc
110+
// transaction.
111+
HtlcTxExtremelyHighFeeRate chainfee.SatPerKWeight
112+
105113
// HtlcTimeoutSweepTxHash is the hash of the htlc timeout sweep tx.
106114
HtlcTimeoutSweepTxHash *chainhash.Hash
107115

@@ -295,8 +303,8 @@ func (l *StaticAddressLoopIn) signMusig2Tx(ctx context.Context,
295303

296304
// createHtlcTx creates the transaction that spend the deposit outpoints into
297305
// a htlc outpoint.
298-
func (l *StaticAddressLoopIn) createHtlcTx(chainParams *chaincfg.Params) (
299-
*wire.MsgTx, error) {
306+
func (l *StaticAddressLoopIn) createHtlcTx(chainParams *chaincfg.Params,
307+
feeRate chainfee.SatPerKWeight) (*wire.MsgTx, error) {
300308

301309
// First Create the tx.
302310
msgTx := wire.NewMsgTx(2)
@@ -312,7 +320,7 @@ func (l *StaticAddressLoopIn) createHtlcTx(chainParams *chaincfg.Params) (
312320

313321
// Calculate htlc tx fee for server provided fee rate.
314322
weight := l.htlcWeight()
315-
fee := l.HtlcTxFeeRate.FeeForWeight(weight)
323+
fee := feeRate.FeeForWeight(weight)
316324

317325
htlc, err := l.getHtlc(chainParams)
318326
if err != nil {
@@ -386,7 +394,7 @@ func (l *StaticAddressLoopIn) createHtlcSweepTx(ctx context.Context,
386394
return nil, err
387395
}
388396

389-
htlcTx, err := l.createHtlcTx(network)
397+
htlcTx, err := l.createHtlcTx(network, l.HtlcTxFeeRate)
390398
if err != nil {
391399
return nil, err
392400
}

swapserverrpc/server.proto

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -776,23 +776,52 @@ message ServerStaticAddressLoopInResponse {
776776
// htlc will expire and the client is free to claim the funds back.
777777
int32 htlc_expiry = 2;
778778

779-
// The nonces that the server used to generate the partial htlc sigs.
779+
// The nonces that the server used to generate the partial htlc tx sigs.
780780
repeated bytes htlc_server_nonces = 3;
781781

782+
// The nonces that the server used to generate the partial high fee htlc tx
783+
// sigs.
784+
repeated bytes htlc_server_nonces_high_fee = 4;
785+
786+
// The nonces that the server used to generate the partial extremely high
787+
// fee htlc tx sigs.
788+
repeated bytes htlc_server_nonces_extremely_high_fee = 5;
789+
782790
// The fee rate in sat/kw that the server wants to use for the htlc tx.
783-
uint64 htlc_fee_rate = 4;
791+
uint64 htlc_fee_rate = 6;
792+
793+
// The high fee rate in sat/kw that the server wants to use for the htlc tx.
794+
uint64 high_htlc_fee_rate = 7;
795+
796+
// The extremely high fee rate in sat/kw that the server wants to use for
797+
// the htlc tx.
798+
uint64 extremely_high_htlc_fee_rate = 8;
784799
}
785800

786801
message PushStaticAddressHtlcSigsRequest {
787-
// The swap hash of the swap that the client wants to push the htlc sigs
788-
// for.
802+
// The swap hash that the client wants to push the htlc sigs for.
789803
bytes swap_hash = 1;
790804

791805
// The nonces that the client used to generate the htlc sigs.
792806
repeated bytes htlc_client_nonces = 2;
793807

794-
// The partial htlc sigs that the client generated for the htlc tx.
795-
repeated bytes htlc_client_sigs = 3;
808+
// The nonces that the client used to generate the partial high fee htlc tx
809+
// sigs.
810+
repeated bytes htlc_client_nonces_high_fee = 3;
811+
812+
// The nonces that the client used to generate the partial extremely high
813+
// htlc tx sigs.
814+
repeated bytes htlc_client_nonces_extremely_high_fee = 4;
815+
816+
// The musig2 htlc sigs that the client generated for the htlc tx.
817+
repeated bytes htlc_client_sigs = 5;
818+
819+
// The musig2 htlc sigs that the client generated for the high fee htlc tx.
820+
repeated bytes htlc_client_sigs_high_fee = 6;
821+
822+
// The musig2 htlc sigs that the client generated for the extremely high fee
823+
// htlc tx.
824+
repeated bytes htlc_client_sigs_extremely_high_fee = 7;
796825
}
797826

798827
message PushStaticAddressHtlcSigsResponse {

0 commit comments

Comments
 (0)