diff --git a/doc/API.md b/doc/API.md index 2cd0fbbe1..ee29f3d21 100644 --- a/doc/API.md +++ b/doc/API.md @@ -4,7 +4,7 @@ If you want use HTTP API you need enable it (`"enabled": true,`) then choice `po Offical HTTP client for API: http://workers.xmrig.info/ -Example configuration: +Example configuration, used in Curl examples below: ```json "api": { @@ -12,11 +12,11 @@ Example configuration: "worker-id": null, }, "http": { - "enabled": false, + "enabled": true, "host": "127.0.0.1", - "port": 0, - "access-token": null, - "restricted": true + "port": 44444, + "access-token": "SECRET", + "restricted": false } ``` @@ -37,30 +37,78 @@ Versions before 2.15 was use another options for API https://github.com/xmrig/xm ## Endpoints -### GET /1/summary +### APIVersion 2 -Get miner summary information. [Example](api/1/summary.json). +#### GET /2/summary -### GET /1/threads +Get miner summary information. [Example](api/2/summary.json). -Get detailed information about miner threads. [Example](api/1/threads.json). +#### GET /2/backends + +Get detailed information about miner backends. [Example](api/2/backends.json). + +### APIVersion 1 (deprecated) + +#### GET /1/summary + +Get miner summary information. Currently identical to `GET /2/summary` + +#### GET /1/threads + +**REMOVED** Get detailed information about miner threads. [Example](api/1/threads.json). + +Functionally replaced by `GET /2/backends` which contains a `threads` item per backend. + +### APIVersion 0 (deprecated) + +#### GET /api.json + +Get miner summary information. Currently identical to `GET /2/summary` ## Restricted endpoints All API endpoints below allow access to sensitive information and remote configure miner. You should set `access-token` and allow unrestricted access (`"restricted": false`). -### GET /1/config +### JSON-RPC Interface -Get current miner configuration. [Example](api/1/config.json). +#### POST /json_rpc +Control miner with JSON-RPC. Methods: `pause`, `resume`, `stop`, `start` -### PUT /1/config +Curl example: + +``` +curl -v --data "{\"method\":\"pause\",\"id\":1}" -H "Content-Type: application/json" -H "Authorization: Bearer SECRET" http://127.0.0.1:44444/json_rpc +``` + +### APIVersion 2 + +#### GET /2/config + +Get current miner configuration. [Example](api/2/config.json). + +#### PUT /2/config Update current miner configuration. Common use case, get current configuration, make changes, and upload it to miner. Curl example: ``` -curl -v --data-binary @config.json -X PUT -H "Content-Type: application/json" -H "Authorization: Bearer SECRET" http://127.0.0.1:44444/1/config +...GET current config... +curl -v -H "Content-Type: application/json" -H "Authorization: Bearer SECRET" http://127.0.0.1:44444/2/config > config.json +...make changes... +vim config.json +...PUT changed config... +curl -v --data-binary @config.json -X PUT -H "Content-Type: application/json" -H "Authorization: Bearer SECRET" http://127.0.0.1:44444/2/config ``` + +### APIVersion 1 (deprecated) + +#### GET /1/config + +Get current miner configuration. Currently identical to `GET /2/config` + +#### PUT /1/config + +Update current miner configuration. Currently identical to `PUT /2/config` diff --git a/doc/api/2/backends.json b/doc/api/2/backends.json new file mode 100644 index 000000000..dfc3920f1 --- /dev/null +++ b/doc/api/2/backends.json @@ -0,0 +1,78 @@ +[ + { + "type": "cpu", + "enabled": true, + "algo": "rx/0", + "profile": "rx", + "hw-aes": true, + "priority": -1, + "msr": true, + "asm": "intel", + "argon2-impl": "AVX2", + "hugepages": [6, 6], + "memory": 6291456, + "hashrate": [1235.78, 1228.89, null], + "threads": [ + { + "intensity": 1, + "affinity": 0, + "av": 1, + "hashrate": [409.75, 406.58, null] + }, + { + "intensity": 1, + "affinity": 2, + "av": 1, + "hashrate": [412.9, 411.33, null] + }, + { + "intensity": 1, + "affinity": 4, + "av": 1, + "hashrate": [413.11, 410.98, null] + } + ] + }, + { + "type": "cuda", + "enabled": true, + "algo": "cn-heavy/xhv", + "profile": "cn-heavy/xhv", + "versions": { + "cuda-runtime": "11.6", + "cuda-driver": "11.6", + "plugin": "6.17.1-dev", + "nvml": "11.512.15", + "driver": "512.15" + }, + "hashrate": [247.02, 247.34, null], + "threads": [ + { + "index": 0, + "threads": 32, + "blocks": 38, + "bfactor": 6, + "bsleep": 25, + "affinity": -1, + "cclock": 0, + "mclock": 0, + "dataset_host": false, + "hashrate": [246.77, 247.26, null], + "name": "NVIDIA GeForce GTX 970", + "bus_id": "01:00.0", + "smx": 13, + "arch": 52, + "global_mem": 4294836224, + "clock": 1177, + "memory_clock": 3666, + "health": { + "temperature": 69, + "power": 161, + "clock": 1328, + "mem_clock": 3662, + "fan_speed": [100] + } + } + ] + } +] \ No newline at end of file diff --git a/doc/api/2/config.json b/doc/api/2/config.json new file mode 100644 index 000000000..5d66ef4c5 --- /dev/null +++ b/doc/api/2/config.json @@ -0,0 +1,136 @@ +{ + "api": { + "id": null, + "worker-id": null + }, + "http": { + "enabled": true, + "host": "127.0.0.1", + "port": 44444, + "access-token": "SECRET", + "restricted": false + }, + "autosave": true, + "background": false, + "colors": true, + "title": true, + "randomx": { + "init": -1, + "init-avx2": -1, + "mode": "auto", + "1gb-pages": true, + "rdmsr": true, + "wrmsr": true, + "cache_qos": false, + "numa": true, + "scratchpad_prefetch_mode": 1 + }, + "cpu": { + "enabled": true, + "huge-pages": true, + "huge-pages-jit": true, + "hw-aes": null, + "priority": null, + "memory-pool": true, + "yield": true, + "asm": true, + "argon2-impl": null, + "argon2": [0, 2, 4, 6, 5, 7], + "astrobwt/v2": [1, 2, 3, 4, 5, 6, 7], + "cn": [ + [1, 0], + [1, 2], + [1, 4] + ], + "cn-heavy": [ + [1, 0], + [1, 2] + ], + "cn-lite": [ + [1, 0], + [1, 2], + [1, 4], + [1, 6], + [1, 5], + [1, 7] + ], + "cn-pico": [ + [2, 1], + [2, 2], + [2, 3], + [2, 4], + [2, 5], + [2, 6], + [2, 7] + ], + "cn/2": [ + [1, 0], + [1, 2], + [1, 4] + ], + "cn/upx2": [ + [2, 1], + [2, 2], + [2, 3], + [2, 4], + [2, 5], + [2, 6], + [2, 7] + ], + "ghostrider": [ + [8, 0], + [8, 2], + [8, 4] + ], + "rx": [0, 2, 4], + "rx/arq": [1, 2, 3, 4, 5, 6, 7], + "rx/keva": [0, 2, 4, 6, 5, 7], + "rx/wow": [0, 2, 4, 6, 5, 7], + "cn-lite/0": false, + "cn/0": "cn" + }, + "log-file": null, + "donate-level": 0, + "donate-over-proxy": 0, + "pools": [ + { + "algo": null, + "coin": null, + "url": "some.pool:10064", + "user": "4blahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahbl", + "pass": "x", + "rig-id": null, + "nicehash": false, + "keepalive": true, + "enabled": true, + "tls": false, + "wss": false, + "daemon": false, + "socks5": null, + "self-select": null, + "submit-to-origin": false + } + ], + "retries": 5, + "retry-pause": 5, + "print-time": 64, + "syslog": false, + "tls": { + "enabled": false, + "protocols": null, + "cert": null, + "cert_key": null, + "ciphers": null, + "ciphersuites": null, + "dhparam": null + }, + "dns": { + "ipv6": false, + "ttl": 30 + }, + "user-agent": null, + "verbose": 1, + "watch": true, + "pause-on-battery": false, + "pause-on-active": false +} diff --git a/doc/api/2/summary.json b/doc/api/2/summary.json new file mode 100644 index 000000000..aaf0108cb --- /dev/null +++ b/doc/api/2/summary.json @@ -0,0 +1,76 @@ +{ + "id": "51aca77da137cb62", + "worker_id": "tpad", + "uptime": 106, + "restricted": false, + "resources": { + "memory": { + "free": 4977348608, + "total": 16659546112, + "resident_set_memory": 16441344 + }, + "load_average": [3.4, 2.7, 2.44], + "hardware_concurrency": 8 + }, + "features": ["api", "asm", "http", "hwloc", "tls"], + "results": { + "diff_current": 37638, + "shares_good": 3, + "shares_total": 3, + "avg_time": 35, + "avg_time_ms": 35603, + "hashes_total": 165638, + "best": [358821, 344438, 73371, 0, 0, 0, 0, 0, 0, 0] + }, + "algo": "rx/0", + "connection": { + "pool": "some.pool:20064", + "ip": "127.1.2.3", + "uptime": 106, + "uptime_ms": 106811, + "ping": 405, + "failures": 0, + "tls": "TLSv1.3", + "tls-fingerprint": null, + "algo": "rx/0", + "diff": 37638, + "accepted": 3, + "rejected": 0, + "avg_time": 35, + "avg_time_ms": 35603, + "hashes_total": 165638 + }, + "version": "6.17.1-dev", + "kind": "miner", + "ua": "XMRig/6.17.1-dev (Linux x86_64) libuv/1.43.0 gcc/9.4.0", + "cpu": { + "brand": "Intel(R) Core(TM) i7-4700MQ CPU @ 2.40GHz", + "family": 6, + "model": 60, + "stepping": 3, + "proc_info": 198339, + "aes": true, + "avx2": true, + "x64": true, + "64_bit": true, + "l2": 1048576, + "l3": 6291456, + "cores": 4, + "threads": 8, + "packages": 1, + "nodes": 1, + "backend": "hwloc/2.7.0", + "msr": "intel", + "assembly": "intel", + "arch": "x86_64", + "flags": ["aes", "avx", "avx2", "bmi2", "osxsave", "pdpe1gb", "sse2", "ssse3", "sse4.1", "popcnt"] + }, + "donate_level": 0, + "paused": false, + "algorithms": ["cn/0", "cn/1", "cn/2", "cn/r", "cn/fast", "cn/half", "cn/xao", "cn/rto", "cn/rwz", "cn/zls", "cn/double", "cn/ccx", "cn-lite/1", "cn-heavy/0", "cn-heavy/tube", "cn-heavy/xhv", "cn-pico", "cn-pico/tlo", "cn/upx2", "rx/0", "rx/wow", "rx/arq", "rx/graft", "rx/sfx", "rx/keva", "argon2/chukwa", "argon2/chukwav2", "argon2/ninja", "astrobwt/v2", "ghostrider"], + "hashrate": { + "total": [1207.19, 1210.82, null], + "highest": 1316.85 + }, + "hugepages": [6, 6] +} diff --git a/src/base/api/interfaces/IApiRequest.h b/src/base/api/interfaces/IApiRequest.h index 35fb27d5f..fb55c2807 100644 --- a/src/base/api/interfaces/IApiRequest.h +++ b/src/base/api/interfaces/IApiRequest.h @@ -52,6 +52,8 @@ public: enum RequestType { REQ_UNKNOWN, REQ_SUMMARY, + REQ_BACKENDS, + REQ_CONFIG, REQ_JSON_RPC }; diff --git a/src/base/api/requests/ApiRequest.h b/src/base/api/requests/ApiRequest.h index c87e822c0..4bfb1637c 100644 --- a/src/base/api/requests/ApiRequest.h +++ b/src/base/api/requests/ApiRequest.h @@ -60,7 +60,7 @@ protected: inline Source source() const override { return m_source; } inline void done(int) override { m_state = STATE_DONE; } - int m_version = 1; + int m_version = 0; RequestType m_type = REQ_UNKNOWN; State m_state = STATE_NEW; String m_rpcMethod; diff --git a/src/base/api/requests/HttpApiRequest.cpp b/src/base/api/requests/HttpApiRequest.cpp index de43f752e..c9f56b075 100644 --- a/src/base/api/requests/HttpApiRequest.cpp +++ b/src/base/api/requests/HttpApiRequest.cpp @@ -67,10 +67,33 @@ xmrig::HttpApiRequest::HttpApiRequest(const HttpData &req, bool restricted) : m_res(req.id()), m_url(req.url.c_str()) { - if (method() == METHOD_GET) { - if (url() == "/1/summary" || url() == "/2/summary" || url() == "/api.json") { - m_type = REQ_SUMMARY; + if (url().size() > 4 && memcmp(url().data(), "/", 1) == 0 && memcmp(url().data()+2, "/", 1) == 0) { + if (memcmp(url().data(), "/2/", 3) == 0) { + m_version = 2; + } else if (memcmp(url().data(), "/1/", 3) == 0) { + m_version = 1; } + switch (url().size()) { + case 9: + if (memcmp(url().data()+3, "config", 6) == 0) { + m_type = REQ_CONFIG; + } + break; + case 10: + if (memcmp(url().data()+3, "summary", 7) == 0) { + m_type = REQ_SUMMARY; + } + break; + case 11: + if (memcmp(url().data()+3, "backends", 8) == 0) { + m_type = REQ_BACKENDS; + } + break; + } + } + + if (url() == "/api.json") { + m_type = REQ_SUMMARY; } if (method() == METHOD_POST && url() == "/json_rpc") { @@ -94,12 +117,6 @@ xmrig::HttpApiRequest::HttpApiRequest(const HttpData &req, bool restricted) : return; } - - if (url().size() > 4) { - if (memcmp(url().data(), "/2/", 3) == 0) { - m_version = 2; - } - } } diff --git a/src/base/kernel/Base.cpp b/src/base/kernel/Base.cpp index 4aa2e466e..b5d30af3e 100644 --- a/src/base/kernel/Base.cpp +++ b/src/base/kernel/Base.cpp @@ -45,13 +45,6 @@ #ifdef XMRIG_FEATURE_API # include "base/api/Api.h" # include "base/api/interfaces/IApiRequest.h" - -namespace xmrig { - -static const char *kConfigPathV1 = "/1/config"; -static const char *kConfigPathV2 = "/2/config"; - -} // namespace xmrig #endif @@ -317,7 +310,7 @@ void xmrig::Base::onFileChanged(const String &fileName) void xmrig::Base::onRequest(IApiRequest &request) { if (request.method() == IApiRequest::METHOD_GET) { - if (request.url() == kConfigPathV1 || request.url() == kConfigPathV2) { + if (request.type() == IApiRequest::REQ_CONFIG) { if (request.isRestricted()) { return request.done(403); } @@ -327,7 +320,7 @@ void xmrig::Base::onRequest(IApiRequest &request) } } else if (request.method() == IApiRequest::METHOD_PUT || request.method() == IApiRequest::METHOD_POST) { - if (request.url() == kConfigPathV1 || request.url() == kConfigPathV2) { + if (request.type() == IApiRequest::REQ_CONFIG) { request.accept(); if (!reload(request.json())) { diff --git a/src/core/Miner.cpp b/src/core/Miner.cpp index 1f99b943f..b0ef37d0f 100644 --- a/src/core/Miner.cpp +++ b/src/core/Miner.cpp @@ -710,7 +710,7 @@ void xmrig::Miner::onRequest(IApiRequest &request) d_ptr->getMiner(request.reply(), request.doc(), request.version()); d_ptr->getHashrate(request.reply(), request.doc(), request.version()); } - else if (request.url() == "/2/backends") { + else if (request.type() == IApiRequest::REQ_BACKENDS && request.version() == 2) { request.accept(); d_ptr->getBackends(request.reply(), request.doc()); @@ -732,6 +732,12 @@ void xmrig::Miner::onRequest(IApiRequest &request) stop(); } + else if (request.rpcMethod() == "start") { + request.accept(); + + const auto config = d_ptr->controller->config(); + onConfigChanged(config, config); + } } for (IBackend *backend : d_ptr->backends) {