Lock-Scripts: Programs as Payouts
A lock-script turns the payout of a Skyhook Bridge swap into a program. In a standard swap, a successful claim pays the escrow to the proposer and the swap ends. In a lock-script request, the escrowed funds pass instead to a program whose root the requester committed inside the Zcash note’s ciphertext, and the program decides how they are distributed. Anyone can trigger execution once the payment is final. The contract checks the script’s position, the transaction’s inclusion and the exact bytecode before running it on the ESMASTe virtual machine, and the outcome is final.
Setup
Alice enables a lock-script by setting the flag at byte 104 of her request blob, a byte reserved for this purpose. The flag makes the contract considerably stricter at acceptance time. The note’s ciphertext must be at least 52 bytes long, and its first 52 bytes (the head) must commit to a script root. Both requirements are checked when the intent is accepted, not merely recorded.
The contract then recomputes the compact digest over the note commitment, the ephemeral key and the 52 head bytes. This binds the script root into the same digest the accept path already verifies, turning a digest that would otherwise be taken as given into one the contract has checked, without adding any new trust assumption. The head is kept on-chain until the script runs.
Execution
Execution is permissionless and can happen at any later time. The caller passes three inputs to executeLockScript: the script’s bytecode, an inclusion proof for the transaction, and a proof that the script root sits at the claimed offset within the retained head. The contract checks them in a fixed order:
- the root’s offset must be one of the 21 legal positions, 0 to 20, which is all a 32-byte root allows within a 52-byte head;
- the transaction’s inclusion must verify against the anchor;
- the bytecode must equal the committed program exactly. A single differing bit means the wrong program, and there is no partial or fuzzy match.
executeLockScript accepts only those 21 positions, then checks inclusion and
exact bytecode equality.
If every check passes, the contract runs the program on ESMASTe, which can make native value calls; the funds are distributed only through those calls. The request is then marked completed. Because both the standard claim and the refund path check the flag first, both are permanently disabled, which makes the program’s decision final.
Failure semantics
The script has full control over the funds, including over what happens when a payment fails. A value call doesn’t revert on its own: TRANSFERNATIVE pushes 1 or 0, and the script decides what to do. The usual pattern is a JZ to a TRAP, and a trap reverts the entire execution, leaving the funds exactly where they were; a script can instead retry, or carry on with a reduced payout. Once a lock has executed successfully, however, it can never be refunded or claimed in the ordinary way: there is one execution and one outcome.
Rationale
Many payouts are not simple transfers: escrows with conditions attached, splits among several parties with uneven shares, releases that unlock at a certain time, or small automated market-makers operating within a settlement. All of these reduce to deterministic programs over committed inputs. Lock-scripts let the bridge express them, turning it from a payment rail into a settlement computer, without changing any line of the accept path that secures it.