We just did a fully trustless shielded ZEC to EVM atomic swap with no new signing primitives! And yes, it is programmable

Hello everyone!

As I might have indicated (non-discreetly) here before, we had been working for several weeks on a new paradigm for trustless cross-chain swaps between Zcash and Ethereum, especially shielded. The main issue was, as always, that there’s no scripting! And adaptor signatures can be frontrun and have no inherent concept of finality, yada, yada.

So, we realised, and as we shared with @daira , that due to Zcash utilising a Segregated Witness based scheme (v5 ZIP-244, and more recently, ZIP 229 for Ironwood), the transaction ID itself can be predicted, and more crucially, reliably manipulated in advance to “commit” to a transaction’s intent without the transaction actually needing to happen.

I have simplified a lot, but basically, after that happens, validating the transaction happened is simply a matter of SPV (we also published a paper on it which will be live soon, where we give several theorems about its security; I’ll post a rough draft soon if it’s wanted).

As such, we were curious: is such a bridge possible to be built where this transaction prediction primitive could be utilised as some sort of a trustless bridge mechanism? The answer being, yes!

We have deployed the Skyhook bridge contract at Ethereum Sepolia testnet: Address: 0x027D2aDC...AD2078c6F | Etherscan Sepolia

We performed the first atomic swap, as follows:

Now, at this stage, look closely at the logs of the transaction:

The proposal ID of 0xAD6268BBB8501254F9E4AEA5B3F52E43C9727D9B48D91560840537DC90083C34 is generated as a Keccak hash of the proposal details, and as such, the contract, using the arcane powers of a segregated witness, generates the transaction ID it HOPES that will exist on the chain at some point:

“txid: 0x97814ade349cb35110912872e0818892943a2f148a307b120c82a3c1476a86f6”

For people who work with Zcash/Bitcoin, that’s a little-endian representation. If we swap the bytes from the back to the front (such that 86f6 at the end becomes f686 in the beginning), also known as a LE->BE conversion or reverse32, we get 0xf6866a47c1a3820c127b308a142f3a94928881e07228911051b39c34de4a8197

And when we look it up on the Zcash testnet… what do we see?

VOILA! The transaction is here already, and it doesn’t reveal the sender or the encrypted ciphertext! Crucially, this is a bog-standard transaction made by a bog-standard light wallet, only it presets the psi, rho and rcm.

Now, we take the block in question, 0000100c77347a67dec5dfcd0284dbe75fa978fd33b33dc45f0535492b82aacf, and present it to the chain, again, trustlessly. This is a job done by oracles, but for illustrative purposes, we submitted it to Ethereum: Sepolia Transaction Hash: 0x22291933ab... | Etherscan Sepolia

Ethereum has now trustlessly validated the block’s existence and verified its Equihash solution, proving the block is real. Of course, in a real scenario, we will have finality mechanisms, several of which we outlined in our paper, and will talk about later in this thread.

Remember the 5,000 szabo we locked earlier? All party B has to do is present a Merkle proof that a submitted block contains the transaction that satisfies their proposal:

And the ETH is theirs! Sepolia Transaction Hash: 0x84c89a928e... | Etherscan Sepolia

We just did a fully trustless swap!

Disclaimer: these were not wrapped in zero-knowledge proofs, and as such, the blinding factors required to generate cmx were made public. While they still do not deanonymise you, fear not, we are diligently working to make them into NIZKs (via BLS12-381 Groth16), but this serves as a nice proof-of-concept!

B-but Strahn, is this just swaps? Adaptor signatures can do the same thing! I want more!

Aha, one would think that… but you would notice a curious detail, namely, remember that when party A placed their order, they simply advertised their pk_d and g_d, and did not have to sign anything?

That’s it! Using this primitive, it is possible to commit to transactions to an address and prove that it was included without actually revealing who you are! That means that it is possible for OTHER contracts to include the bridge and verify receipt of shielded transactions to a given address, which can be multisig (FROST comes to mind?)

That means it is possible to use this to create a trustless wrapper primitive to a guardian multisig, much the same as the red.bridge, but it is widely accessible (virtually any Turing-complete chain can implement this, including Solana, that reminds me, @bentertainer) and instead of relying on a committee of validators, it validates incoming ZEC trustlessly.

Further, for institutional users, who are scared of signing strange signatures such as adaptor signatures, the signature itself is a bog-standard, normal transaction send, so they do not have to migrate any systems and can continue to use their institutionally mandated 69 of 420 FROST+SSS signature scheme.

Wait… someone said this was… checks notes programmable? LOL.

It is. While not implemented yet (as we haven’t formally proven security), the compact digest we verify allows for a encCiphertext[0...52], which, funnily, is not REQUIRED to be ciphertext. Since it is ChaCha-Poly1305 encrypted, it is not possible for validators or your wallet to discern if it is actually a ciphertext or not, and thus it can be used as a mechanism to transmit inter-chain messages from shielded Zcash to the EVM, for example. While 52 bytes clearly do not seem like much, we have devised a mechanism not much unlike Taproot, which allows for cross-chain secure script creation with commit anonymity, too!

