Stratum
Узел включает сервер Stratum для майнеров. Он слушает на 127.0.0.1 порту 3416, смещённом по сети, как и
все остальные порты узла, и работает только при enable_stratum_server = true. Этот ключ по умолчанию равен false в каждой
сети, поэтому сгенерированный epic-server.toml всегда нужно отредактировать перед подключением майнера, включая
usernet. См. таблицу портов и epic-server.toml.
Транспорт — сырой TCP с JSON-сообщениями, разделёнными символом новой строки. Каждое сообщение JSON-RPC — одна строка, завершённая символом новой строки, без HTTP-заголовков, путей и кодов состояния.
Методы и коды ошибок
Обрабатываются пять методов. Всё остальное возвращает -32601.
| Метод | Параметры | Результат при успехе |
|---|---|---|
login | login, pass, agent, все обязательны | "ok". Записывает имя воркера и агент (servers/src/mining/stratumserver.rs:928) |
getjobtemplate | algorithm | Объект шаблона задания |
submit | height, job_id, nonce, pow | "ok", или "blockfound - <block hash>" если шара является блоком |
keepalive | none | "ok" |
status | none | id, height, difficulty, accepted, rejected, stale для вызывающего воркера |
Коды ошибок, которые отправляет сервер:
| Код | Сообщение |
|---|---|
-32000 | Узел синхронизируется — подождите |
-32501 | Шара отклонена из-за низкой сложности |
-32502 | Не удалось проверить решение |
-32503 | Решение отправлено слишком поздно |
-32600 | Некорректный запрос |
-32601 | Метод не найден |
32603 | Внутренняя ошибка |
Запрос работы
getjobtemplate требует параметр algorithm, указывающий, какой из трёх алгоритмов доказательства работы вы намерены
использовать для майнинга. Epic запускает все три одновременно, поэтому задание привязано к
конкретному алгоритму.
algorithm в ответе — тот, который требует политика на данной высоте блока, не обязательно тот, который вы
запросили, поэтому майнеру нужен один воркер на каждый алгоритм, который он намерен использовать.
only_randomx сужает политику до одного алгоритма в частной цепочке. См. политику блоков.
Узел возвращает -32000 Node is syncing - Please wait до тех пор, пока не достигнет peer_min_preferred_outbound_count исходящих пиров. В usernet, который по
замыслу не имеет пиров, установите этот ключ в 0. См. запуск локальной сети.
Поля шаблона задания (servers/src/mining/stratumserver.rs:176):
| Поле | Тип |
|---|---|
height | u64 |
job_id | u64, индекс версии блока, на основе которого построено задание |
pre_pow | hex-строка сериализованного заголовка включительно до полей перед нонсом |
algorithm | cuckoo, randomx или progpow |
difficulty | массив пар [algorithm_name, minimum_share_difficulty] |
block_difficulty | массив пар [algorithm_name, current_block_difficulty] |
epochs | массив троек [start_height, end_height, seed_hash], для выбора сида RandomX |
Оба списка сложности охватывают Cuckatoo, RandomX и ProgPow, в указанном порядке.
Отправка шар
submit принимает height, job_id, nonce как число и pow. pow — перечисление с внешним тегом, то есть
объект с одним ключом, значение которого является кортежем (servers/src/mining/stratumserver.rs:161):
| Алгоритм | pow |
|---|---|
| Cuckoo и Cuckatoo | {"Cuckoo": [edge_bits, [nonces]]}, сначала edge bits, затем 42 нонса |
| RandomX | {"RandomX": [32 bytes]} |
| ProgPow | {"ProgPow": [32 bytes]} |
Шара, сложность которой превышает block_difficulty, обрабатывается как блок, и результат принимает значение
blockfound - <block hash>. Шара, которая превышает только difficulty, засчитывается, и результат равен "ok".
Справочник сообщений
Сериализация определяется структурами в servers/src/mining/stratumserver.rs.
Запросы
id — целое число или строка, jsonrpc — "2.0", а params — объект параметров метода или null (servers/src/mining/stratumserver.rs:64).
{"id":"1","jsonrpc":"2.0","method":"getjobtemplate","params":{"algorithm":"randomx"}}
{"id":"2","jsonrpc":"2.0","method":"keepalive","params":null}
{"id":"3","jsonrpc":"2.0","method":"status","params":null}
{"id":"4","jsonrpc":"2.0","method":"submit","params":{
"height":1000001,
"job_id":7,
"nonce":13476209874311234567,
"pow":{"RandomX":[10,27,44,3,255,18,9,72,64,31,88,120,4,17,203,55,
91,6,142,77,38,199,15,60,231,2,113,49,86,170,25,8]}
}}
{"id":"5","jsonrpc":"2.0","method":"submit","params":{
"height":1000001,"job_id":7,"nonce":42,
"pow":{"Cuckoo":[31,[1042,20984,31006]]}
}}
Ответы
Каждый ответ содержит одни и те же четыре ключа и воспроизводит метод, который его породил (servers/src/mining/stratumserver.rs:73).
result и error присутствуют оба, один из них null.
{"id": <same as the request>, "jsonrpc": "2.0", "method": <the method called>,
"result": <method result> | null, "error": {"code": <int>, "message": <string>} | null}
result равен "ok" для login и keepalive, строке "blockfound - <hash>" или "ok" для submit, объекту шаблона задания для
getjobtemplate и объекту статуса воркера для status.
Просмотр живой сессии
Один запрос, один ответ, с nc:
# Сначала включите сервер: stratum_mining_config.enable_stratum_server = true
printf '{"id":"1","jsonrpc":"2.0","method":"getjobtemplate","params":{"algorithm":"randomx"}}\n' \
| nc 127.0.0.1 3416
По одному заданию на алгоритм, с ответами в форматированном виде:
"""Request one Stratum job per algorithm and print the node's replies.
Standard library only. Run an `epic` node with `enable_stratum_server = true` first.
See https://devdocs.epiccash.com/mining/stratum
Override the defaults for another network:
STRATUM_HOST=127.0.0.1 STRATUM_PORT=23416 python stratum_probe.py
"""
from __future__ import annotations
import json
import os
import socket
from typing import Any
HOST = os.environ.get("STRATUM_HOST", "127.0.0.1")
PORT = int(os.environ.get("STRATUM_PORT", "3416"))
ALGORITHMS = ("randomx", "progpow", "cuckatoo")
def request(stream: Any, request_id: int, method: str, params: Any) -> Any:
"""Send one newline-delimited JSON-RPC request and return the decoded reply.
The transport is raw TCP: one JSON object per line, no HTTP framing.
"""
payload = {"id": str(request_id), "jsonrpc": "2.0", "method": method, "params": params}
stream.write(json.dumps(payload) + "\n")
stream.flush()
line = stream.readline()
if not line:
raise ConnectionError("stratum connection closed before a reply arrived")
return json.loads(line)
def probe(host: str = HOST, port: int = PORT) -> None:
"""Ask for a job template for each algorithm and print what comes back."""
with socket.create_connection((host, port), timeout=15) as sock:
stream = sock.makefile("rw", encoding="utf-8", newline="\n")
for request_id, algorithm in enumerate(ALGORITHMS, start=1):
reply = request(stream, request_id, "getjobtemplate", {"algorithm": algorithm})
print(f"{algorithm}: {json.dumps(reply, indent=2)}")
if __name__ == "__main__":
probe()
Далее
EpicCash/epic-miner — эталонный майнер. Поверхность Foreign JSON-RPC узла предоставляет ту же работу через HTTP
посредством get_block_template, finalize_block_template и submit_block.