staticaddr/loopin: persist risk decisions

Store the server's static loop-in confirmation-risk decision and the time it
was received. This lets recovered swaps reconstruct whether payment waiting had
already started and how much of the payment timeout remains.

Wire notification handling to persist accepted and rejected decisions before
caching and forwarding them. If the swap row is not present yet, the
notification is still cached so the per-swap waiter can replay and store the
decision later.

Recover accepted decisions by starting the payment deadline from the persisted
decision time, and recover rejected decisions by canceling the invoice and
failing the swap instead of waiting forever.
This commit is contained in:
Slyghtning 2026-05-15 14:54:51 +02:00
parent 7b29d9215e
commit 325545143c
No known key found for this signature in database
GPG key ID: F82D456EA023C9BF
16 changed files with 934 additions and 103 deletions

View file

@ -153,7 +153,7 @@ func (q *Queries) GetLoopInSwapUpdates(ctx context.Context, swapHash []byte) ([]
const getStaticAddressLoopInSwap = `-- name: GetStaticAddressLoopInSwap :one
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,
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
FROM
swaps
@ -166,36 +166,38 @@ WHERE
`
type GetStaticAddressLoopInSwapRow struct {
ID int32
SwapHash []byte
Preimage []byte
InitiationTime time.Time
AmountRequested int64
CltvExpiry int32
MaxMinerFee int64
MaxSwapFee int64
InitiationHeight int32
ProtocolVersion int32
Label string
ID_2 int32
SwapHash_2 []byte
SwapInvoice string
LastHop []byte
PaymentTimeoutSeconds int32
QuotedSwapFeeSatoshis int64
DepositOutpoints string
HtlcTxFeeRateSatKw int64
HtlcTimeoutSweepTxID sql.NullString
HtlcTimeoutSweepAddress string
SelectedAmount int64
Fast bool
SwapHash_3 []byte
SenderScriptPubkey []byte
ReceiverScriptPubkey []byte
SenderInternalPubkey []byte
ReceiverInternalPubkey []byte
ClientKeyFamily int32
ClientKeyIndex int32
ID int32
SwapHash []byte
Preimage []byte
InitiationTime time.Time
AmountRequested int64
CltvExpiry int32
MaxMinerFee int64
MaxSwapFee int64
InitiationHeight int32
ProtocolVersion int32
Label string
ID_2 int32
SwapHash_2 []byte
SwapInvoice string
LastHop []byte
PaymentTimeoutSeconds int32
QuotedSwapFeeSatoshis int64
DepositOutpoints string
HtlcTxFeeRateSatKw int64
HtlcTimeoutSweepTxID sql.NullString
HtlcTimeoutSweepAddress string
SelectedAmount int64
Fast bool
ConfirmationRiskDecision string
ConfirmationRiskDecisionTime sql.NullTime
SwapHash_3 []byte
SenderScriptPubkey []byte
ReceiverScriptPubkey []byte
SenderInternalPubkey []byte
ReceiverInternalPubkey []byte
ClientKeyFamily int32
ClientKeyIndex int32
}
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.SelectedAmount,
&i.Fast,
&i.ConfirmationRiskDecision,
&i.ConfirmationRiskDecisionTime,
&i.SwapHash_3,
&i.SenderScriptPubkey,
&i.ReceiverScriptPubkey,
@ -239,7 +243,7 @@ func (q *Queries) GetStaticAddressLoopInSwap(ctx context.Context, swapHash []byt
const getStaticAddressLoopInSwapsByStates = `-- name: GetStaticAddressLoopInSwapsByStates :many
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,
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
FROM
swaps
@ -263,36 +267,38 @@ ORDER BY
`
type GetStaticAddressLoopInSwapsByStatesRow struct {
ID int32
SwapHash []byte
Preimage []byte
InitiationTime time.Time
AmountRequested int64
CltvExpiry int32
MaxMinerFee int64
MaxSwapFee int64
InitiationHeight int32
ProtocolVersion int32
Label string
ID_2 int32
SwapHash_2 []byte
SwapInvoice string
LastHop []byte
PaymentTimeoutSeconds int32
QuotedSwapFeeSatoshis int64
DepositOutpoints string
HtlcTxFeeRateSatKw int64
HtlcTimeoutSweepTxID sql.NullString
HtlcTimeoutSweepAddress string
SelectedAmount int64
Fast bool
SwapHash_3 []byte
SenderScriptPubkey []byte
ReceiverScriptPubkey []byte
SenderInternalPubkey []byte
ReceiverInternalPubkey []byte
ClientKeyFamily int32
ClientKeyIndex int32
ID int32
SwapHash []byte
Preimage []byte
InitiationTime time.Time
AmountRequested int64
CltvExpiry int32
MaxMinerFee int64
MaxSwapFee int64
InitiationHeight int32
ProtocolVersion int32
Label string
ID_2 int32
SwapHash_2 []byte
SwapInvoice string
LastHop []byte
PaymentTimeoutSeconds int32
QuotedSwapFeeSatoshis int64
DepositOutpoints string
HtlcTxFeeRateSatKw int64
HtlcTimeoutSweepTxID sql.NullString
HtlcTimeoutSweepAddress string
SelectedAmount int64
Fast bool
ConfirmationRiskDecision string
ConfirmationRiskDecisionTime sql.NullTime
SwapHash_3 []byte
SenderScriptPubkey []byte
ReceiverScriptPubkey []byte
SenderInternalPubkey []byte
ReceiverInternalPubkey []byte
ClientKeyFamily int32
ClientKeyIndex int32
}
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.SelectedAmount,
&i.Fast,
&i.ConfirmationRiskDecision,
&i.ConfirmationRiskDecisionTime,
&i.SwapHash_3,
&i.SenderScriptPubkey,
&i.ReceiverScriptPubkey,
@ -482,6 +490,26 @@ func (q *Queries) OverrideSelectedSwapAmount(ctx context.Context, arg OverrideSe
return err
}
const recordStaticAddressRiskDecision = `-- name: RecordStaticAddressRiskDecision :exec
UPDATE static_address_swaps
SET
confirmation_risk_decision = $2,
confirmation_risk_decision_time = $3
WHERE
swap_hash = $1
`
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
SELECT
swap_hash