From 79ca3c15ce2babbdbce169ea19503023d6cde8f8 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Mon, 8 Jul 2024 12:27:12 +0200 Subject: [PATCH] code cleanup and improved error handling --- examples/ESP32TestNWC/ESP32TestNWC.cpp | 50 ++++---- examples/ESP32TestNip01/ESP32TestNip01.cpp | 142 +++++++++++---------- src/Nip04.cpp | 26 +--- src/Nip04.h | 4 +- src/Nip47.cpp | 78 ++++------- src/Nip47.h | 4 +- src/NostrEvent.cpp | 16 +-- src/NostrEvent.h | 2 +- src/NostrPool.cpp | 20 +-- src/Transport.h | 2 +- src/Utils.cpp | 11 ++ src/Utils.h | 14 +- src/esp32/ESP32Transport.cpp | 20 ++- src/esp32/ESP32Transport.h | 4 +- src/services/NWC.cpp | 23 +--- 15 files changed, 176 insertions(+), 240 deletions(-) diff --git a/examples/ESP32TestNWC/ESP32TestNWC.cpp b/examples/ESP32TestNWC/ESP32TestNWC.cpp index a622868..aa88daa 100644 --- a/examples/ESP32TestNWC/ESP32TestNWC.cpp +++ b/examples/ESP32TestNWC/ESP32TestNWC.cpp @@ -55,29 +55,33 @@ void loop() { } void testNWC() { - transport = nostr::esp32::ESP32Platform::getTransport(); - nwc = new nostr::NWC(transport, NWC_URL); + try{ + transport = nostr::esp32::ESP32Platform::getTransport(); + nwc = new nostr::NWC(transport, NWC_URL); - nwc->getBalance([&](nostr::GetBalanceResponse resp) { Serial.println("[!] Balance: " + String(resp.balance) + " msatoshis"); }, - [](String err, String errMsg) { Serial.println("[!] Error: " + err + " " + errMsg); }); - nwc->getInfo( - [&](nostr::GetInfoResponse resp) { - Serial.println("[!] Alias: " + resp.alias); - Serial.println("[!] Color: " + resp.color); - Serial.println("[!] Pubkey: " + resp.pubkey); - Serial.println("[!] Network: " + resp.network); - Serial.println("[!] Block height: " + String(resp.blockHeight)); - Serial.println("[!] Block hash: " + resp.blockHash); - Serial.println("[!] Methods: "); - for (auto method : resp.methods) { - Serial.println(" " + method); - } - }, - [](String err, String errMsg) { Serial.println("[!] Error: " + err + " " + errMsg); }); + nwc->getBalance([&](nostr::GetBalanceResponse resp) { Serial.println("[!] Balance: " + String(resp.balance) + " msatoshis"); }, + [](String err, String errMsg) { Serial.println("[!] Error: " + err + " " + errMsg); }); + nwc->getInfo( + [&](nostr::GetInfoResponse resp) { + Serial.println("[!] Alias: " + resp.alias); + Serial.println("[!] Color: " + resp.color); + Serial.println("[!] Pubkey: " + resp.pubkey); + Serial.println("[!] Network: " + resp.network); + Serial.println("[!] Block height: " + String(resp.blockHeight)); + Serial.println("[!] Block hash: " + resp.blockHash); + Serial.println("[!] Methods: "); + for (auto method : resp.methods) { + Serial.println(" " + method); + } + }, + [](String err, String errMsg) { Serial.println("[!] Error: " + err + " " + errMsg); }); - NostrString invoice = transport->getInvoiceFromLNAddr(PAYOUT_ADDRESS, PAYOUT_AMOUNT_MSAT, "Arduino NWC test"); - Serial.println("[!] Paying " + String(PAYOUT_AMOUNT_MSAT) + " msats to " + PAYOUT_ADDRESS + " invoice: " + invoice); - nwc->payInvoice( - invoice, PAYOUT_AMOUNT_MSAT, [&](nostr::PayInvoiceResponse resp) { Serial.println("[!] Payment successful"); }, - [](String err, String errMsg) { Serial.println("[!] Error: " + err + " " + errMsg); }); + NostrString invoice = transport->getInvoiceFromLNAddr(PAYOUT_ADDRESS, PAYOUT_AMOUNT_MSAT, "Arduino NWC test"); + Serial.println("[!] Paying " + String(PAYOUT_AMOUNT_MSAT) + " msats to " + PAYOUT_ADDRESS + " invoice: " + invoice); + nwc->payInvoice( + invoice, PAYOUT_AMOUNT_MSAT, [&](nostr::PayInvoiceResponse resp) { Serial.println("[!] Payment successful"); }, + [](String err, String errMsg) { Serial.println("[!] Error: " + err + " " + errMsg); }); + } catch (std::exception &e) { + Serial.println("[!] Exception: " + String(e.what())); + } } \ No newline at end of file diff --git a/examples/ESP32TestNip01/ESP32TestNip01.cpp b/examples/ESP32TestNip01/ESP32TestNip01.cpp index adc40b1..bd8db62 100644 --- a/examples/ESP32TestNip01/ESP32TestNip01.cpp +++ b/examples/ESP32TestNip01/ESP32TestNip01.cpp @@ -61,74 +61,78 @@ void loop() { nostr::Transport *transport; void testNIP01() { - String relay = RELAY; - String privKey = PRIVKEY; - transport = nostr::esp32::ESP32Platform::getTransport(); - - // We need a NostrPool instance that will handle all the communication - nostr::NostrPool *pool = new nostr::NostrPool(transport); - pools.push_back(pool); // NB. we are adding it to this vector since we need to call - // pool->loop() in the main loop to make it work properly - - // Lets subscribe to the relay - String subId = pool->subscribeMany( - {relay}, - { - {// we set the filters here (see - // https://github.com/nostr-protocol/nips/blob/master/01.md#from-client-to-relay-sending-events-and-creating-subscriptions) - {"kinds", {"1"}}, - // {"since",{"1234567890"}}, - // {"until",{"1234567890"}}, - // {"limit",{"10"}}, - {"#t", {"arduinoTest"}}} //, - // You can add another filter here - }, - [&](const String &subId, nostr::SignedNostrEvent *event) { - // Received events callback, we can access the event content with - // event->getContent() Here you should handle the event, for this - // test we will just serialize it and print to console - JsonDocument doc; - JsonArray arr = doc["data"].to(); - event->toSendableEvent(arr); - String json; - serializeJson(arr, json); - - Serial.println("Event received: " + json); - }, - [&](const String &subId, const String &reason) { - // This is the callback that will be called when the subscription is - // closed - Serial.println("Subscription closed: " + reason); - }, - [&](const String &subId) { - // This is the callback that will be called when the subscription is - // EOSE - Serial.println("Subscription EOSE: " + subId); - }); - - // NB. you might want to save the subId somewhere since you are going to - // need it to close the subscription like so: pool.closeSubscription(subId); - - // The pool will start to listen for events immediately after the next - // loop() - - // Lets try to send an event - // First we create an unsigned event - nostr::UnsignedNostrEvent ev(1, "Hello, World!", nostr::Utils::unixTimeSeconds()); - // we can add some tags - ev.getTags()->addTag("t", {"arduinoTest"}); - // then we sign it with our private key and we will get a SignedNostrEvent - // as result - nostr::SignedNostrEvent signEv = ev.sign(privKey); - // We can verify the event signature (this is not necessary here, but it - // might be when dealing with received events) - if (!signEv.verify()) { - Serial.println("Event signature is invalid"); - return; - } else { - Serial.println("Event signature is valid"); + try{ + String relay = RELAY; + String privKey = PRIVKEY; + transport = nostr::esp32::ESP32Platform::getTransport(); + + // We need a NostrPool instance that will handle all the communication + nostr::NostrPool *pool = new nostr::NostrPool(transport); + pools.push_back(pool); // NB. we are adding it to this vector since we need to call + // pool->loop() in the main loop to make it work properly + + // Lets subscribe to the relay + String subId = pool->subscribeMany( + {relay}, + { + {// we set the filters here (see + // https://github.com/nostr-protocol/nips/blob/master/01.md#from-client-to-relay-sending-events-and-creating-subscriptions) + {"kinds", {"1"}}, + // {"since",{"1234567890"}}, + // {"until",{"1234567890"}}, + // {"limit",{"10"}}, + {"#t", {"arduinoTest"}}} //, + // You can add another filter here + }, + [&](const String &subId, nostr::SignedNostrEvent *event) { + // Received events callback, we can access the event content with + // event->getContent() Here you should handle the event, for this + // test we will just serialize it and print to console + JsonDocument doc; + JsonArray arr = doc["data"].to(); + event->toSendableEvent(arr); + String json; + serializeJson(arr, json); + + Serial.println("Event received: " + json); + }, + [&](const String &subId, const String &reason) { + // This is the callback that will be called when the subscription is + // closed + Serial.println("Subscription closed: " + reason); + }, + [&](const String &subId) { + // This is the callback that will be called when the subscription is + // EOSE + Serial.println("Subscription EOSE: " + subId); + }); + + // NB. you might want to save the subId somewhere since you are going to + // need it to close the subscription like so: pool.closeSubscription(subId); + + // The pool will start to listen for events immediately after the next + // loop() + + // Lets try to send an event + // First we create an unsigned event + nostr::UnsignedNostrEvent ev(1, "Hello, World!", nostr::Utils::unixTimeSeconds()); + // we can add some tags + ev.getTags()->addTag("t", {"arduinoTest"}); + // then we sign it with our private key and we will get a SignedNostrEvent + // as result + nostr::SignedNostrEvent signEv = ev.sign(privKey); + // We can verify the event signature (this is not necessary here, but it + // might be when dealing with received events) + if (!signEv.verify()) { + Serial.println("Event signature is invalid"); + return; + } else { + Serial.println("Event signature is valid"); + } + // Now we can send the event + pool->publish({relay}, &signEv); + // The event will be sent in the next loop() call + } catch (const std::exception &e) { + Serial.println("Error: " + String(e.what())); } - // Now we can send the event - pool->publish({relay}, &signEv); - // The event will be sent in the next loop() call } diff --git a/src/Nip04.cpp b/src/Nip04.cpp index 0ce2495..cdff53c 100644 --- a/src/Nip04.cpp +++ b/src/Nip04.cpp @@ -1,34 +1,30 @@ #include "Nip04.h" using namespace nostr; + NostrString Nip04::decrypt(NostrString &privateKeyHex, NostrString &senderPubKeyHex, NostrString content) { int ivParamIndex = NostrString_indexOf(content, "?iv="); + if (ivParamIndex == -1) throw std::invalid_argument("Encrypted string does not contain ?iv= parameter."); + NostrString encryptedMessage = NostrString_substring(content, 0, ivParamIndex); NostrString encryptedMessageHex = NostrString_base64ToHex(encryptedMessage); int encryptedMessageSize = NostrString_length(encryptedMessageHex) / 2; byte encryptedMessageBin[encryptedMessageSize]; NostrString_hexToBytes(encryptedMessageHex, encryptedMessageBin, encryptedMessageSize); - // fromHex(encryptedMessageHex, encryptedMessageBin, - // encryptedMessageSize); - + NostrString iv = NostrString_substring(content, ivParamIndex + 4); - // String iv = content.substring(content.indexOf("?iv=") + 4); - // String ivHex = base64ToHex(iv); NostrString ivHex = NostrString_base64ToHex(iv); int ivSize = 16; byte ivBin[ivSize]; NostrString_hexToBytes(ivHex, ivBin, ivSize); - // fromHex(ivHex, ivBin, ivSize); int byteSize = 32; byte privateKeyBytes[byteSize]; - // fromHex(privateKeyHex, privateKeyBytes, byteSize); NostrString_hexToBytes(privateKeyHex, privateKeyBytes, byteSize); PrivateKey privateKey(privateKeyBytes); byte senderPublicKeyBin[64]; - // fromHex("02" + String(senderPubKeyHex), senderPublicKeyBin, 64); NostrString_hexToBytes("02" + senderPubKeyHex, senderPublicKeyBin, 64); PublicKey senderPublicKey(senderPublicKeyBin); @@ -41,17 +37,14 @@ NostrString Nip04::decrypt(NostrString &privateKeyHex, NostrString &senderPubKey return message; } + NostrString Nip04::encrypt(NostrString &privateKeyHex, NostrString &recipientPubKeyHex, NostrString content) { - // Get shared point - // Create the private key object int byteSize = 32; byte privateKeyBytes[byteSize]; - // fromHex(privateKeyHex, privateKeyBytes, byteSize); NostrString_hexToBytes(privateKeyHex, privateKeyBytes, byteSize); PrivateKey privateKey(privateKeyBytes); byte publicKeyBin[64]; - // fromHex("02" + NostrString(recipientPubKeyHex), publicKeyBin, 64); NostrString_hexToBytes("02" + NostrString(recipientPubKeyHex), publicKeyBin, 64); PublicKey otherDhPublicKey(publicKeyBin); @@ -59,30 +52,23 @@ NostrString Nip04::encrypt(NostrString &privateKeyHex, NostrString &recipientPub privateKey.ecdh(otherDhPublicKey, sharedPointX, false); NostrString sharedPointXHex = NostrString_bytesToHex(sharedPointX, 32); - // Create the initialization vector uint8_t iv[16]; for (int i = 0; i < sizeof(iv); i++) { iv[i] = (uint8_t)Utils::randomInt(0, 255); } NostrString ivHex = toHex(iv, sizeof(iv)); - String ivBase64 = hexToBase64(ivHex); NostrString encryptedMessageHex = encryptData(sharedPointX, iv, content); - // divide the length of the hex string 2 to get the size of the byte - // array, since each byte consists of 2 hexadecimal characters. int encryptedMessageSize = NostrString_length(encryptedMessageHex) / 2; uint8_t encryptedMessage[encryptedMessageSize]; - // fromHex(encryptedMessageHex, encryptedMessage, - // encryptedMessageSize); NostrString_hexToBytes(encryptedMessageHex, encryptedMessage, encryptedMessageSize); String encryptedMessageBase64 = hexToBase64(encryptedMessageHex); encryptedMessageBase64 += "?iv=" + ivBase64; - return encryptedMessageBase64; } @@ -125,5 +111,5 @@ NostrString Nip04::decryptData(byte key[32], byte iv[16], NostrString messageHex AES_init_ctx_iv(&ctx, key, iv); AES_CBC_decrypt_buffer(&ctx, messageBin, sizeof(messageBin)); - return NostrString((char *)messageBin).substring(0, byteSize); + return NostrString_substring(NostrString((char *)messageBin),0, byteSize); } \ No newline at end of file diff --git a/src/Nip04.h b/src/Nip04.h index 7907a2e..05a0814 100644 --- a/src/Nip04.h +++ b/src/Nip04.h @@ -21,5 +21,5 @@ class Nip04 { NostrString decryptData(byte key[32], byte iv[16], NostrString messageHex); }; -} // namespace nostr -#endif // NIP47_H \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/Nip47.cpp b/src/Nip47.cpp index 5868283..7e7ad12 100644 --- a/src/Nip47.cpp +++ b/src/Nip47.cpp @@ -5,15 +5,12 @@ using namespace nostr; SignedNostrEvent Nip47::createEvent(NostrString method, JsonDocument doc) { doc["method"] = method; NostrString contentString; - serializeJson(doc, contentString); + Utils::jsonStringify(doc, &contentString); doc.clear(); - contentString = this->nip04.encrypt(this->userPrivKey, this->servicePubKey, contentString); - UnsignedNostrEvent event(23194, contentString, Utils::unixTimeSeconds()); NostrEventTags *tags = event.getTags(); tags->addTag("p", {this->servicePubKey}); - return event.sign(this->userPrivKey); } @@ -31,10 +28,10 @@ SignedNostrEvent Nip47::makeInvoice(unsigned long amount, NostrString descriptio JsonDocument doc; JsonObject params = doc["params"].to(); params["amount"] = amount; - if (description.length() > 0) { + if (NostrString_length(description) > 0) { params["description"] = description; } - if (descriptionHash.length() > 0) { + if (NostrString_length(descriptionHash) > 0) { params["descriptionHash"] = descriptionHash; } if (expiry > 0) { @@ -77,15 +74,15 @@ SignedNostrEvent Nip47::multiPayInvoice(std::initializer_list invoices) record["invoice"] = invoice.invoice; record["amount"] = invoice.amount; } - return this->createEvent("multi_pay_invoice", doc); } + SignedNostrEvent Nip47::payKeySend(NostrString pubkey, unsigned long amount, NostrString preimage, std::initializer_list tlv) { JsonDocument doc; JsonObject params = doc["params"].to(); params["pubkey"] = pubkey; params["amount"] = amount; - if (preimage.length() > 0) { + if (NostrString_length(preimage) > 0) { params["preimage"] = preimage; } if (tlv.size() > 0) { @@ -96,7 +93,6 @@ SignedNostrEvent Nip47::payKeySend(NostrString pubkey, unsigned long amount, Nos tlvRecord["value"] = record.value; } } - return this->createEvent("pay_key_send", doc); } @@ -108,7 +104,7 @@ SignedNostrEvent Nip47::multiPayKeySend(std::initializer_list keySends) JsonObject record = ks.add(); record["pubkey"] = keySend.pubkey; record["amount"] = keySend.amount; - if (keySend.preimage.length() > 0) { + if (NostrString_length(keySend.preimage) > 0) { record["preimage"] = keySend.preimage; } if (keySend.tlv.size() > 0) { @@ -120,7 +116,6 @@ SignedNostrEvent Nip47::multiPayKeySend(std::initializer_list keySends) } } } - return this->createEvent("multi_pay_key_send", doc); } @@ -140,10 +135,9 @@ SignedNostrEvent Nip47::listTransactions(unsigned long from, unsigned long until params["offset"] = offset; } params["unpaid"] = unpaid; - if (type.length() > 0) { + if (NostrString_length(type) > 0) { params["type"] = type; } - return this->createEvent("list_transactions", doc); } @@ -151,12 +145,10 @@ void Nip47::parseResponse(SignedNostrEvent *response, Nip47Responseverify()) { NostrString content = this->nip04.decrypt(this->userPrivKey, this->servicePubKey, response->getContent()); - JsonDocument doc; - deserializeJson(doc, content); + Utils::jsonParse(&content, &doc); JsonObject error = doc["error"]; if (!error.isNull()) { out.errorCode = error["code"].as(); @@ -172,21 +164,20 @@ void Nip47::parseResponse(SignedNostrEvent *response, Nip47Response &out) { out.errorCode = ""; out.errorMessage = ""; Utils::log("NWC: received multi_pay_invoice response"); if (response->verify()) { NostrString content = this->nip04.decrypt(this->userPrivKey, this->servicePubKey, response->getContent()); - JsonDocument doc; - deserializeJson(doc, content); + Utils::jsonParse(&content, &doc); JsonObject error = doc["error"]; if (!error.isNull()) { out.errorCode = error["code"].as(); @@ -203,23 +194,20 @@ void Nip47::parseResponse(SignedNostrEvent *response, Nip47Response &out) { out.errorCode = ""; out.errorMessage = ""; - Utils::log("NWC: received pay_keysend response"); - if (response->verify()) { NostrString content = this->nip04.decrypt(this->userPrivKey, this->servicePubKey, response->getContent()); - JsonDocument doc; - deserializeJson(doc, content); + Utils::jsonParse(&content, &doc); JsonObject error = doc["error"]; if (!error.isNull()) { out.errorCode = error["code"].as(); @@ -235,22 +223,20 @@ void Nip47::parseResponse(SignedNostrEvent *response, Nip47Response &out) { out.errorCode = ""; out.errorMessage = ""; Utils::log("NWC: received multi_pay_keysend response"); - if (response->verify()) { NostrString content = this->nip04.decrypt(this->userPrivKey, this->servicePubKey, response->getContent()); - JsonDocument doc; - deserializeJson(doc, content); + Utils::jsonParse(&content, &doc); JsonObject error = doc["error"]; if (!error.isNull()) { out.errorCode = error["code"].as(); @@ -267,22 +253,20 @@ void Nip47::parseResponse(SignedNostrEvent *response, Nip47Response &out) { out.errorCode = ""; out.errorMessage = ""; Utils::log("NWC: received make_invoice response"); - if (response->verify()) { NostrString content = this->nip04.decrypt(this->userPrivKey, this->servicePubKey, response->getContent()); - JsonDocument doc; - deserializeJson(doc, content); + Utils::jsonParse(&content, &doc); JsonObject error = doc["error"]; if (!error.isNull()) { out.errorCode = error["code"].as(); @@ -302,35 +286,31 @@ void Nip47::parseResponse(SignedNostrEvent *response, Nip47Response(); out.result.createdAt = data["created_at"].as(); out.result.expiresAt = data["expires_at"].as(); - JsonDocument metadataDoc; JsonObject metadataObject = metadataDoc.as(); for (JsonPair kv : data["metadata"].as()) { metadataObject[kv.key().c_str()] = kv.value().as(); } out.result.metadata = metadataObject; - return; } } } } - if (NostrString_length(out.errorCode) == 0) { out.errorCode = "OTHER"; out.errorMessage = "Invalid Event"; } } + void Nip47::parseResponse(SignedNostrEvent *response, Nip47Response &out) { out.errorCode = ""; out.errorMessage = ""; Utils::log("NWC: received lookup_invoice response"); - if (response->verify()) { NostrString content = this->nip04.decrypt(this->userPrivKey, this->servicePubKey, response->getContent()); - JsonDocument doc; - deserializeJson(doc, content); + Utils::jsonParse(&content, &doc); JsonObject error = doc["error"]; if (!error.isNull()) { out.errorCode = error["code"].as(); @@ -351,35 +331,31 @@ void Nip47::parseResponse(SignedNostrEvent *response, Nip47Response(); out.result.expiresAt = data["expires_at"].as(); out.result.settledAt = data["settled_at"].as(); - JsonDocument metadataDoc; JsonObject metadataObject = metadataDoc.as(); for (JsonPair kv : data["metadata"].as()) { metadataObject[kv.key().c_str()] = kv.value().as(); } out.result.metadata = metadataObject; - return; } } } } - if (NostrString_length(out.errorCode) == 0) { out.errorCode = "OTHER"; out.errorMessage = "Invalid Event"; } } + void Nip47::parseResponse(SignedNostrEvent *response, Nip47Response &out) { out.errorCode = ""; out.errorMessage = ""; Utils::log("NWC: received list_transactions response"); - if (response->verify()) { NostrString content = this->nip04.decrypt(this->userPrivKey, this->servicePubKey, response->getContent()); - JsonDocument doc; - deserializeJson(doc, content); + Utils::jsonParse(&content, &doc); JsonObject error = doc["error"]; if (!error.isNull()) { out.errorCode = error["code"].as(); @@ -402,14 +378,12 @@ void Nip47::parseResponse(SignedNostrEvent *response, Nip47Response(); transaction.expiresAt = kv.value()["expires_at"].as(); transaction.settledAt = kv.value()["settled_at"].as(); - JsonDocument metadataDoc; JsonObject metadataObject = metadataDoc.as(); for (JsonPair kv : kv.value()["metadata"].as()) { metadataObject[kv.key().c_str()] = kv.value().as(); } transaction.metadata = metadataObject; - out.result.transactions.push_back(transaction); } return; @@ -417,21 +391,20 @@ void Nip47::parseResponse(SignedNostrEvent *response, Nip47Response &out) { out.errorCode = ""; out.errorMessage = ""; Utils::log("NWC: received get_balance response"); - if (response->verify()) { NostrString content = this->nip04.decrypt(this->userPrivKey, this->servicePubKey, response->getContent()); JsonDocument doc; - deserializeJson(doc, content); + Utils::jsonParse(&content, &doc); JsonObject error = doc["error"]; if (!error.isNull()) { out.errorCode = error["code"].as(); @@ -447,22 +420,20 @@ void Nip47::parseResponse(SignedNostrEvent *response, Nip47Response &out) { out.errorCode = ""; out.errorMessage = ""; Utils::log("NWC: received get_info response"); - if (response->verify()) { NostrString content = this->nip04.decrypt(this->userPrivKey, this->servicePubKey, response->getContent()); - JsonDocument doc; - deserializeJson(doc, content); + Utils::jsonParse(&content, &doc); JsonObject error = doc["error"]; if (!error.isNull()) { out.errorCode = error["code"].as(); @@ -487,7 +458,6 @@ void Nip47::parseResponse(SignedNostrEvent *response, Nip47Response(); data.add(0); @@ -88,7 +86,7 @@ SignedNostrEvent UnsignedNostrEvent::sign(NostrString privateKeyHex) { data.add(this->content); NostrString message; - serializeJson(doc["data"], message); + Utils::jsonStringify(data, &message); doc.clear(); Utils::log("Compute signature of: " + message); @@ -116,24 +114,15 @@ SignedNostrEvent UnsignedNostrEvent::sign(NostrString privateKeyHex) { bool SignedNostrEvent::verify() const { Serial.println("Verifying event signature"); byte messageBytes[32]; - // fromHex(this->id, messageBytes, 32); NostrString_hexToBytes(this->id, messageBytes, 32); - // Serial.println("Message hash: "+this->id); Utils::log("Message hash: " + this->id); - byte pubeyBytes[32]; NostrString_hexToBytes("02" + this->pubkey, pubeyBytes, 32); - // fromHex("02"+this->pubkey, pubeyBytes, 32); - // Serial.println("Pubkey: "+this->pubkey); Utils::log("Pubkey: " + this->pubkey); - PublicKey pub(pubeyBytes); - byte signatureBytes[64]; - // fromHex(this->signature, signatureBytes, 64); NostrString_hexToBytes(this->signature, signatureBytes, 64); SchnorrSignature signature(signatureBytes); - // Serial.println("Signature: " + this->signature); Utils::log("Signature: " + this->signature); return pub.schnorr_verify(signature, messageBytes); } @@ -159,7 +148,6 @@ SignedNostrEvent::SignedNostrEvent(JsonArray arr) { if (arr[0] != "EVENT") { throw std::runtime_error("Invalid event type"); } - JsonObject obj; if (arr.size() > 2) { obj = arr[2]; @@ -167,13 +155,11 @@ SignedNostrEvent::SignedNostrEvent(JsonArray arr) { } else { obj = arr[1]; } - this->id = obj["id"].as(); this->pubkey = obj["pubkey"].as(); this->created_at = obj["created_at"].as(); this->kind = obj["kind"].as(); NostrEventTags tt = NostrEventTags(); - JsonArray tags = obj["tags"].as(); for (int i = 0; i < tags.size(); i++) { JsonArray tag = tags[i]; diff --git a/src/NostrEvent.h b/src/NostrEvent.h index ddf9637..c2203b8 100644 --- a/src/NostrEvent.h +++ b/src/NostrEvent.h @@ -164,5 +164,5 @@ class SignedNostrEvent : public NostrEvent { : NostrEvent(kind, tags, content, created_at), id(id), pubkey(pubkey), signature(signature), stored(false){}; }; -} // namespace nostr +} #endif \ No newline at end of file diff --git a/src/NostrPool.cpp b/src/NostrPool.cpp index 4dc6383..a299a5c 100644 --- a/src/NostrPool.cpp +++ b/src/NostrPool.cpp @@ -19,10 +19,8 @@ void NostrRelay::processQueue() { } void NostrPool::onEvent(NostrRelay *relay, NostrString message) { - - Utils::log("Received: " + message); JsonDocument doc; - deserializeJson(doc, message); + Utils::jsonParse(&message, &doc); if (doc.size() == 0) { return; } @@ -56,7 +54,6 @@ void NostrPool::onEvent(NostrRelay *relay, NostrString message) { entry.statusCallback(eventId, success, message); } this->eventStatusCallbackEntries.erase(this->eventStatusCallbackEntries.begin() + i); - break; } } @@ -75,6 +72,7 @@ void NostrPool::onEvent(NostrRelay *relay, NostrString message) { sub.eventCallback(subId, &event); } } + doc.clear(); } NostrString NostrPool::subscribeMany(std::initializer_list urls, std::initializer_list>> filters, @@ -102,7 +100,8 @@ NostrString NostrPool::subscribeMany(std::initializer_list urls, st } NostrString json; - serializeJson(req, json); + Utils::jsonStringify(req, &json); + doc.clear(); if (this->subscriptions.find(subId) == this->subscriptions.end()) { // if subscription does not exist, create it @@ -127,18 +126,16 @@ void NostrPool::closeSubscription(NostrString subId) { // if subscription does not exist, ignore return; } - JsonDocument doc; JsonArray req = doc["req"].to(); req.add("CLOSE"); req.add(subId); NostrString json; - serializeJson(req, json); + Utils::jsonStringify(req, &json); + doc.clear(); for (NostrRelay *r : this->relays) { r->send(json); } - doc.clear(); - this->subscriptions.erase(subId); } @@ -155,15 +152,12 @@ NostrRelay *NostrPool::ensureRelay(NostrString url) { Connection *conn = this->transport->connect(url); relay = new NostrRelay(conn, url); this->relays.push_back(relay); - relay->conn->addMessageListener([this, relay](NostrString message) { this->onEvent(relay, message); }); } - return relay; } void NostrPool::disconnectRelay(NostrString url) { - for (int i = 0; i < this->relays.size(); i++) { if (NostrString_equals(this->relays[i]->url, url)) { this->relays[i]->conn->disconnect(); @@ -188,7 +182,7 @@ void NostrPool::publish(std::initializer_list rs, SignedNostrEvent JsonArray ev = doc.add(); event->toSendableEvent(ev); NostrString evJson; - serializeJson(ev, evJson); + Utils::jsonStringify(ev, &evJson); doc.clear(); for (auto &r : rs) { NostrRelay *relay = this->ensureRelay(r); diff --git a/src/Transport.h b/src/Transport.h index a21b51d..9ae924a 100644 --- a/src/Transport.h +++ b/src/Transport.h @@ -23,5 +23,5 @@ class Transport { virtual bool isReady() = 0; virtual void close() = 0; }; -} // namespace nostr +} #endif \ No newline at end of file diff --git a/src/Utils.cpp b/src/Utils.cpp index 27057e4..ac4e5c2 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -59,4 +59,15 @@ NostrString Utils::getPublicKey(NostrString privKeyHex) { NostrString pubKeyHex = pubKey.toString(); pubKeyHex = NostrString_substring(pubKeyHex, 2); return pubKeyHex; +} + +void Utils::jsonParse(const NostrString *json, JsonDocument *doc) { + DeserializationError error = deserializeJson(*doc, *json); + if (error) { + throw std::runtime_error(error.c_str()); + } +} + +void Utils::jsonStringify(JsonVariantConst source, NostrString *json) { + serializeJson(source, *json); } \ No newline at end of file diff --git a/src/Utils.h b/src/Utils.h index be1f0d5..6ebb589 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -2,10 +2,9 @@ #define NOSTR_UTILS_H #include - #include #include - +#include "ArduinoJson.h" #include "Bitcoin.h" #include "NostrString.h" #define NOSTR_DIGEST_SIZE 32 @@ -24,17 +23,12 @@ class Utils { static void setLogger(std::function logger); static void log(const NostrString &message); static long long unixTimeSeconds(); - static NostrString getNewSubscriptionId(); - - /** - * Generate a random 32-byte value between min and max (inclusive) - * - */ static long int randomInt(long int min, long int max); - static NostrString getPublicKey(NostrString privKeyHex); + static void jsonParse(const NostrString *json, JsonDocument *doc); + static void jsonStringify(JsonVariantConst source, NostrString *json); }; -} // namespace nostr +} #endif \ No newline at end of file diff --git a/src/esp32/ESP32Transport.cpp b/src/esp32/ESP32Transport.cpp index c502625..4dcdc5c 100644 --- a/src/esp32/ESP32Transport.cpp +++ b/src/esp32/ESP32Transport.cpp @@ -26,12 +26,12 @@ NostrString esp32::ESP32Transport::getInvoiceFromLNAddr(NostrString addr, unsign return ""; } NostrString status = doc["status"]; - if (status != "OK") { + if (!NostrString_equals(status, "OK")) { Utils::log("LNURLP status not OK"); return ""; } NostrString callback = doc["callback"]; - if (callback == "") { + if (NostrString_length(callback) == 0) { Utils::log("LNURLP callback not found"); return ""; } @@ -58,12 +58,12 @@ NostrString esp32::ESP32Transport::getInvoiceFromLNAddr(NostrString addr, unsign return ""; } NostrString callbackStatus = doc["status"]; - if (callbackStatus != "OK") { + if (!NostrString_equals(callbackStatus, "OK")) { Utils::log("LNURLP callback status not OK"); return ""; } NostrString invoice = doc["pr"]; - if (invoice == "") { + if (NostrString_length(invoice) == 0) { Utils::log("LNURLP invoice not found"); return ""; } @@ -102,7 +102,7 @@ esp32::ESP32Connection::ESP32Connection(ESP32Transport *transport, NostrString u url = NostrString_substring(url, ssl ? 6 : 5); NostrString host = NostrString_substring(url, 0, NostrString_indexOf(url, "/")); NostrString path = NostrString_substring(url, NostrString_indexOf(url, "/")); - if (path.equals("")) + if (NostrString_equals(path, "")) path = "/"; int port = ssl ? 443 : 80; if (NostrString_indexOf(host, ":") != -1) { @@ -128,10 +128,15 @@ esp32::ESP32Connection::ESP32Connection(ESP32Transport *transport, NostrString u break; case WStype_TEXT: { NostrString message = NostrString_fromChars((char *)payload); + Utils::log("Received message: " + message); for (auto &listener : messageListeners) { - listener(message); + try { + listener(message); + } catch (std::exception &e) { + Utils::log(e.what()); + } } - + break; } case WStype_ERROR: @@ -144,6 +149,7 @@ esp32::ESP32Connection::ESP32Connection(ESP32Transport *transport, NostrString u } void esp32::ESP32Connection::send(NostrString message) { + Utils::log("Sending message: " + message); ws.sendTXT((uint8_t *)NostrString_toChars(message), message.length()); } diff --git a/src/esp32/ESP32Transport.h b/src/esp32/ESP32Transport.h index c6399d0..7b070c6 100644 --- a/src/esp32/ESP32Transport.h +++ b/src/esp32/ESP32Transport.h @@ -43,7 +43,7 @@ class ESP32Transport : public Transport { private: std::vector connections; }; -} // namespace esp32 -} // namespace nostr +} +} #endif diff --git a/src/services/NWC.cpp b/src/services/NWC.cpp index 69653c9..99bc010 100644 --- a/src/services/NWC.cpp +++ b/src/services/NWC.cpp @@ -28,7 +28,6 @@ void NWC::close() { void NWC::loop() { this->pool->loop(); - // maintain callbacks for (auto it = this->callbacks.begin(); it != this->callbacks.end();) { if (it->get()->n == 0) { NostrString subId = it->get()->subId; @@ -56,9 +55,7 @@ NostrString NWC::sendEvent(SignedNostrEvent *event) { if (NostrString_equals(it->get()->eventId, eventRef)) { if (it->get()->n > 0) { it->get()->call(&this->nip47, event); - } else { - // Utils::log("NWC: got event but expired"); - } + } it->get()->n--; break; } @@ -77,7 +74,6 @@ NostrString NWC::sendEvent(SignedNostrEvent *event) { void NWC::payInvoice(NostrString invoice, unsigned long amount, std::function onRes, std::function onErr) { SignedNostrEvent ev = this->nip47.payInvoice(invoice, amount); - // NWCResponseCallback callback; std::unique_ptr> callback(new NWCResponseCallback()); callback->onRes = onRes; callback->onErr = onErr; @@ -91,7 +87,6 @@ void NWC::payInvoice(NostrString invoice, unsigned long amount, std::function invoices, std::function onRes, std::function onErr) { SignedNostrEvent ev = this->nip47.multiPayInvoice(invoices); std::unique_ptr> callback(new NWCResponseCallback()); - callback->onRes = onRes; callback->onErr = onErr; callback->timestampSeconds = Utils::unixTimeSeconds(); @@ -104,7 +99,6 @@ void NWC::multiPayInvoice(std::initializer_list invoices, std::function void NWC::payKeySend(NostrString pubkey, unsigned long amount, NostrString preimage, std::initializer_list tlv, std::function onRes, std::function onErr) { SignedNostrEvent ev = this->nip47.payKeySend(pubkey, amount, preimage, tlv); - // NWCResponseCallback callback; std::unique_ptr> callback(new NWCResponseCallback()); callback->onRes = onRes; callback->onErr = onErr; @@ -112,13 +106,11 @@ void NWC::payKeySend(NostrString pubkey, unsigned long amount, NostrString preim callback->eventId = ev.getId(); callback->n = 1; callback->subId = this->sendEvent(&ev); - // this->callbacks.push_back(callback); this->callbacks.push_back(std::move(callback)); } void NWC::multiPayKeySend(std::initializer_list keySends, std::function onRes, std::function onErr) { SignedNostrEvent ev = this->nip47.multiPayKeySend(keySends); - // NWCResponseCallback callback; std::unique_ptr> callback(new NWCResponseCallback()); callback->onRes = onRes; callback->onErr = onErr; @@ -126,7 +118,7 @@ void NWC::multiPayKeySend(std::initializer_list keySends, std::function callback->eventId = ev.getId(); callback->n = keySends.size(); callback->subId = this->sendEvent(&ev); - // this->callbacks.push_back(callback); + this->callbacks.push_back(std::move(callback)); } void NWC::makeInvoice(unsigned long amount, NostrString description, NostrString descriptionHash, unsigned long expiry, std::function onRes, @@ -139,13 +131,11 @@ void NWC::makeInvoice(unsigned long amount, NostrString description, NostrString callback->eventId = ev.getId(); callback->n = 1; callback->subId = this->sendEvent(&ev); - // this->callbacks.push_back(callback); this->callbacks.push_back(std::move(callback)); } void NWC::lookUpPaymentHash(NostrString paymentHash, std::function onRes, std::function onErr) { SignedNostrEvent ev = this->nip47.lookUpPaymentHash(paymentHash); - // NWCResponseCallback callback; std::unique_ptr> callback(new NWCResponseCallback()); callback->onRes = onRes; callback->onErr = onErr; @@ -153,13 +143,11 @@ void NWC::lookUpPaymentHash(NostrString paymentHash, std::functioneventId = ev.getId(); callback->n = 1; callback->subId = this->sendEvent(&ev); - // this->callbacks.push_back(callback); this->callbacks.push_back(std::move(callback)); } void NWC::lookUpInvoice(NostrString invoice, std::function onRes, std::function onErr) { SignedNostrEvent ev = this->nip47.lookUpInvoice(invoice); - // NWCResponseCallback callback; std::unique_ptr> callback(new NWCResponseCallback()); callback->onRes = onRes; callback->onErr = onErr; @@ -167,14 +155,12 @@ void NWC::lookUpInvoice(NostrString invoice, std::functioneventId = ev.getId(); callback->n = 1; callback->subId = this->sendEvent(&ev); - // this->callbacks.push_back(callback); this->callbacks.push_back(std::move(callback)); } void NWC::listTransactions(unsigned long from, unsigned long until, int limit, int offset, bool unpaid, NostrString type, std::function onRes, std::function onErr) { SignedNostrEvent ev = this->nip47.listTransactions(from, until); - // NWCResponseCallback callback; std::unique_ptr> callback(new NWCResponseCallback()); callback->onRes = onRes; callback->onErr = onErr; @@ -182,13 +168,11 @@ void NWC::listTransactions(unsigned long from, unsigned long until, int limit, i callback->eventId = ev.getId(); callback->n = 1; callback->subId = this->sendEvent(&ev); - // this->callbacks.push_back(callback); this->callbacks.push_back(std::move(callback)); } void NWC::getBalance(std::function onRes, std::function onErr) { SignedNostrEvent ev = this->nip47.getBalance(); - // NWCResponseCallback callback; std::unique_ptr> callback(new NWCResponseCallback()); callback->onRes = onRes; callback->onErr = onErr; @@ -196,13 +180,11 @@ void NWC::getBalance(std::function onRes, std::functio callback->eventId = ev.getId(); callback->n = 1; callback->subId = this->sendEvent(&ev); - // this->callbacks.push_back(callback); this->callbacks.push_back(std::move(callback)); } void NWC::getInfo(std::function onRes, std::function onErr) { SignedNostrEvent ev = this->nip47.getInfo(); - // NWCResponseCallback callback; std::unique_ptr> callback(new NWCResponseCallback()); callback->onRes = onRes; callback->onErr = onErr; @@ -210,6 +192,5 @@ void NWC::getInfo(std::function onRes, std::functioneventId = ev.getId(); callback->n = 1; callback->subId = this->sendEvent(&ev); - // this->callbacks.push_back(callback); this->callbacks.push_back(std::move(callback)); } \ No newline at end of file