Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions cmd/loop/quote.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,23 +229,22 @@ func printQuoteInResp(req *looprpc.QuoteRequest,
resp *looprpc.InQuoteResponse, verbose bool) {

totalFee := resp.HtlcPublishFeeSat + resp.SwapFeeSat
amt := req.Amt
if amt == 0 {
amt = resp.QuotedAmt
}

if req.DepositOutpoints != nil {
if req.Amt == 0 {
fmt.Printf(satAmtFmt, "Previously deposited "+
"on-chain:", resp.QuotedAmt)
} else {
fmt.Printf(satAmtFmt, "Previously deposited "+
"on-chain:", req.Amt)
}
fmt.Printf(satAmtFmt, "Previously deposited on-chain:",
amt)
} else {
fmt.Printf(satAmtFmt, "Send on-chain:", req.Amt)
fmt.Printf(satAmtFmt, "Send on-chain:", amt)
}
fmt.Printf(satAmtFmt, "Receive off-chain:", req.Amt-totalFee)
fmt.Printf(satAmtFmt, "Receive off-chain:", amt-totalFee)

switch {
case req.ExternalHtlc && !verbose:
// If it's external then we don't know the miner fee hence the
// If it's external, then we don't know the miner fee hence the
// total cost.
fmt.Printf(satAmtFmt, "Loop service fee:", resp.SwapFeeSat)

Expand Down
11 changes: 11 additions & 0 deletions staticaddr/loopin/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ import (
"github.com/lightningnetwork/lnd/zpay32"
)

const (
// DefaultLoopInOnChainCltvDelta is the time lock relative to current
// block height that swap server will accept on the swap initiation
// call.
DefaultLoopInOnChainCltvDelta = 1000

// DepositHtlcDelta is a safety buffer of blocks that needs to exist
// between the deposit expiry height and the htlc expiry height.
DepositHtlcDelta = 50
)

type (
// ValidateLoopInContract validates the contract parameters against our
// request.
Expand Down
40 changes: 38 additions & 2 deletions staticaddr/loopin/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -862,8 +862,25 @@ func (m *Manager) GetAllSwaps(ctx context.Context) ([]*StaticAddressLoopIn,
// are needed to cover the amount requested without 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, deposits []*deposit.Deposit,
csvExpiry uint32, blockHeight uint32) ([]*deposit.Deposit, error) {
func SelectDeposits(targetAmount btcutil.Amount,
unfilteredDeposits []*deposit.Deposit, csvExpiry uint32,
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 {
if !IsSwappable(
uint32(d.ConfirmationHeight), blockHeight, csvExpiry,
) {

log.Debugf("Skipping deposit %s as it expires before "+
"the htlc", d.OutPoint.String())

continue
}

deposits = append(deposits, d)
}

// Sort the deposits by amount in descending order, then by
// blocks-until-expiry in ascending order.
Expand Down Expand Up @@ -901,6 +918,25 @@ func SelectDeposits(targetAmount btcutil.Amount, deposits []*deposit.Deposit,
selectedAmount, targetAmount)
}

// IsSwappable checks if a deposit is swappable. It returns true if the deposit
// is not expired and the htlc is not too close to expiry.
func IsSwappable(confirmationHeight, blockHeight, csvExpiry uint32) bool {
// The deposit expiry height is the confirmation height plus the csv
// expiry.
depositExpiryHeight := confirmationHeight + csvExpiry

// The htlc expiry height is the current height plus the htlc
// cltv delta.
htlcExpiryHeight := blockHeight + DefaultLoopInOnChainCltvDelta

// Ensure that the deposit doesn't expire before the htlc.
if depositExpiryHeight < htlcExpiryHeight+DepositHtlcDelta {
return false
}

return true
}

// DeduceSwapAmount calculates the swap amount based on the selected amount and
// the total deposit amount. It checks if the selected amount leaves a dust
// change output or exceeds the total deposits value. Note that if the selected
Expand Down
45 changes: 41 additions & 4 deletions staticaddr/loopin/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@ type testCase struct {
func TestSelectDeposits(t *testing.T) {
d1, d2, d3, d4 := &deposit.Deposit{
Value: 1_000_000,
ConfirmationHeight: 1000,
ConfirmationHeight: 5_000,
}, &deposit.Deposit{
Value: 2_000_000,
ConfirmationHeight: 2000,
ConfirmationHeight: 5_001,
}, &deposit.Deposit{
Value: 3_000_000,
ConfirmationHeight: 3000,
ConfirmationHeight: 5_002,
}, &deposit.Deposit{
Value: 3_000_000,
ConfirmationHeight: 3001,
ConfirmationHeight: 5_003,
}
d1.Hash = chainhash.Hash{1}
d1.Index = 0
Expand Down Expand Up @@ -121,6 +121,43 @@ func TestSelectDeposits(t *testing.T) {
expected: []*deposit.Deposit{d3},
expectedErr: "",
},
{
name: "prefilter filters deposits close to expiry",
deposits: func() []*deposit.Deposit {
// dClose expires before
// htlcExpiry+DepositHtlcDelta and must be
// filtered out. dOK expires exactly at the
// threshold and must be eligible.
dClose := &deposit.Deposit{
Value: 3_000_000,
ConfirmationHeight: 3000,
}
dClose.Hash = chainhash.Hash{5}
dClose.Index = 0
dOK := &deposit.Deposit{
Value: 2_000_000,
ConfirmationHeight: 3050,
}
dOK.Hash = chainhash.Hash{6}
dOK.Index = 0
return []*deposit.Deposit{dClose, dOK}
}(),
targetValue: 1_000_000,
csvExpiry: 1000,
blockHeight: 3000,
expected: func() []*deposit.Deposit {
// Only dOK should be considered.
// dClose is filtered.
dOK := &deposit.Deposit{
Value: 2_000_000,
ConfirmationHeight: 3050,
}
dOK.Hash = chainhash.Hash{6}
dOK.Index = 0
return []*deposit.Deposit{dOK}
}(),
expectedErr: "",
},
}

for _, tc := range testCases {
Expand Down