Skip to content

[5/?] StaticAddr: Deposit summary RPC #721

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions cmd/loop/staticaddr.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ var staticAddressCommands = cli.Command{
newStaticAddressCommand,
listUnspentCommand,
withdrawalCommand,
summaryCommand,
},
}

Expand Down Expand Up @@ -181,6 +182,83 @@ func withdraw(ctx *cli.Context) error {
return nil
}

var summaryCommand = cli.Command{
Name: "summary",
ShortName: "s",
Usage: "Display a summary of static address related information.",
Description: `
Displays various static address related information like deposits,
withdrawals and statistics. The information can be filtered by state.
`,
Flags: []cli.Flag{
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].",
},
},
Action: summary,
}

func summary(ctx *cli.Context) error {
ctxb := context.Background()
if ctx.NArg() > 0 {
return cli.ShowCommandHelp(ctx, "summary")
}

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

var filterState looprpc.DepositState
switch ctx.String("filter") {
case "":
// If no filter is specified, we'll default to showing all.

case "deposited":
filterState = looprpc.DepositState_DEPOSITED

case "withdrawing":
filterState = looprpc.DepositState_WITHDRAWING

case "withdrawn":
filterState = looprpc.DepositState_WITHDRAWN

case "publish_expired_deposit":
filterState = looprpc.DepositState_PUBLISH_EXPIRED

case "wait_for_expiry_sweep":
filterState = looprpc.DepositState_WAIT_FOR_EXPIRY_SWEEP

case "expired":
filterState = looprpc.DepositState_EXPIRED

case "failed":
filterState = looprpc.DepositState_FAILED_STATE

default:
filterState = looprpc.DepositState_UNKNOWN_STATE
}

resp, err := client.GetStaticAddressSummary(
ctxb, &looprpc.StaticAddressSummaryRequest{
StateFilter: filterState,
},
)
if err != nil {
return err
}

printRespJSON(resp)

return nil
}

