NEB Coin Information

Testnet: NEB currently runs as testnet 2. When mainnet launches, 10 % of the testnet coins move to mainnet in a swap. Pool payouts are active (PPLNS, sent every full hour, UTC), so you can test the whole path from shares to a real payment.

Miners online

group

Blockchain height

database

Pool Hashrate

bar_chart

NEB

Proof of Training: an independent chain whose mining work trains a small neural network, one integer training step per attempt

Testnet 2 Proof of Training
Coin Specifications
  • Coin Name
    NEB
  • Ticker Symbol
    NEB
  • Algorithm
    Proof of Training — one integer training step of a 784-128-10 network on 16 samples per attempt; the model changes every block; BLAKE3, 80-byte header
  • Block Time
    60 seconds (ASERT difficulty)
  • Max Supply
    17,999,999.9415 NEB
    17,099,999.9955 NEB to miners, 899,999.946 NEB project share (5 %); computed from the consensus rules (subsidy and project_share in neb-chain state.rs), not a figure published by the project
  • Block Reward
    20 NEB per block, 95 % (19 NEB) to the miner (5 % project share by consensus; halving every 450,000 blocks ≈ 312.5 days)
Pool Information
  • Pool Fee
    1%
  • Payout Scheme
    PPLNS
  • PPLNS Window
    Last 5 rounds
  • Payout Schedule
    Every 60 minutes (on the full hour, UTC), batch transaction (network fee paid by the recipients)
  • Minimum Payout
    0.01 NEB (or your own payout limit)
  • Block Confirmations
    Credited at 1 confirmation (shown as unconfirmed), payable at 101 (coinbase maturity 100)
Fee Structure
  • Pool Fee

    Deducted from the 19 NEB subsidy share the pool credits per block

    1%
  • Project share

    Consensus rule of the NEB chain: 5 % of every block subsidy is paid to the project output of the coinbase (1 of 20 NEB in the first era) before the pool sees the reward

    5%
  • Miner Receives

    99 % of the subsidy share the pool credits per block (19 NEB in the first era; transaction fees stay with the pool), shared PPLNS

    ≈ 18.81 NEB / block

Where a block reward goes

Every NEB block creates 20 NEB. The network pays the 5 % project share first; the pool keeps a 1 % fee of the rest and shares everything else among its miners by PPLNS. Transaction fees are not included.

Miners (PPLNS)

18.81 NEB94.05 %

Pool fee (1 %)

0.19 NEB0.95 %

NEB project share (5 %)

1.00 NEB5.00 %

Stratum protocol for miner and pool developers

The complete technical description of the stratum protocol this pool speaks (NEB stratum v1, used by suprminer -a neb), with real messages and a verified test vector. Write your own miner or pool from it, or point your AI agent at one of these files:

Show the full specification

Version 1 · as implemented by the neb.suprnova.cc pool (a NOMP-based stratum server) and by suprminer -a neb.

This document is meant to be complete: a miner or pool author (or their AI agent) should be able to implement a working NEB stratum client or server from it alone, plus the official NEB specification for the proof of work itself. Everything here is public; the pool needs no keys or credentials from the miner.

1. Overview

NEB is a Proof-of-Training chain. An attempt is not a plain hash: the attempt key BLAKE3(header) selects 16 training samples from a dataset, the miner computes a gradient of a small integer neural network (784 → 128 → 10, 4-bit weights) on them, and the result is hashed into a 256-bit ticket. A ticket that is at or below a target is a share or a block.

The stratum protocol is Bitcoin-style stratum v1 (JSON lines over TCP, mining.subscribe / mining.authorize / mining.notify / mining.submit, extranonce1 + extranonce2 in the coinbase, nTime rolling, difficulty via mining.set_difficulty) with three NEB-specific changes:

  1. mining.set_model: the pool pushes the network's current model weights (base64; 50 816 bytes for the v1 network, 13 000 / 11 140 bytes for the nebhash v2 networks KEP1 / RET2 from block 8800) that the attempts of the following jobs are evaluated on. The model changes with every block.
  2. mining.notify carries NEB fields: block height, the model's weights_hash, the dataset index (slot), the body's tx_count, shares_root, and the upper nTime bound ntime_max. The header is NEB's 80-byte header, and the Merkle tree and body commitment use domain-separated BLAKE3.
  3. Difficulty means expected attempts per share: share_target = floor((2^256 - 1) / difficulty), compared with the ticket as 256-bit little-endian integers.

