Skip to content

Commit 20fb359

Browse files
committed
add all in one commit for the moment
1 parent 178f088 commit 20fb359

24 files changed

+2262
-249
lines changed

cmd/loop/staticaddr.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@ package main
22

33
import (
44
"context"
5+
"encoding/hex"
6+
"errors"
57
"fmt"
8+
"strconv"
9+
"strings"
610

11+
"github.com/btcsuite/btcd/chaincfg/chainhash"
712
"github.com/lightninglabs/loop/looprpc"
813
"github.com/urfave/cli"
914
)
@@ -16,6 +21,7 @@ var staticAddressCommands = cli.Command{
1621
Subcommands: []cli.Command{
1722
newStaticAddressCommand,
1823
listUnspentCommand,
24+
withdrawalCommand,
1925
},
2026
}
2127

@@ -105,3 +111,113 @@ func listUnspent(ctx *cli.Context) error {
105111

106112
return nil
107113
}
114+
115+
var withdrawalCommand = cli.Command{
116+
Name: "withdraw",
117+
ShortName: "w",
118+
Usage: "Withdraw from static address deposits.",
119+
Description: `
120+
Withdraws from all or selected static address deposits by sweeping them
121+
back to our lnd wallet.
122+
`,
123+
Flags: []cli.Flag{
124+
cli.StringSliceFlag{
125+
Name: "utxo",
126+
Usage: "specify utxos as outpoints(tx:idx) which will" +
127+
"be closed.",
128+
},
129+
cli.BoolFlag{
130+
Name: "all",
131+
Usage: "withdraws all static address deposits.",
132+
},
133+
},
134+
Action: withdraw,
135+
}
136+
137+
func withdraw(ctx *cli.Context) error {
138+
if ctx.NArg() > 0 {
139+
return cli.ShowCommandHelp(ctx, "withdraw")
140+
}
141+
142+
client, cleanup, err := getClient(ctx)
143+
if err != nil {
144+
return err
145+
}
146+
defer cleanup()
147+
148+
var (
149+
req = &looprpc.WithdrawDepositsRequest{}
150+
isAllSelected = ctx.IsSet("all")
151+
isUtxoSelected = ctx.IsSet("utxo")
152+
outpoints []*looprpc.OutPoint
153+
ctxb = context.Background()
154+
)
155+
156+
switch {
157+
case isAllSelected == isUtxoSelected:
158+
return errors.New("must select either all or some utxos")
159+
160+
case isAllSelected:
161+
case isUtxoSelected:
162+
utxos := ctx.StringSlice("utxo")
163+
outpoints, err = utxosToOutpoints(utxos)
164+
if err != nil {
165+
return err
166+
}
167+
168+
req.Outpoints = outpoints
169+
170+
default:
171+
return fmt.Errorf("unknown withdrawal request")
172+
}
173+
174+
resp, err := client.WithdrawDeposits(ctxb, &looprpc.WithdrawDepositsRequest{
175+
Outpoints: outpoints,
176+
All: isAllSelected,
177+
})
178+
if err != nil {
179+
return err
180+
}
181+
182+
printRespJSON(resp)
183+
184+
return nil
185+
}
186+
187+
func utxosToOutpoints(utxos []string) ([]*looprpc.OutPoint, error) {
188+
var outpoints []*looprpc.OutPoint
189+
if len(utxos) == 0 {
190+
return nil, fmt.Errorf("no utxos specified")
191+
}
192+
for _, utxo := range utxos {
193+
outpoint, err := NewProtoOutPoint(utxo)
194+
if err != nil {
195+
return nil, err
196+
}
197+
outpoints = append(outpoints, outpoint)
198+
}
199+
200+
return outpoints, nil
201+
}
202+
203+
// NewProtoOutPoint parses an OutPoint into its corresponding lnrpc.OutPoint
204+
// type.
205+
func NewProtoOutPoint(op string) (*looprpc.OutPoint, error) {
206+
parts := strings.Split(op, ":")
207+
if len(parts) != 2 {
208+
return nil, errors.New("outpoint should be of the form " +
209+
"txid:index")
210+
}
211+
txid := parts[0]
212+
if hex.DecodedLen(len(txid)) != chainhash.HashSize {
213+
return nil, fmt.Errorf("invalid hex-encoded txid %v", txid)
214+
}
215+
outputIndex, err := strconv.Atoi(parts[1])
216+
if err != nil {
217+
return nil, fmt.Errorf("invalid output index: %v", err)
218+
}
219+
return &looprpc.OutPoint{
220+
TxidStr: txid,
221+
OutputIndex: uint32(outputIndex),
222+
}, nil
223+
}

