@@ -9,7 +9,10 @@ import (
99 "strings"
1010
1111 "github.com/btcsuite/btcd/chaincfg/chainhash"
12+ "github.com/lightninglabs/loop/labels"
1213 "github.com/lightninglabs/loop/looprpc"
14+ "github.com/lightninglabs/loop/swapserverrpc"
15+ "github.com/lightningnetwork/lnd/routing/route"
1316 "github.com/urfave/cli"
1417)
1518
@@ -24,6 +27,46 @@ var staticAddressCommands = cli.Command{
2427 withdrawalCommand ,
2528 summaryCommand ,
2629 },
30+ Description : `
31+ TODO .
32+ ` ,
33+ Flags : []cli.Flag {
34+ cli.StringSliceFlag {
35+ Name : "utxo" ,
36+ Usage : "specify the utxos of deposits as " +
37+ "outpoints(tx:idx) that should be looped in." ,
38+ },
39+ cli.StringFlag {
40+ Name : "last_hop" ,
41+ Usage : "the pubkey of the last hop to use for this " +
42+ "swap" ,
43+ },
44+ cli.StringFlag {
45+ Name : "label" ,
46+ Usage : fmt .Sprintf ("an optional label for this swap," +
47+ "limited to %v characters. The label may not " +
48+ "start with our reserved prefix: %v." ,
49+ labels .MaxLength , labels .Reserved ),
50+ },
51+ cli.StringSliceFlag {
52+ Name : "route_hints" ,
53+ Usage : "route hints that can each be individually " +
54+ "used to assist in reaching the invoice's " +
55+ "destination" ,
56+ },
57+ cli.BoolFlag {
58+ Name : "private" ,
59+ Usage : "generates and passes routehints. Should be " +
60+ "used if the connected node is only " +
61+ "reachable via private channels" ,
62+ },
63+ cli.BoolFlag {
64+ Name : "force, f" ,
65+ Usage : "Assumes yes during confirmation. Using this " +
66+ "option will result in an immediate swap" ,
67+ },
68+ },
69+ Action : staticAddressLoopIn ,
2770}
2871
2972var newStaticAddressCommand = cli.Command {
@@ -196,7 +239,7 @@ var summaryCommand = cli.Command{
196239 Usage : "specify a filter to only display deposits in " +
197240 "the specified state. The state can be one " +
198241 "of [deposited|withdrawing|withdrawn|" +
199- "publish_expired_deposit|" +
242+ "loopingin|loopedin| publish_expired_deposit|" +
200243 "wait_for_expiry_sweep|expired|failed]." ,
201244 },
202245 },
@@ -229,6 +272,12 @@ func summary(ctx *cli.Context) error {
229272 case "withdrawn" :
230273 filterState = looprpc .DepositState_WITHDRAWN
231274
275+ case "loopingin" :
276+ filterState = looprpc .DepositState_LOOPING_IN
277+
278+ case "loopedin" :
279+ filterState = looprpc .DepositState_LOOPED_IN
280+
232281 case "publish_expired_deposit" :
233282 filterState = looprpc .DepositState_PUBLISH_EXPIRED
234283
@@ -297,3 +346,131 @@ func NewProtoOutPoint(op string) (*looprpc.OutPoint, error) {
297346 OutputIndex : uint32 (outputIndex ),
298347 }, nil
299348}
349+
350+ func staticAddressLoopIn (ctx * cli.Context ) error {
351+ if ctx .NArg () > 0 {
352+ return cli .ShowCommandHelp (ctx , "static" )
353+ }
354+
355+ client , cleanup , err := getClient (ctx )
356+ if err != nil {
357+ return err
358+ }
359+ defer cleanup ()
360+
361+ var (
362+ ctxb = context .Background ()
363+ isAllSelected = ctx .IsSet ("all" )
364+ isUtxoSelected = ctx .IsSet ("utxo" )
365+ label = ctx .String ("static-loop-in" )
366+ hints []* swapserverrpc.RouteHint
367+ lastHop []byte
368+ )
369+
370+ // Validate our label early so that we can fail before getting a quote.
371+ if err := labels .Validate (label ); err != nil {
372+ return err
373+ }
374+
375+ // Private and route hints are mutually exclusive as setting private
376+ // means we retrieve our own route hints from the connected node.
377+ hints , err = validateRouteHints (ctx )
378+ if err != nil {
379+ return err
380+ }
381+
382+ if ctx .IsSet (lastHopFlag .Name ) {
383+ lastHopVertex , err := route .NewVertexFromStr (
384+ ctx .String (lastHopFlag .Name ),
385+ )
386+ if err != nil {
387+ return err
388+ }
389+
390+ lastHop = lastHopVertex [:]
391+ }
392+
393+ // Get the amount we need to quote for.
394+ summaryResp , err := client .GetStaticAddressSummary (
395+ ctxb , & looprpc.StaticAddressSummaryRequest {
396+ StateFilter : looprpc .DepositState_DEPOSITED ,
397+ },
398+ )
399+ if err != nil {
400+ return err
401+ }
402+
403+ var depositOutpoints []string
404+ switch {
405+ case isAllSelected == isUtxoSelected :
406+ return errors .New ("must select either all or some utxos" )
407+
408+ case isAllSelected :
409+ depositOutpoints = depositsToOutpoints (
410+ summaryResp .FilteredDeposits ,
411+ )
412+
413+ case isUtxoSelected :
414+ depositOutpoints = ctx .StringSlice ("utxo" )
415+
416+ default :
417+ return fmt .Errorf ("unknown quote request" )
418+ }
419+
420+ quote , err := client .GetLoopInQuote (
421+ ctxb , & looprpc.QuoteRequest {
422+ LoopInRouteHints : hints ,
423+ LoopInLastHop : lastHop ,
424+ Private : ctx .Bool (privateFlag .Name ),
425+ DepositOutpoints : depositOutpoints ,
426+ },
427+ )
428+ if err != nil {
429+ return err
430+ }
431+
432+ limits := getInLimits (quote )
433+
434+ req := & looprpc.StaticAddressLoopInRequest {
435+ Outpoints : depositOutpoints ,
436+ MaxMinerFee : int64 (limits .maxMinerFee ),
437+ MaxSwapFee : int64 (limits .maxSwapFee ),
438+ LastHop : lastHop ,
439+ Label : label ,
440+ Initiator : defaultInitiator ,
441+ RouteHints : hints ,
442+ Private : ctx .Bool ("private" ),
443+ }
444+
445+ resp , err := client .StaticAddressLoopIn (ctxb , req )
446+ if err != nil {
447+ return err
448+ }
449+
450+ fmt .Printf ("Static loop-in response from the server: %v\n " , resp )
451+
452+ return nil
453+ }
454+
455+ func sumOutpointValues (outpoints []string , deposits []* looprpc.Deposit ) int64 {
456+ var total int64
457+ for _ , outpoint := range outpoints {
458+ for _ , deposit := range deposits {
459+ if deposit .Outpoint == outpoint {
460+ total += deposit .Value
461+ break
462+ }
463+ }
464+ }
465+
466+ return total
467+ }
468+
469+ func depositsToOutpoints (deposits []* looprpc.Deposit ) []string {
470+ outpoints := make ([]string , 0 , len (deposits ))
471+ for _ , deposit := range deposits {
472+ outpoints = append (outpoints , deposit .Outpoint )
473+ }
474+
475+ return outpoints
476+ }
0 commit comments