Skip to content

Commit

Permalink
cmd: static address loop-in
Browse files Browse the repository at this point in the history
  • Loading branch information
hieblmi committed Jul 30, 2024
1 parent 7eaecaf commit 127d76c
Showing 1 changed file with 180 additions and 4 deletions.
184 changes: 180 additions & 4 deletions cmd/loop/staticaddr.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import (
"strings"

"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/lightninglabs/loop/labels"
"github.com/lightninglabs/loop/looprpc"
"github.com/lightninglabs/loop/swapserverrpc"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/urfave/cli"
)

Expand All @@ -24,6 +27,50 @@ var staticAddressCommands = cli.Command{
withdrawalCommand,
summaryCommand,
},
Description: `
Requests a loop-in swap based on static address deposits.
`,
Flags: []cli.Flag{
cli.StringSliceFlag{
Name: "utxo",
Usage: "specify the utxos of deposits as " +
"outpoints(tx:idx) that should be looped in.",
},
cli.BoolFlag{
Name: "all",
Usage: "loop in all static address deposits.",
},
cli.StringFlag{
Name: "last_hop",
Usage: "the pubkey of the last hop to use for this " +
"swap",
},
cli.StringFlag{
Name: "label",
Usage: fmt.Sprintf("an optional label for this swap,"+
"limited to %v characters. The label may not "+
"start with our reserved prefix: %v.",
labels.MaxLength, labels.Reserved),
},
cli.StringSliceFlag{
Name: "route_hints",
Usage: "route hints that can each be individually " +
"used to assist in reaching the invoice's " +
"destination",
},
cli.BoolFlag{
Name: "private",
Usage: "generates and passes route hints. Should be " +
"used if the connected node is only " +
"reachable via private channels",
},
cli.BoolFlag{
Name: "force, f",
Usage: "Assumes yes during confirmation. Using this " +
"option will result in an immediate swap",
},
},
Action: staticAddressLoopIn,
}

var newStaticAddressCommand = cli.Command{
Expand Down Expand Up @@ -194,10 +241,14 @@ var summaryCommand = cli.Command{
cli.StringFlag{
Name: "filter",
Usage: "specify a filter to only display deposits in " +
"the specified state. The state can be one " +
"of [deposited|withdrawing|withdrawn|" +
"publish_expired_deposit|" +
"wait_for_expiry_sweep|expired|failed].",
"the specified state. Leaving out the filter " +
"returns all deposits.\nThe state can be one " +
"of the following: \n" +
"deposited\nwithdrawing\nwithdrawn\n" +
"loopingin\nloopedin\n" +
"publish_expired_deposit\n" +
"sweep_htlc_timeout\nhtlc_timeout_swept\n" +
"wait_for_expiry_sweep\nexpired\nfailed\n.",
},
},
Action: summary,
Expand Down Expand Up @@ -229,9 +280,21 @@ func summary(ctx *cli.Context) error {
case "withdrawn":
filterState = looprpc.DepositState_WITHDRAWN

case "loopingin":
filterState = looprpc.DepositState_LOOPING_IN

case "loopedin":
filterState = looprpc.DepositState_LOOPED_IN

case "publish_expired_deposit":
filterState = looprpc.DepositState_PUBLISH_EXPIRED

case "sweep_htlc_timeout":
filterState = looprpc.DepositState_SWEEP_HTLC_TIMEOUT

case "htlc_timeout_swept":
filterState = looprpc.DepositState_HTLC_TIMEOUT_SWEPT

case "wait_for_expiry_sweep":
filterState = looprpc.DepositState_WAIT_FOR_EXPIRY_SWEEP

Expand Down Expand Up @@ -297,3 +360,116 @@ func NewProtoOutPoint(op string) (*looprpc.OutPoint, error) {
OutputIndex: uint32(outputIndex),
}, nil
}

func staticAddressLoopIn(ctx *cli.Context) error {
if ctx.NArg() > 0 {
return cli.ShowCommandHelp(ctx, "static")
}

client, cleanup, err := getClient(ctx)
if err != nil {
return err
}
defer cleanup()

var (
ctxb = context.Background()
isAllSelected = ctx.IsSet("all")
isUtxoSelected = ctx.IsSet("utxo")
label = ctx.String("static-loop-in")
hints []*swapserverrpc.RouteHint
lastHop []byte
)

// Validate our label early so that we can fail before getting a quote.
if err := labels.Validate(label); err != nil {
return err
}

// Private and route hints are mutually exclusive as setting private
// means we retrieve our own route hints from the connected node.
hints, err = validateRouteHints(ctx)
if err != nil {
return err
}

if ctx.IsSet(lastHopFlag.Name) {
lastHopVertex, err := route.NewVertexFromStr(
ctx.String(lastHopFlag.Name),
)
if err != nil {
return err
}

lastHop = lastHopVertex[:]
}

// Get the amount we need to quote for.
summaryResp, err := client.GetStaticAddressSummary(
ctxb, &looprpc.StaticAddressSummaryRequest{
StateFilter: looprpc.DepositState_DEPOSITED,
},
)
if err != nil {
return err
}

var depositOutpoints []string
switch {
case isAllSelected == isUtxoSelected:
return errors.New("must select either all or some utxos")

case isAllSelected:
depositOutpoints = depositsToOutpoints(
summaryResp.FilteredDeposits,
)

case isUtxoSelected:
depositOutpoints = ctx.StringSlice("utxo")

default:
return fmt.Errorf("unknown quote request")
}

quote, err := client.GetLoopInQuote(
ctxb, &looprpc.QuoteRequest{
LoopInRouteHints: hints,
LoopInLastHop: lastHop,
Private: ctx.Bool(privateFlag.Name),
DepositOutpoints: depositOutpoints,
},
)
if err != nil {
return err
}

limits := getInLimits(quote)

req := &looprpc.StaticAddressLoopInRequest{
Outpoints: depositOutpoints,
MaxSwapFee: int64(limits.maxSwapFee),
LastHop: lastHop,
Label: ctx.String(labelFlag.Name),
Initiator: defaultInitiator,
RouteHints: hints,
Private: ctx.Bool("private"),
}

resp, err := client.StaticAddressLoopIn(ctxb, req)
if err != nil {
return err
}

fmt.Printf("Static loop-in response from the server: %v\n", resp)

return nil
}

func depositsToOutpoints(deposits []*looprpc.Deposit) []string {
outpoints := make([]string, 0, len(deposits))
for _, deposit := range deposits {
outpoints = append(outpoints, deposit.Outpoint)
}

return outpoints
}

0 comments on commit 127d76c

Please sign in to comment.