Wanting community opinion: federated peer-to-peer identity servers and pluggable circuits

Right now, a lot of projects I’ve seen that approach the identification of addresses use an ENS-like approach, where a central registry identifies everyone. While this works for a transparent system like Ethereum or perhaps Zcash Transparent, it is fundamentally not suited for shielded addresses, where address rotation is practically a given, and anonymity/unlinkability matters the most.

Further, most of them require you to contact a light indexer using your IP, which reveals your IP and that you are requesting a specific identity to the server, especially when there is no PIR. We do not claim that we can solve this, but our solution intends to lessen the attack surface from “a general indexer which logs every request” to “only the intended server does”

So, the obvious go-to solution is: federated identity servers, similar to emails. Hence, we thought up a simple, SMTP-like protocol: Zcash Simple Money Transfer Protocol (ZSMTP)

As proposed, the protocol is very simple:

  • Servers expose a service (either on a standard port or via SRV) that uses a CEI (Commit-Effect-Interact)-like protocol to reveal addresses.
  • Initially, both servers authenticate themselves to be true to their DNS via domainkeys (similar to DKIM)
  • Then, the servers perform an Elliptic Curve Diffie Hellman key exchange to establish a secure, deniable session.
  • Then the sender requests a (optionally signed) attestation of an address for a given queried user, which the server can generate on-demand using keypairs to protect privacy; for public identities (ENS-like), this can optionally include an attestation against a static public key or against an entry in the Zcash ledger
  • The sender sends a sealed invoice encrypted using K.
  • The transaction has K as a memo attached, that the receiving server uses a Incoming Viewing Key to decrypt. receiving party uses their IVK to decrypt. The server can either just be a dumb mailbox for the encrypted blob, which the user can decrypt using their IVK, or the server can be relegated the authority by letting it generate its own IVK.
  • The exchange completes seamlessly.

Private Information Retrieval can also be implemented to enhance security of this setup.

The main, and most exciting thing will be that if this were to become a ZIP, we can propose and design many more features that can be added as “plugins” to these servers natively, such as:

  • A proof-of-activity or proof-of-funds circuit where a sender can prove that they currently own a specific UTXO or combination of UTXOs that equal to or exceed a given transaction value X ZEC, without revealing which UTXO or what their address is, using Merkle membership proofs with a range proof. Services can use this to establish anti-fraud measures as well as for invoicing or “free trials”.
  • Loyalty proofs using a shared session key:
    • Essentially, say a company wants to give you an airdrop, or check your loyalty without actually logging all your transactions.
    • You agree on a shared session secret, and then the company server generates a tree of all of their addresses, and gives the tree and root to you.
    • The circuit can privately compute that your address(es) that you have spending keys to have spent a total of more or less than X ZEC across all addresses in the tree, from any number of your own addresses (that you own spending rights to), without revealing either which address were paid from or which addresses were paid into, and what amounts or at what time.
  • And many more, suggestions welcome!

Although we did some initial work, we wanted input as to whether this was something Zcash users would really appreciate, and whether this would be good as a standalone grant, a ZIP first (and subsequent grant), or something that should be incorporated in a later network upgrade instead (this one is kind of overblown but putting it there because why not)

Also, if you want to see if a specific thing can be circuit-ised, just reply to this post and our engineers will see if it is possible for that idea to exist as a future plugin :stuck_out_tongue:

To be clear, these will run as a Layer-2 over Zcash, privately between two entities over ZSMTP rather than something being broadcasted over the network.

EDIT: Crucial clarification:

I failed to write it clearly, possibly because I am sleep deprived. Just like a real email server, the server doesn’t necessarily have to have the IVK, just like the sending server doesn’t necessarily need to have the spending keys to broadcast the transactions. This task can be delegated to the server or passed back down to the actual user who is receiving or sending the things.

We made it this way so:

  • If the server owns the IVK, it can decrypt the memo as normal, there is literally no hassle to the server.
  • If the server does NOT own the IVK, then the person who does own the IVK can fetch the encrypted memo from the server mailbox (we envision an IMAP-like approach), and decrypt it locally via their own transaction history. Crucially, encrypting the memo using K that can be later retrieved exclusively by the owner of the IVK prevents the server from learning about the invoice even if it did not own the IVK, while still not requiring the user to be online to receive the invoice themselves, and still allowing proof-of-integrity (user cannot claim they didn’t know what the payment is for, since the server signed for the (encrypted) invoice)
2 Likes

half ai and half me ill be honest as i wanted some more info about some specifics you mentioned, hope thats ok

Really like the framing here. The point that ENS style registries just don’t fit shielded addresses, because rotation is normal and unlinkability is the whole point, is the strongest part of this. If it becomes a ZIP I’d lead with that.

The bit I’d want a harder look at is step 6.

Having the receiving server decrypt K from the memo with an Incoming Viewing Key is the part that worries me. A viewing key covers the whole account and you can’t take it back. Give one to a server so it can read a single invoice, and that server can now see every shielded payment that account has ever received, forever. You can’t scope it down afterwards and you can’t rotate it without moving your funds.

That matters more here than it normally would, because you’ve modelled this on email. Most people don’t run their own mail server, they use a provider. So the realistic version of this is people handing their provider a permanent key that reads everything they receive. That’s a bigger ask than the indexer problem you started with. You’d be swapping a server that learns who is asking about you for a server that can read everything you’ve ever been paid.

A few things worth looking at before this settles:

  • Scope the decryption to one address or diversifier instead of the whole account, so a compromised server only leaks part of the picture
  • See if K can be delivered another way, so the server never needs viewing access at all
  • Make running it yourself the normal path in the docs, not the advanced one

For context on where I’m coming from, I built a small self hosted invoicing tool for Zcash, and viewing keys were the exact thing that killed payment verification for me. I shipped it with no verification at all rather than put a viewing key anywhere in the design. Invoices get encrypted in the browser and the server never talks to any chain infrastructure to create or serve one. That’s a harsher trade than yours, since the payee just has to watch their own wallet, but it does show the indexer problem is avoidable, and that the viewing key is where the real cost sits.

On your process question, I’d say ZIP first, grant second. This runs alongside the chain rather than in it, so there’s no network upgrade needed. The ZIP really only needs to pin down the things that hurt to change later: the memo format, what an attestation actually means, and how servers prove themselves against DNS. Everything else can keep moving in code.

On the plugin list, proof of funds is easily the most useful one, and I’d put it in the first ZIP rather than save it for later. It gives the payer some assurance without anyone handing over a viewing key, which is the thing the rest of the design is working hard to protect. The loyalty proofs are clever, but they feel like something to add once the core is settled.

1 Like

Okay fuck, I failed to write it clearly, possibly because I am sleep deprived. Just like a real email server, the server doesn’t necessarily have to have the IVK, just like the sending server doesn’t necessarily need to have the spending keys to broadcast the transactions. This task can be delegated to the server or passed back down to the actual user who is receiving or sending the things.

We made it this way so:

  • If the server owns the IVK, it can decrypt the memo as normal, there is literally no hassle to the server.
  • If the server does NOT own the IVK, then the person who does own the IVK can fetch the encrypted memo from the server mailbox (we envision an IMAP-like approach), and decrypt it locally via their own transaction history. Crucially, encrypting the memo using K that can be later retrieved exclusively by the owner of the IVK prevents the server from learning about the invoice even if it did not own the IVK, while still not requiring the user to be online to receive the invoice themselves, and still allowing proof-of-integrity (user cannot claim they didn’t know what the payment is for, since the server signed for the (encrypted) invoice)

It is quite flexible, really, and I apologise for the mishap.

EDIT:

I read the rest of the message, and I quite agree. ZIP->grant is the correct way to proceed.

1 Like

A visualisation for the protocol mechanics:

Thanks for clearing that up, appreciate it. I’ll keep an eye on this, specifically the possibility of verifying a payment without any viewing key involved. If that’s the direction the loyalty circuit ends up going, that would be genuinely useful to me for my little service.

Wait, isn’t that already a thing? Transaction keys? Holy- no… that’s a Monero thing…

We can absolutely add a plugin for a circuit specifically to prove that a payment happened to a specific address at the current block height (using the current nullifier set) of an amount higher than a specific ZEC easily! It will be shit slow to compute on a phone, but eh, it can be done at least.

I will do some research tomorrow when I wake up.

1 Like

The mobile performance constraint may be the practical deciding factor here. Before treating this as a wallet-facing feature, it would help to benchmark a prototype on representative Android and iOS devices:

  • proof-generation time and peak memory use
  • battery and thermal impact
  • proof size and verification time
  • exactly what information becomes public to the verifier
  • replay prevention and behavior during a chain reorganization

If the proof can be generated asynchronously without exposing viewing or spending keys, it could still be useful even if it is not instant. If it takes several minutes or requires substantial memory, a desktop-first implementation may be more realistic.

A small proof-of-concept with benchmarks would probably make the ZIP-versus-grant decision much easier.