mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
loopdb: persist static loop-in risk decisions
Add schema, sqlc queries, store fields, and SqlStore support for recording server confirmation-risk decisions with static loop-in swaps. Store the decision timestamp so payment-deadline recovery can reconstruct elapsed time after restart.
This commit is contained in:
parent
b0f43bbe1e
commit
491fddbc34
9 changed files with 302 additions and 78 deletions
|
|
@ -0,0 +1,3 @@
|
||||||
|
-- Drop confirmation-risk decision fields from static address loop-ins.
|
||||||
|
ALTER TABLE static_address_swaps DROP COLUMN confirmation_risk_decision;
|
||||||
|
ALTER TABLE static_address_swaps DROP COLUMN confirmation_risk_decision_time;
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
-- confirmation_risk_decision records the server's confirmation-risk decision
|
||||||
|
-- for a static address loop-in. Possible values are:
|
||||||
|
-- - '': no decision has been received yet;
|
||||||
|
-- - 'accepted': the server accepted waiting for the low-confirmation
|
||||||
|
-- deposits, which starts or reconstructs the payment deadline;
|
||||||
|
-- - 'rejected': the server stopped waiting for the low-confirmation deposits
|
||||||
|
-- before paying the invoice.
|
||||||
|
-- Once rejected, a later accepted update is ignored.
|
||||||
|
ALTER TABLE static_address_swaps ADD COLUMN confirmation_risk_decision TEXT NOT NULL DEFAULT '';
|
||||||
|
|
||||||
|
-- confirmation_risk_decision_time records when loopd received and persisted
|
||||||
|
-- the server's decision, so payment deadlines can be reconstructed after
|
||||||
|
-- restart. Same-decision replays preserve the original timestamp; changing
|
||||||
|
-- from accepted to rejected updates it.
|
||||||
|
ALTER TABLE static_address_swaps ADD COLUMN confirmation_risk_decision_time TIMESTAMP;
|
||||||
|
|
@ -137,18 +137,20 @@ type StaticAddress struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type StaticAddressSwap struct {
|
type StaticAddressSwap struct {
|
||||||
ID int32
|
ID int32
|
||||||
SwapHash []byte
|
SwapHash []byte
|
||||||
SwapInvoice string
|
SwapInvoice string
|
||||||
LastHop []byte
|
LastHop []byte
|
||||||
PaymentTimeoutSeconds int32
|
PaymentTimeoutSeconds int32
|
||||||
QuotedSwapFeeSatoshis int64
|
QuotedSwapFeeSatoshis int64
|
||||||
DepositOutpoints string
|
DepositOutpoints string
|
||||||
HtlcTxFeeRateSatKw int64
|
HtlcTxFeeRateSatKw int64
|
||||||
HtlcTimeoutSweepTxID sql.NullString
|
HtlcTimeoutSweepTxID sql.NullString
|
||||||
HtlcTimeoutSweepAddress string
|
HtlcTimeoutSweepAddress string
|
||||||
SelectedAmount int64
|
SelectedAmount int64
|
||||||
Fast bool
|
Fast bool
|
||||||
|
ConfirmationRiskDecision string
|
||||||
|
ConfirmationRiskDecisionTime sql.NullTime
|
||||||
}
|
}
|
||||||
|
|
||||||
type StaticAddressSwapUpdate struct {
|
type StaticAddressSwapUpdate struct {
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ type Querier interface {
|
||||||
MapDepositToSwap(ctx context.Context, arg MapDepositToSwapParams) error
|
MapDepositToSwap(ctx context.Context, arg MapDepositToSwapParams) error
|
||||||
OverrideSelectedSwapAmount(ctx context.Context, arg OverrideSelectedSwapAmountParams) error
|
OverrideSelectedSwapAmount(ctx context.Context, arg OverrideSelectedSwapAmountParams) error
|
||||||
OverrideSwapCosts(ctx context.Context, arg OverrideSwapCostsParams) error
|
OverrideSwapCosts(ctx context.Context, arg OverrideSwapCostsParams) error
|
||||||
|
RecordStaticAddressRiskDecision(ctx context.Context, arg RecordStaticAddressRiskDecisionParams) error
|
||||||
SwapHashForDepositID(ctx context.Context, depositID []byte) ([]byte, error)
|
SwapHashForDepositID(ctx context.Context, depositID []byte) ([]byte, error)
|
||||||
UpdateBatch(ctx context.Context, arg UpdateBatchParams) error
|
UpdateBatch(ctx context.Context, arg UpdateBatchParams) error
|
||||||
UpdateDeposit(ctx context.Context, arg UpdateDepositParams) error
|
UpdateDeposit(ctx context.Context, arg UpdateDepositParams) error
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,22 @@ SET
|
||||||
WHERE
|
WHERE
|
||||||
swap_hash = $1;
|
swap_hash = $1;
|
||||||
|
|
||||||
|
-- name: RecordStaticAddressRiskDecision :exec
|
||||||
|
UPDATE static_address_swaps
|
||||||
|
SET
|
||||||
|
confirmation_risk_decision = $2,
|
||||||
|
confirmation_risk_decision_time = CASE
|
||||||
|
WHEN confirmation_risk_decision = $2 THEN
|
||||||
|
COALESCE(confirmation_risk_decision_time, $3)
|
||||||
|
ELSE $3
|
||||||
|
END
|
||||||
|
WHERE
|
||||||
|
swap_hash = $1
|
||||||
|
AND NOT (
|
||||||
|
confirmation_risk_decision = 'rejected'
|
||||||
|
AND $2 = 'accepted'
|
||||||
|
);
|
||||||
|
|
||||||
-- name: InsertStaticAddressMetaUpdate :exec
|
-- name: InsertStaticAddressMetaUpdate :exec
|
||||||
INSERT INTO static_address_swap_updates (
|
INSERT INTO static_address_swap_updates (
|
||||||
swap_hash,
|
swap_hash,
|
||||||
|
|
@ -147,7 +163,3 @@ WHERE
|
||||||
d.swap_hash = $1;
|
d.swap_hash = $1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ func (q *Queries) GetLoopInSwapUpdates(ctx context.Context, swapHash []byte) ([]
|
||||||
const getStaticAddressLoopInSwap = `-- name: GetStaticAddressLoopInSwap :one
|
const getStaticAddressLoopInSwap = `-- name: GetStaticAddressLoopInSwap :one
|
||||||
SELECT
|
SELECT
|
||||||
swaps.id, swaps.swap_hash, swaps.preimage, swaps.initiation_time, swaps.amount_requested, swaps.cltv_expiry, swaps.max_miner_fee, swaps.max_swap_fee, swaps.initiation_height, swaps.protocol_version, swaps.label,
|
swaps.id, swaps.swap_hash, swaps.preimage, swaps.initiation_time, swaps.amount_requested, swaps.cltv_expiry, swaps.max_miner_fee, swaps.max_swap_fee, swaps.initiation_height, swaps.protocol_version, swaps.label,
|
||||||
static_address_swaps.id, static_address_swaps.swap_hash, static_address_swaps.swap_invoice, static_address_swaps.last_hop, static_address_swaps.payment_timeout_seconds, static_address_swaps.quoted_swap_fee_satoshis, static_address_swaps.deposit_outpoints, static_address_swaps.htlc_tx_fee_rate_sat_kw, static_address_swaps.htlc_timeout_sweep_tx_id, static_address_swaps.htlc_timeout_sweep_address, static_address_swaps.selected_amount, static_address_swaps.fast,
|
static_address_swaps.id, static_address_swaps.swap_hash, static_address_swaps.swap_invoice, static_address_swaps.last_hop, static_address_swaps.payment_timeout_seconds, static_address_swaps.quoted_swap_fee_satoshis, static_address_swaps.deposit_outpoints, static_address_swaps.htlc_tx_fee_rate_sat_kw, static_address_swaps.htlc_timeout_sweep_tx_id, static_address_swaps.htlc_timeout_sweep_address, static_address_swaps.selected_amount, static_address_swaps.fast, static_address_swaps.confirmation_risk_decision, static_address_swaps.confirmation_risk_decision_time,
|
||||||
htlc_keys.swap_hash, htlc_keys.sender_script_pubkey, htlc_keys.receiver_script_pubkey, htlc_keys.sender_internal_pubkey, htlc_keys.receiver_internal_pubkey, htlc_keys.client_key_family, htlc_keys.client_key_index
|
htlc_keys.swap_hash, htlc_keys.sender_script_pubkey, htlc_keys.receiver_script_pubkey, htlc_keys.sender_internal_pubkey, htlc_keys.receiver_internal_pubkey, htlc_keys.client_key_family, htlc_keys.client_key_index
|
||||||
FROM
|
FROM
|
||||||
swaps
|
swaps
|
||||||
|
|
@ -166,36 +166,38 @@ WHERE
|
||||||
`
|
`
|
||||||
|
|
||||||
type GetStaticAddressLoopInSwapRow struct {
|
type GetStaticAddressLoopInSwapRow struct {
|
||||||
ID int32
|
ID int32
|
||||||
SwapHash []byte
|
SwapHash []byte
|
||||||
Preimage []byte
|
Preimage []byte
|
||||||
InitiationTime time.Time
|
InitiationTime time.Time
|
||||||
AmountRequested int64
|
AmountRequested int64
|
||||||
CltvExpiry int32
|
CltvExpiry int32
|
||||||
MaxMinerFee int64
|
MaxMinerFee int64
|
||||||
MaxSwapFee int64
|
MaxSwapFee int64
|
||||||
InitiationHeight int32
|
InitiationHeight int32
|
||||||
ProtocolVersion int32
|
ProtocolVersion int32
|
||||||
Label string
|
Label string
|
||||||
ID_2 int32
|
ID_2 int32
|
||||||
SwapHash_2 []byte
|
SwapHash_2 []byte
|
||||||
SwapInvoice string
|
SwapInvoice string
|
||||||
LastHop []byte
|
LastHop []byte
|
||||||
PaymentTimeoutSeconds int32
|
PaymentTimeoutSeconds int32
|
||||||
QuotedSwapFeeSatoshis int64
|
QuotedSwapFeeSatoshis int64
|
||||||
DepositOutpoints string
|
DepositOutpoints string
|
||||||
HtlcTxFeeRateSatKw int64
|
HtlcTxFeeRateSatKw int64
|
||||||
HtlcTimeoutSweepTxID sql.NullString
|
HtlcTimeoutSweepTxID sql.NullString
|
||||||
HtlcTimeoutSweepAddress string
|
HtlcTimeoutSweepAddress string
|
||||||
SelectedAmount int64
|
SelectedAmount int64
|
||||||
Fast bool
|
Fast bool
|
||||||
SwapHash_3 []byte
|
ConfirmationRiskDecision string
|
||||||
SenderScriptPubkey []byte
|
ConfirmationRiskDecisionTime sql.NullTime
|
||||||
ReceiverScriptPubkey []byte
|
SwapHash_3 []byte
|
||||||
SenderInternalPubkey []byte
|
SenderScriptPubkey []byte
|
||||||
ReceiverInternalPubkey []byte
|
ReceiverScriptPubkey []byte
|
||||||
ClientKeyFamily int32
|
SenderInternalPubkey []byte
|
||||||
ClientKeyIndex int32
|
ReceiverInternalPubkey []byte
|
||||||
|
ClientKeyFamily int32
|
||||||
|
ClientKeyIndex int32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) GetStaticAddressLoopInSwap(ctx context.Context, swapHash []byte) (GetStaticAddressLoopInSwapRow, error) {
|
func (q *Queries) GetStaticAddressLoopInSwap(ctx context.Context, swapHash []byte) (GetStaticAddressLoopInSwapRow, error) {
|
||||||
|
|
@ -225,6 +227,8 @@ func (q *Queries) GetStaticAddressLoopInSwap(ctx context.Context, swapHash []byt
|
||||||
&i.HtlcTimeoutSweepAddress,
|
&i.HtlcTimeoutSweepAddress,
|
||||||
&i.SelectedAmount,
|
&i.SelectedAmount,
|
||||||
&i.Fast,
|
&i.Fast,
|
||||||
|
&i.ConfirmationRiskDecision,
|
||||||
|
&i.ConfirmationRiskDecisionTime,
|
||||||
&i.SwapHash_3,
|
&i.SwapHash_3,
|
||||||
&i.SenderScriptPubkey,
|
&i.SenderScriptPubkey,
|
||||||
&i.ReceiverScriptPubkey,
|
&i.ReceiverScriptPubkey,
|
||||||
|
|
@ -239,7 +243,7 @@ func (q *Queries) GetStaticAddressLoopInSwap(ctx context.Context, swapHash []byt
|
||||||
const getStaticAddressLoopInSwapsByStates = `-- name: GetStaticAddressLoopInSwapsByStates :many
|
const getStaticAddressLoopInSwapsByStates = `-- name: GetStaticAddressLoopInSwapsByStates :many
|
||||||
SELECT
|
SELECT
|
||||||
swaps.id, swaps.swap_hash, swaps.preimage, swaps.initiation_time, swaps.amount_requested, swaps.cltv_expiry, swaps.max_miner_fee, swaps.max_swap_fee, swaps.initiation_height, swaps.protocol_version, swaps.label,
|
swaps.id, swaps.swap_hash, swaps.preimage, swaps.initiation_time, swaps.amount_requested, swaps.cltv_expiry, swaps.max_miner_fee, swaps.max_swap_fee, swaps.initiation_height, swaps.protocol_version, swaps.label,
|
||||||
static_address_swaps.id, static_address_swaps.swap_hash, static_address_swaps.swap_invoice, static_address_swaps.last_hop, static_address_swaps.payment_timeout_seconds, static_address_swaps.quoted_swap_fee_satoshis, static_address_swaps.deposit_outpoints, static_address_swaps.htlc_tx_fee_rate_sat_kw, static_address_swaps.htlc_timeout_sweep_tx_id, static_address_swaps.htlc_timeout_sweep_address, static_address_swaps.selected_amount, static_address_swaps.fast,
|
static_address_swaps.id, static_address_swaps.swap_hash, static_address_swaps.swap_invoice, static_address_swaps.last_hop, static_address_swaps.payment_timeout_seconds, static_address_swaps.quoted_swap_fee_satoshis, static_address_swaps.deposit_outpoints, static_address_swaps.htlc_tx_fee_rate_sat_kw, static_address_swaps.htlc_timeout_sweep_tx_id, static_address_swaps.htlc_timeout_sweep_address, static_address_swaps.selected_amount, static_address_swaps.fast, static_address_swaps.confirmation_risk_decision, static_address_swaps.confirmation_risk_decision_time,
|
||||||
htlc_keys.swap_hash, htlc_keys.sender_script_pubkey, htlc_keys.receiver_script_pubkey, htlc_keys.sender_internal_pubkey, htlc_keys.receiver_internal_pubkey, htlc_keys.client_key_family, htlc_keys.client_key_index
|
htlc_keys.swap_hash, htlc_keys.sender_script_pubkey, htlc_keys.receiver_script_pubkey, htlc_keys.sender_internal_pubkey, htlc_keys.receiver_internal_pubkey, htlc_keys.client_key_family, htlc_keys.client_key_index
|
||||||
FROM
|
FROM
|
||||||
swaps
|
swaps
|
||||||
|
|
@ -263,36 +267,38 @@ ORDER BY
|
||||||
`
|
`
|
||||||
|
|
||||||
type GetStaticAddressLoopInSwapsByStatesRow struct {
|
type GetStaticAddressLoopInSwapsByStatesRow struct {
|
||||||
ID int32
|
ID int32
|
||||||
SwapHash []byte
|
SwapHash []byte
|
||||||
Preimage []byte
|
Preimage []byte
|
||||||
InitiationTime time.Time
|
InitiationTime time.Time
|
||||||
AmountRequested int64
|
AmountRequested int64
|
||||||
CltvExpiry int32
|
CltvExpiry int32
|
||||||
MaxMinerFee int64
|
MaxMinerFee int64
|
||||||
MaxSwapFee int64
|
MaxSwapFee int64
|
||||||
InitiationHeight int32
|
InitiationHeight int32
|
||||||
ProtocolVersion int32
|
ProtocolVersion int32
|
||||||
Label string
|
Label string
|
||||||
ID_2 int32
|
ID_2 int32
|
||||||
SwapHash_2 []byte
|
SwapHash_2 []byte
|
||||||
SwapInvoice string
|
SwapInvoice string
|
||||||
LastHop []byte
|
LastHop []byte
|
||||||
PaymentTimeoutSeconds int32
|
PaymentTimeoutSeconds int32
|
||||||
QuotedSwapFeeSatoshis int64
|
QuotedSwapFeeSatoshis int64
|
||||||
DepositOutpoints string
|
DepositOutpoints string
|
||||||
HtlcTxFeeRateSatKw int64
|
HtlcTxFeeRateSatKw int64
|
||||||
HtlcTimeoutSweepTxID sql.NullString
|
HtlcTimeoutSweepTxID sql.NullString
|
||||||
HtlcTimeoutSweepAddress string
|
HtlcTimeoutSweepAddress string
|
||||||
SelectedAmount int64
|
SelectedAmount int64
|
||||||
Fast bool
|
Fast bool
|
||||||
SwapHash_3 []byte
|
ConfirmationRiskDecision string
|
||||||
SenderScriptPubkey []byte
|
ConfirmationRiskDecisionTime sql.NullTime
|
||||||
ReceiverScriptPubkey []byte
|
SwapHash_3 []byte
|
||||||
SenderInternalPubkey []byte
|
SenderScriptPubkey []byte
|
||||||
ReceiverInternalPubkey []byte
|
ReceiverScriptPubkey []byte
|
||||||
ClientKeyFamily int32
|
SenderInternalPubkey []byte
|
||||||
ClientKeyIndex int32
|
ReceiverInternalPubkey []byte
|
||||||
|
ClientKeyFamily int32
|
||||||
|
ClientKeyIndex int32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) GetStaticAddressLoopInSwapsByStates(ctx context.Context, dollar_1 sql.NullString) ([]GetStaticAddressLoopInSwapsByStatesRow, error) {
|
func (q *Queries) GetStaticAddressLoopInSwapsByStates(ctx context.Context, dollar_1 sql.NullString) ([]GetStaticAddressLoopInSwapsByStatesRow, error) {
|
||||||
|
|
@ -328,6 +334,8 @@ func (q *Queries) GetStaticAddressLoopInSwapsByStates(ctx context.Context, dolla
|
||||||
&i.HtlcTimeoutSweepAddress,
|
&i.HtlcTimeoutSweepAddress,
|
||||||
&i.SelectedAmount,
|
&i.SelectedAmount,
|
||||||
&i.Fast,
|
&i.Fast,
|
||||||
|
&i.ConfirmationRiskDecision,
|
||||||
|
&i.ConfirmationRiskDecisionTime,
|
||||||
&i.SwapHash_3,
|
&i.SwapHash_3,
|
||||||
&i.SenderScriptPubkey,
|
&i.SenderScriptPubkey,
|
||||||
&i.ReceiverScriptPubkey,
|
&i.ReceiverScriptPubkey,
|
||||||
|
|
@ -482,6 +490,34 @@ func (q *Queries) OverrideSelectedSwapAmount(ctx context.Context, arg OverrideSe
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const recordStaticAddressRiskDecision = `-- name: RecordStaticAddressRiskDecision :exec
|
||||||
|
UPDATE static_address_swaps
|
||||||
|
SET
|
||||||
|
confirmation_risk_decision = $2,
|
||||||
|
confirmation_risk_decision_time = CASE
|
||||||
|
WHEN confirmation_risk_decision = $2 THEN
|
||||||
|
COALESCE(confirmation_risk_decision_time, $3)
|
||||||
|
ELSE $3
|
||||||
|
END
|
||||||
|
WHERE
|
||||||
|
swap_hash = $1
|
||||||
|
AND NOT (
|
||||||
|
confirmation_risk_decision = 'rejected'
|
||||||
|
AND $2 = 'accepted'
|
||||||
|
)
|
||||||
|
`
|
||||||
|
|
||||||
|
type RecordStaticAddressRiskDecisionParams struct {
|
||||||
|
SwapHash []byte
|
||||||
|
ConfirmationRiskDecision string
|
||||||
|
ConfirmationRiskDecisionTime sql.NullTime
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) RecordStaticAddressRiskDecision(ctx context.Context, arg RecordStaticAddressRiskDecisionParams) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, recordStaticAddressRiskDecision, arg.SwapHash, arg.ConfirmationRiskDecision, arg.ConfirmationRiskDecisionTime)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
const swapHashForDepositID = `-- name: SwapHashForDepositID :one
|
const swapHashForDepositID = `-- name: SwapHashForDepositID :one
|
||||||
SELECT
|
SELECT
|
||||||
swap_hash
|
swap_hash
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,23 @@ import (
|
||||||
"github.com/lightningnetwork/lnd/zpay32"
|
"github.com/lightningnetwork/lnd/zpay32"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ConfirmationRiskDecision records the server's decision on whether it accepts
|
||||||
|
// waiting for low-confirmation deposits before paying a static loop-in invoice.
|
||||||
|
type ConfirmationRiskDecision string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ConfirmationRiskDecisionNone means no risk decision has been received.
|
||||||
|
ConfirmationRiskDecisionNone ConfirmationRiskDecision = ""
|
||||||
|
|
||||||
|
// ConfirmationRiskDecisionAccepted means the server accepted waiting for
|
||||||
|
// deposit confirmations and the payment deadline has started.
|
||||||
|
ConfirmationRiskDecisionAccepted ConfirmationRiskDecision = "accepted"
|
||||||
|
|
||||||
|
// ConfirmationRiskDecisionRejected means the server stopped waiting for
|
||||||
|
// deposit confirmations before paying the invoice.
|
||||||
|
ConfirmationRiskDecisionRejected ConfirmationRiskDecision = "rejected"
|
||||||
|
)
|
||||||
|
|
||||||
// StaticAddressLoopIn represents the in-memory loop-in information.
|
// StaticAddressLoopIn represents the in-memory loop-in information.
|
||||||
type StaticAddressLoopIn struct {
|
type StaticAddressLoopIn struct {
|
||||||
// SwapHash is the hashed preimage of the swap invoice. It represents
|
// SwapHash is the hashed preimage of the swap invoice. It represents
|
||||||
|
|
@ -107,6 +124,15 @@ type StaticAddressLoopIn struct {
|
||||||
// LastUpdateTime is the timestamp of the latest persisted state update.
|
// LastUpdateTime is the timestamp of the latest persisted state update.
|
||||||
LastUpdateTime time.Time
|
LastUpdateTime time.Time
|
||||||
|
|
||||||
|
// ConfirmationRiskDecision records the server's persisted decision on
|
||||||
|
// low-confirmation deposit risk.
|
||||||
|
ConfirmationRiskDecision ConfirmationRiskDecision
|
||||||
|
|
||||||
|
// ConfirmationRiskDecisionTime is when loopd persisted the server risk
|
||||||
|
// decision. It is used to reconstruct payment-deadline timeouts after
|
||||||
|
// restart.
|
||||||
|
ConfirmationRiskDecisionTime time.Time
|
||||||
|
|
||||||
// state is the current state of the swap.
|
// state is the current state of the swap.
|
||||||
state fsm.StateType
|
state fsm.StateType
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,9 @@ var (
|
||||||
// ErrInvalidOutpoint is returned when an outpoint contains the outpoint
|
// ErrInvalidOutpoint is returned when an outpoint contains the outpoint
|
||||||
// separator.
|
// separator.
|
||||||
ErrInvalidOutpoint = errors.New("outpoint contains outpoint separator")
|
ErrInvalidOutpoint = errors.New("outpoint contains outpoint separator")
|
||||||
|
|
||||||
|
// ErrLoopInNotFound is returned when a loop-in swap is not stored.
|
||||||
|
ErrLoopInNotFound = errors.New("static address loop-in not found")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Querier is the interface that contains all the queries generated by sqlc for
|
// Querier is the interface that contains all the queries generated by sqlc for
|
||||||
|
|
@ -51,6 +54,11 @@ type Querier interface {
|
||||||
UpdateStaticAddressLoopIn(ctx context.Context,
|
UpdateStaticAddressLoopIn(ctx context.Context,
|
||||||
arg sqlc.UpdateStaticAddressLoopInParams) error
|
arg sqlc.UpdateStaticAddressLoopInParams) error
|
||||||
|
|
||||||
|
// RecordStaticAddressRiskDecision stores the server's confirmation-risk
|
||||||
|
// decision for a loop-in swap.
|
||||||
|
RecordStaticAddressRiskDecision(ctx context.Context,
|
||||||
|
arg sqlc.RecordStaticAddressRiskDecisionParams) error
|
||||||
|
|
||||||
// GetStaticAddressLoopInSwap retrieves a loop-in swap by its swap hash.
|
// GetStaticAddressLoopInSwap retrieves a loop-in swap by its swap hash.
|
||||||
GetStaticAddressLoopInSwap(ctx context.Context,
|
GetStaticAddressLoopInSwap(ctx context.Context,
|
||||||
swapHash []byte) (sqlc.GetStaticAddressLoopInSwapRow, error)
|
swapHash []byte) (sqlc.GetStaticAddressLoopInSwapRow, error)
|
||||||
|
|
@ -361,6 +369,43 @@ func (s *SqlStore) UpdateLoopIn(ctx context.Context,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecordStaticAddressRiskDecision stores the server's confirmation-risk
|
||||||
|
// decision for a static address loop-in. The timestamp is written by the store
|
||||||
|
// so recovery can reconstruct the remaining payment deadline from one durable
|
||||||
|
// clock source.
|
||||||
|
func (s *SqlStore) RecordStaticAddressRiskDecision(ctx context.Context,
|
||||||
|
swapHash lntypes.Hash, decision ConfirmationRiskDecision) error {
|
||||||
|
|
||||||
|
if decision != ConfirmationRiskDecisionAccepted &&
|
||||||
|
decision != ConfirmationRiskDecisionRejected {
|
||||||
|
|
||||||
|
return errors.New("unknown confirmation risk decision")
|
||||||
|
}
|
||||||
|
|
||||||
|
params := sqlc.RecordStaticAddressRiskDecisionParams{
|
||||||
|
SwapHash: swapHash[:],
|
||||||
|
ConfirmationRiskDecision: string(decision),
|
||||||
|
ConfirmationRiskDecisionTime: sql.NullTime{
|
||||||
|
Time: s.clock.Now(),
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.baseDB.ExecTx(ctx, loopdb.NewSqlWriteOpts(),
|
||||||
|
func(q Querier) error {
|
||||||
|
stored, err := q.IsStored(ctx, swapHash[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !stored {
|
||||||
|
return ErrLoopInNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return q.RecordStaticAddressRiskDecision(ctx, params)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SqlStore) BatchUpdateSelectedSwapAmounts(ctx context.Context,
|
func (s *SqlStore) BatchUpdateSelectedSwapAmounts(ctx context.Context,
|
||||||
updateAmounts map[lntypes.Hash]btcutil.Amount) error {
|
updateAmounts map[lntypes.Hash]btcutil.Amount) error {
|
||||||
|
|
||||||
|
|
@ -584,6 +629,9 @@ func toStaticAddressLoopIn(_ context.Context, network *chaincfg.Params,
|
||||||
DepositOutpoints: depositOutpoints,
|
DepositOutpoints: depositOutpoints,
|
||||||
SelectedAmount: btcutil.Amount(swap.SelectedAmount),
|
SelectedAmount: btcutil.Amount(swap.SelectedAmount),
|
||||||
Fast: swap.Fast,
|
Fast: swap.Fast,
|
||||||
|
ConfirmationRiskDecision: ConfirmationRiskDecision(
|
||||||
|
swap.ConfirmationRiskDecision,
|
||||||
|
),
|
||||||
HtlcTxFeeRate: chainfee.SatPerKWeight(
|
HtlcTxFeeRate: chainfee.SatPerKWeight(
|
||||||
swap.HtlcTxFeeRateSatKw,
|
swap.HtlcTxFeeRateSatKw,
|
||||||
),
|
),
|
||||||
|
|
@ -591,6 +639,10 @@ func toStaticAddressLoopIn(_ context.Context, network *chaincfg.Params,
|
||||||
HtlcTimeoutSweepTxHash: htlcTimeoutSweepTxHash,
|
HtlcTimeoutSweepTxHash: htlcTimeoutSweepTxHash,
|
||||||
Deposits: depositList,
|
Deposits: depositList,
|
||||||
}
|
}
|
||||||
|
if swap.ConfirmationRiskDecisionTime.Valid {
|
||||||
|
loopIn.ConfirmationRiskDecisionTime =
|
||||||
|
swap.ConfirmationRiskDecisionTime.Time
|
||||||
|
}
|
||||||
|
|
||||||
if len(updates) > 0 {
|
if len(updates) > 0 {
|
||||||
lastUpdate := updates[len(updates)-1]
|
lastUpdate := updates[len(updates)-1]
|
||||||
|
|
|
||||||
|
|
@ -349,6 +349,83 @@ func TestCreateLoopIn(t *testing.T) {
|
||||||
require.Equal(t, []string{d1.OutPoint.String(), d2.OutPoint.String()},
|
require.Equal(t, []string{d1.OutPoint.String(), d2.OutPoint.String()},
|
||||||
swap.DepositOutpoints)
|
swap.DepositOutpoints)
|
||||||
require.Equal(t, SignHtlcTx, swap.GetState())
|
require.Equal(t, SignHtlcTx, swap.GetState())
|
||||||
|
require.Equal(
|
||||||
|
t, ConfirmationRiskDecisionNone,
|
||||||
|
swap.ConfirmationRiskDecision,
|
||||||
|
)
|
||||||
|
|
||||||
|
decisionTime := time.Unix(123, 0).UTC()
|
||||||
|
testClock.SetTime(decisionTime)
|
||||||
|
err = swapStore.RecordStaticAddressRiskDecision(
|
||||||
|
ctx, swapHashPending, ConfirmationRiskDecisionAccepted,
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
swap, err = swapStore.GetLoopInByHash(ctx, swapHashPending)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(
|
||||||
|
t, ConfirmationRiskDecisionAccepted,
|
||||||
|
swap.ConfirmationRiskDecision,
|
||||||
|
)
|
||||||
|
require.True(t, swap.ConfirmationRiskDecisionTime.Equal(decisionTime))
|
||||||
|
|
||||||
|
// Replaying the same decision must retain its original deadline anchor.
|
||||||
|
laterDecisionTime := decisionTime.Add(time.Hour)
|
||||||
|
testClock.SetTime(laterDecisionTime)
|
||||||
|
err = swapStore.RecordStaticAddressRiskDecision(
|
||||||
|
ctx, swapHashPending, ConfirmationRiskDecisionAccepted,
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
swap, err = swapStore.GetLoopInByHash(ctx, swapHashPending)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(
|
||||||
|
t, ConfirmationRiskDecisionAccepted,
|
||||||
|
swap.ConfirmationRiskDecision,
|
||||||
|
)
|
||||||
|
require.True(t, swap.ConfirmationRiskDecisionTime.Equal(decisionTime))
|
||||||
|
|
||||||
|
// A different decision is a new event and receives a new timestamp.
|
||||||
|
rejectedDecisionTime := laterDecisionTime.Add(time.Hour)
|
||||||
|
testClock.SetTime(rejectedDecisionTime)
|
||||||
|
err = swapStore.RecordStaticAddressRiskDecision(
|
||||||
|
ctx, swapHashPending, ConfirmationRiskDecisionRejected,
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
swap, err = swapStore.GetLoopInByHash(ctx, swapHashPending)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(
|
||||||
|
t, ConfirmationRiskDecisionRejected,
|
||||||
|
swap.ConfirmationRiskDecision,
|
||||||
|
)
|
||||||
|
require.True(t, swap.ConfirmationRiskDecisionTime.Equal(
|
||||||
|
rejectedDecisionTime,
|
||||||
|
))
|
||||||
|
|
||||||
|
// Rejected is terminal: a racing synthetic acceptance must not replace
|
||||||
|
// the server's rejection or move its deadline anchor.
|
||||||
|
testClock.SetTime(rejectedDecisionTime.Add(time.Hour))
|
||||||
|
err = swapStore.RecordStaticAddressRiskDecision(
|
||||||
|
ctx, swapHashPending, ConfirmationRiskDecisionAccepted,
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
swap, err = swapStore.GetLoopInByHash(ctx, swapHashPending)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(
|
||||||
|
t, ConfirmationRiskDecisionRejected,
|
||||||
|
swap.ConfirmationRiskDecision,
|
||||||
|
)
|
||||||
|
require.True(t, swap.ConfirmationRiskDecisionTime.Equal(
|
||||||
|
rejectedDecisionTime,
|
||||||
|
))
|
||||||
|
|
||||||
|
err = swapStore.RecordStaticAddressRiskDecision(
|
||||||
|
ctx, lntypes.Hash{0x9, 0x9, 0x9},
|
||||||
|
ConfirmationRiskDecisionRejected,
|
||||||
|
)
|
||||||
|
require.ErrorIs(t, err, ErrLoopInNotFound)
|
||||||
|
|
||||||
require.Len(t, swap.Deposits, 2)
|
require.Len(t, swap.Deposits, 2)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue