-
-
Notifications
You must be signed in to change notification settings - Fork 114
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sender fallbacks #1642
Sender fallbacks #1642
Conversation
d75da15
to
989ab5a
Compare
7918299
to
10632fd
Compare
b5bdf36
to
765d9f3
Compare
if (!action.paymentMethods.includes(PAID_ACTION_PAYMENT_METHODS.OPTIMISTIC)) { | ||
throw new Error(`retryPaidAction - action does not support optimism ${actionType}`) | ||
} | ||
|
||
if (!action.retry) { | ||
throw new Error(`retryPaidAction - action does not support retrying ${actionType}`) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So far, I think it's okay to remove these checks to allow retries for pessimistic actions but I will make more sure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see no problem with removing these. Retries for optimistic actions work like before and pessimistic actions support retries as expected now.
components/use-paid-mutation.js
Outdated
walletError = err | ||
// wallet payment error handling always creates a new invoice to retry | ||
// unless no wallet was even able to attempt a payment | ||
if (err.newInvoice) walletInvoice = err.newInvoice |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is maybe the weirdest part of this implementation of sender fallbacks: using an error to pass the new invoice to the calling context
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is very strange indeed, the newInvoice is not logically a property of the error.
Can't you extract the part that recreates the invoice or return it from waitForWalletPayment ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't you extract the part that recreates the invoice or return it from waitForWalletPayment ?
I don't think so. Even if I extract it, I still need to pass the latest invoice to the calling context in some way. I can't use return since that would mean the wallet payment succeeded which isn't the case here.
I do return the latest invoice on success though, see the waitForPayment
call for pessimistic actions below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might seem unimportant in the context this function is used, but these are things that build up technical debt, since now you have a function that leaks invoices, unless you remember that unexpectedly the error contains a new resource you need to cancel if you don't need it, that's why passing around new data in an error field is generally bad practice, errors should report only what and why something failed.
But, unless i'm missing something in the logic, I think you can make a proper error object simply by not recreating the last invoice but just cancelling it and setting a failedInvoice
field in the error. Then when you catch it in waitForPayment
you can recreate it with await invoiceHelper.retry(error.failedInvoice)
only if it is needed.
eg. if skipQr
is true, you won't even need to recreate it, right?
Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I had it like this before but it looked awkward to do something different on the last payment attempt error (not create new invoice) so I changed it to this with less code.
But the argument about "new data" is a good one, I'll change it back to only save the failed invoice in the error and the caller can retry if they want so. Thanks!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did this in 96f76a9.
I realized that I can filter for only the wallets that are actually able to attempt a payment so the logic if there is another wallet we can try is indeed basically just if (i === wallets.length - 1)
. For some reason I didn't think the filter would work so I didn't even try and I think that's why I also didn't do it like this before.
This means the checks in sendPayment
aren't really required anymore so maybe we should remove them 🤔
7636a99
to
4a2355a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this implementation of retryPaidAction doesn't work for p2p actions, it will turn them to pessimistic actions that pay SN.
components/use-paid-mutation.js
Outdated
walletError = err | ||
// wallet payment error handling always creates a new invoice to retry | ||
// unless no wallet was even able to attempt a payment | ||
if (err.newInvoice) walletInvoice = err.newInvoice |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is very strange indeed, the newInvoice is not logically a property of the error.
Can't you extract the part that recreates the invoice or return it from waitForWalletPayment ?
Oh, I didn't test p2p zaps. Thanks, will check and fix! |
4a2355a
to
96f76a9
Compare
Need to change some more stuff like the flow when no wallet is available for 96f76a9. Will put in draft until this is ready for review again. |
7ce033e
to
2398491
Compare
try { | ||
const lnc = await getLNC(credentials, { logger }) | ||
const { paymentError, paymentPreimage: preimage } = await lnc.lnd.lightning.sendPaymentSync({ payment_request: bolt11 }) | ||
if (paymentError) throw new Error(paymentError) | ||
if (!preimage) throw new Error('No preimage in response') | ||
return preimage | ||
} catch (err) { | ||
const msg = err.message || err.toString?.() | ||
if (msg.includes('invoice expired')) throw new InvoiceExpiredError(hash) | ||
if (msg.includes('canceled')) throw new InvoiceCanceledError(hash) | ||
throw err | ||
} | ||
const lnc = await getLNC(credentials, { logger }) | ||
const { paymentError, paymentPreimage: preimage } = await lnc.lnd.lightning.sendPaymentSync({ payment_request: bolt11 }) | ||
if (paymentError) throw new Error(paymentError) | ||
if (!preimage) throw new Error('No preimage in response') | ||
return preimage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the try/catch
here since we already have error handling in waitForPayment
(which also checks for canceled or expired invoices) and in sendPayment
which wrapps wallet.def.sendPayment
.
I think this was just old code that we no longer need.
2398491
to
7a8db53
Compare
Fixed now. The code now retries the same receiver wallet if the forward didn't fail. If the forward failed, we immediately fallback to the SN node but we will try the next sender wallet. I can see more clearly now why it makes sense to do sender + receiver fallbacks in one PR. The difference between both becomes blurry when we start to look into good error handling. However, I think this is still an improvement that we can review and ship on its own. But with the |
Cool, I'm going to try to refactor out the array of |
Main thing I did in b608fb6 was
I'm not sure how generically we'll be able to apply this pattern but it works here. Also (pats back):
|
df72a13
to
f9169c6
Compare
Is this good to merge? I noticed you added another It might be a consequence of my refactor, but because state is kept in memory, retries don't really work after navigation. I don't think we'll be able to rely on memory post-auto-retries anyways, so I don't think that's blocking merging this. It's a strict improvement. Also sorry about the renaming and |
I looked into the zap retry with fallback cache issue some. It's not clear why it's no longer working but these retry cache updates were the hardest part of my initial retry work. The item act retry and item retry are quite similar though - odd it's just affecting one of them. The only thing I can thing of is that the retry mutation in useWalletPayment might be conflicting with the cache update |
Do we always lose hmacs? This is how we authenticate cancelations. If that's inconsequential, this is ready to merge afaict. |
Only in local storage and if we use attached wallets which poll invoices and return the latest state (= without hmac). Since anons don't have attached wallets, this doesn't matter for them (for now at least).
Afaik, cancels between retries use the original invoice state so they work. Since there was no error regarding cancels, I think they work but maybe good to make sure but I am not at home right now.
This is also my view |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feel free to merge into master if there aren't any issues remaining.
Description
Partially solve #1494 for sender side
Based on #1651 #1660
TODO
sendPayment
: wallets that are enabled but can only receive will throwSenderError
instead ofWalletNotConfiguredError
fix hmacs lostdoesn't matter, anon is not affectedManual Tests
Screenshots
how errors from one or multiple wallets will now be shown:
Additional Context
I think the only reason we only allowed retries for optimistic actions was because retries weren't necessary yet for pessimistic actions.
We also should probably move all code from components/payment.js into wallets/payment.js but I didn't to not create more noisy changes than necessary.
Checklist
Are your changes backwards compatible? Please answer below:
tbd
On a scale of 1-10 how well and how have you QA'd this change and any features it might affect? Please answer below:
tbd
For frontend changes: Tested on mobile, light and dark mode? Please answer below:
tbd
Did you introduce any new environment variables? If so, call them out explicitly here:
no, not planned