loopdb: persist deposit address ownership

Associate every deposit with the static address parameters that created
it. This lets restored deposits recover the correct script and signing
keys instead of assuming the legacy root address.
This commit is contained in:
Slyghtning 2026-07-10 11:58:03 +02:00
parent 1baf02e210
commit 29f5ce73ce
No known key found for this signature in database
GPG key ID: F82D456EA023C9BF
15 changed files with 498 additions and 43 deletions

View file

@ -0,0 +1 @@
ALTER TABLE deposits DROP COLUMN static_address_id;

View file

@ -0,0 +1,8 @@
ALTER TABLE deposits ADD static_address_id INT REFERENCES static_addresses(id);
UPDATE deposits
SET static_address_id = (
SELECT id FROM static_addresses ORDER BY id ASC LIMIT 1
)
WHERE static_address_id IS NULL
AND EXISTS (SELECT 1 FROM static_addresses);

View file

@ -20,6 +20,7 @@ type Deposit struct {
ExpirySweepTxid []byte
FinalizedWithdrawalTx sql.NullString
SwapHash []byte
StaticAddressID sql.NullInt32
}
type DepositUpdate struct {

View file

@ -10,7 +10,7 @@ import (
)
type Querier interface {
AllDeposits(ctx context.Context) ([]Deposit, error)
AllDeposits(ctx context.Context) ([]AllDepositsRow, error)
AllStaticAddresses(ctx context.Context) ([]StaticAddress, error)
CancelBatch(ctx context.Context, id int32) error
CreateDeposit(ctx context.Context, arg CreateDepositParams) error
@ -18,19 +18,20 @@ type Querier interface {
CreateStaticAddress(ctx context.Context, arg CreateStaticAddressParams) error
CreateWithdrawal(ctx context.Context, arg CreateWithdrawalParams) error
CreateWithdrawalDeposit(ctx context.Context, arg CreateWithdrawalDepositParams) error
DepositForOutpoint(ctx context.Context, arg DepositForOutpointParams) (Deposit, error)
DepositForOutpoint(ctx context.Context, arg DepositForOutpointParams) (DepositForOutpointRow, error)
DepositIDsForSwapHash(ctx context.Context, swapHash []byte) ([][]byte, error)
DepositsForSwapHash(ctx context.Context, swapHash []byte) ([]DepositsForSwapHashRow, error)
FetchLiquidityParams(ctx context.Context) ([]byte, error)
GetAllWithdrawals(ctx context.Context) ([]Withdrawal, error)
GetBatchSweeps(ctx context.Context, batchID int32) ([]Sweep, error)
GetBatchSweptAmount(ctx context.Context, batchID int32) (int64, error)
GetDeposit(ctx context.Context, depositID []byte) (Deposit, error)
GetDeposit(ctx context.Context, depositID []byte) (GetDepositRow, error)
GetInstantOutSwap(ctx context.Context, swapHash []byte) (GetInstantOutSwapRow, error)
GetInstantOutSwapUpdates(ctx context.Context, swapHash []byte) ([]InstantoutUpdate, error)
GetInstantOutSwaps(ctx context.Context) ([]GetInstantOutSwapsRow, error)
GetLastUpdateID(ctx context.Context, swapHash []byte) (int32, error)
GetLatestDepositUpdate(ctx context.Context, depositID []byte) (DepositUpdate, error)
GetLegacyAddress(ctx context.Context) (StaticAddress, error)
GetLoopInSwap(ctx context.Context, swapHash []byte) (GetLoopInSwapRow, error)
GetLoopInSwapUpdates(ctx context.Context, swapHash []byte) ([]StaticAddressSwapUpdate, error)
GetLoopInSwaps(ctx context.Context) ([]GetLoopInSwapsRow, error)
@ -42,6 +43,7 @@ type Querier interface {
GetReservationUpdates(ctx context.Context, reservationID []byte) ([]ReservationUpdate, error)
GetReservations(ctx context.Context) ([]Reservation, error)
GetStaticAddress(ctx context.Context, pkscript []byte) (StaticAddress, error)
GetStaticAddressID(ctx context.Context, pkscript []byte) (int32, error)
GetStaticAddressLoopInSwap(ctx context.Context, swapHash []byte) (GetStaticAddressLoopInSwapRow, error)
GetStaticAddressLoopInSwapsByStates(ctx context.Context, dollar_1 sql.NullString) ([]GetStaticAddressLoopInSwapsByStatesRow, error)
GetSwapUpdates(ctx context.Context, swapHash []byte) ([]SwapUpdate, error)
@ -68,6 +70,7 @@ type Querier interface {
OverrideSelectedSwapAmount(ctx context.Context, arg OverrideSelectedSwapAmountParams) error
OverrideSwapCosts(ctx context.Context, arg OverrideSwapCostsParams) error
RecordStaticAddressRiskDecision(ctx context.Context, arg RecordStaticAddressRiskDecisionParams) error
SetAllNullDepositsStaticAddressID(ctx context.Context, staticAddressID sql.NullInt32) error
SwapHashForDepositID(ctx context.Context, depositID []byte) ([]byte, error)
UpdateBatch(ctx context.Context, arg UpdateBatchParams) error
UpdateDeposit(ctx context.Context, arg UpdateDepositParams) error

View file

@ -7,7 +7,8 @@ INSERT INTO deposits (
confirmation_height,
timeout_sweep_pk_script,
expiry_sweep_txid,
finalized_withdrawal_tx
finalized_withdrawal_tx,
static_address_id
) VALUES (
$1,
$2,
@ -16,7 +17,8 @@ INSERT INTO deposits (
$5,
$6,
$7,
$8
$8,
$9
);
-- name: UpdateDeposit :exec
@ -43,17 +45,35 @@ INSERT INTO deposit_updates (
-- name: GetDeposit :one
SELECT
*
d.*,
sa.client_pubkey client_pubkey,
sa.server_pubkey server_pubkey,
sa.expiry expiry,
sa.client_key_family client_key_family,
sa.client_key_index client_key_index,
sa.pkscript pkscript,
sa.protocol_version protocol_version,
sa.initiation_height initiation_height
FROM
deposits
deposits d
LEFT JOIN static_addresses sa ON sa.id = d.static_address_id
WHERE
deposit_id = $1;
-- name: DepositForOutpoint :one
SELECT
*
d.*,
sa.client_pubkey client_pubkey,
sa.server_pubkey server_pubkey,
sa.expiry expiry,
sa.client_key_family client_key_family,
sa.client_key_index client_key_index,
sa.pkscript pkscript,
sa.protocol_version protocol_version,
sa.initiation_height initiation_height
FROM
deposits
deposits d
LEFT JOIN static_addresses sa ON sa.id = d.static_address_id
WHERE
tx_hash = $1
AND
@ -61,11 +81,20 @@ AND
-- name: AllDeposits :many
SELECT
*
d.*,
sa.client_pubkey client_pubkey,
sa.server_pubkey server_pubkey,
sa.expiry expiry,
sa.client_key_family client_key_family,
sa.client_key_index client_key_index,
sa.pkscript pkscript,
sa.protocol_version protocol_version,
sa.initiation_height initiation_height
FROM
deposits
deposits d
LEFT JOIN static_addresses sa ON sa.id = d.static_address_id
ORDER BY
id ASC;
d.id ASC;
-- name: GetLatestDepositUpdate :one
SELECT
@ -76,4 +105,9 @@ WHERE
deposit_id = $1
ORDER BY
update_timestamp DESC
LIMIT 1;
LIMIT 1;
-- name: SetAllNullDepositsStaticAddressID :exec
UPDATE deposits
SET static_address_id = $1
WHERE static_address_id IS NULL;

View file

@ -1,10 +1,15 @@
-- name: AllStaticAddresses :many
SELECT * FROM static_addresses;
SELECT * FROM static_addresses
ORDER BY id ASC;
-- name: GetStaticAddress :one
SELECT * FROM static_addresses
WHERE pkscript=$1;
-- name: GetStaticAddressID :one
SELECT id FROM static_addresses
WHERE pkscript=$1;
-- name: CreateStaticAddress :exec
INSERT INTO static_addresses (
client_pubkey,
@ -24,4 +29,9 @@ INSERT INTO static_addresses (
$6,
$7,
$8
);
);
-- name: GetLegacyAddress :one
SELECT * FROM static_addresses
ORDER BY id ASC
LIMIT 1;

View file

@ -13,22 +13,53 @@ import (
const allDeposits = `-- name: AllDeposits :many
SELECT
id, deposit_id, tx_hash, out_index, amount, confirmation_height, timeout_sweep_pk_script, expiry_sweep_txid, finalized_withdrawal_tx, swap_hash
d.id, d.deposit_id, d.tx_hash, d.out_index, d.amount, d.confirmation_height, d.timeout_sweep_pk_script, d.expiry_sweep_txid, d.finalized_withdrawal_tx, d.swap_hash, d.static_address_id,
sa.client_pubkey client_pubkey,
sa.server_pubkey server_pubkey,
sa.expiry expiry,
sa.client_key_family client_key_family,
sa.client_key_index client_key_index,
sa.pkscript pkscript,
sa.protocol_version protocol_version,
sa.initiation_height initiation_height
FROM
deposits
deposits d
LEFT JOIN static_addresses sa ON sa.id = d.static_address_id
ORDER BY
id ASC
d.id ASC
`
func (q *Queries) AllDeposits(ctx context.Context) ([]Deposit, error) {
type AllDepositsRow struct {
ID int32
DepositID []byte
TxHash []byte
OutIndex int32
Amount int64
ConfirmationHeight int64
TimeoutSweepPkScript []byte
ExpirySweepTxid []byte
FinalizedWithdrawalTx sql.NullString
SwapHash []byte
StaticAddressID sql.NullInt32
ClientPubkey []byte
ServerPubkey []byte
Expiry sql.NullInt32
ClientKeyFamily sql.NullInt32
ClientKeyIndex sql.NullInt32
Pkscript []byte
ProtocolVersion sql.NullInt32
InitiationHeight sql.NullInt32
}
func (q *Queries) AllDeposits(ctx context.Context) ([]AllDepositsRow, error) {
rows, err := q.db.QueryContext(ctx, allDeposits)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Deposit
var items []AllDepositsRow
for rows.Next() {
var i Deposit
var i AllDepositsRow
if err := rows.Scan(
&i.ID,
&i.DepositID,
@ -40,6 +71,15 @@ func (q *Queries) AllDeposits(ctx context.Context) ([]Deposit, error) {
&i.ExpirySweepTxid,
&i.FinalizedWithdrawalTx,
&i.SwapHash,
&i.StaticAddressID,
&i.ClientPubkey,
&i.ServerPubkey,
&i.Expiry,
&i.ClientKeyFamily,
&i.ClientKeyIndex,
&i.Pkscript,
&i.ProtocolVersion,
&i.InitiationHeight,
); err != nil {
return nil, err
}
@ -63,7 +103,8 @@ INSERT INTO deposits (
confirmation_height,
timeout_sweep_pk_script,
expiry_sweep_txid,
finalized_withdrawal_tx
finalized_withdrawal_tx,
static_address_id
) VALUES (
$1,
$2,
@ -72,7 +113,8 @@ INSERT INTO deposits (
$5,
$6,
$7,
$8
$8,
$9
)
`
@ -85,6 +127,7 @@ type CreateDepositParams struct {
TimeoutSweepPkScript []byte
ExpirySweepTxid []byte
FinalizedWithdrawalTx sql.NullString
StaticAddressID sql.NullInt32
}
func (q *Queries) CreateDeposit(ctx context.Context, arg CreateDepositParams) error {
@ -97,15 +140,25 @@ func (q *Queries) CreateDeposit(ctx context.Context, arg CreateDepositParams) er
arg.TimeoutSweepPkScript,
arg.ExpirySweepTxid,
arg.FinalizedWithdrawalTx,
arg.StaticAddressID,
)
return err
}
const depositForOutpoint = `-- name: DepositForOutpoint :one
SELECT
id, deposit_id, tx_hash, out_index, amount, confirmation_height, timeout_sweep_pk_script, expiry_sweep_txid, finalized_withdrawal_tx, swap_hash
d.id, d.deposit_id, d.tx_hash, d.out_index, d.amount, d.confirmation_height, d.timeout_sweep_pk_script, d.expiry_sweep_txid, d.finalized_withdrawal_tx, d.swap_hash, d.static_address_id,
sa.client_pubkey client_pubkey,
sa.server_pubkey server_pubkey,
sa.expiry expiry,
sa.client_key_family client_key_family,
sa.client_key_index client_key_index,
sa.pkscript pkscript,
sa.protocol_version protocol_version,
sa.initiation_height initiation_height
FROM
deposits
deposits d
LEFT JOIN static_addresses sa ON sa.id = d.static_address_id
WHERE
tx_hash = $1
AND
@ -117,9 +170,31 @@ type DepositForOutpointParams struct {
OutIndex int32
}
func (q *Queries) DepositForOutpoint(ctx context.Context, arg DepositForOutpointParams) (Deposit, error) {
type DepositForOutpointRow struct {
ID int32
DepositID []byte
TxHash []byte
OutIndex int32
Amount int64
ConfirmationHeight int64
TimeoutSweepPkScript []byte
ExpirySweepTxid []byte
FinalizedWithdrawalTx sql.NullString
SwapHash []byte
StaticAddressID sql.NullInt32
ClientPubkey []byte
ServerPubkey []byte
Expiry sql.NullInt32
ClientKeyFamily sql.NullInt32
ClientKeyIndex sql.NullInt32
Pkscript []byte
ProtocolVersion sql.NullInt32
InitiationHeight sql.NullInt32
}
func (q *Queries) DepositForOutpoint(ctx context.Context, arg DepositForOutpointParams) (DepositForOutpointRow, error) {
row := q.db.QueryRowContext(ctx, depositForOutpoint, arg.TxHash, arg.OutIndex)
var i Deposit
var i DepositForOutpointRow
err := row.Scan(
&i.ID,
&i.DepositID,
@ -131,22 +206,62 @@ func (q *Queries) DepositForOutpoint(ctx context.Context, arg DepositForOutpoint
&i.ExpirySweepTxid,
&i.FinalizedWithdrawalTx,
&i.SwapHash,
&i.StaticAddressID,
&i.ClientPubkey,
&i.ServerPubkey,
&i.Expiry,
&i.ClientKeyFamily,
&i.ClientKeyIndex,
&i.Pkscript,
&i.ProtocolVersion,
&i.InitiationHeight,
)
return i, err
}
const getDeposit = `-- name: GetDeposit :one
SELECT
id, deposit_id, tx_hash, out_index, amount, confirmation_height, timeout_sweep_pk_script, expiry_sweep_txid, finalized_withdrawal_tx, swap_hash
d.id, d.deposit_id, d.tx_hash, d.out_index, d.amount, d.confirmation_height, d.timeout_sweep_pk_script, d.expiry_sweep_txid, d.finalized_withdrawal_tx, d.swap_hash, d.static_address_id,
sa.client_pubkey client_pubkey,
sa.server_pubkey server_pubkey,
sa.expiry expiry,
sa.client_key_family client_key_family,
sa.client_key_index client_key_index,
sa.pkscript pkscript,
sa.protocol_version protocol_version,
sa.initiation_height initiation_height
FROM
deposits
deposits d
LEFT JOIN static_addresses sa ON sa.id = d.static_address_id
WHERE
deposit_id = $1
`
func (q *Queries) GetDeposit(ctx context.Context, depositID []byte) (Deposit, error) {
type GetDepositRow struct {
ID int32
DepositID []byte
TxHash []byte
OutIndex int32
Amount int64
ConfirmationHeight int64
TimeoutSweepPkScript []byte
ExpirySweepTxid []byte
FinalizedWithdrawalTx sql.NullString
SwapHash []byte
StaticAddressID sql.NullInt32
ClientPubkey []byte
ServerPubkey []byte
Expiry sql.NullInt32
ClientKeyFamily sql.NullInt32
ClientKeyIndex sql.NullInt32
Pkscript []byte
ProtocolVersion sql.NullInt32
InitiationHeight sql.NullInt32
}
func (q *Queries) GetDeposit(ctx context.Context, depositID []byte) (GetDepositRow, error) {
row := q.db.QueryRowContext(ctx, getDeposit, depositID)
var i Deposit
var i GetDepositRow
err := row.Scan(
&i.ID,
&i.DepositID,
@ -158,6 +273,15 @@ func (q *Queries) GetDeposit(ctx context.Context, depositID []byte) (Deposit, er
&i.ExpirySweepTxid,
&i.FinalizedWithdrawalTx,
&i.SwapHash,
&i.StaticAddressID,
&i.ClientPubkey,
&i.ServerPubkey,
&i.Expiry,
&i.ClientKeyFamily,
&i.ClientKeyIndex,
&i.Pkscript,
&i.ProtocolVersion,
&i.InitiationHeight,
)
return i, err
}
@ -209,6 +333,17 @@ func (q *Queries) InsertDepositUpdate(ctx context.Context, arg InsertDepositUpda
return err
}
const setAllNullDepositsStaticAddressID = `-- name: SetAllNullDepositsStaticAddressID :exec
UPDATE deposits
SET static_address_id = $1
WHERE static_address_id IS NULL
`
func (q *Queries) SetAllNullDepositsStaticAddressID(ctx context.Context, staticAddressID sql.NullInt32) error {
_, err := q.db.ExecContext(ctx, setAllNullDepositsStaticAddressID, staticAddressID)
return err
}
const updateDeposit = `-- name: UpdateDeposit :exec
UPDATE deposits
SET

View file

@ -45,7 +45,7 @@ func (q *Queries) DepositIDsForSwapHash(ctx context.Context, swapHash []byte) ([
const depositsForSwapHash = `-- name: DepositsForSwapHash :many
SELECT
d.id, d.deposit_id, d.tx_hash, d.out_index, d.amount, d.confirmation_height, d.timeout_sweep_pk_script, d.expiry_sweep_txid, d.finalized_withdrawal_tx, d.swap_hash,
d.id, d.deposit_id, d.tx_hash, d.out_index, d.amount, d.confirmation_height, d.timeout_sweep_pk_script, d.expiry_sweep_txid, d.finalized_withdrawal_tx, d.swap_hash, d.static_address_id,
u.update_state,
u.update_timestamp
FROM
@ -73,6 +73,7 @@ type DepositsForSwapHashRow struct {
ExpirySweepTxid []byte
FinalizedWithdrawalTx sql.NullString
SwapHash []byte
StaticAddressID sql.NullInt32
UpdateState sql.NullString
UpdateTimestamp sql.NullTime
}
@ -97,6 +98,7 @@ func (q *Queries) DepositsForSwapHash(ctx context.Context, swapHash []byte) ([]D
&i.ExpirySweepTxid,
&i.FinalizedWithdrawalTx,
&i.SwapHash,
&i.StaticAddressID,
&i.UpdateState,
&i.UpdateTimestamp,
); err != nil {

View file

@ -11,6 +11,7 @@ import (
const allStaticAddresses = `-- name: AllStaticAddresses :many
SELECT id, client_pubkey, server_pubkey, expiry, client_key_family, client_key_index, pkscript, protocol_version, initiation_height FROM static_addresses
ORDER BY id ASC
`
func (q *Queries) AllStaticAddresses(ctx context.Context) ([]StaticAddress, error) {
@ -93,6 +94,29 @@ func (q *Queries) CreateStaticAddress(ctx context.Context, arg CreateStaticAddre
return err
}
const getLegacyAddress = `-- name: GetLegacyAddress :one
SELECT id, client_pubkey, server_pubkey, expiry, client_key_family, client_key_index, pkscript, protocol_version, initiation_height FROM static_addresses
ORDER BY id ASC
LIMIT 1
`
func (q *Queries) GetLegacyAddress(ctx context.Context) (StaticAddress, error) {
row := q.db.QueryRowContext(ctx, getLegacyAddress)
var i StaticAddress
err := row.Scan(
&i.ID,
&i.ClientPubkey,
&i.ServerPubkey,
&i.Expiry,
&i.ClientKeyFamily,
&i.ClientKeyIndex,
&i.Pkscript,
&i.ProtocolVersion,
&i.InitiationHeight,
)
return i, err
}
const getStaticAddress = `-- name: GetStaticAddress :one
SELECT id, client_pubkey, server_pubkey, expiry, client_key_family, client_key_index, pkscript, protocol_version, initiation_height FROM static_addresses
WHERE pkscript=$1
@ -114,3 +138,15 @@ func (q *Queries) GetStaticAddress(ctx context.Context, pkscript []byte) (Static
)
return i, err
}
const getStaticAddressID = `-- name: GetStaticAddressID :one
SELECT id FROM static_addresses
WHERE pkscript=$1
`
func (q *Queries) GetStaticAddressID(ctx context.Context, pkscript []byte) (int32, error) {
row := q.db.QueryRowContext(ctx, getStaticAddressID, pkscript)
var id int32
err := row.Scan(&id)
return id, err
}

View file

@ -42,7 +42,14 @@ func (s *SqlStore) CreateStaticAddress(ctx context.Context,
return s.baseDB.Queries.CreateStaticAddress(ctx, createArgs)
}
// GetAllStaticAddresses returns all address known to the server.
// GetStaticAddressID retrieves the database ID for a static address script.
func (s *SqlStore) GetStaticAddressID(ctx context.Context,
pkScript []byte) (int32, error) {
return s.baseDB.Queries.GetStaticAddressID(ctx, pkScript)
}
// GetAllStaticAddresses returns all addresses known to the client.
func (s *SqlStore) GetAllStaticAddresses(ctx context.Context) (
[]*script.Parameters, error) {
@ -64,6 +71,18 @@ func (s *SqlStore) GetAllStaticAddresses(ctx context.Context) (
return result, nil
}
// GetLegacyParameters returns the first static address created for this L402.
func (s *SqlStore) GetLegacyParameters(ctx context.Context) (
*script.Parameters, error) {
staticAddress, err := s.baseDB.Queries.GetLegacyAddress(ctx)
if err != nil {
return nil, err
}
return s.toAddressParameters(staticAddress)
}
// toAddressParameters transforms a database representation of a static address
// to an AddressParameters struct.
func (s *SqlStore) toAddressParameters(row sqlc.StaticAddress) (
@ -80,6 +99,7 @@ func (s *SqlStore) toAddressParameters(row sqlc.StaticAddress) (
}
return &script.Parameters{
ID: row.ID,
ClientPubkey: clientPubkey,
ServerPubkey: serverPubkey,
PkScript: row.Pkscript,

View file

@ -9,6 +9,8 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/loop/fsm"
"github.com/lightninglabs/loop/staticaddr/script"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lntypes"
)
@ -70,6 +72,11 @@ type Deposit struct {
// FinalizedWithdrawalTx is the coop-signed withdrawal transaction. It
// is republished on new block arrivals and on client restarts.
FinalizedWithdrawalTx *wire.MsgTx
// AddressParams are the static address parameters that produced this
// deposit's pkScript. Spending code must use these per-deposit
// parameters rather than assuming all deposits belong to one address.
AddressParams *script.Parameters
}
// IsInFinalState returns true if the deposit is final.
@ -152,6 +159,19 @@ func (d *Deposit) GetConfirmationHeightNoLock() int64 {
return d.ConfirmationHeight
}
// GetStaticAddressScript reconstructs the static address script for this
// deposit's matched address parameters.
func (d *Deposit) GetStaticAddressScript() (*script.StaticAddress, error) {
if d.AddressParams == nil {
return nil, fmt.Errorf("missing static address parameters")
}
return script.NewStaticAddress(
input.MuSig2Version100RC2, int64(d.AddressParams.Expiry),
d.AddressParams.ClientPubkey, d.AddressParams.ServerPubkey,
)
}
// GetRandomDepositID generates a random deposit ID.
func GetRandomDepositID() (ID, error) {
var id ID

View file

@ -6,14 +6,19 @@ import (
"database/sql"
"encoding/hex"
"errors"
"fmt"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/loop/fsm"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/loopdb/sqlc"
"github.com/lightninglabs/loop/staticaddr/script"
"github.com/lightninglabs/loop/staticaddr/version"
"github.com/lightningnetwork/lnd/clock"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lntypes"
)
@ -49,6 +54,17 @@ func (s *SqlStore) CreateDeposit(ctx context.Context, deposit *Deposit) error {
Amount: int64(deposit.Value),
ConfirmationHeight: deposit.GetConfirmationHeight(),
TimeoutSweepPkScript: deposit.TimeOutSweepPkScript,
StaticAddressID: sql.NullInt32{},
}
if deposit.AddressParams != nil {
if deposit.AddressParams.ID <= 0 {
return fmt.Errorf("static address ID must be set")
}
createArgs.StaticAddressID = sql.NullInt32{
Int32: deposit.AddressParams.ID,
Valid: true,
}
}
updateArgs := sqlc.InsertDepositUpdateParams{
@ -147,7 +163,9 @@ func (s *SqlStore) GetDeposit(ctx context.Context, id ID) (*Deposit, error) {
return err
}
deposit, err = ToDeposit(row, latestUpdate)
deposit, err = toDeposit(
depositRowFromGet(row), latestUpdate,
)
if err != nil {
return err
}
@ -193,7 +211,9 @@ func (s *SqlStore) DepositForOutpoint(ctx context.Context,
return err
}
deposit, err = ToDeposit(row, latestUpdate)
deposit, err = toDeposit(
depositRowFromOutpoint(row), latestUpdate,
)
if err != nil {
return err
}
@ -245,8 +265,105 @@ func (s *SqlStore) AllDeposits(ctx context.Context) ([]*Deposit, error) {
return allDeposits, nil
}
// ToDeposit converts an sql deposit to a deposit.
func ToDeposit(row sqlc.Deposit, lastUpdate sqlc.DepositUpdate) (*Deposit,
// ToDeposit converts an sql deposit row with joined static address metadata to
// a deposit.
func ToDeposit(row sqlc.AllDepositsRow, lastUpdate sqlc.DepositUpdate) (*Deposit,
error) {
return toDeposit(depositRowFromAll(row), lastUpdate)
}
type depositRow struct {
DepositID []byte
TxHash []byte
OutIndex int32
Amount int64
ConfirmationHeight int64
TimeoutSweepPkScript []byte
ExpirySweepTxid []byte
FinalizedWithdrawalTx sql.NullString
SwapHash []byte
StaticAddressID sql.NullInt32
ClientPubkey []byte
ServerPubkey []byte
Expiry sql.NullInt32
ClientKeyFamily sql.NullInt32
ClientKeyIndex sql.NullInt32
Pkscript []byte
ProtocolVersion sql.NullInt32
InitiationHeight sql.NullInt32
}
func depositRowFromAll(row sqlc.AllDepositsRow) depositRow {
return depositRow{
DepositID: row.DepositID,
TxHash: row.TxHash,
OutIndex: row.OutIndex,
Amount: row.Amount,
ConfirmationHeight: row.ConfirmationHeight,
TimeoutSweepPkScript: row.TimeoutSweepPkScript,
ExpirySweepTxid: row.ExpirySweepTxid,
FinalizedWithdrawalTx: row.FinalizedWithdrawalTx,
SwapHash: row.SwapHash,
StaticAddressID: row.StaticAddressID,
ClientPubkey: row.ClientPubkey,
ServerPubkey: row.ServerPubkey,
Expiry: row.Expiry,
ClientKeyFamily: row.ClientKeyFamily,
ClientKeyIndex: row.ClientKeyIndex,
Pkscript: row.Pkscript,
ProtocolVersion: row.ProtocolVersion,
InitiationHeight: row.InitiationHeight,
}
}
func depositRowFromGet(row sqlc.GetDepositRow) depositRow {
return depositRow{
DepositID: row.DepositID,
TxHash: row.TxHash,
OutIndex: row.OutIndex,
Amount: row.Amount,
ConfirmationHeight: row.ConfirmationHeight,
TimeoutSweepPkScript: row.TimeoutSweepPkScript,
ExpirySweepTxid: row.ExpirySweepTxid,
FinalizedWithdrawalTx: row.FinalizedWithdrawalTx,
SwapHash: row.SwapHash,
StaticAddressID: row.StaticAddressID,
ClientPubkey: row.ClientPubkey,
ServerPubkey: row.ServerPubkey,
Expiry: row.Expiry,
ClientKeyFamily: row.ClientKeyFamily,
ClientKeyIndex: row.ClientKeyIndex,
Pkscript: row.Pkscript,
ProtocolVersion: row.ProtocolVersion,
InitiationHeight: row.InitiationHeight,
}
}
func depositRowFromOutpoint(row sqlc.DepositForOutpointRow) depositRow {
return depositRow{
DepositID: row.DepositID,
TxHash: row.TxHash,
OutIndex: row.OutIndex,
Amount: row.Amount,
ConfirmationHeight: row.ConfirmationHeight,
TimeoutSweepPkScript: row.TimeoutSweepPkScript,
ExpirySweepTxid: row.ExpirySweepTxid,
FinalizedWithdrawalTx: row.FinalizedWithdrawalTx,
SwapHash: row.SwapHash,
StaticAddressID: row.StaticAddressID,
ClientPubkey: row.ClientPubkey,
ServerPubkey: row.ServerPubkey,
Expiry: row.Expiry,
ClientKeyFamily: row.ClientKeyFamily,
ClientKeyIndex: row.ClientKeyIndex,
Pkscript: row.Pkscript,
ProtocolVersion: row.ProtocolVersion,
InitiationHeight: row.InitiationHeight,
}
}
func toDeposit(row depositRow, lastUpdate sqlc.DepositUpdate) (*Deposit,
error) {
id := ID{}
@ -296,7 +413,7 @@ func ToDeposit(row sqlc.Deposit, lastUpdate sqlc.DepositUpdate) (*Deposit,
swapHash = &hash
}
return &Deposit{
deposit := &Deposit{
ID: id,
state: fsm.StateType(lastUpdate.UpdateState),
OutPoint: wire.OutPoint{
@ -309,5 +426,57 @@ func ToDeposit(row sqlc.Deposit, lastUpdate sqlc.DepositUpdate) (*Deposit,
ExpirySweepTxid: expirySweepTxid,
SwapHash: swapHash,
FinalizedWithdrawalTx: finalizedWithdrawalTx,
}, nil
}
if row.StaticAddressID.Valid {
clientPubkey, err := btcec.ParsePubKey(row.ClientPubkey)
if err != nil {
return nil, err
}
serverPubkey, err := btcec.ParsePubKey(row.ServerPubkey)
if err != nil {
return nil, err
}
deposit.AddressParams = &script.Parameters{
ID: row.StaticAddressID.Int32,
ClientPubkey: clientPubkey,
ServerPubkey: serverPubkey,
Expiry: uint32(row.Expiry.Int32),
PkScript: row.Pkscript,
KeyLocator: keychain.KeyLocator{
Family: keychain.KeyFamily(
row.ClientKeyFamily.Int32,
),
Index: uint32(row.ClientKeyIndex.Int32),
},
ProtocolVersion: version.AddressProtocolVersion(
row.ProtocolVersion.Int32,
),
InitiationHeight: row.InitiationHeight.Int32,
}
}
return deposit, nil
}
// BatchSetStaticAddressID sets the static address id for all deposits that
// predate the deposit-to-address schema link.
func (s *SqlStore) BatchSetStaticAddressID(ctx context.Context,
staticAddressID int32) error {
if staticAddressID <= 0 {
return fmt.Errorf("static address ID must be set")
}
return s.baseDB.ExecTx(ctx, loopdb.NewSqlWriteOpts(),
func(q *sqlc.Queries) error {
return q.SetAllNullDepositsStaticAddressID(
ctx, sql.NullInt32{
Int32: staticAddressID,
Valid: true,
},
)
})
}

View file

@ -1,6 +1,7 @@
package deposit
import (
"context"
"database/sql"
"testing"
@ -8,10 +9,21 @@ import (
"github.com/jackc/pgx/v5"
"github.com/lightninglabs/loop/fsm"
"github.com/lightninglabs/loop/loopdb/sqlc"
"github.com/lightninglabs/loop/staticaddr/script"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/stretchr/testify/require"
)
func TestCreateDepositRejectsUnpersistedAddress(t *testing.T) {
store := NewSqlStore(nil)
deposit := &Deposit{
AddressParams: &script.Parameters{},
}
err := store.CreateDeposit(context.Background(), deposit)
require.ErrorContains(t, err, "static address ID must be set")
}
func TestToDeposit(t *testing.T) {
depositID, err := GetRandomDepositID()
require.NoError(t, err)
@ -24,13 +36,13 @@ func TestToDeposit(t *testing.T) {
tests := []struct {
name string
row sqlc.Deposit
row sqlc.AllDepositsRow
lastUpdate sqlc.DepositUpdate
expectErr bool
}{
{
name: "fully valid data",
row: sqlc.Deposit{
row: sqlc.AllDepositsRow{
DepositID: depositID[:],
TxHash: txHash[:],
Amount: 100000000,
@ -44,7 +56,7 @@ func TestToDeposit(t *testing.T) {
},
{
name: "fully valid data",
row: sqlc.Deposit{
row: sqlc.AllDepositsRow{
DepositID: depositID[:],
TxHash: txHash[:],
Amount: 100000000,

View file

@ -601,7 +601,7 @@ func toStaticAddressLoopIn(_ context.Context, network *chaincfg.Params,
return nil, err
}
sqlcDeposit := sqlc.Deposit{
sqlcDeposit := sqlc.AllDepositsRow{
DepositID: id[:],
TxHash: d.TxHash,
Amount: d.Amount,

View file

@ -9,6 +9,10 @@ import (
// Parameters holds all the necessary information for the 2-of-2 multisig
// address.
type Parameters struct {
// ID is the database primary key of the static address row. A zero value
// means the parameters have not been persisted yet.
ID int32
// ClientPubkey is the client's pubkey for the static address. It is
// used for the 2-of-2 funding output as well as for the client's
// timeout path.