chalice is my design for censorship-resitant cross-chain mixer-bridge.
originally i worked on hideyour.cash which was a groth16 mixer deployed on near. we developed the necessary libraries and deployed the first zk verifier on near. however that was very close to the tornado.cash sanction, and sentiment was tense around that time. we even developed a few compliance solutions, namely withdrawal screening via on-chain kyt (freezing funds in case of fraud), and deposit-screening by pki credentials.
recent designs provide better solutions than what we did back then (e.g. privacy pools allows for rage-quit instead of freezing funds; some allow users to collude to selective deanonymize certain actors; many protocols support better, more widely available, zk credentials such as zkpassport)
the cornerstone of chalice is the idea of cross-chain anonimity sets. through an mpc network such as near's, which provides chain signatures based on smart-contract logic, we can embue privacy potential into any transparent transfer on some chain, tapping into a bigger anonimity set.
our key example is how we can create a private intent that freezes zcash that can be claimed by proving a transaction on solana.
- originator and counterparty agree on an intent constituted by: recipient chain/address, counterparty, token pair, quote
- originator locks his zcash by sending it to an address controlled by near mpc
- counterparty waits for confirmation of the funds being locked on zcash
- counterparty transfers to the recepient the amount specified on the quote (both data already committed to)
- near light-client catches up to zcash, bitcoin, ethereum current blocks
- near light-client eventually receives wormhole message of the current solana block-hash
- counterparty proves inclusion of transaction
- public inputs:
- { zcash, bitcoin, ethereum, solana } last N block hashes
- exclusion list (e.g. sanctioned addresses)
- private inputs:
- original intent
- inclusion proof of intent commitment on zcash
- inclusion proof of counterparty transaction on solana
- circuit verifies:
- inclusion proofs well-formed
- locked funds of commitment tx matches the intent
- counterparty tx matches the intent
- no address is included in the exclusion list
- public inputs:
- near mpc emits a chain-signature, allowing counterparty to take the funds
as a result you can trade your zcash for any other transparent token, on any supported chain, without leaving any trace that your new transparent funds where bought with coins traceable to a mixer.
so not only can you onboard to zcash without leaving a trail, you can offboard off of zcash in a new address not tied to privacy protocols at all. and even more:
- you can simply use this as a private cross-chain escrow, but still trade transparently (maybe with stealth addresses)
- you can use zcash to fund a brand new wallet on any chain (you could further specify that you only want recently minted coins!)
- you can trade tokens between mixers on any chains (allowing you to boost anonimity in one of them)
- funds have to be locked for some time, counterparty may try to squeeze free option out of originator, effectively censoring (ironic huh? but this has a solution)
- compliance can still be applied, the only new primitive is connecting different chains through private intents, but on the destination chains the smart-contracts are still normal mixers which can borrow from any of the aforementioned complicance solutions.
- we are using near as the smart-contract layer for other chains which may not support such features (i.e. zk verification) such as zcash and bitcoin. were near mpc to be hacked, the chain signatures could be emitted without going through the verification step on the smart-contract. we still can use their scripting capabilities to introduce hack resistance mechanisms (e.g. freezing, allowing users to rage-quit in time to escape the hack).
- we could make it so claiming the note from the pool is a different signature that doesn't reveal the intent, all withdrawals take a long time, but withdrawals revealing their intent commitment go through instantly (a rage-quit in contrast would be the specific case where it would go back to the originator, if it failed deposit screening for example)
they all require different crypto, which's annoying. at the very least to support only bitcoin and zcash we already need sha256 and ecdsa
zcash and bitcoin should be sufficiently similar, it would be good if we could index all the merkle roots before the near light-client contracts got rid of them, because then we don't need to provide them and prove that the block hashes commit to the merkle roots.
- near's light-client is always catched-up to the latest block
- need to realize segwit txs are more annoying to commit to (the witness is only committed on the first transaction of the block, not in the header), so it's easier to exclude them from the anonimity set
- i don't think we have access to a merkle-root, the concurrency model is a bit weird, but we do have slot-hashes, that may be all we need (i.e. don't bother to assemble slots in a block, we already have inclusion guarantees of the slot hashes by virtue of them being stored in SysvarSl1otHashes)
- supported via light-client by near which makes things easy too
- for the use-case where zcash is always the maker/originator chain, we only need to care about the state of the near mpc controlled mixer address deployed on zcash (i.e. we only care about its merkle-roots, not all zcash txs)
- i really tried to figure out how near intents worked with shielded zcash but found no help so as of now we only support zcash on the transparent pool, but that's not a big design hurdle, as the funds will only briefly be unshielded (they can get immediately locked after unshielding, and immediately shielded again after claiming)
- because of that, for the use-case where zcash can be the taker/counterparty chain, i suspect the inclusion proof will end up being very similar to bitcoin's (cryptography and utxo model is almost identical and near supports the light-client)
requirements
- zero-knowledge
- succinct (not just sublinear)
- on-chain verifier (preferably WASM for NEAR but Solidity also works)
- reasonable mobile proving story
- branching (meaning you don't pay for circuit size for branches you don't need when proving)
no system satisfies all of these which honestly just hightlights how we failed as an industry.
alternatives:
- circom, groth16/plonk
- verifies on NEAR (we made the lib!)
- zero-knowledge & succinct
- best mobile proving amongst the options left
- no branching
- noir
- zk & succinct
- ok mobile proving (especially with mopro stuff)
- verifies on solidity & wasm (but I think it's not the NEAR supported system interface)
- no branching
- sp1
- succinct
- is a vm so it branches
- probably not good for mobile
- can wrap to groth16/plonk to verify on NEAR & become zero-knowledge
- becomes impossible to prove on mobile cuz requires 16 GB
IN RETROSPECT: miden satisfied all requirements and already compiled a verifier to wasm32-unknown-unknown. i missed that, could have helped. only question is if the miden program would run in few enough cycles as to not require much memory, to be able to prove on mobile.
by using circuits i'm paying for all the inclusion proofs (in circuit size) even though we only need to do 2: one for the originator chain, which is public; one for the counterparty chain, which is private.
i thought of a clever way to use recursive proofs to deal with the branching, but it's not like that is any better either.
zkvms would be better for this but none of them have reasonable
biggest hurdle is find of a good use-case supporting this that isn't otc and we can do counterparty discovery without risking being deanonymized by snoopers.
ideally we could have a system where counterparties commit to a zk credential and originators can flag them when they bail out, generating a reliability score
we could use a mpc auction for match-making. have to make the originator's filter public though. ideally only winning bid gets to learn originator's identity and vice-versa. ideally the cerimony in itself already outputs encrypted signed intents from both parties (similar to... zkp2p or renegade i think)
seems cumbersome to instrument and test, in sheer amount of work
TODO:
- host local solana devnet
- host local near devnet
- host local wormhole development environment (e.g. mock guardian)
- develop relayer smart-contract (SysvarS1otHashes111111111111111111111111111 -> wormhole)
- test that we can receive the correct slot hashes on near