There are no other extensions. The miner needs the public NEB dataset file (section 3) and a PoW implementation; the pool sends everything else.

2. Connection

Host neb.suprnova.cc
Transport plain TCP, no TLS; one JSON object per line, terminated by \n (UTF-8)
Port 7801 VarDiff, start 20 000, min 500 (CPU and small GPUs); custom difficulty allowed
Port 7802 fixed difficulty 20 000
Port 7803 fixed difficulty 2 000 000
Port 7804 VarDiff for big GPUs, start 5·10^8, min 10^5 (recommended for GPUs); custom difficulty allowed
Port 7805 SOLO: VarDiff, start 5·10^7, min 500; custom difficulty allowed. Same protocol; a block found here is credited to its finder alone (99 %), shares only measure hashrate
VarDiff aims at one share per 30 s; retargets after at least 90 s and 3 accepted shares (at once after 12 fast shares), at most ×4 or ÷4 per step; lowers the difficulty when a session has no accepted share for 90 s
Username <neb1 address> or <neb1 address>.<worker>; worker = 1–32 characters of A–Z a–z 0–9 _ -
Password anything (x); optionally d=<number> for a fixed difficulty on ports 7801, 7804 and 7805, within the port's limits

The address is a NEB testnet address: neb1 followed by bech32m data, 62 lowercase characters in total. Payouts go to that address; there is no registration.

3. What a miner needs locally

  • The dataset file neb-datasets.bin of NEB 0.3.0 (release asset, 259 897 080 bytes, also as a 26 MB zip). Its BLAKE3 hash is pinned by the network: 296a174edbf67339fa0af341f063154292bba224b3d2cacf5b18b31c70da80eb. It holds 4 datasets (format: spec/shares.md section 11.6, spec/nebhash-v2.md section 2): index 0 Kepler transit signals and index 1 retinal OCT scans (NMD1, the v1 network, used up to block 8799), index 2 Kepler signals for KEP1 and index 3 retinal scans at 64 × 64 for RET2 (NMD2, from block 8800 / 8801). The pool names the dataset index of every job. The old 2-dataset file of NEB 0.2.1 (b1008b97…) still works until block 8799 only.
  • A ticket function ticket(model, dataset[index], key): for an NMD1 dataset exactly as in spec/shares.md sections 3–5 (BLAKE3, ChaCha8, sample selection from the key, integer forward/backward pass, gradient digest, ticket = BLAKE3(digest || key)); for an NMD2 dataset nebhash v2 (spec/nebhash-v2.md, the small convolutional networks KEP1 and RET2, same sample selection, digest and ticket rule). The C reference in the NEB repository (pow/ref/shares.c, pow/ref/nebhash2.c) is normative; the official CPU and GPU miners and suprminer are bit-exact with it. Any mismatch makes all your shares invalid, so test against the vectors in section 9.

4. Message flow

client                                                   pool
  | --- mining.subscribe ----------------------------------> |
  | <-- result [subscriptions, extranonce1, 8] ------------- |
  | <-- mining.set_difficulty [D] -------------------------- |
  | --- mining.authorize [user, pass] ---------------------> |
  | <-- mining.set_difficulty [D]   (only if it changed) --- |
  | <-- mining.set_model [weights_hash, index, base64] ----- |
  | <-- mining.notify [job ...] ---------------------------- |
  | <-- result true ---------------------------------------- |   (authorize result comes AFTER the first job)
  |                                                          |
  | --- mining.submit [user, job_id, en2, ntime, nonce] ---> |
  | <-- result true  /  error [code, message, null] -------- |
  |          ...                                             |
  | <-- mining.set_model (when the model changes) ---------- |
  | <-- mining.notify (new block: clean_jobs = true) ------- |
  | <-- mining.notify (same block, refresh: clean = false) - |

