Release: Zakura 1.2.0, 87% faster full block processing
We have released Zakura 1.2.0, a performance release. Since zcashd reached end of support, the network has seen elevated orphan rates. This release removes the largest cause we found: nodes were performing redundant proof verification during block acceptance. On full Ironwood blocks with regular transactions, block processing time on our benchmark machines drops from 2.5 seconds to 0.32 seconds. For median block times, that is nearly a 5% reduction in wasted work.
We recommend that all node operators update. Mining pools benefit the most: the work removed here directly affects miner revenue.
This release also adds streaming block-range backfill to the indexer gRPC service (thanks to @maxdesalle) and hardens the indexer RPC listeners.
Evidence something was wrong on the network
We profiled orphan rates on Zebra 4.3.0 months ago. Mainnet orphan rates were around 0.1%, and our simulated, far more decentralized testnet was around 1%.
Since zcashd reached end of support, there has been evidence of repeated performance regressions. Outlier block times increased, with far more 10+ minute blocks than statistically expected, and on July 31st the network's orphan rate reached nearly 1%, far above the old 0.1% to 0.2% baseline. Our first fixes patched peer connectivity after the upgrade and removed disconnects on slow blocks, which appears to have reduced the initial spikes of 10+ minute blocks. Even so, the baseline orphan rate settled around 0.3%, with spikes to 1% on days with many quarter-filled blocks. Zakura treats orphan rate as a core performance metric, so we kept investigating.
Re-running the same analysis now, Evan saw orphan rates on Zakura of 3.2% with 75-second blocks and 42% with 20-second blocks.
Every proof was being verified twice: once when the transaction arrived over mempool gossip, and again when it arrived inside the block that included it. Measured on one machine with matched block sizes, a block's Halo2 batch weights summed to exactly the block's Orchard action count plus the mempool's. This slowed block processing, and with it the miner's switch to the new block.
Zebra used to avoid this duplication with a proof-check cache, and our distributed tests traced the regression to that cache's removal in Zebra 4.3.1 (Zebra #10494) as part of a critical security fix: the old cache stored the verdict valid(transaction, height, block time, spent outputs) under a key that did not determine that proposition.
Caching proof verification results
Zakura 1.2.0 caches the stateless proof verification itself rather than the verdict. Verifying a single Halo2 item against a verifying key is a pure function of its inputs, so a cache hit is bit-identical to the computation it replaces. Nothing else is skipped. On a cache hit, transaction verification still runs end to end at the block's own height, with the block's time and the block's spent outputs. The cache key commits to the full transaction, so a malleated transaction cannot reuse a cache hit.
The flow now looks like this:
Before: receive block -> verify every proof again -> finish block checks -> update template
After: verify each proof once, in the mempool
|
receive block -> reuse the proof results -> run every block check -> update template
The change is Zakura #597. It moves the duplicated work out of the critical path between receiving a block and updating the miner's block template.
Benchmarking the update
In our globally distributed full block experiments, this duplication was the single largest contributor to the orphan rates we were seeing. These runs use an internal testnet of 77 to 80 nodes with CPU miners and deliberately compressed block-time bands, so the absolute numbers describe that environment, not Mainnet. The runs also include fixes that keep CPU miners from finishing a solution on a stale block.
In the 25-second band, the orphan rate fell from roughly 30% to about 3.6%. In the 75-second band, we measured 1.57%.
That puts 25-second blocks back in a safe range. Both block propagation and CPU time can still improve, and more CPU improvements are coming next week.
Operators with dashboards can watch the cache directly through the new zakura.consensus.halo2.cache.hit, miss, insert, and evict counters and the zakura.consensus.halo2.cache.size gauge.
Indexer block-range streaming
Indexers backfilling history through the indexer gRPC service previously paid one unary request per block, and each GetBlock read the block's raw bytes from storage, fully deserialized every transaction, then re-serialized the block back to the bytes it started from.
Zakura 1.2.0 adds GetBlockRange, proposed and contributed by @maxdesalle. It streams a height range of finalized blocks in ascending order and serves them from the stored raw bytes, without that round trip. The stream covers only blocks committed to the finalized state and ends cleanly at the finalized tip, matching the existing contract for finalized history. Serving block ranges requires building with the indexer compile-time feature.
Security
- Indexer RPC listeners are hardened. Mutual TLS is now required for indexer RPC listeners on non-loopback addresses, indexer connections and streams are bounded, and blocks without a coinbase height are rejected before state preparation.
- Protocol services enforce their frame caps. Zakura protocol services now enforce their declared frame caps for inbound and outbound application frames, rather than declaring them and accepting larger ones.
Other improvements
- Chain work is stored at its exact width. Block work and cumulative chain work are now kept at their full 256-bit width, so valid hard targets are representable instead of failing conversion. Fork choice and the committed chain history root are unchanged for Mainnet and Testnet, and no database migration is required.
- Maximum block time activates per network. The maximum-block-time activation height is now a per-network parameter. Configured networks and Regtest activate the MTP-plus-90-minutes rule at height 2 instead of inheriting the public Testnet height, generated local networks activate it after their seed chain, and Regtest accepts a
max_block_time_start_heightconfig field. Mainnet and public Testnet behavior is unchanged.
End-of-support halt
Zakura 1.2.0 keeps the 40-day support window introduced in 1.1.1. Mainnet nodes running this release halt at height 3,495,707 (~2026-09-23). Upgrade warnings keep their three-day lead and begin ~2026-09-20.
Updating
You can update using our signed release binaries or by upgrading the zakura crate to 1.2.0 via Cargo. See the download page for installation and verification instructions.