Let's create the ultimate Grin Wallet experience! (Grin++ UX/UI)

Sweet thanks, thinking about it this way really helped me understand the process. I have been thinking sender was signing on the first round.

I can understand some people’s reservation with the use of “contract,” But similarly it has a connotation of security and finality.

All things considered I would go with proposal, sign, sign, and then still with some kind of graphical interface to “hide” The slate pack altogether, as suggested in the linked thread

Self-descriptive UI (SRS flow)

I decided to draw a bit. I think these screens could describe better the process and maybe it feels natural for Grin. I think this flow is more descriptive and clear than trying to do everything under the hood, also I think this could also be implemented on the CLI easly.

This is the first step, to fill the receiver address, the amount and the strategy.


*(The Receiver’s address is not mandatory but recommended)

Now, as is defined in the RFC015, if an address is provided, we try to establish a connection via Tor. A happy path would look like this:

If something fails, we should manually complete the transaction, maybe like this:


From here the Sender can either Cancel the transaction or try to complete the transaction manually.

Now the user can import the Transaction agreement signed by Receiver.

The Transaction is now Complete.

Auto-Finalize (?) Flag

If we want to go with this flow, we might need to add an extra flag to the response from the Receiver. This can an optional configuration, true by default. This also will help if we want to have multiple addresses listening at the same time. I will go in detail later.

SRS flowchart

A simple SRS flow would look like this.

6 Likes

Receiver-Sender-Receiver flow

Invoice transactions are built much the same way, but with a different order where the receiver initiates the transaction by asking for a certain amount of coins.

  1. A slatepack address is provided by the sender (optional).
  2. Receiver creates an invoice, requesting to be paid the specified amount. In practice he starts building the transaction slate and writes a pre-determined amount into it.
  3. Sender decrypts the slate to confirm the amount he is about to pay, and adds his own data and signature.
  4. Receiver finalizes the transaction building process and posts it to the chain.

You can see this flow in action here: Possible interactive transaction flow

Receiver

The first step is to create an invoice, which again, it is an unsigned transaction agreement too since the sender must agree on the amount to send.

Now, the receiver can share the invoice with the payer or sender. From here, the receiver can either cancel the invoice or continue to collect the payment.

To collect the payment the receiver just need to import the agreement signed by the sender and sign it.

If everything goes well, the payment is collected by the receiver.

Sender

For the sender, things are more easy, the sender just need to accept, add signature and send back the transaction agreement.

RSR flowchart

A simple RSR flow would look like this.

5 Likes

NOTE: If someone wants to add/edit something to the draws, you can download the mockups from here: misc/ – Keybase.pub and install Pencil to modify it.

1 Like

Thanks for taking the time and creating these mocks!
SRS:

  • in step 1 I would probably put strategy under some advanced section (so hide it away from 99% of the users who don’t need this) and only if u click it you can see strategy and in the future probably some other settings as well. Should the input list be displayed when the default strategy is selected?
  • not sure whether we will ever support manual confirmations but if we do, it might be that Sending Unsigned Transaction Agreement went through, but the response from receiver’s wallet is Waiting for user's manual acceptance
  • how does the sender import the receiver’s signed transaction? Does he have a global import or does he click on the related transaction (from some tx list) and then click continue tx or smth?
  • in the flow does Is Address a valid Grin Address mean that it also needs to be reachable through TOR?

RSR:

  • I would probably have RSR go through TOR and business would create a memo committing to the content of the payment (explained a bit more here)

You can ignore the memo/manual confirmations if we don’t go with it

1 Like

Let’s think about when we use cash to pay for a coffee… what do I do? first, I open my wallet to get a $5 bill to pay for the coffee, then I realize I’m poor so I have no money in my wallet, then in a desperate move I check my pockets and I find an old bill (thank God :raised_hands:), I take the bill from my pocket, the cashier double-checks the bill (with all reasons), accepts it, and keeps the bill.

What did we do? Interact with each other, and if we want to emulate that, wouldn’t it be nice if could “establish the communication” with the cashier in the safest way possible?

Interactivity… well, what can I say about interactivity that hasn’t been said before? I personally have a mixed feeling when it comes to the Interactivity discussion… but if we agree on that one of the Grin’s purposes is to serve as digital cash, interactivity is actually a core characteristic of all transactions based on cash. So, for the sake of this experiment I’m going to assume that Grin is like Cash and then I’m going to (re)imagine the flow from a Receiver perspective.