Rules a client can rely on:

  • Responses carry the request id. Notifications have "id": null and may arrive at any time, before or after any response. Never assume the next line is the answer to your last request.
  • The first set_difficulty, set_model and notify of a session are sent before the mining.authorize result.
  • A mining.set_model is sent before a mining.notify whenever that job's weights_hash differs from the one of the previous job on this session. In practice this happens with every new block (the model is trained by every block, and the dataset alternates). The pool sends it at most once per change; cache it.
  • A mining.set_difficulty is sent right before a mining.notify and applies from then on. A share that meets only the difficulty in force before the last change is still accepted, so work in flight is never lost.
  • The pool sends a mining.notify at least every 75 seconds (a new block, a refreshed job, or the current job re-sent). A client may treat 180 seconds without any data as a dead connection and reconnect.
  • Line sizes: pool → client lines are at most about 70 KB (mining.set_model is about 68 KB); client → pool lines must stay below 10 KB.

Reading the socket (important)

Read like cpuminer or XMRig: one network thread does blocking reads into a persistent buffer and hands every complete line (ending in \n) to the miner. A 68 KB set_model line arrives in many TCP segments; a client that polls with short timeouts and discards a partially received line will drop it and loop forever. Treat a read timeout as "no data yet" (on Windows it is WSAETIMEDOUT 10060 / TimedOut, on Unix EAGAIN / WouldBlock), never as a lost connection. Reconnect with a backoff (for example 1, 2, 4 … 60 s).

5. Methods

All hex strings are lowercase, without 0x.

5.1 mining.subscribe

{"id":1,"method":"mining.subscribe","params":["myminer/1.0"]}
{"id":1,"result":[[["mining.set_difficulty","neb0000000001e4"],["mining.notify","neb0000000001e4"]],"4e0000012317f237",8],"error":null}
  • result[1] = extranonce1, 8 bytes (16 hex), unique per session.
  • result[2] = extranonce2 size = 8 bytes. The miner chooses extranonce2 (16 hex) freely, for example a per-GPU counter.
  • params are optional and ignored. Subscribing twice on one connection is an error. mining.extranonce.subscribe is answered with false (the extranonce never changes within a session).

5.2 mining.authorize

{"id":2,"method":"mining.authorize","params":["neb1…your-address….rig01","x"]}
{"id":2,"result":true,"error":null}

false means the address is not a valid neb1 address or the worker name breaks the rules in section 2. One worker per connection: run one connection per worker name.

5.3 mining.set_difficulty (pool → miner)

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

D = expected number of attempts per share (a number, may be fractional). Share target:

share_target = floor( (2^256 - 1) / D )

In exact integer arithmetic. The pool computes floor((2^256 - 1) * 10^6 / round(D * 10^6)), which is the same for any D with at most 6 decimals. For D = 20000 the target as 32 little-endian bytes is:

21fdf675e09c11a5bdc117265305a3923a014d840d4faf946588635ddc460300

5.4 mining.set_model (pool → miner)

{"id":null,"method":"mining.set_model","params":["a0022b7e3683ae94695b79f06005484ec98ddd98372e9f540672a50365cc3606",1,"IB4hPQ/wACv+ETBN…(67 756 base64 characters)…"]}
# field meaning
0 weights_hash 32 bytes hex: BLAKE3("neb-model-weights" ‖ pack(W1) ‖ pack(W2)) (spec/shares.md section 7)
1 index index of the dataset in the network file this model trains: height mod 2 (0 or 1) up to block 8799, then 2 (KEP1) at even and 3 (RET2) at odd heights (spec/shares.md 11.7)
2 weights base64 of the packed weights. v1 (index 0/1): 50 816 bytes, pack(W1) (50 176 bytes, W1 is 128 × 784) followed by pack(W2) (640 bytes, W2 is 10 × 128). KEP1 (index 2): 13 000 bytes, RET2 (index 3): 11 140 bytes: all weights of the architecture's layers in layer order, each tensor row-major (spec/nebhash-v2.md sections 3 and 6). Always signed 4-bit values −8..7, element e in the low nibble of byte e/2 when e is even and in the high nibble when odd (spec/shares.md section 3.4)

