From 6cf5d6c50e42223e3fd1eb879798af6df031bcda Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 18 Sep 2025 11:33:35 -0400 Subject: [PATCH] First fully working version. --- web/superball-builder.html | 96 ++++++++++++++------------------------ web/superball.html | 80 +++++++++++++++---------------- 2 files changed, 76 insertions(+), 100 deletions(-) diff --git a/web/superball-builder.html b/web/superball-builder.html index 6e969b4..1531710 100644 --- a/web/superball-builder.html +++ b/web/superball-builder.html @@ -362,49 +362,6 @@ return window.NostrTools.getPublicKey(randomKey); } - // Generate actual padding string based on requested bytes - function generatePaddingString(paddingInput) { - console.log('DEBUG: generatePaddingString called with:', paddingInput); - - if (!paddingInput || paddingInput.trim() === '') { - console.log('DEBUG: No padding input, returning null'); - return null; - } - - let cleanInput = paddingInput.trim(); - - // If no +/- prefix, assume it's addition - if (!/^[+-]/.test(cleanInput)) { - cleanInput = '+' + cleanInput; - console.log('DEBUG: Added + prefix, now:', cleanInput); - } - - const match = cleanInput.match(/^([+-])(\d+)$/); - if (!match) { - console.log('DEBUG: Padding format invalid, returning null'); - return null; - } - - const operation = match[1]; // "+" or "-" - const bytes = parseInt(match[2]); - - console.log('DEBUG: Padding operation:', operation, 'bytes:', bytes); - - if (operation === '-') { - // For removal, we'll return the operation and count - console.log('DEBUG: Returning removal padding:', cleanInput); - return cleanInput; // Keep as "-50" format - } else { - // For addition, generate actual padding string - // Use repeating digits: "0123456789012345..." - let paddingStr = ''; - for (let i = 0; i < bytes; i++) { - paddingStr += (i % 10).toString(); - } - console.log('DEBUG: Generated padding string:', paddingStr); - return paddingStr; - } - } // Generate random pubkey for testing (since we don't have real superballs yet) function generateRandomPubkey(bounceId) { @@ -495,8 +452,13 @@
- - + + +
+
+ + + Only used when forwarding to another Superball
@@ -525,6 +487,15 @@ const auditTag = generateAuditTag(); document.getElementById(`audit-tag-${bounceId}`).value = auditTag; + // Hide daemon padding field for final bounce (first one created) + if (bounces.length === 0) { + // This is the final bounce - hide daemon padding field since it makes no sense + const daemonPaddingDiv = document.getElementById(`daemon-padding-${bounceId}`); + if (daemonPaddingDiv) { + daemonPaddingDiv.style.display = 'none'; + } + } + // Update bounce labels to reflect execution order updateBounceLabels(); } @@ -588,7 +559,8 @@ const superballPubkey = document.getElementById(`superball-pubkey-${bounceId}`).value.trim(); const bounceRelays = document.getElementById(`bounce-relays-${bounceId}`).value.split(',').map(r => r.trim()).filter(r => r); const delay = parseInt(document.getElementById(`delay-${bounceId}`).value); - const padding = document.getElementById(`padding-${bounceId}`).value.trim(); + const builderPadding = parseInt(document.getElementById(`padding-${bounceId}`).value) || 0; + const addPaddingBytes = parseInt(document.getElementById(`add-padding-bytes-${bounceId}`).value) || 0; const payment = document.getElementById(`payment-${bounceId}`).value.trim(); // Debug the input values @@ -596,7 +568,8 @@ console.log(' - superballPubkey:', superballPubkey); console.log(' - bounceRelays:', bounceRelays); console.log(' - delay:', delay); - console.log(' - padding:', `"${padding}"`); + console.log(' - builderPadding:', builderPadding); + console.log(' - addPaddingBytes:', addPaddingBytes); console.log(' - payment:', `"${payment}"`); if (!superballPubkey || superballPubkey.length !== 64) { @@ -640,19 +613,10 @@ audit: auditTag }; - // Add optional fields - console.log('DEBUG: Processing padding field:', padding); - if (padding) { - const paddingStr = generatePaddingString(padding); - console.log('DEBUG: Generated padding result:', paddingStr); - if (paddingStr !== null) { - routingInstructions.padding = paddingStr; - console.log('DEBUG: Added padding to routing instructions:', paddingStr); - } else { - console.log('DEBUG: Padding string was null, not adding to routing'); - } - } else { - console.log('DEBUG: No padding field provided'); + // Add daemon instruction to add padding when forwarding (if specified and not final bounce) + if (!isLastBounce && addPaddingBytes > 0) { + routingInstructions.add_padding_bytes = addPaddingBytes; + console.log('DEBUG: Added add_padding_bytes instruction:', addPaddingBytes); } if (payment) { @@ -669,6 +633,18 @@ } // Note: If this IS the last bounce (first one created), no 'p' field means final posting + // Add builder padding to routing instructions (if specified) + if (builderPadding > 0) { + // Generate counting padding string (0123456789012...) + let paddingString = ''; + for (let i = 0; i < builderPadding; i++) { + paddingString += (i % 10).toString(); + } + routingInstructions.padding = paddingString; + + console.log('DEBUG: Added', builderPadding, 'bytes of builder padding to routing instructions'); + } + // Create the payload to encrypt const payload = { event: eventToWrap, diff --git a/web/superball.html b/web/superball.html index f2c1ddd..38fcea8 100644 --- a/web/superball.html +++ b/web/superball.html @@ -899,13 +899,8 @@ 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)}`); + // Log full event without truncation + addLogEntry('info', `Full received event:\n${JSON.stringify(nostrEvent, null, 2)}`); handleIncomingEvent(nostrEvent); } else if (message[0] === 'EOSE' && message[1] === subscriptionId) { addLogEntry('info', `End of stored events from ${relayUrl}`); @@ -953,11 +948,19 @@ const innerEvent = decryptedPayload.event; addLogEntry('info', `Discarding padding: "${decryptedPayload.padding}"`); + // DEBUG: Log the inner event BEFORE attempting second decryption + addLogEntry('info', `INNER EVENT BEFORE SECOND DECRYPTION:`); + addLogEntry('info', ` Inner event ID: ${innerEvent.id}`); + addLogEntry('info', ` Inner event pubkey: ${innerEvent.pubkey}`); + addLogEntry('info', ` Inner event content length: ${innerEvent.content.length}`); + addLogEntry('info', ` Inner event full JSON:\n${JSON.stringify(innerEvent, null, 2)}`); + // Second decryption to get the actual routing instructions that were encrypted for me + // Use the inner event's pubkey, not the outer event's pubkey decryptedPayload = await decryptRoutingEvent(innerEvent); if (!decryptedPayload) { - addLogEntry('error', `Failed to decrypt inner event ${event.id.substring(0,16)}...`); + addLogEntry('error', `Failed to decrypt inner event ${innerEvent.id.substring(0,16)}...`); return; } @@ -966,13 +969,8 @@ 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)}`); + // Log the complete decrypted payload without truncation + addLogEntry('info', `Final routing payload:\n${JSON.stringify(decryptedPayload, null, 2)}`); // Parse routing instructions (these are from the builder, specific to this daemon) const { event: wrappedEvent, routing } = decryptedPayload; @@ -1020,11 +1018,32 @@ // Decrypt routing event using NIP-44 via NIP-07 interface async function decryptRoutingEvent(event) { try { + // DEBUG: Log full decryption attempt details + addLogEntry('info', `DEBUG DECRYPTION ATTEMPT:`); + addLogEntry('info', ` Event ID: ${event.id}`); + addLogEntry('info', ` Event pubkey (who signed): ${event.pubkey}`); + addLogEntry('info', ` My daemon pubkey: ${userPubkey}`); + addLogEntry('info', ` Content length: ${event.content.length}`); + addLogEntry('info', ` Full event JSON:\n${JSON.stringify(event, null, 2)}`); + + // DEBUG: Log exact parameters being passed to NIP-44 decrypt + addLogEntry('info', ` CALLING window.nostr.nip44.decrypt() with:`); + addLogEntry('info', ` pubkey parameter: "${event.pubkey}"`); + addLogEntry('info', ` content parameter: "${event.content}"`); + addLogEntry('info', ` content parameter length: ${event.content.length}`); + addLogEntry('info', ` content parameter type: ${typeof event.content}`); + // Use window.nostr.nip44.decrypt() which handles the private key internally const decrypted = await window.nostr.nip44.decrypt(event.pubkey, event.content); + + addLogEntry('success', ` Decryption successful! Decrypted length: ${decrypted.length}`); + addLogEntry('info', ` Decrypted preview: ${decrypted.substring(0, 100)}...`); + return JSON.parse(decrypted); } catch (error) { + addLogEntry('error', ` Decryption failed: ${error.message}`); + addLogEntry('error', ` Error details: ${JSON.stringify(error, null, 2)}`); console.error('Decryption error:', error); return null; } @@ -1122,17 +1141,8 @@ 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)}`); + // Log the complete padding payload before encryption + addLogEntry('info', `Padding payload to encrypt:\n${JSON.stringify(paddingPayload, null, 2)}`); // Encrypt padding payload to next Superball let ephemeralKeyHex; @@ -1169,13 +1179,8 @@ // 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)}`); + // Log the complete rewrapped event before publishing + addLogEntry('info', `Padding-wrapped event to publish:\n${JSON.stringify(signedEvent, null, 2)}`); // Publish to specified relays await publishToRelays(signedEvent, routing.relays); @@ -1200,13 +1205,8 @@ 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)}`); + // Log full published event + addLogEntry('info', `Full published event:\n${JSON.stringify(event, null, 2)}`); } catch (aggregateError) { const errorMessages = aggregateError.errors.map((err, index) => `${relays[index]}: ${err.message}`