Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@pkariz
Last active October 4, 2020 21:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pkariz/9da5c3decffba0661a11f49c645b966e to your computer and use it in GitHub Desktop.
Save pkariz/9da5c3decffba0661a11f49c645b966e to your computer and use it in GitHub Desktop.
transaction building alternative

Transaction building alternative

Schnorr notations

s = k + e*r, where:

  • k is a secret nonce
  • e is a schnorr challenge
  • r is the private key

For multisig:

ss + sr = ks + kr + H(M | Ks + Kr | Rs + Rr)*(rs + rr) where s and r define whether this variable is from sender or receiver, so Ks is sender's public nonce while rr is receiver's private sum of his blinding factors.

Introduction

In this document we introduce a new transaction building flow that keeps the same Schnorr challenge, but allows the sender to construct the partial signature right away from the receiver's slatepack address and obtain a payment proof for it. This allows the receiver to finalize the transaction in the next step. The same flow also works for the receiver->sender flow.

Slatepack address

Currently the Slatepack address contains:

  • receiver_pubkey (ed25519 public key)
  • hrp (human readable prefix, usually "grin")

Along with receiver_pubkey and hrp we also send enc_T. We introduce new curve points T1 = t*G and T2 = t*G which are randomly chosen by the receiver. Receiver creates enc_T like this: enc_T = encrypt({T1, T2}, receiver_privatekey). Whoever has the address can compute T1, T2 = decrypt(enc_T, receiver_pubkey). T1 and T2 are encrypted in enc_T with the receiver_privatekey so that nobody can change them. This way it guarantees that T1 and T2 were picked by the owner of receiver_pubkey. The address is for one-time use.

Sender -> Receiver

  1. Receiver shares his address with the sender through some communication channel (directly with him or make it public).

  2. Sender

    1. calculates T1, T2 = decrypt(enc_T, receiver_pubkey) and Rr = T1. We use T1 so that the sender doesn't know the private key of Rr.
    2. randomly chooses his private nonce ks and calculates his rs.
    3. calculates R = Rs + Rr
    4. calculates Kr = H(amount | sender_pub | receiver_pub | enc_T | R | Ks) * T2 where:
      • we need amount and sender_pub to know who sent how much
      • we need receiver_pub, and enc_T to prove we had the right info from the receiver (we also need T2 but that's already a point with which we multiply the hash)
      • we need R to link hash with some kernel on the chain
      • we need Ks to assure the sender can't fake Ks when proving his payment - explained later
      • we multiply it by T2 so that the sender doesn't know private key of Kr, because Kr = (H(...)*t2) * G and he doesn't know t2. T2 must be the one from the enc_T (and that's not T1!).
    5. calculates ss = ks + e*rs where e = H(M | Ks + Kr | Rs + Rr)
    6. sends { ss, amount, Ks, Rs, sender_pub, enc_T } to the receiver. Note that Kr and Rr are not sent.
  3. Receiver

    1. wallet keeps track of used enc_T's, so they must be unique to avoid nonce and excess reuse.
    2. calculates Rr, R and Kr
    3. calculates sr = kr + e*rr
    4. finalizes the transactions and broadcasts it

Note: the sender can't lie about the amount because the receiver computes Kr from the amount variable that he received. If the sender constructed Kr for a different amount then they would get a different schnorr challenge and the transaction would not be valid.

Payment proofs

If the sender doesn't directly receive anything from the receiver then he must somehow create the payment proof.

Sender needs to prove that he knows how to calculate K = Ks + Kr where:

  • Kr = H(amount | sender_pub | receiver_pub | enc_T | R | Ks) * T2
  • K is public nonce of the kernel with excess R which is on chain

You need Ks in the hash because otherwise the sender could fake his Ks by defining it as Ks = K - Kr. I think the receiver can prove it in the same way since he also can't fake Ks.

So the proof is a set { amount, sender_pub, receiver_pub, enc_T, R, Ks }.

Receiver -> Sender

The flow should be very similar. Sender shares his slatepack address, which contains T1 and T2, to the receiver. Receiver calculates Rs = T1, picks his rr and kr and then calculates Ks = H(U | amount | sender_pub | receiver_pub | enc_T | R | Kr) * T2.

Payment proof

The payment proof should be similar as when the sender initiates except that here you prove the knowledge of Ks instead of Kr. Therefore the proof is a set { amount, sender_pub, receiver_pub, enc_T, R, Kr }.

Pros & Cons

Pros

  • does not require a consensus change
  • both parties end up with a payment proof so it can be done in any direction

Cons

  • not proven secure
  • wallets need to keep distinct slatepack addresses - they can't be reused through wallets because wallets keep track of enc_T values for per address to avoid T1 and T2 reuse
  • wallets need to store enc_T values per each address they generate
  • wallet restore invalidates previous unused addresses
  • with the current version you can't have parties pay for their own fees because the sender commits to fee in the first phase (unless the fee amount was sent with the address by the receiver)

Possible improvements

  • can we somehow make it possible to generate a permanent address?
  • missing solution for different fee payment strategies (eg. each his own, all finalizer)
@tromp
Copy link

tromp commented Oct 4, 2020

Looks insecure. What unknowns (to sender) does equation sr = kr + e*rr have, other than t ?

@pkariz
Copy link
Author

pkariz commented Oct 4, 2020

Looks insecure. What unknowns (to sender) does equation sr = kr + e*rr have, other than t ?

ah right, the same mistake i made in the previous attempt 🤦‍♂️
So i know how to solve this by introducing another variable in the address (like i did in the previous version), do you see a more elegant solution?
And thank you for checking it!

@tromp
Copy link

tromp commented Oct 4, 2020

by introducing another variable in the address

Then it's still a single use address, since reuse gives two equations in two unknowns.

@pkariz
Copy link
Author

pkariz commented Oct 4, 2020

by introducing another variable in the address

Then it's still a single use address, since reuse gives two equations in two unknowns.

yeah you're right

@pkariz
Copy link
Author

pkariz commented Oct 4, 2020

does the payment proof make sense? or is there also some error? (let's say for unique addresses)

@tromp
Copy link

tromp commented Oct 4, 2020

The kernel makes no sense to me, since Rs+Rr does not look like a difference between outputs and inputs.

@pkariz
Copy link
Author

pkariz commented Oct 4, 2020

The kernel makes no sense to me, since Rs+Rr does not look like a difference between outputs and inputs.

Rs is the standard O - I of the sender, receiver knows rr so he can pick blinding factors of his outputs so that his O-I=Rr or not?

@tromp
Copy link

tromp commented Oct 4, 2020

I got confused because r normally denotes a blinding factor, not a (private excess).
I suggest replacing r/R by x/X.

Regarding the payment proof, I see no obvious way to break it, so it might work.

@pkariz
Copy link
Author

pkariz commented Oct 4, 2020

I got confused because r normally denotes a blinding factor, not a (private excess).
I suggest replacing r/R by x/X.

Regarding the payment proof, I see no obvious way to break it, so it might work.

Thanks for all the help. I've updated the document (removed U since it's useless now that each address has 2 random points) and fixed
things. I understand the annoyance people (with high knowledge of schnorr) have with my variable name decisions but i feel like schnorr
variable names are insanely confusing to work with when you're writing a solution for grin because of all the overlapping in variable names
we would have, so i feel like K = Ks + Kr and R = Rs + Rr fits grin solutions more. Thanks again, really appreciate your help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment