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.encodePackedblobs 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 thataddress(this) == arg1andblock.chainid == arg2- An opcode to verify the msg.sender’s identity, such as
OP_VERIFYSENDERSIG, which doesecrecover(keccak256(msg.sender), ext1, ext2, ext3) == stack1to 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 instack1 - 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!