Shielded to EVM cross-chain programmability while keeping the sender anonymous

This is a follow-up post to https://forum.zcashcommunity.com/t/we-just-did-a-fully-trustless-shielded-zec-to-evm-swap-with-no-new-signing-primitives-and-yes-it-is-programmable/57827, we advise you read it first. Crucially, this post deals in speculatives, and is not proven practically.

So, continuing the context from the last post, this post deals with the question:

Given only 52 bytes of context feasibly transmissible across the chains, is it possible to make it expressive enough to be programmable without turning useless?

Note: We COULD use the whole 580-600 byte space offered for memos, but then we would need to do twice the number of operations, even quadratically more, in the future when we switch to a proper zero-knowledge proof, so we decide to leave it unused and user-determinable.

Fortunately, the answer to that question seems to be yes, at least from our perspective.

Our answer: TapScript, but for memos.

A fundamental realisation happens when you think that the 52-byte field simply signifies a verified field, not anything that computes by itself. Meaning it can carry arbitrary data, so we need to stop thinking of programmability as something that executes, rather, something that declares.

We define two chains, R (reference ledger, e.g. Ethereum) and S (settlement ledger, e.g. Ironwood). Here, while S expresses or declares the intent, it is R that actually executes the intent, with the only purpose of its presence relative to S being its binding property with a given transaction.

So, it is entirely possible to simply commit a 32-byte Merkle root hash to the 52-byte field, and then deriving script paths from it.

A sample flow with relayers would be:

  • You devise two script paths, path A (which simply pays the relayer a fee and forwards it to another address), and path B (lock it in AAVe, and pay the relayer).
  • These script paths are encoded as EIP-712 objects with abi.encodePacked blobs that represent script bytecode (which we will define soon).
  • Each object is hashed via Keccak256, yielding a hash, which is then subsumed (folded) into a Keccak256 Merkle tree root.
  • You create a transaction request or fulfill someone else’s request, and in your proposal’s memo, you privately commit to this Merkle tree hash.
  • At this point, you don’t necessarily need to claim the Ethereum or any other locked token, which can stay that way forever, as the other party has obtained their ZEC and is happy.

In an arbitrary point in the future, you can contact a relayer and reveal to them a leaf from the tree and its preimage, which they can post to the blockchain to claim the fee, or you can post it yourselves, and the bridge contract will interpret the script and execute it in context.

This is remarkably similar to the fusion of two major protocols, Taproot (from Bitcoin) and the Entrypoint architecture from EIP-4337 Smart Wallets.

The bytecode itself can be relatively simple, with uint8/uint16 opcodes followed by a designated set of arguments, while a “stack” can be provided for hardcoded inputs, and an external stack for relayer inputs.

Some opcodes I can envision are:

  • OP_VERIFYCONTEXT: takes address arg1, uint arg2, and verifies that address(this) == arg1 and block.chainid == arg2
  • An opcode to verify the msg.sender’s identity, such as OP_VERIFYSENDERSIG, which does ecrecover(keccak256(msg.sender), ext1, ext2, ext3) == stack1 to pull external stack variables 1, 2, 3 (v, r, s) to verify if the sender is authorised by a script-specific signature whose address is provided in stack1
  • Other fundamental opcodes for EVM operations.

How does everyone think about it?

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!

2 Likes

This is actually very clever. So the merkle tree is like MAST, but instead of executing the code on zcash, you just execute it on Ethereum/any bridged chain?

Again, I worked on something similar for Nexa which I called MAST Contract Paths Notion

but the execution was on the chain, which is actually more limiting as Nexa is just UTXO-based. Whereas your idea can have literally anything implemented on the EVM side.

Have you thought about if there is a way to have the zcash side be controlled by the EVM side as well?

1 Like