Skip to main content
How to pull work from a KRNX node, hand it to your miners, accept a share and send it back. Self-contained: everything a pool needs is here. KRNX is a merged-mining chain. The pool mines a parent-chain header (BCH, LTC, RVN or ZEC), and for every accepted share the KRNX node credits a KRNX reward to the address the pool named in the template request. The parent coin does not go to the pool, and parent transactions are never handed out: the project coordinator keeps them. Your miners need no changes — this is an ordinary SHA256d / Scrypt / KawPoW / Equihash header. Only the source of work is different.

1. What you need from the node

Your own KRNX node (or access to one) with the krnx namespace enabled:
A parent-chain node (bchd/litecoind/ravend/zebrad) is not required. Four methods, nothing else: Chain profiles:

2. krnx_getMiningInfo

This is the only consensus parameter that concerns a pool. A share is anchored to a KRNX block (krnxBlockNumber in the template) and has to reach the chain before the head has moved inclusionWindowBlocks - 1 blocks past that anchor. Later than that it is stale_work. See §10.

3. krnx_getBlockTemplate

Request

The Bitcoin GBT mode field is accepted but pointless here: the only allowed value is "template", and that is what a missing field means anyway. "mode":"proposal" is rejected with unsupported_mode — this API has no block-validation mode. The coinbase reserves exactly 30 bytes for extradata. A longer tag is truncated, not refused: 31 bytes gets you a template with a 30-byte tag. The limit counts UTF-8 bytes rather than characters, and the cut lands on a rune boundary — 16 two-byte characters (32 bytes) come back as 15, never as 15 and a half of one. invalid_extradata is left for one case only: input that is not valid UTF-8. Anything shorter than 30 bytes is zero-padded to exactly 30, so your tag never changes the coinbase length. Extranonce values only matter if you want the node to return a template with them already spliced in (and, for ZEC, a finished coinbaseTx). On the Bitcoin-family chains a pool normally sends zeros and splices its own — see §5.

Response

Full parent transactions are not in the response and never will be: the pool mines a header, the coordinator assembles the parent block.

Byte order and prefixes

The most common source of silent bugs. The rule: Bitcoin-style fields arrive without 0x, KRNX-style fields with 0x. On submit it flips: parentHeader and coinbaseTx travel as 0x-hex. So the ZEC coinbaseTx from the template has to go back with a 0x prepended.

4. How the coinbase is built

The node builds the coinbase itself and hands it over in two halves. A pool does not compose one — it only inserts the extranonce. Inside the scriptSig:
fabe6d6d is the standard merged-mining marker (as in Namecoin and Dogecoin). Its tree holds the KRNX leaf that binds your beneficiary to the proof of work found. That is exactly why the scriptSig cannot be rewritten and the outputs cannot be swapped: the node verifies the commitment and rejects the share. The coinbase outputs (coinb2) are signed by the coordinator and compared byte for byte. coinb1 ends right before the 12-byte extranonce slot; coinb2 starts right after it:

5. Building work: BCH, LTC, RVN

5.1 Coinbase and merkle root

Sanity check when you integrate: with the extranonce you passed in the request, merkle[::-1].hex() must equal t["merkleroot"]. The pool is free to roll its own extranonce — those 12 bytes are yours, and you do not need a fresh template for it.

5.2 The 80-byte header (BCH, LTC)

PoW and the target comparison:
  • BCH: int.from_bytes(dsha(header), "little") <= target
  • LTC: int.from_bytes(scrypt(header, header, N=1024, r=1, p=1, dkLen=32), "little") <= target

5.3 The 120-byte header (RVN, KawPoW)

KawPoW is not fed the header itself but the hash of its first 80 bytes:
The node recomputes mix_hash and rejects a header whose committed mix does not match, so there is no point in filling it with junk. nHeight comes from the template’s height: a different height is a different DAG epoch. RVN’s stratum dialect is the ethash-style one (mining.notify with a seed hash), not the Bitcoin one.

6. Building work: ZEC (Equihash)

