Add NIP-42 auth support to relay pool with verbose auth diagnostics test and docs updates
This commit is contained in:
34
POOL_API.md
34
POOL_API.md
@@ -6,11 +6,12 @@ This document describes the public API for the Nostr Relay Pool implementation i
|
||||
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
| [`nostr_relay_pool_create()`](nostr_core/core_relay_pool.c:219) | Create and initialize a new relay pool |
|
||||
| [`nostr_relay_pool_destroy()`](nostr_core/core_relay_pool.c:304) | Destroy pool and cleanup all resources |
|
||||
| [`nostr_relay_pool_add_relay()`](nostr_core/core_relay_pool.c:229) | Add a relay URL to the pool |
|
||||
| [`nostr_relay_pool_remove_relay()`](nostr_core/core_relay_pool.c:273) | Remove a relay URL from the pool |
|
||||
| [`nostr_relay_pool_subscribe()`](nostr_core/core_relay_pool.c:399) | Create async subscription with callbacks |
|
||||
| [`nostr_relay_pool_create()`](nostr_core/core_relay_pool.c:594) | Create and initialize a new relay pool |
|
||||
| [`nostr_relay_pool_destroy()`](nostr_core/core_relay_pool.c:733) | Destroy pool and cleanup all resources |
|
||||
| [`nostr_relay_pool_add_relay()`](nostr_core/core_relay_pool.c:648) | Add a relay URL to the pool |
|
||||
| [`nostr_relay_pool_remove_relay()`](nostr_core/core_relay_pool.c:702) | Remove a relay URL from the pool |
|
||||
| [`nostr_relay_pool_set_auth()`](nostr_core/core_relay_pool.c:616) | Configure pool-wide NIP-42 authentication key and enable flag |
|
||||
| [`nostr_relay_pool_subscribe()`](nostr_core/core_relay_pool.c:778) | Create async subscription with callbacks |
|
||||
| [`nostr_pool_subscription_close()`](nostr_core/core_relay_pool.c:491) | Close subscription and free resources |
|
||||
| [`nostr_relay_pool_run()`](nostr_core/core_relay_pool.c:1192) | Run event loop for specified timeout |
|
||||
| [`nostr_relay_pool_poll()`](nostr_core/core_relay_pool.c:1232) | Single iteration poll and dispatch |
|
||||
@@ -71,6 +72,26 @@ void cleanup_pool(nostr_relay_pool_t* pool) {
|
||||
|
||||
## Relay Management
|
||||
|
||||
### Configure Pool Authentication (NIP-42)
|
||||
**Function:** [`nostr_relay_pool_set_auth()`](nostr_core/core_relay_pool.c:616)
|
||||
```c
|
||||
int nostr_relay_pool_set_auth(nostr_relay_pool_t* pool, const unsigned char* private_key, int enable);
|
||||
```
|
||||
|
||||
**Description:**
|
||||
- Sets a pool-wide private key used to sign NIP-42 `AUTH` responses.
|
||||
- Enables/disables NIP-42 handling for all relays in the pool.
|
||||
- Propagates auth-enabled state to existing relay connections.
|
||||
|
||||
**Example:**
|
||||
```c
|
||||
unsigned char privkey[32];
|
||||
nostr_hex_to_bytes("91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe", privkey, 32);
|
||||
|
||||
nostr_relay_pool_t* pool = nostr_relay_pool_create(nostr_pool_reconnect_config_default());
|
||||
nostr_relay_pool_set_auth(pool, privkey, 1); // enable NIP-42 auto AUTH handling
|
||||
```
|
||||
|
||||
### Add Relay
|
||||
**Function:** [`nostr_relay_pool_add_relay()`](nostr_core/core_relay_pool.c:229)
|
||||
```c
|
||||
@@ -751,5 +772,6 @@ int main() {
|
||||
- **Memory ownership**: The pool duplicates filters and URLs internally. Caller owns returned events and must free them.
|
||||
- **Event deduplication** is applied pool-wide using a circular buffer of 1000 event IDs.
|
||||
- **Ping functionality** is currently disabled in this build.
|
||||
- **NIP-42 behavior**: With [`nostr_relay_pool_set_auth()`](nostr_core/core_relay_pool.c:616) enabled, the pool will respond to relay `AUTH` challenges automatically using [`nostr_nip42_create_auth_event()`](nostr_core/nip042.c:26) and [`nostr_nip42_create_auth_message()`](nostr_core/nip042.c:84).
|
||||
- **Reconnection** happens on-demand when sending, but active subscriptions are not automatically re-sent after reconnect.
|
||||
- **Polling model**: You must drive the event loop via [`nostr_relay_pool_run()`](nostr_core/core_relay_pool.c:1192) or [`nostr_relay_pool_poll()`](nostr_core/core_relay_pool.c:1232) to receive events.
|
||||
- **Polling model**: You must drive the event loop via [`nostr_relay_pool_run()`](nostr_core/core_relay_pool.c:1745) or [`nostr_relay_pool_poll()`](nostr_core/core_relay_pool.c:1785) to receive events.
|
||||
@@ -292,6 +292,7 @@ publish_result_t* synchronous_publish_event_with_progress(const char** relay_url
|
||||
nostr_pool_reconnect_config_t* config = nostr_pool_reconnect_config_default();
|
||||
nostr_relay_pool_t* nostr_relay_pool_create(nostr_pool_reconnect_config_t* config);
|
||||
int nostr_relay_pool_add_relay(nostr_relay_pool_t* pool, const char* relay_url);
|
||||
int nostr_relay_pool_set_auth(nostr_relay_pool_t* pool, const unsigned char* private_key, int enable);
|
||||
void nostr_relay_pool_destroy(nostr_relay_pool_t* pool);
|
||||
|
||||
// Subscribe to events (with auto-reconnection)
|
||||
@@ -300,7 +301,12 @@ nostr_pool_subscription_t* nostr_relay_pool_subscribe(
|
||||
void (*on_event)(cJSON* event, const char* relay_url, void* user_data),
|
||||
void (*on_eose)(void* user_data), void* user_data, int close_on_eose);
|
||||
|
||||
// Run event loop (handles reconnection automatically)
|
||||
// Enable NIP-42 auth (auto-responds to relay AUTH challenges)
|
||||
unsigned char private_key[32];
|
||||
nostr_hex_to_bytes("91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe", private_key, 32);
|
||||
nostr_relay_pool_set_auth(pool, private_key, 1);
|
||||
|
||||
// Run event loop (handles reconnection + incoming AUTH/NOTICE/OK messages)
|
||||
int nostr_relay_pool_run(nostr_relay_pool_t* pool, int timeout_ms);
|
||||
int nostr_relay_pool_poll(nostr_relay_pool_t* pool, int timeout_ms);
|
||||
|
||||
|
||||
266
debug.log
266
debug.log
@@ -1285,3 +1285,269 @@
|
||||
"since": 1759830747,
|
||||
"limit": 10
|
||||
}]
|
||||
|
||||
=== NOSTR WebSocket Debug Log Started ===
|
||||
[08:43:20.134] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369000,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #1 at 1772369000",
|
||||
"id": "3f05bcb1eecceb8a793f3900ebf4a53e53e0aa8a5c54bb50c180031ec980535e",
|
||||
"sig": "bcb3d84e75cd7db83b4b9014d25e13ea3de120538e44fde04d0bbb5a86fb240d054033a186c26c9fb8ff62257b126792e48bc9adaa57c3e664eb804c9b00ec15"
|
||||
}]
|
||||
[08:43:20.235] RECV 127.0.0.1:7777: ["AUTH", "5b063a862be4208984903f034d4af6e4b3d9cf4f3cab73c14eacdd2a69666326"]
|
||||
[08:43:20.235] SEND 127.0.0.1:7777: ["AUTH",{"pubkey":"b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1","created_at":1772369000,"kind":22242,"tags":[["relay","ws://127.0.0.1:7777"],["challenge","5b063a862be4208984903f034d4af6e4b3d9cf4f3cab73c14eacdd2a69666326"]],"content":"","id":"26fe2d66f9b2d387b2a1fe561c2a2fc542c749bb27b35952e2becf0987192773","sig":"e8866c1dfc99d05461c633b56f027da9ff8a0748c7cd010c7ac89930de352da276dc0cd946b2ff770c99cfe612b84c878b91dadbc95d13b0cfbde626e1e887ad"}]
|
||||
[08:43:20.336] RECV 127.0.0.1:7777: ["NOTICE", "NIP-42 authentication successful"]
|
||||
[08:43:20.737] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369000,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #2 at 1772369000",
|
||||
"id": "366a67b33dfe33783940c62511c7fb9be2a4e979a6b064136d482e4f7274019e",
|
||||
"sig": "9d50286783ef9bbce8c2f1fe1996472f1a83d2f8d67c90496284b7816c0f909b6400bc20f48d6a1ce80be9faec47126f06153414fd4951958b45e2ebba025585"
|
||||
}]
|
||||
[08:43:20.838] RECV 127.0.0.1:7777: ["OK", "366a67b33dfe33783940c62511c7fb9be2a4e979a6b064136d482e4f7274019e", true, ""]
|
||||
[08:43:21.239] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369001,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #3 at 1772369001",
|
||||
"id": "13bde5c47b56820035e41237f488a51493520cd803c1b8e5f174101ab3086d54",
|
||||
"sig": "067c75f7b52fd45c29c034ac2a1d9be35aa4d1346e73431efe09437f8ec4d5ad720cc671d85ab438f204ea489ba5cd80ca987fb2738028c5ee766bd8b8325903"
|
||||
}]
|
||||
[08:43:21.339] RECV 127.0.0.1:7777: ["OK", "13bde5c47b56820035e41237f488a51493520cd803c1b8e5f174101ab3086d54", true, ""]
|
||||
[08:43:21.941] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369001,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #4 at 1772369001",
|
||||
"id": "211025bfe6e0e88c61ffeb993e3ad772a18fb861ccc7c605b1b5a11383314459",
|
||||
"sig": "7b61f914fbb9fcdc5e9160040830f49ac6f765d1fbd71c4710ba1dbe612996ed5bab075a7051fd7e7ad26757caae06d874cedc4a23a6dbb2ec406708737347a5"
|
||||
}]
|
||||
[08:43:22.041] RECV 127.0.0.1:7777: ["OK", "211025bfe6e0e88c61ffeb993e3ad772a18fb861ccc7c605b1b5a11383314459", true, ""]
|
||||
[08:43:22.643] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369002,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #5 at 1772369002",
|
||||
"id": "89304aec9b662b1855f59e099dfebc70ab0711ca0ef9c1fa4b2ff53dca75a5f2",
|
||||
"sig": "43f3f0b998cfaaee4be65106df4e039d9992e71cddde8eca18e7747bded89965f75351688ec806aea1b2f2f5cd77abd81d7bd077b3982fae3b378ac02e92fa7b"
|
||||
}]
|
||||
[08:43:22.743] RECV 127.0.0.1:7777: ["OK", "89304aec9b662b1855f59e099dfebc70ab0711ca0ef9c1fa4b2ff53dca75a5f2", true, ""]
|
||||
[08:43:23.345] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369003,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #6 at 1772369003",
|
||||
"id": "48885a859de2937b3638a5ce55176d8bba5e257bc1989f923499ccecd5333c7c",
|
||||
"sig": "082d6316270e03f87f27ab79861ebf9a4defad783c298b4922de0df771eefa74df68eda0706d2b02c88cd4b2a0305c63e8315a71ce3be95aa1b46a7c91833950"
|
||||
}]
|
||||
[08:43:23.446] RECV 127.0.0.1:7777: ["OK", "48885a859de2937b3638a5ce55176d8bba5e257bc1989f923499ccecd5333c7c", true, ""]
|
||||
[08:43:23.847] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369003,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #7 at 1772369003",
|
||||
"id": "788d2bce8d8ad86eb3ce33d2fe6bb48771ab3ac2eba04c1943c034434d309e0a",
|
||||
"sig": "f2051d78052aa145ad9feed09e7b2121cf92b7bfb8e6f0a64b0a24b0eb5b3a62e4acff5e4fe24451247242e99685f6aa1dac3b5d9619f09d6b5e3ef1ba466f5c"
|
||||
}]
|
||||
[08:43:23.948] RECV 127.0.0.1:7777: ["OK", "788d2bce8d8ad86eb3ce33d2fe6bb48771ab3ac2eba04c1943c034434d309e0a", true, ""]
|
||||
[08:43:24.550] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369004,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #8 at 1772369004",
|
||||
"id": "e097ab0e55e2255755aca9f4cb1bd29dd443d616b1f53355a4f0fbed19d4a911",
|
||||
"sig": "e8c70ab86daeb8368db007d4431ae11042a5399083aa1c7f69cf77640f6fbfe25a0b203a737e221f9a2a9e3a86c7b6a549e99999c876197240319d547df4e324"
|
||||
}]
|
||||
[08:43:24.650] RECV 127.0.0.1:7777: ["OK", "e097ab0e55e2255755aca9f4cb1bd29dd443d616b1f53355a4f0fbed19d4a911", true, ""]
|
||||
|
||||
=== NOSTR WebSocket Debug Log Started ===
|
||||
[08:48:11.341] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369291,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #1 at 1772369291",
|
||||
"id": "1c62334a228667b563e6eb857f143501c3917e1c9229fcec5cdeab5667de3ed2",
|
||||
"sig": "1eaac0663eedc6cb84082e6b3e0631c698f3d355418e9d346cc8a14af0cdac2a36881b449595338bd3c551635b082feba794ffdabbcfb48b42ebdef16767da1e"
|
||||
}]
|
||||
[08:48:11.441] RECV 127.0.0.1:7777: ["AUTH", "1a1a55ddc3f661a8b81fcb8069122fbcd0afed5a64511993731ac17a4947129c"]
|
||||
[08:48:11.442] SEND 127.0.0.1:7777: ["AUTH",{"pubkey":"b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1","created_at":1772369291,"kind":22242,"tags":[["relay","ws://127.0.0.1:7777"],["challenge","1a1a55ddc3f661a8b81fcb8069122fbcd0afed5a64511993731ac17a4947129c"]],"content":"","id":"1e515c71de2580aa06dddb0ce3cccf45727469ab89aded2592d1ee57ecc44be0","sig":"94023c5f58a149b40a1ae2077a3b65e1f702c443287c4928ec81a042175e82c93e4bfe549099ff60c09cb955323aad8586976384fbe0321fd83ee46d940f3050"}]
|
||||
[08:48:11.542] RECV 127.0.0.1:7777: ["NOTICE", "NIP-42 authentication successful"]
|
||||
[08:48:11.943] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369291,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #2 at 1772369291",
|
||||
"id": "40ab4111852c7d9e2cf3e8e9dfe26bcfc133e5cef3e95df73f80f4df780ec0f9",
|
||||
"sig": "0da0ff807eef8c6cf589d41384c0acc5e067a46ba3c59c114d45afc69468660103150da25a3635b9da76dd6a7168374bcc4b117617c43dc91852b9254931c38a"
|
||||
}]
|
||||
[08:48:12.043] RECV 127.0.0.1:7777: ["OK", "40ab4111852c7d9e2cf3e8e9dfe26bcfc133e5cef3e95df73f80f4df780ec0f9", true, ""]
|
||||
[08:48:12.445] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369292,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #3 at 1772369292",
|
||||
"id": "6fb2a3c20d8d396c40f59e50f02253674cbdef481d9229041a7e52f91812da09",
|
||||
"sig": "b9aafec899b4c1863658b1554b88f7bbbc24753c41e853bc7717cc4b340ea53aab7ae26d1160ad960391ebe0462b8154016afb7e5090e99b67b3ee1f4fcf624b"
|
||||
}]
|
||||
[08:48:12.545] RECV 127.0.0.1:7777: ["OK", "6fb2a3c20d8d396c40f59e50f02253674cbdef481d9229041a7e52f91812da09", true, ""]
|
||||
[08:48:13.147] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369293,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #4 at 1772369293",
|
||||
"id": "20b9a8807d174c1b6cf2b5c9a1306b3e747b794fb6238372eae6f552003ace57",
|
||||
"sig": "12198032d5b7f2072c32b260918cd2a0b34495a1efd4ca079406faf58f9b9de0dd5f0ac48d051cc46819059815fa72bb3877cb26bf2afbfd57cd816fdbb091b5"
|
||||
}]
|
||||
[08:48:13.247] RECV 127.0.0.1:7777: ["OK", "20b9a8807d174c1b6cf2b5c9a1306b3e747b794fb6238372eae6f552003ace57", true, ""]
|
||||
[08:48:13.850] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369293,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #5 at 1772369293",
|
||||
"id": "d2b0c699f190d0f64c66a3fa5bbe9953aebdb33dea800c7b6510d8326f64502a",
|
||||
"sig": "ed70bbcf717ef2ef84015eac68ec556ded86829989c519aadcc57beb12eab62e5f065a7e566a8a4c779c559f5e7c021a61d0b92d35bea12922566e1d4e898323"
|
||||
}]
|
||||
[08:48:13.950] RECV 127.0.0.1:7777: ["OK", "d2b0c699f190d0f64c66a3fa5bbe9953aebdb33dea800c7b6510d8326f64502a", true, ""]
|
||||
[08:48:14.552] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369294,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #6 at 1772369294",
|
||||
"id": "8999d101b39cfdd9c23e77e090b00516ca0659590304be2cee87a482ce099945",
|
||||
"sig": "42a78a8b8ede4dde81e7e011c90593eb228c00ef3d098b752faab7bd767e7106f77ceb0efaeb1dbc4220e4b03ea2e11d0e93c69b1e1b6c932727fa734ea15e14"
|
||||
}]
|
||||
[08:48:14.653] RECV 127.0.0.1:7777: ["OK", "8999d101b39cfdd9c23e77e090b00516ca0659590304be2cee87a482ce099945", true, ""]
|
||||
[08:48:15.054] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369295,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #7 at 1772369295",
|
||||
"id": "e34be3b488d277d39f2b5ef2c5a651ef06e8f28da3eed87de006edc7db2843e0",
|
||||
"sig": "f7f6768eb8f7cf91bb4e31305e93044e3fcea2bfcdd462161da6bf8948a02ae3217e38f1bb4f85f6a8be5753f6aa15369dd7b1b428b87903f95b7525f3bb5024"
|
||||
}]
|
||||
[08:48:15.154] RECV 127.0.0.1:7777: ["OK", "e34be3b488d277d39f2b5ef2c5a651ef06e8f28da3eed87de006edc7db2843e0", true, ""]
|
||||
[08:48:15.756] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369295,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #8 at 1772369295",
|
||||
"id": "90b5052b8bc56c183510b2c64374aa9c8a83580d83a3416d76ff6173fe30ef22",
|
||||
"sig": "2e6dd1d5eb3c97b6f9ae4936174110c8d4c72c3a89585f9f1a4d863444caabff9a6705d0cac3bc1257c30ec6209ae409a8c65bf91352fe5d355b60f182cfe3f4"
|
||||
}]
|
||||
[08:48:15.856] RECV 127.0.0.1:7777: ["OK", "90b5052b8bc56c183510b2c64374aa9c8a83580d83a3416d76ff6173fe30ef22", true, ""]
|
||||
[08:48:17.761] SEND 127.0.0.1:7777: ["REQ", "pool_1_1772369297", {
|
||||
"kinds": [22242],
|
||||
"authors": ["b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1"],
|
||||
"limit": 20
|
||||
}]
|
||||
[08:48:17.763] RECV 127.0.0.1:7777: ["EOSE", "pool_1_1772369297"]
|
||||
[08:48:17.763] SEND 127.0.0.1:7777: ["CLOSE", "pool_1_1772369297"]
|
||||
|
||||
=== NOSTR WebSocket Debug Log Started ===
|
||||
[08:49:30.820] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369370,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #1 at 1772369370",
|
||||
"id": "32dd2821b9290cd8b7c448ec1aaef8f74a46f18aed04ac1bec6d37c0a04ed257",
|
||||
"sig": "1e8de3e16d806f4533c930f064f9e190cf310573acd32c7e5541e2cd2999b7ab4d4299f21b114ef093dd382f01ca6849eba2b1614cc3f7b5b33995e3d4998cd4"
|
||||
}]
|
||||
[08:49:30.920] RECV 127.0.0.1:7777: ["AUTH", "17192ffaf320375908c6e268d20e82432ffc9bcb2aa49e15c9531be9e4787376"]
|
||||
[08:49:30.921] SEND 127.0.0.1:7777: ["AUTH",{"pubkey":"b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1","created_at":1772369370,"kind":22242,"tags":[["relay","ws://127.0.0.1:7777"],["challenge","17192ffaf320375908c6e268d20e82432ffc9bcb2aa49e15c9531be9e4787376"]],"content":"","id":"a3f4e7cfccc9825253b0191dbcf479676f6f0522d80951eccc50ca117f451307","sig":"0d2bf563986bed1bd4eb7bb4f3364f9c84f9fc222be72eed307b41e6f7496331baf850b2a2ca8fb0be5a06d79a74bb2456bd7f86b0c32b5a86ea0565026fc1d8"}]
|
||||
[08:49:31.021] RECV 127.0.0.1:7777: ["NOTICE", "NIP-42 authentication successful"]
|
||||
[08:49:31.422] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369371,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #2 at 1772369371",
|
||||
"id": "3a3b1e1b63aa60bd61b90f93fbb4c130b91a777889a2598c956e05cba5ebe759",
|
||||
"sig": "975310b4a5bffbfb53ac09f801b292af2ead2824ba9db0aef9d7cfc42f39fba3ba2bff48b34811d350ee70fb7b85c0819f3ff7b0fa39582fd1e1748942376051"
|
||||
}]
|
||||
[08:49:31.523] RECV 127.0.0.1:7777: ["OK", "3a3b1e1b63aa60bd61b90f93fbb4c130b91a777889a2598c956e05cba5ebe759", true, ""]
|
||||
[08:49:31.925] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369371,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #3 at 1772369371",
|
||||
"id": "63a4e3a45377010261734b2d10c15cde25c2473527f60f4337583a6077ab76b5",
|
||||
"sig": "f3034607a1bc049c3fd071f4353f76e8541ec00a07b79c3f7f6afaff295fb3f687729843d0e5646cb0696c91b5b0821f2f99410d3828f64fc8bb11f0b247163f"
|
||||
}]
|
||||
[08:49:32.025] RECV 127.0.0.1:7777: ["OK", "63a4e3a45377010261734b2d10c15cde25c2473527f60f4337583a6077ab76b5", true, ""]
|
||||
[08:49:32.627] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369372,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #4 at 1772369372",
|
||||
"id": "8aed3ffd3986b2b2acc66f6a8bb6569500c0d8901a363ab9b1a107f5e4f79318",
|
||||
"sig": "83ed90beb88e0cb72bc394ac9c1f9e32e8dbd5a9e58d27051fc50f39b5ae4dc212d5bb64198e0cdf76da99003e2d5556bb94d444990b9cc8b5c9b7c3139f870e"
|
||||
}]
|
||||
[08:49:32.727] RECV 127.0.0.1:7777: ["OK", "8aed3ffd3986b2b2acc66f6a8bb6569500c0d8901a363ab9b1a107f5e4f79318", true, ""]
|
||||
[08:49:33.329] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369373,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #5 at 1772369373",
|
||||
"id": "e3239d2346517c8e9fa6c5468b175266aceb23468cf704de7cde4eccadf1f35c",
|
||||
"sig": "d38a36c90844381d76a21432ebb383a54b30f78291c945683fc92f2760a9067e36095dae9fd55166bf07231c0223dd68b5468f98746dab7f6a59f88155eb4fb8"
|
||||
}]
|
||||
[08:49:33.429] RECV 127.0.0.1:7777: ["OK", "e3239d2346517c8e9fa6c5468b175266aceb23468cf704de7cde4eccadf1f35c", true, ""]
|
||||
[08:49:34.031] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369374,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #6 at 1772369374",
|
||||
"id": "5ec32bec6aead83ab70c3a0fe636787db4e675c50ef3398d694cdc98b4673a0d",
|
||||
"sig": "e56b428052fee7c02c045a4b990cdd6208891582ab4379b4f71c1ee6f51903ee3321d37afa364daba9e2162b8f3c44a4dc283c72a40c52fb0da7d3067865460d"
|
||||
}]
|
||||
[08:49:34.132] RECV 127.0.0.1:7777: ["OK", "5ec32bec6aead83ab70c3a0fe636787db4e675c50ef3398d694cdc98b4673a0d", true, ""]
|
||||
[08:49:34.533] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369374,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #7 at 1772369374",
|
||||
"id": "a4272777a8d4701393eed9343dd5a737e02b7594e2fdb46b2575c306ba57813c",
|
||||
"sig": "42379e05c7e14ab64a4a686f79e293c27897ae776c2e88c0f4010db74783d6a8c4e5df0cfc70cbb333fd2624b5e1d0481efb4ad2a458edaf2ef389ef3ff07b87"
|
||||
}]
|
||||
[08:49:34.634] RECV 127.0.0.1:7777: ["OK", "a4272777a8d4701393eed9343dd5a737e02b7594e2fdb46b2575c306ba57813c", true, ""]
|
||||
[08:49:35.236] SEND 127.0.0.1:7777: ["EVENT", {
|
||||
"pubkey": "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1",
|
||||
"created_at": 1772369375,
|
||||
"kind": 4,
|
||||
"tags": [["p", "0000000000000000000000000000000000000000000000000000000000000000"]],
|
||||
"content": "pool-auth-test message #8 at 1772369375",
|
||||
"id": "da022254d3eeec701c68388ab0be032bca21f0cce91f471f7212447fbaa446f6",
|
||||
"sig": "d01e0a2f0f464029d6b7e251d1c467359ad4026036fdea8c5b67cc1241d56553634bd50349bc72e6e43f064ba9ee26bf29ca1fb95a169ca96c4118c616246b01"
|
||||
}]
|
||||
[08:49:35.336] RECV 127.0.0.1:7777: ["OK", "da022254d3eeec701c68388ab0be032bca21f0cce91f471f7212447fbaa446f6", true, ""]
|
||||
[08:49:37.240] SEND 127.0.0.1:7777: ["REQ", "pool_1_1772369377", {
|
||||
"kinds": [22242],
|
||||
"authors": ["b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1"],
|
||||
"limit": 20
|
||||
}]
|
||||
[08:49:37.243] RECV 127.0.0.1:7777: ["EOSE", "pool_1_1772369377"]
|
||||
[08:49:37.243] SEND 127.0.0.1:7777: ["CLOSE", "pool_1_1772369377"]
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
// cJSON for JSON handling
|
||||
#include "../cjson/cJSON.h"
|
||||
|
||||
|
||||
|
||||
// NIP-42 Authentication
|
||||
#include "nip042.h"
|
||||
|
||||
// =============================================================================
|
||||
// RELAY POOL MANAGEMENT
|
||||
@@ -105,6 +105,12 @@ typedef struct relay_connection {
|
||||
char last_connection_error[512]; // Last error message from relay connection
|
||||
time_t last_connection_error_time; // When the connection error occurred
|
||||
|
||||
// NIP-42 Authentication fields
|
||||
nostr_auth_state_t auth_state; // Current authentication state
|
||||
char auth_challenge[NOSTR_NIP42_MAX_CHALLENGE_LENGTH]; // Stored challenge
|
||||
time_t auth_challenge_time; // When challenge was received
|
||||
int nip42_enabled; // Whether NIP-42 is enabled for this relay
|
||||
|
||||
// Statistics
|
||||
nostr_relay_stats_t stats;
|
||||
} relay_connection_t;
|
||||
@@ -165,6 +171,11 @@ struct nostr_relay_pool {
|
||||
|
||||
// Pool-wide settings
|
||||
int default_timeout_ms;
|
||||
|
||||
// NIP-42 Authentication configuration
|
||||
int nip42_enabled;
|
||||
unsigned char private_key[32];
|
||||
int has_private_key;
|
||||
};
|
||||
|
||||
// Helper function to generate unique subscription IDs
|
||||
@@ -602,6 +613,38 @@ nostr_relay_pool_t* nostr_relay_pool_create_compat(void) {
|
||||
return nostr_relay_pool_create(NULL);
|
||||
}
|
||||
|
||||
int nostr_relay_pool_set_auth(nostr_relay_pool_t* pool,
|
||||
const unsigned char* private_key,
|
||||
int enable) {
|
||||
if (!pool) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
pool->nip42_enabled = enable ? 1 : 0;
|
||||
|
||||
if (private_key) {
|
||||
memcpy(pool->private_key, private_key, sizeof(pool->private_key));
|
||||
pool->has_private_key = 1;
|
||||
} else {
|
||||
memset(pool->private_key, 0, sizeof(pool->private_key));
|
||||
pool->has_private_key = 0;
|
||||
}
|
||||
|
||||
// Propagate to existing relay connections
|
||||
for (int i = 0; i < pool->relay_count; i++) {
|
||||
if (pool->relays[i]) {
|
||||
pool->relays[i]->nip42_enabled = pool->nip42_enabled;
|
||||
if (!pool->nip42_enabled) {
|
||||
pool->relays[i]->auth_state = NOSTR_AUTH_STATE_NONE;
|
||||
memset(pool->relays[i]->auth_challenge, 0, sizeof(pool->relays[i]->auth_challenge));
|
||||
pool->relays[i]->auth_challenge_time = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
int nostr_relay_pool_add_relay(nostr_relay_pool_t* pool, const char* relay_url) {
|
||||
if (!pool || !relay_url || pool->relay_count >= NOSTR_POOL_MAX_RELAYS) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
@@ -640,6 +683,12 @@ int nostr_relay_pool_add_relay(nostr_relay_pool_t* pool, const char* relay_url)
|
||||
relay->ping_pending = 0;
|
||||
relay->pending_ping_start_ms = 0.0;
|
||||
|
||||
// Initialize NIP-42 authentication fields
|
||||
relay->auth_state = NOSTR_AUTH_STATE_NONE;
|
||||
memset(relay->auth_challenge, 0, sizeof(relay->auth_challenge));
|
||||
relay->auth_challenge_time = 0;
|
||||
relay->nip42_enabled = pool->nip42_enabled;
|
||||
|
||||
// Initialize statistics
|
||||
memset(&relay->stats, 0, sizeof(relay->stats));
|
||||
relay->stats.connection_uptime_start = time(NULL);
|
||||
@@ -716,6 +765,11 @@ void nostr_relay_pool_destroy(nostr_relay_pool_t* pool) {
|
||||
}
|
||||
}
|
||||
|
||||
if (pool->has_private_key) {
|
||||
memset(pool->private_key, 0, sizeof(pool->private_key));
|
||||
pool->has_private_key = 0;
|
||||
}
|
||||
|
||||
free(pool);
|
||||
}
|
||||
|
||||
@@ -1067,8 +1121,36 @@ static void process_relay_message(nostr_relay_pool_t* pool, relay_connection_t*
|
||||
// Update relay statistics
|
||||
if (success) {
|
||||
relay->stats.events_published_ok++;
|
||||
if (relay->auth_state == NOSTR_AUTH_STATE_AUTHENTICATING) {
|
||||
relay->auth_state = NOSTR_AUTH_STATE_AUTHENTICATED;
|
||||
}
|
||||
} else {
|
||||
relay->stats.events_published_failed++;
|
||||
|
||||
if (error_message && strstr(error_message, "auth-required") != NULL) {
|
||||
relay->auth_state = NOSTR_AUTH_STATE_REJECTED;
|
||||
|
||||
// If we already have a challenge, immediately attempt re-authentication
|
||||
if (relay->nip42_enabled && pool->has_private_key && relay->auth_challenge[0] != '\0') {
|
||||
cJSON* auth_event = nostr_nip42_create_auth_event(
|
||||
relay->auth_challenge,
|
||||
relay->url,
|
||||
pool->private_key,
|
||||
0
|
||||
);
|
||||
if (auth_event) {
|
||||
char* auth_message = nostr_nip42_create_auth_message(auth_event);
|
||||
if (auth_message) {
|
||||
if (nostr_ws_send_text(relay->ws_client, auth_message) >= 0) {
|
||||
relay->auth_state = NOSTR_AUTH_STATE_AUTHENTICATING;
|
||||
}
|
||||
free(auth_message);
|
||||
}
|
||||
cJSON_Delete(auth_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store error message for legacy API
|
||||
if (error_message) {
|
||||
strncpy(relay->last_publish_error, error_message,
|
||||
@@ -1094,6 +1176,51 @@ static void process_relay_message(nostr_relay_pool_t* pool, relay_connection_t*
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (strcmp(msg_type, "AUTH") == 0) {
|
||||
// Handle AUTH challenge message: ["AUTH", <challenge-string>]
|
||||
if (relay->nip42_enabled && pool->has_private_key &&
|
||||
cJSON_IsArray(parsed) && cJSON_GetArraySize(parsed) >= 2) {
|
||||
cJSON* challenge_json = cJSON_GetArrayItem(parsed, 1);
|
||||
if (cJSON_IsString(challenge_json)) {
|
||||
const char* challenge = cJSON_GetStringValue(challenge_json);
|
||||
|
||||
strncpy(relay->auth_challenge, challenge, sizeof(relay->auth_challenge) - 1);
|
||||
relay->auth_challenge[sizeof(relay->auth_challenge) - 1] = '\0';
|
||||
relay->auth_challenge_time = time(NULL);
|
||||
relay->auth_state = NOSTR_AUTH_STATE_CHALLENGE_RECEIVED;
|
||||
|
||||
cJSON* auth_event = nostr_nip42_create_auth_event(
|
||||
challenge,
|
||||
relay->url,
|
||||
pool->private_key,
|
||||
0
|
||||
);
|
||||
if (auth_event) {
|
||||
char* auth_message = nostr_nip42_create_auth_message(auth_event);
|
||||
if (auth_message) {
|
||||
if (nostr_ws_send_text(relay->ws_client, auth_message) >= 0) {
|
||||
relay->auth_state = NOSTR_AUTH_STATE_AUTHENTICATING;
|
||||
}
|
||||
free(auth_message);
|
||||
}
|
||||
cJSON_Delete(auth_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (strcmp(msg_type, "NOTICE") == 0) {
|
||||
// Handle NOTICE message: ["NOTICE", <message>]
|
||||
if (cJSON_IsArray(parsed) && cJSON_GetArraySize(parsed) >= 2) {
|
||||
cJSON* notice_msg = cJSON_GetArrayItem(parsed, 1);
|
||||
if (cJSON_IsString(notice_msg)) {
|
||||
const char* notice = cJSON_GetStringValue(notice_msg);
|
||||
if (notice && strstr(notice, "auth-required") != NULL) {
|
||||
relay->auth_state = NOSTR_AUTH_STATE_REJECTED;
|
||||
strncpy(relay->last_publish_error, notice, sizeof(relay->last_publish_error) - 1);
|
||||
relay->last_publish_error[sizeof(relay->last_publish_error) - 1] = '\0';
|
||||
relay->last_publish_error_time = time(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (strcmp(msg_type, "PONG") == 0) {
|
||||
// Handle PONG response for connection health monitoring
|
||||
handle_pong_response(relay);
|
||||
@@ -1221,6 +1348,37 @@ cJSON** nostr_relay_pool_query_sync(
|
||||
eose_count++;
|
||||
}
|
||||
}
|
||||
} else if (msg_type && strcmp(msg_type, "AUTH") == 0) {
|
||||
// Handle AUTH challenge message: ["AUTH", <challenge-string>]
|
||||
if (relay->nip42_enabled && pool->has_private_key &&
|
||||
cJSON_IsArray(parsed) && cJSON_GetArraySize(parsed) >= 2) {
|
||||
cJSON* challenge_json = cJSON_GetArrayItem(parsed, 1);
|
||||
if (cJSON_IsString(challenge_json)) {
|
||||
const char* challenge = cJSON_GetStringValue(challenge_json);
|
||||
|
||||
strncpy(relay->auth_challenge, challenge, sizeof(relay->auth_challenge) - 1);
|
||||
relay->auth_challenge[sizeof(relay->auth_challenge) - 1] = '\0';
|
||||
relay->auth_challenge_time = time(NULL);
|
||||
relay->auth_state = NOSTR_AUTH_STATE_CHALLENGE_RECEIVED;
|
||||
|
||||
cJSON* auth_event = nostr_nip42_create_auth_event(
|
||||
challenge,
|
||||
relay->url,
|
||||
pool->private_key,
|
||||
0
|
||||
);
|
||||
if (auth_event) {
|
||||
char* auth_message = nostr_nip42_create_auth_message(auth_event);
|
||||
if (auth_message) {
|
||||
if (nostr_ws_send_text(relay->ws_client, auth_message) >= 0) {
|
||||
relay->auth_state = NOSTR_AUTH_STATE_AUTHENTICATING;
|
||||
}
|
||||
free(auth_message);
|
||||
}
|
||||
cJSON_Delete(auth_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (msg_type) free(msg_type);
|
||||
if (parsed) cJSON_Delete(parsed);
|
||||
|
||||
@@ -34,7 +34,11 @@ static void safe_copy(char* dst, size_t dst_size, const char* src) {
|
||||
dst[0] = '\0';
|
||||
return;
|
||||
}
|
||||
snprintf(dst, dst_size, "%s", src);
|
||||
|
||||
size_t src_len = strlen(src);
|
||||
size_t copy_len = (src_len < (dst_size - 1U)) ? src_len : (dst_size - 1U);
|
||||
memcpy(dst, src, copy_len);
|
||||
dst[copy_len] = '\0';
|
||||
}
|
||||
|
||||
static int is_hex_64(const char* s) {
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
#define NOSTR_CORE_H
|
||||
|
||||
// Version information (auto-updated by increment_and_push.sh)
|
||||
#define VERSION "v0.4.9"
|
||||
#define VERSION "v0.4.10"
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 4
|
||||
#define VERSION_PATCH 9
|
||||
#define VERSION_PATCH 10
|
||||
|
||||
/*
|
||||
* NOSTR Core Library - Complete API Reference
|
||||
@@ -249,6 +249,7 @@ nostr_relay_pool_t* nostr_relay_pool_create(nostr_pool_reconnect_config_t* confi
|
||||
nostr_pool_reconnect_config_t* nostr_pool_reconnect_config_default(void);
|
||||
int nostr_relay_pool_add_relay(nostr_relay_pool_t* pool, const char* relay_url);
|
||||
int nostr_relay_pool_remove_relay(nostr_relay_pool_t* pool, const char* relay_url);
|
||||
int nostr_relay_pool_set_auth(nostr_relay_pool_t* pool, const unsigned char* private_key, int enable);
|
||||
void nostr_relay_pool_destroy(nostr_relay_pool_t* pool);
|
||||
|
||||
// Subscription management
|
||||
|
||||
209
plans/nip42_relay_pool_plan.md
Normal file
209
plans/nip42_relay_pool_plan.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# NIP-42 Authentication Support for core_relay_pool.c
|
||||
|
||||
## Problem Statement
|
||||
|
||||
The relay pool ([`core_relay_pool.c`](nostr_core/core_relay_pool.c)) does not handle NIP-42 authentication. When a relay sends an `AUTH` challenge message, the pool silently ignores it, causing:
|
||||
|
||||
- Subscriptions to fail on auth-required relays (events never delivered)
|
||||
- Publishes to be rejected with `auth-required:` errors
|
||||
- No visibility into authentication state per relay
|
||||
|
||||
The synchronous relay functions in [`core_relays.c`](nostr_core/core_relays.c) already have full NIP-42 support (lines 58-63, 211-239, 595-624). The NIP-42 primitives in [`nip042.c`](nostr_core/nip042.c) / [`nip042.h`](nostr_core/nip042.h) are production-ready. This plan wires them into the pool.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Relay sends AUTH challenge] --> B[process_relay_message parses AUTH]
|
||||
B --> C{pool has private_key?}
|
||||
C -->|No| D[Log warning, mark relay auth_state = CHALLENGE_RECEIVED]
|
||||
C -->|Yes| E[nostr_nip42_create_auth_event]
|
||||
E --> F[nostr_nip42_create_auth_message]
|
||||
F --> G[nostr_ws_send_text to relay]
|
||||
G --> H[Mark relay auth_state = AUTHENTICATING]
|
||||
H --> I[Relay sends OK for auth event]
|
||||
I --> J[Mark relay auth_state = AUTHENTICATED]
|
||||
|
||||
K[Relay sends OK with auth-required] --> L[process_relay_message detects prefix]
|
||||
L --> M{relay already authenticated?}
|
||||
M -->|No| N[Trigger auth flow if private_key available]
|
||||
M -->|Yes| O[Surface as publish failure]
|
||||
|
||||
P[Relay sends NOTICE] --> Q{Contains auth-required?}
|
||||
Q -->|Yes| R[Log auth-required notice]
|
||||
Q -->|No| S[Ignore or log]
|
||||
```
|
||||
|
||||
## Detailed Changes
|
||||
|
||||
### 1. Add NIP-42 include to core_relay_pool.c
|
||||
|
||||
Add `#include "nip042.h"` alongside the existing includes at the top of [`core_relay_pool.c`](nostr_core/core_relay_pool.c:27).
|
||||
|
||||
### 2. Add auth fields to `relay_connection_t` struct
|
||||
|
||||
Modeled after [`core_relays.c:58-63`](nostr_core/core_relays.c:58), add to the [`relay_connection_t`](nostr_core/core_relay_pool.c:76) struct:
|
||||
|
||||
```c
|
||||
// NIP-42 Authentication fields
|
||||
nostr_auth_state_t auth_state;
|
||||
char auth_challenge[NOSTR_NIP42_MAX_CHALLENGE_LENGTH];
|
||||
time_t auth_challenge_time;
|
||||
int nip42_enabled;
|
||||
```
|
||||
|
||||
These go after the existing `last_connection_error_time` field (around line 106).
|
||||
|
||||
### 3. Add private key and NIP-42 config to `nostr_relay_pool` struct
|
||||
|
||||
Add to the [`nostr_relay_pool`](nostr_core/core_relay_pool.c:146) struct:
|
||||
|
||||
```c
|
||||
// NIP-42 Authentication configuration
|
||||
int nip42_enabled;
|
||||
unsigned char private_key[32];
|
||||
int has_private_key;
|
||||
```
|
||||
|
||||
### 4. Add public API: `nostr_relay_pool_set_auth()`
|
||||
|
||||
Add to [`nostr_core.h`](nostr_core/nostr_core.h) after the existing pool management functions (around line 252):
|
||||
|
||||
```c
|
||||
// NIP-42 Authentication configuration for relay pool
|
||||
int nostr_relay_pool_set_auth(nostr_relay_pool_t* pool,
|
||||
const unsigned char* private_key,
|
||||
int enable);
|
||||
```
|
||||
|
||||
Implement in [`core_relay_pool.c`](nostr_core/core_relay_pool.c):
|
||||
- Copies the 32-byte private key into the pool struct
|
||||
- Sets `nip42_enabled` flag
|
||||
- Propagates `nip42_enabled` to all existing relay connections
|
||||
|
||||
### 5. Handle AUTH in `process_relay_message()`
|
||||
|
||||
Add an `AUTH` handler in [`process_relay_message()`](nostr_core/core_relay_pool.c:912) between the existing `OK` and `PONG` handlers (around line 1097). The logic mirrors [`core_relays.c:211-239`](nostr_core/core_relays.c:211):
|
||||
|
||||
```c
|
||||
} else if (strcmp(msg_type, "AUTH") == 0) {
|
||||
// Handle AUTH challenge: ["AUTH", <challenge-string>]
|
||||
if (pool->nip42_enabled && pool->has_private_key &&
|
||||
cJSON_IsArray(parsed) && cJSON_GetArraySize(parsed) >= 2) {
|
||||
cJSON* challenge_json = cJSON_GetArrayItem(parsed, 1);
|
||||
if (cJSON_IsString(challenge_json)) {
|
||||
const char* challenge = cJSON_GetStringValue(challenge_json);
|
||||
|
||||
// Store challenge
|
||||
strncpy(relay->auth_challenge, challenge,
|
||||
sizeof(relay->auth_challenge) - 1);
|
||||
relay->auth_challenge[sizeof(relay->auth_challenge) - 1] = '\0';
|
||||
relay->auth_challenge_time = time(NULL);
|
||||
relay->auth_state = NOSTR_AUTH_STATE_CHALLENGE_RECEIVED;
|
||||
|
||||
// Create and send auth event
|
||||
cJSON* auth_event = nostr_nip42_create_auth_event(
|
||||
challenge, relay->url, pool->private_key, 0);
|
||||
if (auth_event) {
|
||||
char* auth_message = nostr_nip42_create_auth_message(auth_event);
|
||||
if (auth_message) {
|
||||
if (nostr_ws_send_text(relay->ws_client, auth_message) >= 0) {
|
||||
relay->auth_state = NOSTR_AUTH_STATE_AUTHENTICATING;
|
||||
}
|
||||
free(auth_message);
|
||||
}
|
||||
cJSON_Delete(auth_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Handle AUTH in `nostr_relay_pool_query_sync()` inline loop
|
||||
|
||||
The [`nostr_relay_pool_query_sync()`](nostr_core/core_relay_pool.c:1107) function has its own inline message parsing loop (lines 1178-1227) that bypasses `process_relay_message()`. Add AUTH handling there too, similar to the pattern above.
|
||||
|
||||
### 7. Handle NOTICE messages
|
||||
|
||||
Add a `NOTICE` handler in [`process_relay_message()`](nostr_core/core_relay_pool.c:912):
|
||||
|
||||
```c
|
||||
} else if (strcmp(msg_type, "NOTICE") == 0) {
|
||||
// Handle NOTICE: ["NOTICE", <message>]
|
||||
if (cJSON_IsArray(parsed) && cJSON_GetArraySize(parsed) >= 2) {
|
||||
cJSON* notice_msg = cJSON_GetArrayItem(parsed, 1);
|
||||
if (cJSON_IsString(notice_msg)) {
|
||||
const char* notice = cJSON_GetStringValue(notice_msg);
|
||||
// Detect auth-required notices
|
||||
if (strstr(notice, "auth-required") != NULL) {
|
||||
relay->auth_state = NOSTR_AUTH_STATE_NONE; // Reset for re-auth
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 8. Handle OK auth-required rejections
|
||||
|
||||
Enhance the existing [`OK` handler](nostr_core/core_relay_pool.c:1048) to detect `auth-required:` prefix in error messages. When detected:
|
||||
- If the pool has a private key and the relay hasn't been authenticated yet, trigger the auth flow
|
||||
- Surface the rejection through the publish callback with the auth-required message
|
||||
|
||||
### 9. Initialize auth fields in `nostr_relay_pool_add_relay()`
|
||||
|
||||
In [`nostr_relay_pool_add_relay()`](nostr_core/core_relay_pool.c:605), initialize the new auth fields:
|
||||
|
||||
```c
|
||||
// Initialize NIP-42 authentication fields
|
||||
relay->auth_state = NOSTR_AUTH_STATE_NONE;
|
||||
memset(relay->auth_challenge, 0, sizeof(relay->auth_challenge));
|
||||
relay->auth_challenge_time = 0;
|
||||
relay->nip42_enabled = pool->nip42_enabled;
|
||||
```
|
||||
|
||||
### 10. Secure cleanup in `nostr_relay_pool_destroy()`
|
||||
|
||||
In [`nostr_relay_pool_destroy()`](nostr_core/core_relay_pool.c:684), zero the private key before freeing:
|
||||
|
||||
```c
|
||||
// Securely zero private key
|
||||
if (pool->has_private_key) {
|
||||
memset(pool->private_key, 0, sizeof(pool->private_key));
|
||||
pool->has_private_key = 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 11. Test file
|
||||
|
||||
Create `tests/nip42_pool_test.c` that:
|
||||
- Creates a pool with auth configured
|
||||
- Verifies `nostr_relay_pool_set_auth()` stores the key
|
||||
- Simulates AUTH challenge parsing logic (unit-level, no live relay needed)
|
||||
- Verifies auth event creation with the pool's private key
|
||||
|
||||
### 12. Documentation updates
|
||||
|
||||
- Update [`POOL_API.md`](POOL_API.md) with `nostr_relay_pool_set_auth()` documentation
|
||||
- Update [`README.md`](README.md) with a relay pool NIP-42 usage example
|
||||
|
||||
## Files Modified
|
||||
|
||||
| File | Change Type | Description |
|
||||
|------|-------------|-------------|
|
||||
| [`nostr_core/core_relay_pool.c`](nostr_core/core_relay_pool.c) | Modified | Add auth fields, AUTH/NOTICE handlers, auth config API |
|
||||
| [`nostr_core/nostr_core.h`](nostr_core/nostr_core.h) | Modified | Add `nostr_relay_pool_set_auth()` declaration |
|
||||
| [`tests/nip42_pool_test.c`](tests/nip42_pool_test.c) | New | NIP-42 pool authentication tests |
|
||||
| [`POOL_API.md`](POOL_API.md) | Modified | Document new auth API |
|
||||
| [`README.md`](README.md) | Modified | Add pool auth usage example |
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
1. **Private key stored in pool, not per-relay**: A single identity authenticates to all relays in the pool. This matches the common use case and mirrors how `core_relays.c` accepts a single `private_key` parameter.
|
||||
|
||||
2. **Auth state tracked per-relay**: Each relay connection tracks its own `nostr_auth_state_t` since different relays may challenge at different times.
|
||||
|
||||
3. **Automatic re-auth on reconnect**: When a relay reconnects (via the existing reconnection logic), the auth state resets to `NOSTR_AUTH_STATE_NONE`, allowing the relay to re-challenge.
|
||||
|
||||
4. **No blocking on auth**: The AUTH response is sent asynchronously. If a relay requires auth before accepting subscriptions, the relay will re-send events after authentication succeeds. The pool does not block waiting for auth confirmation.
|
||||
|
||||
5. **Reuse existing NIP-42 primitives**: All crypto and message formatting uses [`nostr_nip42_create_auth_event()`](nostr_core/nip042.c:26) and [`nostr_nip42_create_auth_message()`](nostr_core/nip042.c:84) — no new crypto code needed.
|
||||
Binary file not shown.
Binary file not shown.
BIN
tests/nip42_pool_test
Executable file
BIN
tests/nip42_pool_test
Executable file
Binary file not shown.
309
tests/nip42_pool_test.c
Normal file
309
tests/nip42_pool_test.c
Normal file
@@ -0,0 +1,309 @@
|
||||
#define _DEFAULT_SOURCE
|
||||
#include "../nostr_core/nostr_core.h"
|
||||
#include "../cjson/cJSON.h"
|
||||
#include "../nostr_websocket/nostr_websocket_tls.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int g_callback_count = 0;
|
||||
static int g_publish_ok = 0;
|
||||
static int g_publish_fail = 0;
|
||||
static int g_auth_required_seen = 0;
|
||||
|
||||
static const char* relay_status_str(nostr_pool_relay_status_t status) {
|
||||
switch (status) {
|
||||
case NOSTR_POOL_RELAY_DISCONNECTED: return "DISCONNECTED";
|
||||
case NOSTR_POOL_RELAY_CONNECTING: return "CONNECTING";
|
||||
case NOSTR_POOL_RELAY_CONNECTED: return "CONNECTED";
|
||||
case NOSTR_POOL_RELAY_ERROR: return "ERROR";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static double now_ms(void) {
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
|
||||
return (double)time(NULL) * 1000.0;
|
||||
}
|
||||
return ts.tv_sec * 1000.0 + ts.tv_nsec / 1000000.0;
|
||||
}
|
||||
|
||||
static void publish_callback(const char* relay_url,
|
||||
const char* event_id,
|
||||
int success,
|
||||
const char* message,
|
||||
void* user_data) {
|
||||
(void)user_data;
|
||||
|
||||
g_callback_count++;
|
||||
if (success) {
|
||||
g_publish_ok++;
|
||||
} else {
|
||||
g_publish_fail++;
|
||||
if (message && strstr(message, "auth-required") != NULL) {
|
||||
g_auth_required_seen++;
|
||||
}
|
||||
}
|
||||
|
||||
printf("[POOL CALLBACK %d] relay=%s event_id=%s success=%d", g_callback_count,
|
||||
relay_url ? relay_url : "(null)", event_id ? event_id : "(null)", success);
|
||||
if (message) {
|
||||
printf(" message=\"%s\"", message);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static cJSON* create_kind4_event(const unsigned char* private_key, int sequence) {
|
||||
if (!private_key) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char content[256];
|
||||
snprintf(content, sizeof(content), "pool-auth-test message #%d at %ld", sequence, (long)time(NULL));
|
||||
|
||||
cJSON* tags = cJSON_CreateArray();
|
||||
if (!tags) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON* p_tag = cJSON_CreateArray();
|
||||
if (!p_tag) {
|
||||
cJSON_Delete(tags);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON_AddItemToArray(p_tag, cJSON_CreateString("p"));
|
||||
cJSON_AddItemToArray(p_tag, cJSON_CreateString("0000000000000000000000000000000000000000000000000000000000000000"));
|
||||
cJSON_AddItemToArray(tags, p_tag);
|
||||
|
||||
cJSON* event = nostr_create_and_sign_event(4, content, tags, private_key, 0);
|
||||
cJSON_Delete(tags);
|
||||
return event;
|
||||
}
|
||||
|
||||
static void run_relay_auth_probe(const char* relay_url) {
|
||||
printf("\n=== Relay AUTH Probe (raw responses) ===\n");
|
||||
|
||||
nostr_ws_client_t* probe = nostr_ws_connect(relay_url);
|
||||
if (!probe) {
|
||||
printf("[AUTH PROBE] connect failed for %s\n", relay_url);
|
||||
return;
|
||||
}
|
||||
|
||||
int auth_seen = 0;
|
||||
int ok_seen = 0;
|
||||
int notice_seen = 0;
|
||||
|
||||
double start = now_ms();
|
||||
while ((now_ms() - start) < 2500.0) {
|
||||
char buffer[8192];
|
||||
int len = nostr_ws_receive(probe, buffer, sizeof(buffer) - 1, 150);
|
||||
if (len <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
buffer[len] = '\0';
|
||||
printf("[AUTH PROBE RAW] %s\n", buffer);
|
||||
|
||||
char* msg_type = NULL;
|
||||
cJSON* parsed = NULL;
|
||||
if (nostr_parse_relay_message(buffer, &msg_type, &parsed) == 0) {
|
||||
if (msg_type) {
|
||||
printf("[AUTH PROBE PARSED] type=%s\n", msg_type);
|
||||
}
|
||||
|
||||
if (msg_type && strcmp(msg_type, "AUTH") == 0 && cJSON_IsArray(parsed) && cJSON_GetArraySize(parsed) >= 2) {
|
||||
cJSON* challenge_json = cJSON_GetArrayItem(parsed, 1);
|
||||
if (cJSON_IsString(challenge_json)) {
|
||||
printf("[AUTH PROBE] challenge=%s\n", cJSON_GetStringValue(challenge_json));
|
||||
}
|
||||
auth_seen++;
|
||||
} else if (msg_type && strcmp(msg_type, "OK") == 0) {
|
||||
ok_seen++;
|
||||
} else if (msg_type && strcmp(msg_type, "NOTICE") == 0) {
|
||||
notice_seen++;
|
||||
}
|
||||
}
|
||||
|
||||
if (msg_type) free(msg_type);
|
||||
if (parsed) cJSON_Delete(parsed);
|
||||
}
|
||||
|
||||
printf("[AUTH PROBE] summary: AUTH=%d OK=%d NOTICE=%d\n", auth_seen, ok_seen, notice_seen);
|
||||
nostr_ws_close(probe);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("=== NIP-42 Relay Pool Publish Test (kind-4 over 5s) ===\n");
|
||||
|
||||
if (nostr_init() != NOSTR_SUCCESS) {
|
||||
printf("FAILED: nostr_init() failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* relay_url = "ws://127.0.0.1:7777";
|
||||
const char* private_key_hex = "91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe";
|
||||
|
||||
unsigned char private_key[32];
|
||||
memset(private_key, 0, sizeof(private_key));
|
||||
if (nostr_hex_to_bytes(private_key_hex, private_key, sizeof(private_key)) != NOSTR_SUCCESS) {
|
||||
printf("FAILED: unable to decode private key hex\n");
|
||||
nostr_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned char public_key[32];
|
||||
char pubkey_hex[65];
|
||||
if (nostr_ec_public_key_from_private_key(private_key, public_key) != NOSTR_SUCCESS) {
|
||||
printf("FAILED: unable to derive public key\n");
|
||||
memset(private_key, 0, sizeof(private_key));
|
||||
nostr_cleanup();
|
||||
return 1;
|
||||
}
|
||||
nostr_bytes_to_hex(public_key, 32, pubkey_hex);
|
||||
printf("Using pubkey: %s\n", pubkey_hex);
|
||||
|
||||
nostr_relay_pool_t* pool = nostr_relay_pool_create(NULL);
|
||||
if (!pool) {
|
||||
printf("FAILED: unable to create relay pool\n");
|
||||
nostr_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (nostr_relay_pool_set_auth(pool, private_key, 1) != NOSTR_SUCCESS) {
|
||||
printf("FAILED: unable to configure relay pool authentication\n");
|
||||
nostr_relay_pool_destroy(pool);
|
||||
nostr_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (nostr_relay_pool_add_relay(pool, relay_url) != NOSTR_SUCCESS) {
|
||||
printf("FAILED: unable to add relay %s\n", relay_url);
|
||||
nostr_relay_pool_destroy(pool);
|
||||
nostr_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
run_relay_auth_probe(relay_url);
|
||||
|
||||
const char* relays[] = { relay_url };
|
||||
int sent_attempts = 0;
|
||||
|
||||
const double test_duration_ms = 5000.0;
|
||||
const double publish_interval_ms = 650.0;
|
||||
double start = now_ms();
|
||||
double next_publish = start;
|
||||
int sequence = 1;
|
||||
|
||||
nostr_pool_relay_status_t last_status = NOSTR_POOL_RELAY_DISCONNECTED;
|
||||
char last_error_snapshot[512] = {0};
|
||||
|
||||
while ((now_ms() - start) < test_duration_ms) {
|
||||
nostr_relay_pool_poll(pool, 100);
|
||||
|
||||
nostr_pool_relay_status_t current_status = nostr_relay_pool_get_relay_status(pool, relay_url);
|
||||
if (current_status != last_status) {
|
||||
printf("[POOL STATUS] %s -> %s\n", relay_status_str(last_status), relay_status_str(current_status));
|
||||
last_status = current_status;
|
||||
}
|
||||
|
||||
const char* last_err_live = nostr_relay_pool_get_relay_last_publish_error(pool, relay_url);
|
||||
if (last_err_live && strcmp(last_err_live, last_error_snapshot) != 0) {
|
||||
strncpy(last_error_snapshot, last_err_live, sizeof(last_error_snapshot) - 1);
|
||||
last_error_snapshot[sizeof(last_error_snapshot) - 1] = '\0';
|
||||
printf("[POOL ERROR] %s\n", last_error_snapshot);
|
||||
}
|
||||
|
||||
double t = now_ms();
|
||||
if (t >= next_publish) {
|
||||
cJSON* event = create_kind4_event(private_key, sequence++);
|
||||
if (!event) {
|
||||
printf("WARN: failed to create signed event, skipping publish\n");
|
||||
next_publish += publish_interval_ms;
|
||||
usleep(100000);
|
||||
continue;
|
||||
}
|
||||
|
||||
int sent = nostr_relay_pool_publish_async(pool, relays, 1, event, publish_callback, NULL);
|
||||
if (sent > 0) {
|
||||
sent_attempts++;
|
||||
}
|
||||
|
||||
printf("[PUBLISH] attempt=%d sent=%d\n", sequence - 1, sent);
|
||||
|
||||
cJSON_Delete(event);
|
||||
next_publish += publish_interval_ms;
|
||||
}
|
||||
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
// Drain callbacks/messages a bit after last publish
|
||||
double drain_start = now_ms();
|
||||
while ((now_ms() - drain_start) < 1500.0) {
|
||||
nostr_relay_pool_poll(pool, 100);
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
const char* last_err = nostr_relay_pool_get_relay_last_publish_error(pool, relay_url);
|
||||
|
||||
cJSON* auth_filter = cJSON_CreateObject();
|
||||
cJSON* auth_kinds = cJSON_CreateArray();
|
||||
cJSON* auth_authors = cJSON_CreateArray();
|
||||
cJSON_AddItemToArray(auth_kinds, cJSON_CreateNumber(22242));
|
||||
cJSON_AddItemToArray(auth_authors, cJSON_CreateString(pubkey_hex));
|
||||
cJSON_AddItemToObject(auth_filter, "kinds", auth_kinds);
|
||||
cJSON_AddItemToObject(auth_filter, "authors", auth_authors);
|
||||
cJSON_AddItemToObject(auth_filter, "limit", cJSON_CreateNumber(20));
|
||||
|
||||
int auth_event_count = 0;
|
||||
cJSON** auth_events = nostr_relay_pool_query_sync(pool, relays, 1, auth_filter, &auth_event_count, 2500);
|
||||
cJSON_Delete(auth_filter);
|
||||
|
||||
printf("\n=== AUTH Events Seen On Relay (kind 22242) ===\n");
|
||||
printf("count=%d\n", auth_event_count);
|
||||
for (int i = 0; i < auth_event_count; i++) {
|
||||
cJSON* id = cJSON_GetObjectItem(auth_events[i], "id");
|
||||
cJSON* created_at = cJSON_GetObjectItem(auth_events[i], "created_at");
|
||||
printf("[AUTH EVENT %d] id=%s created_at=%lld\n",
|
||||
i + 1,
|
||||
(id && cJSON_IsString(id)) ? cJSON_GetStringValue(id) : "(no-id)",
|
||||
(long long)((created_at && cJSON_IsNumber(created_at)) ? cJSON_GetNumberValue(created_at) : 0));
|
||||
}
|
||||
|
||||
printf("\n=== Summary ===\n");
|
||||
printf("Relay: %s\n", relay_url);
|
||||
printf("Publish attempts sent: %d\n", sent_attempts);
|
||||
printf("Callbacks: %d\n", g_callback_count);
|
||||
printf("Accepted: %d\n", g_publish_ok);
|
||||
printf("Rejected/Failed: %d\n", g_publish_fail);
|
||||
printf("auth-required seen in callbacks: %d\n", g_auth_required_seen);
|
||||
printf("Last relay publish error: %s\n", last_err ? last_err : "(none)");
|
||||
|
||||
if (auth_events) {
|
||||
for (int i = 0; i < auth_event_count; i++) {
|
||||
if (auth_events[i]) {
|
||||
cJSON_Delete(auth_events[i]);
|
||||
}
|
||||
}
|
||||
free(auth_events);
|
||||
}
|
||||
|
||||
nostr_relay_pool_destroy(pool);
|
||||
memset(private_key, 0, sizeof(private_key));
|
||||
memset(public_key, 0, sizeof(public_key));
|
||||
nostr_cleanup();
|
||||
|
||||
// Test is considered successful if the loop executed and we attempted publishes.
|
||||
// Auth behavior is diagnosed by callback details and summary output.
|
||||
if (sent_attempts <= 0) {
|
||||
printf("RESULT: FAIL (no publish attempts were sent)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("RESULT: PASS (publish attempts sent; inspect auth behavior in logs above)\n");
|
||||
return 0;
|
||||
}
|
||||
BIN
tests/nip46_test
BIN
tests/nip46_test
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user