SkyhookIdeas: Future Directions

The Bidirectional MAST Engine

Design direction, not funded

The bidirectional MAST engine is a proposed extension of Skyhook’s lock-scripts that inverts how escrow is released. Today a lock-script request escrows funds and later releases them by executing a committed program on ESMASTe. In the proposed direction, funds are escrowed against a committed set of constraints, and anyone who makes a Zcash payment satisfying them can claim the escrow. The claim is checked by pure verification, with no virtual machine involved, and the lock can hold several alternative sets of terms in a Merkle tree while revealing only the one that is exercised. The rest of the lifecycle, and every existing claim path, stays unchanged.

The inversion

The proposed direction keeps the skeleton of a lock-script swap and replaces its ending. Funds are escrowed against committed constraints rather than a committed program; a ZEC holder builds a transaction that satisfies them; and the claim path verifies that transaction before paying out, replacing execution with verification at every step.

A constraint check compares fields of the Zcash action with what the leaf expects: the recipient key pk_d, the diversified base g_d, and the value. Exact equality covers fixed-price arrangements, in which a lock names precise recipient keys and an exact amount. Bounded ranges could cover floor and ceiling prices in a later revision, at the cost of more audit surface; version one restricts itself to exact equality on the three fields, because every generalization multiplies the review burden while the core mechanism is still unproven in production.

The MAST structure

MAST stands for Merklized Alternative Script Tree, a term taken from the vocabulary of Bitcoin’s Taproot. The lock commits to the Merkle root of a set of acceptable leaves, each encoding one complete claims package: the field constraints, the payout policy and the authentication parameters.

Claiming one branch of a MAST lockInteractive

The lock commits only to the root. Choose which leaf a claim exercises.

MAST root (committed)
Branch AB
Branch CD
Leaf A pk_d = alice…, value = 10 ZEC
pay claimant, single-use
hidden
Leaf B pk_d = bob…, value = 25 ZEC
pay claimant, single-use
hidden
Leaf C pk_d = carol…, value = 4 ZEC
pay per delivery, multi-fill
hidden
Leaf D pk_d = dave…, value = 60 ZEC
pay claimant, single-use
hidden
  1. Inclusion: the transaction folds to a finalized anchor root
  2. Leaf membership: the revealed leaf folds to the lock's MAST root
  3. Field constraints: pk_d, g_d and value match the leaf exactly
  4. Realness binding: the recomputed note commitment equals the claimed one
  5. Authentication: the ticket recovers to the leaf's script key
  6. Nullifier marking: the note is recorded as spent
The claim reveals the exercised leaf (dark), plus the sibling hashes needed to fold it to the root (blue). Every other leaf stays hidden permanently, so observers learn the terms that were satisfied and nothing about the alternatives. The six gates then run in this fixed order, with no virtual machine involved. Keys and hashes are illustrative.

A claim reveals exactly one leaf, together with its Merkle proof into the committed root, and verification checks the path before examining the leaf’s contents. The other leaves stay hidden permanently: an observer of a settled claim learns which terms were satisfied but nothing about the alternatives the lock’s author considered. Arrangements with several possible recipients can therefore share one lock without advertising the full list to competitors or to surveillance. A lock with a single set of constraints is simply a tree with one leaf, checked by the same code.

The six gates

A claim in the inverted direction passes six checks in a fixed order, each assuming the ones before it:

  1. Inclusion. The transaction’s Merkle path folds to a finalized anchor root through the standard computation.
  2. Leaf membership. The revealed leaf hashes through its proof to the request’s MAST root, using the established proof pattern.
  3. Field constraints. The decoded action fields match the leaf exactly, with no tolerance or partial credit.
  4. Realness binding. The recomputed note commitment equals the claimed one, which ties the claimed randomness to the fields so that action contents can’t be invented independently of the commitment.
  5. Authentication. The claim’s ticket is confirmed before any state changes, as specified in Ticket Authentication.
  6. Nullifier marking. The note is recorded in the shared spent set, so it can’t settle twice through either direction.

Opaque consumption

Throughout the inverted path, verification consumes committed bytes exactly as stored. Ciphertext regions enter the checks without being decrypted, since plaintext is never needed where committed bytes suffice, and signature components enter recovery without normalization or canonicalization, preserving honest creator output byte for byte. The recovery identifier is treated as pseudorandom output over the committed bytes and is only range-checked. Each transformation left out is one that can’t introduce a divergence between independent implementations; since honest output is already pseudorandom, canonicalizing it would add checks without adding security.

Shared nullifier set

The inverted direction marks the same nullifier set as the existing one. A note spent through a constraint claim can never later settle a program execution, and vice versa, so neither direction can pay twice for any note, however many locks refer to it. The two directions interact only through this shared state, not through any awareness of each other: each verification path stays independently auditable, with no logic about the other beyond the common marking operation.

Cost and privacy

Inverted claims always cost less than program executions, because no virtual machine runs. The six gates have bounded costs: two Merkle paths for inclusion and membership, one hash recomputation for the commitment, one signature recovery for the ticket, and two storage writes recording the nullifier and completion. Because there is no program whose complexity could vary the cost, budgeting is exact for every shape of claim.

The privacy gain is asymmetric. The fields a leaf constrains, the recipient’s address and the value, are revealed to the contract when a claim is made, as in today’s swaps until Milestone 3. The lock’s unexercised alternatives, however, stay hidden behind unrevealed branches, so the design reveals exactly the terms that were exercised and nothing more.

Relationship to the current engine

The proposal changes no existing path. Program-execution claims continue through the runner with identical semantics, and the inverted direction adds a parallel claim entry that reuses the codebase’s proven patterns: escrow, digest binding at accept time, inclusion proofs, commitment recomputation and nullifier discipline. Because both directions share these patterns, a single audit can cover both, which keeps the marginal cost of the extension small.

graph LR REQ["request + escrow"] --> ACC["accept (digest binding)"] ACC --> EX["execute program leaf<br/>(current)"] ACC --> SAT["satisfy constraint leaf<br/>(proposed)"] EX --> PAY1["program disburses"] SAT --> PAY2["direct disbursal"]

See also