Skip to content

Commit bea1827

Browse files
committed
assets+loopdb: implement asset deposit withdrawal
This commit implements the necessary plumbing to enable deposit withdrawal. Withdrawing reveals the server's internal key for the deposit, allowing the client to sweep it independently.
1 parent d132819 commit bea1827

File tree

6 files changed

+129
-1
lines changed

6 files changed

+129
-1
lines changed

assets/deposit/manager.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,3 +1012,76 @@ func (m *Manager) releaseDepositSweepInputs(ctx context.Context,
10121012

10131013
return nil
10141014
}
1015+
1016+
// WithdrawDeposits withdraws the deposits with the given IDs. It will first ask
1017+
// the server for the deposit keys, then initate the withdrawal by updating the
1018+
// deposit state.
1019+
func (m *Manager) WithdrawDeposits(ctx context.Context,
1020+
depositIDs []string) error {
1021+
1022+
done, err := m.scheduleNextCall()
1023+
if err != nil {
1024+
return err
1025+
}
1026+
defer done()
1027+
1028+
for _, depositID := range depositIDs {
1029+
d, ok := m.deposits[depositID]
1030+
if !ok {
1031+
return fmt.Errorf("deposit %v not found", depositID)
1032+
}
1033+
1034+
if d.State != StateConfirmed {
1035+
return fmt.Errorf("deposit %v is not withdrawable, "+
1036+
"current state: %v", depositID, d.State)
1037+
}
1038+
1039+
log.Infof("Initiating deposit withdrawal %v: %v",
1040+
depositID, d.Amount)
1041+
}
1042+
1043+
keys, err := m.depositServiceClient.WithdrawAssetDeposits(
1044+
ctx, &swapserverrpc.WithdrawAssetDepositsServerReq{
1045+
DepositIds: depositIDs,
1046+
},
1047+
)
1048+
if err != nil {
1049+
return fmt.Errorf("unable to request withdrawal: %w", err)
1050+
}
1051+
1052+
for depositID, privKeyBytes := range keys.DepositKeys {
1053+
d, ok := m.deposits[depositID]
1054+
if !ok {
1055+
log.Warnf("Skipping withdrawal of unknown deposit: %v",
1056+
depositID)
1057+
continue
1058+
}
1059+
1060+
privKey, pubKey := btcec.PrivKeyFromBytes(privKeyBytes)
1061+
if !d.CoSignerInternalKey.IsEqual(pubKey) {
1062+
return fmt.Errorf("revealed co-signer internal key "+
1063+
"does not match local key for %v", depositID)
1064+
}
1065+
1066+
err := m.store.SetAssetDepositServerKey(ctx, depositID, privKey)
1067+
if err != nil {
1068+
return err
1069+
}
1070+
1071+
d.State = StateWithdrawn
1072+
err = d.GenerateSweepKeys(ctx, m.tapClient)
1073+
if err != nil {
1074+
log.Errorf("Unable to generate sweep keys for deposit "+
1075+
"withdrawal %v: %v", d.ID, err)
1076+
1077+
return err
1078+
}
1079+
1080+
err = m.handleDepositStateUpdate(ctx, d)
1081+
if err != nil {
1082+
return err
1083+
}
1084+
}
1085+
1086+
return nil
1087+
}

assets/deposit/server.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,5 +149,19 @@ func (s *Server) WithdrawAssetDeposits(ctx context.Context,
149149
in *looprpc.WithdrawAssetDepositsRequest) (
150150
*looprpc.WithdrawAssetDepositsResponse, error) {
151151

152-
return nil, status.Error(codes.Unimplemented, "unimplemented")
152+
if s.manager == nil {
153+
return nil, ErrAssetDepositsUnavailable
154+
}
155+
156+
if len(in.DepositIds) == 0 {
157+
return nil, status.Error(codes.InvalidArgument,
158+
"at least one deposit id must be provided")
159+
}
160+
161+
err := s.manager.WithdrawDeposits(ctx, in.DepositIds)
162+
if err != nil {
163+
return nil, status.Error(codes.Internal, err.Error())
164+
}
165+
166+
return &looprpc.WithdrawAssetDepositsResponse{}, nil
153167
}

assets/deposit/sql_store.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ type Querier interface {
3535

3636
GetActiveAssetDeposits(ctx context.Context) (
3737
[]sqlc.GetActiveAssetDepositsRow, error)
38+
39+
SetAssetDepositServerInternalKey(ctx context.Context,
40+
arg sqlc.SetAssetDepositServerInternalKeyParams) error
3841
}
3942

4043
// DepositBaseDB is the interface that contains all the queries generated
@@ -135,6 +138,8 @@ func (s *SQLStore) UpdateDeposit(ctx context.Context, d *Deposit) error {
135138
}
136139

137140
case StateExpired:
141+
fallthrough
142+
case StateWithdrawn:
138143
scriptKey := d.SweepScriptKey.SerializeCompressed()
139144
internalKey := d.SweepInternalKey.SerializeCompressed()
140145
err := tx.SetAssetDepositSweepKeys(
@@ -310,3 +315,16 @@ func (s *SQLStore) GetActiveDeposits(ctx context.Context) ([]Deposit, error) {
310315

311316
return deposits, nil
312317
}
318+
319+
// SetAssetDepositServerKey sets the server's internal key for the give asset
320+
// deposit.
321+
func (s *SQLStore) SetAssetDepositServerKey(ctx context.Context,
322+
depositID string, key *btcec.PrivateKey) error {
323+
324+
return s.db.SetAssetDepositServerInternalKey(
325+
ctx, sqlc.SetAssetDepositServerInternalKeyParams{
326+
DepositID: depositID,
327+
ServerInternalKey: key.Serialize(),
328+
},
329+
)
330+
}

loopdb/sqlc/asset_deposits.sql.go

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

loopdb/sqlc/querier.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

loopdb/sqlc/queries/asset_deposits.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,8 @@ WHERE u.id = (
5959
)
6060
AND u.update_state IN (0, 1, 2, 3, 4, 5, 6);
6161

62+
-- name: SetAssetDepositServerInternalKey :exec
63+
UPDATE asset_deposits
64+
SET server_internal_key = $2
65+
WHERE deposit_id = $1
66+
AND server_internal_key IS NULL;

0 commit comments

Comments
 (0)