SkyhookChain Finality

Proving Chain Identity

Chain identity is the question of whether a set of Zcash blocks belongs to the real Zcash network. Valid blocks and correct inclusion proofs don’t settle it: an attacker can mine a private fork under exactly the same consensus rules, with genuine proof-of-work and well-formed Merkle structures, and every other check would pass. Skyhook answers the question with FlyClient sampling.1 Each rollup must supply blocks sampled from the history behind its main block, and producing those blocks at the required difficulty would cost a forger as much as out-mining the real network. The sampling starts from the bridge’s last trusted block, whose difficulty also sets a minimum floor that the contract enforces.

Threat model

The adversary considered here follows the consensus rules exactly and does real mining work on a private branch. Its headers, proofs and commitments are valid in every technical respect; the branch simply isn’t the chain everyone else uses. Format checks and work checks both pass against such an adversary. What distinguishes the fork is the amount of work behind it: a private branch has far less accumulated work than the real chain, and the defense is built on making that difference visible cheaply.

Sampling

FlyClient replaces downloading the whole history with checking a small, unpredictable sample of it. A rollup’s tail samples are blocks drawn from the history between the last finalized block and its main block. Skyhook uses full, multi-sample FlyClient by default. Each verification performs several independent FlyClient sampling runs of about 5 × log₁₀(gap) samples each: ten runs for a gap of 1,000 blocks or more, five below 1,000, three below 500 and two below 100. A single run is what the FlyClient equation itself calls for; repeating it independently makes evasion exponentially less likely, and the ten-run figure is the worst case. The totals grow logarithmically: about 30 samples for a 100-block gap, 150 for 1,000, 200 for 10,000 and 250 for 100,000, so a gap a hundred times longer than 1,000 blocks needs only 1.66 times as many samples. For a sparse rollup, each gap between non-consecutive blocks is sampled the same way. The samples are derived with the Fiat–Shamir heuristic from the block headers, so no one chooses them, and a forger who wants a different set has to mine a different block.

Each sample must carry a valid Equihash solution and a valid difficulty. Because the forger can’t know in advance which blocks will be required, it can’t get away with forging only some of them: to survive arbitrary samples it must forge the entire range, at the full proof-of-work cost of the real network over the same span. With this many samples the chance of evasion is negligible even if no participant checks anything off-chain, which is why it is the default; the cost of verification still grows only logarithmically with the length of the chain. That trade-off is the central result of the FlyClient work.

Sampling is affordable because of oracles. A sample that is already in the block store was verified when it was submitted, so a rollup only references it. If no oracle has stored the blocks, a swap can submit them itself at about 950k gas each; a 1,000-block gap then takes nine transactions, a little under two dollars on Base.

The difficulty floor

Each bridge has a “genesis”, its last trusted block, set when the bridge is deployed and updatable later by agreement. Its role is narrow: its difficulty is the minimum difficulty the contract accepts for submitted blocks. The floor exists because Zcash’s difficulty adjustment algorithm, DigiShield, can’t be replicated effectively on Ethereum, so the contract enforces only a lower bound while participants check the real expected difficulty themselves, off-chain.

The difficulty floor, and who checks whatInteractive
Contract
Careful joiners

The floor is set when a bridge is deployed and updated by whatever process its deployer chose (a DAO or a multisig, for example). The contract only checks that a sample's difficulty is valid and above the floor; participants check the real difficulty themselves and won't join a rollup that fails. A stale floor can at worst invite spam. Difficulty values are illustrative, on an arbitrary scale.

This makes the floor “trusted” only in quotation marks. It protects users who trust a bridge blindly; careful participants recompute the real difficulty and refuse to join a rollup that fails, whatever the floor says. Conversely, a rollup may ignore the floor if all of its participants agree off-chain. If a careless operator or DAO lets the floor fall out of date, the worst outcome is spam (a denial-of-service nuisance), and even that doesn’t stall the bridge.

Skyhook’s contracts are a public good that anyone can deploy, much as Uniswap v2 is. Each deployer, such as the Zcash Foundation, sets its own floor and chooses how it is updated, for example through a DAO or a multisig. That choice belongs to the deployment, not the protocol.

Test networks

Testnet blocks carry valid proof-of-work and are correctly formatted, so a defense based on format would accept them. What excludes them is discontinuity: the test network has a different genesis and an entirely separate history, so its blocks can’t link to a mainnet bridge’s last trusted block or satisfy its samples. No special rule is needed, because chain identity reduces to continuity with the bridge’s starting point.

Online-offline finality

Full sampling protects participants who check nothing themselves. Parties who are willing to check the chain can agree to a lighter mode, online-offline finality, when they agree the quote. Rollups built this way are called root-anchored. The quote then carries a block root: the root of the counterparty’s Merkle Mountain Range at the last block it knows. Because both parties have agreed to that root, any block proven to lie under it is a block they already trust, and it skips proof-of-work verification entirely. Each sample is instead checked with an MMR proof against the root, for about 21,500 gas plus about 15,000 for its calldata and slicing, so 36,500 gas per sample against about 950,000 for verifying Equihash.

GapSamplesSamples’ gasWith the rollup’s 4MTransactions
100 blocks30~1.1M~5.1M1
1,000 blocks150~5.5M~9.5M1
100,000 blocks (about three months)250~9.1M~13.1M1

The constant 4M gas covers the rollup’s blocks after the main block. Because every case fits under Ethereum’s per-transaction cap of about 16.8M gas, online-offline finality always completes in a single transaction, even if no oracle has submitted a block for three months.

The mode is only as sound as the check behind the agreement. The party whose escrow is at risk, usually the requester, must compare the root with its own view of the chain, for example from its own node, before accepting the quote; a root accepted without checking would let the counterparty prove a payment against a history it invented. Neither party can impose the mode on the other: without an agreed root, the default full sampling applies. And because a root is only as good as the check behind it, a root-anchored rollup never becomes the starting point for a fully sampled one; the anchor keeps the two apart.

Identity and finality

Identity and finality are separate properties, and a claim needs both. Identity establishes which chain the blocks belong to; finality establishes that the block holding a payment is deep enough to stay there, which is what the head blocks of a rollup provide. A block can belong to the main chain but be too recent to be final, or be buried under blocks of a foreign chain.

See also

References

  1. Bünz, Kiayias and Leonardos, FlyClient: Super-Light Clients for Cryptocurrencies, IEEE S&P 2020. ↩