Proof-of-Work Verification on the EVM
Skyhook contracts verify Zcash’s Equihash proof-of-work directly on the EVM, because a correctly formatted header carries no evidence on its own that anyone mined it. Equihash is asymmetric by design: finding a solution takes a great deal of computation, but checking one is cheap. Miners do the search, and the contract does only the check. In Skyhook the check happens once per block, when an oracle submits the block as an orphan, a header whose parent the contract may not have seen. After several rounds of optimization this costs about 950k gas, and every rollup that later uses the block references it by hash instead of verifying it again.
Orphans
Every block enters the contract through submitOrphan, and nothing in the existing chain vouches for an orphan, so accepting it on formatting alone would let anyone invent history. Formatting is free to produce, while work is the one scarce input, so the contract verifies the solution completely: it runs the Wagner verification over all solution indices, with no shortcuts and no partial credit, and only a block that passes is stored. A block whose predecessor is already on-chain is then linked to it with linkOrphan, in the same multicall; linking records the order of blocks but doesn’t replace the proof-of-work check.
Treating every block as an orphan is what lets oracles submit blocks in any order, and lets a rollup sample blocks from anywhere in history without the chain between them having been uploaded.
How verification works
An Equihash solution is a list of indices into a generated sequence of strings. The strings at those indices must XOR to zero, and the indices must satisfy further ordering rules. Miners find solutions with Wagner’s algorithm, which organizes the search into successive rounds of collisions; the verifier only regenerates the strings at the listed indices and checks the XOR and ordering conditions against the final result.
Verification is cheap because its cost depends on the size of the solution rather than on the search effort. The solution names its indices explicitly, so the verifier hashes a small, fixed set of strings, whereas mining cost grows with the full search, which the verifier never repeats. This asymmetry is what makes on-chain checking practical.
Optimization
The first implementation cost nearly 4M gas per orphan, dominated by hashing and set operations, which put orphan handling at scale in doubt. Successive passes brought the cost down to about 950k gas per block on the deployment profile, including writing the block to the store:
- the Wagner rounds are written in assembly;
- leaf unpacking uses a faster path;
- expansion loops are unrolled;
- distinct-element handling avoids repeating work across batched proofs.
Verifying distinct proofs in a batch shares their setup cost further; a batch of twenty brings the per-proof price into a practical range for routine operation. The general rule behind each change was to recompute as little as possible on-chain, and each retained byte reflects a trade-off documented in the code.