353 if (!str || !*str || !placeholder || !value)
return;
355 char* current = *str;
356 size_t placeholder_len = strlen(placeholder);
357 size_t value_len = strlen(value);
359 size_t new_len = strlen(current) + 1;
360 char* result = malloc(new_len);
361 strcpy(result, current);
364 while ((pos = strstr(result, placeholder)) != NULL) {
365 size_t prefix_len = pos - result;
366 size_t suffix_len = strlen(pos + placeholder_len);
367 new_len = prefix_len + value_len + suffix_len + 1;
369 char* new_result = realloc(result, new_len);
375 pos = result + prefix_len;
377 memmove(pos + value_len, pos + placeholder_len, suffix_len + 1);
378 memcpy(pos, value, value_len);
470 printf(CONSOLE_ESC(2J));
471 printf(CONSOLE_ESC(1;1H)
"Finding a node from master server...");
478 char* json_copy = NULL;
483 curl_global_init(CURL_GLOBAL_DEFAULT);
484 curl = curl_easy_init();
490 curl_easy_setopt(curl, CURLOPT_URL,
GET_POOL);
491 curl_easy_setopt(curl, CURLOPT_USERAGENT,
"libnx curl nxducominer");
493 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (
void*)&chunk);
495 res = curl_easy_perform(curl);
496 if (
res != CURLE_OK) {
500 curl_easy_cleanup(curl);
503 curl_easy_cleanup(curl);
509 jsmntok_t tokens[13];
512 int ret = jsmn_parse(&parser, json_copy, strlen(json_copy), tokens, 13);
514 if(json_copy) free(json_copy);
516 curl_easy_cleanup(curl);
517 cleanup(
"failed to parse JSON");
520 for (
int i = 1; i < ret; i++) {
521 if (tokens[i].type == JSMN_STRING) {
522 if (strncmp(json_copy + tokens[i].start,
"ip", tokens[i].end - tokens[i].start) == 0) {
525 int length = tokens[i + 1].end - tokens[i + 1].start;
526 strncpy(ip_str, json_copy + tokens[i + 1].start, length);
528 strncpy(*ip, json_copy + tokens[i + 1].start, length);
529 (*ip)[length] =
'\0';
533 else if (strncmp(json_copy + tokens[i].start,
"port", tokens[i].end - tokens[i].start) == 0) {
536 int length = tokens[i + 1].end - tokens[i + 1].start;
537 strncpy(port_str, json_copy + tokens[i + 1].start, length);
538 port_str[length] =
'\0';
539 *port = atoi(port_str);
543 else if (strncmp(json_copy + tokens[i].start,
"name", tokens[i].end - tokens[i].start) == 0) {
546 int length = tokens[i + 1].end - tokens[i + 1].start;
547 strncpy(name_str, json_copy + tokens[i + 1].start, length);
549 strncpy(*name, json_copy + tokens[i + 1].start, length);
550 (*name)[length] =
'\0';
562 if(json_copy) free(json_copy);
571 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
572 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
579 pthread_testcancel();
580 int s = socket(AF_INET, SOCK_STREAM, 0);
583 snprintf(errbuf,
sizeof(errbuf),
"Socket creation failed: %s", strerror(errno));
593 struct hostent* server = gethostbyname(
mc.
node);
601 struct sockaddr_in serv_addr;
602 memset(&serv_addr, 0,
sizeof(serv_addr));
603 serv_addr.sin_family = AF_INET;
604 serv_addr.sin_port = htons(
mc.
port);
605 memcpy(&serv_addr.sin_addr.s_addr, server->h_addr, server->h_length);
607 if (connect(td->
socket_fd, (
struct sockaddr*)&serv_addr,
sizeof(serv_addr)) < 0) {
613 read(td->
socket_fd, recv_buf,
sizeof(4));
617 pthread_testcancel();
620 char job_request[256];
624 snprintf(job_request,
sizeof(job_request),
629 snprintf(job_request,
sizeof(job_request),
635 if (write_job <= 0)
goto reconnect;
638 memset(recv_buf, 0, 1024);
639 int read_job = read(td->
socket_fd, recv_buf, 1024 - 1);
640 if (read_job <= 0)
goto reconnect;
645 char* token = strtok_r(recv_buf,
",", &saveptr);
646 for (
int i = 0; i < 3 && token; i++) {
647 job_parts[i] = token;
648 token = strtok_r(NULL,
",", &saveptr);
651 int difficulty = atoi(job_parts[2]);
653 char expected_hash[41];
654 strcpy(base_str, job_parts[0]);
655 strcpy(expected_hash, job_parts[1]);
658 Sha1Context base_ctx;
659 Sha1Context temp_ctx;
660 sha1ContextCreate(&base_ctx);
661 sha1ContextUpdate(&base_ctx, (
const unsigned char*)base_str, strlen(base_str));
663 time_t start_time = time(NULL);
664 char result_hash[41];
667 for (nonce = 0; nonce <= (100 * difficulty + 1); nonce++) {
668 pthread_testcancel();
670 unsigned char hash[20];
674 int len = sprintf(result_str,
"%d", nonce);
675 sha1ContextUpdate(&temp_ctx, (
const unsigned char*)result_str, len);
676 sha1ContextGetHash(&temp_ctx, hash);
679 for (
int i = 0; i < 20; i++) {
680 snprintf(result_hash + (i * 2), 3,
"%02x", hash[i]);
683 if (memcmp(result_hash, expected_hash, 20) == 0) {
684 double elapsed = difftime(time(NULL), start_time);
685 double hashrate = nonce / (elapsed > 0 ? elapsed : 1);
688 char submit_buf[128];
689 int len = snprintf(submit_buf,
sizeof(submit_buf),
"%d,%.2f,%s,%s,,%i",
692 if (write_result <= 0)
goto reconnect;
694 int read_result = read(td->
socket_fd, recv_buf, 1024 - 1);
695 if(read_result <= 0)
goto reconnect;
697 if (strncmp(recv_buf,
"GOOD", 4) == 0) {
700 else if (strncmp(recv_buf,
"BLOCK", 5) == 0) {
728 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
729 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
731 struct sockaddr_in address = { 0 };
732 int addrlen =
sizeof(address);
735 cleanup(
"web dashboard socket failed.");
741 address.sin_family = AF_INET;
742 address.sin_addr.s_addr = INADDR_ANY;
743 address.sin_port = htons(8080);
750 cleanup(
"web dashboard socket failed to listen");
754 pthread_testcancel();
758 if (errno == EWOULDBLOCK || errno == EAGAIN) {
759 svcSleepThread(1000000);
768 if (!
template)
return NULL;
770 char threads_buf[12];
771 char hashrate_buf[64];
774 char sensors_buf[48];
781 snprintf(threads_buf,
sizeof(threads_buf),
"%i",
mc.
threads);
796 svcSleepThread(1000000);
810 padConfigureInput(1, HidNpadStyleSet_NpadStandard);
812 padInitializeDefault(&pad);
815 appletSetAutoSleepDisabled(
true);
816 appletSetTvPowerStateMatchingMode(AppletTvPowerStateMatchingMode_Unknown1);
818 socketInitializeDefault();
835 appletSetCpuBoostMode(ApmCpuBoostMode_FastLoad);
839 Result tcrc = tcInitialize();
840 if (R_FAILED(tcrc)) {
841 cleanup(
"failed to initialize tc");
845 Result psmrc = psmInitialize();
846 if (R_FAILED(psmrc)) {
847 cleanup(
"failed to initialize psm");
857 cleanup(
"memory allocation failed");
864 cleanup(
"failed to start web dashboard thread");
874 cleanup(
"failed to start mining thread");
880 time_t last_draw = 0;
881 while (appletMainLoop()) {
885 u64 kDown = padGetButtonsDown(&pad);
887 if (kDown & HidNpadButton_Plus) {
895 if (difftime(current_time, last_draw) >= 2) {
899 PsmChargerType chargeType;
900 psmrc = psmGetChargerType(&chargeType);
904 psmrc = psmGetBatteryChargePercentage(&
res.
charge);
925 printf(CONSOLE_ESC(2J));
928 printf(CONSOLE_ESC(1;1H)
"Node: %s",
mc.
name);
931 printf(CONSOLE_ESC(1;1H)
"Node: %s:%i",
mc.
node,
mc.
port);
935 printf(CONSOLE_ESC(2;1H)
"Current Time: %s", timebuf);
953 printf(CONSOLE_ESC(7;1H)
"Rig ID: %s",
mc.
rig_id);
958 printf(CONSOLE_ESC(12;1H)
"Shares");
963 printf(CONSOLE_ESC(17;1H)
LIGHT_GREY "|_ " RESET "Accepted %i/%i Rejected (%d%% Accepted)",
968 printf(CONSOLE_ESC(20;1H)
"Threads (%i)",
mc.
threads);
988 printf(CONSOLE_ESC(1;53H)
" ######## ");
989 printf(CONSOLE_ESC(2;53H)
" ############### ");
990 printf(CONSOLE_ESC(3;53H)
" ################### ");
991 printf(CONSOLE_ESC(4;53H)
" #### ######## ");
992 printf(CONSOLE_ESC(5;53H)
" ############ ####### ");
993 printf(CONSOLE_ESC(6;53H)
" ###### ### ###### ");
994 printf(CONSOLE_ESC(7;53H)
" ########### ## ###### ");
995 printf(CONSOLE_ESC(8;53H)
" ########### ## ###### ");
996 printf(CONSOLE_ESC(9;53H)
" ###### ### ###### ");
997 printf(CONSOLE_ESC(10;53H)
" ############ ####### ");
998 printf(CONSOLE_ESC(11;53H)
" #### ######## ");
999 printf(CONSOLE_ESC(12;53H)
" ################### ");
1000 printf(CONSOLE_ESC(13;53H)
" ############### ");
1001 printf(CONSOLE_ESC(14;53H)
" ####### ");
1003 printf(CONSOLE_ESC(15;52H)
"github.com/tbwcjw/nxducominer");
1012 last_draw = current_time;
1014 consoleUpdate(NULL);
1016 svcSleepThread(1000000);
static size_t write_memory_callback(void *contents, size_t size, size_t nmemb, void *userp)
CURL write callback for memory buffer.
void replace_placeholder(char **str, const char *placeholder, const char *value)
replace all occurrences of placeholder in string (html) with value
#define DUCO_ORANGE
duinocoin branding https://github.com/revoxhere/duino-coin/tree/useful-tools#branding
#define CONSOLE_ESC_NSTR(fmt)
console escape helper, does not stringify.
void * web_dashboard(void *arg)
web dashboard server thread
void * do_mining_work(void *arg)
mining thread worker function
void get_node(char **ip, int *port, char **name)
get mining node from DuinoCoin server
void cleanup(char *msg)
clean up resources and exit
void parse_config_file(MiningConfig *config)
parse configuration file