upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/main/tollgate_api.c
diff options
context:
space:
mode:
Diffstat (limited to 'main/tollgate_api.c')
-rw-r--r--main/tollgate_api.c189
1 files changed, 188 insertions, 1 deletions
diff --git a/main/tollgate_api.c b/main/tollgate_api.c
index 650b0f3..7113511 100644
--- a/main/tollgate_api.c
+++ b/main/tollgate_api.c
@@ -4,6 +4,9 @@
4#include "session.h" 4#include "session.h"
5#include "firewall.h" 5#include "firewall.h"
6#include "nucula_wallet.h" 6#include "nucula_wallet.h"
7#include "mining_payment.h"
8#include "stratum_proxy.h"
9#include "stratum_client.h"
7#include "esp_log.h" 10#include "esp_log.h"
8#include "cJSON.h" 11#include "cJSON.h"
9#include "lwip/sockets.h" 12#include "lwip/sockets.h"
@@ -128,6 +131,18 @@ static esp_err_t api_get_discovery(httpd_req_t *req)
128 cJSON_AddItemToArray(tips_tag, cJSON_CreateString("5")); 131 cJSON_AddItemToArray(tips_tag, cJSON_CreateString("5"));
129 cJSON_AddItemToArray(tags, tips_tag); 132 cJSON_AddItemToArray(tags, tips_tag);
130 133
134 if (cfg->mining_enabled) {
135 cJSON *mining_tag = cJSON_CreateArray();
136 cJSON_AddItemToArray(mining_tag, cJSON_CreateString("price_per_step"));
137 cJSON_AddItemToArray(mining_tag, cJSON_CreateString("mining"));
138 char mining_port_str[16];
139 snprintf(mining_port_str, sizeof(mining_port_str), "%d", cfg->mining_port);
140 cJSON_AddItemToArray(mining_tag, cJSON_CreateString(mining_port_str));
141 cJSON_AddItemToArray(mining_tag, cJSON_CreateString("GH/s"));
142 cJSON_AddItemToArray(mining_tag, cJSON_CreateString("sv1"));
143 cJSON_AddItemToArray(tags, mining_tag);
144 }
145
131 cJSON_AddItemToObject(root, "tags", tags); 146 cJSON_AddItemToObject(root, "tags", tags);
132 cJSON_AddStringToObject(root, "content", ""); 147 cJSON_AddStringToObject(root, "content", "");
133 148
@@ -463,6 +478,168 @@ static esp_err_t api_post_wallet_send(httpd_req_t *req)
463 return ESP_OK; 478 return ESP_OK;
464} 479}
465 480
481static esp_err_t api_get_mining_job(httpd_req_t *req)
482{
483 const stratum_job_t *job = stratum_proxy_get_current_job();
484 if (!job || !job->valid) {
485 httpd_resp_set_status(req, "503 Service Unavailable");
486 httpd_resp_set_type(req, "application/json");
487 httpd_resp_send(req, "{\"error\":\"no job\"}", 15);
488 return ESP_OK;
489 }
490
491 cJSON *root = cJSON_CreateObject();
492 cJSON_AddNumberToObject(root, "job_id", job->job_id);
493
494 char prevhash_hex[65];
495 for (int i = 0; i < 32; i++) snprintf(prevhash_hex + i * 2, 3, "%02x", job->prevhash[i]);
496 cJSON_AddStringToObject(root, "prevhash", prevhash_hex);
497
498 char merkle_hex[65];
499 for (int i = 0; i < 32; i++) snprintf(merkle_hex + i * 2, 3, "%02x", job->merkle_root[i]);
500 cJSON_AddStringToObject(root, "merkle_root", merkle_hex);
501
502 cJSON_AddNumberToObject(root, "version", job->version);
503 cJSON_AddNumberToObject(root, "nbits", job->nbits);
504 cJSON_AddNumberToObject(root, "ntime", job->ntime);
505 cJSON_AddNumberToObject(root, "hashprice", mining_get_current_hashprice());
506
507 char *json = cJSON_PrintUnformatted(root);
508 httpd_resp_set_type(req, "application/json");
509 httpd_resp_send(req, json, strlen(json));
510 cJSON_free(json);
511 cJSON_Delete(root);
512 return ESP_OK;
513}
514
515static esp_err_t api_post_mining_share(httpd_req_t *req)
516{
517 uint32_t client_ip = 0;
518 get_client_ip(req, &client_ip);
519
520 int content_len = req->content_len;
521 if (content_len <= 0 || content_len > 512) {
522 httpd_resp_set_status(req, "400 Bad Request");
523 httpd_resp_set_type(req, "application/json");
524 httpd_resp_send(req, "{\"error\":\"invalid body\"}", 21);
525 return ESP_OK;
526 }
527
528 char body[512];
529 int total = 0;
530 while (total < content_len) {
531 int r = httpd_req_recv(req, body + total, content_len - total);
532 if (r <= 0) {
533 httpd_resp_set_status(req, "400 Bad Request");
534 httpd_resp_set_type(req, "text/plain");
535 httpd_resp_send(req, "bad request", 11);
536 return ESP_OK;
537 }
538 total += r;
539 }
540 body[total] = '\0';
541
542 cJSON *root = cJSON_Parse(body);
543 if (!root) {
544 httpd_resp_set_status(req, "400 Bad Request");
545 httpd_resp_set_type(req, "application/json");
546 httpd_resp_send(req, "{\"error\":\"invalid json\"}", 21);
547 return ESP_OK;
548 }
549
550 cJSON *j_job_id = cJSON_GetObjectItem(root, "job_id");
551 cJSON *j_nonce = cJSON_GetObjectItem(root, "nonce");
552 cJSON *j_ntime = cJSON_GetObjectItem(root, "ntime");
553 cJSON *j_version = cJSON_GetObjectItem(root, "version");
554 if (!j_job_id || !j_nonce || !j_ntime || !j_version) {
555 cJSON_Delete(root);
556 httpd_resp_set_status(req, "400 Bad Request");
557 httpd_resp_set_type(req, "application/json");
558 httpd_resp_send(req, "{\"error\":\"missing fields\"}", 22);
559 return ESP_OK;
560 }
561
562 uint32_t job_id = (uint32_t)j_job_id->valuedouble;
563 uint32_t nonce = (uint32_t)j_nonce->valuedouble;
564 uint32_t ntime = (uint32_t)j_ntime->valuedouble;
565 uint32_t version = (uint32_t)j_version->valuedouble;
566 cJSON_Delete(root);
567
568 const stratum_job_t *job = stratum_proxy_get_current_job();
569 if (!job || !job->valid || job->job_id != job_id) {
570 httpd_resp_set_status(req, "400 Bad Request");
571 httpd_resp_set_type(req, "application/json");
572 httpd_resp_send(req, "{\"error\":\"stale job\"}", 19);
573 return ESP_OK;
574 }
575
576 esp_err_t share_err = stratum_client_submit_share(job_id, nonce, ntime, version);
577 bool accepted = (share_err == ESP_OK);
578
579 mining_update_hashrate(client_ip, accepted);
580 mining_client_stats_t *stats = mining_get_or_create_client(client_ip);
581
582 if (accepted) {
583 const tollgate_config_t *cfg = tollgate_config_get();
584 double hashprice = mining_get_current_hashprice();
585 uint64_t allotment_ms = mining_shares_to_allotment_ms(
586 stats->hashrate_ghs, hashprice, cfg->price_per_step, cfg->step_size_ms);
587
588 session_t *session = session_find_by_ip(client_ip);
589 if (!session || !session->active || session->payment_method != PAYMENT_METHOD_MINING) {
590 session = session_create(client_ip, allotment_ms);
591 if (session) session->payment_method = PAYMENT_METHOD_MINING;
592 } else {
593 session_extend(session, allotment_ms);
594 }
595 }
596
597 cJSON *resp = cJSON_CreateObject();
598 cJSON_AddBoolToObject(resp, "accepted", accepted);
599 cJSON_AddNumberToObject(resp, "hashrate_ghs", stats ? stats->hashrate_ghs : 0.0);
600 char *json = cJSON_PrintUnformatted(resp);
601 httpd_resp_set_type(req, "application/json");
602 httpd_resp_send(req, json, strlen(json));
603 cJSON_free(json);
604 cJSON_Delete(resp);
605 return ESP_OK;
606}
607
608static esp_err_t api_get_mining_stats(httpd_req_t *req)
609{
610 stratum_proxy_stats_t proxy_stats;
611 stratum_proxy_get_stats(&proxy_stats);
612
613 const stratum_client_state_t *client_state = stratum_client_get_state();
614
615 cJSON *root = cJSON_CreateObject();
616
617 cJSON *proxy = cJSON_CreateObject();
618 cJSON_AddNumberToObject(proxy, "hashrate_ghs", proxy_stats.hashrate_ghs);
619 cJSON_AddNumberToObject(proxy, "total_shares", (double)proxy_stats.total_shares);
620 cJSON_AddNumberToObject(proxy, "total_accepted", (double)proxy_stats.total_accepted);
621 cJSON_AddNumberToObject(proxy, "total_rejected", (double)proxy_stats.total_rejected);
622 cJSON_AddNumberToObject(proxy, "hashprice", proxy_stats.current_hashprice);
623 cJSON_AddNumberToObject(proxy, "active_miners", proxy_stats.active_miners);
624 cJSON_AddItemToObject(root, "proxy", proxy);
625
626 cJSON *upstream = cJSON_CreateObject();
627 cJSON_AddBoolToObject(upstream, "connected", client_state->connected);
628 cJSON_AddStringToObject(upstream, "pool_host", client_state->pool_host);
629 cJSON_AddNumberToObject(upstream, "pool_port", client_state->pool_port);
630 cJSON_AddNumberToObject(upstream, "difficulty", (double)client_state->difficulty);
631 cJSON_AddNumberToObject(upstream, "shares_accepted", (double)client_state->shares_accepted);
632 cJSON_AddNumberToObject(upstream, "shares_rejected", (double)client_state->shares_rejected);
633 cJSON_AddItemToObject(root, "upstream", upstream);
634
635 char *json = cJSON_PrintUnformatted(root);
636 httpd_resp_set_type(req, "application/json");
637 httpd_resp_send(req, json, strlen(json));
638 cJSON_free(json);
639 cJSON_Delete(root);
640 return ESP_OK;
641}
642
466static const httpd_uri_t uri_discovery = { .uri = "/", .method = HTTP_GET, .handler = api_get_discovery }; 643static const httpd_uri_t uri_discovery = { .uri = "/", .method = HTTP_GET, .handler = api_get_discovery };
467static const httpd_uri_t uri_payment = { .uri = "/", .method = HTTP_POST, .handler = api_post_payment }; 644static const httpd_uri_t uri_payment = { .uri = "/", .method = HTTP_POST, .handler = api_post_payment };
468static const httpd_uri_t uri_usage = { .uri = "/usage", .method = HTTP_GET, .handler = api_get_usage }; 645static const httpd_uri_t uri_usage = { .uri = "/usage", .method = HTTP_GET, .handler = api_get_usage };
@@ -470,6 +647,9 @@ static const httpd_uri_t uri_whoami = { .uri = "/whoami", .method = HTTP_GET, .h
470static const httpd_uri_t uri_wallet = { .uri = "/wallet", .method = HTTP_GET, .handler = api_get_wallet }; 647static const httpd_uri_t uri_wallet = { .uri = "/wallet", .method = HTTP_GET, .handler = api_get_wallet };
471static const httpd_uri_t uri_wallet_swap = { .uri = "/wallet/swap", .method = HTTP_POST, .handler = api_post_wallet_swap }; 648static const httpd_uri_t uri_wallet_swap = { .uri = "/wallet/swap", .method = HTTP_POST, .handler = api_post_wallet_swap };
472static const httpd_uri_t uri_wallet_send = { .uri = "/wallet/send", .method = HTTP_POST, .handler = api_post_wallet_send }; 649static const httpd_uri_t uri_wallet_send = { .uri = "/wallet/send", .method = HTTP_POST, .handler = api_post_wallet_send };
650static const httpd_uri_t uri_mining_job = { .uri = "/mining/job", .method = HTTP_GET, .handler = api_get_mining_job };
651static const httpd_uri_t uri_mining_share = { .uri = "/mining/share", .method = HTTP_POST, .handler = api_post_mining_share };
652static const httpd_uri_t uri_mining_stats = { .uri = "/mining/stats", .method = HTTP_GET, .handler = api_get_mining_stats };
473 653
474esp_err_t tollgate_api_start(void) 654esp_err_t tollgate_api_start(void)
475{ 655{
@@ -478,7 +658,7 @@ esp_err_t tollgate_api_start(void)
478 httpd_config_t config = HTTPD_DEFAULT_CONFIG(); 658 httpd_config_t config = HTTPD_DEFAULT_CONFIG();
479 config.server_port = 2121; 659 config.server_port = 2121;
480 config.ctrl_port = 32769; 660 config.ctrl_port = 32769;
481 config.max_uri_handlers = 10; 661 config.max_uri_handlers = 16;
482 config.stack_size = 16384; 662 config.stack_size = 16384;
483 663
484 esp_err_t ret = httpd_start(&s_api_server, &config); 664 esp_err_t ret = httpd_start(&s_api_server, &config);
@@ -495,6 +675,13 @@ esp_err_t tollgate_api_start(void)
495 httpd_register_uri_handler(s_api_server, &uri_wallet_swap); 675 httpd_register_uri_handler(s_api_server, &uri_wallet_swap);
496 httpd_register_uri_handler(s_api_server, &uri_wallet_send); 676 httpd_register_uri_handler(s_api_server, &uri_wallet_send);
497 677
678 const tollgate_config_t *cfg = tollgate_config_get();
679 if (cfg->mining_enabled) {
680 httpd_register_uri_handler(s_api_server, &uri_mining_job);
681 httpd_register_uri_handler(s_api_server, &uri_mining_share);
682 httpd_register_uri_handler(s_api_server, &uri_mining_stats);
683 }
684
498 ESP_LOGI(TAG, "TollGate API started on port 2121"); 685 ESP_LOGI(TAG, "TollGate API started on port 2121");
499 return ESP_OK; 686 return ESP_OK;
500} 687}