> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kronex.network/llms.txt
> Use this file to discover all available pages before exploring further.

# Merged mining: pool integration

> Pull work from a Kronex node, hand it to your miners, take a share and send it back — for BCH, LTC, RVN and ZEC pools.

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:

```bash theme={null}
krnxd --http --http.api eth,net,web3,krnx --http.addr 127.0.0.1 --http.port 8545
```

A parent-chain node (bchd/litecoind/ravend/zebrad) is **not** required.

Four methods, nothing else:

| Method                                                                                                   | Purpose                                                       |
| -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| `krnx_getMiningInfo`                                                                                     | the window, in blocks, in which a share can still be included |
| `krnx_getBlockTemplate`                                                                                  | work                                                          |
| `krnx_submitShaBlock` / `krnx_submitScryptBlock` / `krnx_submitKawpowBlock` / `krnx_submitEquihashBlock` | share                                                         |
| `krnx_getWorkshareReward`                                                                                | reward lookup (optional)                                      |

Chain profiles:

| `rules`              | `algorithm` | `parentChain` | `powId` | submit method              | Header size | PoW                       |
| -------------------- | ----------- | ------------- | ------- | -------------------------- | ----------- | ------------------------- |
| `sha256d` (or `sha`) | `sha256d`   | BCH           | 1       | `krnx_submitShaBlock`      | 80          | double-SHA256             |
| `scrypt`             | `scrypt`    | LTC           | 2       | `krnx_submitScryptBlock`   | 80          | Scrypt (N=1024, r=1, p=1) |
| `kawpow`             | `kawpow`    | RVN           | 3       | `krnx_submitKawpowBlock`   | 120         | KawPoW                    |
| `equihash`           | `equihash`  | ZEC           | 4       | `krnx_submitEquihashBlock` | 1487        | Equihash (200,9)          |

***

## 2. `krnx_getMiningInfo`

```bash theme={null}
curl -sS -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","id":1,"method":"krnx_getMiningInfo","params":[]}' \
  http://127.0.0.1:8545 | jq
```

```json theme={null}
{"inclusionWindowBlocks": "0x5"}
```

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

```bash theme={null}
curl -sS -H 'Content-Type: application/json' --data '{
  "jsonrpc":"2.0","id":1,"method":"krnx_getBlockTemplate","params":[{
    "rules":["sha256d"],
    "coinbase":"0x299f5F0Ae73248455543Da5A8E14195Bc9CA1D22",
    "extranonce1":"deadbeef",
    "extranonce2":"0000000000000001",
    "extradata":"/my-pool/"
  }]
}' http://127.0.0.1:8545 | jq
```

| Field         | Required         | Rule                                                                                                         |
| ------------- | ---------------- | ------------------------------------------------------------------------------------------------------------ |
| `rules`       | in practice, yes | the first supported algorithm from §1. An empty list means `sha256d`                                         |
| `coinbase`    | yes              | **the pool's KRNX address**, 20 bytes with `0x`. This is where the reward lands. Not a parent-chain address! |
| `extranonce1` | no               | hex, up to 4 bytes, left-padded with zeros                                                                   |
| `extranonce2` | no               | hex, up to 8 bytes, left-padded with zeros                                                                   |
| `extradata`   | no               | your coinbase tag; valid UTF-8, anything past 30 bytes is cut off                                            |

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

