Bidirectional payment proofs

This post already described the idea, but it was joined with other ideas which are completely unrelated. I thought it would be worth to present the idea as a separate thing which is why I described it here Bidirectional payment proofs for Mimblewimble | grinvestigation.

This shows a way for parties to commit to another (hidden) arbitrary message at step2 which in this case is our payment proof.

I’m calling it bidirectional because it allows not only to prove the direction

sender ------sent to------> receiver

but also

sender <---received from--- receiver

NOTE: This is not a proposal. While I personally don’t think it’s more complicated than other payment proofs because it’s really just another message commitment, it’s unclear to me which payment proof method is best. It also has not been studied deeply by anyone else than myself so it could very well be insecure.

NOTE2: I’m not sure that replacing f * Rr with Rr + f*G would be secure, because both parties share the same f. In case one party didn’t add f*G the other could add 2f*G. Whether that’s feasible or not and in which cases isn’t clear to me, so I’m scaling both nonce by f to avoid this situation.

5 Likes

Yesterday, @tromp pointed that this bidirectional approach leaks some privacy, specifically, the exchange could demand proof of origin of coins you received. Since this bidirectional scheme means both parties have the same information it would mean that the receiver should always be able to provide this information.

That’s a great point and something I think we should protect against.

I believe both the early payment proofs and this bidirectional scheme suffer from this to some degree and I think there’s a simple fix for it. In the early payment proofs, instead of signing the sender_addr, the sender picks a random scalar y and computes a new, ephemeral address ephemeral_addr = y*G. The receiver can show this address, but since it’s a one-time address by definition, it shouldn’t leak anything. If the receiver shows the payment proof, the sender can simply say “nope, it wasn’t me”. On the other hand, if the sender wants to prove they paid, they can show (Z, sig(Z, M), u) where Z is a random curve point, sig is a proof of knowledge of private key for Z, M is the message we sign which can be the latest block hash or a message of choosing by the receiver, and u is a scalar such that ephemeral_addr = Z + u*G.

This should allow the sender to prove they paid while leaving the receiver with a random curve point.

I think it shouldn’t matter who the sender is, it should only matter whether the sender can prove they sent money to the receiver. In other words, we want the receiver to have an identity, but we might want to blind the identity of the sender.

NOTE: We can use this same trick in the bidirectional scheme by using a random curve point instead of the sender_addr.

NOTE2: If the sender always generated a one-time address, then this could be our ephemeral_addr. The difference is on which curve we hold the ephemeral identities (ed25519 or secp256k1). In both cases, the payment proof would need to store some ephemeral private keys which were generated per transaction (unless we had derivations, but this complicates things and can again introduce a way to link things).

3 Likes

The early payment proof promise doesn’t prove the identity of the payer. The receiver can put any arbitrary sender_addr in there.
And then have any arbitrary payer (e.g. himself) make a witnessing payment. That doesn’t prove that it was sender_addr that paid…

1 Like

Yes, but they have a reference to the sender through sender_addr. It can’t be proven that it wasn’t modified, but I think it can be assumed that most won’t modify it. So you sort of probabilistically leak the sender.

Leak to whom? And how? In what circumstance would 3rd parties get a hold of these payment proofs without the payer’s consent?

In theory they could ask for a payment proof signature for an output you used. Though I’m wondering if this signature that the receiver provides to the sender is just “fire and forget”, meaning that the receiver doesn’t really have it stored.

But that’s pretty useless since you can make up arbitrary such promise signatures on the spot.

This doesn’t seem like a good enough reason to complicate the promise.

You could, yes, but what users do on average is important. If 95% show the actual promise, out of 20 promises you have only 1 that lies. Not sure if it matters though. Perhaps I’m overthinking it. But if the receiver simply forgets about the promise they made (not store it anywhere), then you have the opposite probability where only 5% will have anything to show at all.