Skip to content

Commit a49c60a

Browse files
authored
Merge pull request #853 from lightninglabs/static-addr-staging
StaticAddr: merge staging to master
2 parents 2ec44f0 + acea303 commit a49c60a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+13684
-886
lines changed

client.go

+28-6
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,13 @@ var (
4343
ErrSwapAmountTooHigh = errors.New("swap amount too high")
4444

4545
// ErrExpiryTooFar is returned when the server proposes an expiry that
46-
// is too soon for us.
46+
// is too far in the future.
4747
ErrExpiryTooFar = errors.New("swap expiry too far")
4848

49+
// ErrExpiryTooSoon is returned when the server proposes an expiry that
50+
// is too soon.
51+
ErrExpiryTooSoon = errors.New("swap expiry too soon")
52+
4953
// ErrInsufficientBalance indicates insufficient confirmed balance to
5054
// publish a swap.
5155
ErrInsufficientBalance = errors.New("insufficient confirmed balance")
@@ -137,6 +141,22 @@ type ClientConfig struct {
137141
// MaxPaymentRetries is the maximum times we retry an off-chain payment
138142
// (used in loop out).
139143
MaxPaymentRetries int
144+
145+
// MaxStaticAddrHtlcFeePercentage is the percentage of the swap amount
146+
// that we allow the server to charge for the htlc transaction.
147+
// Although highly unlikely, this is a defense against the server
148+
// publishing the htlc without paying the swap invoice, forcing us to
149+
// sweep the timeout path.
150+
MaxStaticAddrHtlcFeePercentage float64
151+
152+
// MaxStaticAddrHtlcBackupFeePercentage is the percentage of the swap
153+
// amount that we allow the server to charge for the htlc backup
154+
// transactions. This is a defense against the server publishing the
155+
// htlc backup without paying the swap invoice, forcing us to sweep the
156+
// timeout path. This value is elevated compared to
157+
// MaxStaticAddrHtlcFeePercentage since it serves the server as backup
158+
// transaction in case of fee spikes.
159+
MaxStaticAddrHtlcBackupFeePercentage float64
140160
}
141161

142162
// NewClient returns a new instance to initiate swaps with.
@@ -694,9 +714,9 @@ func (s *Client) LoopIn(globalCtx context.Context,
694714
return swapInfo, nil
695715
}
696716

697-
// LoopInQuote takes an amount and returns a break down of estimated
698-
// costs for the client. Both the swap server and the on-chain fee estimator are
699-
// queried to get to build the quote response.
717+
// LoopInQuote takes an amount and returns a breakdown of estimated costs for
718+
// the client. Both the swap server and the on-chain fee estimator are queried
719+
// to get to build the quote response.
700720
func (s *Client) LoopInQuote(ctx context.Context,
701721
request *LoopInQuoteRequest) (*LoopInQuote, error) {
702722

@@ -742,7 +762,7 @@ func (s *Client) LoopInQuote(ctx context.Context,
742762

743763
quote, err := s.Server.GetLoopInQuote(
744764
ctx, request.Amount, s.lndServices.NodePubkey, request.LastHop,
745-
request.RouteHints, request.Initiator,
765+
request.RouteHints, request.Initiator, request.NumDeposits,
746766
)
747767
if err != nil {
748768
return nil, err
@@ -752,7 +772,9 @@ func (s *Client) LoopInQuote(ctx context.Context,
752772

753773
// We don't calculate the on-chain fee if the HTLC is going to be
754774
// published externally.
755-
if request.ExternalHtlc {
775+
// We also don't calculate the on-chain fee if the loop in is funded by
776+
// static address deposits because we don't publish the HTLC on-chain.
777+
if request.ExternalHtlc || request.NumDeposits > 0 {
756778
return &LoopInQuote{
757779
SwapFee: swapFee,
758780
MinerFee: 0,

cmd/loop/main.go

+16-8
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,15 @@ var (
7676
Name: "verbose, v",
7777
Usage: "show expanded details",
7878
}
79+
80+
commands = []cli.Command{
81+
loopOutCommand, loopInCommand, termsCommand,
82+
monitorCommand, quoteCommand, listAuthCommand, fetchL402Command,
83+
listSwapsCommand, swapInfoCommand, getLiquidityParamsCommand,
84+
setLiquidityRuleCommand, suggestSwapCommand, setParamsCommand,
85+
getInfoCommand, abandonSwapCommand, reservationsCommands,
86+
instantOutCommand, listInstantOutsCommand,
87+
}
7988
)
8089

8190
const (
@@ -142,14 +151,7 @@ func main() {
142151
tlsCertFlag,
143152
macaroonPathFlag,
144153
}
145-
app.Commands = []cli.Command{
146-
loopOutCommand, loopInCommand, termsCommand,
147-
monitorCommand, quoteCommand, listAuthCommand, fetchL402Command,
148-
listSwapsCommand, swapInfoCommand, getLiquidityParamsCommand,
149-
setLiquidityRuleCommand, suggestSwapCommand, setParamsCommand,
150-
getInfoCommand, abandonSwapCommand, reservationsCommands,
151-
instantOutCommand, listInstantOutsCommand,
152-
}
154+
app.Commands = commands
153155

154156
err := app.Run(os.Args)
155157
if err != nil {
@@ -279,6 +281,12 @@ func displayInDetails(req *looprpc.QuoteRequest,
279281
"wallet.\n\n")
280282
}
281283

284+
if req.DepositOutpoints != nil {
285+
fmt.Printf("On-chain fees for static address loop-ins are not " +
286+
"included.\nThey were already paid when the deposits " +
287+
"were created.\n\n")
288+
}
289+
282290
printQuoteInResp(req, resp, verbose)
283291

284292
fmt.Printf("\nCONTINUE SWAP? (y/n): ")

cmd/loop/quote.go

+70-12
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"os"
77
"time"
88

9+
"github.com/btcsuite/btcd/btcutil"
910
"github.com/lightninglabs/loop"
1011
"github.com/lightninglabs/loop/looprpc"
1112
"github.com/lightningnetwork/lnd/routing/route"
@@ -19,10 +20,11 @@ var quoteCommand = cli.Command{
1920
}
2021

2122
var quoteInCommand = cli.Command{
22-
Name: "in",
23-
Usage: "get a quote for the cost of a loop in swap",
24-
ArgsUsage: "amt",
25-
Description: "Allows to determine the cost of a swap up front",
23+
Name: "in",
24+
Usage: "get a quote for the cost of a loop in swap",
25+
ArgsUsage: "amt",
26+
Description: "Allows to determine the cost of a swap up front." +
27+
"Either specify an amount or deposit outpoints.",
2628
Flags: []cli.Flag{
2729
cli.StringFlag{
2830
Name: lastHopFlag.Name,
@@ -33,20 +35,38 @@ var quoteInCommand = cli.Command{
3335
verboseFlag,
3436
privateFlag,
3537
routeHintsFlag,
38+
cli.StringSliceFlag{
39+
Name: "deposit_outpoint",
40+
Usage: "one or more static address deposit outpoints " +
41+
"to quote for. Deposit outpoints are not to " +
42+
"be used in combination with an amount. Each" +
43+
"additional outpoint can be added by " +
44+
"specifying --deposit_outpoint tx_id:idx",
45+
},
3646
},
3747
Action: quoteIn,
3848
}
3949

4050
func quoteIn(ctx *cli.Context) error {
4151
// Show command help if the incorrect number arguments was provided.
42-
if ctx.NArg() != 1 {
52+
if ctx.NArg() != 1 && !ctx.IsSet("deposit_outpoint") {
4353
return cli.ShowCommandHelp(ctx, "in")
4454
}
4555

46-
args := ctx.Args()
47-
amt, err := parseAmt(args[0])
48-
if err != nil {
49-
return err
56+
var (
57+
manualAmt btcutil.Amount
58+
depositAmt btcutil.Amount
59+
depositOutpoints []string
60+
err error
61+
ctxb = context.Background()
62+
)
63+
64+
if ctx.NArg() == 1 {
65+
args := ctx.Args()
66+
manualAmt, err = parseAmt(args[0])
67+
if err != nil {
68+
return err
69+
}
5070
}
5171

5272
client, cleanup, err := getClient(ctx)
@@ -62,11 +82,20 @@ func quoteIn(ctx *cli.Context) error {
6282
return err
6383
}
6484

85+
if ctx.IsSet("deposit_outpoint") {
86+
depositOutpoints = ctx.StringSlice("deposit_outpoint")
87+
depositAmt, err = depositAmount(ctxb, client, depositOutpoints)
88+
if err != nil {
89+
return err
90+
}
91+
}
92+
6593
quoteReq := &looprpc.QuoteRequest{
66-
Amt: int64(amt),
94+
Amt: int64(manualAmt),
6795
ConfTarget: int32(ctx.Uint64("conf_target")),
6896
LoopInRouteHints: hints,
6997
Private: ctx.Bool(privateFlag.Name),
98+
DepositOutpoints: depositOutpoints,
7099
}
71100

72101
if ctx.IsSet(lastHopFlag.Name) {
@@ -80,7 +109,6 @@ func quoteIn(ctx *cli.Context) error {
80109
quoteReq.LoopInLastHop = lastHopVertex[:]
81110
}
82111

83-
ctxb := context.Background()
84112
quoteResp, err := client.GetLoopInQuote(ctxb, quoteReq)
85113
if err != nil {
86114
return err
@@ -98,10 +126,36 @@ func quoteIn(ctx *cli.Context) error {
98126
"amount.\n")
99127
}
100128

129+
// If the user specified static address deposits, we quoted for their
130+
// total value and need to display that value instead of the manually
131+
// selected one.
132+
if manualAmt == 0 {
133+
quoteReq.Amt = int64(depositAmt)
134+
}
101135
printQuoteInResp(quoteReq, quoteResp, ctx.Bool("verbose"))
102136
return nil
103137
}
104138

139+
func depositAmount(ctx context.Context, client looprpc.SwapClientClient,
140+
depositOutpoints []string) (btcutil.Amount, error) {
141+
142+
addressSummary, err := client.ListStaticAddressDeposits(
143+
ctx, &looprpc.ListStaticAddressDepositsRequest{
144+
Outpoints: depositOutpoints,
145+
},
146+
)
147+
if err != nil {
148+
return 0, err
149+
}
150+
151+
var depositAmt btcutil.Amount
152+
for _, deposit := range addressSummary.FilteredDeposits {
153+
depositAmt += btcutil.Amount(deposit.Value)
154+
}
155+
156+
return depositAmt, nil
157+
}
158+
105159
var quoteOutCommand = cli.Command{
106160
Name: "out",
107161
Usage: "get a quote for the cost of a loop out swap",
@@ -174,7 +228,11 @@ func printQuoteInResp(req *looprpc.QuoteRequest,
174228

175229
totalFee := resp.HtlcPublishFeeSat + resp.SwapFeeSat
176230

177-
fmt.Printf(satAmtFmt, "Send on-chain:", req.Amt)
231+
if req.DepositOutpoints != nil {
232+
fmt.Printf(satAmtFmt, "Previously deposited on-chain:", req.Amt)
233+
} else {
234+
fmt.Printf(satAmtFmt, "Send on-chain:", req.Amt)
235+
}
178236
fmt.Printf(satAmtFmt, "Receive off-chain:", req.Amt-totalFee)
179237

180238
switch {

0 commit comments

Comments
 (0)