Check: decode, verify BLAKE3("neb-model-weights" ‖ bytes) == weights_hash, and key the model by weights_hash. Jobs name the model they need by its weights_hash (notify field 10).

5.5 mining.notify (pool → miner)

{"id":null,"method":"mining.notify","params":[
 "10985",
 "513d8d34b62ba1aa3ec4495006dfca9256560e07791c931d9483dccc2121e76c",
 "01010000000000000000000000000000000000000000000000000000000000000000ffffffff1441150000",
 "0200b33f71000000000104290170dc4dc035fdc27c5a1db5b7bc7c0e09aee08a28f37abb6256a01827b700e1f5050000000002270bbd6380c0c7022176e202083d5671ed6bb7bc0c27350bcbd134846ce87dd5",
 [],
 "00000001","c3ce4480","6af447ee",true,
 5441,
 "a0022b7e3683ae94695b79f06005484ec98ddd98372e9f540672a50365cc3606",1,
 "00000001",
 "81e74225b9436f0fcd0ed90b31817f2acebb2e2b3d28577b90a7e46ad736c5f9",
 "6af455fe"]}
# field type meaning
0 job_id string opaque job id, echo it in mining.submit
1 parent hex 32 B block id of the parent, in header byte order (copy as is)
2 coinb1 hex coinbase transaction up to the extranonce
3 coinb2 hex coinbase transaction after the extranonce
4 merkle_branch array of hex 32 B siblings of the coinbase leaf, bottom-up (empty when the block has only the coinbase)
5 version hex u32 header version (big-endian hex of the value; write it little-endian)
6 nbits hex u32 compact block target (big-endian hex of the value; write it little-endian)
7 ntime hex u32 job start time = lowest allowed nTime (big-endian hex)
8 clean_jobs bool true = new parent block: drop all older jobs, their shares are stale
9 height number height of the block being mined
10 weights_hash hex 32 B the model (from mining.set_model) to evaluate attempts on
11 slot number dataset index of the job's model (same as set_model param 1): height mod 2 up to block 8799, then 2 / 3
12 tx_count hex u32 number of transactions in the body including the coinbase (big-endian hex)
13 shares_root hex 32 B commitment to the block's list of training shares (copy as is)
14 ntime_max hex u32 highest allowed nTime for this job (big-endian hex)

Jobs with clean_jobs = false refresh the block template on the same parent (the node refreshes every 30 s). Shares on the older jobs of the same parent stay valid, up to 16 jobs back.

5.6 mining.submit

{"id":4,"method":"mining.submit","params":["neb1…your-address….rig01","10985","0000000000000000","6af447ee","00002eb1"]}
{"id":4,"result":true,"error":null}
# field format
0 user the authorized username (informational)
1 job_id from notify field 0
2 extranonce2 16 hex = the 8 bytes you placed in the coinbase, in byte order
3 ntime 8 hex = big-endian hex of the u32 nTime you placed in the header
4 nonce 8 hex = big-endian hex of the u32 nonce you placed in the header

Submit every attempt whose ticket meets the share target. A ticket that also meets the block target (nbits) is a block; submit it the same way and the pool forwards it to the node at once. At most 16 submits may be unanswered at a time; send further ones after replies arrive.

5.7 Other methods

method answer
mining.suggest_difficulty [D] true; on ports 7801/7804/7805 the difficulty is set to D clamped to the port's limits
mining.configure {} (no extensions)
mining.extranonce.subscribe false with error [20, "Not supported.", null]
anything else error [20, "Unknown method", null]; the connection stays open

6. Building an attempt

