Skip to content

Commit

Permalink
swap: restored sanity check
Browse files Browse the repository at this point in the history
Restored sanity check of the maximum htlc limit.
* To check max htlc more strictly than CLN's own limits.
* To prevents a tiny bit of database bloat
in situations where it is guaranteed to fail.
  • Loading branch information
YusukeShimizu committed Nov 26, 2023
1 parent 5c30387 commit 5d79e9e
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 0 deletions.
29 changes: 29 additions & 0 deletions clightning/clightning.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,35 @@ func min(x, y uint64) uint64 {
return y
}

// SpendableMsat returns an estimate of the total we could send through the
// channel with given scid. Falls back to the owned amount in the channel.
func (cl *ClightningClient) SpendableMsat(scid string) (uint64, error) {
scid = lightning.Scid(scid).ClnStyle()
var res ListPeerChannelsResponse
err := cl.glightning.Request(ListPeerChannelsRequest{}, &res)
if err != nil {
return 0, err
}
for _, ch := range res.Channels {
if ch.ShortChannelId == scid {
if err = cl.checkChannel(ch); err != nil {
return 0, err
}
maxHtlcAmtMsat, err := cl.getMaxHtlcAmtMsat(scid, cl.nodeId)
if err != nil {
return 0, err
}
// since the max htlc limit is not always set reliably,
// the check is skipped if it is not set.
if maxHtlcAmtMsat == 0 {
return ch.GetSpendableMsat(), nil
}
return min(maxHtlcAmtMsat, ch.GetSpendableMsat()), nil
}
}
return 0, fmt.Errorf("could not find a channel with scid: %s", scid)
}

// ReceivableMsat returns an estimate of the total we could receive through the
// channel with given scid.
func (cl *ClightningClient) ReceivableMsat(scid string) (uint64, error) {
Expand Down
37 changes: 37 additions & 0 deletions lnd/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,43 @@ func min(x, y uint64) uint64 {
return y
}

// SpendableMsat returns an estimate of the total we could send through the
// channel with given scid.
func (l *Client) SpendableMsat(scid string) (uint64, error) {
s := lightning.Scid(scid)
r, err := l.lndClient.ListChannels(context.Background(), &lnrpc.ListChannelsRequest{
ActiveOnly: false,
InactiveOnly: false,
PublicOnly: false,
PrivateOnly: false,
})
if err != nil {
return 0, err
}
for _, ch := range r.Channels {
channelShortId := lnwire.NewShortChanIDFromInt(ch.ChanId)
if channelShortId.String() == s.LndStyle() {
if err = l.checkChannel(ch); err != nil {
return 0, err
}
maxHtlcAmtMsat, err := l.getMaxHtlcAmtMsat(ch.ChanId, l.pubkey)
if err != nil {
return 0, err
}
spendable := (uint64(ch.GetLocalBalance()) -
ch.GetLocalConstraints().GetChanReserveSat()*1000)
// since the max htlc limit is not always set reliably,
// the check is skipped if it is not set.
if maxHtlcAmtMsat == 0 {
return spendable, nil
}
return min(maxHtlcAmtMsat, spendable), nil

}
}
return 0, fmt.Errorf("could not find a channel with scid: %s", scid)
}

// ReceivableMsat returns an estimate of the total we could receive through the
// channel with given scid.
func (l *Client) ReceivableMsat(scid string) (uint64, error) {
Expand Down
8 changes: 8 additions & 0 deletions swap/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,14 @@ func (r *PayFeeInvoiceAction) Execute(services *SwapServices, swap *SwapData) Ev
if err != nil {
return swap.HandleError(err)
}
sp, err := ll.SpendableMsat(swap.SwapOutRequest.Scid)
if err != nil {
return swap.HandleError(err)
}

if sp <= swap.SwapOutRequest.Amount*1000 {
return swap.HandleError(err)
}
success, failureReason, err := ll.ProbePayment(swap.SwapOutRequest.Scid, swap.SwapOutRequest.Amount*1000)
if err != nil {
return swap.HandleError(err)
Expand Down
32 changes: 32 additions & 0 deletions swap/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,14 @@ func (s *SwapService) SwapOut(peer string, chain string, channelId string, initi
return nil, err
}

sp, err := s.swapServices.lightning.SpendableMsat(channelId)
if err != nil {
return nil, err
}
if sp <= amtSat*1000 {
return nil, fmt.Errorf("exceeding spendable amount_msat: %d", sp)
}

success, failureReason, err := s.swapServices.lightning.ProbePayment(channelId, amtSat*1000)
if err != nil {
return nil, err
Expand Down Expand Up @@ -506,6 +514,30 @@ func (s *SwapService) OnSwapInRequestReceived(swapId *SwapId, peerId string, mes
return err
}

sp, err := s.swapServices.lightning.SpendableMsat(message.Scid)
if err != nil {
msg := fmt.Sprintf("from the %s peer: %s", s.swapServices.lightning.Implementation(), err.Error())
// We want to tell our peer why we can not do this swap.
msgBytes, msgType, err := MarshalPeerswapMessage(&CancelMessage{
SwapId: swapId,
Message: msg,
})
s.swapServices.messenger.SendMessage(peerId, msgBytes, msgType)
return err
}

if sp <= message.Amount*1000 {
err = fmt.Errorf("exceeding spendable amount_msat: %d", sp)
msg := fmt.Sprintf("from the %s peer: %s", s.swapServices.lightning.Implementation(), err.Error())
// We want to tell our peer why we can not do this swap.
msgBytes, msgType, err := MarshalPeerswapMessage(&CancelMessage{
SwapId: swapId,
Message: msg,
})
s.swapServices.messenger.SendMessage(peerId, msgBytes, msgType)
return err
}

success, failureReason, err := s.swapServices.lightning.ProbePayment(message.Scid, message.Amount*1000)
if err != nil {
msg := fmt.Sprintf("from the %s peer: %s", s.swapServices.lightning.Implementation(), err.Error())
Expand Down
1 change: 1 addition & 0 deletions swap/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type LightningClient interface {
RebalancePayment(payreq string, channel string) (preimage string, err error)
CanSpend(amountMsat uint64) error
Implementation() string
SpendableMsat(scid string) (uint64, error)
ReceivableMsat(scid string) (uint64, error)
ProbePayment(scid string, amountMsat uint64) (bool, string, error)
}
Expand Down

0 comments on commit 5d79e9e

Please sign in to comment.