Suppose party A wants to share some data with party B. We want to do that in a way to prevent spam without giving much information about the data. The main idea is to have a centralized service which produces blind signatures for outputs in the utxo set. These signatures are used to store data on the service (e.g. partially completed contracts). This makes sure that the service can't reliably link the output with a contract while also preventing spam attacks.
Suppose we have a central service S
that provides three endpoints:
/claim
- claims storage coupons for an output that is in the UTXO set. Returns 2 blind signatures/store
- receives an unused unblinded signature and stores data with a certain ttl (e.g. 3 days)/list
- lists all the public data
Claim is an operation that receives an output from the UTXO set with ComSignature and, if it has not produced blind signatures for that output commitment yet, it returns two blind signatures (similar like it's done in Chaumian ecash). The service keeps track of the commitments(utxos) it produced the blind signatures for. Let's call these blind signatures s1
and s2
.
Store is an operation that receives a single unblinded signature and, if that signature has not been used yet, the service stores the data and adds the signature to the list of used signatures. Stored data is expired after 3 days.
This operation returns a list of all current public data.
Suppose Alice has an output O1
on the chain. She calls claim
for O1
to obtain two blind signatures (s1, s2)
from the service. She now wants to send some coins to Bob and prepares the encrypted slate for Bob. She calls store
with s1
to store the signature on the service. Since Bob needs to store the signed contract, she encrypts the remaining blind signature s2
for Bob so he's able to use it to store the signed contract. Note that after Bob calls store
we have two contracts (step1 and step2) which are just two separate store calls.
The communication with the service would need to be done through some anonymous channel to avoid learning the IPs.
Since we're using payjoin and sometimes contributing multiple inputs in a transaction, we could be sending encrypted dummy data to the service just to produce noise in the list of contracts.
- The service can't provably link a contract to an output on the chain, it can only do heuristic based on timing correlations. In theory, the contract could be done by any output that redeemed the blind signatures.
- A spam attacker could store 2 contracts per on-chain output. This makes it free for the normal users to store, but if someone wanted to perform a spam attack with
N
contracts, they'd need to have claimed N/2 outputs which means they'd have to pay onchain fees for these. Even if the attacker had money to do that, this means the attacker must be paying fees on the chain for these outputs (or has done so in the past), so they're providing security to the network. We won't complain.
When the receiver starts the contract, they may not have a blind signature available. What to do here?
Not sure yet. Perhaps the sender could share the blind signature with the receiver if the receiver didn't have any on-chain output.
The list of spent signature grows forever. Can we improve this?
To solve the ever growing nullifier set, we simply add another endpoint that communicates the current public key for service S
. The service also communicates the next public key the service will use. By swapping the keys, the service can forget old blind signatures.
Should we give some more blind signatures in case they have to redo the dance?
The service could return 4 blind signatures to account for re-spend transactions.
Could we make contracts indistinguishable?
Yes. One way would be to do what is described here. Note that the dummy field doesn't need to be a part of the slatepack. We could in this case simply provide a layer that adds this dummy data and encrypt that. The data is encrypted so we can augment the structure all we want.
@phyro I see. Thank you for the additional clarity. One other question pertains to the economics: given that a confirmed output can be signed and provided to the service in exchange for storage credits, why do you fix the storage credits at a constant? An output which contains more of the underlying currency (bitcoin, or, in your example, grin), should be issued more storage credits, no? Or are you saying that the fixed ratio of 1 output to N storage credits (10 in your example) is necessary to maximize the privacy properties?