Skip to content
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

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Changes from 3 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
82 changes: 82 additions & 0 deletions text/0000-safe-cancel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@

- Title: safe-cancel
- Authors: [John Tromp](mailto:john.tromp@gmail.com)
- Start date: Oct 10, 2019
- RFC PR: Edit if merged: [mimblewimble/grin-rfcs#0000](https://github.com/mimblewimble/grin-rfcs/pull/0000)
- Tracking issue: [Edit if merged with link to tracking github issue]

---

## Summary
[summary]: #summary

Allow for safe cancellation of pending transactions, preventing future so-called play attacks.

## Motivation
[motivation]: #motivation

A wallet cannot simply cancel a pending transaction but forgetting about it and returning its inputs to the wallet balance.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo but --> by

Especially not when the other party is responsible for completing and broadcasting the transaction.
They may still do so at any time as long as the inputs are not spent differently.

## Community-level explanation
[community-level-explanation]: #community-level-explanation

Suppose Bob sends Alice an invoice slatepack to which Alice responds, but then
Bob doesn't finalize it. He might suggest a problem with the invoice, with his
wallet, or the exchange rate, or the (suddenly realized)
need to pay in a different currency. Whatever the case, he convinces Alice to
have the current transaction cancelled. Alice is fine with that, but needs to
make sure that Bob doesn't later complete the transaction and steal Alice's
inputs.

Alice has two options; spend a transaction input back to herself, or
construct a new transaction that shares at least one input with the
old transaction. The former is cleaner, but requires separate fees, and possibly waiting
for confirmation.

The wallet provides a command for each of these options.

grin-wallet respend [OPTIONS]

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]

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).

Copy link
Member

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?

Copy link
Contributor Author

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?


will generate and broadcast a self spend of an input of
the pending transaction, specified either by ID or TxID UUID,

## Reference-level explanation
[reference-level-explanation]: #reference-level-explanation

## Drawbacks
[drawbacks]: #drawbacks

The user is required to understand the trade-offs between the two types of cancel.

## Rationale and alternatives
[rationale-and-alternatives]: #rationale-and-alternatives

The simpler alternative is to always do a self spend. Giving the user control
over whether to spend extra fees seems preferrable though.

## Prior art
[prior-art]: #prior-art

Coins such as Monero suggest the use of self spends to reduce linkability, but our motivation is quite different,
and with likely no aggregation in the Dandelion stem phase, self-spends in Grin do nothing to reduce linkability.

## Unresolved questions
[unresolved-questions]: #unresolved-questions

## Future possibilities
[future-possibilities]: #future-possibilities

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?

Copy link
Member

@phyro phyro Apr 4, 2021

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:

  1. (SRS/RSR) receiver blocks the tx by creating a tx which reuses output created in step 2 in SRS
  2. (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:

  1. it only works if the output ends on the chain and doesn't work if it is still in the mempool
  2. 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.

Copy link
Contributor Author

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.


## References
[references]: #references

Include any references such as links to other documents or reference implementations.

- [reference 1](link)
- [reference 2](link)