Skip to content

Commit cf8300c

Browse files
committed
loopd: static address summary
1 parent bbf468f commit cf8300c

File tree

3 files changed

+283
-0
lines changed

3 files changed

+283
-0
lines changed

cmd/loop/staticaddr.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ var staticAddressCommands = cli.Command{
2222
newStaticAddressCommand,
2323
listUnspentCommand,
2424
withdrawalCommand,
25+
summaryCommand,
2526
},
2627
}
2728

@@ -181,6 +182,83 @@ func withdraw(ctx *cli.Context) error {
181182
return nil
182183
}
183184

185+
var summaryCommand = cli.Command{
186+
Name: "summary",
187+
ShortName: "s",
188+
Usage: "Display a summary of static address related information.",
189+
Description: `
190+
Displays various static address related information like deposits,
191+
withdrawals and statistics. The information can be filtered by state.
192+
`,
193+
Flags: []cli.Flag{
194+
cli.StringFlag{
195+
Name: "filter",
196+
Usage: "specify a filter to only display deposits in " +
197+
"the specified state. The state can be one " +
198+
"of [deposited|withdrawing|withdrawn|" +
199+
"publish_expired_deposit|" +
200+
"wait_for_expiry_sweep|expired|failed].",
201+
},
202+
},
203+
Action: summary,
204+
}
205+
206+
func summary(ctx *cli.Context) error {
207+
ctxb := context.Background()
208+
if ctx.NArg() > 0 {
209+
return cli.ShowCommandHelp(ctx, "summary")
210+
}
211+
212+
client, cleanup, err := getClient(ctx)
213+
if err != nil {
214+
return err
215+
}
216+
defer cleanup()
217+
218+
var filterState looprpc.DepositState
219+
switch ctx.String("filter") {
220+
case "":
221+
// If no filter is specified, we'll default to showing all.
222+
223+
case "deposited":
224+
filterState = looprpc.DepositState_DEPOSITED
225+
226+
case "withdrawing":
227+
filterState = looprpc.DepositState_WITHDRAWING
228+
229+
case "withdrawn":
230+
filterState = looprpc.DepositState_WITHDRAWN
231+
232+
case "publish_expired_deposit":
233+
filterState = looprpc.DepositState_PUBLISH_EXPIRED
234+
235+
case "wait_for_expiry_sweep":
236+
filterState = looprpc.DepositState_WAIT_FOR_EXPIRY_SWEEP
237+
238+
case "expired":
239+
filterState = looprpc.DepositState_EXPIRED
240+
241+
case "failed":
242+
filterState = looprpc.DepositState_FAILED_STATE
243+
244+
default:
245+
filterState = looprpc.DepositState_UNKNOWN_STATE
246+
}
247+
248+
resp, err := client.GetStaticAddressSummary(
249+
ctxb, &looprpc.StaticAddressSummaryRequest{
250+
StateFilter: filterState,
251+
},
252+
)
253+
if err != nil {
254+
return err
255+
}
256+
257+
printRespJSON(resp)
258+
259+
return nil
260+
}
261+
184262
func utxosToOutpoints(utxos []string) ([]*looprpc.OutPoint, error) {
185263
outpoints := make([]*looprpc.OutPoint, 0, len(utxos))
186264
if len(utxos) == 0 {

loopd/perms/perms.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ var RequiredPermissions = map[string][]bakery.Op{
9494
Entity: "loop",
9595
Action: "in",
9696
}},
97+
"/looprpc.SwapClient/GetStaticAddressSummary": {{
98+
Entity: "swap",
99+
Action: "read",
100+
}, {
101+
Entity: "loop",
102+
Action: "in",
103+
}},
97104
"/looprpc.SwapClient/GetLsatTokens": {{
98105
Entity: "auth",
99106
Action: "read",

loopd/swapclient_server.go

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/lightninglabs/aperture/l402"
2020
"github.com/lightninglabs/lndclient"
2121
"github.com/lightninglabs/loop"
22+
"github.com/lightninglabs/loop/fsm"
2223
"github.com/lightninglabs/loop/instantout"
2324
"github.com/lightninglabs/loop/instantout/reservation"
2425
"github.com/lightninglabs/loop/labels"
@@ -1410,6 +1411,203 @@ func (s *swapClientServer) WithdrawDeposits(ctx context.Context,
14101411
return &looprpc.WithdrawDepositsResponse{}, err
14111412
}
14121413

1414+
// GetStaticAddressSummary returns a summary static address related information.
1415+
// Amongst deposits and withdrawals and their total values it also includes a
1416+
// list of detailed deposit information filtered by their state.
1417+
func (s *swapClientServer) GetStaticAddressSummary(ctx context.Context,
1418+
req *looprpc.StaticAddressSummaryRequest) (
1419+
*looprpc.StaticAddressSummaryResponse, error) {
1420+
1421+
if req.StateFilter != looprpc.DepositState_UNKNOWN_STATE &&
1422+
len(req.Outpoints) > 0 {
1423+
1424+
return nil, fmt.Errorf("can either filter by state or " +
1425+
"outpoints")
1426+
}
1427+
1428+
allDeposits, err := s.depositManager.GetAllDeposits()
1429+
if err != nil {
1430+
return nil, err
1431+
}
1432+
1433+
return s.depositSummary(
1434+
ctx, allDeposits, req.StateFilter, req.Outpoints,
1435+
)
1436+
}
1437+
1438+
func (s *swapClientServer) depositSummary(ctx context.Context,
1439+
deposits []*deposit.Deposit, stateFilter looprpc.DepositState,
1440+
outpointsFilter []string) (*looprpc.StaticAddressSummaryResponse,
1441+
error) {
1442+
1443+
var (
1444+
totalNumDeposits = len(deposits)
1445+
valueUnconfirmed int64
1446+
valueDeposited int64
1447+
valueExpired int64
1448+
valueWithdrawn int64
1449+
)
1450+
1451+
// Value unconfirmed.
1452+
utxos, err := s.staticAddressManager.ListUnspent(
1453+
ctx, 0, deposit.MinConfs-1,
1454+
)
1455+
if err != nil {
1456+
return nil, err
1457+
}
1458+
for _, u := range utxos {
1459+
valueUnconfirmed += int64(u.Value)
1460+
}
1461+
1462+
// Confirmed total values by category.
1463+
for _, d := range deposits {
1464+
value := int64(d.Value)
1465+
switch d.GetState() {
1466+
case deposit.Deposited:
1467+
valueDeposited += value
1468+
1469+
case deposit.Expired:
1470+
valueExpired += value
1471+
1472+
case deposit.Withdrawn:
1473+
valueWithdrawn += value
1474+
}
1475+
}
1476+
1477+
// Deposits filtered by state or outpoints.
1478+
var clientDeposits []*looprpc.Deposit
1479+
if len(outpointsFilter) > 0 {
1480+
f := func(d *deposit.Deposit) bool {
1481+
for _, outpoint := range outpointsFilter {
1482+
if outpoint == d.OutPoint.String() {
1483+
return true
1484+
}
1485+
}
1486+
return false
1487+
}
1488+
clientDeposits = filter(deposits, f)
1489+
1490+
if len(outpointsFilter) != len(clientDeposits) {
1491+
return nil, fmt.Errorf("not all outpoints found in " +
1492+
"deposits")
1493+
}
1494+
} else {
1495+
f := func(d *deposit.Deposit) bool {
1496+
if stateFilter == looprpc.DepositState_UNKNOWN_STATE {
1497+
// Per default, we return deposits in all
1498+
// states.
1499+
return true
1500+
}
1501+
1502+
return d.GetState() == toServerState(stateFilter)
1503+
}
1504+
clientDeposits = filter(deposits, f)
1505+
}
1506+
1507+
params, err := s.staticAddressManager.GetStaticAddressParameters(ctx)
1508+
if err != nil {
1509+
return nil, err
1510+
}
1511+
1512+
address, err := s.staticAddressManager.GetTaprootAddress(
1513+
params.ClientPubkey, params.ServerPubkey, int64(params.Expiry),
1514+
)
1515+
if err != nil {
1516+
return nil, err
1517+
}
1518+
1519+
return &looprpc.StaticAddressSummaryResponse{
1520+
StaticAddress: address.String(),
1521+
TotalNumDeposits: uint32(totalNumDeposits),
1522+
ValueUnconfirmed: valueUnconfirmed,
1523+
ValueDeposited: valueDeposited,
1524+
ValueExpired: valueExpired,
1525+
ValueWithdrawn: valueWithdrawn,
1526+
FilteredDeposits: clientDeposits,
1527+
}, nil
1528+
}
1529+
1530+
type filterFunc func(deposits *deposit.Deposit) bool
1531+
1532+
func filter(deposits []*deposit.Deposit, f filterFunc) []*looprpc.Deposit {
1533+
var clientDeposits []*looprpc.Deposit
1534+
for _, d := range deposits {
1535+
if !f(d) {
1536+
continue
1537+
}
1538+
1539+
hash := d.Hash
1540+
outpoint := wire.NewOutPoint(&hash, d.Index).String()
1541+
deposit := &looprpc.Deposit{
1542+
Id: d.ID[:],
1543+
State: toClientState(d.GetState()),
1544+
Outpoint: outpoint,
1545+
Value: int64(d.Value),
1546+
ConfirmationHeight: d.ConfirmationHeight,
1547+
}
1548+
1549+
clientDeposits = append(clientDeposits, deposit)
1550+
}
1551+
1552+
return clientDeposits
1553+
}
1554+
1555+
func toClientState(state fsm.StateType) looprpc.DepositState {
1556+
switch state {
1557+
case deposit.Deposited:
1558+
return looprpc.DepositState_DEPOSITED
1559+
1560+
case deposit.Withdrawing:
1561+
return looprpc.DepositState_WITHDRAWING
1562+
1563+
case deposit.Withdrawn:
1564+
return looprpc.DepositState_WITHDRAWN
1565+
1566+
case deposit.PublishExpiredDeposit:
1567+
return looprpc.DepositState_PUBLISH_EXPIRED
1568+
1569+
case deposit.WaitForExpirySweep:
1570+
return looprpc.DepositState_WAIT_FOR_EXPIRY_SWEEP
1571+
1572+
case deposit.Expired:
1573+
return looprpc.DepositState_EXPIRED
1574+
1575+
case deposit.Failed:
1576+
return looprpc.DepositState_FAILED_STATE
1577+
1578+
default:
1579+
return looprpc.DepositState_UNKNOWN_STATE
1580+
}
1581+
}
1582+
1583+
func toServerState(state looprpc.DepositState) fsm.StateType {
1584+
switch state {
1585+
case looprpc.DepositState_DEPOSITED:
1586+
return deposit.Deposited
1587+
1588+
case looprpc.DepositState_WITHDRAWING:
1589+
return deposit.Withdrawing
1590+
1591+
case looprpc.DepositState_WITHDRAWN:
1592+
return deposit.Withdrawn
1593+
1594+
case looprpc.DepositState_PUBLISH_EXPIRED:
1595+
return deposit.PublishExpiredDeposit
1596+
1597+
case looprpc.DepositState_WAIT_FOR_EXPIRY_SWEEP:
1598+
return deposit.WaitForExpirySweep
1599+
1600+
case looprpc.DepositState_EXPIRED:
1601+
return deposit.Expired
1602+
1603+
case looprpc.DepositState_FAILED_STATE:
1604+
return deposit.Failed
1605+
1606+
default:
1607+
return fsm.EmptyState
1608+
}
1609+
}
1610+
14131611
func toServerOutpoints(outpoints []*looprpc.OutPoint) ([]wire.OutPoint,
14141612
error) {
14151613

0 commit comments

Comments
 (0)