So, we want community input!

Is this something you want coming into Zcash? Should we apply for a community grant to productionise it? We’ll soon be making a post detailing how the programmability can work in the future. Feel free to ask any questions you like!

Crucially, it is on Sepolia and not Base (where it was originally intended) because to be fully honest, we run on basically no funding, so we have to make a request for donations:

Please donate if you support our work! (let us know if you do, we’ll also give you early previews of things we build in the future wink wink). Even small amounts are highly appreciated!

Donations go to: u1dcq3dr3ydfnafssthhqvc6ls7gngs6n8pcgmt74jag552lrfr2rjxnky5wf7hj65cc5c4j9c0p5dalxuzqdr2gneepml94eygy2ff97m9d8tp8mdmhxlhpyhcuzy75exykt82fcwt7phacq45s2y3gqdpk5dpeve2krtfqdchguj4ewc!

14 Likes

Follow-up post about programmability: Shielded to EVM cross-chain programmability while keeping the sender anonymous !

1 Like

Love your work, thanks for the detailed explainers. My initial reaction is there is something here that should be given research time to, I support. You’re moving fast, let me catch up! :smiling_face_with_three_hearts: :student:

3 Likes

Hey, man.
I have some questions and concerns. Should I share them here, or ask you in our DM?

1 Like

Feel free to ask here!

I love the direction you are going with this! I worked on a protocol very similar to this on the Nexa blockchain I called ‘Shadowchains’ (feel free to steal the name as it was never implemented) and I think perhaps the thing you have missed (or perhaps I have misunderstood) is this:

“Ethereum has now trustlessly validated the block’s existence and verified its Equihash solution, proving the block is real.”

I believe this is not actually true. It proves the block is valid, not real. This is an important distinction because it is possible to produce (and then submit to the network) a block that is valid but isn’t the block that is the winning block on zcash. This will happen every time two solutions are found within a short window. On Ethereum, because the blocks are so fast, an attacker doesn’t have much time to submit a conflicting solution but they do have time.

I’m sure you have thought of this, but just in case you haven’t, the solution is to have a mechanism in the ethereum contract to handle the fact that multiple valid solutions can be provided but only one is real. So even if a solution is provided it needs to be possible for alternative solutions to be provided as well, and for them to be built upon with the winner being decided within some reasonable time. It’s an annoying edge case, because it’s the kind you have to design for so that no one ever bothers to try and virtually fork the (shadow)chain, but it’s needed I think. Perhaps that was what you were referring to with ‘finality’.

I’d love to get involved in this if you want more help as I have a stealth project that would potentially amplify the utility of this massively.

2 Likes

Indeed! We mention this, as

We even devised some EVM-specific finality mechanisms, such as a GHOST-inspired n-rank finality system that uses block weights emergent as runtime such that oracles pay the cost of maintaining the tip weight, but again, it is still something we are discussing!

In a realistic scenario, the finality would be 8 or 24 blocks, as the transaction desires. I will do a writeup on it soon!

Sure!

Yeh, realistically the solution doesn’t have to perfect. I had also erred towards just setting a fixed sane block finality that would be beyond what would ever happen in practice. At the end of the day, it’s only a swap engine so the funds are only in it temporarily. If there is a major chain fork/reorg then you would just need a new contract linked to the correct chain tip. So perhaps all you also need is a kill switch in the contract so that you can kill it if you ever see a reorg/fork happen so that no one can use the contract any more.

Ours is not, in fact, just a swap engine. We did do a section about programmability! Read Shielded to EVM cross-chain programmability while keeping the sender anonymous !

B-but why though? GHOST-related schemes support orphan blocks natively, so will our scheme, without the risk of a DoS simply because it offloads the cost of submission to the malicious oracle, not you.

We will not need one!

2 Likes

Speaking of, I just realised something, if you do put some kind of a pause button on a trustless bridge for any reason, doesn’t it make it centralised? I mean, we do have one in testnet, but it’s for pausing in-dev.

Reading now.

I’ll have to do some more reading.

1 Like

I’ll catch you up lol, in short, GHOST schemes assign a “weight” to each block based on the weights of the blocks behind and after it. Since valid orphan blocks do attach to a parent but cannot provide a proof that they definitely are the longest chain by providing a chain of NUM_FINALITY blocks that use it as prevBlockHash, the network easily discards it, while honest blocks with a higher weight can provide a proof of finality. Further, we freeze block submissions after more than NUM_FINALITY / 2 blocks have been published.