Notation: ‖ = concatenation; H(tag, a, b, …) = BLAKE3(ASCII(tag) ‖ a ‖ b ‖ …) with no separator or length prefix (spec/node.md section 3); LE32(x) = 4 bytes little-endian; all 256-bit numbers (targets, tickets) are little-endian byte strings.

coinbase    = coinb1 ‖ extranonce1 (8 B) ‖ extranonce2 (8 B) ‖ coinb2
leaf        = H("leaf", BLAKE3(coinbase))
acc         = leaf;  for each sibling s in merkle_branch:  acc = H("node", acc ‖ s)
body_root   = H("body", LE32(tx_count) ‖ acc ‖ shares_root)
header (80) = LE32(version) ‖ parent (32) ‖ body_root (32) ‖ LE32(ntime) ‖ LE32(nbits) ‖ LE32(nonce)
key         = BLAKE3(header)                 (this is also the block id)
ticket      = NEB ticket of key on (model of weights_hash, dataset[index])  (v1: spec/shares.md 5.4, KEP1/RET2: spec/nebhash-v2.md 4)
share       if ticket <= share_target        (256-bit little-endian compare)
block       if ticket <= target(nbits)       target = mantissa << shift, nbits = shift << 24 | mantissa
  • The coinbase leaf is always the leftmost leaf, so it is always the left input of H("node", …).
  • Treat coinb1 and coinb2 as opaque bytes. For reference: the coinbase witness is LE32(height) ‖ extranonce1 ‖ extranonce2; coinb1 ends with the witness length and LE32(height), and coinb2 holds the outputs (pool and project share).
  • Work space: the 32-bit nonce gives 2^32 attempts per header. For more, change extranonce2 (a new body_root) or nTime. An attempt costs far more than a hash, so a GPU rarely exhausts a nonce range within one job.
  • nTime must satisfy ntime ≤ ntime_value ≤ ntime_max from the job, and must not be more than 90 s ahead of the pool's clock. Using the job's ntime unchanged is always fine.
  • The ticket depends on the model and the dataset of the job; the weights size and the dataset's NMD1/NMD2 header tell which network to run. After clean_jobs = true (new block) the model changes, so all in-flight attempts on older jobs are worthless; stop them at once.

7. Errors

error is [code, message, null].