```json theme={null}
{
  "algorithm": "sha256d",
  "parentChain": "BCH",
  "powId": 1,
  "version": 536870912,
  "previousblockhash": "0000000018aaa36330910e960c9d7534dc29d194584596d70412da21e0cf4e02",
  "height": 1718709,
  "bits": "1d00ffff",
  "target": "00000063ff9c0000000000000000000000000000000000000000000000000000",
  "difficulty": "0.010000000000",
  "curtime": 1784209408,
  "mintime": 1784209408,
  "noncerange": "00000000ffffffff",
  "coinb1": "0100000001000000000000000000000000000000000000000000000000000000000000000000ffffffff61036d63412cfabe6d6d…",
  "coinb2": "…ffffffff0240597307000000001976a914…88ac00000000",
  "extranonce1Length": 4,
  "extranonce2Length": 8,
  "coinbaseAuxExtraBytesLength": 30,
  "merklebranch": ["a1b2…", "c3d4…"],
  "merkleroot": "e2e52affed9937016d4bd7d8f4dd01a59b0b3a519950440c3202f43d0c83fb21",
  "beneficiary": "0x299f5f0ae73248455543da5a8e14195bc9ca1d22",
  "krnxBlockNumber": "0x3b91",
  "krnxBlockHash": "0x8116e11b419077e926260330f20da71b391effd670a4ce4ae444130d1027f93a",
  "workid": "0xa9434553f78e5d62d23adbb8649ef60580faf60949ec6553bd6340e0baabd712",
  "workTemplate": "0xf901b50104b90135f9013204…",
  "auxMerkleBranch": [],
  "auxMerkleIndex": "0x0"
}
```

| Field                                                                  | Meaning                                                                                                                    |
| ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `version`, `previousblockhash`, `bits`, `height`                       | parent-chain header fields; not yours to change                                                                            |
| `target`                                                               | the **KRNX share target**: clear it and the share earns a KRNX reward                                                      |
| `difficulty`                                                           | the same target as a decimal pool difficulty                                                                               |
| `curtime`, `mintime`                                                   | the `nTime` window: see §7                                                                                                 |
| `noncerange`                                                           | always `00000000ffffffff`. On RVN and ZEC the field is **meaningless** (their nonces are 8 and 32 bytes) — ignore it there |
| `coinb1`, `coinb2`                                                     | the coinbase halves around the 12-byte extranonce slot (Bitcoin family)                                                    |
| `extranonce1Length`, `extranonce2Length`                               | always 4 and 8                                                                                                             |
| `coinbaseAuxExtraBytesLength`                                          | always 30 — the size of the `extradata` slot                                                                               |
| `merklebranch`                                                         | the coinbase branch up to the root, in **display** order                                                                   |
| `merkleroot`                                                           | the root for the extranonce you passed, display order. Splice your own extranonce and you recompute it                     |
| `beneficiary`                                                          | echo of your KRNX address; check the node took the one you sent                                                            |
| `krnxBlockNumber`, `krnxBlockHash`                                     | the KRNX anchor of this work; it defines the window from §2                                                                |
| `workid`                                                               | work identity: when it changes, the work changed                                                                           |
| `workTemplate`                                                         | opaque token: **do not decode, do not modify**, return it byte for byte on submit                                          |
| `auxMerkleBranch`, `auxMerkleIndex`                                    | the KRNX leaf's proof in the merged-mining tree; echo both back on submit                                                  |
| `blockcommitments`, `coinbaseTx`                                       | ZEC only, see §6                                                                                                           |
| `dogeAuxHash`, `dogeBits`, `dogeAuxMerkleBranch`, `dogeAuxMerkleIndex` | LTC only. This is the coordinator's server-side DOGE work. **Pools ignore these**; there is nothing to do with them        |

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`**.

| Field                                                                       | `0x` prefix | Order      | What to do                             |
| --------------------------------------------------------------------------- | ----------- | ---------- | -------------------------------------- |
| `previousblockhash`                                                         | no          | display    | reverse before writing into the header |
| `merkleroot`, `blockcommitments`                                            | no          | display    | reverse before writing into the header |
| `merklebranch[i]`                                                           | no          | display    | reverse before folding                 |
| `bits`, `target`                                                            | no          | big-endian | use as is                              |
| `coinb1`, `coinb2`, `coinbaseTx`                                            | no          | as is      | use as is                              |
| `workid`, `workTemplate`, `krnxBlock*`, `beneficiary`, `auxMerkleBranch[i]` | yes         | —          | use as is                              |

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:

```text theme={null}
OP_PUSH<n>  <height>                                        BIP34, minimal encoding
OP_PUSH44   fabe6d6d | auxMerkleRoot(32, reversed) | merkleSize(4 LE) | merkleNonce(4 LE)
OP_PUSH42   extranonce1(4) | extranonce2(8) | extradata(30)
OP_PUSH4    signatureTime(4 LE)
```

`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:

```text theme={null}
coinbaseTx = coinb1 || extranonce1(4) || extranonce2(8) || coinb2
```

***

## 5. Building work: BCH, LTC, RVN

### 5.1 Coinbase and merkle root

```python theme={null}
coinbase = bytes.fromhex(t["coinb1"]) + en1 + en2 + bytes.fromhex(t["coinb2"])
#          en1 is exactly 4 bytes, en2 exactly 8

def dsha(b):
    return hashlib.sha256(hashlib.sha256(b).digest()).digest()

merkle = dsha(coinbase)
for node in t["merklebranch"]:
    merkle = dsha(merkle + bytes.fromhex(node)[::-1])   # the branch arrives in display order
# merkle is now in internal order — exactly what goes into the header
```

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)

| Offset | Size | Field                         |
| ------ | ---- | ----------------------------- |
| 0      | 4    | `version`, LE                 |
| 4      | 32   | `previousblockhash`, reversed |
| 36     | 32   | merkle root, internal order   |
| 68     | 4    | `nTime`, LE                   |
| 72     | 4    | `bits`, LE                    |
| 76     | 4    | nonce, LE                     |

```python theme={null}
header = struct.pack("<I", t["version"]) \
       + bytes.fromhex(t["previousblockhash"])[::-1] \
       + merkle \
       + struct.pack("<III", ntime, int(t["bits"], 16), nonce)
```

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)

| Offset | Size | Field                                                        |
| ------ | ---- | ------------------------------------------------------------ |
| 0      | 4    | `version`, LE                                                |
| 4      | 32   | `previousblockhash`, reversed                                |
| 36     | 32   | merkle root, internal order                                  |
| 68     | 4    | `nTime`, LE                                                  |
| 72     | 4    | `bits`, LE                                                   |
| 76     | 4    | **`nHeight`, LE** — must equal `height` from the template    |
| 80     | 8    | `nNonce64`, LE                                               |
| 88     | 32   | `mix_hash`, internal order (reverse what the miner returned) |

KawPoW is not fed the header itself but the hash of its first 80 bytes:

```python theme={null}
header_hash = dsha(header120[:80])[::-1]     # display order
mix, digest = kawpow(header_hash, height, nonce64)
# int.from_bytes(digest, "big") <= target
```

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**.

| Offset | Size | Field                                                    |
| ------ | ---- | -------------------------------------------------------- |
| 0      | 4    | `version`, LE                                            |
| 4      | 32   | `previousblockhash`, reversed                            |
| 36     | 32   | merkle root, reversed from `merkleroot`                  |
| 68     | 32   | `hashBlockCommitments`, reversed from `blockcommitments` |
| 100    | 4    | `nTime`, LE                                              |
| 104    | 4    | `bits`, LE                                               |
| 108    | 32   | nonce (full 32 bytes)                                    |
| 140    | 3    | `fd4005` — CompactSize of the solution length            |
| 143    | 1344 | Equihash (200,9) solution                                |

The target comparison runs against the block id:

```python theme={null}
block_id = dsha(serialized_header_1487)
# int.from_bytes(block_id, "little") <= target
```

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:

| What                          | Bounds                                                |
| ----------------------------- | ----------------------------------------------------- |
| nonce                         | the whole range: 4 bytes (BCH/LTC), 8 (RVN), 32 (ZEC) |
| `extranonce1` + `extranonce2` | 12 bytes, BCH/LTC/RVN only                            |
| `nTime`                       | from `mintime` to `now + 2 hours`                     |
| `extradata`                   | 30 bytes, fixed once in the template request          |

Not allowed — the node checks and rejects:

| What                                          | Error                     |
| --------------------------------------------- | ------------------------- |
| `version`, `previousblockhash`, `bits`        | `invalid_parent_header`   |
| coinbase height                               | `invalid_parent_header`   |
| header `nHeight` (RVN)                        | `invalid_parent_header`   |
| coinbase outputs / transaction envelope (ZEC) | `signed_outputs_mismatch` |
| merkle branch and merkle root                 | `invalid_merkle_root`     |
| the `fabe6d6d` commitment in the scriptSig    | `invalid_seal_commitment` |
| `workid` / `workTemplate`                     | `invalid_work_template`   |
| `nTime` outside the window                    | `invalid_parent_header`   |

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:

