Перейти к основному содержимому

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.

МетодПараметрыРезультат при успехе
loginlogin, pass, agent, все обязательны"ok". Записывает имя воркера и агент (servers/src/mining/stratumserver.rs:928)
getjobtemplatealgorithmОбъект шаблона задания
submitheight, job_id, nonce, pow"ok", или "blockfound - <block hash>" если шара является блоком
keepalivenone"ok"
statusnoneid, 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):

ПолеТип
heightu64
job_idu64, индекс версии блока, на основе которого построено задание
pre_powhex-строка сериализованного заголовка включительно до полей перед нонсом
algorithmcuckoo, 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).

client -> server, one line each
{"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}
client -> server, a RandomX share, one line
{"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]}
}}
client -> server, a Cuckatoo share, nonce list abbreviated to 3 of 42, one line
{"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.

server -> client, envelope
{"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

По одному заданию на алгоритм, с ответами в форматированном виде:

examples/python/stratum_probe.py
"""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.