mining.notify and mining.submit: The Core Work Loop
Once the handshake is done — subscribe, configure, authorize — the real work begins. From this point on, your miner’s life becomes a tight loop: receive a job, grind hashes, submit results. Over and over, thousands of times per day.
This loop is powered by two methods: mining.notify (pool tells the miner what to work on) and mining.submit (miner sends back a result). Let’s tear them apart.
mining.notify: Here’s Your Job
Section titled “mining.notify: Here’s Your Job”mining.notify is a server-initiated message. The pool pushes it to your miner whenever there’s a new job to work on. Your miner doesn’t ask for it — it arrives automatically.
Here’s what a real mining.notify message looks like:
{ "id": null, "method": "mining.notify", "params": [ "bf", "4d16b6f85af6e2198f44ae2a6de67f78487ae5611b32aba40000000000000000", "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2c0342fe0c", "ffffffff0100f2052a010000001976a914904a49878c0adfc3aa05de7afad2cc15f483a56a88ac00000000", [ "f0dbfe210e0e2e7ef4a6c8e58b3e4e6e3f3e2e1d1c1b1a191817161514131211" ], "20000000", "1903a30c", "649c3a21", true ]}That’s a lot of hex. Let’s decode every single parameter.
Parameter breakdown
Section titled “Parameter breakdown”The params array has 9 elements, in this exact order:
1. job_id ("bf")
A short string that identifies this specific job. When your miner finds a share, it needs to tell the pool which job it was working on. The pool uses this to look up the corresponding block template.
2. prevhash (64 hex chars)
The hash of the previous block in the blockchain. This is what you’re building on top of. When a new block is found on the network, this value changes and the pool sends a new mining.notify with clean_jobs = true.
3. coinbase1 (hex string)
The first part of the coinbase transaction. This is everything up to where the extraNonce values go.
4. coinbase2 (hex string)
The second part of the coinbase transaction. This is everything after the extraNonce values.
5. merkle_branches (array of hex strings)
A list of transaction hashes needed to compute the Merkle root. The pool has already arranged all the block’s transactions into a Merkle tree — these are the “sibling” hashes your miner needs to climb from the coinbase transaction up to the root.
6. version (hex, 4 bytes)
The block version field. With version rolling enabled (via mining.configure), your miner can modify certain bits of this field as additional nonce space.
7. nbits (hex, 4 bytes)
The encoded target difficulty. This is the compact representation of the 256-bit target value. Your miner uses this to know what difficulty the network requires.
8. ntime (hex, 4 bytes)
The current timestamp for the block. Your miner can increment this slightly (rolling ntime) to get more nonce space, but most of the heavy lifting is done by iterating the nonce and extraNonce2.
9. clean_jobs (boolean)
The most important flag. When true, it means: stop everything you’re doing and switch to this job immediately. This happens when a new block has been found on the network, making all previous work invalid. When false, the miner can finish its current work before switching.
How the Miner Builds a Block Header
Section titled “How the Miner Builds a Block Header”This is the fascinating part. Your miner receives all those hex strings and must assemble them into an 80-byte block header that it can hash. Here’s the step-by-step process:
Step 1: Build the coinbase transaction
Section titled “Step 1: Build the coinbase transaction”coinbase_tx = coinbase1 + extraNonce1 + extraNonce2 + coinbase2extraNonce1was assigned by the pool duringmining.subscribeextraNonce2is the value your miner iterates (this is the main search space your miner controls)
Step 2: Hash the coinbase transaction
Section titled “Step 2: Hash the coinbase transaction”coinbase_hash = SHA256d(coinbase_tx)(SHA256d means double SHA-256: hash it once, then hash the result again.)
Step 3: Compute the Merkle root
Section titled “Step 3: Compute the Merkle root”Starting with coinbase_hash, combine it with each hash in merkle_branches in sequence:
current = coinbase_hashfor branch in merkle_branches: current = SHA256d(current + branch)merkle_root = currentEach step concatenates the current hash with the next branch hash and double-SHA256s the result. After processing all branches, you have the Merkle root.
Step 4: Assemble the 80-byte block header
Section titled “Step 4: Assemble the 80-byte block header”header = version (4 bytes) + prevhash (32 bytes) + merkle_root (32 bytes) + ntime (4 bytes) + nbits (4 bytes) + nonce (4 bytes)That’s exactly 80 bytes. This is what your ASIC chips actually hash.
Step 5: Hash and check
Section titled “Step 5: Hash and check”block_hash = SHA256d(header)If the resulting hash is below the pool’s target (share difficulty), you’ve found a valid share — submit it. If it’s also below the network’s target, congratulations: you just found a block.
The miner iterates through this process billions of times per second, changing the nonce (and extraNonce2 and version bits) each time, looking for a hash that meets the target.
mining.submit: I Found Something
Section titled “mining.submit: I Found Something”When your miner finds a hash that meets the pool’s share difficulty, it sends a mining.submit message:
{ "id": 4, "method": "mining.submit", "params": [ "worker1.rig01", "bf", "00000002", "649c3a25", "6a909d70" ]}Parameter breakdown
Section titled “Parameter breakdown”1. worker_name ("worker1.rig01")
The authorized worker name, so the pool knows which miner submitted this share.
2. job_id ("bf")
Must match the job_id from the mining.notify that this share was based on. If the pool no longer has this job (because a new block was found), you’ll get an error 21 (job not found).
3. extranonce2 ("00000002")
The extraNonce2 value your miner used when it found this share. The pool combines this with its extraNonce1 and the coinbase parts to reconstruct and verify the coinbase transaction.
4. ntime ("649c3a25")
The timestamp value used. This might differ slightly from the original ntime in mining.notify if the miner rolled the timestamp.
5. nonce ("6a909d70")
The 4-byte nonce value that produced the valid hash.
With version rolling
Section titled “With version rolling”If version rolling was negotiated via mining.configure, there’s an additional 6th parameter:
{ "id": 4, "method": "mining.submit", "params": [ "worker1.rig01", "bf", "00000002", "649c3a25", "6a909d70", "20000000" ]}The 6th parameter is the version_bits mask that was applied to the block version field. The pool needs this to reconstruct the exact header your miner hashed.
Pool’s Response
Section titled “Pool’s Response”On success:
{ "id": 4, "result": true, "error": null}Your share was accepted. The pool verified that the hash meets the share difficulty target.
On error:
{ "id": 4, "result": null, "error": [21, "Job not found", null]}This share was rejected. Common reasons are covered in the next article about error handling.
The Full Loop
Section titled “The Full Loop”Here’s what happens continuously during mining:
- Pool sends
mining.notifywith a new job - Miner constructs coinbase → Merkle root → block header
- ASIC chips iterate nonce at billions of hashes per second
- When nonce space is exhausted, miner increments extraNonce2 and rebuilds from step 2
- If version rolling is active, version bits are varied in parallel with nonce
- When a hash meets the share target →
mining.submit - Pool verifies and responds with
trueor an error - Repeat until a new
mining.notifyarrives
This loop runs 24/7 for the entire life of your miner. Every share submitted earns you a fraction of a fraction of a Bitcoin, but millions of shares per day add up to your daily mining revenue.