upleb.uk

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

summaryrefslogtreecommitdiff
path: root/main/mining_payment.c
diff options
context:
space:
mode:
Diffstat (limited to 'main/mining_payment.c')
-rw-r--r--main/mining_payment.c169
1 files changed, 169 insertions, 0 deletions
diff --git a/main/mining_payment.c b/main/mining_payment.c
new file mode 100644
index 0000000..8c5e4d5
--- /dev/null
+++ b/main/mining_payment.c
@@ -0,0 +1,169 @@
1#include "mining_payment.h"
2#include "config.h"
3#include "esp_log.h"
4#include "freertos/FreeRTOS.h"
5#include "freertos/task.h"
6#include <string.h>
7#include <math.h>
8
9static const char *TAG = "mining_payment";
10
11static mining_client_stats_t s_clients[MINING_MAX_CLIENTS];
12static int s_client_count = 0;
13static double s_current_hashprice = 0.0;
14static uint32_t s_current_nbits = 0;
15static uint64_t s_current_difficulty = 1;
16
17static int64_t get_time_ms(void)
18{
19 return (int64_t)xTaskGetTickCount() * portTICK_PERIOD_MS;
20}
21
22uint64_t mining_nbits_to_difficulty(uint32_t nbits)
23{
24 if (nbits == 0) return UINT64_MAX;
25
26 uint32_t exponent = (nbits >> 24) & 0xFF;
27 uint32_t mantissa = nbits & 0x007FFFFF;
28
29 if (exponent <= 3) {
30 mantissa >>= (8 * (3 - exponent));
31 if (mantissa == 0) return UINT64_MAX;
32 return 0x00000000FFFF0000ULL / mantissa;
33 }
34
35 uint64_t target = (uint64_t)mantissa << (8 * (exponent - 3));
36 if (target == 0) return UINT64_MAX;
37
38 uint64_t pdiff = 0x00000000FFFF0000ULL;
39 uint64_t diff = pdiff / (target >> (exponent > 7 ? 0 : 0));
40 if (diff == 0) diff = 1;
41 return diff;
42}
43
44double mining_calculate_hashprice(uint32_t nbits)
45{
46 uint64_t diff = mining_nbits_to_difficulty(nbits);
47 if (diff == 0 || diff == UINT64_MAX) return 0.0;
48
49 double network_hashrate_th = (double)diff * 4294967296.0 / 1e12;
50 double daily_sats = (double)MINING_BLOCK_SUBSIDY_SATS * (double)MINING_BLOCKS_PER_DAY;
51 double sats_per_th_day = daily_sats / network_hashrate_th;
52 return sats_per_th_day / 1000.0;
53}
54
55double mining_calculate_hashprice_override(uint64_t sats_per_ghs_day)
56{
57 return (double)sats_per_ghs_day;
58}
59
60esp_err_t mining_validate_share(const uint8_t *header80, uint32_t nonce, const uint8_t *target, int target_len)
61{
62 (void)header80;
63 (void)nonce;
64 (void)target;
65 (void)target_len;
66 return ESP_OK;
67}
68
69uint64_t mining_shares_to_allotment_ms(double hashrate_ghs, double hashprice_sats_per_ghs_s,
70 int price_per_step, int step_size_ms)
71{
72 if (hashrate_ghs <= 0.0 || hashprice_sats_per_ghs_s <= 0.0 || price_per_step <= 0) return 0;
73
74 double sats_per_ms = hashrate_ghs * hashprice_sats_per_ghs_s / 86400000.0;
75 double steps_earned = sats_per_ms * (double)step_size_ms / (double)price_per_step;
76 uint64_t allotment = (uint64_t)(steps_earned * (double)step_size_ms);
77 return allotment > 0 ? allotment : 1;
78}
79
80uint64_t mining_shares_to_allotment_bytes(double hashrate_ghs, double hashprice_sats_per_ghs_s,
81 int price_per_step, int step_size_bytes)
82{
83 if (hashrate_ghs <= 0.0 || hashprice_sats_per_ghs_s <= 0.0 || price_per_step <= 0) return 0;
84
85 double sats_per_ms = hashrate_ghs * hashprice_sats_per_ghs_s / 86400000.0;
86 double steps_earned = sats_per_ms * 1000.0 / (double)price_per_step;
87 uint64_t allotment = (uint64_t)(steps_earned * (double)step_size_bytes);
88 return allotment > 0 ? allotment : 1;
89}
90
91mining_client_stats_t *mining_get_or_create_client(uint32_t client_ip)
92{
93 for (int i = 0; i < s_client_count; i++) {
94 if (s_clients[i].ip == client_ip) return &s_clients[i];
95 }
96
97 if (s_client_count >= MINING_MAX_CLIENTS) {
98 for (int i = 0; i < MINING_MAX_CLIENTS; i++) {
99 int64_t age = get_time_ms() - s_clients[i].last_share_time_ms;
100 if (age > MINING_SHARE_WINDOW_S * 2000) {
101 memset(&s_clients[i], 0, sizeof(mining_client_stats_t));
102 s_clients[i].ip = client_ip;
103 s_clients[i].first_share_time_ms = get_time_ms();
104 return &s_clients[i];
105 }
106 }
107 return NULL;
108 }
109
110 mining_client_stats_t *c = &s_clients[s_client_count];
111 memset(c, 0, sizeof(mining_client_stats_t));
112 c->ip = client_ip;
113 c->first_share_time_ms = get_time_ms();
114 s_client_count++;
115 return c;
116}
117
118void mining_update_hashrate(uint32_t client_ip, bool accepted)
119{
120 mining_client_stats_t *stats = mining_get_or_create_client(client_ip);
121 if (!stats) return;
122
123 if (accepted) {
124 stats->shares_accepted++;
125 } else {
126 stats->shares_rejected++;
127 }
128 stats->last_share_time_ms = get_time_ms();
129
130 int64_t window_ms = stats->last_share_time_ms - stats->first_share_time_ms;
131 if (window_ms < 1000) window_ms = 1000;
132
133 double window_s = (double)window_ms / 1000.0;
134 double shares_per_s = (double)stats->shares_accepted / window_s;
135 double diff = (s_current_difficulty > 0) ? (double)s_current_difficulty : 1.0;
136 stats->hashrate_ghs = shares_per_s * diff * 4294967296.0 / 1e9;
137}
138
139const mining_client_stats_t *mining_get_client_stats(uint32_t client_ip)
140{
141 for (int i = 0; i < s_client_count; i++) {
142 if (s_clients[i].ip == client_ip) return &s_clients[i];
143 }
144 return NULL;
145}
146
147double mining_get_current_hashprice(void)
148{
149 return s_current_hashprice;
150}
151
152void mining_set_current_nbits(uint32_t nbits)
153{
154 s_current_nbits = nbits;
155 s_current_difficulty = mining_nbits_to_difficulty(nbits);
156 s_current_hashprice = mining_calculate_hashprice(nbits);
157 ESP_LOGI(TAG, "nbits updated: 0x%08lx, diff=%llu, hashprice=%.6f sat/GH/s/day",
158 (unsigned long)nbits, (unsigned long long)s_current_difficulty, s_current_hashprice);
159}
160
161void mining_payment_init(void)
162{
163 memset(s_clients, 0, sizeof(s_clients));
164 s_client_count = 0;
165 s_current_hashprice = 0.0;
166 s_current_nbits = 0;
167 s_current_difficulty = 1;
168 ESP_LOGI(TAG, "Mining payment module initialized");
169}