```bash theme={null}
curl -sS -H 'Content-Type: application/json' --data '{
  "jsonrpc":"2.0","id":2,"method":"krnx_submitShaBlock","params":[{
    "workid":"0xa943…",
    "workTemplate":"0xf901b5…",
    "parentHeader":"0x0000002…",
    "coinbaseTx":"0x01000000010000…",
    "auxMerkleBranch":[],
    "auxMerkleIndex":"0x0"
  }]
}' http://127.0.0.1:8545 | jq
```

| Field                               | Rule                                                                                |
| ----------------------------------- | ----------------------------------------------------------------------------------- |
| `workid`, `workTemplate`            | from the exact template this share was found on, byte for byte                      |
| `parentHeader`                      | the solved header in full: 80 / 120 / 1487 bytes, `0x`-hex                          |
| `coinbaseTx`                        | the coinbase you assembled (for ZEC, the one the node gave you), `0x`-hex           |
| `auxMerkleBranch`, `auxMerkleIndex` | echoed from the template. Usually `[]` and `"0x0"`, but both fields must be present |

### Success

```json theme={null}
{
  "status": "accepted",
  "candidateMode": "reward",
  "hash": "0x6666…",
  "parentBlockHash": "0x6666…",
  "number": "0x3b92",
  "workshareId": "0x7777…",
  "difficulty": "0.014200000000",
  "parentBlockCandidate": false,
  "pendingShareCount": "0x1"
}
```

| Field                    | Meaning                                                                                                                                                                                                                                                      |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `status`                 | `accepted` — taken; `known` — idempotent resend of a share already taken                                                                                                                                                                                     |
| `candidateMode`          | `reward` — the share earns a KRNX reward. `bch_submit_only` / `ltc_submit_only` / `rvn_submit_only` / `zec_submit_only` — the KRNX anchor has moved on, but the proof completes a parent block: it will be relayed to the parent network with no KRNX reward |
| `rewardIneligibleReason` | present only when `candidateMode` is not `reward`                                                                                                                                                                                                            |
| `hash`                   | the share hash (also the powHash for §11)                                                                                                                                                                                                                    |
| `parentBlockHash`        | the parent block id. On BCH/RVN/ZEC identical to `hash`; on LTC it differs by byte order                                                                                                                                                                     |
| `number`                 | the KRNX anchor number of this work                                                                                                                                                                                                                          |
| `workshareId`            | the canonical share id — your accounting key, and the lookup key for §11                                                                                                                                                                                     |
| `difficulty`             | the share's actual difficulty                                                                                                                                                                                                                                |
| `parentBlockCandidate`   | the share also cleared the parent network target                                                                                                                                                                                                             |
| `pendingShareCount`      | how many shares are waiting to be included in a KRNX block                                                                                                                                                                                                   |

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

```text theme={null}
workshareId = Keccak256(RLP(1, powId, parentHeader, coinbaseTxHash))
```

### Errors

Anything other than `accepted`/`known` arrives as a JSON-RPC error with code
**-32042**:

```json theme={null}
{"error":{"code":-32042,"message":"low_difficulty: share hash is above the anchored KRNX DAA target",
          "data":{"reason":"low_difficulty","detail":"…"}}}
```

Switch on `data.reason`, never on the message text:

