diff options
Diffstat (limited to 'main/tollgate_api.c')
| -rw-r--r-- | main/tollgate_api.c | 190 |
1 files changed, 189 insertions, 1 deletions
diff --git a/main/tollgate_api.c b/main/tollgate_api.c index 21bf9ef..b775f55 100644 --- a/main/tollgate_api.c +++ b/main/tollgate_api.c | |||
| @@ -7,6 +7,9 @@ | |||
| 7 | #include "nucula_wallet.h" | 7 | #include "nucula_wallet.h" |
| 8 | #include "mint_health.h" | 8 | #include "mint_health.h" |
| 9 | #include "market.h" | 9 | #include "market.h" |
| 10 | #include "mining_payment.h" | ||
| 11 | #include "stratum_proxy.h" | ||
| 12 | #include "stratum_client.h" | ||
| 10 | #include "esp_log.h" | 13 | #include "esp_log.h" |
| 11 | #include "esp_system.h" | 14 | #include "esp_system.h" |
| 12 | #include "cJSON.h" | 15 | #include "cJSON.h" |
| @@ -150,6 +153,18 @@ static esp_err_t api_get_discovery(httpd_req_t *req) | |||
| 150 | cJSON_AddItemToArray(tips_tag, cJSON_CreateString("5")); | 153 | cJSON_AddItemToArray(tips_tag, cJSON_CreateString("5")); |
| 151 | cJSON_AddItemToArray(tags, tips_tag); | 154 | cJSON_AddItemToArray(tags, tips_tag); |
| 152 | 155 | ||
| 156 | if (cfg->mining_enabled) { | ||
| 157 | cJSON *mining_tag = cJSON_CreateArray(); | ||
| 158 | cJSON_AddItemToArray(mining_tag, cJSON_CreateString("price_per_step")); | ||
| 159 | cJSON_AddItemToArray(mining_tag, cJSON_CreateString("mining")); | ||
| 160 | char mining_port_str[16]; | ||
| 161 | snprintf(mining_port_str, sizeof(mining_port_str), "%d", cfg->mining_port); | ||
| 162 | cJSON_AddItemToArray(mining_tag, cJSON_CreateString(mining_port_str)); | ||
| 163 | cJSON_AddItemToArray(mining_tag, cJSON_CreateString("GH/s")); | ||
| 164 | cJSON_AddItemToArray(mining_tag, cJSON_CreateString("sv1")); | ||
| 165 | cJSON_AddItemToArray(tags, mining_tag); | ||
| 166 | } | ||
| 167 | |||
| 153 | cJSON_AddItemToObject(root, "tags", tags); | 168 | cJSON_AddItemToObject(root, "tags", tags); |
| 154 | cJSON_AddStringToObject(root, "content", ""); | 169 | cJSON_AddStringToObject(root, "content", ""); |
| 155 | 170 | ||
| @@ -504,6 +519,169 @@ static esp_err_t api_get_mints(httpd_req_t *req) | |||
| 504 | return ESP_OK; | 519 | return ESP_OK; |
| 505 | } | 520 | } |
| 506 | 521 | ||
| 522 | static esp_err_t api_get_mining_job(httpd_req_t *req) | ||
| 523 | { | ||
| 524 | const stratum_job_t *job = stratum_proxy_get_current_job(); | ||
| 525 | if (!job || !job->valid) { | ||
| 526 | httpd_resp_set_status(req, "503 Service Unavailable"); | ||
| 527 | httpd_resp_set_type(req, "application/json"); | ||
| 528 | httpd_resp_send(req, "{\"error\":\"no job\"}", 15); | ||
| 529 | return ESP_OK; | ||
| 530 | } | ||
| 531 | |||
| 532 | cJSON *root = cJSON_CreateObject(); | ||
| 533 | cJSON_AddNumberToObject(root, "job_id", job->job_id); | ||
| 534 | |||
| 535 | char prevhash_hex[65]; | ||
| 536 | for (int i = 0; i < 32; i++) snprintf(prevhash_hex + i * 2, 3, "%02x", job->prevhash[i]); | ||
| 537 | cJSON_AddStringToObject(root, "prevhash", prevhash_hex); | ||
| 538 | |||
| 539 | char merkle_hex[65]; | ||
| 540 | for (int i = 0; i < 32; i++) snprintf(merkle_hex + i * 2, 3, "%02x", job->merkle_root[i]); | ||
| 541 | cJSON_AddStringToObject(root, "merkle_root", merkle_hex); | ||
| 542 | |||
| 543 | cJSON_AddNumberToObject(root, "version", job->version); | ||
| 544 | cJSON_AddNumberToObject(root, "nbits", job->nbits); | ||
| 545 | cJSON_AddNumberToObject(root, "ntime", job->ntime); | ||
| 546 | cJSON_AddNumberToObject(root, "hashprice", mining_get_current_hashprice()); | ||
| 547 | |||
| 548 | char *json = cJSON_PrintUnformatted(root); | ||
| 549 | httpd_resp_set_type(req, "application/json"); | ||
| 550 | httpd_resp_send(req, json, strlen(json)); | ||
| 551 | cJSON_free(json); | ||
| 552 | cJSON_Delete(root); | ||
| 553 | return ESP_OK; | ||
| 554 | } | ||
| 555 | |||
| 556 | static esp_err_t api_post_mining_share(httpd_req_t *req) | ||
| 557 | { | ||
| 558 | uint32_t client_ip = 0; | ||
| 559 | get_client_ip(req, &client_ip); | ||
| 560 | |||
| 561 | int content_len = req->content_len; | ||
| 562 | if (content_len <= 0 || content_len > 512) { | ||
| 563 | httpd_resp_set_status(req, "400 Bad Request"); | ||
| 564 | httpd_resp_set_type(req, "application/json"); | ||
| 565 | httpd_resp_send(req, "{\"error\":\"invalid body\"}", 21); | ||
| 566 | return ESP_OK; | ||
| 567 | } | ||
| 568 | |||
| 569 | char body[512]; | ||
| 570 | int total = 0; | ||
| 571 | while (total < content_len) { | ||
| 572 | int r = httpd_req_recv(req, body + total, content_len - total); | ||
| 573 | if (r <= 0) { | ||
| 574 | httpd_resp_set_status(req, "400 Bad Request"); | ||
| 575 | httpd_resp_set_type(req, "text/plain"); | ||
| 576 | httpd_resp_send(req, "bad request", 11); | ||
| 577 | return ESP_OK; | ||
| 578 | } | ||
| 579 | total += r; | ||
| 580 | } | ||
| 581 | body[total] = '\0'; | ||
| 582 | |||
| 583 | cJSON *root = cJSON_Parse(body); | ||
| 584 | if (!root) { | ||
| 585 | httpd_resp_set_status(req, "400 Bad Request"); | ||
| 586 | httpd_resp_set_type(req, "application/json"); | ||
| 587 | httpd_resp_send(req, "{\"error\":\"invalid json\"}", 21); | ||
| 588 | return ESP_OK; | ||
| 589 | } | ||
| 590 | |||
| 591 | cJSON *j_job_id = cJSON_GetObjectItem(root, "job_id"); | ||
| 592 | cJSON *j_nonce = cJSON_GetObjectItem(root, "nonce"); | ||
| 593 | cJSON *j_ntime = cJSON_GetObjectItem(root, "ntime"); | ||
| 594 | cJSON *j_version = cJSON_GetObjectItem(root, "version"); | ||
| 595 | if (!j_job_id || !j_nonce || !j_ntime || !j_version) { | ||
| 596 | cJSON_Delete(root); | ||
| 597 | httpd_resp_set_status(req, "400 Bad Request"); | ||
| 598 | httpd_resp_set_type(req, "application/json"); | ||
| 599 | httpd_resp_send(req, "{\"error\":\"missing fields\"}", 22); | ||
| 600 | return ESP_OK; | ||
| 601 | } | ||
| 602 | |||
| 603 | uint32_t job_id = (uint32_t)j_job_id->valuedouble; | ||
| 604 | uint32_t nonce = (uint32_t)j_nonce->valuedouble; | ||
| 605 | uint32_t ntime = (uint32_t)j_ntime->valuedouble; | ||
| 606 | uint32_t version = (uint32_t)j_version->valuedouble; | ||
| 607 | cJSON_Delete(root); | ||
| 608 | |||
| 609 | const stratum_job_t *job = stratum_proxy_get_current_job(); | ||
| 610 | if (!job || !job->valid || job->job_id != job_id) { | ||
| 611 | httpd_resp_set_status(req, "400 Bad Request"); | ||
| 612 | httpd_resp_set_type(req, "application/json"); | ||
| 613 | httpd_resp_send(req, "{\"error\":\"stale job\"}", 19); | ||
| 614 | return ESP_OK; | ||
| 615 | } | ||
| 616 | |||
| 617 | esp_err_t share_err = stratum_client_submit_share(job_id, nonce, ntime, version); | ||
| 618 | bool accepted = (share_err == ESP_OK); | ||
| 619 | |||
| 620 | mining_update_hashrate(client_ip, accepted); | ||
| 621 | mining_client_stats_t *stats = mining_get_or_create_client(client_ip); | ||
| 622 | |||
| 623 | if (accepted) { | ||
| 624 | const tollgate_config_t *cfg = tollgate_config_get(); | ||
| 625 | double hashprice = mining_get_current_hashprice(); | ||
| 626 | uint64_t allotment_ms = mining_shares_to_allotment_ms( | ||
| 627 | stats->hashrate_ghs, hashprice, cfg->price_per_step, cfg->step_size_ms); | ||
| 628 | |||
| 629 | session_t *session = session_find_by_ip(client_ip); | ||
| 630 | if (!session || !session->active || session->payment_method != PAYMENT_METHOD_MINING) { | ||
| 631 | session = session_create(client_ip, allotment_ms); | ||
| 632 | if (session) session->payment_method = PAYMENT_METHOD_MINING; | ||
| 633 | } else { | ||
| 634 | session_extend(session, allotment_ms); | ||
| 635 | } | ||
| 636 | } | ||
| 637 | |||
| 638 | cJSON *resp = cJSON_CreateObject(); | ||
| 639 | cJSON_AddBoolToObject(resp, "accepted", accepted); | ||
| 640 | cJSON_AddNumberToObject(resp, "hashrate_ghs", stats ? stats->hashrate_ghs : 0.0); | ||
| 641 | char *json = cJSON_PrintUnformatted(resp); | ||
| 642 | httpd_resp_set_type(req, "application/json"); | ||
| 643 | httpd_resp_send(req, json, strlen(json)); | ||
| 644 | cJSON_free(json); | ||
| 645 | cJSON_Delete(resp); | ||
| 646 | return ESP_OK; | ||
| 647 | } | ||
| 648 | |||
| 649 | static esp_err_t api_get_mining_stats(httpd_req_t *req) | ||
| 650 | { | ||
| 651 | stratum_proxy_stats_t proxy_stats; | ||
| 652 | stratum_proxy_get_stats(&proxy_stats); | ||
| 653 | |||
| 654 | const stratum_client_state_t *client_state = stratum_client_get_state(); | ||
| 655 | |||
| 656 | cJSON *root = cJSON_CreateObject(); | ||
| 657 | |||
| 658 | cJSON *proxy = cJSON_CreateObject(); | ||
| 659 | cJSON_AddNumberToObject(proxy, "hashrate_ghs", proxy_stats.hashrate_ghs); | ||
| 660 | cJSON_AddNumberToObject(proxy, "total_shares", (double)proxy_stats.total_shares); | ||
| 661 | cJSON_AddNumberToObject(proxy, "total_accepted", (double)proxy_stats.total_accepted); | ||
| 662 | cJSON_AddNumberToObject(proxy, "total_rejected", (double)proxy_stats.total_rejected); | ||
| 663 | cJSON_AddNumberToObject(proxy, "hashprice", proxy_stats.current_hashprice); | ||
| 664 | cJSON_AddNumberToObject(proxy, "active_miners", proxy_stats.active_miners); | ||
| 665 | cJSON_AddItemToObject(root, "proxy", proxy); | ||
| 666 | |||
| 667 | cJSON *upstream = cJSON_CreateObject(); | ||
| 668 | cJSON_AddBoolToObject(upstream, "connected", client_state->connected); | ||
| 669 | cJSON_AddStringToObject(upstream, "pool_host", client_state->pool_host); | ||
| 670 | cJSON_AddNumberToObject(upstream, "pool_port", client_state->pool_port); | ||
| 671 | cJSON_AddNumberToObject(upstream, "difficulty", (double)client_state->difficulty); | ||
| 672 | cJSON_AddNumberToObject(upstream, "shares_accepted", (double)client_state->shares_accepted); | ||
| 673 | cJSON_AddNumberToObject(upstream, "shares_rejected", (double)client_state->shares_rejected); | ||
| 674 | cJSON_AddItemToObject(root, "upstream", upstream); | ||
| 675 | |||
| 676 | char *json = cJSON_PrintUnformatted(root); | ||
| 677 | httpd_resp_set_type(req, "application/json"); | ||
| 678 | httpd_resp_send(req, json, strlen(json)); | ||
| 679 | cJSON_free(json); | ||
| 680 | cJSON_Delete(root); | ||
| 681 | >>>>>>> feature/mining-payment | ||
| 682 | return ESP_OK; | ||
| 683 | } | ||
| 684 | |||
| 507 | static const httpd_uri_t uri_discovery = { .uri = "/", .method = HTTP_GET, .handler = api_get_discovery }; | 685 | static const httpd_uri_t uri_discovery = { .uri = "/", .method = HTTP_GET, .handler = api_get_discovery }; |
| 508 | static const httpd_uri_t uri_payment = { .uri = "/", .method = HTTP_POST, .handler = api_post_payment }; | 686 | static const httpd_uri_t uri_payment = { .uri = "/", .method = HTTP_POST, .handler = api_post_payment }; |
| 509 | static const httpd_uri_t uri_mints = { .uri = "/mints", .method = HTTP_GET, .handler = api_get_mints }; | 687 | static const httpd_uri_t uri_mints = { .uri = "/mints", .method = HTTP_GET, .handler = api_get_mints }; |
| @@ -512,6 +690,9 @@ static const httpd_uri_t uri_whoami = { .uri = "/whoami", .method = HTTP_GET, .h | |||
| 512 | static const httpd_uri_t uri_wallet = { .uri = "/wallet", .method = HTTP_GET, .handler = api_get_wallet }; | 690 | static const httpd_uri_t uri_wallet = { .uri = "/wallet", .method = HTTP_GET, .handler = api_get_wallet }; |
| 513 | static const httpd_uri_t uri_wallet_swap = { .uri = "/wallet/swap", .method = HTTP_POST, .handler = api_post_wallet_swap }; | 691 | static const httpd_uri_t uri_wallet_swap = { .uri = "/wallet/swap", .method = HTTP_POST, .handler = api_post_wallet_swap }; |
| 514 | static const httpd_uri_t uri_wallet_send = { .uri = "/wallet/send", .method = HTTP_POST, .handler = api_post_wallet_send }; | 692 | static const httpd_uri_t uri_wallet_send = { .uri = "/wallet/send", .method = HTTP_POST, .handler = api_post_wallet_send }; |
| 693 | static const httpd_uri_t uri_mining_job = { .uri = "/mining/job", .method = HTTP_GET, .handler = api_get_mining_job }; | ||
| 694 | static const httpd_uri_t uri_mining_share = { .uri = "/mining/share", .method = HTTP_POST, .handler = api_post_mining_share }; | ||
| 695 | static const httpd_uri_t uri_mining_stats = { .uri = "/mining/stats", .method = HTTP_GET, .handler = api_get_mining_stats }; | ||
| 515 | 696 | ||
| 516 | static esp_err_t api_get_market(httpd_req_t *req) | 697 | static esp_err_t api_get_market(httpd_req_t *req) |
| 517 | { | 698 | { |
| @@ -559,7 +740,7 @@ esp_err_t tollgate_api_start(void) | |||
| 559 | httpd_config_t config = HTTPD_DEFAULT_CONFIG(); | 740 | httpd_config_t config = HTTPD_DEFAULT_CONFIG(); |
| 560 | config.server_port = 2121; | 741 | config.server_port = 2121; |
| 561 | config.ctrl_port = 32769; | 742 | config.ctrl_port = 32769; |
| 562 | config.max_uri_handlers = 12; | 743 | config.max_uri_handlers = 16; |
| 563 | config.stack_size = 16384; | 744 | config.stack_size = 16384; |
| 564 | 745 | ||
| 565 | esp_err_t ret = httpd_start(&s_api_server, &config); | 746 | esp_err_t ret = httpd_start(&s_api_server, &config); |
| @@ -579,6 +760,13 @@ esp_err_t tollgate_api_start(void) | |||
| 579 | httpd_register_uri_handler(s_api_server, &uri_wallet_send); | 760 | httpd_register_uri_handler(s_api_server, &uri_wallet_send); |
| 580 | httpd_register_uri_handler(s_api_server, &uri_market); | 761 | httpd_register_uri_handler(s_api_server, &uri_market); |
| 581 | 762 | ||
| 763 | const tollgate_config_t *cfg = tollgate_config_get(); | ||
| 764 | if (cfg->mining_enabled) { | ||
| 765 | httpd_register_uri_handler(s_api_server, &uri_mining_job); | ||
| 766 | httpd_register_uri_handler(s_api_server, &uri_mining_share); | ||
| 767 | httpd_register_uri_handler(s_api_server, &uri_mining_stats); | ||
| 768 | } | ||
| 769 | |||
| 582 | ESP_LOGI(TAG, "TollGate API started on port 2121"); | 770 | ESP_LOGI(TAG, "TollGate API started on port 2121"); |
| 583 | return ESP_OK; | 771 | return ESP_OK; |
| 584 | } | 772 | } |