func utxosToOutpoints(utxos []string) ([]*looprpc.OutPoint, error) {
outpoints := make([]*looprpc.OutPoint, 0, len(utxos))
if len(utxos) == 0 {
Expand Down
7 changes: 7 additions & 0 deletions loopd/perms/perms.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@ var RequiredPermissions = map[string][]bakery.Op{
Entity: "loop",
Action: "in",
}},
"/looprpc.SwapClient/GetStaticAddressSummary": {{
Entity: "swap",
Action: "read",
}, {
Entity: "loop",
Action: "in",
}},
"/looprpc.SwapClient/GetLsatTokens": {{
Entity: "auth",
Action: "read",
Expand Down
198 changes: 198 additions & 0 deletions loopd/swapclient_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/lightninglabs/aperture/l402"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop"
"github.com/lightninglabs/loop/fsm"
"github.com/lightninglabs/loop/instantout"
"github.com/lightninglabs/loop/instantout/reservation"
"github.com/lightninglabs/loop/labels"
Expand Down Expand Up @@ -1395,6 +1396,203 @@ func (s *swapClientServer) WithdrawDeposits(ctx context.Context,
return &clientrpc.WithdrawDepositsResponse{}, err
}

// GetStaticAddressSummary returns a summary static address related information.
// Amongst deposits and withdrawals and their total values it also includes a
// list of detailed deposit information filtered by their state.
func (s *swapClientServer) GetStaticAddressSummary(ctx context.Context,
req *clientrpc.StaticAddressSummaryRequest) (
*clientrpc.StaticAddressSummaryResponse, error) {

if req.StateFilter != clientrpc.DepositState_UNKNOWN_STATE &&
len(req.Outpoints) > 0 {

return nil, fmt.Errorf("can either filter by state or " +
"outpoints")
}

allDeposits, err := s.depositManager.GetAllDeposits()
if err != nil {
return nil, err
}

return s.depositSummary(
ctx, allDeposits, req.StateFilter, req.Outpoints,
)
}

func (s *swapClientServer) depositSummary(ctx context.Context,
deposits []*deposit.Deposit, stateFilter clientrpc.DepositState,
outpointsFilter []string) (*clientrpc.StaticAddressSummaryResponse,
error) {

var (
totalNumDeposits = len(deposits)
valueUnconfirmed int64
valueDeposited int64
valueExpired int64
valueWithdrawn int64
)

// Value unconfirmed.
utxos, err := s.staticAddressManager.ListUnspent(
ctx, 0, deposit.MinConfs-1,
)
if err != nil {
return nil, err
}
for _, u := range utxos {
valueUnconfirmed += int64(u.Value)
}

// Confirmed total values by category.
for _, d := range deposits {
value := int64(d.Value)
switch d.GetState() {
case deposit.Deposited:
valueDeposited += value

case deposit.Expired:
valueExpired += value

case deposit.Withdrawn:
valueWithdrawn += value
}
}

// Deposits filtered by state or outpoints.
var clientDeposits []*clientrpc.Deposit
if len(outpointsFilter) > 0 {
f := func(d *deposit.Deposit) bool {
for _, outpoint := range outpointsFilter {
if outpoint == d.OutPoint.String() {
return true
}
}
return false
}
clientDeposits = filter(deposits, f)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we could also filter in the DB but since this is running on the client its not a performance concern. Other thoughts?


if len(outpointsFilter) != len(clientDeposits) {
return nil, fmt.Errorf("not all outpoints found in " +
"deposits")
}
} else {
f := func(d *deposit.Deposit) bool {
if stateFilter == clientrpc.DepositState_UNKNOWN_STATE {
// Per default, we return deposits in all
// states.
return true
}

return d.GetState() == toServerState(stateFilter)
}
clientDeposits = filter(deposits, f)
}

params, err := s.staticAddressManager.GetStaticAddressParameters(ctx)
if err != nil {
return nil, err
}

address, err := s.staticAddressManager.GetTaprootAddress(
params.ClientPubkey, params.ServerPubkey, int64(params.Expiry),
)
if err != nil {
return nil, err
}

return &clientrpc.StaticAddressSummaryResponse{
StaticAddress: address.String(),
TotalNumDeposits: uint32(totalNumDeposits),
ValueUnconfirmed: valueUnconfirmed,
ValueDeposited: valueDeposited,
ValueExpired: valueExpired,
ValueWithdrawn: valueWithdrawn,
FilteredDeposits: clientDeposits,
}, nil
}

type filterFunc func(deposits *deposit.Deposit) bool

func filter(deposits []*deposit.Deposit, f filterFunc) []*clientrpc.Deposit {
var clientDeposits []*clientrpc.Deposit
for _, d := range deposits {
if !f(d) {
continue
}

hash := d.Hash
outpoint := wire.NewOutPoint(&hash, d.Index).String()
deposit := &clientrpc.Deposit{
Id: d.ID[:],
State: toClientState(d.GetState()),
Outpoint: outpoint,
Value: int64(d.Value),
ConfirmationHeight: d.ConfirmationHeight,
}

clientDeposits = append(clientDeposits, deposit)
}

return clientDeposits
}

func toClientState(state fsm.StateType) clientrpc.DepositState {
switch state {
case deposit.Deposited:
return clientrpc.DepositState_DEPOSITED

case deposit.Withdrawing:
return clientrpc.DepositState_WITHDRAWING

case deposit.Withdrawn:
return clientrpc.DepositState_WITHDRAWN

case deposit.PublishExpiredDeposit:
return clientrpc.DepositState_PUBLISH_EXPIRED

case deposit.WaitForExpirySweep:
return clientrpc.DepositState_WAIT_FOR_EXPIRY_SWEEP

case deposit.Expired:
return clientrpc.DepositState_EXPIRED

case deposit.Failed:
return clientrpc.DepositState_FAILED_STATE

default:
return clientrpc.DepositState_UNKNOWN_STATE
}
}

func toServerState(state clientrpc.DepositState) fsm.StateType {
switch state {
case clientrpc.DepositState_DEPOSITED:
return deposit.Deposited

case clientrpc.DepositState_WITHDRAWING:
return deposit.Withdrawing

case clientrpc.DepositState_WITHDRAWN:
return deposit.Withdrawn

case clientrpc.DepositState_PUBLISH_EXPIRED:
return deposit.PublishExpiredDeposit

case clientrpc.DepositState_WAIT_FOR_EXPIRY_SWEEP:
return deposit.WaitForExpirySweep

case clientrpc.DepositState_EXPIRED:
return deposit.Expired

case clientrpc.DepositState_FAILED_STATE:
return deposit.Failed

default:
return fsm.EmptyState
}
}

func toServerOutpoints(outpoints []*clientrpc.OutPoint) ([]wire.OutPoint,
error) {

Expand Down
Loading
Loading