This commit is contained in:
parent
1b28f78f44
commit
ecf248dfc2
121
DAEMON.md
121
DAEMON.md
|
@ -31,48 +31,59 @@ I am Superball - an anonymizing node that provides location privacy for Nostr us
|
|||
- Confirm the `p` tag contains my pubkey
|
||||
- Ensure it's kind 22222
|
||||
|
||||
### 2. Decrypt
|
||||
### 2. Decrypt and Identify Payload Type
|
||||
- Use my private key with NIP-44 to decrypt the content
|
||||
- Extract the payload which contains:
|
||||
```json
|
||||
{
|
||||
"event": { /* The event to forward */ },
|
||||
- Check payload structure to determine type:
|
||||
|
||||
"routing": { /* Superball Instructions */
|
||||
"relays": ["wss://relay1.com", "wss://relay2.com"],
|
||||
"delay": 30,
|
||||
"padding": "+150", // or "-50"
|
||||
"p": "next_superball_pubkey", // Optional - missing means final posting
|
||||
"audit": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456", // Required audit tag
|
||||
"payment": "eCash_token" // Optional
|
||||
}
|
||||
#### Type 1: Routing Payload (Created by Builder)
|
||||
```json
|
||||
{
|
||||
"event": { /* Final event or inner wrapped event */ },
|
||||
"routing": { /* My routing instructions from builder */
|
||||
"relays": ["wss://relay1.com", "wss://relay2.com"],
|
||||
"delay": 30,
|
||||
"p": "next_superball_pubkey", // Optional - missing means final posting
|
||||
"audit": "audit_tag", // Required audit tag
|
||||
"payment": "eCash_token", // Optional
|
||||
"add_padding_bytes": 256 // Optional
|
||||
}
|
||||
```
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Process Routing Instructions
|
||||
#### Type 2: Padding Payload (Created by Previous Daemon)
|
||||
```json
|
||||
{
|
||||
"event": { /* Still-encrypted inner event */ },
|
||||
"padding": "01234567890123" // Padding data to discard
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Handle Payload Type
|
||||
|
||||
#### If Padding Payload:
|
||||
1. **Discard padding** - Ignore padding field completely
|
||||
2. **Decrypt again** - The "event" field contains another encrypted payload
|
||||
3. **Process the inner payload** - This will be a routing payload meant for me
|
||||
|
||||
#### If Routing Payload:
|
||||
1. **Process routing instructions** - These were created by the builder specifically for me
|
||||
2. **Continue with normal processing**
|
||||
|
||||
### 4. Process Routing Instructions
|
||||
|
||||
#### Delay
|
||||
- Wait the specified number of seconds before forwarding
|
||||
- Add random jitter (±10%) to prevent timing analysis
|
||||
- Queue the event for delayed processing
|
||||
|
||||
#### Padding
|
||||
- **Remove padding (`"padding": "-N"`)**: Delete N bytes worth of padding tags from the event
|
||||
- **Add padding (`"padding": "+N"`)**: Create new routing wrapper with N bytes of padding tags
|
||||
|
||||
#### Relays
|
||||
- Post to ALL relays in the `relays` array
|
||||
- Validate all relay URLs are properly formatted
|
||||
- Provides redundancy and availability
|
||||
|
||||
#### Next Hop Logic
|
||||
- **`p` field present**: Create routing event for specified next Superball (can apply padding)
|
||||
- **`p` field missing**: Extract inner event and post directly to relays (end chain, no padding changes)
|
||||
|
||||
#### Padding Logic
|
||||
- **`p` field present + `padding` field**: Apply padding changes when creating routing wrapper
|
||||
- **`p` field missing**: Ignore any `padding` field - cannot modify signed event
|
||||
- **Final hop rule**: Never modify signed events, post exactly as received
|
||||
- **`p` field present**: Forward to next Superball with padding-only wrapper
|
||||
- **`p` field missing**: Post inner event directly to relays (end chain)
|
||||
|
||||
#### Audit Tag Processing
|
||||
- **`audit` field**: Always present - include as `["p", "<audit_tag>"]` in routing event
|
||||
|
@ -83,30 +94,34 @@ I am Superball - an anonymizing node that provides location privacy for Nostr us
|
|||
- **`payment` field present**: Process eCash token for service payment
|
||||
- **`payment` field missing**: Process for free (if daemon allows)
|
||||
|
||||
### 4. Forward Event
|
||||
### 5. Forward Event
|
||||
|
||||
#### Always Rewrap (Important for Privacy)
|
||||
**ALWAYS** create a new routing event to hide whether padding was added or removed:
|
||||
#### Two-Path Processing
|
||||
|
||||
**Path 1: Forward to Next Superball (`p` field present)**
|
||||
- Create padding-only wrapper (never create routing instructions)
|
||||
- Generate fresh ephemeral keypair
|
||||
- Create padding payload:
|
||||
```json
|
||||
{
|
||||
"kind": 22222, // Always use routing event
|
||||
"pubkey": "<my_ephemeral_key>", // Generate fresh ephemeral key
|
||||
"content": "<encrypted_inner_event>", // Re-encrypt with my key
|
||||
"tags": [
|
||||
["p", "<next_hop_or_final_destination>"],
|
||||
["p", "<audit_tag_from_routing>"], // Always include audit tag as p tag
|
||||
["padding", "<random_data_1>"], // Adjusted padding
|
||||
["padding", "<random_data_2>"] // May be more or less than before
|
||||
]
|
||||
"event": { /* The still-encrypted inner event */ },
|
||||
"padding": "random_padding_data_123456789"
|
||||
}
|
||||
```
|
||||
- Encrypt to next Superball's pubkey
|
||||
- Create routing event with next hop's pubkey in p tag
|
||||
|
||||
#### Next Hop Handling
|
||||
- **If `p` field in routing**: Create routing event with that pubkey in p tag
|
||||
- **If no `p` field in routing**: Extract inner event and post directly to all relays
|
||||
- **Multi-relay posting**: Post to every relay in the `relays` array
|
||||
- **End of chain**: When no `p` field, I am the final hop
|
||||
**Path 2: Final Posting (`p` field missing)**
|
||||
- Extract inner event from payload
|
||||
- Post directly to all relays in routing.relays array
|
||||
- No wrapping or encryption needed
|
||||
- End of chain
|
||||
|
||||
#### Critical Rules
|
||||
1. **Daemons NEVER create routing instructions** - Only padding
|
||||
2. **Routing instructions come ONLY from the builder** - Pre-encrypted for each hop
|
||||
3. **Always use fresh ephemeral keys** when forwarding
|
||||
4. **Include audit tag** in routing event p tags for camouflage
|
||||
|
||||
## My Rules
|
||||
|
||||
|
@ -144,11 +159,21 @@ I am Superball - an anonymizing node that provides location privacy for Nostr us
|
|||
|
||||
## Example Processing Flow
|
||||
|
||||
### Single Unwrapping (Routing Payload)
|
||||
1. **Receive**: Kind 22222 event with my pubkey in p tag
|
||||
2. **Decrypt**: Extract inner event + routing instructions
|
||||
3. **Queue**: Schedule for delayed processing (e.g., 30 seconds + jitter)
|
||||
4. **Process**: Apply padding changes and prepare for forwarding
|
||||
5. **Forward**: Post to target relay(s)
|
||||
6. **Clean**: Clear all decrypted data from memory
|
||||
2. **Decrypt**: Get payload with routing instructions
|
||||
3. **Process**: These routing instructions were created for me by builder
|
||||
4. **Forward or Post**: Based on routing.p field
|
||||
|
||||
### Double Unwrapping (Padding Payload)
|
||||
1. **Receive**: Kind 22222 event with my pubkey in p tag
|
||||
2. **First Decrypt**: Get padding payload - discard padding
|
||||
3. **Second Decrypt**: Decrypt the inner event to get my routing instructions
|
||||
4. **Process**: These routing instructions were created for me by builder
|
||||
5. **Forward or Post**: Based on routing.p field
|
||||
|
||||
### Clean Up
|
||||
- **Queue**: Schedule for delayed processing (e.g., 30 seconds + jitter)
|
||||
- **Clean**: Clear all decrypted data from memory after processing
|
||||
|
||||
I am a privacy-preserving relay that helps users post content while hiding their location. I ask no questions, store no logs, and remember nothing about the events that pass through me.
|
|
@ -0,0 +1,420 @@
|
|||
# Ball Structure
|
||||
|
||||
Final Event (What gets posted at the end)
|
||||
Message Content:
|
||||
2 bounce
|
||||
Create Event That Will Be Published Publicly
|
||||
{
|
||||
"kind": 1,
|
||||
"content": "2 bounce",
|
||||
"tags": [],
|
||||
"created_at": 1758196136,
|
||||
"pubkey": "8ff74724ed641b3c28e5a86d7c5cbc49c37638ace8c6c38935860e7a5eedde0e",
|
||||
"id": "2451f908dba9b2281751ecb91df143b83866795d29bbadde60093dd17cd039dd",
|
||||
"sig": "31400031068313ba8338faf852ca718ef3495e393a6ef66a925db6e26ef21dd825fe5beca36ad9a2af7ea87ecd1dbc50550112505b7ee30c19125ae89b610b4c"
|
||||
}
|
||||
🏀 Bounce 2 (Kind 22222 Routing Event)
|
||||
Superball Pubkey:
|
||||
03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25
|
||||
🎲 Random
|
||||
Target Relays (comma separated):
|
||||
wss://relay.laantungir.net
|
||||
Delay (seconds):
|
||||
10
|
||||
Padding (+N or -N bytes):
|
||||
+150 or -50
|
||||
Payment (optional):
|
||||
eCash token or payment info
|
||||
Audit Tag (auto-generated):
|
||||
3413dc76625f59dc88b4ff4c290643110f21208c9f4f99a678a75730fd35cef3
|
||||
Create Bounce 2
|
||||
{
|
||||
"kind": 22222,
|
||||
"content": "Aob4...2at6",
|
||||
"tags": [
|
||||
[
|
||||
"p",
|
||||
"03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"3413dc76625f59dc88b4ff4c290643110f21208c9f4f99a678a75730fd35cef3"
|
||||
]
|
||||
],
|
||||
"created_at": 1758196144,
|
||||
"pubkey": "acb7a5fe35eb69a0f30e58dc790558a86143d5c489749fe9c4db52bdb3f7b310",
|
||||
"id": "00d7536a425b2d955084f920c45c1d5ddf86b5503374daa02922af60f536b953",
|
||||
"sig": "a2f9ad4f85bda8fc6ddbce28b2773e8938a747964dc278b2e89553666f777e9d27cbf37d6c69a30223dd8e2e2ace3155d31711ebb9b0b7cd0c5d1c92f8ecefec"
|
||||
}
|
||||
🔓 Decrypt Content
|
||||
|
||||
|
||||
🔓 Decrypted Payload:
|
||||
|
||||
|
||||
{
|
||||
"event": {
|
||||
"kind": 1,
|
||||
"content": "2 bounce",
|
||||
"tags": [],
|
||||
"created_at": 1758196136,
|
||||
"pubkey": "8ff74724ed641b3c28e5a86d7c5cbc49c37638ace8c6c38935860e7a5eedde0e",
|
||||
"id": "2451f908dba9b2281751ecb91df143b83866795d29bbadde60093dd17cd039dd",
|
||||
"sig": "31400031068313ba8338faf852ca718ef3495e393a6ef66a925db6e26ef21dd825fe5beca36ad9a2af7ea87ecd1dbc50550112505b7ee30c19125ae89b610b4c"
|
||||
},
|
||||
"routing": {
|
||||
"relays": [
|
||||
"wss://relay.laantungir.net"
|
||||
],
|
||||
"delay": 10,
|
||||
"audit": "3413dc76625f59dc88b4ff4c290643110f21208c9f4f99a678a75730fd35cef3"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
🏀 Bounce 1 (Kind 22222 Routing Event)
|
||||
Superball Pubkey:
|
||||
e295a831c9a3ae50da2ca3115f8b13b035805a5ebbbd68d95a1e0caf13bff440
|
||||
🎲 Random
|
||||
Target Relays (comma separated):
|
||||
wss://relay.laantungir.net
|
||||
Delay (seconds):
|
||||
10
|
||||
Padding (+N or -N bytes):
|
||||
+150 or -50
|
||||
Payment (optional):
|
||||
eCash token or payment info
|
||||
Audit Tag (auto-generated):
|
||||
ab35d9f342afd9541dac9679dae7a68d79eb52d01e35743d108ce0b4c56facd7
|
||||
Create Bounce 1
|
||||
{
|
||||
"kind": 22222,
|
||||
"content": "AnMH3c...B3LPiw==",
|
||||
"tags": [
|
||||
[
|
||||
"p",
|
||||
"e295a831c9a3ae50da2ca3115f8b13b035805a5ebbbd68d95a1e0caf13bff440"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"ab35d9f342afd9541dac9679dae7a68d79eb52d01e35743d108ce0b4c56facd7"
|
||||
]
|
||||
],
|
||||
"created_at": 1758196166,
|
||||
"pubkey": "ef4663112f67ab47450605fc33e03f9cf19ddcd4d8ecf83c13bf3ecc9215461d",
|
||||
"id": "1fa0869733fa11781b9130f8392b672e452e5025d7604182f83551542de21517",
|
||||
"sig": "9a0a9deee3971e62deb9ff6974251c8599c4e6fd822bca12f28fe3df1646845fa0537ae5c3764e4227ee76874c233813896ee08a1b11e8b9f6e03192577891e6"
|
||||
}
|
||||
🔓 Decrypt Content
|
||||
|
||||
|
||||
🔓 Decrypted Payload:
|
||||
|
||||
|
||||
{
|
||||
"event": {
|
||||
"kind": 22222,
|
||||
"content": "Aob44...w2at6",
|
||||
"tags": [
|
||||
[
|
||||
"p",
|
||||
"03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"3413dc76625f59dc88b4ff4c290643110f21208c9f4f99a678a75730fd35cef3"
|
||||
]
|
||||
],
|
||||
"created_at": 1758196144,
|
||||
"pubkey": "acb7a5fe35eb69a0f30e58dc790558a86143d5c489749fe9c4db52bdb3f7b310",
|
||||
"id": "00d7536a425b2d955084f920c45c1d5ddf86b5503374daa02922af60f536b953",
|
||||
"sig": "a2f9ad4f85bda8fc6ddbce28b2773e8938a747964dc278b2e89553666f777e9d27cbf37d6c69a30223dd8e2e2ace3155d31711ebb9b0b7cd0c5d1c92f8ecefec"
|
||||
},
|
||||
"routing": {
|
||||
"relays": [
|
||||
"wss://relay.laantungir.net"
|
||||
],
|
||||
"delay": 10,
|
||||
"audit": "ab35d9f342afd9541dac9679dae7a68d79eb52d01e35743d108ce0b4c56facd7",
|
||||
"p": "03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Process at first bounce pubkey:e295a831c9a3ae50da2ca3115f8b13b035805a5ebbbd68d95a1e0caf13bff440
|
||||
|
||||
7:49:44 AM Successfully processed event 1fa0869733fa1178...
|
||||
7:49:44 AM Forwarded event with audit tag ab35d9f342afd954...
|
||||
7:49:44 AM
|
||||
Full published event:
|
||||
{
|
||||
"kind": 22222,
|
||||
"content": "AtcPM...GGA==",
|
||||
"tags": [
|
||||
[
|
||||
"p",
|
||||
"03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"ab35d9f342afd9541dac9679dae7a68d79eb52d01e35743d108ce0b4c56facd7"
|
||||
]
|
||||
],
|
||||
"created_at": 1758196183,
|
||||
"pubkey": "c2ff758233b1099682d99687e096859e4f930ea20e73905756166ffdcab9279c",
|
||||
"id": "843d662891c497365d514b995c4135fb0cf080531cda4cef842d396cf0d1c1db",
|
||||
"sig": "765c822b75fe7772e6c10c96e159bc1e64534571d3cd9e6daf25e5276e2a5efdd6dbb3523db75aa1483ca3b1062d8444145b69d0cd0823579980fe5074f8354b"
|
||||
}
|
||||
7:49:44 AM Published to relays: wss://relay.laantungir.net
|
||||
7:49:43 AM
|
||||
Rewrapped event to publish:
|
||||
{
|
||||
"kind": 22222,
|
||||
"content": "AtcPM...GGA==",
|
||||
"tags": [
|
||||
[
|
||||
"p",
|
||||
"03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"ab35d9f342afd9541dac9679dae7a68d79eb52d01e35743d108ce0b4c56facd7"
|
||||
]
|
||||
],
|
||||
"created_at": 1758196183,
|
||||
"pubkey": "c2ff758233b1099682d99687e096859e4f930ea20e73905756166ffdcab9279c",
|
||||
"id": "843d662891c497365d514b995c4135fb0cf080531cda4cef842d396cf0d1c1db",
|
||||
"sig": "765c822b75fe7772e6c10c96e159bc1e64534571d3cd9e6daf25e5276e2a5efdd6dbb3523db75aa1483ca3b1062d8444145b69d0cd0823579980fe5074f8354b"
|
||||
}
|
||||
7:49:43 AM
|
||||
New payload to encrypt:
|
||||
{
|
||||
"event": {
|
||||
"kind": 22222,
|
||||
"content": "Aob44...w2at6",
|
||||
"tags": [
|
||||
[
|
||||
"p",
|
||||
"03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"3413dc76625f59dc88b4ff4c290643110f21208c9f4f99a678a75730fd35cef3"
|
||||
]
|
||||
],
|
||||
"created_at": 1758196144,
|
||||
"pubkey": "acb7a5fe35eb69a0f30e58dc790558a86143d5c489749fe9c4db52bdb3f7b310",
|
||||
"id": "00d7536a425b2d955084f920c45c1d5ddf86b5503374daa02922af60f536b953",
|
||||
"sig": "a2f9ad4f85bda8fc6ddbce28b2773e8938a747964dc278b2e89553666f777e9d27cbf37d6c69a30223dd8e2e2ace3155d31711ebb9b0b7cd0c5d1c92f8ecefec"
|
||||
},
|
||||
"routing": {
|
||||
"relays": [
|
||||
"wss://relay.laantungir.net"
|
||||
],
|
||||
"delay": 10,
|
||||
"audit": "ab35d9f342afd9541dac9679dae7a68d79eb52d01e35743d108ce0b4c56facd7",
|
||||
"p": "03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25"
|
||||
}
|
||||
}
|
||||
7:49:43 AM DEBUG Rewrapping: newRouting.p = "03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25" (string)
|
||||
7:49:43 AM Forwarding to next Superball: 03f857567fc96b47...
|
||||
7:49:43 AM DEBUG Will FORWARD event
|
||||
7:49:43 AM DEBUG Decision point - routing.p = "03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25" (string)
|
||||
7:49:43 AM Processing event 1fa0869733fa1178...
|
||||
7:49:33 AM Event queued for processing in 10s: 1fa0869733fa1178...
|
||||
7:49:33 AM DEBUG !routing.p: false
|
||||
7:49:33 AM DEBUG routing.p === null: false
|
||||
7:49:33 AM DEBUG routing.p === undefined: false
|
||||
7:49:33 AM DEBUG routing.p = "03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25" (type: string)
|
||||
7:49:33 AM
|
||||
Decrypted payload:
|
||||
{
|
||||
"event": {
|
||||
"kind": 22222,
|
||||
"content": "Aob44...w2at6",
|
||||
"tags": [
|
||||
[
|
||||
"p",
|
||||
"03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"3413dc76625f59dc88b4ff4c290643110f21208c9f4f99a678a75730fd35cef3"
|
||||
]
|
||||
],
|
||||
"created_at": 1758196144,
|
||||
"pubkey": "acb7a5fe35eb69a0f30e58dc790558a86143d5c489749fe9c4db52bdb3f7b310",
|
||||
"id": "00d7536a425b2d955084f920c45c1d5ddf86b5503374daa02922af60f536b953",
|
||||
"sig": "a2f9ad4f85bda8fc6ddbce28b2773e8938a747964dc278b2e89553666f777e9d27cbf37d6c69a30223dd8e2e2ace3155d31711ebb9b0b7cd0c5d1c92f8ecefec"
|
||||
},
|
||||
"routing": {
|
||||
"relays": [
|
||||
"wss://relay.laantungir.net"
|
||||
],
|
||||
"delay": 10,
|
||||
"audit": "ab35d9f342afd9541dac9679dae7a68d79eb52d01e35743d108ce0b4c56facd7",
|
||||
"p": "03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25"
|
||||
}
|
||||
}
|
||||
7:49:33 AM Successfully decrypted routing event 1fa0869733fa1178...
|
||||
7:49:33 AM Received routing event: 1fa0869733fa1178...
|
||||
7:49:33 AM
|
||||
Full received event:
|
||||
{
|
||||
"content": "AnMH3...Piw==",
|
||||
"created_at": 1758196166,
|
||||
"id": "1fa0869733fa11781b9130f8392b672e452e5025d7604182f83551542de21517",
|
||||
"kind": 22222,
|
||||
"pubkey": "ef4663112f67ab47450605fc33e03f9cf19ddcd4d8ecf83c13bf3ecc9215461d",
|
||||
"sig": "9a0a9deee3971e62deb9ff6974251c8599c4e6fd822bca12f28fe3df1646845fa0537ae5c3764e4227ee76874c233813896ee08a1b11e8b9f6e03192577891e6",
|
||||
"tags": [
|
||||
[
|
||||
"p",
|
||||
"e295a831c9a3ae50da2ca3115f8b13b035805a5ebbbd68d95a1e0caf13bff440"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"ab35d9f342afd9541dac9679dae7a68d79eb52d01e35743d108ce0b4c56facd7"
|
||||
]
|
||||
]
|
||||
}
|
||||
7:49:33 AM Received EVENT from wss://relay.laantungir.net: 1fa0869733fa1178...
|
||||
|
||||
|
||||
# Process at second bounce pubkey: 03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25
|
||||
|
||||
7:49:54 AM Forwarded event with audit tag ab35d9f342afd954...
|
||||
7:49:54 AM
|
||||
Full published event:
|
||||
{
|
||||
"kind": 22222,
|
||||
"content": "ArEYH...cAw==",
|
||||
"tags": [
|
||||
[
|
||||
"p",
|
||||
"03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"ab35d9f342afd9541dac9679dae7a68d79eb52d01e35743d108ce0b4c56facd7"
|
||||
]
|
||||
],
|
||||
"created_at": 1758196194,
|
||||
"pubkey": "76c213037af4de471d6af7fa996f0b02882ccf2606e871f49ebb6dccef7f7642",
|
||||
"id": "ddeebcc4091610ec5c7246a06ebfe31f3539492e273c920e9da5e10816b0d0d1",
|
||||
"sig": "a3845cca91cfa79eb184499350b8f1b2486ee65313371a7b9ca61506cd1abcb684673eefecf9b7b0c17e50a70f4976d750310ef393a77cd69cbe923fa351aa37"
|
||||
}
|
||||
7:49:54 AM Published to relays: wss://relay.laantungir.net
|
||||
7:49:54 AM
|
||||
Rewrapped event to publish:
|
||||
{
|
||||
"kind": 22222,
|
||||
"content": "ArEYH...cAw==",
|
||||
"tags": [
|
||||
[
|
||||
"p",
|
||||
"03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"ab35d9f342afd9541dac9679dae7a68d79eb52d01e35743d108ce0b4c56facd7"
|
||||
]
|
||||
],
|
||||
"created_at": 1758196194,
|
||||
"pubkey": "76c213037af4de471d6af7fa996f0b02882ccf2606e871f49ebb6dccef7f7642",
|
||||
"id": "ddeebcc4091610ec5c7246a06ebfe31f3539492e273c920e9da5e10816b0d0d1",
|
||||
"sig": "a3845cca91cfa79eb184499350b8f1b2486ee65313371a7b9ca61506cd1abcb684673eefecf9b7b0c17e50a70f4976d750310ef393a77cd69cbe923fa351aa37"
|
||||
}
|
||||
7:49:54 AM
|
||||
New payload to encrypt:
|
||||
{
|
||||
"event": {
|
||||
"kind": 22222,
|
||||
"content": "Aob44...w2at6",
|
||||
"tags": [
|
||||
[
|
||||
"p",
|
||||
"03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"3413dc76625f59dc88b4ff4c290643110f21208c9f4f99a678a75730fd35cef3"
|
||||
]
|
||||
],
|
||||
"created_at": 1758196144,
|
||||
"pubkey": "acb7a5fe35eb69a0f30e58dc790558a86143d5c489749fe9c4db52bdb3f7b310",
|
||||
"id": "00d7536a425b2d955084f920c45c1d5ddf86b5503374daa02922af60f536b953",
|
||||
"sig": "a2f9ad4f85bda8fc6ddbce28b2773e8938a747964dc278b2e89553666f777e9d27cbf37d6c69a30223dd8e2e2ace3155d31711ebb9b0b7cd0c5d1c92f8ecefec"
|
||||
},
|
||||
"routing": {
|
||||
"relays": [
|
||||
"wss://relay.laantungir.net"
|
||||
],
|
||||
"delay": 10,
|
||||
"audit": "ab35d9f342afd9541dac9679dae7a68d79eb52d01e35743d108ce0b4c56facd7",
|
||||
"p": "03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25"
|
||||
}
|
||||
}
|
||||
7:49:54 AM DEBUG Rewrapping: newRouting.p = "03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25" (string)
|
||||
7:49:54 AM Forwarding to next Superball: 03f857567fc96b47...
|
||||
7:49:54 AM DEBUG Will FORWARD event
|
||||
7:49:54 AM DEBUG Decision point - routing.p = "03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25" (string)
|
||||
7:49:54 AM Processing event 843d662891c49736...
|
||||
7:49:44 AM Event queued for processing in 10s: 843d662891c49736...
|
||||
7:49:44 AM DEBUG !routing.p: false
|
||||
7:49:44 AM DEBUG routing.p === null: false
|
||||
7:49:44 AM DEBUG routing.p === undefined: false
|
||||
7:49:44 AM DEBUG routing.p = "03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25" (type: string)
|
||||
7:49:44 AM
|
||||
Decrypted payload:
|
||||
{
|
||||
"event": {
|
||||
"kind": 22222,
|
||||
"content": "Aob44...w2at6",
|
||||
"tags": [
|
||||
[
|
||||
"p",
|
||||
"03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"3413dc76625f59dc88b4ff4c290643110f21208c9f4f99a678a75730fd35cef3"
|
||||
]
|
||||
],
|
||||
"created_at": 1758196144,
|
||||
"pubkey": "acb7a5fe35eb69a0f30e58dc790558a86143d5c489749fe9c4db52bdb3f7b310",
|
||||
"id": "00d7536a425b2d955084f920c45c1d5ddf86b5503374daa02922af60f536b953",
|
||||
"sig": "a2f9ad4f85bda8fc6ddbce28b2773e8938a747964dc278b2e89553666f777e9d27cbf37d6c69a30223dd8e2e2ace3155d31711ebb9b0b7cd0c5d1c92f8ecefec"
|
||||
},
|
||||
"routing": {
|
||||
"relays": [
|
||||
"wss://relay.laantungir.net"
|
||||
],
|
||||
"delay": 10,
|
||||
"audit": "ab35d9f342afd9541dac9679dae7a68d79eb52d01e35743d108ce0b4c56facd7",
|
||||
"p": "03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25"
|
||||
}
|
||||
}
|
||||
7:49:44 AM Successfully decrypted routing event 843d662891c49736...
|
||||
7:49:44 AM Received routing event: 843d662891c49736...
|
||||
7:49:44 AM
|
||||
Full received event:
|
||||
{
|
||||
"content": "AtcPM...GGA==",
|
||||
"created_at": 1758196183,
|
||||
"id": "843d662891c497365d514b995c4135fb0cf080531cda4cef842d396cf0d1c1db",
|
||||
"kind": 22222,
|
||||
"pubkey": "c2ff758233b1099682d99687e096859e4f930ea20e73905756166ffdcab9279c",
|
||||
"sig": "765c822b75fe7772e6c10c96e159bc1e64534571d3cd9e6daf25e5276e2a5efdd6dbb3523db75aa1483ca3b1062d8444145b69d0cd0823579980fe5074f8354b",
|
||||
"tags": [
|
||||
[
|
||||
"p",
|
||||
"03f857567fc96b47b68632457a818563c53e09aaf0028ac1081450afe3352e25"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"ab35d9f342afd9541dac9679dae7a68d79eb52d01e35743d108ce0b4c56facd7"
|
||||
]
|
||||
]
|
||||
}
|
||||
7:49:44 AM Received EVENT from wss://relay.laantungir.net: 843d662891c49736...
|
|
@ -478,7 +478,7 @@
|
|||
bounceSection.id = `bounce-${bounceId}`;
|
||||
|
||||
bounceSection.innerHTML = `
|
||||
<h2>🏀 Bounce ${bounceId} (Kind 22222 Routing Event)</h2>
|
||||
<h2 id="bounce-${bounceId}-header">🏀 Bounce ${bounceId} (Kind 22222 Routing Event)</h2>
|
||||
<div class="input-group">
|
||||
<label for="superball-pubkey-${bounceId}">Superball Pubkey:</label>
|
||||
<div style="display: flex; gap: 10px;">
|
||||
|
@ -506,7 +506,7 @@
|
|||
<label for="audit-tag-${bounceId}">Audit Tag (auto-generated):</label>
|
||||
<input type="text" id="audit-tag-${bounceId}" readonly style="background: #f5f5f5;">
|
||||
</div>
|
||||
<button onclick="createBounce(${bounceId})">Create Bounce ${bounceId}</button>
|
||||
<button onclick="createBounce(${bounceId})" id="create-bounce-btn-${bounceId}">Create Bounce ${bounceId}</button>
|
||||
|
||||
<div id="bounce-${bounceId}-display" class="json-display"></div>
|
||||
<div style="text-align: right; margin-top: 5px;">
|
||||
|
@ -524,8 +524,37 @@
|
|||
// Automatically generate and fill in the audit tag
|
||||
const auditTag = generateAuditTag();
|
||||
document.getElementById(`audit-tag-${bounceId}`).value = auditTag;
|
||||
|
||||
// Update bounce labels to reflect execution order
|
||||
updateBounceLabels();
|
||||
}
|
||||
|
||||
// Update bounce labels to reflect execution order (newest bounce is Bounce 1, oldest is Bounce N)
|
||||
function updateBounceLabels() {
|
||||
// Get all existing bounce sections
|
||||
const bounceContainer = document.getElementById('bounces-container');
|
||||
const bounceSections = bounceContainer.querySelectorAll('.bounce-section');
|
||||
|
||||
// Update labels in reverse order (newest first gets Bounce 1)
|
||||
bounceSections.forEach((section, index) => {
|
||||
const bounceId = section.id.replace('bounce-', '');
|
||||
const executionOrder = bounceSections.length - index; // Reverse the index
|
||||
|
||||
// Update the header
|
||||
const header = document.getElementById(`bounce-${bounceId}-header`);
|
||||
if (header) {
|
||||
header.textContent = `🏀 Bounce ${executionOrder} (Kind 22222 Routing Event)`;
|
||||
}
|
||||
|
||||
// Update the create button text
|
||||
const createBtn = document.getElementById(`create-bounce-btn-${bounceId}`);
|
||||
if (createBtn) {
|
||||
createBtn.textContent = `Create Bounce ${executionOrder}`;
|
||||
}
|
||||
});
|
||||
|
||||
console.log('INFO: Updated bounce labels for execution order');
|
||||
}
|
||||
|
||||
// Update all timeline absolute times continuously
|
||||
function updateAllTimelineTimes() {
|
||||
|
@ -634,10 +663,11 @@
|
|||
|
||||
// Only add 'p' field if this isn't the last bounce
|
||||
if (!isLastBounce) {
|
||||
// Get the superball pubkey from the previous bounce
|
||||
// Get the superball pubkey from the previous bounce (the next hop in the chain)
|
||||
const prevBounce = bounces[bounces.length - 1];
|
||||
routingInstructions.p = prevBounce.superballPubkey;
|
||||
}
|
||||
// Note: If this IS the last bounce (first one created), no 'p' field means final posting
|
||||
|
||||
// Create the payload to encrypt
|
||||
const payload = {
|
||||
|
@ -712,6 +742,9 @@
|
|||
generateVisualization();
|
||||
|
||||
console.log('SUCCESS', `Bounce ${bounceId} created successfully`);
|
||||
|
||||
// Update bounce labels after creation to reflect execution order
|
||||
updateBounceLabels();
|
||||
|
||||
} catch (error) {
|
||||
console.log('ERROR', `Failed to create bounce ${bounceId}: ${error.message}`);
|
||||
|
@ -779,12 +812,12 @@
|
|||
}
|
||||
|
||||
try {
|
||||
// Get the first (outermost) bounce to publish
|
||||
const firstBounce = bounces[0];
|
||||
const routingEvent = firstBounce.routingEvent;
|
||||
// Get the last (outermost) bounce to publish - the most recently created bounce
|
||||
const outermostBounce = bounces[bounces.length - 1];
|
||||
const routingEvent = outermostBounce.routingEvent;
|
||||
|
||||
// Get relays to publish to - use the first bounce's target relays
|
||||
const targetRelays = firstBounce.payload.routing.relays;
|
||||
// Get relays to publish to - use the outermost bounce's target relays
|
||||
const targetRelays = outermostBounce.payload.routing.relays;
|
||||
|
||||
if (!targetRelays || targetRelays.length === 0) {
|
||||
alert('No target relays configured for the first bounce');
|
||||
|
|
|
@ -898,6 +898,14 @@
|
|||
if (message[0] === 'EVENT' && message[1] === subscriptionId) {
|
||||
const nostrEvent = message[2];
|
||||
addLogEntry('success', `Received EVENT from ${relayUrl}: ${nostrEvent.id.substring(0, 16)}...`);
|
||||
|
||||
// Truncate content for readability
|
||||
const truncatedEvent = { ...nostrEvent };
|
||||
if (truncatedEvent.content && truncatedEvent.content.length > 10) {
|
||||
const content = truncatedEvent.content;
|
||||
truncatedEvent.content = content.substring(0, 5) + '...' + content.substring(content.length - 5);
|
||||
}
|
||||
addLogEntry('info', `Full received event:\n${JSON.stringify(truncatedEvent, null, 2)}`);
|
||||
handleIncomingEvent(nostrEvent);
|
||||
} else if (message[0] === 'EOSE' && message[1] === subscriptionId) {
|
||||
addLogEntry('info', `End of stored events from ${relayUrl}`);
|
||||
|
@ -928,18 +936,59 @@
|
|||
|
||||
try {
|
||||
// Decrypt the event payload
|
||||
const decryptedPayload = await decryptRoutingEvent(event);
|
||||
let decryptedPayload = await decryptRoutingEvent(event);
|
||||
|
||||
if (!decryptedPayload) {
|
||||
addLogEntry('error', `Failed to decrypt event ${event.id.substring(0,16)}...`);
|
||||
return;
|
||||
}
|
||||
|
||||
addLogEntry('success', `Successfully decrypted routing event ${event.id.substring(0,16)}...`);
|
||||
addLogEntry('success', `First decryption successful for event ${event.id.substring(0,16)}...`);
|
||||
|
||||
// Parse routing instructions
|
||||
// Check payload type according to corrected DAEMON.md protocol
|
||||
if (decryptedPayload.padding !== undefined) {
|
||||
addLogEntry('info', `Detected Type 2 (Padding Payload) - discarding padding and performing second decryption`);
|
||||
|
||||
// This is a padding layer from previous daemon - discard padding and decrypt again
|
||||
const innerEvent = decryptedPayload.event;
|
||||
addLogEntry('info', `Discarding padding: "${decryptedPayload.padding}"`);
|
||||
|
||||
// Second decryption to get the actual routing instructions that were encrypted for me
|
||||
decryptedPayload = await decryptRoutingEvent(innerEvent);
|
||||
|
||||
if (!decryptedPayload) {
|
||||
addLogEntry('error', `Failed to decrypt inner event ${event.id.substring(0,16)}...`);
|
||||
return;
|
||||
}
|
||||
|
||||
addLogEntry('success', `Second decryption successful - found my original routing instructions from builder`);
|
||||
} else {
|
||||
addLogEntry('info', `Detected Type 1 (Routing Payload) - processing routing instructions directly`);
|
||||
}
|
||||
|
||||
// Log the complete decrypted payload with truncated content
|
||||
const truncatedPayload = { ...decryptedPayload };
|
||||
if (truncatedPayload.event && truncatedPayload.event.content && truncatedPayload.event.content.length > 10) {
|
||||
const content = truncatedPayload.event.content;
|
||||
truncatedPayload.event.content = content.substring(0, 5) + '...' + content.substring(content.length - 5);
|
||||
}
|
||||
addLogEntry('info', `Final routing payload:\n${JSON.stringify(truncatedPayload, null, 2)}`);
|
||||
|
||||
// Parse routing instructions (these are from the builder, specific to this daemon)
|
||||
const { event: wrappedEvent, routing } = decryptedPayload;
|
||||
|
||||
if (!routing) {
|
||||
addLogEntry('error', `No routing instructions found in final payload for ${event.id.substring(0,16)}...`);
|
||||
return;
|
||||
}
|
||||
|
||||
// DEBUG: Log routing decision
|
||||
addLogEntry('info', `DEBUG routing.p = "${routing.p}" (type: ${typeof routing.p})`);
|
||||
if (routing.add_padding_bytes) {
|
||||
addLogEntry('info', `DEBUG add_padding_bytes = ${routing.add_padding_bytes}`);
|
||||
}
|
||||
addLogEntry('info', `DEBUG Will ${routing.p ? 'FORWARD to next hop' : 'POST FINAL EVENT'}`);
|
||||
|
||||
if (!validateRoutingInstructions(routing)) {
|
||||
addLogEntry('error', `Invalid routing instructions in event ${event.id.substring(0,16)}...`);
|
||||
return;
|
||||
|
@ -990,7 +1039,7 @@
|
|||
return true;
|
||||
}
|
||||
|
||||
// Process a queued event
|
||||
// Process a queued event according to corrected DAEMON.md protocol
|
||||
async function processQueuedEvent(queueItem) {
|
||||
if (!daemonRunning) return;
|
||||
|
||||
|
@ -1001,19 +1050,19 @@
|
|||
try {
|
||||
const { wrappedEvent, routing } = queueItem;
|
||||
|
||||
// Apply padding if specified
|
||||
let eventToForward = { ...wrappedEvent };
|
||||
if (routing.padding) {
|
||||
eventToForward = applyPadding(eventToForward, routing.padding);
|
||||
}
|
||||
|
||||
// Check if this is final posting or continued routing
|
||||
addLogEntry('info', `DEBUG Decision point - routing.p = "${routing.p}" (${typeof routing.p})`);
|
||||
if (routing.add_padding_bytes) {
|
||||
addLogEntry('info', `DEBUG add_padding_bytes = ${routing.add_padding_bytes} (will add padding when forwarding)`);
|
||||
}
|
||||
addLogEntry('info', `DEBUG Will ${routing.p ? 'FORWARD with padding wrapper' : 'POST FINAL EVENT'}`);
|
||||
|
||||
if (routing.p) {
|
||||
// Continue routing to next Superball
|
||||
await forwardToNextSuperball(eventToForward, routing);
|
||||
// Continue routing to next Superball with padding-only wrapper
|
||||
await forwardToNextSuperball(wrappedEvent, routing);
|
||||
} else {
|
||||
// Final posting - post original event directly
|
||||
await postFinalEvent(eventToForward, routing.relays);
|
||||
// Final posting - post the wrapped event directly
|
||||
await postFinalEvent(wrappedEvent, routing.relays);
|
||||
}
|
||||
|
||||
processedEvents++;
|
||||
|
@ -1037,43 +1086,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Apply padding to event
|
||||
// Legacy padding function - no longer used in corrected protocol
|
||||
// Padding is now handled during forwarding with add_padding_bytes
|
||||
function applyPadding(event, paddingInstruction) {
|
||||
if (!paddingInstruction || typeof paddingInstruction !== 'string') return event;
|
||||
|
||||
const match = paddingInstruction.match(/^([+-])(\d+)$/);
|
||||
if (!match) return event;
|
||||
|
||||
const [, operation, amount] = match;
|
||||
const padding = '1'.repeat(parseInt(amount));
|
||||
|
||||
const modifiedEvent = { ...event };
|
||||
|
||||
if (operation === '+') {
|
||||
// Add padding
|
||||
if (!modifiedEvent.tags.find(tag => tag[0] === 'padding')) {
|
||||
modifiedEvent.tags.push(['padding', padding]);
|
||||
}
|
||||
addLogEntry('info', `Added ${amount} bytes of padding`);
|
||||
} else if (operation === '-') {
|
||||
// Remove padding
|
||||
const paddingTagIndex = modifiedEvent.tags.findIndex(tag => tag[0] === 'padding');
|
||||
if (paddingTagIndex !== -1) {
|
||||
const currentPadding = modifiedEvent.tags[paddingTagIndex][1] || '';
|
||||
const newPadding = currentPadding.substring(parseInt(amount));
|
||||
if (newPadding.length > 0) {
|
||||
modifiedEvent.tags[paddingTagIndex][1] = newPadding;
|
||||
} else {
|
||||
modifiedEvent.tags.splice(paddingTagIndex, 1);
|
||||
}
|
||||
}
|
||||
addLogEntry('info', `Removed ${amount} bytes of padding`);
|
||||
}
|
||||
|
||||
return modifiedEvent;
|
||||
// This function is deprecated in the corrected protocol
|
||||
// Padding is now generated during forwarding based on routing.add_padding_bytes
|
||||
addLogEntry('info', 'Legacy padding function called - no action taken (using new protocol)');
|
||||
return event;
|
||||
}
|
||||
|
||||
// Forward event to next Superball (Always Rewrap)
|
||||
// Forward event to next Superball with padding-only wrapper (DAEMON.md corrected protocol)
|
||||
async function forwardToNextSuperball(event, routing) {
|
||||
addLogEntry('info', `Forwarding to next Superball: ${routing.p.substring(0,16)}...`);
|
||||
|
||||
|
@ -1081,54 +1103,84 @@
|
|||
const ephemeralKey = window.NostrTools.generateSecretKey();
|
||||
const ephemeralPubkey = window.NostrTools.getPublicKey(ephemeralKey);
|
||||
|
||||
// Create new encrypted payload
|
||||
const payload = {
|
||||
event: event,
|
||||
routing: {
|
||||
relays: routing.relays,
|
||||
delay: routing.delay,
|
||||
padding: routing.padding,
|
||||
p: routing.p,
|
||||
audit: routing.audit,
|
||||
payment: routing.payment
|
||||
// Generate padding based on add_padding_bytes instruction
|
||||
let paddingData = '';
|
||||
if (routing.add_padding_bytes && routing.add_padding_bytes > 0) {
|
||||
// Create random padding of specified length
|
||||
const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
for (let i = 0; i < routing.add_padding_bytes; i++) {
|
||||
paddingData += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||
}
|
||||
addLogEntry('info', `Generated ${paddingData.length} bytes of padding`);
|
||||
}
|
||||
|
||||
// Create padding-only payload (NEVER create routing instructions - only builder does that)
|
||||
const paddingPayload = {
|
||||
event: event, // This is the still-encrypted inner event
|
||||
padding: paddingData // Padding to discard
|
||||
};
|
||||
|
||||
// Encrypt to next Superball
|
||||
addLogEntry('info', `DEBUG Creating padding payload with ${paddingData.length} bytes of padding`);
|
||||
|
||||
// Log the complete padding payload with truncated content before encryption
|
||||
const truncatedPaddingPayload = { ...paddingPayload };
|
||||
if (truncatedPaddingPayload.event && truncatedPaddingPayload.event.content && truncatedPaddingPayload.event.content.length > 10) {
|
||||
const content = truncatedPaddingPayload.event.content;
|
||||
truncatedPaddingPayload.event.content = content.substring(0, 5) + '...' + content.substring(content.length - 5);
|
||||
}
|
||||
if (truncatedPaddingPayload.padding && truncatedPaddingPayload.padding.length > 10) {
|
||||
const padding = truncatedPaddingPayload.padding;
|
||||
truncatedPaddingPayload.padding = padding.substring(0, 5) + '...' + padding.substring(padding.length - 5);
|
||||
}
|
||||
addLogEntry('info', `Padding payload to encrypt:\n${JSON.stringify(truncatedPaddingPayload, null, 2)}`);
|
||||
|
||||
// Encrypt padding payload to next Superball
|
||||
let ephemeralKeyHex;
|
||||
if (window.NostrTools.utils && window.NostrTools.utils.bytesToHex) {
|
||||
ephemeralKeyHex = window.NostrTools.utils.bytesToHex(ephemeralKey);
|
||||
} else if (window.NostrTools.bytesToHex) {
|
||||
ephemeralKeyHex = window.NostrTools.bytesToHex(ephemeralKey);
|
||||
} else {
|
||||
// Fallback: convert Uint8Array to hex manually
|
||||
ephemeralKeyHex = Array.from(ephemeralKey).map(b => b.toString(16).padStart(2, '0')).join('');
|
||||
}
|
||||
|
||||
const conversationKey = window.NostrTools.nip44.v2.utils.getConversationKey(
|
||||
window.NostrTools.bytesToHex(ephemeralKey),
|
||||
ephemeralKeyHex,
|
||||
routing.p
|
||||
);
|
||||
|
||||
const encryptedContent = window.NostrTools.nip44.v2.encrypt(
|
||||
JSON.stringify(payload),
|
||||
JSON.stringify(paddingPayload),
|
||||
conversationKey
|
||||
);
|
||||
|
||||
// Create new routing event
|
||||
// Create new routing event (forwarding to next hop)
|
||||
const routingEvent = {
|
||||
kind: 22222,
|
||||
content: encryptedContent,
|
||||
tags: [
|
||||
['p', routing.p], // Next Superball
|
||||
['p', routing.audit] // Audit tag (looks like pubkey)
|
||||
['p', routing.audit] // Audit tag (camouflage)
|
||||
],
|
||||
created_at: Math.floor(Date.now() / 1000)
|
||||
};
|
||||
|
||||
// Add padding tag if present
|
||||
const paddingTag = event.tags.find(tag => tag[0] === 'padding');
|
||||
if (paddingTag) {
|
||||
routingEvent.tags.push(['padding', paddingTag[1]]);
|
||||
}
|
||||
|
||||
// Sign with ephemeral key
|
||||
const signedEvent = window.NostrTools.finalizeEvent(routingEvent, ephemeralKey);
|
||||
|
||||
// Log the complete rewrapped event with truncated content before publishing
|
||||
const truncatedSignedEvent = { ...signedEvent };
|
||||
if (truncatedSignedEvent.content && truncatedSignedEvent.content.length > 10) {
|
||||
const content = truncatedSignedEvent.content;
|
||||
truncatedSignedEvent.content = content.substring(0, 5) + '...' + content.substring(content.length - 5);
|
||||
}
|
||||
addLogEntry('info', `Padding-wrapped event to publish:\n${JSON.stringify(truncatedSignedEvent, null, 2)}`);
|
||||
|
||||
// Publish to specified relays
|
||||
await publishToRelays(signedEvent, routing.relays);
|
||||
|
||||
addLogEntry('success', `Forwarded event with audit tag ${routing.audit.substring(0,16)}...`);
|
||||
addLogEntry('success', `Forwarded with padding layer to ${routing.p.substring(0,16)}... (audit: ${routing.audit.substring(0,16)}...)`);
|
||||
}
|
||||
|
||||
// Post final event directly to relays
|
||||
|
@ -1147,6 +1199,14 @@
|
|||
try {
|
||||
await Promise.any(pool.publish(relays, event));
|
||||
addLogEntry('success', `Published to relays: ${relays.join(', ')}`);
|
||||
|
||||
// Truncate content for readability
|
||||
const truncatedEvent = { ...event };
|
||||
if (truncatedEvent.content && truncatedEvent.content.length > 10) {
|
||||
const content = truncatedEvent.content;
|
||||
truncatedEvent.content = content.substring(0, 5) + '...' + content.substring(content.length - 5);
|
||||
}
|
||||
addLogEntry('info', `Full published event:\n${JSON.stringify(truncatedEvent, null, 2)}`);
|
||||
} catch (aggregateError) {
|
||||
const errorMessages = aggregateError.errors.map((err, index) =>
|
||||
`${relays[index]}: ${err.message}`
|
||||
|
@ -1249,9 +1309,11 @@
|
|||
logEntries.slice().reverse().forEach(entry => {
|
||||
const div = document.createElement('div');
|
||||
div.className = `log-entry ${entry.type}`;
|
||||
div.innerHTML = `
|
||||
<span class="log-timestamp">${entry.timestamp}</span> ${entry.message}
|
||||
`;
|
||||
// Handle multiline messages (like JSON) with proper formatting
|
||||
const messageContent = entry.message.includes('\n') ?
|
||||
`<span class="log-timestamp">${entry.timestamp}</span><br><pre style="margin: 5px 0; font-family: monospace; font-size: 11px; white-space: pre-wrap;">${entry.message}</pre>` :
|
||||
`<span class="log-timestamp">${entry.timestamp}</span> ${entry.message}`;
|
||||
div.innerHTML = messageContent;
|
||||
container.appendChild(div);
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue