upleb.uk

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

summaryrefslogtreecommitdiff
path: root/main/wallet_persist.c
diff options
context:
space:
mode:
Diffstat (limited to 'main/wallet_persist.c')
-rw-r--r--main/wallet_persist.c147
1 files changed, 147 insertions, 0 deletions
diff --git a/main/wallet_persist.c b/main/wallet_persist.c
new file mode 100644
index 0000000..45c932f
--- /dev/null
+++ b/main/wallet_persist.c
@@ -0,0 +1,147 @@
1#include "wallet_persist.h"
2#include "wallet.h"
3#include "config.h"
4#include "esp_log.h"
5#include "cJSON.h"
6#include <string.h>
7#include <stdio.h>
8#include <unistd.h>
9
10static const char *TAG = "wallet_persist";
11static const char *WALLET_FILE = "/spiffs/wallet.json";
12
13esp_err_t wallet_persist_save(void)
14{
15 const tollgate_config_t *cfg = tollgate_config_get();
16 wallet_t *w = wallet_get();
17
18 if (w->balance < cfg->persist_threshold_sats) {
19 if (w->proof_count == 0) {
20 unlink(WALLET_FILE);
21 ESP_LOGI(TAG, "Wallet empty, removed persist file");
22 }
23 return ESP_OK;
24 }
25
26 cJSON *root = cJSON_CreateObject();
27 cJSON_AddNumberToObject(root, "balance", (double)w->balance);
28
29 cJSON *proofs = cJSON_CreateArray();
30 for (int i = 0; i < w->proof_count; i++) {
31 cJSON *p = cJSON_CreateObject();
32 cJSON_AddNumberToObject(p, "amount", (double)w->proofs[i].amount);
33 cJSON_AddStringToObject(p, "id", w->proofs[i].id);
34 cJSON_AddStringToObject(p, "secret", w->proofs[i].secret);
35 cJSON_AddStringToObject(p, "C", w->proofs[i].c);
36 cJSON_AddItemToArray(proofs, p);
37 }
38 cJSON_AddItemToObject(root, "proofs", proofs);
39
40 cJSON *keysets = cJSON_CreateArray();
41 for (int i = 0; i < w->keyset_count; i++) {
42 cJSON *ks = cJSON_CreateObject();
43 cJSON_AddStringToObject(ks, "id", w->keysets[i].id);
44 cJSON_AddItemToArray(keysets, ks);
45 }
46 cJSON_AddItemToObject(root, "keysets", keysets);
47
48 char *json_str = cJSON_PrintUnformatted(root);
49 cJSON_Delete(root);
50
51 FILE *f = fopen(WALLET_FILE, "w");
52 if (!f) {
53 ESP_LOGE(TAG, "Failed to open %s for writing", WALLET_FILE);
54 cJSON_free(json_str);
55 return ESP_FAIL;
56 }
57
58 size_t written = fwrite(json_str, 1, strlen(json_str), f);
59 fclose(f);
60 cJSON_free(json_str);
61
62 ESP_LOGI(TAG, "Wallet persisted: %d proofs, balance=%llu (%zu bytes)",
63 w->proof_count, (unsigned long long)w->balance, written);
64 return ESP_OK;
65}
66
67esp_err_t wallet_persist_load(void)
68{
69 wallet_t *w = wallet_get();
70
71 FILE *f = fopen(WALLET_FILE, "r");
72 if (!f) {
73 ESP_LOGI(TAG, "No persisted wallet found, starting fresh");
74 return ESP_OK;
75 }
76
77 fseek(f, 0, SEEK_END);
78 long fsize = ftell(f);
79 fseek(f, 0, SEEK_SET);
80
81 if (fsize <= 0 || fsize > 65536) {
82 fclose(f);
83 ESP_LOGW(TAG, "Wallet file size invalid: %ld", fsize);
84 return ESP_FAIL;
85 }
86
87 char *buf = malloc(fsize + 1);
88 if (!buf) {
89 fclose(f);
90 return ESP_ERR_NO_MEM;
91 }
92
93 fread(buf, 1, fsize, f);
94 buf[fsize] = '\0';
95 fclose(f);
96
97 cJSON *root = cJSON_Parse(buf);
98 free(buf);
99 if (!root) {
100 ESP_LOGE(TAG, "Failed to parse wallet.json");
101 return ESP_FAIL;
102 }
103
104 cJSON *balance_j = cJSON_GetObjectItemCaseSensitive(root, "balance");
105 if (balance_j && cJSON_IsNumber(balance_j)) {
106 w->balance = (uint64_t)balance_j->valuedouble;
107 }
108
109 cJSON *proofs = cJSON_GetObjectItemCaseSensitive(root, "proofs");
110 if (proofs && cJSON_IsArray(proofs)) {
111 int count = cJSON_GetArraySize(proofs);
112 if (count > WALLET_MAX_PROOFS) count = WALLET_MAX_PROOFS;
113 for (int i = 0; i < count; i++) {
114 cJSON *p = cJSON_GetArrayItem(proofs, i);
115 cJSON *amt = cJSON_GetObjectItemCaseSensitive(p, "amount");
116 cJSON *id = cJSON_GetObjectItemCaseSensitive(p, "id");
117 cJSON *secret = cJSON_GetObjectItemCaseSensitive(p, "secret");
118 cJSON *c = cJSON_GetObjectItemCaseSensitive(p, "C");
119 if (amt) w->proofs[i].amount = (uint64_t)amt->valuedouble;
120 if (id && cJSON_IsString(id))
121 strncpy(w->proofs[i].id, id->valuestring, WALLET_KEYSET_ID_LEN - 1);
122 if (secret && cJSON_IsString(secret))
123 strncpy(w->proofs[i].secret, secret->valuestring, WALLET_SECRET_LEN - 1);
124 if (c && cJSON_IsString(c))
125 strncpy(w->proofs[i].c, c->valuestring, WALLET_SIG_LEN - 1);
126 w->proof_count++;
127 }
128 }
129
130 cJSON *keysets = cJSON_GetObjectItemCaseSensitive(root, "keysets");
131 if (keysets && cJSON_IsArray(keysets)) {
132 int count = cJSON_GetArraySize(keysets);
133 if (count > WALLET_MAX_KEYSETS) count = WALLET_MAX_KEYSETS;
134 for (int i = 0; i < count; i++) {
135 cJSON *ks = cJSON_GetArrayItem(keysets, i);
136 cJSON *id = cJSON_GetObjectItemCaseSensitive(ks, "id");
137 if (id && cJSON_IsString(id))
138 strncpy(w->keysets[i].id, id->valuestring, WALLET_KEYSET_ID_LEN - 1);
139 w->keyset_count++;
140 }
141 }
142
143 cJSON_Delete(root);
144 ESP_LOGI(TAG, "Wallet loaded: %d proofs, %d keysets, balance=%llu",
145 w->proof_count, w->keyset_count, (unsigned long long)w->balance);
146 return ESP_OK;
147}