How should receiving grin look like?

Grin uses Tor to establish a communication between Sender and Receiver, something like: Channel?Tunnel? Avenue? Route? Canal?

I will discard the word “Channel” since it can be confused with Payment Channels, which allow users to make multiple transactions without commiting all of the transactions to the blockchain. I personally like: Route, because we can say something like this then:

Grin uses Addresses to encrypt the transaction information and to establish a secure route via the Tor network, where Sender and Receiver can anonymously communicate with each other.

And let’s be honest, that’s a really cool feature! With that in mind I can make the next assumptions:

  • Addresses could be disposable.
  • Reusing an address should be the exception, not the rule.
  • Sometimes I would like to be able to run several “listeners” or “encrypted routes” or addresses at the same time.

It doesn’t make sense not to generate a new address every time I receive some grin, but I get if you don’t want to. We could even generate a new one after every send, but it would be a big mess. We want to provide users with flexibility without making things confusing.

Now we can think in these terms: Disposable addresses and Reusable addresses.

Disposable means that I can not use that address anymore after completing the receiving process because it is a one-time use address. Reusable means that I can use this address at any moment. For example, If I’m mining in two different pools, I can create two addresses, one for one pool and another one for another pool and I could start an encrypted route for each address at the same time.

But, we also want, one, to keep the compatibility for current users, and second, we want to keep things simple for those who don’t care about these things. In this case we can have a Default address, this address is always listening by default, and then it can be replaced manually by a new one.

“Ok, David, but what about the Dust?” you might ask; Jameson Lopp recently wrote an article explaining the Bitcoin Dust Attack, I will cite him for the sake of giving context.

What Is Dust?

Bitcoin dust refers to UTXOs with tiny values that are so small that they are economically unspendable. That is, it costs more (in transaction fees) to spend than the value of the UTXOs. It’s possible to end up with dust in your wallet due to poor UTXO management. It’s also possible to receive dust deposits that you did not request. This is because you can’t stop someone from sending funds to a valid bitcoin address and every bitcoin address that has ever received funds is publicly viewable on the blockchain.

He also said that “while it’s technically possible it’s incredibly unlikely.”, I also believe that but anyways we can solve this in Grin by:

  • Manually deactivating a listener and generating a new address.
  • Disabling the “auto finalize” behavior.
  • Rejecting transactions below an amount.

This can be also solved in a non-interactive scheme, but the point that I would like to make is that it can also be easily solved with the interactivity.

Do we want wallets to automatically generate a hierarchical tree-like structure of private/public key pairs? Probably, yes.

Do we need on-chain bookkeeping? I’m not sure what pains we are solving with that.

Do we want all enabled addresses to be listening automatically? I don’t think so.

Do we want any of our wallet to be automatically on? I’m not sure, maybe I just want to transact off-line.

I feel it worth experimenting with this. I think about this as an opt-in feature, people are not forced to make use of these capabilities but they could at any moment.

Screens

Initially the screen could look like this
addresses_001

One could manage the have extra addresses like this.
addresses_002

7 Likes

I like it, except that I am favour of putting reusable adresses under an account.
E.g

  • m/0/0/1’/1’ bet1
  • m/0/0/1’/2 bet2

In this way even with 1000 bet adresses, the user only has to remember to ditch that 1 account branch. Even without online bookkeeping reasonably safe.

Having reusable and disposable addresses also looks like a step in the right direction. I don’t have an opinion on how to achieve this as I have not put much thought into this yet, but I’ll add some comments about dust attacks. I’ve referred to these as output injection attack rather than dust attack because it doesn’t seem necessary the attacker adds minimal value to the output.
So far, I’ve seen only one actual solution for output injection attacks and it’s based on some form of receiver confirmation/interactivity. Setting up a threshold which serves as a point below which we label outputs as dust isn’t solving the problem, it merely makes the attack more expensive. In order to really solve the output injection, you need to tell what you want to have in your wallet. There are two ways to do that:

  1. before receiving the coins
    This means that the user has to manually confirm the step2 in either flow - I’m pretty sure the finalize step can always be automated.
  2. after receiving the coins
    In this case, the user needs to select which outputs they consider valid. This is again a requirement to confirm the outputs.

Whether you’re battling identity/outputs correlation, spam or something else, I don’t see any other complete solution than to select what you own, either before or after you’ve received money. I’ve made a gist about output injection a few months ago that talks about these two a bit more.

