Exploring Payment Proofs for invoice flow

The payment proofs RFC at

states payment proofs for invoices as an Unresolved question.
In the invoice transaction building flow we have

  1. receiver sends her public nonce and excess
  2. sender sends back his public nonce and excess, and other stuff including a partial kernel signature
  3. receiver computes their partial kernel signature and finalizes

The payment proof is a signed statement that the receiver accepts the on-chain confirmation of a specific kernel as proof of payment of a specific amount by a specific sender.

The receiver doesn’t know the kernel yet in step 1, as the kernel excess depends on the sender excess sent in step 2. So for the sender to withhold payment until they have a payment proof would require two extra rounds, if we want the receiver to finalize (for which there are good reasons):

  1. receiver sends her public nonce and excess
  2. sender sends back his public nonce and excess
  3. receiver sends payment proof
  4. sender sends other stuff including a a partial kernel signature
  5. receiver computes their partial kernel signature and finalizes

This is obviously undesirable. So the question is if we can come up with a payment proof that can be sent in round 1, depending only on the receiver nonce R_r and excess X_r.

Recall that in step 3, the receiver produces a partial signature (s_r, R_r) that satisfies

s_r * G = R_r + e * X_r

where e is the hash challenge H((R | X | …) .

What if the receiver accepts, as proof of payment of a specific amount by a specific sender, any pair (s_r, i) where i is the index of an on-chain kernel whose hash challenge e satisfies the above equation?

No one should be able to present this information unless the receiver made her partial signature, and the resulting transaction hit the chain.

1 Like

Don’t we already have a potential solution here: https://github.com/DavidBurkett/grin-rfcs/blob/eliminate_finalize/text/0000-eliminate-finalize.md#building-the-proof

At a technical level, the RFC is roughly just a switch to invoices with the addition of payment proofs and some UX tweaks to reduce the number of steps (this is a loaded word, so don’t read too much into it).

As far as I know, the scheme (developed by kurt, which I simplified when including in the RFC) hasn’t been shown to be insecure. The only thing that was found to be insecure was the separate idea, discussed in the comments, which involved modifying the actual signature scheme.

It’s quite similar to your proposal, but much closer to the existing payment proofs, as the payment info is signed by the ed25519 key.

I find yours harder to reason about, since it puts the payment info into a proof_nonce that gets added to the existing sender and receiver nonce.

For instance, when you say

  1. Show that sender.public_nonce + receiver.public_nonce + (proof_nonce * G) = total_nonce * G (ie. the k*G in the kernel signature)
  2. Provide the preimage to proof_nonce ( sender.public_nonce | receiver.address | tx.amount )

it appears that the pair of sender.nonce and proof_nonce is malleable until one notices the proof_nonce depending on the sender nonce.

1 Like

I don’t care anymore about > 2-step txs, and i would hope leaders of the coin and technical people here do too (some like David, made a lot of efforts for this, including the great idea for 2 steps). It is just the expression of striving for the better for the user, rather than accepting a text-book and useless status quo.

I think the main idea behind the payment proofs described in this document could be used. The idea is that Kr has a structure such that it commits to a few things, including the amount and the receiver_pub_key.

I like the simplicity of your invoice solution, seems pretty easy to reason about:

  1. only the sender can know sr since only he knows ss
  2. only the sender can know Rr since only he knows Rs
  3. only the sender can know Xr since only he knows Xs

Ofc the receiver also knows the above stuff.

My solution is a bit more complicated and therefore harder to reason about. It has a nice property though in that both the receiver and the sender can prove it in the same way + the payment proof when the other party finalizes the transaction is symmetric. I haven’t thought much about your solution but it feels like yours is also symmetric (or even better, the same), so when the sender finalizes it he can provide (sr, i) + signed message. Is that correct? I know we have payment proofs for that flow but am just wondering if that holds.

Another question: why do you use i and not R or K to reference the kernel?

When sender initiates, then the receiver can send the exact same payment proof (that they send in round 1 of invoice flow) in round 2 instead. That is, an ed25519 signature on payment info, receiver public nonce and excess, to be witnessed by a pair (sr,i).

Because specifying K takes more bytes and the first thing a verifier would do is lookup K in the kernel MMR to find its index and check that it has sufficient confirmations. So we might as well provide the index directly.

1 Like