diff options
Diffstat (limited to 'main/tollgate_api.c')
| -rw-r--r-- | main/tollgate_api.c | 189 |
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 | ||
| 481 | static 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 | |||
| 515 | static 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 | |||
| 608 | static 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 | |||
| 466 | static const httpd_uri_t uri_discovery = { .uri = "/", .method = HTTP_GET, .handler = api_get_discovery }; | 643 | static const httpd_uri_t uri_discovery = { .uri = "/", .method = HTTP_GET, .handler = api_get_discovery }; |
| 467 | static const httpd_uri_t uri_payment = { .uri = "/", .method = HTTP_POST, .handler = api_post_payment }; | 644 | static const httpd_uri_t uri_payment = { .uri = "/", .method = HTTP_POST, .handler = api_post_payment }; |
| 468 | static const httpd_uri_t uri_usage = { .uri = "/usage", .method = HTTP_GET, .handler = api_get_usage }; | 645 | static 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 | |||
| 470 | static const httpd_uri_t uri_wallet = { .uri = "/wallet", .method = HTTP_GET, .handler = api_get_wallet }; | 647 | static const httpd_uri_t uri_wallet = { .uri = "/wallet", .method = HTTP_GET, .handler = api_get_wallet }; |
| 471 | static const httpd_uri_t uri_wallet_swap = { .uri = "/wallet/swap", .method = HTTP_POST, .handler = api_post_wallet_swap }; | 648 | static const httpd_uri_t uri_wallet_swap = { .uri = "/wallet/swap", .method = HTTP_POST, .handler = api_post_wallet_swap }; |
| 472 | static const httpd_uri_t uri_wallet_send = { .uri = "/wallet/send", .method = HTTP_POST, .handler = api_post_wallet_send }; | 649 | static const httpd_uri_t uri_wallet_send = { .uri = "/wallet/send", .method = HTTP_POST, .handler = api_post_wallet_send }; |
| 650 | static const httpd_uri_t uri_mining_job = { .uri = "/mining/job", .method = HTTP_GET, .handler = api_get_mining_job }; | ||
| 651 | static const httpd_uri_t uri_mining_share = { .uri = "/mining/share", .method = HTTP_POST, .handler = api_post_mining_share }; | ||
| 652 | static const httpd_uri_t uri_mining_stats = { .uri = "/mining/stats", .method = HTTP_GET, .handler = api_get_mining_stats }; | ||
| 473 | 653 | ||
| 474 | esp_err_t tollgate_api_start(void) | 654 | esp_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 | } |