code message (examples) meaning
20 incorrect size or hex of extranonce2 / ntime / nonce, ntime out of range, malformed submit params, too many shares in flight, Unknown method malformed or out-of-range request
21 job not found stale: the job belongs to an older block (or is more than 16 jobs old)
22 duplicate share the same extranonce2/ntime/nonce was already submitted, or the same header on this parent
23 low difficulty share of 1234.56 ticket above the share target (the number is the ticket's difficulty)
24 unauthorized worker submit before a successful authorize
25 not subscribed submit before subscribe

A few stale or duplicate shares around block changes are normal. Code 23 on every share means the ticket computation, the header assembly or a byte order is wrong; check them against section 9.

8. Limits and protection

  • At most 50 messages per second and 16 unanswered submits per connection; a client line longer than 10 KB, invalid JSON or more than 50 messages per second closes the connection.
  • A client that stops reading (more than 4 MB of unsent data) is disconnected.
  • If 50 % or more of the last 100 shares of a connection are invalid, its IP is banned for 10 minutes.
  • A connection that has sent no share for 600 s is closed at the next job.
  • Idle TCP connections are closed after 600 s.

9. Test vector

The session above (the pool on port 7802, difficulty 20000) with extranonce2 = 0000000000000000 and ntime = 6af447ee. The full messages, including the complete set_model line, are in https://neb.suprnova.cc/neb-stratum-example.json.

extranonce1  4e0000012317f237
coinbase     01010000000000000000000000000000000000000000000000000000000000000000ffffffff14411500004e0000012317f23700000000000000000200b33f71000000000104290170dc4dc035fdc27c5a1db5b7bc7c0e09aee08a28f37abb6256a01827b700e1f5050000000002270bbd6380c0c7022176e202083d5671ed6bb7bc0c27350bcbd134846ce87dd5
merkle root  480b7029aa6d97b2cc61edafb5ab59a8b1cd999b637e9b73e9fa5c1dfdccdba7   (= leaf, the branch is empty)
body_root    022746bed909155d8814ac149e210f23cdd7040f996dc242d092b215a2c92977

nonce 00000000
  header     01000000513d8d34b62ba1aa3ec4495006dfca9256560e07791c931d9483dccc2121e76c022746bed909155d8814ac149e210f23cdd7040f996dc242d092b215a2c92977ee47f46a8044cec300000000
  key        b1ca9209fee3ecb5a4f6579af92dea2021eda429fdf676132192dba783387b81
  ticket     44ec6b4e59d8c0b6d159588155687d709fbf6499d027b55d56276259932a1c8f   (not a share)

nonce 00002eb1   (the first share, found at the 11 954th attempt)
  header     01000000513d8d34b62ba1aa3ec4495006dfca9256560e07791c931d9483dccc2121e76c022746bed909155d8814ac149e210f23cdd7040f996dc242d092b215a2c92977ee47f46a8044cec3b12e0000
  key        06343b25ea077e3e5b322fa11b632433969add41d74e0e4784b38ec5e166c7fc
  ticket     eb366f7a7fa33f35b8138e5347533af03e0697726dc1a19b46fe288df0700200   (difficulty 26846.14 ≥ 20000: a share)
  submit     ["<address>.<worker>","10985","0000000000000000","6af447ee","00002eb1"]

Check your implementation step by step: the coinbase, then body_root, then the header bytes (note how ntime 6af447ee and nbits c3ce4480 appear little-endian in the header), then the key, and finally the ticket with the model from the example file and dataset slot 1.

9.1 nebhash v2 test vector

https://neb.suprnova.cc/neb-stratum-example-v2.json is the same exercise after the fork: a session with a KEP1 job (height 8800, dataset index 2, the fork model = the initial state of the upgrade seed BLAKE3("neb-model-upgrade" || LE32(8800) || LE32(0)), 13 000-byte set_model), the coinbase, body_root, header, key and ticket of nonce 0 and of the first share at difficulty 200, and the accepted submit. Check your KEP1 ticket against it and the official vectors vectors/nebhash2.txt of NEB 0.3.0.

10. Checklist for a new miner

  1. Load neb-datasets.bin and check its BLAKE3 hash.
  2. Connect, subscribe, authorize; read lines with a persistent buffer (section 4).
  3. On set_model: decode the base64, check the hash, store the model under weights_hash.
  4. On notify: build coinbase → body_root → header for your extranonce2; take the model by weights_hash and the dataset by its index (field 11); run v1 or nebhash v2 by the dataset's network; on clean_jobs abort old work.
  5. Roll the nonce (then extranonce2); compute key = BLAKE3(header) and the ticket; submit when ticket ≤ share_target.
  6. Reproduce the test vector of section 9 before mining on the pool.

11. NEB 0.3.0 network upgrade (nebhash v2 from block 8800)

From block 8800 (about Friday 13 November 2026, 19:00 UTC; the height decides) both models of the chain are small convolutional networks, KEP1 (Kepler signals, from block 8800) and RET2 (retinal scans at 64 × 64, normal or abnormal, from block 8801), each starting from a new model. On this pool's protocol only the values change, not the messages: set_model and notify name dataset index 2 or 3, the weights are 13 000 or 11 140 bytes, and the ticket is computed with nebhash v2. One attempt costs much more than a v1 attempt (CPU about 2 ms for KEP1 and 7 ms for RET2 instead of 0.4 ms), so hash rates drop by that factor and the difficulty follows; VarDiff adjusts each connection. A miner that only knows the v1 network or the old dataset file cannot mine after block 8799. The pool also hands every share that meets the chain's share target to its node, so the training steps of this pool's miners become part of the chain (spec/pool.md section 7.6).

Official NEB Links

NEB is developed on GitHub at SilasEigen/neb; the release page has the node, the wallet, the reference miners, neb-datasets.bin and the signed SHA256SUMS.