mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
staticaddr/deposit: restore owning address parameters
Join each selected deposit with its persisted static-address row during loop-in recovery. Hydrate legacy rows as needed so restored swaps retain the scripts and key locators required for signing.
This commit is contained in:
parent
1b212fa105
commit
b8157ef901
9 changed files with 348 additions and 64 deletions
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightninglabs/loop/looprpc"
|
||||
"github.com/lightninglabs/loop/staticaddr/address"
|
||||
"github.com/lightninglabs/loop/staticaddr/deposit"
|
||||
"github.com/lightninglabs/loop/staticaddr/loopin"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
|
@ -196,6 +197,9 @@ func TestWarningDepositSelectionMatchesLoopInSelection(t *testing.T) {
|
|||
OutPoint: outpoint,
|
||||
Value: btcutil.Amount(fixture.value),
|
||||
ConfirmationHeight: fixture.confirmationHeight,
|
||||
AddressParams: &address.Parameters{
|
||||
Expiry: csvExpiry,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -204,8 +208,7 @@ func TestWarningDepositSelectionMatchesLoopInSelection(t *testing.T) {
|
|||
)
|
||||
|
||||
loopInSelected, err := loopin.SelectDeposits(
|
||||
btcutil.Amount(targetAmount), loopInDeposits, csvExpiry,
|
||||
blockHeight,
|
||||
btcutil.Amount(targetAmount), loopInDeposits, blockHeight,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
|
|||
|
|
@ -147,10 +147,19 @@ WHERE
|
|||
-- name: DepositsForSwapHash :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,
|
||||
u.update_state,
|
||||
u.update_timestamp
|
||||
FROM
|
||||
deposits d
|
||||
LEFT JOIN static_addresses sa ON sa.id = d.static_address_id
|
||||
LEFT JOIN
|
||||
deposit_updates u ON u.id = (
|
||||
SELECT id
|
||||
|
|
|
|||
|
|
@ -46,10 +46,19 @@ 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.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,
|
||||
u.update_state,
|
||||
u.update_timestamp
|
||||
FROM
|
||||
deposits d
|
||||
LEFT JOIN static_addresses sa ON sa.id = d.static_address_id
|
||||
LEFT JOIN
|
||||
deposit_updates u ON u.id = (
|
||||
SELECT id
|
||||
|
|
@ -74,6 +83,14 @@ type DepositsForSwapHashRow struct {
|
|||
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
|
||||
UpdateState sql.NullString
|
||||
UpdateTimestamp sql.NullTime
|
||||
}
|
||||
|
|
@ -99,6 +116,14 @@ func (q *Queries) DepositsForSwapHash(ctx context.Context, swapHash []byte) ([]D
|
|||
&i.FinalizedWithdrawalTx,
|
||||
&i.SwapHash,
|
||||
&i.StaticAddressID,
|
||||
&i.ClientPubkey,
|
||||
&i.ServerPubkey,
|
||||
&i.Expiry,
|
||||
&i.ClientKeyFamily,
|
||||
&i.ClientKeyIndex,
|
||||
&i.Pkscript,
|
||||
&i.ProtocolVersion,
|
||||
&i.InitiationHeight,
|
||||
&i.UpdateState,
|
||||
&i.UpdateTimestamp,
|
||||
); err != nil {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightninglabs/lndclient"
|
||||
"github.com/lightninglabs/loop/fsm"
|
||||
"github.com/lightninglabs/loop/staticaddr/address"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
)
|
||||
|
|
@ -69,8 +70,8 @@ type Manager struct {
|
|||
// mu guards access to the activeDeposits map.
|
||||
mu sync.Mutex
|
||||
|
||||
// reconcileMu serializes deposit reconciliation so new deposits are
|
||||
// discovered and retained exactly once per outpoint.
|
||||
// reconcileMu serializes startup recovery and deposit reconciliation so
|
||||
// new deposits are discovered and retained exactly once per outpoint.
|
||||
reconcileMu sync.Mutex
|
||||
|
||||
// activeDeposits contains all the active static address outputs.
|
||||
|
|
@ -213,6 +214,9 @@ func (m *Manager) notifyActiveDeposits(ctx context.Context,
|
|||
// recoverDeposits recovers static address parameters, previous deposits and
|
||||
// state machines from the database and starts the deposit notifier.
|
||||
func (m *Manager) recoverDeposits(ctx context.Context) error {
|
||||
m.reconcileMu.Lock()
|
||||
defer m.reconcileMu.Unlock()
|
||||
|
||||
log.Infof("Recovering static address parameters and deposits...")
|
||||
|
||||
// Recover deposits.
|
||||
|
|
@ -222,6 +226,11 @@ func (m *Manager) recoverDeposits(ctx context.Context) error {
|
|||
}
|
||||
|
||||
for i, d := range deposits {
|
||||
err = m.hydrateLegacyDepositAddressParams(ctx, d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.deposits[d.OutPoint] = deposits[i]
|
||||
|
||||
// If the current deposit is final it wasn't active when we
|
||||
|
|
@ -258,6 +267,66 @@ func (m *Manager) recoverDeposits(ctx context.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// hydrateLegacyDepositAddressParams fills in address parameters for deposits
|
||||
// that predate the durable deposit-to-static-address link. Those deposits all
|
||||
// belonged to the legacy/root static address, so the legacy address manager
|
||||
// lookup preserves the behavior that existed before multi-address support.
|
||||
func (m *Manager) hydrateLegacyDepositAddressParams(ctx context.Context,
|
||||
deposits ...*Deposit) error {
|
||||
|
||||
needsHydration := false
|
||||
for _, d := range deposits {
|
||||
if d != nil && d.AddressParams == nil {
|
||||
needsHydration = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !needsHydration {
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.cfg == nil || m.cfg.AddressManager == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var legacyParams *address.Parameters
|
||||
for _, d := range deposits {
|
||||
if d == nil || d.AddressParams != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if legacyParams == nil {
|
||||
params, err := m.cfg.AddressManager.
|
||||
GetStaticAddressParameters(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load legacy "+
|
||||
"static address parameters for deposit %v: %w",
|
||||
d.OutPoint, err)
|
||||
}
|
||||
if params == nil {
|
||||
return fmt.Errorf("missing legacy static address "+
|
||||
"parameters for deposit %v", d.OutPoint)
|
||||
}
|
||||
|
||||
if params.ID <= 0 {
|
||||
params.ID, err = m.cfg.AddressManager.
|
||||
GetStaticAddressID(ctx, params.PkScript)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load legacy "+
|
||||
"static address ID for deposit %v: %w",
|
||||
d.OutPoint, err)
|
||||
}
|
||||
}
|
||||
|
||||
legacyParams = params
|
||||
}
|
||||
|
||||
d.AddressParams = legacyParams
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// pollDeposits periodically polls for new deposits to our static address. This
|
||||
// complements the block-driven reconciliation in the main event loop: while new
|
||||
// blocks trigger reconcileDeposits to promptly detect confirmations, the ticker
|
||||
|
|
@ -805,7 +874,17 @@ func (m *Manager) removeActiveDeposit(outpoint wire.OutPoint) {
|
|||
|
||||
// GetAllDeposits returns all known deposits from the database.
|
||||
func (m *Manager) GetAllDeposits(ctx context.Context) ([]*Deposit, error) {
|
||||
return m.cfg.Store.AllDeposits(ctx)
|
||||
deposits, err := m.cfg.Store.AllDeposits(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = m.hydrateLegacyDepositAddressParams(ctx, deposits...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return deposits, nil
|
||||
}
|
||||
|
||||
// GetVisibleDeposits returns deposits that should be exposed through normal
|
||||
|
|
@ -820,6 +899,11 @@ func (m *Manager) GetVisibleDeposits(ctx context.Context) ([]*Deposit, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = m.hydrateLegacyDepositAddressParams(ctx, deposits...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
|
|
@ -896,6 +980,11 @@ func (m *Manager) DepositsForOutpoints(ctx context.Context,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = m.hydrateLegacyDepositAddressParams(ctx, deposit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deposits = append(deposits, deposit)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightninglabs/loop/fsm"
|
||||
"github.com/lightninglabs/loop/staticaddr/address"
|
||||
"github.com/lightninglabs/loop/staticaddr/script"
|
||||
"github.com/lightninglabs/loop/staticaddr/version"
|
||||
"github.com/lightninglabs/loop/test"
|
||||
|
|
@ -41,8 +42,15 @@ func TestReconcileDepositsSerialized(t *testing.T) {
|
|||
"ListUnspent", mock.Anything, int32(0), int32(MaxConfs),
|
||||
).Return([]*lnwallet.Utxo{utxo}, nil)
|
||||
mockAddressManager.On(
|
||||
"GetStaticAddressParameters", mock.Anything,
|
||||
).Return((*script.Parameters)(nil), errors.New("fsm init failed"))
|
||||
"GetParameters", mock.Anything,
|
||||
).Return(&address.Parameters{
|
||||
ID: 1,
|
||||
ClientPubkey: defaultServerPubkey,
|
||||
ServerPubkey: defaultServerPubkey,
|
||||
Expiry: defaultExpiry,
|
||||
PkScript: utxo.PkScript,
|
||||
ProtocolVersion: 999,
|
||||
})
|
||||
|
||||
mockStore := new(mockStore)
|
||||
var createCalls atomic.Int32
|
||||
|
|
@ -137,8 +145,15 @@ func TestReconcileConfirmedDepositUsesCurrentHeight(t *testing.T) {
|
|||
"ListUnspent", mock.Anything, int32(0), int32(MaxConfs),
|
||||
).Return([]*lnwallet.Utxo{utxo}, nil)
|
||||
mockAddressManager.On(
|
||||
"GetStaticAddressParameters", mock.Anything,
|
||||
).Return((*script.Parameters)(nil), errors.New("fsm init failed"))
|
||||
"GetParameters", mock.Anything,
|
||||
).Return(&address.Parameters{
|
||||
ID: 1,
|
||||
ClientPubkey: defaultServerPubkey,
|
||||
ServerPubkey: defaultServerPubkey,
|
||||
Expiry: defaultExpiry,
|
||||
PkScript: utxo.PkScript,
|
||||
ProtocolVersion: 999,
|
||||
})
|
||||
|
||||
mockStore := new(mockStore)
|
||||
mockStore.On(
|
||||
|
|
@ -471,6 +486,12 @@ func TestReconcileDepositsReactivatesReappearedDeposit(t *testing.T) {
|
|||
OutPoint: outpoint,
|
||||
Value: btcutil.Amount(100_000),
|
||||
ConfirmationHeight: 77,
|
||||
AddressParams: &address.Parameters{
|
||||
ClientPubkey: defaultServerPubkey,
|
||||
ServerPubkey: defaultServerPubkey,
|
||||
Expiry: defaultExpiry,
|
||||
ProtocolVersion: version.ProtocolVersion_V0,
|
||||
},
|
||||
}
|
||||
deposit.SetState(Deposited)
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightninglabs/lndclient"
|
||||
"github.com/lightninglabs/loop/staticaddr/address"
|
||||
"github.com/lightninglabs/loop/staticaddr/script"
|
||||
"github.com/lightninglabs/loop/swap"
|
||||
"github.com/lightninglabs/loop/swapserverrpc"
|
||||
|
|
@ -524,6 +525,96 @@ func TestManagerSkipsExpiryNotificationOnReconcileFailure(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRecoverDepositsKeepsSpentWithdrawing(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
id, err := GetRandomDepositID()
|
||||
require.NoError(t, err)
|
||||
|
||||
storedDeposit := &Deposit{
|
||||
ID: id,
|
||||
OutPoint: wire.OutPoint{
|
||||
Hash: chainhash.Hash{2},
|
||||
Index: 2,
|
||||
},
|
||||
state: Withdrawing,
|
||||
Value: btcutil.Amount(100000),
|
||||
ConfirmationHeight: 42,
|
||||
}
|
||||
|
||||
testContext := newManagerTestContextWithStoredDeposits(
|
||||
t, []*Deposit{storedDeposit}, nil,
|
||||
)
|
||||
|
||||
err = testContext.manager.recoverDeposits(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
deposits, err := testContext.manager.GetActiveDepositsInState(Withdrawing)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, deposits, 1)
|
||||
require.Equal(t, storedDeposit.OutPoint, deposits[0].OutPoint)
|
||||
}
|
||||
|
||||
func TestRecoverDepositsHydratesLegacyAddressParams(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
id, err := GetRandomDepositID()
|
||||
require.NoError(t, err)
|
||||
|
||||
storedDeposit := &Deposit{
|
||||
ID: id,
|
||||
OutPoint: wire.OutPoint{
|
||||
Hash: chainhash.Hash{3},
|
||||
Index: 3,
|
||||
},
|
||||
state: Deposited,
|
||||
Value: btcutil.Amount(100000),
|
||||
ConfirmationHeight: 42,
|
||||
TimeOutSweepPkScript: []byte{0x42, 0x21, 0x69},
|
||||
}
|
||||
|
||||
testContext := newManagerTestContextWithStoredDeposits(
|
||||
t, []*Deposit{storedDeposit}, nil,
|
||||
)
|
||||
|
||||
storedDeposit.AddressParams = nil
|
||||
|
||||
err = testContext.manager.recoverDeposits(ctx)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, storedDeposit.AddressParams)
|
||||
require.NotZero(t, storedDeposit.AddressParams.ID)
|
||||
}
|
||||
|
||||
func TestGetAllDepositsHydratesLegacyAddressParams(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
id, err := GetRandomDepositID()
|
||||
require.NoError(t, err)
|
||||
|
||||
storedDeposit := &Deposit{
|
||||
ID: id,
|
||||
OutPoint: wire.OutPoint{
|
||||
Hash: chainhash.Hash{4},
|
||||
Index: 4,
|
||||
},
|
||||
state: Withdrawn,
|
||||
Value: btcutil.Amount(100000),
|
||||
ConfirmationHeight: 42,
|
||||
}
|
||||
|
||||
testContext := newManagerTestContextWithStoredDeposits(
|
||||
t, []*Deposit{storedDeposit}, nil,
|
||||
)
|
||||
|
||||
storedDeposit.AddressParams = nil
|
||||
|
||||
deposits, err := testContext.manager.GetAllDeposits(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, deposits, 1)
|
||||
require.NotNil(t, deposits[0].AddressParams)
|
||||
require.NotZero(t, deposits[0].AddressParams.ID)
|
||||
}
|
||||
|
||||
// ManagerTestContext is a helper struct that contains all the necessary
|
||||
// components to test the reservation manager.
|
||||
type ManagerTestContext struct {
|
||||
|
|
@ -540,6 +631,39 @@ type ManagerTestContext struct {
|
|||
|
||||
// newManagerTestContext creates a new test context for the reservation manager.
|
||||
func newManagerTestContext(t *testing.T) *ManagerTestContext {
|
||||
ID, err := GetRandomDepositID()
|
||||
require.NoError(t, err)
|
||||
|
||||
utxo := &lnwallet.Utxo{
|
||||
AddressType: lnwallet.TaprootPubkey,
|
||||
Value: btcutil.Amount(100000),
|
||||
Confirmations: int64(defaultDepositConfirmations),
|
||||
PkScript: []byte("pkscript"),
|
||||
OutPoint: wire.OutPoint{
|
||||
Hash: chainhash.Hash{},
|
||||
Index: 0xffffffff,
|
||||
},
|
||||
}
|
||||
|
||||
storedDeposits := []*Deposit{
|
||||
{
|
||||
ID: ID,
|
||||
state: Deposited,
|
||||
OutPoint: utxo.OutPoint,
|
||||
Value: utxo.Value,
|
||||
ConfirmationHeight: 3,
|
||||
TimeOutSweepPkScript: []byte{0x42, 0x21, 0x69},
|
||||
},
|
||||
}
|
||||
|
||||
return newManagerTestContextWithStoredDeposits(
|
||||
t, storedDeposits, []*lnwallet.Utxo{utxo},
|
||||
)
|
||||
}
|
||||
|
||||
func newManagerTestContextWithStoredDeposits(t *testing.T,
|
||||
storedDeposits []*Deposit, utxos []*lnwallet.Utxo) *ManagerTestContext {
|
||||
|
||||
mockLnd := test.NewMockLnd()
|
||||
lndContext := test.NewContext(t, mockLnd)
|
||||
|
||||
|
|
@ -552,29 +676,6 @@ func newManagerTestContext(t *testing.T) *ManagerTestContext {
|
|||
blockChan := make(chan int32)
|
||||
blockErrChan := make(chan error)
|
||||
|
||||
ID, err := GetRandomDepositID()
|
||||
utxo := &lnwallet.Utxo{
|
||||
AddressType: lnwallet.TaprootPubkey,
|
||||
Value: btcutil.Amount(100000),
|
||||
Confirmations: int64(defaultDepositConfirmations),
|
||||
PkScript: []byte("pkscript"),
|
||||
OutPoint: wire.OutPoint{
|
||||
Hash: chainhash.Hash{},
|
||||
Index: 0xffffffff,
|
||||
},
|
||||
}
|
||||
require.NoError(t, err)
|
||||
storedDeposits := []*Deposit{
|
||||
{
|
||||
ID: ID,
|
||||
state: Deposited,
|
||||
OutPoint: utxo.OutPoint,
|
||||
Value: utxo.Value,
|
||||
ConfirmationHeight: 3,
|
||||
TimeOutSweepPkScript: []byte{0x42, 0x21, 0x69},
|
||||
},
|
||||
}
|
||||
|
||||
mockStore.On(
|
||||
"AllDeposits", mock.Anything,
|
||||
).Return(storedDeposits, nil)
|
||||
|
|
@ -583,17 +684,29 @@ func newManagerTestContext(t *testing.T) *ManagerTestContext {
|
|||
"UpdateDeposit", mock.Anything, mock.Anything,
|
||||
).Return(nil)
|
||||
|
||||
staticAddress, addrParams := generateStaticAddress(
|
||||
context.Background(), mockLnd, lndContext.T,
|
||||
)
|
||||
for _, storedDeposit := range storedDeposits {
|
||||
if storedDeposit.AddressParams == nil {
|
||||
storedDeposit.AddressParams = addrParams
|
||||
}
|
||||
}
|
||||
|
||||
var manager *Manager
|
||||
|
||||
mockAddressManager.On(
|
||||
"GetStaticAddressParameters", mock.Anything,
|
||||
).Return(&script.Parameters{
|
||||
Expiry: defaultExpiry,
|
||||
}, nil)
|
||||
).Return(addrParams, nil)
|
||||
|
||||
mockAddressManager.On(
|
||||
"ListUnspent", mock.Anything, mock.Anything, mock.Anything,
|
||||
).Return(func() []*lnwallet.Utxo {
|
||||
currentUtxo := *utxo
|
||||
if len(utxos) != 1 {
|
||||
return utxos
|
||||
}
|
||||
|
||||
currentUtxo := *utxos[0]
|
||||
currentHeight := manager.currentHeight.Load()
|
||||
if currentHeight < defaultDepositConfirmations {
|
||||
currentUtxo.Confirmations = 0
|
||||
|
|
@ -638,9 +751,6 @@ func newManagerTestContext(t *testing.T) *ManagerTestContext {
|
|||
blockErrChan: blockErrChan,
|
||||
}
|
||||
|
||||
staticAddress := generateStaticAddress(
|
||||
context.Background(), testContext,
|
||||
)
|
||||
mockAddressManager.On(
|
||||
"GetStaticAddress", mock.Anything,
|
||||
).Return(staticAddress, nil)
|
||||
|
|
@ -648,19 +758,30 @@ func newManagerTestContext(t *testing.T) *ManagerTestContext {
|
|||
return testContext
|
||||
}
|
||||
|
||||
func generateStaticAddress(ctx context.Context,
|
||||
t *ManagerTestContext) *script.StaticAddress {
|
||||
func generateStaticAddress(ctx context.Context, mockLnd *test.LndMockServices,
|
||||
t *testing.T) (*script.StaticAddress, *address.Parameters) {
|
||||
|
||||
keyDescriptor, err := t.mockLnd.WalletKit.DeriveNextKey(
|
||||
keyDescriptor, err := mockLnd.WalletKit.DeriveNextKey(
|
||||
ctx, swap.StaticAddressKeyFamily,
|
||||
)
|
||||
require.NoError(t.context.T, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
staticAddress, err := script.NewStaticAddress(
|
||||
input.MuSig2Version100RC2, int64(defaultExpiry),
|
||||
keyDescriptor.PubKey, defaultServerPubkey,
|
||||
)
|
||||
require.NoError(t.context.T, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
return staticAddress
|
||||
pkScript, err := staticAddress.StaticAddressScript()
|
||||
require.NoError(t, err)
|
||||
|
||||
return staticAddress, &address.Parameters{
|
||||
ID: 1,
|
||||
ClientPubkey: keyDescriptor.PubKey,
|
||||
ServerPubkey: defaultServerPubkey,
|
||||
Expiry: defaultExpiry,
|
||||
PkScript: pkScript,
|
||||
KeyLocator: keyDescriptor.KeyLocator,
|
||||
ProtocolVersion: 0,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -678,19 +678,8 @@ func (m *Manager) initiateLoopIn(ctx context.Context,
|
|||
"deposits: %w", err)
|
||||
}
|
||||
|
||||
// TODO(hieblmi): add params to deposit for multi-address
|
||||
// support.
|
||||
params, err := m.cfg.AddressManager.GetStaticAddressParameters(
|
||||
ctx,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to retrieve static "+
|
||||
"address parameters: %w", err)
|
||||
}
|
||||
|
||||
selectedDeposits, err = SelectDeposits(
|
||||
req.SelectedAmount, allDeposits, params.Expiry,
|
||||
m.currentHeight.Load(),
|
||||
req.SelectedAmount, allDeposits, m.currentHeight.Load(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to select deposits: %w",
|
||||
|
|
@ -870,15 +859,21 @@ func (m *Manager) activeDepositsForLoopIn(loopIn *StaticAddressLoopIn) (
|
|||
// leaving a dust change. It returns an error if the sum of deposits minus dust
|
||||
// is less than the requested amount.
|
||||
func SelectDeposits(targetAmount btcutil.Amount,
|
||||
unfilteredDeposits []*deposit.Deposit, csvExpiry uint32,
|
||||
blockHeight uint32) ([]*deposit.Deposit, error) {
|
||||
unfilteredDeposits []*deposit.Deposit, blockHeight uint32) (
|
||||
[]*deposit.Deposit, error) {
|
||||
|
||||
// Filter out deposits that are too close to expiry to be swapped.
|
||||
var deposits []*deposit.Deposit
|
||||
for _, d := range unfilteredDeposits {
|
||||
confirmationHeight := d.GetConfirmationHeight()
|
||||
if d.AddressParams == nil {
|
||||
return nil, fmt.Errorf("missing static address parameters "+
|
||||
"for deposit %s", d.OutPoint.String())
|
||||
}
|
||||
|
||||
if !IsSwappable(
|
||||
uint32(confirmationHeight), blockHeight, csvExpiry,
|
||||
uint32(confirmationHeight), blockHeight,
|
||||
d.AddressParams.Expiry,
|
||||
) {
|
||||
|
||||
log.Debugf("Skipping deposit %s as it expires before "+
|
||||
|
|
@ -905,11 +900,11 @@ func SelectDeposits(targetAmount btcutil.Amount,
|
|||
if deposits[i].Value == deposits[j].Value {
|
||||
iExp := blocksUntilDepositExpiry(
|
||||
uint32(iConfirmationHeight), blockHeight,
|
||||
csvExpiry,
|
||||
deposits[i].AddressParams.Expiry,
|
||||
)
|
||||
jExp := blocksUntilDepositExpiry(
|
||||
uint32(jConfirmationHeight), blockHeight,
|
||||
csvExpiry,
|
||||
deposits[j].AddressParams.Expiry,
|
||||
)
|
||||
|
||||
return iExp < jExp
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/lightninglabs/loop"
|
||||
"github.com/lightninglabs/loop/fsm"
|
||||
"github.com/lightninglabs/loop/labels"
|
||||
"github.com/lightninglabs/loop/staticaddr/address"
|
||||
"github.com/lightninglabs/loop/staticaddr/deposit"
|
||||
"github.com/lightninglabs/loop/staticaddr/script"
|
||||
"github.com/lightninglabs/loop/swap"
|
||||
|
|
@ -193,9 +194,11 @@ func TestSelectDeposits(t *testing.T) {
|
|||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
setTestDepositParams(tc.deposits, tc.csvExpiry)
|
||||
setTestDepositParams(tc.expected, tc.csvExpiry)
|
||||
|
||||
selectedDeposits, err := SelectDeposits(
|
||||
tc.targetValue, tc.deposits, tc.csvExpiry,
|
||||
tc.blockHeight,
|
||||
tc.targetValue, tc.deposits, tc.blockHeight,
|
||||
)
|
||||
if tc.expectedErr == "" {
|
||||
require.NoError(t, err)
|
||||
|
|
@ -417,6 +420,14 @@ func TestGetAllSwapsPreservesStoreDeposits(t *testing.T) {
|
|||
require.Equal(t, []*deposit.Deposit{currentDeposit}, swaps[0].Deposits)
|
||||
}
|
||||
|
||||
func setTestDepositParams(deposits []*deposit.Deposit, expiry uint32) {
|
||||
for _, d := range deposits {
|
||||
d.AddressParams = &address.Parameters{
|
||||
Expiry: expiry,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestIsSwappableUnconfirmed checks that an unconfirmed deposit is considered
|
||||
// swappable because its CSV timeout has not started yet.
|
||||
func TestIsSwappableUnconfirmed(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -610,6 +610,16 @@ func toStaticAddressLoopIn(_ context.Context, network *chaincfg.Params,
|
|||
TimeoutSweepPkScript: d.TimeoutSweepPkScript,
|
||||
ExpirySweepTxid: d.ExpirySweepTxid,
|
||||
FinalizedWithdrawalTx: d.FinalizedWithdrawalTx,
|
||||
SwapHash: d.SwapHash,
|
||||
StaticAddressID: d.StaticAddressID,
|
||||
ClientPubkey: d.ClientPubkey,
|
||||
ServerPubkey: d.ServerPubkey,
|
||||
Expiry: d.Expiry,
|
||||
ClientKeyFamily: d.ClientKeyFamily,
|
||||
ClientKeyIndex: d.ClientKeyIndex,
|
||||
Pkscript: d.Pkscript,
|
||||
ProtocolVersion: d.ProtocolVersion,
|
||||
InitiationHeight: d.InitiationHeight,
|
||||
}
|
||||
|
||||
sqlcDepositUpdate := sqlc.DepositUpdate{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue