Skip to content

Deep Dive: mining.subscribe, mining.authorize, and mining.configure

Before a miner can hash a single block header, it needs to complete a handshake with the pool. This handshake is where the miner and pool agree on the rules of engagement: how to identify shares, how much nonce space the miner gets, and what protocol extensions are in play.

In this article, we will dissect three methods that make up this handshake: mining.configure, mining.subscribe, and mining.authorize. We will look at every field, every edge case, and every quirk.

In modern mining, mining.configure is actually the first message sent — even before mining.subscribe. It was added later as a protocol extension (BIP 310) to handle features that the original Stratum design did not anticipate, most importantly version rolling.

The Bitcoin block header has a version field (4 bytes). Originally, this was meant to signal which protocol version the block uses. But mining hardware discovered that you could repurpose unused bits in this field as extra nonce space. Instead of only iterating over the 4-byte nonce, the ASIC can also flip bits in the version field, effectively multiplying the search space.

This technique is closely related to AsicBoost, an optimization that certain ASIC designs use to speed up SHA-256 hashing. For version rolling to work, the miner and pool need to agree on which bits of the version field the miner is allowed to change. That is what mining.configure negotiates.

{
"id": 1,
"method": "mining.configure",
"params": [
["version-rolling"],
{
"version-rolling.mask": "1fffe000",
"version-rolling.min-bit-count": 2
}
]
}

Let us break down the parameters:

First parameter: extension list An array of extension names the miner wants to use. Currently, "version-rolling" is by far the most common (and often the only) extension. Other extensions could be added in the future.

Second parameter: extension parameters A dictionary of settings for each requested extension.

  • version-rolling.mask: A hexadecimal bitmask ("1fffe000") that represents which bits of the version field the miner wants to modify. In binary, 1fffe000 is 0001 1111 1111 1111 1110 0000 0000 0000. The 1 bits are the ones the miner wants to roll.

  • version-rolling.min-bit-count: The minimum number of bits the miner needs to be able to roll. If the pool’s mask provides fewer rollable bits than this, the miner considers the negotiation failed.

{
"id": 1,
"result": {
"version-rolling": true,
"version-rolling.mask": "1fffe000"
},
"error": null
}
  • version-rolling: true — The pool agrees to support version rolling.
  • version-rolling.mask — The mask the pool actually allows. This might be a subset of what the miner requested. The miner must AND this with its own mask to determine the final set of rollable bits.

If the pool does not support the mining.configure method at all, it may respond with an error or simply ignore the message. Well-behaved miners should handle this gracefully and proceed without version rolling. Some older pools will respond with:

{
"id": 1,
"result": {
"version-rolling": false
},
"error": null
}

In this case, the miner must not modify the version field in any block headers it constructs.

mining.subscribe: Registering with the Pool

Section titled “mining.subscribe: Registering with the Pool”

After mining.configure (or immediately upon connecting, for miners that do not use extensions), the miner sends mining.subscribe. This is the most important handshake message because it establishes the miner’s identity within the pool’s system and assigns the extraNonce parameters that define the miner’s search space.

{
"id": 2,
"method": "mining.subscribe",
"params": [
"cgminer/4.12.1",
null
]
}

Parameter 1: User agent ("cgminer/4.12.1") A string identifying the mining software. This is informational — the pool uses it for statistics and debugging. Common values include:

  • "bmminer/2.0.0" (Bitmain firmware)
  • "cgminer/4.12.1" (open-source miner)
  • "BraiinsOS/2023-06" (Braiins firmware)
  • "NerdMiner/1.0" (hobby miner projects)

Parameter 2: Previous subscription ID (null) Used for session resumption. On a fresh connection, this is null. If the miner is reconnecting after a disconnect, it can pass the subscription ID from the previous session. If the pool still has that session in memory, it may restore the same extraNonce1, allowing the miner to resume more quickly.

{
"id": 2,
"result": [
[
["mining.set_difficulty", "b4b6693b72a50c7116db18d6497cac52"],
["mining.notify", "ae6812eb4cd7735a302a8a9dd95cf71f"]
],
"08000002",
4
],
"error": null
}

The result is an array with three elements. Let us examine each one.

[
["mining.set_difficulty", "b4b6693b72a50c7116db18d6497cac52"],
["mining.notify", "ae6812eb4cd7735a302a8a9dd95cf71f"]
]

This is an array of two-element arrays, each pairing a method name with a subscription ID. These tell the miner:

  • “You are subscribed to mining.set_difficulty notifications, and your subscription ID for that is b4b6693b72a50c7116db18d6497cac52.”
  • “You are subscribed to mining.notify notifications, and your subscription ID is ae6812eb4cd7735a302a8a9dd95cf71f.”

In practice, most mining software ignores these subscription IDs. They were originally intended to allow selective unsubscription, but no one actually uses that feature. The miner simply expects to receive mining.set_difficulty and mining.notify messages after subscribing.

"08000002"

This is the extraNonce1 value, a hex string assigned by the pool. This is critically important. Let us understand why.

In Bitcoin mining, every miner is constructing a slightly different block. Even though they all build on the same previous block hash and include similar transactions, they need some way to produce different hashes. The nonce field in the block header provides one dimension of variation, but 4 bytes (about 4.3 billion values) is not enough for modern ASICs that compute trillions of hashes per second.

The extraNonce is additional nonce space embedded in the coinbase transaction (the special transaction that pays the mining reward). The pool splits this space into two parts:

  • extraNonce1: Assigned by the pool, unique to each miner/session. The miner must not change this.
  • extraNonce2: The miner’s own space to iterate over.

When constructing the coinbase transaction, the miner concatenates: coinbase1 + extraNonce1 + extraNonce2 + coinbase2. By varying extraNonce2, the miner changes the coinbase transaction hash, which changes the merkle root, which changes the block header, which produces completely different SHA-256 hashes.

In this example, "08000002" is 4 bytes (8 hex characters). Every share this miner submits will include these bytes in the coinbase transaction, allowing the pool to identify which miner produced it.

4

This integer tells the miner how many bytes it can use for extraNonce2. In this example, 4 bytes means the miner has 2^32 (about 4.3 billion) possible extraNonce2 values.

Combined with the 4-byte header nonce, this gives:

  • 4.3 billion (nonce) x 4.3 billion (extraNonce2) = approximately 1.8 x 10^19 possible combinations per job

For reference, a modern Antminer S21 hashes at about 200 TH/s. At that rate, it would take about 25 hours to exhaust this combined space — far longer than a job typically lasts (usually seconds to minutes).

When a miner reconnects after a disconnect, it can attempt to resume its previous session:

{
"id": 2,
"method": "mining.subscribe",
"params": [
"cgminer/4.12.1",
"ae6812eb4cd7735a302a8a9dd95cf71f"
]
}

The second parameter is the previous subscription ID (from the mining.notify subscription pair). If the pool still has this session:

Successful resumption:

{
"id": 2,
"result": [
[
["mining.set_difficulty", "b4b6693b72a50c7116db18d6497cac52"],
["mining.notify", "ae6812eb4cd7735a302a8a9dd95cf71f"]
],
"08000002",
4
],
"error": null
}

Notice the same extraNonce1 ("08000002") is returned. The miner can continue using shares it was working on.

Resumption rejected (new session assigned):

{
"id": 2,
"result": [
[
["mining.set_difficulty", "new_subscription_id_1"],
["mining.notify", "new_subscription_id_2"]
],
"09000017",
4
],
"error": null
}

A different extraNonce1 is returned, indicating the old session is gone. The miner must discard any in-progress work and wait for new mining.notify messages.

mining.authorize: Authenticating the Worker

Section titled “mining.authorize: Authenticating the Worker”

After subscribing, the miner authenticates one or more workers. This tells the pool which account should receive credit for shares submitted by this connection.

{
"id": 3,
"method": "mining.authorize",
"params": [
"myaccount.rig01",
"x"
]
}

Parameter 1: Worker name ("myaccount.rig01")

The format depends on the pool:

Pool typeWorker name formatExample
Account-based poolaccount.worker"john.antminer_s19"
Wallet-based pooladdress.worker"bc1q...xyz.rig01"
Some poolsaccount (no worker)"john"

The part after the dot is the worker name — it is just a label that helps you identify individual machines in the pool’s dashboard. You can name them anything: rig01, shelf3_pos7, garage_unit, whatever makes sense for your setup.

Parameter 2: Password ("x")

Most pools ignore this field entirely. Miners typically send "x", "", or "123". However, some pools repurpose the password field for configuration:

  • Difficulty settings: "d=1024" to request a specific starting difficulty
  • Payout threshold: "mc=0.01" for minimum payout
  • Algorithm selection: on multi-algorithm pools
{
"id": 3,
"result": true,
"error": null
}

Simple: result: true means the worker is authorized and can submit shares.

{
"id": 3,
"result": false,
"error": [24, "Unauthorized worker", null]
}

Error code 24 means “unauthorized.” Common reasons for failure:

  • Account does not exist on the pool
  • Invalid Bitcoin address (for wallet-based pools)
  • Pool requires workers to be created in the web interface first
  • Too many workers already connected for this account

A single TCP connection can have multiple authorized workers:

{"id": 3, "method": "mining.authorize", "params": ["account.rig01", "x"]}
{"id": 4, "method": "mining.authorize", "params": ["account.rig02", "x"]}
{"id": 5, "method": "mining.authorize", "params": ["account.rig03", "x"]}

Each receives an independent response. When submitting shares later via mining.submit, the miner specifies which worker name to credit. This is commonly used by mining proxies that aggregate multiple physical ASICs behind a single pool connection.

The pool tracks hashrate and statistics for each worker independently, even though they share the same extraNonce1 and TCP connection.

Putting It All Together: The Complete Handshake

Section titled “Putting It All Together: The Complete Handshake”

Here is a real-world complete handshake sequence as you would capture it with a network analyzer. Each line is one JSON message on the wire:

1. Miner sends mining.configure:

{"id":1,"method":"mining.configure","params":[["version-rolling"],{"version-rolling.mask":"1fffe000","version-rolling.min-bit-count":2}]}

2. Pool responds to configure:

{"id":1,"result":{"version-rolling":true,"version-rolling.mask":"1fffe000"},"error":null}

3. Miner sends mining.subscribe:

{"id":2,"method":"mining.subscribe","params":["bmminer/2.0.0",null]}

4. Pool responds to subscribe:

{"id":2,"result":[[["mining.set_difficulty","1"],["mining.notify","1"]],"08000002",4],"error":null}

5. Miner sends mining.authorize:

{"id":3,"method":"mining.authorize","params":["account.worker1","x"]}

6. Pool responds to authorize:

{"id":3,"result":true,"error":null}

7. Pool pushes initial difficulty:

{"id":null,"method":"mining.set_difficulty","params":[512]}

8. Pool pushes first job:

{"id":null,"method":"mining.notify","params":["bf","00000000000000000002...","01000000...","ffffffff...0100000000...","[\"abc123...\",\"def456...\"]","20000000","1705c739","64b3e1a7",true]}

The miner is now fully operational and begins hashing.

Some miners skip mining.configure or send it after mining.subscribe. This is technically allowed — mining.configure is an extension, not a requirement. However, if version rolling is needed, the pool will not know to enable it, and the miner may waste hash power by not using the extra nonce space.

Best practice: always send mining.configure first if your hardware supports version rolling.

If the pool deauthorizes a worker mid-session (rare, but possible during pool maintenance), the pool may send:

{
"id": null,
"method": "client.reconnect",
"params": ["newpool.example.com", 3333, 0]
}

This tells the miner to disconnect and reconnect to a different address. The parameters are: host, port, and wait time (seconds before reconnecting). Some miners support this; many ignore it.

Different pools use different sizes for extraNonce1:

  • Small pools: 2-3 bytes (supporting fewer simultaneous miners)
  • Large pools: 4-8 bytes (supporting millions of connections)

The total coinbase nonce space is fixed, so a larger extraNonce1 means less space for extraNonce2, and vice versa. With 8 bytes total nonce space in the coinbase, a pool using 4-byte extraNonce1 leaves 4 bytes for extraNonce2. A pool using 6-byte extraNonce1 leaves only 2 bytes for extraNonce2 (65,536 values), which may not be enough for very fast ASICs.

Some pools change their behavior based on the user agent. For example, a pool might automatically enable version rolling if it sees a known ASIC firmware identifier, or might set a different default difficulty for known high-performance miners. While this is pool-specific behavior, it is worth knowing that the user agent string is not just cosmetic.

The Stratum handshake establishes the foundation for all mining communication:

  • mining.configure negotiates protocol extensions (primarily version rolling), giving modern ASICs extra nonce space through the block version field
  • mining.subscribe registers the miner with the pool and assigns the critical extraNonce1 and extraNonce2 size parameters that define the miner’s search space
  • mining.authorize authenticates one or more workers, linking submitted shares to an account for reward tracking

These three methods run in under 250 milliseconds on a good connection, but the parameters they establish — particularly extraNonce1 and the version rolling mask — govern everything the miner does for the lifetime of the connection.

Next up: mining.notify and mining.submit — the core work loop where jobs arrive, headers are built, and shares are submitted.