From 71a72dc7ed70e18eeb4f8f9d02dcf9a45fee2a46 Mon Sep 17 00:00:00 2001 From: Laan Tungir Date: Thu, 6 Aug 2026 10:17:17 -0400 Subject: [PATCH] Add nostr_signer_nsigner_from_client, nostr_signer_last_error, make from_transport public - nostr_signer_nsigner_from_transport: made public (was static) so callers who open their own transport can create a signer from it. - nostr_signer_nsigner_from_client: new constructor that creates a signer from an already-created low-level nsigner_client_t. Lets callers use both the high-level typed wrappers and the low-level nsigner_client_call for raw passthrough on the same connection. - nostr_signer_last_error: returns the raw n_signer RPC error message (e.g. 'path_not_allowed', 'unknown_role') from the last failed call. Delegates to nsigner_client_last_error for the remote backend. - Forward declarations for nsigner_transport_t and nsigner_client_t added to nostr_signer.h so the new constructors can be declared without pulling in the transport/client headers. --- nostr_core/nostr_signer.c | 36 ++++++++++++++++++++++++++++-------- nostr_core/nostr_signer.h | 19 +++++++++++++++++++ 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/nostr_core/nostr_signer.c b/nostr_core/nostr_signer.c index d1438dde..0c320192 100644 --- a/nostr_core/nostr_signer.c +++ b/nostr_core/nostr_signer.c @@ -1442,22 +1442,15 @@ int nostr_signer_mine_event(nostr_signer_t* signer, } #if defined(NOSTR_ENABLE_NSIGNER_CLIENT) -static nostr_signer_t* nostr_signer_nsigner_from_transport(nsigner_transport_t* transport, const char* role) { - nsigner_client_t* client = NULL; +nostr_signer_t* nostr_signer_nsigner_from_client(nsigner_client_t* client, const char* role) { nostr_signer_t* signer = NULL; - if (transport == NULL) { - return NULL; - } - - client = nsigner_client_new(transport); if (client == NULL) { return NULL; } signer = (nostr_signer_t*)calloc(1, sizeof(*signer)); if (signer == NULL) { - nsigner_client_free(client); return NULL; } @@ -1471,6 +1464,21 @@ static nostr_signer_t* nostr_signer_nsigner_from_transport(nsigner_transport_t* return signer; } +nostr_signer_t* nostr_signer_nsigner_from_transport(nsigner_transport_t* transport, const char* role) { + nsigner_client_t* client = NULL; + + if (transport == NULL) { + return NULL; + } + + client = nsigner_client_new(transport); + if (client == NULL) { + return NULL; + } + + return nostr_signer_nsigner_from_client(client, role); +} + nostr_signer_t* nostr_signer_nsigner_unix(const char* socket_name, const char* role, int timeout_ms) { nsigner_transport_t* transport = NULL; char discovered[8][64]; @@ -1523,6 +1531,18 @@ int nostr_signer_nsigner_set_auth(nostr_signer_t* signer, return nsigner_client_set_auth(signer->u.remote.client, auth_privkey, label); } +const char* nostr_signer_last_error(const nostr_signer_t* signer) { + if (signer == NULL) { + return "null signer"; + } +#if defined(NOSTR_ENABLE_NSIGNER_CLIENT) + if (signer->backend == NOSTR_SIGNER_BACKEND_NSIGNER_REMOTE && signer->u.remote.client != NULL) { + return nsigner_client_last_error(signer->u.remote.client); + } +#endif + return "no error details available"; +} + nostr_signer_t* nostr_signer_nsigner_qrexec(const char* target_qube, const char* service_name, const char* role, int timeout_ms) { nsigner_transport_t* transport; diff --git a/nostr_core/nostr_signer.h b/nostr_core/nostr_signer.h index 1b0e8050..d575fab3 100644 --- a/nostr_core/nostr_signer.h +++ b/nostr_core/nostr_signer.h @@ -15,6 +15,12 @@ typedef struct nostr_signer nostr_signer_t; nostr_signer_t* nostr_signer_local(const unsigned char private_key[32]); void nostr_signer_free(nostr_signer_t* signer); +/* Returns a human-readable error string from the last failed call. For the + * remote nsigner backend this is the raw n_signer RPC error message (e.g. + * "path_not_allowed", "unknown_role"). For the local backend returns a + * generic string. Safe to call after any wrapper returns non-NOSTR_SUCCESS. */ +const char* nostr_signer_last_error(const nostr_signer_t* signer); + /* Core verbs */ int nostr_signer_get_public_key(nostr_signer_t* signer, char out_pubkey_hex[65]); int nostr_signer_sign_event(nostr_signer_t* signer, const cJSON* unsigned_event, cJSON** signed_event_out); @@ -137,6 +143,19 @@ int nostr_signer_mine_event(nostr_signer_t* signer, cJSON** signed_event_out); #if defined(NOSTR_ENABLE_NSIGNER_CLIENT) +/* Forward declaration so from_transport can be declared without including + * nsigner_transport.h (which would create a header dependency cycle). */ +typedef struct nsigner_transport nsigner_transport_t; +typedef struct nsigner_client nsigner_client_t; +/* Create a remote nsigner signer from an already-opened transport. Takes + * ownership of the transport. This is the most flexible constructor — the + * caller opens the transport (unix/tcp/serial/fds/qrexec) and passes it in. */ +nostr_signer_t* nostr_signer_nsigner_from_transport(nsigner_transport_t* transport, const char* role); +/* Create a remote nsigner signer from an already-created low-level client. + * Takes ownership of the client (freed by nostr_signer_free). Useful when + * the caller needs both the high-level typed wrappers and the low-level + * nsigner_client_call for raw passthrough on the same connection. */ +nostr_signer_t* nostr_signer_nsigner_from_client(nsigner_client_t* client, const char* role); nostr_signer_t* nostr_signer_nsigner_unix(const char* socket_name, const char* role, int timeout_ms); nostr_signer_t* nostr_signer_nsigner_serial(const char* device_path, const char* role, int timeout_ms); nostr_signer_t* nostr_signer_nsigner_tcp(const char* host, int port, const char* role, int timeout_ms);