v0.1.3 - Make cashu wallet initialization unconditional and add lazy wallet readiness for first-run DM tool usage

This commit is contained in:
Your Name
2026-03-19 19:26:12 -04:00
parent e81b91fa8c
commit 8790c4ae4d
4 changed files with 65 additions and 38 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.2
## Current Status — v0.1.3
**Active build — this project is barely working. Experiment at your own risk.**
> Last release update: v0.1.2 — Implement cashu_wallet_receive_token tool flow and integrate Cashu receive/swap handling
> Last release update: v0.1.3 — Make cashu wallet initialization unconditional and add lazy wallet readiness for first-run DM tool usage
- Connects to configured relays with auto-reconnect and relay state transition logging
- Publishes configured startup events per relay as each relay becomes connected
+45 -16
View File
@@ -256,17 +256,35 @@ void cashu_wallet_cleanup(void) {
memset(&g_wallet, 0, sizeof(g_wallet));
}
static int ensure_wallet_ready(void) {
if (!g_wallet.initialized || !g_wallet.cfg) return -1;
int wallet_loaded = 0;
int auto_load = 0;
pthread_mutex_lock(&g_wallet.mutex);
wallet_loaded = g_wallet.wallet_loaded;
auto_load = g_wallet.cfg->cashu_wallet.auto_load ? 1 : 0;
pthread_mutex_unlock(&g_wallet.mutex);
if (wallet_loaded) return 0;
int rc = 0;
if (auto_load) {
rc = cashu_wallet_load_from_relays();
if (rc == 0) return 0;
}
rc = cashu_wallet_create_new_from_config();
if (rc == 0) return 0;
return rc;
}
int cashu_wallet_create_new_from_config(void) {
if (!g_wallet.initialized || !g_wallet.cfg) return -1;
if (!g_wallet.cfg->cashu_wallet.enabled) return -1;
pthread_mutex_lock(&g_wallet.mutex);
if (!g_wallet.cfg->cashu_wallet.mint_urls || g_wallet.cfg->cashu_wallet.mint_count <= 0) {
pthread_mutex_unlock(&g_wallet.mutex);
return -1;
}
unsigned char priv[32] = {0};
unsigned char pub[32] = {0};
if (nostr_generate_keypair(priv, pub) != NOSTR_SUCCESS) {
@@ -279,19 +297,21 @@ int cashu_wallet_create_new_from_config(void) {
nostr_bytes_to_hex(priv, 32, wallet_data.privkey);
wallet_data.mint_count = g_wallet.cfg->cashu_wallet.mint_count;
wallet_data.mint_urls = (char**)calloc((size_t)wallet_data.mint_count, sizeof(char*));
if (!wallet_data.mint_urls) {
pthread_mutex_unlock(&g_wallet.mutex);
return -1;
}
for (int i = 0; i < wallet_data.mint_count; i++) {
wallet_data.mint_urls[i] = strdup(g_wallet.cfg->cashu_wallet.mint_urls[i]);
if (!wallet_data.mint_urls[i]) {
nostr_nip60_free_wallet_data(&wallet_data);
if (wallet_data.mint_count > 0) {
wallet_data.mint_urls = (char**)calloc((size_t)wallet_data.mint_count, sizeof(char*));
if (!wallet_data.mint_urls) {
pthread_mutex_unlock(&g_wallet.mutex);
return -1;
}
for (int i = 0; i < wallet_data.mint_count; i++) {
wallet_data.mint_urls[i] = strdup(g_wallet.cfg->cashu_wallet.mint_urls[i]);
if (!wallet_data.mint_urls[i]) {
nostr_nip60_free_wallet_data(&wallet_data);
pthread_mutex_unlock(&g_wallet.mutex);
return -1;
}
}
}
int rc = publish_wallet_event_and_store_locked(&wallet_data, NULL);
@@ -397,6 +417,7 @@ int cashu_wallet_load_from_relays(void) {
int cashu_wallet_balance_json(cJSON** out_json) {
if (!out_json || !g_wallet.initialized || !g_wallet.cfg) return -1;
(void)ensure_wallet_ready();
pthread_mutex_lock(&g_wallet.mutex);
@@ -436,6 +457,7 @@ int cashu_wallet_balance_json(cJSON** out_json) {
int cashu_wallet_info_json(const char* mint_url, cJSON** out_json) {
if (!out_json || !g_wallet.initialized || !g_wallet.cfg) return -1;
if (ensure_wallet_ready() != 0) return -1;
pthread_mutex_lock(&g_wallet.mutex);
const char* resolved = resolve_mint_url_locked(mint_url);
@@ -483,6 +505,7 @@ int cashu_wallet_info_json(const char* mint_url, cJSON** out_json) {
int cashu_wallet_mint_quote_json(const char* mint_url, uint64_t amount, const char* unit, cJSON** out_json) {
if (!out_json || amount == 0 || !g_wallet.initialized || !g_wallet.cfg) return -1;
if (ensure_wallet_ready() != 0) return -1;
pthread_mutex_lock(&g_wallet.mutex);
const char* resolved = resolve_mint_url_locked(mint_url);
@@ -516,6 +539,7 @@ int cashu_wallet_mint_quote_json(const char* mint_url, uint64_t amount, const ch
int cashu_wallet_mint_check_json(const char* mint_url, const char* quote_id, cJSON** out_json) {
if (!out_json || !quote_id || quote_id[0] == '\0' || !g_wallet.initialized || !g_wallet.cfg) return -1;
if (ensure_wallet_ready() != 0) return -1;
pthread_mutex_lock(&g_wallet.mutex);
const char* resolved = resolve_mint_url_locked(mint_url);
@@ -546,6 +570,7 @@ int cashu_wallet_mint_check_json(const char* mint_url, const char* quote_id, cJS
int cashu_wallet_mint_claim_json(const char* mint_url, const char* quote_id, uint64_t amount, cJSON** out_json) {
if (!out_json || !quote_id || quote_id[0] == '\0' || !g_wallet.initialized || !g_wallet.cfg) return -1;
if (ensure_wallet_ready() != 0) return -1;
pthread_mutex_lock(&g_wallet.mutex);
const char* resolved = resolve_mint_url_locked(mint_url);
@@ -679,6 +704,7 @@ int cashu_wallet_mint_claim_json(const char* mint_url, const char* quote_id, uin
int cashu_wallet_melt_quote_json(const char* mint_url, const char* payment_request, const char* unit, cJSON** out_json) {
if (!out_json || !payment_request || payment_request[0] == '\0' || !g_wallet.initialized || !g_wallet.cfg) return -1;
if (ensure_wallet_ready() != 0) return -1;
pthread_mutex_lock(&g_wallet.mutex);
const char* resolved = resolve_mint_url_locked(mint_url);
@@ -711,6 +737,7 @@ int cashu_wallet_melt_quote_json(const char* mint_url, const char* payment_reque
int cashu_wallet_melt_pay_json(const char* mint_url, const char* quote_id, cJSON** out_json) {
if (!out_json || !quote_id || quote_id[0] == '\0' || !g_wallet.initialized || !g_wallet.cfg) return -1;
if (ensure_wallet_ready() != 0) return -1;
pthread_mutex_lock(&g_wallet.mutex);
const char* resolved = resolve_mint_url_locked(mint_url);
@@ -891,6 +918,7 @@ int cashu_wallet_melt_pay_json(const char* mint_url, const char* quote_id, cJSON
int cashu_wallet_check_proofs_json(const char* mint_url, cJSON** out_json) {
if (!out_json || !g_wallet.initialized || !g_wallet.cfg) return -1;
if (ensure_wallet_ready() != 0) return -1;
pthread_mutex_lock(&g_wallet.mutex);
const char* resolved = resolve_mint_url_locked(mint_url);
@@ -990,6 +1018,7 @@ int cashu_wallet_check_proofs_json(const char* mint_url, cJSON** out_json) {
int cashu_wallet_receive_token_json(const char* token_string, const char* mint_url, cJSON** out_json) {
if (!out_json || !token_string || token_string[0] == '\0' || !g_wallet.initialized || !g_wallet.cfg) return -1;
if (ensure_wallet_ready() != 0) return -1;
cashu_decoded_token_t decoded;
memset(&decoded, 0, sizeof(decoded));
+16 -18
View File
@@ -1385,25 +1385,23 @@ int main(int argc, char** argv) {
DEBUG_INFO("[didactyl] startup phase: subscribe DMs end");
startup_step_ok(15, "Subscribe DMs", NULL);
if (cfg.cashu_wallet.enabled) {
startup_step_begin(16, "Initialize cashu wallet");
if (cashu_wallet_init(&cfg) != 0) {
DEBUG_WARN("[didactyl] startup phase: cashu_wallet_init failed (continuing without wallet)");
startup_step_fail(16, "Initialize cashu wallet", "cashu_wallet_init failed; wallet features disabled for this run");
startup_step_begin(16, "Initialize cashu wallet");
if (cashu_wallet_init(&cfg) != 0) {
DEBUG_WARN("[didactyl] startup phase: cashu_wallet_init failed (continuing without wallet)");
startup_step_fail(16, "Initialize cashu wallet", "cashu_wallet_init failed; wallet tools may return errors");
} else {
int wallet_rc = 0;
if (cfg.cashu_wallet.auto_load) {
wallet_rc = cashu_wallet_load_from_relays();
}
if (wallet_rc != 0 && cfg.cashu_wallet.mint_count > 0) {
wallet_rc = cashu_wallet_create_new_from_config();
}
if (wallet_rc != 0) {
DEBUG_WARN("[didactyl] startup phase: cashu wallet load/create deferred to first tool call");
startup_step_ok(16, "Initialize cashu wallet", "initialized; load/create deferred");
} else {
int wallet_rc = 0;
if (cfg.cashu_wallet.auto_load) {
wallet_rc = cashu_wallet_load_from_relays();
}
if (wallet_rc != 0 && cfg.cashu_wallet.mint_count > 0) {
wallet_rc = cashu_wallet_create_new_from_config();
}
if (wallet_rc != 0) {
DEBUG_WARN("[didactyl] startup phase: cashu wallet load/create failed (continuing)");
startup_step_fail(16, "Initialize cashu wallet", "wallet load/create failed; tools may return errors until wallet is configured");
} else {
startup_step_ok(16, "Initialize cashu wallet", cfg.cashu_wallet.auto_load ? "loaded or created" : "initialized");
}
startup_step_ok(16, "Initialize cashu wallet", cfg.cashu_wallet.auto_load ? "loaded or created" : "initialized");
}
}
+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 2
#define DIDACTYL_VERSION "v0.1.2"
#define DIDACTYL_VERSION_PATCH 3
#define DIDACTYL_VERSION "v0.1.3"
// Agent metadata
#define DIDACTYL_NAME "Didactyl"