I think transaction level confirmation at step2 solves four problems at once:

  1. We gain control over incoming money into the wallet (I think that’s unique to MW because in pure MW it’s impossible to noninteractively send money) which solves output injection.
  2. We gain the ability for both parties to verify the amount in the transaction. If the sender or the receiver mistyped the amount either accidentally or purposedly hoping the other party auto-signs, the other party can catch that and stop the transaction.
  3. We gain the ability for both the sender and the receiver to commit to an arbitrary statement/document with our transaction. This is because they both can read what they’re commiting to before they confirm (sign) the transaction.
  4. This may make transaction cancellation more intuitive because the person would actually sign the transaction with their confirmation. Auto-signing makes it seem as if the signing doesn’t exist and hence it’s completely unintuitive when someone should cancel a transaction.

Some people could be annoyed because they’d need to click on the “confirm” button at step2 (or even better, input the amount they expect), but the benefits of added control on all parts of the transaction far outweights this additional UX element press in my opinion. Mimblewimble is already unique in that the receive proves they can spend the coins at the time they created the transaction so it’s less stressful to transact. Why not go a step further and prove not only do they know how to spend but they also know what they’re getting. It might push the stress levels further down if both parties checked and confirmed the transaction, especially if it’s done between two people that trust each other.

4 Likes

I can use a payment proof to prove that I made a payment to a particular address. This makes sense for reusable addresses, that a receiver can publicize and tie to thier identity.

If that address is disposable then how do I link it to a particular recipient?

1 Like

After validating both signatures, we just want to check whether one of the addresses belongs to the open wallet, right? we could agree on generating disposable addresses from a specific keychain path for example m/0/[account]/0/x, I can think in few ways to do it, but none of them very efficient way tbh. I’m open to suggestions. I think this is a good question.

Makes sense to me.

That’s another reason why this feature should be optional I think.

I have not stop to think about the keychain paths and it is a very important part of all of this. Thanks for sharing.

I see… well, the more I think about it, the more I’m convinced that maybe it is time to have manual confirmations. This can be included within the API response to give a better feedback to the user.

To have some consistency maybe we want to have RSR go through Tor too I guess, but I don’t the details of the implementation, I could take a look though. Regarding a memo I would say that it could be out of the scope for now, at least, for me because it could turn out into an endless and completely discussion.

An use case for a store using multi addresses is that the store could generate an address by day or for each invoice (in a RSR flow) and link the address with the buyer. This is something that should be manage by a payment gateway implementation probably.

It can definitely be optional, but we need to be aware this may come at the cost of making some concepts harder to understand. Transaction cancel I mentioned above seems to be such case because if you hide the “sign” step, it makes it hard to understand what transaction cancel means since its use depends on whether you signed the transaction or not (and whether you’re on the receiving/sending side). The safe-cancel idea can be thought of as a “remove signature” from a transaction, but this action comes in a form of a self-spend transaction. My current understanding is that the unavoidable side-effect of hiding the signing process is that transaction cancellation becomes harder to grasp. From what I can tell, the tradeoff between the two interactivity flows is:

  1. “Noninteractive” interactivity where people have no clue they’re actually signing things - in other words, they don’t really know what they’re doing. This can simplify some things, but as pointed above, comes at a cost when you actually need the knowledge that was hidden.
  2. Interactive interactivity where people manually put a signature on a transaction, but this makes them understand the process better (and it’s not really that hard).

There may be something obvious I’m not seeing right now and I’ll be glad if someone points out if I missed something.

Perhaps something fun to think about. I think SRS/RSR can look almost the same if we require the receiver to input the amount they’re receiving in the transaction:

# SRS
1. Send: <amount>, RecipientAddr: <addr>
2. Receive: <amount>
3. Auto-finalize

# RSR
1. Receive: <amount>, SenderAddr: <addr>
2. Send: <amount>
3. Auto-finalize

While this makes the receiver type the value in an input form, it’s also a bit more similar to cash because both parties count the coins when we buy stuff with cash. In this case, both parties input the amount of the receiver either in step1 or step2 as both flows require you to input the amount regardless whether you’re the sender or the receiver.

I agree, this would have been too much. We should only keep this in mind to avoid closing the opportunity to do this in the future.

1 Like

You probably mean disable “auto receive” in SRS?

