Skip to content

Commit

Permalink
all loopin changes
Browse files Browse the repository at this point in the history
  • Loading branch information
hieblmi committed Jul 16, 2024
1 parent e635d63 commit 7ee61f6
Show file tree
Hide file tree
Showing 32 changed files with 4,837 additions and 580 deletions.
178 changes: 177 additions & 1 deletion 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,46 @@ var staticAddressCommands = cli.Command{
withdrawalCommand,
summaryCommand,
},
Description: `
TODO .
`,
Flags: []cli.Flag{
cli.StringSliceFlag{
Name: "utxo",
Usage: "specify the utxos of deposits as " +
"outpoints(tx:idx) that should be looped in.",
},
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 routehints. 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 @@ -196,7 +239,7 @@ var summaryCommand = cli.Command{
Usage: "specify a filter to only display deposits in " +
"the specified state. The state can be one " +
"of [deposited|withdrawing|withdrawn|" +
"publish_expired_deposit|" +
"loopingin|loopedin|publish_expired_deposit|" +
"wait_for_expiry_sweep|expired|failed].",
},
},
Expand Down Expand Up @@ -229,6 +272,12 @@ 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

Expand Down Expand Up @@ -297,3 +346,130 @@ 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: label,
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 sumOutpointValues(outpoints []string, deposits []*looprpc.Deposit) int64 {
var total int64
for _, outpoint := range outpoints {
for _, deposit := range deposits {
if deposit.Outpoint == outpoint {
total += deposit.Value
break
}
}
}

return total
}

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

return outpoints
}
38 changes: 38 additions & 0 deletions interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,44 @@ type LoopInRequest struct {
RouteHints [][]zpay32.HopHint
}

// StaticAddressLoopInRequest contains the required parameters for the swap.
type StaticAddressLoopInRequest struct {
DepositOutpoints []string

// MaxSwapFee is the maximum we are willing to pay the server for the
// swap. This value is not disclosed in the swap initiation call, but if
// the server asks for a higher fee, we abort the swap. Typically this
// value is taken from the response of the LoopInQuote call. It
// includes the pre-pay amount.
MaxSwapFee btcutil.Amount

// LastHop optionally specifies the last hop to use for the loop in
// payment.
LastHop *route.Vertex

// Label contains an optional label for the swap.
Label string

// Initiator is an optional string that identifies what software
// initiated the swap (loop CLI, autolooper, LiT UI and so on) and is
// appended to the user agent string.
Initiator string

// Private indicates whether the destination node should be considered
// private. In which case, loop will generate hophints to assist with
// probing and payment.
Private bool

// RouteHints are optional route hints to reach the destination through
// private channels.
RouteHints [][]zpay32.HopHint
}

// StaticAddressLoopInResponse contains the parameters for the static address
// loop-in.
type StaticAddressLoopInResponse struct {
}

// LoopInTerms are the server terms on which it executes loop in swaps.
type LoopInTerms struct {
// MinSwapAmount is the minimum amount that the server requires for a
Expand Down
54 changes: 54 additions & 0 deletions loopd/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto/tls"
"errors"
"fmt"
"github.com/lightninglabs/loop/staticaddr/loopin"
"net"
"net/http"
"strings"
Expand Down Expand Up @@ -513,6 +514,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
staticAddressManager *address.Manager
depositManager *deposit.Manager
withdrawalManager *withdraw.Manager
staticLoopInManager *loopin.Manager
)
// Create the reservation and instantout managers.
if d.cfg.EnableExperimental {
Expand Down Expand Up @@ -589,6 +591,30 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
Signer: d.lnd.Signer,
}
withdrawalManager = withdraw.NewManager(withdrawalCfg)

// Static address loop-in manager setup.
staticAddressLoopInStore := loopin.NewSqlStore(
loopdb.NewTypedStore[loopin.Querier](baseDb),
clock.NewDefaultClock(), depositStore,
d.lnd.ChainParams,
)

loopinCfg := &loopin.Config{
StaticAddressServerClient: staticAddressClient,
SwapClient: swapClient,
LndClient: d.lnd.Client,
InvoicesClient: d.lnd.Invoices,
NodePubkey: d.lnd.NodePubkey,
AddressManager: staticAddressManager,
DepositManager: depositManager,
WithdrawalManager: withdrawalManager,
Store: staticAddressLoopInStore,
WalletKit: d.lnd.WalletKit,
ChainNotifier: d.lnd.ChainNotifier,
ChainParams: d.lnd.ChainParams,
Signer: d.lnd.Signer,
}
staticLoopInManager = loopin.NewManager(loopinCfg)
}

// Now finally fully initialize the swap client RPC server instance.
Expand All @@ -607,6 +633,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
staticAddressManager: staticAddressManager,
depositManager: depositManager,
withdrawalManager: withdrawalManager,
staticLoopInManager: staticLoopInManager,
}

// Retrieve all currently existing swaps from the database.
Expand Down Expand Up @@ -764,6 +791,33 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
withdrawalManager.WaitInitComplete()
}

// Start the static address loop-in manager.
if staticLoopInManager != nil {
d.wg.Add(1)
go func() {
defer d.wg.Done()

// Lnd's GetInfo call supplies us with the current block
// height.
info, err := d.lnd.Client.GetInfo(d.mainCtx)
if err != nil {
d.internalErrChan <- err
return
}

log.Info("Starting static address loop-in manager...")
err = staticLoopInManager.Run(
d.mainCtx, info.BlockHeight,
)
if err != nil && !errors.Is(context.Canceled, err) {
d.internalErrChan <- err
}
log.Info("Starting static address loop-in manager " +
"stopped")
}()
staticLoopInManager.WaitInitComplete()
}

// Last, start our internal error handler. This will return exactly one
// error or nil on the main error channel to inform the caller that
// something went wrong or that shutdown is complete. We don't add to
Expand Down
7 changes: 7 additions & 0 deletions loopd/perms/perms.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ var RequiredPermissions = map[string][]bakery.Op{
Entity: "loop",
Action: "in",
}},
"/looprpc.SwapClient/StaticAddressLoopIn": {{
Entity: "swap",
Action: "read",
}, {
Entity: "loop",
Action: "in",
}},
"/looprpc.SwapClient/GetLsatTokens": {{
Entity: "auth",
Action: "read",
Expand Down
Loading

0 comments on commit 7ee61f6

Please sign in to comment.