extranonce1 len is not 8: A Z15 Pro, a ZIP-301 Detail, and an Answer Hidden in a Domain Name

A debugging journey through Zcash mining, the Stratum protocol, and the gap between spec sheet and actual machine behaviour.

Hashrate Index
Hashrate Index

A lot of the debugging work in this article was done jointly by Ethan Lang and colleagues. Leandro Yampolsky in particular was a real rock during the first wave of issues.


The log line that never finished

In early June 2026, an Antminer Z15 Pro kept printing the same few lines, over and over. Its clock was still fresh from the factory — the first entries are stamped 1970-01-01, until the RTC syncs and they jump to 2026-06-04. It dutifully marched through a full self-check: three hashboards, PIC firmware versions, fan speeds, three EEPROMs… all fine.

And then it hit a wall:

time="2026-06-04 04:49:37" level=error pid=1415 msg="extranonce1 len is not 8"
time="2026-06-04 04:49:37" level=error pid=1415 msg="JSON-RPC call failed: (unknown reason), req_id=0"
time="2026-06-04 04:49:37" level=error pid=1415 poolno=0 msg="...retry after 5 seconds failures 1"

Five seconds later, it started over. Hit the wall again, retried again. The hardware was healthy, the network was fine, yet the machine could never reach the one step that mattered: actually mining. It couldn’t connect to Luxor’s ZEC pool.

The only line with any signal in it was that red one: extranonce1 len is not 8.

And the most baffling part: by the book, what we were sending was perfectly legal.


First, what extranonce1 is — and why its length could break anything

Zcash mining runs on the Stratum protocol defined in ZIP-301. It differs from Bitcoin’s Stratum in one crucial way: how the nonce is split.

The nonce in a Zcash block header is 32 bytes. The pool and the miner divide those 32 bytes between them:

  • The pool sends down NONCE_1 (what we usually call extranonce1, or en1) in the mining.subscribe response — the first part of the nonce;
  • The miner owns the rest, NONCE_2, and enumerates that space looking for a solution.

ZIP-301 imposes exactly one hard constraint here:

The server MUST pick such that len(NONCE_1) < 32 in bytes.
The miner MUST pick such that len(NONCE_2) = 32 - len(NONCE_1) in bytes.

In other words, the spec only sets an upper boundextranonce1 is legal as long as it’s strictly under 32 bytes (fewer than 64 hex characters). It never says how many bytes it must be.

The en1 Luxor was sending was 6 bytes — 12 hex characters. Far below the 32-byte ceiling. By ZIP-301, beyond reproach.

But the Z15 Pro’s 2023 firmware disagreed. When it received the en1, it ran a hard-coded check on whether that hex string was exactly 8 characters long. We gave it 12. 12 ≠ 8, and without hesitation it errored out, disconnected, and reconnected — treating what was meant to be a suggested value as a contract that must match exactly.

ZEC miner connection and en1 size determination flow
Figure 1: The full picture of the final design — several domains route through a single GCP load balancer into the Luxor ZEC backend, where the en1-size logic decides the length to serve. (Note: the default value in the diagram’s inner boxes should be read as stated here — the default en1 is 4 bytes, i.e. 8 hex characters.)

The failed handshake looks like this:

Failed handshake: the Z15 Pro receives a 12-hex en1, fails its hard-coded length check, and retries forever.

The core tension was now in plain sight: legal by the spec is not the same as runnable on the device. The firmware was stricter than the spec — and, as we were about to learn, stricter in a way that varied by version.


Not one magic number — a whole field of them

The natural first move: it wants 8 hex characters (4 bytes), so just change what we send to 4 bytes. The 2023 firmware was indeed happy — but this was like yanking a too-short blanket toward one end of the bed.

At almost the same time, a community miner’s investigation (@HobbyistMiner) surfaced a more painful fact: different Z15 Pro firmware versions want completely different en1 lengths — 2023 firmware wants 4 bytes, FR-1.9 firmware wants 8 bytes, and still other versions want other values.

Which means there is no single “correct” en1 length. Every fixed value you pick pleases one batch of miners while breaking another:

en1 length servedhex charsZ15 Pro 2023 fw (wants 4B)Z15 Pro FR-1.9 fw (wants 8B)
4 bytes8
6 bytes12 len is not 8
8 bytes16
Adaptive by hostnameadaptive

Only the last row is what we actually wanted. The question was: how?


Why we couldn’t just “deploy one pool per length”

The most direct engineering answer: spin up a separate pool instance for 4, 6, and 8 bytes, each on its own public port.

