breez / breez-sdk-greenlight

MIT License
241 stars 42 forks source link

swap: Leave some fees to send to the swap service #1056

Closed cdecker closed 3 months ago

cdecker commented 3 months ago

After analyzing a number of failed swaps we found that the sendpay command used to send the HTLC to the swap service does not leave any funds for fees, thus dooming the swap to fail.

The following is a payment that @rnaar and I debugged earlier today:

ERROR: Generic: Failed to pay HODL invoice: 
status: Unknown, 
message: "Error calling method SendPay: RpcError { 
  code: Some(204), 
  message: \\"failed: WIRE_TEMPORARY_CHANNEL_FAILURE (First peer not ready)\\",
  data: Some(Object {
    \\"erring_channel\\": String(\\"827879x2781x0\\"),
    \\"erring_direction\\": Number(0),
    \\"erring_index\\": Number(0),
    \\"erring_node\\": String(\\"02aafc309f67296b1874c4ef4db84a0596b5932526b41f884f3a7751bf2f199708\\"),
    \\"failcode\\": Number(4103),
    \\"failcodename\\": String(\\"WIRE_TEMPORARY_CHANNEL_FAILURE\\")})
}", details: \[\], metadata: MetadataMap { headers: {"content-type": "application/grpc", "date": "Sat, 27 Jul 2024 18:44:04 GMT", "content-length": "0"} }, source: None

Looking into the corresponding session we can see the following logs:

lightningd: Sending 397047000msat over 3 hops to deliver 397047000msat
lightningd: Selected channel 827879x2781x0 (397047862msat) for selector 827879x2781x0 (397047000msat) 

Notice that we are using a channel whose amount, rounded to sat, matches exactly the amount to be sent. This cannot work in routed payments as soon as we have a base fee >0.862sat or a PPM fee of >2.17 sat / Msat.

Please either directly connect to the swap service, in which case a fee-less route is possible, or leave some satoshis for fees.

roeierez commented 3 months ago

Thanks @cdecker for the anlysis. We have a function in the sdk onchain_payment_limits which should return the max amount the user can swap out. we do that by composing the shortest path to the swapper and reverse comuting the fees so the amount the user sees is the channels balance minus the fees. Then we use send_pay to send using this specific route. If the user ignores this value and attempts to send all the balance then might fall into this. Is it connected to any supsport case where I can return to the user and ask for some info? Perhaps relai?

cdecker commented 3 months ago

I see, yes, in that case the behavior is correct, since the SDK accounts for fees. I misinterpreted the amounts matching as the situation at the recipient (sans fees) and not at the sender (with fees). So this is all good, I just got confused.