loopd/daemon.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
loop_looprpc "github.com/lightninglabs/loop/looprpc"
2424
"github.com/lightninglabs/loop/staticaddr/address"
2525
"github.com/lightninglabs/loop/staticaddr/deposit"
26+
"github.com/lightninglabs/loop/staticaddr/withdraw"
2627
loop_swaprpc "github.com/lightninglabs/loop/swapserverrpc"
2728
"github.com/lightninglabs/loop/sweepbatcher"
2829
"github.com/lightningnetwork/lnd/clock"
@@ -437,6 +438,12 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
437438
swapClient.Conn,
438439
)
439440

441+
// Create a static address client that cooperatively closes deposits
442+
// with the server.
443+
withdrawalClient := loop_swaprpc.NewWithdrawalServerClient(
444+
swapClient.Conn,
445+
)
446+
440447
// Both the client RPC server and the swap server client should stop
441448
// on main context cancel. So we create it early and pass it down.
442449
d.mainCtx, d.mainCtxCancel = context.WithCancel(context.Background())
@@ -500,6 +507,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
500507

501508
staticAddressManager *address.Manager
502509
depositManager *deposit.Manager
510+
withdrawalManager *withdraw.Manager
503511
)
504512
// Create the reservation and instantout managers.
505513
if d.cfg.EnableExperimental {
@@ -561,6 +569,18 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
561569
Signer: d.lnd.Signer,
562570
}
563571
depositManager = deposit.NewManager(depoCfg)
572+
573+
// Static address deposit withdrawal manager setup.
574+
closeCfg := &withdraw.ManagerConfig{
575+
WithdrawalServerClient: withdrawalClient,
576+
AddressManager: staticAddressManager,
577+
DepositManager: depositManager,
578+
WalletKit: d.lnd.WalletKit,
579+
ChainParams: d.lnd.ChainParams,
580+
ChainNotifier: d.lnd.ChainNotifier,
581+
Signer: d.lnd.Signer,
582+
}
583+
withdrawalManager = withdraw.NewManager(closeCfg)
564584
}
565585

566586
// Now finally fully initialize the swap client RPC server instance.
@@ -578,6 +598,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
578598
instantOutManager: instantOutManager,
579599
staticAddressManager: staticAddressManager,
580600
depositManager: depositManager,
601+
withdrawalManager: withdrawalManager,
581602
}
582603

583604
// Retrieve all currently existing swaps from the database.
@@ -709,6 +730,32 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
709730
depositManager.WaitInitComplete()
710731
}
711732

733+
// Start the static address deposit withdrawal manager.
734+
if withdrawalManager != nil {
735+
d.wg.Add(1)
736+
go func() {
737+
defer d.wg.Done()
738+
739+
// Lnd's GetInfo call supplies us with the current block
740+
// height.
741+
info, err := d.lnd.Client.GetInfo(d.mainCtx)
742+
if err != nil {
743+
d.internalErrChan <- err
744+
return
745+
}
746+
747+
log.Info("Starting static address deposit withdrawal " +
748+
"manager...")
749+
err = withdrawalManager.Run(d.mainCtx, info.BlockHeight)
750+
if err != nil && !errors.Is(context.Canceled, err) {
751+
d.internalErrChan <- err
752+
}
753+
log.Info("Static address deposit withdrawal manager " +
754+
"stopped")
755+
}()
756+
withdrawalManager.WaitInitComplete()
757+
}
758+
712759
// Last, start our internal error handler. This will return exactly one
713760
// error or nil on the main error channel to inform the caller that
714761
// something went wrong or that shutdown is complete. We don't add to