So, the GHOST weights combined with a custom finality proof checker means that unless an adversary can mine blocks faster than the legitimate network before freezes happen, and they constantly mine from one specific fraudulent tip (pausing their legitimate mining operations entirely), then yes, in that theoretical edge case, the bridge can be hacked. Nakamoto Consensus tells us that its just ~2% likely that even if an attacker with ~50% power appears this attack will proceed. But again, that is a 51% attack scenario, and it means the network itself is also compromised, not just the bridge.

Of course, transactions can just choose to opt-out of this entirely and instead rely on a crowdsourced pool of blocks submitted by others for THEIR transactions to finalise their own.

Right, this all makes sense.

What just have to make sure of then is that the cost of the attack always (ideally significantly) outweighs the potential reward. So as the bridge gets increasing economic usage, the potential reward to attack it increases, but the cost to attack it is uncorrelated (depends on zcash mining reward value). So I guess it means it’s worth setting the finality threshold at what the potential usage could be, especially if you want to use it for smart-contracts as well. Potentially worth investigating. The fact zcash has ASICs and is the biggest coin with the equihash algo helps you a lot here though actually, because its not easy to just spin up a bunch of ASICs out of nowhere.

Yes! Exactly! The only real scenarios where we get hacked is a 51% attack, assuming a faithful oracle or at least a faithful counterparty to submit blocks or a fraud proof. We are gas viable on L2 networks like Base, where Coinbase’s cbZEC acts as a reliable 1:1 price anchor!

1 Like

Please donate if you support our work! (let us know if you do, we’ll also give you early previews of things we build in the future wink wink). Even small amounts are highly appreciated!

Donations go to: u1dcq3dr3ydfnafssthhqvc6ls7gngs6n8pcgmt74jag552lrfr2rjxnky5wf7hj65cc5c4j9c0p5dalxuzqdr2gneepml94eygy2ff97m9d8tp8mdmhxlhpyhcuzy75exykt82fcwt7phacq45s2y3gqdpk5dpeve2krtfqdchguj4ewc!

OK.

  1. After Party A locks ETH, what happens if Party B never broadcasts the matching Zcash tx? Is there a timeout/refund path, and how do you size it against Zcash confirmation depth so B can’t lose ZEC to a refund race? Also, how do you handle the free-option problem, where B only completes if the price moves in his favor?
    If you haven’t thought in the game theory side, I have some ideas for it.
  2. Both party A and party B are disclosing their connection of EVM address to the Zcash address. If you haven’t thought of a way for this, again, I can help.
  3. Since this design looks like a simple P2P-style swap/bridge, have you thought of ways to have ways for an intent-based-style of it, with market makers to do the work for a fee?
    Again, if you haven’t, I have some ideas.

I also had a question regarding the block confirmation, which you and @zcashminer1 already discussed.
Also, other than the transaction passing style you have, I think I have come up with a viewing-key style of verification, which if in your interest, we can discuss it to see which is more reliable, securer, and definitely less costly.

Yes, the contract does maintain a timeout state for the transaction. Well, I’m not sure if I coded this specific bridge to have a timeout, but I seem to recall I did plan to add one so.

A “refund race” is a mempool concept, which doesn’t apply here unlike classic adaptor signatures as all transactions are required to be mined into a block first, requiring you to spend considerable hashpower. And yes, a finality mechanism is planned!

Funnily, no. The sender on the ZEC side (party B) never discloses their sender address ANYWHERE, and as for the receiver, we are working on a BLS12-381 Groth16 CMX proof system to hide that by proving the cmx (commitment note hash) is valid. It is a b*tch to deal with because its Sinsemilla, which is a 255-bit hash, making it completely unfeasible in BN254, and we are only experimenting with BLS381 (where modadd is virtually native mod p, but modmul is still a headache)

Did you- I literally mentioned a programmable side to the post. Why is no one reading the post??? Shielded to EVM cross-chain programmability while keeping the sender anonymous

Why would that be needed? The entire point of this was to prevent a spending or viewing key from entering the process, a là trustless. Plus, that would be SHIT EXPENSIVE to do as a proof, trust me, we tried ages before we devised this.

Great.

Well done. If you had a problem with the theory or coding in this part, call me.

My bad :sweat_smile: Yeah, I haven’t find time to actually read that post, as well. :grin:

Well if you had it thought of and know it’s expensive, I won’t push. I didn’t know which would be faster and cheaper, hence the idea.

No, no, you’re right, on-chain would definitely be cheaper, the issue is off-chain it would make transactions completely uncomputable. A Groth16 circuit with 8M constraints takes upto 10 GB of RAM to compute. On most phones,that’s OOM!

Have you written the circuits to see how big they actually get? If you haven’t, I can put a bit of time on it to see how big it gets.

The project I sent you in DM, does the whole proving on any part client-side (on the browser), and that actually never took a lot of RAM on my tests of the project.

Actually, nevermind! You ruled my idea out because of another reason.