But that road has very real costs — multiple instances, multiple public endpoints, multiple monitoring and operational burdens; not to mention forcing every miner to figure out “which port is mine?” For a public pool meant to serve the whole network, that’s a non-starter.

What we wanted was one deployment, one public endpoint that could still serve different lengths to different miners. So the problem narrowed to a single point:

At the instant of the handshake, how does the pool know how many bytes this particular miner wants? The firmware doesn’t volunteer its version.

The answer was in the handshake all along

Reading the ZIP-301 mining.subscribe parameters again, this time word by word:

mining.subscribe params:
  [0] MINER_USER_AGENT   miner software / version
  [1] SESSION_ID         used to resume a session, may be empty
  [2] CONNECT_HOST       the hostname the miner connected to   ← it was here all along
  [3] CONNECT_PORT       the port the miner connected to

The third parameter, CONNECT_HOST — the miner volunteers which hostname it dialed. This field was meant for vhost-style routing, and we’d never given it a second glance.

That was the spark: if we offer several hostnames that all point at the same load-balancer IP, then which hostname a miner connected to becomes a declaration of the en1 length it needs.

zec.global.luxor.tech           ->  34.120.179.99   (unchanged, default 4 bytes)
zec.global.en1size4.luxor.tech  ->  34.120.179.99
zec.global.en1size7.luxor.tech  ->  34.120.179.99
zec.global.en1size8.luxor.tech  ->  34.120.179.99

Four domains, one IP. An FR-1.9 miner that needs 8 bytes simply points its pool address at zec.global.en1size8.luxor.tech; it reports that hostname verbatim in mining.subscribe, and the pool reads off “this one wants 8 bytes” at a glance. Zero firmware changes on the miner side — just one connection address.

If the trick sounds familiar, it should: it’s the same wisdom as Bitcoin miners stuffing difficulty into the password field of mining.authorize (e.g. d=8192) — borrowing an existing protocol field to carry out-of-band configuration. We didn’t invent a new protocol; we just noticed an overlooked surface in the old one.


How it works

For every session handshake, the pool decides how long an en1 to serve using this logic:

en1 size determination flow: the pool parses the connect host, validates the requested length, and serves an exact N-byte en1 or falls back to the default.

A few design choices worth calling out:

  • Adaptive, but bounded. Each en1 is a random prefix plus a per-session incrementing counter (the random prefix keeps different sessions from colliding in nonce space), so the length we can serve has a floor and a ceiling — comfortably covering the 4-to-8 bytes real firmware asks for. Any out-of-range, unparseable, or unrecognized hostname falls back safely to the default length — zec.global.luxor.tech rides exactly that default path.
  • Each length stays isolated. Every recognized length gets its own dedicated nonce space and its own random prefix, so different lengths and different sessions never collide.
  • The length has to be exact. The en1 we serve is exactly N bytes — that is, 2 × N hex characters. Because — as that one miner taught us the hard way — the firmware counts hex characters, and not one can be missing or extra.

What we took away

This investigation started with a single line, extranonce1 len is not 8, and ended on a DNS record. Looking back, three things are worth writing down:

  1. The spec defines what’s legal, not what runs. ZIP-301 allows an en1 of anywhere from 1 to 31 bytes; but real-world firmware reads that same spec in ways far stricter — and mutually contradictory. When you build a protocol against hardware, go by how the devices actually behave, not by the letter of the spec.
  2. When there’s no single right answer, stop hunting for a magic number that doesn’t exist. Changing 6 to 4 just moved the pain from one batch of miners to another. The real way out is a mechanism that can hold every answer at once.
  3. The most elegant fix is often not a new layer of protocol, but an overlooked surface in the old one. Miners already report the hostname they dialed in mining.subscribe — all we had to do was start listening.

That Z15 Pro runs happily now. It connects tozec.global.en1size4.luxor.tech; the pool reads, right at the handshake, that it wants 8 hex characters, and serves it a clean 4-byte en1. It hasn’t had to start over since.

— Happy Hashing!

About Luxor Technology Corporation 

Luxor delivers hardware, software, and financial services that power the global compute and energy industry. Its product suite spans Bitcoin Mining Pools, ASIC Firmware, Hardware trading, Hashrate Derivatives, Energy services, a Miner Management software, Commander, and a bitcoin mining data platform, Hashrate Index.

Disclaimer

This content is for informational purposes only, you should not construe any such information or other material as legal, investment, financial, or other advice. 

Bitcoin Mining ConceptsMining Hardware (ASIC)