loopd/perms/perms.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,13 @@ var RequiredPermissions = map[string][]bakery.Op{
8383
Entity: "loop",
8484
Action: "in",
8585
}},
86+
"/looprpc.SwapClient/WithdrawDeposits": {{
87+
Entity: "swap",
88+
Action: "execute",
89+
}, {
90+
Entity: "loop",
91+
Action: "in",
92+
}},
8693
"/looprpc.SwapClient/GetLsatTokens": {{
8794
Entity: "auth",
8895
Action: "read",

loopd/swapclient_server.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/btcsuite/btcd/btcec/v2"
1616
"github.com/btcsuite/btcd/btcutil"
1717
"github.com/btcsuite/btcd/chaincfg"
18+
"github.com/btcsuite/btcd/wire"
1819
"github.com/lightninglabs/aperture/lsat"
1920
"github.com/lightninglabs/lndclient"
2021
"github.com/lightninglabs/loop"
@@ -26,6 +27,7 @@ import (
2627
clientrpc "github.com/lightninglabs/loop/looprpc"
2728
"github.com/lightninglabs/loop/staticaddr/address"
2829
"github.com/lightninglabs/loop/staticaddr/deposit"
30+
"github.com/lightninglabs/loop/staticaddr/withdraw"
2931
"github.com/lightninglabs/loop/swap"
3032
looprpc "github.com/lightninglabs/loop/swapserverrpc"
3133
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
@@ -87,6 +89,7 @@ type swapClientServer struct {
8789
instantOutManager *instantout.Manager
8890
staticAddressManager *address.Manager
8991
depositManager *deposit.Manager
92+
withdrawalManager *withdraw.Manager
9093
swaps map[lntypes.Hash]loop.SwapInfo
9194
subscribers map[int]chan<- interface{}
9295
statusChan chan loop.SwapInfo
@@ -1280,6 +1283,67 @@ func (s *swapClientServer) ListUnspentDeposits(ctx context.Context,
12801283
return &clientrpc.ListUnspentDepositsResponse{Utxos: respUtxos}, nil
12811284
}
12821285

1286+
// WithdrawDeposits tries to obtain a partial signature from the server to spend
1287+
// the selected deposits to the client's wallet.
1288+
func (s *swapClientServer) WithdrawDeposits(ctx context.Context,
1289+
req *clientrpc.WithdrawDepositsRequest) (
1290+
*clientrpc.WithdrawDepositsResponse, error) {
1291+
1292+
var (
1293+
isAllSelected = req.All
1294+
isUtxoSelected = req.Outpoints != nil
1295+
outpoints []wire.OutPoint
1296+
err error
1297+
)
1298+
1299+
switch {
1300+
case isAllSelected == isUtxoSelected:
1301+
return nil, fmt.Errorf("must select either all or some utxos")
1302+
1303+
case isAllSelected:
1304+
deposits, err := s.depositManager.GetActiveDepositsInState(
1305+
deposit.Deposited,
1306+
)
1307+
if err != nil {
1308+
return nil, err
1309+
}
1310+
1311+
for _, d := range deposits {
1312+
outpoints = append(outpoints, d.OutPoint)
1313+
}
1314+
1315+
case isUtxoSelected:
1316+
outpoints, err = toServerOutpoints(req.Outpoints)
1317+
if err != nil {
1318+
return nil, err
1319+
}
1320+
}
1321+
1322+
err = s.withdrawalManager.WithdrawDeposits(ctx, outpoints)
1323+
if err != nil {
1324+
return nil, err
1325+
}
1326+
1327+
return &clientrpc.WithdrawDepositsResponse{}, err
1328+
}
1329+
1330+
func toServerOutpoints(outpoints []*clientrpc.OutPoint) ([]wire.OutPoint,
1331+
error) {
1332+
1333+
var serverOutpoints []wire.OutPoint
1334+
for _, o := range outpoints {
1335+
outpointStr := fmt.Sprintf("%s:%d", o.TxidStr, o.OutputIndex)
1336+
newOutpoint, err := wire.NewOutPointFromString(outpointStr)
1337+
if err != nil {
1338+
return nil, err
1339+
}
1340+
1341+
serverOutpoints = append(serverOutpoints, *newOutpoint)
1342+
}
1343+
1344+
return serverOutpoints, nil
1345+
}
1346+
12831347
func rpcAutoloopReason(reason liquidity.Reason) (clientrpc.AutoReason, error) {
12841348
switch reason {
12851349
case liquidity.ReasonNone:

loopdb/sqlc/migrations/000008_static_address_deposits.up.sql

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ CREATE TABLE IF NOT EXISTS deposits (
2424
timeout_sweep_pk_script BYTEA NOT NULL,
2525

2626
-- expiry_sweep_txid is the transaction id of the expiry sweep.
27-
expiry_sweep_txid BLOB
27+
expiry_sweep_txid BLOB,
28+
29+
-- withdrawal_sweep_pk_script is the address that will be used to sweep the
30+
-- deposit cooperatively with the server before it has expired.
31+
withdrawal_sweep_address TEXT
2832
);
2933

3034
-- deposit_updates contains all the updates to a deposit.

loopdb/sqlc/models.go

Lines changed: 9 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

loopdb/sqlc/queries/static_address_deposits.sql

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@ INSERT INTO deposits (
66
amount,
77
confirmation_height,
88
timeout_sweep_pk_script,
9-
expiry_sweep_txid
9+
expiry_sweep_txid,
10+
withdrawal_sweep_address
1011
) VALUES (
1112
$1,
1213
$2,
1314
$3,
1415
$4,
1516
$5,
1617
$6,
17-
$7
18+
$7,
19+
$8
1820
);
1921

2022
-- name: UpdateDeposit :exec
@@ -23,7 +25,8 @@ SET
2325
tx_hash = $2,
2426
out_index = $3,
2527
confirmation_height = $4,
26-
expiry_sweep_txid = $5
28+
expiry_sweep_txid = $5,
29+
withdrawal_sweep_address = $6
2730
WHERE
2831
deposits.deposit_id = $1;
2932

0 commit comments

Comments
 (0)