Implement NIP-46 remote signing API, tests/example, build integration, and refresh README docs

This commit is contained in:
2026-02-28 09:53:35 -04:00
parent 57ecbf0c11
commit 2ce2ecbbfb
12 changed files with 1975 additions and 65 deletions
+125
View File
@@ -0,0 +1,125 @@
/*
* NIP-46 Remote Signer Example
*
* Demonstrates signer session setup, bunker URL generation,
* request parsing, request handling, and encrypted response event creation.
*/
#include "../nostr_core/nostr_core.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int parse_hex_key(const char* hex, unsigned char out[32]) {
if (!hex || strlen(hex) != 64) return -1;
return nostr_hex_to_bytes(hex, out, 32);
}
int main(int argc, char** argv) {
if (argc < 3) {
fprintf(stderr, "Usage: %s <signer_privkey_hex> <user_privkey_hex> [relay_url]\n", argv[0]);
fprintf(stderr, "Example: %s <64hex> <64hex> wss://relay.example.com\n", argv[0]);
return 1;
}
const char* relay = (argc >= 4) ? argv[3] : "wss://relay.example.com";
unsigned char signer_sk[32];
unsigned char user_sk[32];
if (parse_hex_key(argv[1], signer_sk) != 0 || parse_hex_key(argv[2], user_sk) != 0) {
fprintf(stderr, "Invalid private key hex; expected 64 hex characters for each key\n");
return 1;
}
if (nostr_init() != NOSTR_SUCCESS) {
fprintf(stderr, "Failed to initialize nostr library\n");
return 1;
}
const char* relays[] = { relay };
nostr_nip46_signer_session_t signer;
int rc = nostr_nip46_signer_session_init(&signer, signer_sk, user_sk, relays, 1);
if (rc != NOSTR_SUCCESS) {
fprintf(stderr, "signer init failed: %s\n", nostr_strerror(rc));
nostr_cleanup();
return 1;
}
char bunker_url[1024];
rc = nostr_nip46_signer_create_bunker_url(&signer, "demo-secret", bunker_url, sizeof(bunker_url));
if (rc != NOSTR_SUCCESS) {
fprintf(stderr, "failed to create bunker url: %s\n", nostr_strerror(rc));
nostr_nip46_signer_session_destroy(&signer);
nostr_cleanup();
return 1;
}
printf("NIP-46 signer initialized\n");
printf("Signer pubkey: %s\n", signer.signer_pubkey_hex);
printf("User pubkey: %s\n", signer.user_pubkey_hex);
printf("Relay: %s\n", relay);
printf("Bunker URL: %s\n\n", bunker_url);
// Simulate receiving a connect request and producing a response
const char* connect_params[] = { signer.signer_pubkey_hex, "demo-secret", "sign_event:1,get_public_key" };
nostr_nip46_request_t req;
rc = nostr_nip46_create_request("demo-req-1", NOSTR_NIP46_METHOD_CONNECT, connect_params, 3, &req);
if (rc != NOSTR_SUCCESS) {
fprintf(stderr, "failed to create demo request: %s\n", nostr_strerror(rc));
nostr_nip46_signer_session_destroy(&signer);
nostr_cleanup();
return 1;
}
nostr_nip46_response_t resp;
rc = nostr_nip46_signer_handle_request(&signer, &req, &resp);
if (rc != NOSTR_SUCCESS) {
fprintf(stderr, "failed to handle demo request: %s\n", nostr_strerror(rc));
nostr_nip46_free_request(&req);
nostr_nip46_signer_session_destroy(&signer);
nostr_cleanup();
return 1;
}
printf("Handled method: %s\n", req.method_str);
printf("Response result: %s\n", resp.result ? resp.result : "<none>");
printf("Response error: %s\n", resp.error ? resp.error : "<none>");
// Simulate building encrypted response event back to a client
unsigned char client_sk[32];
unsigned char client_pk[32];
memset(client_sk, 0, sizeof(client_sk));
client_sk[31] = 1; // deterministic demo key
if (nostr_ec_public_key_from_private_key(client_sk, client_pk) != 0) {
fprintf(stderr, "failed to derive demo client pubkey\n");
nostr_nip46_free_response(&resp);
nostr_nip46_free_request(&req);
nostr_nip46_signer_session_destroy(&signer);
nostr_cleanup();
return 1;
}
cJSON* response_event = nostr_nip46_create_response_event(&resp, signer.signer_private_key, client_pk, 0);
if (!response_event) {
fprintf(stderr, "failed to create encrypted response event\n");
nostr_nip46_free_response(&resp);
nostr_nip46_free_request(&req);
nostr_nip46_signer_session_destroy(&signer);
nostr_cleanup();
return 1;
}
char* response_event_json = cJSON_Print(response_event);
if (response_event_json) {
printf("\nEncrypted response event (publish this to relays):\n%s\n", response_event_json);
free(response_event_json);
}
cJSON_Delete(response_event);
nostr_nip46_free_response(&resp);
nostr_nip46_free_request(&req);
nostr_nip46_signer_session_destroy(&signer);
nostr_cleanup();
return 0;
}