-
Notifications
You must be signed in to change notification settings - Fork 29
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
[WIP] Safe cancel #71
base: master
Are you sure you want to change the base?
Conversation
Just want to mention that respend doesn't help if you reinstall the wallet before sending the next transaction because the new wallet has no clue about which utxos were marked for respend. That's also true if you use multiple devices since we don't have a shared wallet data |
That's a separate issue. A restored wallet doesn't know what transactions are pending, and will count all their inputs towards its balance. This RFC doesn't deal with that general problem. |
text/0000-safe-cancel.md
Outdated
## Motivation | ||
[motivation]: #motivation | ||
|
||
A wallet cannot simply cancel a pending transaction bu forgetting about it and returning its inputs to the wallet balance. |
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.
typo -> bu
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.
fixed
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.
Linked text doesn't show the fixed version
text/0000-safe-cancel.md
Outdated
## Motivation | ||
[motivation]: #motivation | ||
|
||
A wallet cannot simply cancel a pending transaction but forgetting about it and returning its inputs to the wallet balance. |
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.
Typo but --> by
So the safe-cancel should occur in these cases:
should self-spend be a 1-1 or a 1-2 transaction?
1-2 consequences:
|
A cancel can be done on any pending transaction. In the case of SRS, where sender has signed the transaction and broadcasted it, but it was blocked by the receiver, the status should change from "pending" to a new state "blocked by receiver". Unspends are always self spends, while respends can be any spend, or even a payjoin receive. |
I think it's best to plan this also for the scenario of full blocks. In this case, we'd need to have an option to define the cancel fee to define the priority of a cancel. |
What do you mean by a cancel fee? The fee of an unspend tx? I think a self-spend tx could show up in the wallet as a mark/label on an output, |
When you say mark/label on an output you mean literally on an output (using a new BP bit) or labeling in the wallet context? |
I mean in the wallet. |
will mark the pending transaction, specified either by ID or TxID UUID, | ||
as requiring one of its inputs to be spent in the very next wallet spend. | ||
|
||
grin-wallet unspend [OPTIONS] |
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.
Maybe make respend/unspend
one sub-command with options to choose the transaction-style + fees. Think this would be clearer to the user, and could even have an interactive UI component explaining the tradeoffs (more of an implementation detail).
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'm thinking about this as well. We will have 3 ways to cancel a transaction "cancel, unspend, respend" which might be confusing to the user. Would it make sense to join these commands e.g. ./grin-wallet cancel -unspend
or ./grin-wallet cancel -respend
?
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.
and grin-wallet cancel -unsafe to reproduce current behaviour?
[unresolved-questions]: #unresolved-questions | ||
|
||
## Future possibilities | ||
[future-possibilities]: #future-possibilities |
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.
One possibility that came to mind: mitigate the receiver's ability to block by creating another transaction with the same output. Not sure exactly how this would work, but maybe including in the output some contribution from the sender that can't be included/unlocked by the receiver until the transaction is fully signed.
My understanding of the problem is that the receiver creates/spends a transaction between step 2-3 in SRS flow (1-2 or later in RSR), which spends their output added in step 2. If the sender creates the receiver's output, and includes a contribution (something like a commitment opened at the end of step 3), then the receiver would be unable to create a tx with the duplicate output.
Again, not sure exactly how that would work, since the commitment would need contribution from both sender and receiver, else sender could just steal back the output from the receiver.
What do you think?
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.
There are 2 ways to attack as the receiver:
- (SRS/RSR) receiver blocks the tx by creating a tx which reuses output created in step 2 in SRS
- (RSR) receiver simply does not perform step 3 and can finish it later
If I understand correctly, you're tackling the 1. problem with your idea. I thought about a similar concept a long time ago which I referred to as "unpredictable outputs" where I wanted to randomize the commitments based on the PoW in the block (or block hash) they were included in (you know in which block an output was added so you could get the original commitment back through a simple computation), but this has some obvious downsides:
- it only works if the output ends on the chain and doesn't work if it is still in the mempool
- changing commitments based on PoW is a really bad idea if a reorg happens because you can't replay any transaction
I think your direction of having the output have this unpredictability at the transaction level does not suffer from these two issues and might solve the blocking tx problem (however, the withholding would still be an issue I believe). But I'm not sure how this would be done.
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 think that the receiver in MW should have full control over their outputs, which includes the ability to create duplicates. It doesn't seem too difficult to identify the reason why a published tx fails to confirm.
After some work on atomic signatures, and a conversation with @phyro on keybase, came up with the idea of using a concurrent Thoughts? |
I think it is an interesting idea that deserves to be written down for anyone else reading this discussion in the future. If the |
AFAIK, neither Grin implementation has yet implemented a replace-by-fee policy, so extra fees on a refund tx (the idea of which dates back many years) don't matter much. |
One possible issue is the following scenario. Suppose Alice sends Bob 100k Grin in RSR. Bob at step 3 withholds the transaction and says it doesn't work. Alice performs the safe-cancel and sees the tx on the chain. Now they create another transaction and when Bob gets to step 3, he reorgs the chain to remove the self-spend from Alice and include both of the transactions to double-spend Alice. This could be solved by using |
What does transaction cancel mean it is done by Bob (the receiver) due to Alice (the sender) not finishing the steps? A scenario would be SRS flow where step3 is not done. There are two transaction cases I see:
Handling 1.: The only way to really cancel the transaction from Bob side where he contributed only outputs is to create the same output in a self-spend and never spend it, but we really don't want this. So I assume we handle this by dropping the expected outputs that the wallet is tracking or similar. This is probably an implementation question, but what happens if you remove the expected outputs and the tx later appears on the chain? Handling 2.: This is a more interesting scenario. When Bob cancels a payjoin transaction, does it mean that Bob double-spends the contributed inputs (at least one of them)? |
In case 1, I don't see any need for Bob to cancel the tx, since no funds of his are at stake. In case 2, the funds in his input are still considered part of his balance, and again he is not at risk of losing funds. The only risk is this output getting doublespent when Bob cancels with a respend |
I think we could make it a 2-step process. The main difference between respend/unspend is the speed at which they execute (fees are much less important imo). Both do the exact same thing. They first mark a transaction for cancellation and at some point in the future, a transaction we create consumes an input of this transaction. We can thus simplify these to:
This means that safe-cancel can be just Separating marking from the respend itself might also be a better option for services consuming this API because you don't need a separate transaction for every cancelled transaction. You can mark multiple transaction for respending and then create a self-spend to consume all of them at once. Thus a fast respend is just a Edit: Hmm, while this would allow to batch cancel transactions in a single transaction, it might be a bit more complex handling a failure of this respend e.g. one of the cancel transactions actually lands on the chain before it is cancelled. |
Cancel pools are a nice idea, but when you run
|
Thanks for the comments and you have some good points, especially that it may complicate the handling when one of the transactions appears on the chain (though I'm not yet sure it does). However, observing this self-spend transaction and handling possible cases is unavoidable, even in the case of |
yes, it would show as "cancelling", but in case of unspend you know that this cancelling is short-lived. It can turn into "completed" though yes if the original tx gets mined.
only set status in db as "canceled" and unlock any locked inputs, same as cancel does today
for each tx you cancel new "canceling tx" is created. I'm not sure if the user should see these "cancel-txs" or not, probably not. |
The best would be to show the original transaction, labelled as 'cancelled safely', or something similar. So basically you combine the safe cancelling transaction with the original, at least in the way that it is presented to the user. |
Cancel safely in face of play attacks.
Link to rendered text