Currently, grin transactions are created using the following 3 (simplified) steps:
- Send - Sender commits to amount, fee, and a public nonce, public excess. It could also commit to tx offset, inputs, change output, and probably others.
- Receive - The receiver adds their output & rangeproof, and commits to their public nonce and excess. It then updates the total kernel commitment, and signs for their half of the kernel.
- Finalize - The sender adds their half of the signature, aggregates the 2 partial signatures, and builds the final transaction.
The sender can’t just include their part of the signature as part of the send step, because it doesn’t yet know the receiver’s public nonce and public excess. But what if it did?
Grin now has slatepack addresses, which are just encoded ed25519 public keys. If we extended these to include a secp256k1 public key, then we could treat those addresses as BIP32 parent public keys, and the sender could generate the public nonce and public excess for the receiver, and just inform them of which keychain path they used to generate the public keys.
In fact, the sender could generate everything about the transaction except the bulletproof and the receiver’s half of the signature. Then they could just pass that to the receiver, who just adds their half of the signature, and appends a bulletproof to their output. The transaction is complete - no finalize step necessary!
This is a pretty radical change that would require rethinking nearly everything about the wallet: Play & replay attacks, payment proofs, invoices, etc. But I believe these are all solvable, and will propose solutions for these later, if there’s enough interest in this idea.