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
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
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
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
Ncontracts, 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.