This doesn’t work with wallet reinstall (you would need to manually set every output again which is practically impossible since you don’t know which is which).

I would require a confirm by default with maybe an optional setting per address to auto-receive (eg. you might have one address per person, one of them is your mum so maybe you can trust her :P)

Definitely, we can deal with that later, might also require some payment proof changes for that. Also payment proofs for RSR are still in the RFC phase

They need to understand signatures even here since TOR communication might not go through. It’s harder to grasp though

The receiver must set the amount yes, it’s the invoice flow, when you get an invoice you see how much you need to pay :stuck_out_tongue:

I think that we need to figure out this before we start implementing anything. Without proper payment proofs a system can’t work so they’re really important and we need a way for an entity to have a public address and the payment proof must somehow be bound to it, even if grin addresses are different with each tx (this would probably require some changes in the payment proofs)

1 Like

Let’s assume we have a “manual_confirmation” flag in our configuration that means we want to manually confirm a transaction when is done via Tor.

SRS
Manual Confirmation Flow

Alice=Sender
Bob=Receiver
Alice wants to send 5 grins to Bob.

Bob’s wallet has manual_confirmation=false

Scenario A: Alice set the amount that she wants to send but not Bob’s address; since Alice did not provide any address, the slatepack is shown in Alice’s screen. Alice manually shares the slatepack with Bob. Bob manually sign the transaction and send back the slatepack to Alice. Alice’s wallet finalizes the transaction. The transaction is broadcasted.

Scenario B: Alice set the amount that she wants to send and Bob’s address; Alice’s wallet can’t communicate with Bob’s wallet via Tor, so the partial slate is shown in Alice’s screen. Alice manually shares the slatepack with Bob. Bob manually signs the transaction and send back the Slatepack to Alice. Alice’s wallet finalizes the transaction. The transaction is broadcasted.

Scenario C: Alice set the amount that she wants to send and Bob’s address; Alice’s wallet is able to communicate with Bob’s wallet via Tor. Bob receives Alice’s partial slate and responds back to Alice’s wallet. Alice’s wallet finalizes the transaction. The transaction is broadcasted.


Bob’s wallet has manual_confirmation=true

Scenario A is the same as the Scenario A written above.

Scenario B is the same as the Scenario B written above.

Scenario C: Alice set the amount that she wants to send and Bob’s address; Alice’s wallet is able to communicate with Bob’s wallet via Tor. Bob receives Alice’s partial slate and responds back to Alice’s wallet. Alice’s wallet can’t finalize the transaction because she did not receive a slatepack from Bob. Bob now sees an incoming transaction, but rejects the incoming transaction.

What happens then? The transaction can’t be just hang there until the end of the times, right?

Scenario D: Alice set the amount that she wants to send and Bob’s address; Alice’s wallet is able to communicate with Bob’s wallet via Tor. Bob receives Alice’s partial slate and responds back to Alice’s wallet. Alice’s wallet can’t finalize the transaction because she did not receive a slatepack from Bob. Bob now sees an incoming transaction, accepts the incoming transaction. Bob is able of communicate with Alice’s wallet via Tor. Alice’s wallet finalizes the transaction. The transaction is broadcasted.

Scenario E: Alice set the amount that she wants to send and Bob’s address; Alice’s wallet is able to communicate with Bob’s wallet via Tor. Bob receives Alice’s partial slate and responds back to Alice’s wallet. Alice’s wallet can’t finalize the transaction because she did not receive a slatepack from Bob. Bob now sees an incoming transaction, accepts the incoming transaction. Bob is not able of communicate with Alice’s wallet. Bob shares the slatepack with Alice. Alice cancels the transaction.

What happens then?

What happens if Alice and Bob both cancels the transaction?

More mockups! I’m assuming we add an optional “Manual Confirmation” feature, let’s se…

Transactions

The list of transactions could look like this:

SRS Flow

Since we can now manually accept transactions, we can see a new button: “Accept”. “Complete” is the same as “Finalize”; “Reject” and “Cancel” means that we’re either rejecting the incoming transaction or canceling the unfinalized transaction.

When we click on “Complete” it is the same process of what we know now as “finalize”.

The new thing is the manual confirmation. When we click on “Accept” we are able of confirming the amount and the receiver address.

After accepting the transaction, our wallet will try to communicate via Tor with the sender, if everything goes well, we see something like this:

Well, the reality is that things can go wrong, and probably we could not reach the sender. In this case, we will need to manually share the signed transaction agreement with the sender.

RSR Flow

We could have also invoices. From the wallet we can:

  • Generate invoices
  • Pay invoices

When we generate an invoice, we will have an transaction pending for payment (Payment Requested), we need to wait for the sender to pay the invoice; this invoice then can be manually completed when the sender shares the transaction agreement aka slatepack with us. Invoices can be also canceled.

In order to register the payment, we should accept the transaction agreement signed by the sender:

If we receive an invoice, we have a transaction pending for payment (Payment Pending); we can decide to pay it, but also, we can just reject it.

After clicking on “Pay”, we try to communicate via Tor with the receiver, if something goes wrong, we then should share the transaction agreement manually.

Conclusion

Having both flows together (Sender-Receiver-Sender and Receiver-Sender-Receiver), in addition with manual confirmation for SRS, looks pretty similar. I don’t think it will add any confusion at all, we just need to be less technical and pick better wording to describe the transaction building process. Maybe “Add Signature” is over simplifying, that’s why “Add participant data” sounds a bit more accurate. We can keep iterating until we find a better way to describe things for the regular user. Achieving this will require to define and pass some RFCs, but again, I think it is worth it.

6 Likes

I agree, if done properly, a user will not consciously thing if a transaction is SRS or RSR.
Just a though, if we go for manual settings such as flag for auto-confirmation, possibly setting a threshold for auto-accepting and who knows more properties/settings in the future, best do this on an account level.
So each account uses the general settings, unless someone clicks Account Settings button next to the account and changes them.

If we go with a different signing key (not the tor’s one) then the “accept transaction agreement” view should also show signing key’s public key

I think “add participant data” is a bit too abstract since it doesn’t tell you what data you add and while it’s true that you need to add multiple data, the user should only really be aware of his signatures, that’s why i prefer signatures :stuck_out_tongue:

It would be great if these awesome user flows could be designed in an SDK library. Like each step could be a method.

Then a developer could build their own wallet UI or even include a grin wallet in any number of apps like a messaging app.

Agree, I am in favor of simply using something simple like ‘sign transaction’ or ‘sign and respond’ for the first signature and ‘sign and broadcast’ for the user adding the last signature. As stated before, I am in favor of using a double checkmark system to show the three phases ✓ ✓:
1- transaction unsigned (2 grey check-marks ✓ ✓)
2- transaction signed by one pary (one green check-mark on gray check-mark ✓)
3- signed by both parties and broadcasted (2 green check-marks ✓ ✓).

Just like WhatsApp, Telegram Signal Messenger or whichever APP nowadays uses. In my opinion we can be both intuitive and simple and still show (although abstract) to the user the steps of signing transactions

3 Likes

I am already picturing an interface that makes it almost fun / satisfying.

Since you need to go through more steps to totally finalize a transaction, you’ll need to do more actions each time you open the app.

Maybe you see the transactions vertically stacked like emails in an inbox. Then you can do things like swipe right or left on the transactions to do actions quickly. Or also do bulk actions.

In this case, I think providing a tool tip that displays an example of what to paste/input would be more helpful (totally bypass the terminology issue).

Ultimately, bikeshedding over terminology is not the most productive thing to do, in my opinion. I think as long as terms are used consistently, and there is an accessible/known place that defines the terms, people will catch on.

For example, the discussion over changing “slatepack” to something else. While some people might not understand what it means, someone with the context that the name derives from Keybase’s “saltpack” would likely have an easier time.

Technical jargon can make things harder to understand for people unfamiliar with the terms, and simpler for people that are familiar. Also, if you change fundamental technical terms, there is either going to be a lot of churn in the code to update words, or a mismatch between explanatory terms and the code.

I love the mockups and takes on making transaction building more accessible. Second the idea that manual_confirm should be optional for people concerned about the transactions they receive.

The explanations on-screen are great for some parts of tranaction building known to be confusing.

After working on wallet code and transaction building, I don’t think it’s worth it to try to make Grin transactions non-interactive (in the traditional sense). It would require fundamental changes to the cryptography used in Grin, and likely isn’t even possible to be fully non-interactive like other coins. Two-round transactions might be possible, but would likely require a significant amount of work (probably not worth it).

Explainers, documentation, and maybe even some games/demos could help people become familiar with how Grin works.

7 Likes