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

Yes, that’s exactly what it is. If you have a transaction you have signed that may or may not ever be published, you can resolve this “unknown” state by spending the same input - which prevents play attacks.

Agree. Grin is different because of its interactivity - I’m also of the opinion that we should take advantage of that instead of making a hybrid transaction building out of it. A hybrid doesn’t bring the benefits of ITX nor does it achieve NITX so you miss on the good parts of both world.

It’s probably safe to say that most of us believe the naming could be much better. There was a long discussion on the naming of rounds here Transaction Round Naming Challenge but we have yet to reach a conclusion.

I’m glad people want to improve this part, it’s something I was hoping more people would be interested in for a long time. I believe we could move the transaction building process to a higher level, but this requires us to think about safe-cancel, one-time addresses and possibly other things as well - to prevent creating a flow that looks good today, but when you want to make it safe it hold grounds. This is also the reason why I asked Gene if he would be interested in improving the wallet experience. We need to figure out what we would consider good though and for this we need discussions like this.

Offer/acceptance/deal

“Sending” can be perceived as a completed action on its own, whereas “offer” implies the next step - acceptance. Just my thoughts that may be biased ))

2 Likes

I really like this one, If I understand @oryhp correctly, the process doesn’t have the sender signature til the 3rd “finalize” step;

so it is like drafting the contract, sending it to the second party for review, they agree, and the drafter signs and seals.

Maybe propose/review/deal

Yeah, the finalize step is the “sign” step for the person that started the transaction. I really liked antioch’s steps here Transaction Round Naming Challenge - #52 by antioch

// SRS flow
send <addr> <amount> -o slate1.gtx       /* outputs 0/2 blank slatepack
sign -i slate1.gtx -o slate2.gtx         /* outputs 1/2 signed
sign -i slate2.gtx -o tx.gtx             /* outputs 2/2 signed
// RSR flow
receive <addr> <amount> -o slate1.gtx    /* outputs 0/2 blank slatepack
sign -i slate1.gtx -o slate2.gtx         /* outputs 1/2 signed
sign -i slate2.gtx -o tx.gtx             /* outputs 2/2 signed

It either:

  1. creates an empty “slate” or a “deal/offer” if we prefer to call it that or
  2. signs an existing slate passed through -i (input) to produce a new slate and save it to file -o (output).

If the output is not defined, the output slate is printed to stdout. I’m not sure if the above format is good, but we likely want to have small commands that are composable and do one thing only because these make integrations simpler and add flexibility to integrations - the alternative could be unix-like behaviour with expecting the input through stdin and doing cat slate1.gtx | sign > slate2.gtx but I’m not sure how this works on other operating systems.

Note that there is no broadcast in the above flow. This is because we want to clearly separate the signing from broadcasting. The two can happen even on different devices e.g. hardware wallet signs, but doesn’t broadcast because it doesn’t have a connection to external world e.g. a node.

Since signing of the last signature is often accompanied with a broadcast, an alias command composition sign -i slate2.gtx -o tx.gtx && broadcast tx.gtx would be available.

1 Like

Nice, yeah maybe the simplest verbally and represented by some kind of gui, would just be an old-fashioned contract with two signature slots.

Contract, sign, sign

Thinkibg about Adobe PDF forms; both parties sign the blanks, the initiator (like leasing office or hr) then has the form to process at its leisure (broadcast).

I think that could be representated easily graphically and cognitively

1 Like

I’m not familiar with Adobe Forms, but this contract, sign, sign idea has been floating around for quite some time e.g. Possible interactive transaction flow which was later implemented as a testnet exchange example (though it supported only rust wallet due to the RSR deposits).

I do agree that the word contract is scary though. On the plus side, it makes people more aware that signing in Grin matters for both sides.

1 Like

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