ZEC is the one chain where the pool does not assemble the coinbase. Rewriting a Zcash scriptSig moves hashAuthDataRoot, and recomputing that requires the auth digests of every other transaction in the block, which a pool never sees. So the node hands over finished values:
  • coinbaseTx — the complete v5/v6 coinbase transaction;
  • merkleroot — the root for it;
  • blockcommitments — the finished hashBlockCommitments field.
Both hashes are in display order. coinb1/coinb2 are present in the ZEC response for shape compatibility, but you cannot build the coinbase from them: with your own extranonce there is nothing to recompute blockcommitments with. If you need a different extranonce, request a new template with different extranonce1/extranonce2. In practice you never need to: Zcash has a 32-byte nonce, which is plenty of search space on its own. The header is 140 bytes of PoW header + fd 40 05 + 1344 bytes of solution = 1487 bytes. The target comparison runs against the block id:
The stratum dialect is s-nomp (lolMiner, miniZ, EWBF, nheqminer): mining.notify fields are in the serialized header’s own byte order, with none of the Bitcoin reversals.

7. What you may change and what you may not

Free — this is the search space: Not allowed — the node checks and rejects: Check order on the node: every cheap comparison first, PoW last. Deliberately so — KawPoW and Equihash are the only steps whose cost the submitter chooses.

8. Targets: KRNX share vs parent block

The template’s target is the only bar a pool needs. Clear it and the share earns a KRNX reward. You do not need to watch for shares that also solve the parent chain: the node says so itself, in the parentBlockCandidate field of the submit response. Such a share is picked up by the coordinator and relayed to the parent network; it changes nothing about the KRNX reward. Set your own vardiff as usual — it must be no weaker than target, or most shares come back as low_difficulty.

9. Submitting a share

One call; the fields are the same for all four methods:

Success

workshareId is derived from immutable fields and comes out the same on any node:

Errors

Anything other than accepted/known arrives as a JSON-RPC error with code -32042:
Switch on data.reason, never on the message text:

10. Refreshing work

  • Poll krnx_getBlockTemplate roughly once per second.
  • A changed workid means changed work: a new signed template, a new KRNX anchor, a new DAA target or a different beneficiary. Issue a new job.
  • Set clean_jobs=true only when the parent block’s identity changes (height, previousblockhash, bits, powId). A workid change on its own does not invalidate outstanding jobs: they stay acceptable while their anchor is in the window.
  • The window from §2: a share whose anchor is older than inclusionWindowBlocks - 1 blocks gets stale_work. The one exception is a share that cleared the parent target — accepted as *_submit_only, but with no KRNX reward.
  • mintime is the lower bound for nTime, now + 2h the upper one. Roll time freely between them.

11. Checking the reward

The selector is either workshareId or the pair powId + powHash, where powHash is the hash field from the submit response. Exactly one of the two. The response carries status (included / locked / not found), includedBlockNumber, confirmations, beneficiary, baseReward / feeReward / totalReward and unlockTime. The method is reorg-aware: it re-checks that the inclusion block is still canonical.

12. A complete minimal client (sha256d)


13. Integration checklist

  1. beneficiary in the response equals your KRNX address, lower-cased.
  2. With the extranonce from your request, your recomputed merkle root equals the template’s merkleroot — that proves your reversals are right.
  3. The first share comes back status: accepted, candidateMode: reward.
  4. Resending the same share returns status: known with the same workshareId — idempotency works and your accounting will not double-count.
  5. pendingShareCount grows, and krnx_getWorkshareReward on the workshareId shows inclusion in a block.
  6. Your stale_work rate is near zero. If it is not, you refresh templates too rarely.
  7. Your low_difficulty rate is near zero. If it is not, your vardiff is weaker than target.

Further reading

Mining the main chain

kHeavyHash, the kaspa stratum dialect, and the node-to-stratum contract for a Kronex main-chain pool.

What a block pays

How the sealer, the uncles and the workshares in a block share its reward, and when those coins unlock.