mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
Merge pull request #988 from hieblmi/normalize-deposit-swap
staticaddr: migrate deposit swap hashes
This commit is contained in:
commit
2f21a488d8
16 changed files with 976 additions and 44 deletions
2
go.mod
2
go.mod
|
|
@ -16,6 +16,7 @@ require (
|
|||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0
|
||||
github.com/jackc/pgconn v1.14.3
|
||||
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438
|
||||
github.com/jackc/pgx/v5 v5.6.0
|
||||
github.com/jessevdk/go-flags v1.4.0
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/lightninglabs/aperture v0.3.13-beta
|
||||
|
|
@ -104,7 +105,6 @@ require (
|
|||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgtype v1.14.0 // indirect
|
||||
github.com/jackc/pgx/v4 v4.18.2 // indirect
|
||||
github.com/jackc/pgx/v5 v5.6.0 // indirect
|
||||
github.com/jackc/puddle v1.3.0 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||
github.com/jackpal/gateway v1.0.5 // indirect
|
||||
|
|
|
|||
|
|
@ -626,6 +626,16 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
|
|||
clock.NewDefaultClock(), d.lnd.ChainParams,
|
||||
)
|
||||
|
||||
// Run the deposit swap hash migration.
|
||||
err = loopin.MigrateDepositSwapHash(
|
||||
d.mainCtx, swapDb, depositStore, staticAddressLoopInStore,
|
||||
)
|
||||
if err != nil {
|
||||
errorf("Deposit swap hash migration failed: %v", err)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
staticLoopInManager = loopin.NewManager(&loopin.Config{
|
||||
Server: staticAddressClient,
|
||||
QuoteGetter: swapClient.Server,
|
||||
|
|
|
|||
|
|
@ -45,4 +45,4 @@ CREATE TABLE IF NOT EXISTS deposit_updates (
|
|||
|
||||
-- update_timestamp is the timestamp of the update.
|
||||
update_timestamp TIMESTAMP NOT NULL
|
||||
);
|
||||
);
|
||||
1
loopdb/sqlc/migrations/000017_deposit_swaphash.down.sql
Normal file
1
loopdb/sqlc/migrations/000017_deposit_swaphash.down.sql
Normal file
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE deposits DROP COLUMN swap_hash;
|
||||
1
loopdb/sqlc/migrations/000017_deposit_swaphash.up.sql
Normal file
1
loopdb/sqlc/migrations/000017_deposit_swaphash.up.sql
Normal file
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE deposits ADD swap_hash BLOB;
|
||||
|
|
@ -19,6 +19,7 @@ type Deposit struct {
|
|||
TimeoutSweepPkScript []byte
|
||||
ExpirySweepTxid []byte
|
||||
FinalizedWithdrawalTx sql.NullString
|
||||
SwapHash []byte
|
||||
}
|
||||
|
||||
type DepositUpdate struct {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ type Querier interface {
|
|||
CreateWithdrawal(ctx context.Context, arg CreateWithdrawalParams) error
|
||||
CreateWithdrawalDeposit(ctx context.Context, arg CreateWithdrawalDepositParams) error
|
||||
DepositForOutpoint(ctx context.Context, arg DepositForOutpointParams) (Deposit, 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)
|
||||
|
|
@ -62,7 +64,9 @@ type Querier interface {
|
|||
InsertSwap(ctx context.Context, arg InsertSwapParams) error
|
||||
InsertSwapUpdate(ctx context.Context, arg InsertSwapUpdateParams) error
|
||||
IsStored(ctx context.Context, swapHash []byte) (bool, error)
|
||||
MapDepositToSwap(ctx context.Context, arg MapDepositToSwapParams) error
|
||||
OverrideSwapCosts(ctx context.Context, arg OverrideSwapCostsParams) error
|
||||
SwapHashForDepositID(ctx context.Context, depositID []byte) ([]byte, error)
|
||||
UpdateBatch(ctx context.Context, arg UpdateBatchParams) error
|
||||
UpdateDeposit(ctx context.Context, arg UpdateDepositParams) error
|
||||
UpdateInstantOut(ctx context.Context, arg UpdateInstantOutParams) error
|
||||
|
|
|
|||
|
|
@ -93,3 +93,51 @@ SELECT EXISTS (
|
|||
FROM static_address_swaps
|
||||
WHERE swap_hash = $1
|
||||
);
|
||||
|
||||
-- name: MapDepositToSwap :exec
|
||||
UPDATE
|
||||
deposits
|
||||
SET
|
||||
swap_hash = $2
|
||||
WHERE
|
||||
deposit_id = $1;
|
||||
|
||||
-- name: SwapHashForDepositID :one
|
||||
SELECT
|
||||
swap_hash
|
||||
FROM
|
||||
deposits
|
||||
WHERE
|
||||
deposit_id = $1;
|
||||
|
||||
-- name: DepositIDsForSwapHash :many
|
||||
SELECT
|
||||
deposit_id
|
||||
FROM
|
||||
deposits
|
||||
WHERE
|
||||
swap_hash = $1;
|
||||
|
||||
-- name: DepositsForSwapHash :many
|
||||
SELECT
|
||||
d.*,
|
||||
u.update_state,
|
||||
u.update_timestamp
|
||||
FROM
|
||||
deposits d
|
||||
LEFT JOIN
|
||||
deposit_updates u ON u.id = (
|
||||
SELECT id
|
||||
FROM deposit_updates
|
||||
WHERE deposit_id = d.deposit_id
|
||||
ORDER BY update_timestamp DESC
|
||||
LIMIT 1
|
||||
)
|
||||
WHERE
|
||||
d.swap_hash = $1;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ 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
|
||||
id, deposit_id, tx_hash, out_index, amount, confirmation_height, timeout_sweep_pk_script, expiry_sweep_txid, finalized_withdrawal_tx, swap_hash
|
||||
FROM
|
||||
deposits
|
||||
ORDER BY
|
||||
|
|
@ -39,6 +39,7 @@ func (q *Queries) AllDeposits(ctx context.Context) ([]Deposit, error) {
|
|||
&i.TimeoutSweepPkScript,
|
||||
&i.ExpirySweepTxid,
|
||||
&i.FinalizedWithdrawalTx,
|
||||
&i.SwapHash,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -102,7 +103,7 @@ func (q *Queries) CreateDeposit(ctx context.Context, arg CreateDepositParams) er
|
|||
|
||||
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
|
||||
id, deposit_id, tx_hash, out_index, amount, confirmation_height, timeout_sweep_pk_script, expiry_sweep_txid, finalized_withdrawal_tx, swap_hash
|
||||
FROM
|
||||
deposits
|
||||
WHERE
|
||||
|
|
@ -129,13 +130,14 @@ func (q *Queries) DepositForOutpoint(ctx context.Context, arg DepositForOutpoint
|
|||
&i.TimeoutSweepPkScript,
|
||||
&i.ExpirySweepTxid,
|
||||
&i.FinalizedWithdrawalTx,
|
||||
&i.SwapHash,
|
||||
)
|
||||
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
|
||||
id, deposit_id, tx_hash, out_index, amount, confirmation_height, timeout_sweep_pk_script, expiry_sweep_txid, finalized_withdrawal_tx, swap_hash
|
||||
FROM
|
||||
deposits
|
||||
WHERE
|
||||
|
|
@ -155,6 +157,7 @@ func (q *Queries) GetDeposit(ctx context.Context, depositID []byte) (Deposit, er
|
|||
&i.TimeoutSweepPkScript,
|
||||
&i.ExpirySweepTxid,
|
||||
&i.FinalizedWithdrawalTx,
|
||||
&i.SwapHash,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,108 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
const depositIDsForSwapHash = `-- name: DepositIDsForSwapHash :many
|
||||
SELECT
|
||||
deposit_id
|
||||
FROM
|
||||
deposits
|
||||
WHERE
|
||||
swap_hash = $1
|
||||
`
|
||||
|
||||
func (q *Queries) DepositIDsForSwapHash(ctx context.Context, swapHash []byte) ([][]byte, error) {
|
||||
rows, err := q.db.QueryContext(ctx, depositIDsForSwapHash, swapHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items [][]byte
|
||||
for rows.Next() {
|
||||
var deposit_id []byte
|
||||
if err := rows.Scan(&deposit_id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, deposit_id)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
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,
|
||||
u.update_state,
|
||||
u.update_timestamp
|
||||
FROM
|
||||
deposits d
|
||||
LEFT JOIN
|
||||
deposit_updates u ON u.id = (
|
||||
SELECT id
|
||||
FROM deposit_updates
|
||||
WHERE deposit_id = d.deposit_id
|
||||
ORDER BY update_timestamp DESC
|
||||
LIMIT 1
|
||||
)
|
||||
WHERE
|
||||
d.swap_hash = $1
|
||||
`
|
||||
|
||||
type DepositsForSwapHashRow struct {
|
||||
ID int32
|
||||
DepositID []byte
|
||||
TxHash []byte
|
||||
OutIndex int32
|
||||
Amount int64
|
||||
ConfirmationHeight int64
|
||||
TimeoutSweepPkScript []byte
|
||||
ExpirySweepTxid []byte
|
||||
FinalizedWithdrawalTx sql.NullString
|
||||
SwapHash []byte
|
||||
UpdateState sql.NullString
|
||||
UpdateTimestamp sql.NullTime
|
||||
}
|
||||
|
||||
func (q *Queries) DepositsForSwapHash(ctx context.Context, swapHash []byte) ([]DepositsForSwapHashRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, depositsForSwapHash, swapHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []DepositsForSwapHashRow
|
||||
for rows.Next() {
|
||||
var i DepositsForSwapHashRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.DepositID,
|
||||
&i.TxHash,
|
||||
&i.OutIndex,
|
||||
&i.Amount,
|
||||
&i.ConfirmationHeight,
|
||||
&i.TimeoutSweepPkScript,
|
||||
&i.ExpirySweepTxid,
|
||||
&i.FinalizedWithdrawalTx,
|
||||
&i.SwapHash,
|
||||
&i.UpdateState,
|
||||
&i.UpdateTimestamp,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getLoopInSwapUpdates = `-- name: GetLoopInSwapUpdates :many
|
||||
SELECT
|
||||
static_address_swap_updates.id, static_address_swap_updates.swap_hash, static_address_swap_updates.update_state, static_address_swap_updates.update_timestamp
|
||||
|
|
@ -328,6 +430,41 @@ func (q *Queries) IsStored(ctx context.Context, swapHash []byte) (bool, error) {
|
|||
return exists, err
|
||||
}
|
||||
|
||||
const mapDepositToSwap = `-- name: MapDepositToSwap :exec
|
||||
UPDATE
|
||||
deposits
|
||||
SET
|
||||
swap_hash = $2
|
||||
WHERE
|
||||
deposit_id = $1
|
||||
`
|
||||
|
||||
type MapDepositToSwapParams struct {
|
||||
DepositID []byte
|
||||
SwapHash []byte
|
||||
}
|
||||
|
||||
func (q *Queries) MapDepositToSwap(ctx context.Context, arg MapDepositToSwapParams) error {
|
||||
_, err := q.db.ExecContext(ctx, mapDepositToSwap, arg.DepositID, arg.SwapHash)
|
||||
return err
|
||||
}
|
||||
|
||||
const swapHashForDepositID = `-- name: SwapHashForDepositID :one
|
||||
SELECT
|
||||
swap_hash
|
||||
FROM
|
||||
deposits
|
||||
WHERE
|
||||
deposit_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) SwapHashForDepositID(ctx context.Context, depositID []byte) ([]byte, error) {
|
||||
row := q.db.QueryRowContext(ctx, swapHashForDepositID, depositID)
|
||||
var swap_hash []byte
|
||||
err := row.Scan(&swap_hash)
|
||||
return swap_hash, err
|
||||
}
|
||||
|
||||
const updateStaticAddressLoopIn = `-- name: UpdateStaticAddressLoopIn :exec
|
||||
UPDATE static_address_swaps
|
||||
SET
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ func (s *SqlStore) GetDeposit(ctx context.Context, id ID) (*Deposit, error) {
|
|||
return err
|
||||
}
|
||||
|
||||
deposit, err = s.toDeposit(row, latestUpdate)
|
||||
deposit, err = ToDeposit(row, latestUpdate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -177,7 +177,7 @@ func (s *SqlStore) DepositForOutpoint(ctx context.Context,
|
|||
return err
|
||||
}
|
||||
|
||||
deposit, err = s.toDeposit(row, latestUpdate)
|
||||
deposit, err = ToDeposit(row, latestUpdate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -212,7 +212,7 @@ func (s *SqlStore) AllDeposits(ctx context.Context) ([]*Deposit, error) {
|
|||
return err
|
||||
}
|
||||
|
||||
d, err := s.toDeposit(deposit, latestUpdate)
|
||||
d, err := ToDeposit(deposit, latestUpdate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -229,9 +229,9 @@ func (s *SqlStore) AllDeposits(ctx context.Context) ([]*Deposit, error) {
|
|||
return allDeposits, nil
|
||||
}
|
||||
|
||||
// toDeposit converts an sql deposit to a deposit.
|
||||
func (s *SqlStore) toDeposit(row sqlc.Deposit,
|
||||
lastUpdate sqlc.DepositUpdate) (*Deposit, error) {
|
||||
// ToDeposit converts an sql deposit to a deposit.
|
||||
func ToDeposit(row sqlc.Deposit, lastUpdate sqlc.DepositUpdate) (*Deposit,
|
||||
error) {
|
||||
|
||||
id := ID{}
|
||||
err := id.FromByteSlice(row.DepositID)
|
||||
|
|
|
|||
88
staticaddr/loopin/deposit_swaphash_migration.go
Normal file
88
staticaddr/loopin/deposit_swaphash_migration.go
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
package loopin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/lightninglabs/loop/loopdb"
|
||||
"github.com/lightninglabs/loop/staticaddr/deposit"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
)
|
||||
|
||||
const (
|
||||
// depositSwapHashMigrationID is the identifier for the deposit swap
|
||||
// hash migration.
|
||||
depositSwapHashMigrationID = "deposit_swap_hash"
|
||||
)
|
||||
|
||||
// MigrateDepositSwapHash will retrieve the comma separated deposit list of
|
||||
// past and pending swaps and map them to the swap hash in the deposits table.
|
||||
func MigrateDepositSwapHash(ctx context.Context, db loopdb.SwapStore,
|
||||
depositStore *deposit.SqlStore, swapStore *SqlStore) error {
|
||||
|
||||
migrationDone, err := db.HasMigration(
|
||||
ctx, depositSwapHashMigrationID,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to check migration status: %w", err)
|
||||
}
|
||||
if migrationDone {
|
||||
log.Infof("Deposit swap hash migration already done, " +
|
||||
"skipping")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Infof("Starting deposit swap hash migration")
|
||||
startTs := time.Now()
|
||||
defer func() {
|
||||
log.Infof("Finished deposit swap hash migration in %v",
|
||||
time.Since(startTs))
|
||||
}()
|
||||
|
||||
// First we'll fetch all past loop in swaps from the database.
|
||||
swaps, err := swapStore.GetStaticAddressLoopInSwapsByStates(
|
||||
ctx, AllStates,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Now we'll map each deposit of a swap to its respective swap hash.
|
||||
depositsToSwapHashes := make(map[deposit.ID]lntypes.Hash)
|
||||
for _, swap := range swaps {
|
||||
for _, outpoint := range swap.DepositOutpoints {
|
||||
deposit, err := depositStore.DepositForOutpoint(
|
||||
ctx, outpoint,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to fetch deposit "+
|
||||
"for outpoint %s: %w", outpoint, err)
|
||||
}
|
||||
if deposit == nil {
|
||||
return fmt.Errorf("deposit for outpoint %s "+
|
||||
"not found", outpoint)
|
||||
}
|
||||
|
||||
if _, ok := depositsToSwapHashes[deposit.ID]; !ok {
|
||||
depositsToSwapHashes[deposit.ID] = swap.SwapHash
|
||||
} else {
|
||||
log.Warnf("Duplicate deposit ID %s found for "+
|
||||
"outpoint %s, skipping",
|
||||
deposit.ID, outpoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("Batch-mapping %d deposits to swap hashes",
|
||||
len(depositsToSwapHashes))
|
||||
|
||||
err = swapStore.BatchMapDepositsToSwapHashes(ctx, depositsToSwapHashes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Finally mark the migration as done.
|
||||
return db.SetMigration(ctx, depositSwapHashMigrationID)
|
||||
}
|
||||
195
staticaddr/loopin/deposit_swaphash_migration_test.go
Normal file
195
staticaddr/loopin/deposit_swaphash_migration_test.go
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
package loopin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightninglabs/loop/loopdb"
|
||||
"github.com/lightninglabs/loop/loopdb/sqlc"
|
||||
"github.com/lightninglabs/loop/staticaddr/deposit"
|
||||
"github.com/lightninglabs/loop/test"
|
||||
"github.com/lightningnetwork/lnd/clock"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
P2wkhAddr = "bcrt1qq68r6ff4k4pjx39efs44gcyccf7unqnu5qtjjz"
|
||||
)
|
||||
|
||||
// TestDepositSwapHashMigration tests deposit to swap hash migration.
|
||||
func TestDepositSwapHashMigration(t *testing.T) {
|
||||
// Set up test context objects.
|
||||
ctxb := context.Background()
|
||||
testDb := loopdb.NewTestDB(t)
|
||||
testClock := clock.NewTestClock(time.Now())
|
||||
defer testDb.Close()
|
||||
|
||||
db := loopdb.NewStoreMock(t)
|
||||
depositStore := deposit.NewSqlStore(testDb.BaseDB)
|
||||
swapStore := NewSqlStore(
|
||||
loopdb.NewTypedStore[Querier](testDb), testClock,
|
||||
&chaincfg.RegressionNetParams,
|
||||
)
|
||||
|
||||
newID := func() deposit.ID {
|
||||
did, err := deposit.GetRandomDepositID()
|
||||
require.NoError(t, err)
|
||||
|
||||
return did
|
||||
}
|
||||
|
||||
d1, d2 := &deposit.Deposit{
|
||||
ID: newID(),
|
||||
OutPoint: wire.OutPoint{
|
||||
Hash: chainhash.Hash{0x1a, 0x2b, 0x3c, 0x4d},
|
||||
Index: 0,
|
||||
},
|
||||
Value: btcutil.Amount(100_000),
|
||||
TimeOutSweepPkScript: []byte{
|
||||
0x00, 0x14, 0x1a, 0x2b, 0x3c, 0x41,
|
||||
},
|
||||
},
|
||||
&deposit.Deposit{
|
||||
ID: newID(),
|
||||
OutPoint: wire.OutPoint{
|
||||
Hash: chainhash.Hash{0x2a, 0x2b, 0x3c, 0x4e},
|
||||
Index: 1,
|
||||
},
|
||||
Value: btcutil.Amount(200_000),
|
||||
TimeOutSweepPkScript: []byte{
|
||||
0x00, 0x14, 0x1a, 0x2b, 0x3c, 0x4d,
|
||||
},
|
||||
}
|
||||
|
||||
err := depositStore.CreateDeposit(ctxb, d1)
|
||||
require.NoError(t, err)
|
||||
err = depositStore.CreateDeposit(ctxb, d2)
|
||||
require.NoError(t, err)
|
||||
|
||||
outpoints := []string{
|
||||
d1.OutPoint.String(),
|
||||
d2.OutPoint.String(),
|
||||
}
|
||||
_, clientPubKey := test.CreateKey(1)
|
||||
_, serverPubKey := test.CreateKey(2)
|
||||
addr, err := btcutil.DecodeAddress(P2wkhAddr, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
swapHash := lntypes.Hash{0x1, 0x2, 0x3, 0x4}
|
||||
loopIn := StaticAddressLoopIn{
|
||||
SwapHash: swapHash,
|
||||
DepositOutpoints: outpoints,
|
||||
ClientPubkey: clientPubKey,
|
||||
ServerPubkey: serverPubKey,
|
||||
HtlcTimeoutSweepAddress: addr,
|
||||
}
|
||||
loopIn.SetState(Succeeded)
|
||||
|
||||
// Insert the swap without the deposit mapping.
|
||||
err = swapStore.baseDB.ExecTx(ctxb, loopdb.NewSqlWriteOpts(),
|
||||
func(q Querier) error {
|
||||
swapArgs := sqlc.InsertSwapParams{
|
||||
SwapHash: loopIn.SwapHash[:],
|
||||
Preimage: loopIn.SwapPreimage[:],
|
||||
InitiationTime: loopIn.InitiationTime,
|
||||
AmountRequested: int64(loopIn.TotalDepositAmount()),
|
||||
CltvExpiry: loopIn.HtlcCltvExpiry,
|
||||
MaxSwapFee: int64(loopIn.MaxSwapFee),
|
||||
InitiationHeight: int32(loopIn.InitiationHeight),
|
||||
ProtocolVersion: int32(loopIn.ProtocolVersion),
|
||||
Label: loopIn.Label,
|
||||
}
|
||||
|
||||
htlcKeyArgs := sqlc.InsertHtlcKeysParams{
|
||||
SwapHash: loopIn.SwapHash[:],
|
||||
SenderScriptPubkey: loopIn.ClientPubkey.SerializeCompressed(),
|
||||
ReceiverScriptPubkey: loopIn.ServerPubkey.SerializeCompressed(),
|
||||
ClientKeyFamily: int32(loopIn.HtlcKeyLocator.Family),
|
||||
ClientKeyIndex: int32(loopIn.HtlcKeyLocator.Index),
|
||||
}
|
||||
|
||||
// Sanity check, if any of the outpoints contain the outpoint separator.
|
||||
// If so, we reject the loop-in to prevent potential issues with
|
||||
// parsing.
|
||||
for _, outpoint := range loopIn.DepositOutpoints {
|
||||
if strings.Contains(outpoint, outpointSeparator) {
|
||||
return ErrInvalidOutpoint
|
||||
}
|
||||
}
|
||||
|
||||
joinedOutpoints := strings.Join(
|
||||
loopIn.DepositOutpoints, outpointSeparator,
|
||||
)
|
||||
staticAddressLoopInParams := sqlc.InsertStaticAddressLoopInParams{
|
||||
SwapHash: loopIn.SwapHash[:],
|
||||
SwapInvoice: loopIn.SwapInvoice,
|
||||
LastHop: loopIn.LastHop,
|
||||
QuotedSwapFeeSatoshis: int64(loopIn.QuotedSwapFee),
|
||||
HtlcTimeoutSweepAddress: loopIn.HtlcTimeoutSweepAddress.String(),
|
||||
HtlcTxFeeRateSatKw: int64(loopIn.HtlcTxFeeRate),
|
||||
DepositOutpoints: joinedOutpoints,
|
||||
PaymentTimeoutSeconds: int32(loopIn.PaymentTimeoutSeconds),
|
||||
}
|
||||
|
||||
updateArgs := sqlc.InsertStaticAddressMetaUpdateParams{
|
||||
SwapHash: loopIn.SwapHash[:],
|
||||
UpdateTimestamp: testClock.Now(),
|
||||
UpdateState: string(loopIn.GetState()),
|
||||
}
|
||||
err := q.InsertSwap(ctxb, swapArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = q.InsertHtlcKeys(ctxb, htlcKeyArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = q.InsertStaticAddressLoopIn(
|
||||
ctxb, staticAddressLoopInParams,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return q.InsertStaticAddressMetaUpdate(ctxb, updateArgs)
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
depositIDs, err := swapStore.DepositIDsForSwapHash(ctxb, swapHash)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, depositIDs, 0)
|
||||
|
||||
swapHashes, err := swapStore.SwapHashesForDepositIDs(
|
||||
ctxb, []deposit.ID{d1.ID, d2.ID},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, swapHashes, 0)
|
||||
|
||||
err = MigrateDepositSwapHash(ctxb, db, depositStore, swapStore)
|
||||
require.NoError(t, err)
|
||||
|
||||
depositIDs, err = swapStore.DepositIDsForSwapHash(ctxb, swapHash)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, depositIDs, 2)
|
||||
require.Contains(t, depositIDs, d1.ID)
|
||||
require.Contains(t, depositIDs, d2.ID)
|
||||
|
||||
swapHashes, err = swapStore.SwapHashesForDepositIDs(
|
||||
ctxb, []deposit.ID{d1.ID, d2.ID},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, swapHashes, 1)
|
||||
require.Len(t, swapHashes[swapHash], 2)
|
||||
require.Contains(t, swapHashes[swapHash], d1.ID)
|
||||
require.Contains(t, swapHashes[swapHash], d2.ID)
|
||||
}
|
||||
|
|
@ -130,15 +130,16 @@ var (
|
|||
|
||||
var PendingStates = []fsm.StateType{
|
||||
InitHtlcTx, SignHtlcTx, MonitorInvoiceAndHtlcTx, PaymentReceived,
|
||||
SweepHtlcTimeout, MonitorHtlcTimeoutSweep,
|
||||
UnlockDeposits,
|
||||
SweepHtlcTimeout, MonitorHtlcTimeoutSweep, UnlockDeposits,
|
||||
}
|
||||
|
||||
var FinalStates = []fsm.StateType{
|
||||
HtlcTimeoutSwept, Succeeded, SucceededTransitioningFailed, Failed,
|
||||
}
|
||||
|
||||
var AllStates = append(PendingStates, FinalStates...)
|
||||
var AllStates = append(
|
||||
append([]fsm.StateType{}, PendingStates...), FinalStates...,
|
||||
)
|
||||
|
||||
// Events.
|
||||
var (
|
||||
|
|
@ -149,7 +150,6 @@ var (
|
|||
OnHtlcTimeoutSweepPublished = fsm.EventType("OnHtlcTimeoutSweepPublished")
|
||||
OnHtlcTimeoutSwept = fsm.EventType("OnHtlcTimeoutSwept")
|
||||
OnPaymentReceived = fsm.EventType("OnPaymentReceived")
|
||||
OnPaymentDeadlineExceeded = fsm.EventType("OnPaymentDeadlineExceeded")
|
||||
OnSwapTimedOut = fsm.EventType("OnSwapTimedOut")
|
||||
OnSucceeded = fsm.EventType("OnSucceeded")
|
||||
OnRecover = fsm.EventType("OnRecover")
|
||||
|
|
|
|||
|
|
@ -10,9 +10,11 @@ import (
|
|||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/lightninglabs/loop/fsm"
|
||||
"github.com/lightninglabs/loop/loopdb"
|
||||
"github.com/lightninglabs/loop/loopdb/sqlc"
|
||||
"github.com/lightninglabs/loop/staticaddr/deposit"
|
||||
"github.com/lightninglabs/loop/staticaddr/version"
|
||||
"github.com/lightningnetwork/lnd/clock"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
|
|
@ -68,6 +70,24 @@ type Querier interface {
|
|||
// IsStored returns true if a swap with the given hash is stored in the
|
||||
// database, false otherwise.
|
||||
IsStored(ctx context.Context, swapHash []byte) (bool, error)
|
||||
|
||||
// MapDepositToSwap maps a deposit to a swap in the database.
|
||||
MapDepositToSwap(ctx context.Context,
|
||||
arg sqlc.MapDepositToSwapParams) error
|
||||
|
||||
// SwapHashForDepositID retrieves the swap hash for the given deposit
|
||||
// ID.
|
||||
SwapHashForDepositID(ctx context.Context,
|
||||
depositID []byte) ([]byte, error)
|
||||
|
||||
// DepositIDsForSwapHash retrieves all deposit IDs for a given swap
|
||||
// hash.
|
||||
DepositIDsForSwapHash(ctx context.Context,
|
||||
swapHash []byte) ([][]byte, error)
|
||||
|
||||
// DepositsForSwapHash retrieves all deposits for a given swap hash.
|
||||
DepositsForSwapHash(ctx context.Context,
|
||||
swapHash []byte) ([]sqlc.DepositsForSwapHashRow, error)
|
||||
}
|
||||
|
||||
// BaseDB is the interface that contains all the queries generated by sqlc for
|
||||
|
|
@ -106,10 +126,15 @@ func (s *SqlStore) GetLoopInByHash(ctx context.Context,
|
|||
|
||||
var (
|
||||
err error
|
||||
row sqlc.GetStaticAddressLoopInSwapRow
|
||||
swap sqlc.GetStaticAddressLoopInSwapRow
|
||||
updates []sqlc.StaticAddressSwapUpdate
|
||||
)
|
||||
row, err = s.baseDB.GetStaticAddressLoopInSwap(ctx, swapHash[:])
|
||||
swap, err = s.baseDB.GetStaticAddressLoopInSwap(ctx, swapHash[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deposits, err := s.baseDB.DepositsForSwapHash(ctx, swapHash[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -119,9 +144,7 @@ func (s *SqlStore) GetLoopInByHash(ctx context.Context,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return toStaticAddressLoopIn(
|
||||
ctx, s.network, row, updates,
|
||||
)
|
||||
return toStaticAddressLoopIn(ctx, s.network, swap, deposits, updates)
|
||||
}
|
||||
|
||||
// GetStaticAddressLoopInSwapsByStates returns all static address loop-ins from
|
||||
|
|
@ -149,6 +172,11 @@ func (s *SqlStore) GetStaticAddressLoopInSwapsByStates(ctx context.Context,
|
|||
|
||||
loopIns := make([]*StaticAddressLoopIn, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
deposits, err := s.baseDB.DepositsForSwapHash(ctx, row.SwapHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updates, err = s.baseDB.GetLoopInSwapUpdates(
|
||||
ctx, row.SwapHash,
|
||||
)
|
||||
|
|
@ -158,7 +186,7 @@ func (s *SqlStore) GetStaticAddressLoopInSwapsByStates(ctx context.Context,
|
|||
|
||||
loopIn, err = toStaticAddressLoopIn(
|
||||
ctx, s.network, sqlc.GetStaticAddressLoopInSwapRow(row),
|
||||
updates,
|
||||
deposits, updates,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -190,6 +218,14 @@ func toStrings(states []fsm.StateType) []string {
|
|||
func (s *SqlStore) CreateLoopIn(ctx context.Context,
|
||||
loopIn *StaticAddressLoopIn) error {
|
||||
|
||||
if loopIn == nil {
|
||||
return errors.New("loop-in cannot be nil")
|
||||
}
|
||||
|
||||
if len(loopIn.Deposits) == 0 {
|
||||
return errors.New("loop-in must have at least one deposit")
|
||||
}
|
||||
|
||||
swapArgs := sqlc.InsertSwapParams{
|
||||
SwapHash: loopIn.SwapHash[:],
|
||||
Preimage: loopIn.SwapPreimage[:],
|
||||
|
|
@ -258,8 +294,24 @@ func (s *SqlStore) CreateLoopIn(ctx context.Context,
|
|||
return err
|
||||
}
|
||||
|
||||
// Map each deposit to the swap hash in the
|
||||
// deposit_to_swap table. This allows us to track which
|
||||
// deposits are used for which swaps.
|
||||
for _, d := range loopIn.Deposits {
|
||||
err = q.MapDepositToSwap(
|
||||
ctx, sqlc.MapDepositToSwapParams{
|
||||
DepositID: d.ID[:],
|
||||
SwapHash: loopIn.SwapHash[:],
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return q.InsertStaticAddressMetaUpdate(ctx, updateArgs)
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// UpdateLoopIn updates the loop-in in the database.
|
||||
|
|
@ -306,35 +358,119 @@ func (s *SqlStore) IsStored(ctx context.Context, swapHash lntypes.Hash) (bool,
|
|||
return s.baseDB.IsStored(ctx, swapHash[:])
|
||||
}
|
||||
|
||||
// BatchMapDepositsToSwapHashes maps multiple deposits to their respective swap
|
||||
// hashes in a single transaction.
|
||||
func (s *SqlStore) BatchMapDepositsToSwapHashes(ctx context.Context,
|
||||
depositsToHashes map[deposit.ID]lntypes.Hash) error {
|
||||
|
||||
return s.baseDB.ExecTx(ctx, loopdb.NewSqlWriteOpts(),
|
||||
func(q Querier) error {
|
||||
for deposit, swapHash := range depositsToHashes {
|
||||
err := q.MapDepositToSwap(
|
||||
ctx, sqlc.MapDepositToSwapParams{
|
||||
DepositID: deposit[:],
|
||||
SwapHash: swapHash[:],
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// SwapHashesForDepositIDs retrieves the swap hashes for the given deposit IDs.
|
||||
func (s *SqlStore) SwapHashesForDepositIDs(ctx context.Context,
|
||||
depositIDs []deposit.ID) (map[lntypes.Hash][]deposit.ID, error) {
|
||||
|
||||
swapHashes := make(map[lntypes.Hash][]deposit.ID)
|
||||
for _, id := range depositIDs {
|
||||
swapHash, err := s.baseDB.SwapHashForDepositID(ctx, id[:])
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) ||
|
||||
errors.Is(err, pgx.ErrNoRows) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if swapHash == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if len(swapHash) != lntypes.HashSize {
|
||||
return nil, errors.New("invalid swap hash length")
|
||||
}
|
||||
|
||||
swapHashParsed, err := lntypes.MakeHash(swapHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Place the deposit ID in the map under the
|
||||
// corresponding swap hash.
|
||||
swapHashes[swapHashParsed] = append(
|
||||
swapHashes[swapHashParsed], id,
|
||||
)
|
||||
}
|
||||
|
||||
return swapHashes, nil
|
||||
}
|
||||
|
||||
// DepositIDsForSwapHash retrieves all deposit IDs for a given swap hash.
|
||||
func (s *SqlStore) DepositIDsForSwapHash(ctx context.Context,
|
||||
swapHash lntypes.Hash) ([]deposit.ID, error) {
|
||||
|
||||
byteIDs, err := s.baseDB.DepositIDsForSwapHash(ctx, swapHash[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
depositIDs := make([]deposit.ID, len(byteIDs))
|
||||
for i, id := range byteIDs {
|
||||
if len(id) != deposit.IdLength {
|
||||
return nil, errors.New("invalid deposit ID length")
|
||||
}
|
||||
copy(depositIDs[i][:], id)
|
||||
}
|
||||
|
||||
return depositIDs, nil
|
||||
}
|
||||
|
||||
// toStaticAddressLoopIn converts sql rows to an instant out struct.
|
||||
func toStaticAddressLoopIn(_ context.Context, network *chaincfg.Params,
|
||||
row sqlc.GetStaticAddressLoopInSwapRow,
|
||||
swap sqlc.GetStaticAddressLoopInSwapRow,
|
||||
deposits []sqlc.DepositsForSwapHashRow,
|
||||
updates []sqlc.StaticAddressSwapUpdate) (*StaticAddressLoopIn, error) {
|
||||
|
||||
swapHash, err := lntypes.MakeHash(row.SwapHash)
|
||||
swapHash, err := lntypes.MakeHash(swap.SwapHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
swapPreImage, err := lntypes.MakePreimage(row.Preimage)
|
||||
swapPreImage, err := lntypes.MakePreimage(swap.Preimage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clientKey, err := btcec.ParsePubKey(row.SenderScriptPubkey)
|
||||
clientKey, err := btcec.ParsePubKey(swap.SenderScriptPubkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serverKey, err := btcec.ParsePubKey(row.ReceiverScriptPubkey)
|
||||
serverKey, err := btcec.ParsePubKey(swap.ReceiverScriptPubkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var htlcTimeoutSweepTxHash *chainhash.Hash
|
||||
if row.HtlcTimeoutSweepTxID.Valid {
|
||||
if swap.HtlcTimeoutSweepTxID.Valid {
|
||||
htlcTimeoutSweepTxHash, err = chainhash.NewHashFromStr(
|
||||
row.HtlcTimeoutSweepTxID.String,
|
||||
swap.HtlcTimeoutSweepTxID.String,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -342,10 +478,10 @@ func toStaticAddressLoopIn(_ context.Context, network *chaincfg.Params,
|
|||
}
|
||||
|
||||
depositOutpoints := strings.Split(
|
||||
row.DepositOutpoints, outpointSeparator,
|
||||
swap.DepositOutpoints, outpointSeparator,
|
||||
)
|
||||
|
||||
timeoutAddressString := row.HtlcTimeoutSweepAddress
|
||||
timeoutAddressString := swap.HtlcTimeoutSweepAddress
|
||||
var timeoutAddress btcutil.Address
|
||||
if timeoutAddressString != "" {
|
||||
timeoutAddress, err = btcutil.DecodeAddress(
|
||||
|
|
@ -356,33 +492,68 @@ func toStaticAddressLoopIn(_ context.Context, network *chaincfg.Params,
|
|||
}
|
||||
}
|
||||
|
||||
depositList := make([]*deposit.Deposit, 0, len(deposits))
|
||||
for _, d := range deposits {
|
||||
id := deposit.ID{}
|
||||
err = id.FromByteSlice(d.DepositID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sqlcDeposit := sqlc.Deposit{
|
||||
DepositID: id[:],
|
||||
TxHash: d.TxHash,
|
||||
Amount: d.Amount,
|
||||
OutIndex: d.OutIndex,
|
||||
ConfirmationHeight: d.ConfirmationHeight,
|
||||
TimeoutSweepPkScript: d.TimeoutSweepPkScript,
|
||||
ExpirySweepTxid: d.ExpirySweepTxid,
|
||||
FinalizedWithdrawalTx: d.FinalizedWithdrawalTx,
|
||||
}
|
||||
|
||||
sqlcDepositUpdate := sqlc.DepositUpdate{
|
||||
DepositID: id[:],
|
||||
UpdateState: d.UpdateState.String,
|
||||
UpdateTimestamp: d.UpdateTimestamp.Time,
|
||||
}
|
||||
deposit, err := deposit.ToDeposit(
|
||||
sqlcDeposit, sqlcDepositUpdate,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
depositList = append(depositList, deposit)
|
||||
}
|
||||
|
||||
loopIn := &StaticAddressLoopIn{
|
||||
SwapHash: swapHash,
|
||||
SwapPreimage: swapPreImage,
|
||||
HtlcCltvExpiry: row.CltvExpiry,
|
||||
MaxSwapFee: btcutil.Amount(row.MaxSwapFee),
|
||||
InitiationHeight: uint32(row.InitiationHeight),
|
||||
InitiationTime: row.InitiationTime,
|
||||
HtlcCltvExpiry: swap.CltvExpiry,
|
||||
MaxSwapFee: btcutil.Amount(swap.MaxSwapFee),
|
||||
InitiationHeight: uint32(swap.InitiationHeight),
|
||||
InitiationTime: swap.InitiationTime,
|
||||
ProtocolVersion: version.AddressProtocolVersion(
|
||||
row.ProtocolVersion,
|
||||
swap.ProtocolVersion,
|
||||
),
|
||||
Label: row.Label,
|
||||
Label: swap.Label,
|
||||
ClientPubkey: clientKey,
|
||||
ServerPubkey: serverKey,
|
||||
HtlcKeyLocator: keychain.KeyLocator{
|
||||
Family: keychain.KeyFamily(row.ClientKeyFamily),
|
||||
Index: uint32(row.ClientKeyIndex),
|
||||
Family: keychain.KeyFamily(swap.ClientKeyFamily),
|
||||
Index: uint32(swap.ClientKeyIndex),
|
||||
},
|
||||
SwapInvoice: row.SwapInvoice,
|
||||
PaymentTimeoutSeconds: uint32(row.PaymentTimeoutSeconds),
|
||||
LastHop: row.LastHop,
|
||||
QuotedSwapFee: btcutil.Amount(row.QuotedSwapFeeSatoshis),
|
||||
SwapInvoice: swap.SwapInvoice,
|
||||
PaymentTimeoutSeconds: uint32(swap.PaymentTimeoutSeconds),
|
||||
LastHop: swap.LastHop,
|
||||
QuotedSwapFee: btcutil.Amount(swap.QuotedSwapFeeSatoshis),
|
||||
DepositOutpoints: depositOutpoints,
|
||||
HtlcTxFeeRate: chainfee.SatPerKWeight(
|
||||
row.HtlcTxFeeRateSatKw,
|
||||
swap.HtlcTxFeeRateSatKw,
|
||||
),
|
||||
HtlcTimeoutSweepAddress: timeoutAddress,
|
||||
HtlcTimeoutSweepTxHash: htlcTimeoutSweepTxHash,
|
||||
Deposits: depositList,
|
||||
}
|
||||
|
||||
if len(updates) > 0 {
|
||||
|
|
|
|||
273
staticaddr/loopin/sql_store_test.go
Normal file
273
staticaddr/loopin/sql_store_test.go
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
package loopin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightninglabs/loop/loopdb"
|
||||
"github.com/lightninglabs/loop/staticaddr/deposit"
|
||||
"github.com/lightninglabs/loop/test"
|
||||
"github.com/lightningnetwork/lnd/clock"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestGetStaticAddressLoopInSwapsByStates tests that we can retrieve
|
||||
// StaticAddressLoopIn swaps by their states and that the deposits
|
||||
// associated with the swaps are correctly populated.
|
||||
func TestGetStaticAddressLoopInSwapsByStates(t *testing.T) {
|
||||
// Set up test context objects.
|
||||
ctxb := context.Background()
|
||||
testDb := loopdb.NewTestDB(t)
|
||||
testClock := clock.NewTestClock(time.Now())
|
||||
defer testDb.Close()
|
||||
|
||||
depositStore := deposit.NewSqlStore(testDb.BaseDB)
|
||||
swapStore := NewSqlStore(
|
||||
loopdb.NewTypedStore[Querier](testDb), testClock,
|
||||
&chaincfg.RegressionNetParams,
|
||||
)
|
||||
|
||||
newID := func() deposit.ID {
|
||||
did, err := deposit.GetRandomDepositID()
|
||||
require.NoError(t, err)
|
||||
|
||||
return did
|
||||
}
|
||||
|
||||
loopingDepositID := newID()
|
||||
loopedInDepositID := newID()
|
||||
d1, d2 := &deposit.Deposit{
|
||||
ID: loopingDepositID,
|
||||
OutPoint: wire.OutPoint{
|
||||
Hash: chainhash.Hash{0x1a, 0x2b, 0x3c, 0x4d},
|
||||
Index: 0,
|
||||
},
|
||||
Value: btcutil.Amount(100_000),
|
||||
TimeOutSweepPkScript: []byte{
|
||||
0x00, 0x14, 0x1a, 0x2b, 0x3c, 0x41,
|
||||
},
|
||||
},
|
||||
&deposit.Deposit{
|
||||
ID: loopedInDepositID,
|
||||
OutPoint: wire.OutPoint{
|
||||
Hash: chainhash.Hash{0x2a, 0x2b, 0x3c, 0x4e},
|
||||
Index: 1,
|
||||
},
|
||||
Value: btcutil.Amount(200_000),
|
||||
TimeOutSweepPkScript: []byte{
|
||||
0x00, 0x14, 0x1a, 0x2b, 0x3c, 0x4d,
|
||||
},
|
||||
}
|
||||
|
||||
err := depositStore.CreateDeposit(ctxb, d1)
|
||||
require.NoError(t, err)
|
||||
err = depositStore.CreateDeposit(ctxb, d2)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Add two updates per deposit, expect the last to be retrieved.
|
||||
d1.SetState(deposit.Deposited)
|
||||
d2.SetState(deposit.Deposited)
|
||||
|
||||
err = depositStore.UpdateDeposit(ctxb, d1)
|
||||
require.NoError(t, err)
|
||||
err = depositStore.UpdateDeposit(ctxb, d2)
|
||||
require.NoError(t, err)
|
||||
|
||||
d1.SetState(deposit.LoopingIn)
|
||||
d2.SetState(deposit.LoopedIn)
|
||||
|
||||
err = depositStore.UpdateDeposit(ctxb, d1)
|
||||
require.NoError(t, err)
|
||||
err = depositStore.UpdateDeposit(ctxb, d2)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, clientPubKey := test.CreateKey(1)
|
||||
_, serverPubKey := test.CreateKey(2)
|
||||
addr, err := btcutil.DecodeAddress(P2wkhAddr, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create pending swap.
|
||||
swapHashPending := lntypes.Hash{0x1, 0x2, 0x3, 0x4}
|
||||
swapPending := StaticAddressLoopIn{
|
||||
SwapHash: swapHashPending,
|
||||
SwapPreimage: lntypes.Preimage{0x1, 0x2, 0x3, 0x4},
|
||||
DepositOutpoints: []string{d1.OutPoint.String()},
|
||||
Deposits: []*deposit.Deposit{d1},
|
||||
ClientPubkey: clientPubKey,
|
||||
ServerPubkey: serverPubKey,
|
||||
HtlcTimeoutSweepAddress: addr,
|
||||
}
|
||||
swapPending.SetState(SignHtlcTx)
|
||||
|
||||
err = swapStore.CreateLoopIn(ctxb, &swapPending)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create succeeded swap.
|
||||
swapHashSucceeded := lntypes.Hash{0x2, 0x2, 0x3, 0x5}
|
||||
swapSucceeded := StaticAddressLoopIn{
|
||||
SwapHash: swapHashSucceeded,
|
||||
SwapPreimage: lntypes.Preimage{0x2, 0x2, 0x3, 0x5},
|
||||
DepositOutpoints: []string{d2.OutPoint.String()},
|
||||
Deposits: []*deposit.Deposit{d2},
|
||||
ClientPubkey: clientPubKey,
|
||||
ServerPubkey: serverPubKey,
|
||||
HtlcTimeoutSweepAddress: addr,
|
||||
}
|
||||
swapSucceeded.SetState(Succeeded)
|
||||
|
||||
err = swapStore.CreateLoopIn(ctxb, &swapSucceeded)
|
||||
require.NoError(t, err)
|
||||
|
||||
pendingSwaps, err := swapStore.GetStaticAddressLoopInSwapsByStates(ctxb, PendingStates)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, pendingSwaps, 1)
|
||||
require.Equal(t, swapHashPending, pendingSwaps[0].SwapHash)
|
||||
require.Equal(t, []string{d1.OutPoint.String()}, pendingSwaps[0].DepositOutpoints)
|
||||
require.Equal(t, SignHtlcTx, pendingSwaps[0].GetState())
|
||||
|
||||
pendingDeposits := pendingSwaps[0].Deposits
|
||||
require.Len(t, pendingDeposits, 1)
|
||||
require.Equal(t, d1.ID, pendingDeposits[0].ID)
|
||||
require.Equal(t, d1.OutPoint, pendingDeposits[0].OutPoint)
|
||||
require.Equal(t, d1.Value, pendingDeposits[0].Value)
|
||||
require.Equal(t, deposit.LoopingIn, pendingDeposits[0].GetState())
|
||||
|
||||
finalizedSwaps, err := swapStore.GetStaticAddressLoopInSwapsByStates(ctxb, FinalStates)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, finalizedSwaps, 1)
|
||||
require.Equal(t, swapHashSucceeded, finalizedSwaps[0].SwapHash)
|
||||
require.Equal(t, []string{d2.OutPoint.String()}, finalizedSwaps[0].DepositOutpoints)
|
||||
require.Equal(t, Succeeded, finalizedSwaps[0].GetState())
|
||||
|
||||
finalizedDeposits := finalizedSwaps[0].Deposits
|
||||
require.Len(t, finalizedDeposits, 1)
|
||||
require.Equal(t, d2.ID, finalizedDeposits[0].ID)
|
||||
require.Equal(t, d2.OutPoint, finalizedDeposits[0].OutPoint)
|
||||
require.Equal(t, d2.Value, finalizedDeposits[0].Value)
|
||||
require.Equal(t, deposit.LoopedIn, finalizedDeposits[0].GetState())
|
||||
}
|
||||
|
||||
// TestCreateLoopIn tests that CreateLoopIn correctly creates a new
|
||||
// StaticAddressLoopIn swap and associates it with the provided deposits.
|
||||
func TestCreateLoopIn(t *testing.T) {
|
||||
// Set up test context objects.
|
||||
ctxb := context.Background()
|
||||
testDb := loopdb.NewTestDB(t)
|
||||
testClock := clock.NewTestClock(time.Now())
|
||||
defer testDb.Close()
|
||||
|
||||
depositStore := deposit.NewSqlStore(testDb.BaseDB)
|
||||
swapStore := NewSqlStore(
|
||||
loopdb.NewTypedStore[Querier](testDb), testClock,
|
||||
&chaincfg.RegressionNetParams,
|
||||
)
|
||||
|
||||
newID := func() deposit.ID {
|
||||
did, err := deposit.GetRandomDepositID()
|
||||
require.NoError(t, err)
|
||||
|
||||
return did
|
||||
}
|
||||
|
||||
d1, d2 := &deposit.Deposit{
|
||||
ID: newID(),
|
||||
OutPoint: wire.OutPoint{
|
||||
Hash: chainhash.Hash{0x1a, 0x2b, 0x3c, 0x4d},
|
||||
Index: 0,
|
||||
},
|
||||
Value: btcutil.Amount(100_000),
|
||||
TimeOutSweepPkScript: []byte{
|
||||
0x00, 0x14, 0x1a, 0x2b, 0x3c, 0x41,
|
||||
},
|
||||
},
|
||||
&deposit.Deposit{
|
||||
ID: newID(),
|
||||
OutPoint: wire.OutPoint{
|
||||
Hash: chainhash.Hash{0x2a, 0x2b, 0x3c, 0x4e},
|
||||
Index: 1,
|
||||
},
|
||||
Value: btcutil.Amount(200_000),
|
||||
TimeOutSweepPkScript: []byte{
|
||||
0x00, 0x14, 0x1a, 0x2b, 0x3c, 0x4d,
|
||||
},
|
||||
}
|
||||
|
||||
err := depositStore.CreateDeposit(ctxb, d1)
|
||||
require.NoError(t, err)
|
||||
err = depositStore.CreateDeposit(ctxb, d2)
|
||||
require.NoError(t, err)
|
||||
|
||||
d1.SetState(deposit.LoopingIn)
|
||||
d2.SetState(deposit.LoopingIn)
|
||||
|
||||
err = depositStore.UpdateDeposit(ctxb, d1)
|
||||
require.NoError(t, err)
|
||||
err = depositStore.UpdateDeposit(ctxb, d2)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, clientPubKey := test.CreateKey(1)
|
||||
_, serverPubKey := test.CreateKey(2)
|
||||
addr, err := btcutil.DecodeAddress(P2wkhAddr, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create pending swap.
|
||||
swapHashPending := lntypes.Hash{0x1, 0x2, 0x3, 0x4}
|
||||
swapPending := StaticAddressLoopIn{
|
||||
SwapHash: swapHashPending,
|
||||
SwapPreimage: lntypes.Preimage{0x1, 0x2, 0x3, 0x4},
|
||||
DepositOutpoints: []string{d1.OutPoint.String(),
|
||||
d2.OutPoint.String()},
|
||||
Deposits: []*deposit.Deposit{d1, d2},
|
||||
ClientPubkey: clientPubKey,
|
||||
ServerPubkey: serverPubKey,
|
||||
HtlcTimeoutSweepAddress: addr,
|
||||
}
|
||||
swapPending.SetState(SignHtlcTx)
|
||||
|
||||
err = swapStore.CreateLoopIn(ctxb, &swapPending)
|
||||
require.NoError(t, err)
|
||||
|
||||
depositIDs, err := swapStore.DepositIDsForSwapHash(
|
||||
ctxb, swapHashPending,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, depositIDs, 2)
|
||||
require.Contains(t, depositIDs, d1.ID)
|
||||
require.Contains(t, depositIDs, d2.ID)
|
||||
|
||||
swapHashes, err := swapStore.SwapHashesForDepositIDs(
|
||||
ctxb, []deposit.ID{depositIDs[0], depositIDs[1]},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, swapHashes, 1)
|
||||
require.Len(t, swapHashes[swapHashPending], 2)
|
||||
require.Contains(t, swapHashes[swapHashPending], depositIDs[0])
|
||||
require.Contains(t, swapHashes[swapHashPending], depositIDs[1])
|
||||
|
||||
swap, err := swapStore.GetLoopInByHash(ctxb, swapHashPending)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, swapHashPending, swap.SwapHash)
|
||||
require.Equal(t, []string{d1.OutPoint.String(), d2.OutPoint.String()},
|
||||
swap.DepositOutpoints)
|
||||
require.Equal(t, SignHtlcTx, swap.GetState())
|
||||
|
||||
require.Len(t, swap.Deposits, 2)
|
||||
|
||||
require.Equal(t, d1.ID, swap.Deposits[0].ID)
|
||||
require.Equal(t, d1.OutPoint, swap.Deposits[0].OutPoint)
|
||||
require.Equal(t, d1.Value, swap.Deposits[0].Value)
|
||||
require.Equal(t, deposit.LoopingIn, swap.Deposits[0].GetState())
|
||||
|
||||
require.Equal(t, d2.ID, swap.Deposits[1].ID)
|
||||
require.Equal(t, d2.OutPoint, swap.Deposits[1].OutPoint)
|
||||
require.Equal(t, d2.Value, swap.Deposits[1].Value)
|
||||
require.Equal(t, deposit.LoopingIn, swap.Deposits[1].GetState())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue