Spread Currency 'Round the Table

(sorry for the tongue in cheek heading)

I’m pasting some thoughts i had in the community telegram here so we can record the design process.

A few users were discussing how one can create a clean address with SCRT that isn’t linked to any other address. I gave the following idea:

We can have a contract that lets you deposit SCRT, a recipient, and entropy. The entropy is added to all previously collected entropy, a number between 5 and 30 (exact figures TBD) is randomly generated from that entropy, and the amount+recipient pair is saved along with that number.
Each time a deposit happens, the contract checks the list of previous deposits to see if for any withstanding deposits and if the number of interactions since they were made if greater than or equal to the number that was generated when the deposit was originally made. If there were matching deposits, then those deposits are released in the same TX.
So each time you deposit, you also release between 0 and 25 previously made deposits, and your deposit will be released within 1 and 26 interactions later, by some other random user.
Require all deposits in sSCRT so people can’t see how much you put in. Then on the withdrawal do it in two steps: (a contract can trigger multiple callbacks at once)

  1. The contract redeems enough sSCRT to SCRT to cover all withdrawals in this TX.
  2. The contract adds a list of bank operations to immediately distribute the SCRT it received in that internal tx between the intended recipients.

You can even use viewing keys to see if you have any pending deposits and how many more deposits need to happen until it gets released.
Plus have an option to retract it if it’s moving too slowly or something and you need the money back.

EDIT: made the sentences clearer and more sensible

2 Likes

I love it. I might even look for a way to add sTOKENS to this if the infrastructure is already built. The way the sender and receiver accounts interact with the contract at the same time is the key issue with the current privacy. Adding this delay mechanism seems like the perfect foil to that and I think would be good for all coins, not just SCRT.

I’m not sure what you mean. if you’re sending a SNIP-20 from one account to another, then the two accounts don’t have to interact with the contract at the same time at all. The receiver only needs to create a VK once if he didn’t have it before. There’s no timing relationship between senders and receivers.

EDIT: or did you mean interactions with the bridge?

After thinking about this for a bit, the design detailed above is vulnerable to a replay/fork side channel attack.
If we represent each deposit of some size and receiver to the contract as a letter, e.g. a sequence of three deposits as "A B C", where each such deposit is automatically released by a random future deposit, then we de-anonymize a sequence by comparing two different forks of the chain where the only difference is one deposit. For example if Z is a deposit known to the attacker, then the attacker can compare the set of withdrawal receivers between the two following custom forks:

ABCDEFGZZZZZZZZZZZZ (etc...)
ABCDEF-ZZZZZZZZZZZZ (etc...)

If the anonymity set is 25, then after at most 25 instances of Z the receiver address of G will become known.
This is probably an acceptable risk for most people and still constitutes a useful product, but still, let’s try to think of a solution to this.

A possible solution is:
Only a set of contract-admin approved “operator” accounts (of well known entities?) have permissions to trigger withdrawals. Withdrawals are still only allowed by the contract after enough other operations have been performed (i.e. as long as the anonymity set is decent). Maybe we collect a fee from depositors which is used to reimburse the “operatos”?

This is a very general idea with a lot of gaps in it, but maybe it can get a ball rolling that leads somewhere…but let’s say that the mixer contract and some oracle contract shared some private key or mnemonic such that they can both deterministically derive some hash that uses current block info in the derivation somehow. Only authorized address(es) can execute the call to the oracle contract to make it derive the new hash, and the mixer contract will use an authenticated query to the oracle contract and only send withdrawals if the hash it gets back matches the one that it just generated. Again, there are things that need to get filled in, but the general idea is that we still let users trigger withdrawals, but someone wouldn’t be able to trigger a withdrawal on their own fork because they don’t have a way to trigger the oracle contract to create the necessary matching key

We’d probably have to do something to make sure that the timing works out such that we can be sure the oracle contract was updated before the mixer contract queries it…

@baedrik I don’t think your idea help here… For one, the block height parameter is untrusted so that’s a bummer. Second, i think that having a separate oracle contract is just “extra steps”. You can have an “oracle interface” built in to the mixer contract for the purposes we’re trying to achieve.

That being said, there is value in having a “block height” oracle. It’s super simple - A list of authorized addresses are allowed to call an interface with no parameters which just stores the block height of the call. Then, other contracts query the oracle, and are guaranteed that the current block height is at least the one reported by the oracle. Under an environment controlled by an advanced attacker, the minimum block height can fluctuate up and down (incorrect data syncing attack), but it will never be higher than the current block height.

Also, thinking more about it, it still doesn’t solve the problem of having to trust that your oracle operator isn’t going to be the one to try the attack. So if it still requires trust, you might as well keep it simple and just have the operator trigger the withdrawals.

So probably just have something where you need x out of n of the authorized operators to execute the command to send out withdrawals. And hopefully that multisig situation makes it less likely that enough will collude to do the side chain attack

1 Like

I think, fundamentally, the issue is that the desired end result (having SCRT sent to a new address) is a public event which reveals enough information to make the attack possible. So all you can really do is make sure that only trusted entities can release that information (i.e. trigger withdrawals) and try to implement it in a way that helps minimize the amount of trust

1 Like