| `reason`                                                                                                      | What happened                                                                                         | What to do                                                            |
| ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| `low_difficulty`                                                                                              | the share did not clear `target`                                                                      | raise your vardiff                                                    |
| `stale_work`                                                                                                  | the work's KRNX anchor fell out of the inclusion window                                               | refresh templates more often (§10)                                    |
| `invalid_work_template`                                                                                       | `workid`/`workTemplate` mangled, swapped, or from another algorithm                                   | send the token byte for byte                                          |
| `invalid_parent_header`                                                                                       | size, `version`/`prevhash`/`bits`/height or `nTime` do not line up                                    | re-check §5–§7                                                        |
| `invalid_coinbase`                                                                                            | the coinbase does not parse                                                                           | check the `coinb1`+12+`coinb2` splice                                 |
| `signed_outputs_mismatch`                                                                                     | coinbase outputs were changed                                                                         | do not touch `coinb2`                                                 |
| `invalid_merkle_root`                                                                                         | the root does not fold from your coinbase                                                             | check the reversals in the branch (§3)                                |
| `invalid_seal_commitment`                                                                                     | the `fabe6d6d` commitment is broken                                                                   | do not rewrite the scriptSig; echo `auxMerkleBranch`/`auxMerkleIndex` |
| `template_unavailable`                                                                                        | the node has no fresh signed template                                                                 | keep polling `krnx_getBlockTemplate`                                  |
| `daa_unavailable`                                                                                             | the node is not synced                                                                                | wait for sync                                                         |
| `unsupported_algorithm`, `unsupported_mode`, `invalid_beneficiary`, `invalid_extranonce`, `invalid_extradata` | bad template request (`invalid_extradata` means invalid UTF-8; an oversized tag is truncated instead) | see §3                                                                |

***

## 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

```bash theme={null}
curl -sS -H 'Content-Type: application/json' --data '{
  "jsonrpc":"2.0","id":3,"method":"krnx_getWorkshareReward",
  "params":[{"workshareId":"0x7777…"}]
}' http://127.0.0.1:8545 | jq
```

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)

```python theme={null}
import hashlib, json, struct, urllib.request

RPC = "http://127.0.0.1:8545"
POOL_KRNX_ADDRESS = "0x299f5F0Ae73248455543Da5A8E14195Bc9CA1D22"

def rpc(method, params):
    req = urllib.request.Request(
        RPC, json.dumps({"jsonrpc": "2.0", "id": 1, "method": method, "params": params}).encode(),
        {"Content-Type": "application/json"})
    body = json.loads(urllib.request.urlopen(req).read())
    if "error" in body:
        raise RuntimeError(body["error"]["data"]["reason"])   # switch on reason, not text
    return body["result"]

def dsha(b):
    return hashlib.sha256(hashlib.sha256(b).digest()).digest()

t = rpc("krnx_getBlockTemplate", [{
    "rules": ["sha256d"],
    "coinbase": POOL_KRNX_ADDRESS, "extradata": "/my-pool/",
}])

en1, en2 = b"\x00\x12\x34\x56", b"\x00" * 8            # 4 + 8 bytes, the pool rolls these
coinbase = bytes.fromhex(t["coinb1"]) + en1 + en2 + bytes.fromhex(t["coinb2"])

merkle = dsha(coinbase)
for node in t["merklebranch"]:
    merkle = dsha(merkle + bytes.fromhex(node)[::-1])

prefix = (struct.pack("<I", t["version"])
          + bytes.fromhex(t["previousblockhash"])[::-1]
          + merkle)
ntime, bits, target = int(t["curtime"]), int(t["bits"], 16), int(t["target"], 16)

for nonce in range(1 << 32):                            # this part is your miner's job
    header = prefix + struct.pack("<III", ntime, bits, nonce)
    if int.from_bytes(dsha(header), "little") <= target:
        break

res = rpc("krnx_submitShaBlock", [{
    "workid": t["workid"],
    "workTemplate": t["workTemplate"],
    "parentHeader": "0x" + header.hex(),                # on submit: with 0x
    "coinbaseTx": "0x" + coinbase.hex(),
    "auxMerkleBranch": t.get("auxMerkleBranch") or [],
    "auxMerkleIndex": t.get("auxMerkleIndex") or "0x0",
}])
print(res["status"], res["workshareId"], res["difficulty"], res["candidateMode"])
```

***

## 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

<CardGroup cols={2}>
  <Card title="Mining the main chain" icon="microchip" href="/guides/mining-pool">
    kHeavyHash, the kaspa stratum dialect, and the node-to-stratum contract for
    a Kronex main-chain pool.
  </Card>

  <Card title="What a block pays" icon="coins" href="/learn/rewards">
    How the sealer, the uncles and the workshares in a block share its reward,
    and when those coins unlock.
  </Card>
</CardGroup>
