v0.1.18 - Auto-clean spent Cashu proofs in check_proofs, publish deletions, and add spent token events to kind-10000 ignore list

This commit is contained in:
Your Name
2026-03-21 09:00:51 -04:00
parent 6baa02cfe1
commit d51f8e1a7c
3 changed files with 83 additions and 6 deletions
+2 -2
View File
@@ -55,11 +55,11 @@ Skills compose by adoption-list order (`10123`) and trigger tags carry runtime e
Didactyl will support local inference, which is very privacy preserving. Remote inference does however have it's advantages, and in those cases Didactyl supports using Bitcoin Lightning and eCash inference providers.
## Current Status — v0.1.17
## Current Status — v0.1.18
**Active build — this project is barely working. Experiment at your own risk.**
> Last release update: v0.1.17Fix Cashu v4 token field encoding (i bytes, s text, c bytes) and rebuild nostr_core_lib integration
> Last release update: v0.1.18Auto-clean spent Cashu proofs in check_proofs, publish deletions, and add spent token events to kind-10000 ignore list
- Connects to configured relays with auto-reconnect and relay state transition logging
- Publishes configured startup events per relay as each relay becomes connected
+79 -2
View File
@@ -14,6 +14,7 @@
#include "debug.h"
#include "nostr_handler.h"
#include "nostr_block_list.h"
#include "../../nostr_core_lib/nostr_core/nostr_core.h"
#define CASHU_WALLET_QUERY_TIMEOUT_MS 6000
@@ -1354,6 +1355,7 @@ int cashu_wallet_check_proofs(const char* mint_url, cJSON** out_json) {
if (!resolved) return -1;
pthread_mutex_lock(&g_wallet.mutex);
int token_count_snapshot = g_wallet.token_count;
int target_proofs = 0;
for (int i = 0; i < g_wallet.token_count; i++) {
if (!g_wallet.tokens[i].token.mint_url || strcmp(g_wallet.tokens[i].token.mint_url, resolved) != 0) continue;
@@ -1371,6 +1373,9 @@ int cashu_wallet_check_proofs(const char* mint_url, cJSON** out_json) {
cJSON_AddNumberToObject(root, "proof_count", 0);
cJSON_AddNumberToObject(root, "spent_count", 0);
cJSON_AddNumberToObject(root, "unknown_count", 0);
cJSON_AddNumberToObject(root, "deleted_token_events", 0);
cJSON_AddNumberToObject(root, "ignored_token_events", 0);
cJSON_AddNumberToObject(root, "remaining_balance", 0);
*out_json = root;
pthread_mutex_unlock(&g_wallet.mutex);
return 0;
@@ -1378,9 +1383,15 @@ int cashu_wallet_check_proofs(const char* mint_url, cJSON** out_json) {
const char** ys = (const char**)calloc((size_t)target_proofs, sizeof(char*));
char (*y_hexes)[67] = (char(*)[67])calloc((size_t)target_proofs, sizeof(*y_hexes));
if (!ys || !y_hexes) {
int* proof_to_token_idx = (int*)calloc((size_t)target_proofs, sizeof(int));
int* token_total_counts = (int*)calloc((size_t)token_count_snapshot, sizeof(int));
int* token_spent_counts = (int*)calloc((size_t)token_count_snapshot, sizeof(int));
if (!ys || !y_hexes || !proof_to_token_idx || !token_total_counts || !token_spent_counts) {
free(ys);
free(y_hexes);
free(proof_to_token_idx);
free(token_total_counts);
free(token_spent_counts);
pthread_mutex_unlock(&g_wallet.mutex);
return -1;
}
@@ -1393,10 +1404,15 @@ int cashu_wallet_check_proofs(const char* mint_url, cJSON** out_json) {
if (!secret || cashu_secret_to_y_hex(secret, y_hexes[y_idx]) != 0) {
free(ys);
free(y_hexes);
free(proof_to_token_idx);
free(token_total_counts);
free(token_spent_counts);
pthread_mutex_unlock(&g_wallet.mutex);
return -1;
}
ys[y_idx] = y_hexes[y_idx];
proof_to_token_idx[y_idx] = i;
token_total_counts[i]++;
y_idx++;
}
}
@@ -1406,6 +1422,9 @@ int cashu_wallet_check_proofs(const char* mint_url, cJSON** out_json) {
if (cashu_build_checkstate_request(ys, y_idx, &request_body) != 0 || !request_body) {
free(ys);
free(y_hexes);
free(proof_to_token_idx);
free(token_total_counts);
free(token_spent_counts);
cJSON_Delete(request_body);
return -1;
}
@@ -1416,6 +1435,9 @@ int cashu_wallet_check_proofs(const char* mint_url, cJSON** out_json) {
free(ys);
free(y_hexes);
if (rc != 0 || !response_body) {
free(proof_to_token_idx);
free(token_total_counts);
free(token_spent_counts);
cJSON_Delete(response_body);
return -1;
}
@@ -1425,22 +1447,71 @@ int cashu_wallet_check_proofs(const char* mint_url, cJSON** out_json) {
rc = cashu_parse_checkstate_response(response_body, &parsed);
cJSON_Delete(response_body);
if (rc != 0) {
free(proof_to_token_idx);
free(token_total_counts);
free(token_spent_counts);
return -1;
}
int spent_count = 0;
int unknown_count = 0;
for (int i = 0; i < parsed.state_count; i++) {
int state_n = parsed.state_count < y_idx ? parsed.state_count : y_idx;
for (int i = 0; i < state_n; i++) {
if (strcasecmp(parsed.states[i].state, "SPENT") == 0) {
spent_count++;
int token_idx = proof_to_token_idx[i];
if (token_idx >= 0 && token_idx < token_count_snapshot) {
token_spent_counts[token_idx]++;
}
} else if (strcasecmp(parsed.states[i].state, "UNSPENT") != 0) {
unknown_count++;
}
}
int deleted_token_events = 0;
int ignored_token_events = 0;
uint64_t remaining = 0;
pthread_mutex_lock(&g_wallet.mutex);
for (int i = 0; i < g_wallet.token_count && i < token_count_snapshot; i++) {
if (!g_wallet.tokens[i].token.mint_url || strcmp(g_wallet.tokens[i].token.mint_url, resolved) != 0) continue;
if (token_total_counts[i] <= 0) continue;
if (token_spent_counts[i] < token_total_counts[i]) continue;
if (g_wallet.tokens[i].event_id[0] != '\0') {
if (publish_token_deletion_event(g_wallet.tokens[i].event_id) == 0) {
deleted_token_events++;
}
if (nostr_block_list_add("e", g_wallet.tokens[i].event_id, 0) == 0) {
ignored_token_events++;
}
}
nostr_nip60_free_token_data(&g_wallet.tokens[i].token);
memset(&g_wallet.tokens[i], 0, sizeof(g_wallet.tokens[i]));
}
int compact = 0;
for (int i = 0; i < g_wallet.token_count; i++) {
if (g_wallet.tokens[i].event_id[0] == '\0' && !g_wallet.tokens[i].token.mint_url) continue;
if (compact != i) g_wallet.tokens[compact] = g_wallet.tokens[i];
compact++;
}
g_wallet.token_count = compact;
if (g_wallet.token_count == 0) {
free(g_wallet.tokens);
g_wallet.tokens = NULL;
}
token_proofs_total_for_mint_locked(resolved, &remaining);
pthread_mutex_unlock(&g_wallet.mutex);
cJSON* root = cJSON_CreateObject();
if (!root) {
cashu_mint_free_checkstate_response(&parsed);
free(proof_to_token_idx);
free(token_total_counts);
free(token_spent_counts);
return -1;
}
@@ -1449,10 +1520,16 @@ int cashu_wallet_check_proofs(const char* mint_url, cJSON** out_json) {
cJSON_AddNumberToObject(root, "proof_count", y_idx);
cJSON_AddNumberToObject(root, "spent_count", spent_count);
cJSON_AddNumberToObject(root, "unknown_count", unknown_count);
cJSON_AddNumberToObject(root, "deleted_token_events", deleted_token_events);
cJSON_AddNumberToObject(root, "ignored_token_events", ignored_token_events);
cJSON_AddNumberToObject(root, "remaining_balance", (double)remaining);
*out_json = root;
cashu_mint_free_checkstate_response(&parsed);
free(proof_to_token_idx);
free(token_total_counts);
free(token_spent_counts);
return 0;
}
+2 -2
View File
@@ -12,8 +12,8 @@
// Using DIDACTYL_ prefix to avoid conflicts with nostr_core_lib VERSION macros
#define DIDACTYL_VERSION_MAJOR 0
#define DIDACTYL_VERSION_MINOR 1
#define DIDACTYL_VERSION_PATCH 17
#define DIDACTYL_VERSION "v0.1.17"
#define DIDACTYL_VERSION_PATCH 18
#define DIDACTYL_VERSION "v0.1.18"
// Agent metadata
#define DIDACTYL_NAME "Didactyl"