aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar buttercat1791 <mjjurkoic@gmail.com>2024-05-14 09:43:25 -0500
committerLibravatar buttercat1791 <mjjurkoic@gmail.com>2024-05-14 09:43:25 -0500
commit5ed5b4a3baec273a9552bbb466d727267eeb8f06 (patch)
treedb6a234d55ec5c32b712212c99219d50c18badb5
parent4e558f0c63b0f57196d00c8d1c69139633bfce92 (diff)
Add noscrypt and reorganize namespaces
-rw-r--r--CMakeLists.txt29
-rw-r--r--include/client/web_socket_client.hpp3
-rw-r--r--include/data/data.hpp114
-rw-r--r--include/nostr.hpp130
-rw-r--r--include/signer/signer.hpp31
-rw-r--r--src/client/websocketpp_client.cpp3
-rw-r--r--src/data/event.cpp (renamed from src/event.cpp)7
-rw-r--r--src/data/filters.cpp (renamed from src/filters.cpp)5
-rw-r--r--src/nostr_service.cpp39
-rw-r--r--src/signer/noscrypt_signer.cpp34
-rw-r--r--test/nostr_service_test.cpp135
11 files changed, 319 insertions, 211 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 754b0f9..e8be787 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -8,7 +8,8 @@ include(FetchContent)
# Specify the C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
-SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
get_directory_property(HAS_PARENT PARENT_DIRECTORY)
if(HAS_PARENT)
@@ -45,39 +46,50 @@ FetchContent_Declare(
GIT_TAG v1.0.0
)
FetchContent_Populate(uuid_v4)
-set(uuid_v4_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/_deps/uuid_v4-src/)
-find_path(uuid_v4_INCLUDE_DIR uuid_v4.h)
-include_directories(${uuid_v4_INCLUDE_DIR})
+FetchContent_GetProperties(uuid_v4)
+include_directories(${uuid_v4_SOURCE_DIR})
#======== Configure noscrypt ========#
FetchContent_Declare(
libnoscrypt
GIT_REPOSITORY git@github.com:VnUgE/noscrypt.git
- GIT_TAG 872c49d1925b2576f85ec2587747119e895a675b
+ GIT_TAG d09d9330415d463ca19be9394b02ce11b3366f7e
)
FetchContent_MakeAvailable(libnoscrypt)
+FetchContent_GetProperties(libnoscrypt)
+include_directories(${libnoscrypt_SOURCE_DIR}/include)
+
set_target_properties(noscrypt PROPERTIES CRYPTO_LIB openssl)
#======== Build the project ========#
set(INCLUDE_DIR ./include)
set(CLIENT_INCLUDE_DIR ./include/client)
+set(DATA_INCLUDE_DIR ./include/data)
+set(SIGNER_INCLUDE_DIR ./include/signer)
include_directories(${INCLUDE_DIR})
include_directories(${CLIENT_INCLUDE_DIR})
+include_directories(${DATA_INCLUDE_DIR})
+include_directories(${SIGNER_INCLUDE_DIR})
set(HEADERS
${INCLUDE_DIR}/nostr.hpp
${CLIENT_INCLUDE_DIR}/web_socket_client.hpp
+ ${DATA_INCLUDE_DIR}/data.hpp
+ ${SIGNER_INCLUDE_DIR}/signer.hpp
)
set(SOURCE_DIR ./src)
set(CLIENT_SOURCE_DIR ./src/client)
+set(DATA_SOURCE_DIR ./src/data)
+set(SIGNER_SOURCE_DIR ./src/signer)
set(SOURCES
- ${SOURCE_DIR}/event.cpp
- ${SOURCE_DIR}/filters.cpp
${SOURCE_DIR}/nostr_service.cpp
${CLIENT_SOURCE_DIR}/websocketpp_client.cpp
+ ${DATA_SOURCE_DIR}/event.cpp
+ ${DATA_SOURCE_DIR}/filters.cpp
+ ${SIGNER_SOURCE_DIR}/noscrypt_signer.cpp
)
add_library(aedile ${SOURCES} ${HEADERS})
@@ -87,6 +99,7 @@ target_link_libraries(aedile PRIVATE
OpenSSL::Crypto
plog::plog
websocketpp::websocketpp
+ noscrypt
)
set_target_properties(aedile PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS YES)
@@ -111,12 +124,12 @@ set(TEST_SOURCES
add_executable(aedile_test ${TEST_SOURCES} ${HEADERS})
target_link_libraries(aedile_test PRIVATE
- aedile
GTest::gmock
GTest::gtest
GTest::gtest_main
plog::plog
websocketpp::websocketpp
+ aedile
)
set_target_properties(aedile_test PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS YES)
diff --git a/include/client/web_socket_client.hpp b/include/client/web_socket_client.hpp
index 63fa634..e186e90 100644
--- a/include/client/web_socket_client.hpp
+++ b/include/client/web_socket_client.hpp
@@ -6,6 +6,8 @@
#include <websocketpp/client.hpp>
#include <websocketpp/config/asio_client.hpp>
+namespace nostr
+{
namespace client
{
/**
@@ -72,3 +74,4 @@ public:
virtual void closeConnection(std::string uri) = 0;
};
} // namespace client
+} // namespace nostr
diff --git a/include/data/data.hpp b/include/data/data.hpp
new file mode 100644
index 0000000..46156cd
--- /dev/null
+++ b/include/data/data.hpp
@@ -0,0 +1,114 @@
+#pragma once
+
+#include <chrono>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <nlohmann/json.hpp>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+
+namespace nostr
+{
+namespace data
+{
+/**
+ * @brief A Nostr event.
+ * @remark All data transmitted over the Nostr protocol is encoded in JSON blobs. This struct
+ * is common to every Nostr event kind. The significance of each event is determined by the
+ * `tags` and `content` fields.
+*/
+struct Event
+{
+ std::string id; ///< SHA-256 hash of the event data.
+ std::string pubkey; ///< Public key of the event creator.
+ std::time_t createdAt; ///< Unix timestamp of the event creation.
+ int kind; ///< Event kind.
+ std::vector<std::vector<std::string>> tags; ///< Arbitrary event metadata.
+ std::string content; ///< Event content.
+ std::string sig; ///< Event signature created with the private key of the event creator.
+
+ /**
+ * @brief Serializes the event to a JSON object.
+ * @returns A stringified JSON object representing the event.
+ * @throws `std::invalid_argument` if the event object is invalid.
+ */
+ std::string serialize();
+
+ /**
+ * @brief Deserializes the event from a JSON string.
+ * @param jsonString A stringified JSON object representing the event.
+ * @returns An event instance created from the JSON string.
+ */
+ static Event fromString(std::string jsonString);
+
+ /**
+ * @brief Deserializes the event from a JSON object.
+ * @param j A JSON object representing the event.
+ * @returns An event instance created from the JSON object.
+ */
+ static Event fromJson(nlohmann::json j);
+
+ /**
+ * @brief Compares two events for equality.
+ * @remark Two events are considered equal if they have the same ID, since the ID is uniquely
+ * generated from the event data. If the `id` field is empty for either event, the comparison
+ * function will throw an exception.
+ */
+ bool operator==(const Event& other) const;
+
+private:
+ /**
+ * @brief Validates the event.
+ * @throws `std::invalid_argument` if the event object is invalid.
+ * @remark The `createdAt` field defaults to the present if it is not already set.
+ */
+ void validate();
+
+ /**
+ * @brief Generates an ID for the event.
+ * @param serializedData The serialized JSON string of all of the event data except the ID and
+ * the signature.
+ * @return A valid Nostr event ID.
+ * @remark The ID is a 32-bytes lowercase hex-encoded sha256 of the serialized event data.
+ */
+ std::string generateId(std::string serializedData) const;
+};
+
+/**
+ * @brief A set of filters for querying Nostr relays.
+ * @remark The `limit` field should always be included to keep the response size reasonable. The
+ * `since` field is not required, and the `until` field will default to the present. At least one
+ * of the other fields must be set for a valid filter.
+ */
+struct Filters
+{
+ std::vector<std::string> ids; ///< Event IDs.
+ std::vector<std::string> authors; ///< Event author npubs.
+ std::vector<int> kinds; ///< Kind numbers.
+ std::unordered_map<std::string, std::vector<std::string>> tags; ///< Tag names mapped to lists of tag values.
+ std::time_t since; ///< Unix timestamp. Matching events must be newer than this.
+ std::time_t until; ///< Unix timestamp. Matching events must be older than this.
+ int limit; ///< The maximum number of events the relay should return on the initial query.
+
+ /**
+ * @brief Serializes the filters to a JSON object.
+ * @param subscriptionId A string up to 64 chars in length that is unique per relay connection.
+ * @returns A stringified JSON object representing the filters.
+ * @throws `std::invalid_argument` if the filter object is invalid.
+ * @remarks The Nostr client is responsible for managing subscription IDs. Responses from the
+ * relay will be organized by subscription ID.
+ */
+ std::string serialize(std::string& subscriptionId);
+
+private:
+ /**
+ * @brief Validates the filters.
+ * @throws `std::invalid_argument` if the filter object is invalid.
+ * @remark The `until` field defaults to the present if it is not already set.
+ */
+ void validate();
+};
+} // namespace data
+} // namespace nostr
diff --git a/include/nostr.hpp b/include/nostr.hpp
index e5b29c7..76bacd9 100644
--- a/include/nostr.hpp
+++ b/include/nostr.hpp
@@ -9,8 +9,6 @@
#include <vector>
#include <nlohmann/json.hpp>
-#include <openssl/evp.h>
-#include <openssl/sha.h>
#include <plog/Init.h>
#include <plog/Log.h>
#include <websocketpp/client.hpp>
@@ -18,121 +16,24 @@
#include <uuid_v4.h>
#include "client/web_socket_client.hpp"
+#include "data/data.hpp"
+#include "signer/signer.hpp"
namespace nostr
{
-class ISigner;
class NostrService;
-/**
- * @brief A Nostr event.
- * @remark All data transmitted over the Nostr protocol is encoded in JSON blobs. This struct
- * is common to every Nostr event kind. The significance of each event is determined by the
- * `tags` and `content` fields.
-*/
-struct Event
-{
- std::string id; ///< SHA-256 hash of the event data.
- std::string pubkey; ///< Public key of the event creator.
- std::time_t createdAt; ///< Unix timestamp of the event creation.
- int kind; ///< Event kind.
- std::vector<std::vector<std::string>> tags; ///< Arbitrary event metadata.
- std::string content; ///< Event content.
- std::string sig; ///< Event signature created with the private key of the event creator.
-
- /**
- * @brief Serializes the event to a JSON object.
- * @returns A stringified JSON object representing the event.
- * @throws `std::invalid_argument` if the event object is invalid.
- */
- std::string serialize();
-
- /**
- * @brief Deserializes the event from a JSON string.
- * @param jsonString A stringified JSON object representing the event.
- * @returns An event instance created from the JSON string.
- */
- static Event fromString(std::string jsonString);
-
- /**
- * @brief Deserializes the event from a JSON object.
- * @param j A JSON object representing the event.
- * @returns An event instance created from the JSON object.
- */
- static Event fromJson(nlohmann::json j);
-
- /**
- * @brief Compares two events for equality.
- * @remark Two events are considered equal if they have the same ID, since the ID is uniquely
- * generated from the event data. If the `id` field is empty for either event, the comparison
- * function will throw an exception.
- */
- bool operator==(const Event& other) const;
-
-private:
- /**
- * @brief Validates the event.
- * @throws `std::invalid_argument` if the event object is invalid.
- * @remark The `createdAt` field defaults to the present if it is not already set.
- */
- void validate();
-
- /**
- * @brief Generates an ID for the event.
- * @param serializedData The serialized JSON string of all of the event data except the ID and
- * the signature.
- * @return A valid Nostr event ID.
- * @remark The ID is a 32-bytes lowercase hex-encoded sha256 of the serialized event data.
- */
- std::string generateId(std::string serializedData) const;
-};
-
-/**
- * @brief A set of filters for querying Nostr relays.
- * @remark The `limit` field should always be included to keep the response size reasonable. The
- * `since` field is not required, and the `until` field will default to the present. At least one
- * of the other fields must be set for a valid filter.
- */
-struct Filters
-{
- std::vector<std::string> ids; ///< Event IDs.
- std::vector<std::string> authors; ///< Event author npubs.
- std::vector<int> kinds; ///< Kind numbers.
- std::unordered_map<std::string, std::vector<std::string>> tags; ///< Tag names mapped to lists of tag values.
- std::time_t since; ///< Unix timestamp. Matching events must be newer than this.
- std::time_t until; ///< Unix timestamp. Matching events must be older than this.
- int limit; ///< The maximum number of events the relay should return on the initial query.
-
- /**
- * @brief Serializes the filters to a JSON object.
- * @param subscriptionId A string up to 64 chars in length that is unique per relay connection.
- * @returns A stringified JSON object representing the filters.
- * @throws `std::invalid_argument` if the filter object is invalid.
- * @remarks The Nostr client is responsible for managing subscription IDs. Responses from the
- * relay will be organized by subscription ID.
- */
- std::string serialize(std::string& subscriptionId);
-
-private:
- /**
- * @brief Validates the filters.
- * @throws `std::invalid_argument` if the filter object is invalid.
- * @remark The `until` field defaults to the present if it is not already set.
- */
- void validate();
-};
-
class NostrService
{
public:
NostrService(
std::shared_ptr<plog::IAppender> appender,
std::shared_ptr<client::IWebSocketClient> client,
- std::shared_ptr<ISigner> signer);
+ std::shared_ptr<signer::ISigner> signer);
NostrService(
std::shared_ptr<plog::IAppender> appender,
std::shared_ptr<client::IWebSocketClient> client,
- std::shared_ptr<ISigner> signer,
+ std::shared_ptr<signer::ISigner> signer,
std::vector<std::string> relays);
~NostrService();
@@ -171,7 +72,7 @@ public:
* to which relays the event was published successfully, and to which relays the event failed
* to publish.
*/
- std::tuple<std::vector<std::string>, std::vector<std::string>> publishEvent(std::shared_ptr<Event> event);
+ std::tuple<std::vector<std::string>, std::vector<std::string>> publishEvent(std::shared_ptr<data::Event> event);
/**
* @brief Queries all open relay connections for events matching the given set of filters, and
@@ -185,7 +86,7 @@ public:
* set on the filters in the range 1-64, inclusive. If no valid limit is given, it will be
* defaulted to 16.
*/
- std::vector<std::shared_ptr<Event>> queryRelays(std::shared_ptr<Filters> filters);
+ std::vector<std::shared_ptr<data::Event>> queryRelays(std::shared_ptr<data::Filters> filters);
/**
* @brief Queries all open relay connections for events matching the given set of filters.
@@ -202,8 +103,8 @@ public:
* events, and they will not be accessible via `getNewEvents`.
*/
std::string queryRelays(
- std::shared_ptr<Filters> filters,
- std::function<void(const std::string&, std::shared_ptr<Event>)> eventHandler,
+ std::shared_ptr<data::Filters> filters,
+ std::function<void(const std::string&, std::shared_ptr<data::Event>)> eventHandler,
std::function<void(const std::string&)> eoseHandler,
std::function<void(const std::string&, const std::string&)> closeHandler);
@@ -242,7 +143,7 @@ private:
///< The WebSocket client used to communicate with relays.
std::shared_ptr<client::IWebSocketClient> _client;
///< The signer used to sign Nostr events.
- std::shared_ptr<ISigner> _signer;
+ std::shared_ptr<signer::ISigner> _signer;
///< A mutex to protect the instance properties.
std::mutex _propertyMutex;
@@ -324,7 +225,7 @@ private:
*/
void onSubscriptionMessage(
std::string message,
- std::function<void(const std::string&, std::shared_ptr<Event>)> eventHandler,
+ std::function<void(const std::string&, std::shared_ptr<data::Event>)> eventHandler,
std::function<void(const std::string&)> eoseHandler,
std::function<void(const std::string&, const std::string&)> closeHandler);
@@ -336,15 +237,4 @@ private:
*/
void onAcceptance(std::string message, std::function<void(const bool)> acceptanceHandler);
};
-
-class ISigner
-{
-public:
- /**
- * @brief Signs the given Nostr event.
- * @param event The event to sign.
- * @remark The event's `sig` field will be updated in-place with the signature.
- */
- virtual void sign(std::shared_ptr<Event> event) = 0;
-};
} // namespace nostr
diff --git a/include/signer/signer.hpp b/include/signer/signer.hpp
new file mode 100644
index 0000000..e68df93
--- /dev/null
+++ b/include/signer/signer.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include "data/data.hpp"
+
+namespace nostr
+{
+namespace signer
+{
+/**
+ * @brief An interface for Nostr event signing that implements NIP-46.
+ */
+class ISigner
+{
+public:
+ virtual void receiveConnection(std::string connectionToken) = 0;
+
+ virtual void initiateConnection(
+ std::string relay,
+ std::string name,
+ std::string url,
+ std::string description) = 0;
+
+ /**
+ * @brief Signs the given Nostr event.
+ * @param event The event to sign.
+ * @remark The event's `sig` field will be updated in-place with the signature.
+ */
+ virtual void sign(std::shared_ptr<nostr::data::Event> event) = 0;
+};
+} // namespace signer
+} // namespace nostr
diff --git a/src/client/websocketpp_client.cpp b/src/client/websocketpp_client.cpp
index baae054..9967d74 100644
--- a/src/client/websocketpp_client.cpp
+++ b/src/client/websocketpp_client.cpp
@@ -9,6 +9,8 @@ using std::string;
using std::tuple;
using std::unordered_map;
+namespace nostr
+{
namespace client
{
/**
@@ -127,3 +129,4 @@ private:
};
};
} // namespace client
+} // namespace nostr
diff --git a/src/event.cpp b/src/data/event.cpp
index 703efae..620ee3f 100644
--- a/src/event.cpp
+++ b/src/data/event.cpp
@@ -1,12 +1,12 @@
-#include <ctime>
-
-#include "nostr.hpp"
+#include "data/data.hpp"
using namespace nlohmann;
using namespace std;
namespace nostr
{
+namespace data
+{
string Event::serialize()
{
try
@@ -123,4 +123,5 @@ bool Event::operator==(const Event& other) const
return this->id == other.id;
};
+} // namespace data
} // namespace nostr
diff --git a/src/filters.cpp b/src/data/filters.cpp
index 40596eb..7e1c744 100644
--- a/src/filters.cpp
+++ b/src/data/filters.cpp
@@ -1,10 +1,12 @@
-#include "nostr.hpp"
+#include "data/data.hpp"
using namespace nlohmann;
using namespace std;
namespace nostr
{
+namespace data
+{
string Filters::serialize(string& subscriptionId)
{
try
@@ -64,4 +66,5 @@ void Filters::validate()
throw invalid_argument("Filters::validate: At least one filter must be set.");
}
};
+} // namespace data
} // namespace nostr
diff --git a/src/nostr_service.cpp b/src/nostr_service.cpp
index 664243f..9f6b8ce 100644
--- a/src/nostr_service.cpp
+++ b/src/nostr_service.cpp
@@ -1,5 +1,4 @@
#include "nostr.hpp"
-#include "client/web_socket_client.hpp"
using namespace nlohmann;
using namespace std;
@@ -9,13 +8,13 @@ namespace nostr
NostrService::NostrService(
shared_ptr<plog::IAppender> appender,
shared_ptr<client::IWebSocketClient> client,
- shared_ptr<ISigner> signer)
+ shared_ptr<signer::ISigner> signer)
: NostrService(appender, client, signer, {}) { };
NostrService::NostrService(
shared_ptr<plog::IAppender> appender,
shared_ptr<client::IWebSocketClient> client,
- shared_ptr<ISigner> signer,
+ shared_ptr<signer::ISigner> signer,
vector<string> relays)
: _defaultRelays(relays), _client(client), _signer(signer)
{
@@ -58,8 +57,8 @@ vector<string> NostrService::openRelayConnections(vector<string> relays)
connectionThread.join();
}
- size_t targetCount = relays.size();
- size_t activeCount = this->_activeRelays.size();
+ std::size_t targetCount = relays.size();
+ std::size_t activeCount = this->_activeRelays.size();
PLOG_INFO << "Connected to " << activeCount << "/" << targetCount << " target relays.";
// This property should only contain successful relays at this point.
@@ -102,7 +101,7 @@ void NostrService::closeRelayConnections(vector<string> relays)
};
// TODO: Make this method return a promise.
-tuple<vector<string>, vector<string>> NostrService::publishEvent(shared_ptr<Event> event)
+tuple<vector<string>, vector<string>> NostrService::publishEvent(shared_ptr<data::Event> event)
{
vector<string> successfulRelays;
vector<string> failedRelays;
@@ -174,8 +173,8 @@ tuple<vector<string>, vector<string>> NostrService::publishEvent(shared_ptr<Even
}
}
- size_t targetCount = targetRelays.size();
- size_t successfulCount = successfulRelays.size();
+ std::size_t targetCount = targetRelays.size();
+ std::size_t successfulCount = successfulRelays.size();
PLOG_INFO << "Published event to " << successfulCount << "/" << targetCount << " target relays.";
return make_tuple(successfulRelays, failedRelays);
@@ -183,7 +182,7 @@ tuple<vector<string>, vector<string>> NostrService::publishEvent(shared_ptr<Even
// TODO: Make this method return a promise.
// TODO: Add a timeout to this method to prevent hanging while waiting for the relay.
-vector<shared_ptr<Event>> NostrService::queryRelays(shared_ptr<Filters> filters)
+vector<shared_ptr<data::Event>> NostrService::queryRelays(shared_ptr<data::Filters> filters)
{
if (filters->limit > 64 || filters->limit < 1)
{
@@ -191,7 +190,7 @@ vector<shared_ptr<Event>> NostrService::queryRelays(shared_ptr<Filters> filters)
filters->limit = 16;
}
- vector<shared_ptr<Event>> events;
+ vector<shared_ptr<data::Event>> events;
string subscriptionId = this->generateSubscriptionId();
string request;
@@ -229,7 +228,7 @@ vector<shared_ptr<Event>> NostrService::queryRelays(shared_ptr<Filters> filters)
{
this->onSubscriptionMessage(
payload,
- [&events](const string&, shared_ptr<Event> event)
+ [&events](const string&, shared_ptr<data::Event> event)
{
events.push_back(event);
},
@@ -278,8 +277,8 @@ vector<shared_ptr<Event>> NostrService::queryRelays(shared_ptr<Filters> filters)
};
string NostrService::queryRelays(
- shared_ptr<Filters> filters,
- function<void(const string&, shared_ptr<Event>)> eventHandler,
+ shared_ptr<data::Filters> filters,
+ function<void(const string&, shared_ptr<data::Event>)> eventHandler,
function<void(const string&)> eoseHandler,
function<void(const string&, const string&)> closeHandler)
{
@@ -322,8 +321,8 @@ string NostrService::queryRelays(
}
}
- size_t targetCount = this->_activeRelays.size();
- size_t successfulCount = successfulRelays.size();
+ std::size_t targetCount = this->_activeRelays.size();
+ std::size_t successfulCount = successfulRelays.size();
PLOG_INFO << "Sent query to " << successfulCount << "/" << targetCount << " open relay connections.";
return subscriptionId;
@@ -335,7 +334,7 @@ tuple<vector<string>, vector<string>> NostrService::closeSubscription(string sub
vector<string> failedRelays;
vector<string> subscriptionRelays;
- size_t subscriptionRelayCount;
+ std::size_t subscriptionRelayCount;
vector<future<tuple<string, bool>>> closeFutures;
try
@@ -375,7 +374,7 @@ tuple<vector<string>, vector<string>> NostrService::closeSubscription(string sub
}
}
- size_t successfulCount = successfulRelays.size();
+ std::size_t successfulCount = successfulRelays.size();
PLOG_INFO << "Sent CLOSE request for subscription " << subscriptionId << " to " << successfulCount << "/" << subscriptionRelayCount << " open relay connections.";
// If there were no failures, and the subscription has been closed on all of its relays, forget
@@ -596,7 +595,7 @@ bool NostrService::hasSubscription(string subscriptionId, string relay)
void NostrService::onSubscriptionMessage(
string message,
- function<void(const string&, shared_ptr<Event>)> eventHandler,
+ function<void(const string&, shared_ptr<data::Event>)> eventHandler,
function<void(const string&)> eoseHandler,
function<void(const string&, const string&)> closeHandler)
{
@@ -607,8 +606,8 @@ void NostrService::onSubscriptionMessage(
if (messageType == "EVENT")
{
string subscriptionId = jMessage.at(1);
- Event event = Event::fromString(jMessage.at(2));
- eventHandler(subscriptionId, make_shared<Event>(event));
+ data::Event event = data::Event::fromString(jMessage.at(2));
+ eventHandler(subscriptionId, make_shared<data::Event>(event));
}
else if (messageType == "EOSE")
{
diff --git a/src/signer/noscrypt_signer.cpp b/src/signer/noscrypt_signer.cpp
new file mode 100644
index 0000000..a798271
--- /dev/null
+++ b/src/signer/noscrypt_signer.cpp
@@ -0,0 +1,34 @@
+#include <noscrypt.h>
+
+#include "signer.hpp"
+
+using namespace std;
+
+namespace nostr
+{
+namespace signer
+{
+class NoscryptSigner : public ISigner
+{
+public:
+ void receiveConnection(string connectionToken) override
+ {
+ // Receive the connection token here.
+ };
+
+ void initiateConnection(
+ string relay,
+ string name,
+ string url,
+ string description) override
+ {
+ // Initiate the connection here.
+ };
+
+ void sign(shared_ptr<data::Event> event) override
+ {
+ // Sign the event here.
+ };
+};
+} // namespace signer
+} // namespace nostr
diff --git a/test/nostr_service_test.cpp b/test/nostr_service_test.cpp
index b3b9b28..3e951ad 100644
--- a/test/nostr_service_test.cpp
+++ b/test/nostr_service_test.cpp
@@ -1,15 +1,18 @@
#include <chrono>
+#include <iostream>
+
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <nlohmann/json.hpp>
#include <plog/Appenders/ConsoleAppender.h>
#include <plog/Formatters/TxtFormatter.h>
-#include <iostream>
#include <websocketpp/client.hpp>
-#include <client/web_socket_client.hpp>
-#include <nostr.hpp>
+#include "nostr.hpp"
+#include "client/web_socket_client.hpp"
+#include "signer/signer.hpp"
+using namespace nostr;
using namespace std;
using namespace ::testing;
@@ -29,13 +32,27 @@ public:
MOCK_METHOD(void, closeConnection, (string uri), (override));
};
-class FakeSigner : public nostr::ISigner
+class FakeSigner : public signer::ISigner
{
public:
- void sign(shared_ptr<nostr::Event> event) override
+ void receiveConnection(string connectionToken) override
+ {
+ // Do nothing.
+ };
+
+ void initiateConnection(
+ string relay,
+ string name,
+ string url,
+ string description) override
+ {
+ // Do nothing.
+ };
+
+ void sign(shared_ptr<nostr::data::Event> event) override
{
event->sig = "fake_signature";
- }
+ };
};
class NostrServiceTest : public testing::Test
@@ -47,9 +64,9 @@ public:
"wss://nostr.thesamecat.io"
};
- static const nostr::Event getTextNoteTestEvent()
+ static const nostr::data::Event getTextNoteTestEvent()
{
- nostr::Event event;
+ nostr::data::Event event;
event.pubkey = "13tn5ccv2guflxgffq4aj0hw5x39pz70zcdrfd6vym887gry38zys28dask";
event.kind = 1;
event.tags =
@@ -63,12 +80,12 @@ public:
return event;
};
- static const vector<nostr::Event> getMultipleTextNoteTestEvents()
+ static const vector<nostr::data::Event> getMultipleTextNoteTestEvents()
{
auto now = std::chrono::system_clock::now();
std::time_t currentTime = std::chrono::system_clock::to_time_t(now);
- nostr::Event event1;
+ nostr::data::Event event1;
event1.pubkey = "13tn5ccv2guflxgffq4aj0hw5x39pz70zcdrfd6vym887gry38zys28dask";
event1.kind = 1;
event1.tags =
@@ -80,7 +97,7 @@ public:
event1.content = "Hello, World!";
event1.createdAt = currentTime;
- nostr::Event event2;
+ nostr::data::Event event2;
event2.pubkey = "1l9d9jh67rkwayalrxcy686aujyz5pper5kzjv8jvg8pu9v9ns4ls0xvq42";
event2.kind = 1;
event2.tags =
@@ -92,7 +109,7 @@ public:
event2.content = "Welcome to Nostr!";
event2.createdAt = currentTime;
- nostr::Event event3;
+ nostr::data::Event event3;
event3.pubkey = "187ujhtmnv82ftg03h4heetwk3dd9mlfkf8th3fvmrk20nxk9mansuzuyla";
event3.kind = 1;
event3.tags =
@@ -107,9 +124,9 @@ public:
return { event1, event2, event3 };
};
- static const nostr::Event getLongFormTestEvent()
+ static const nostr::data::Event getLongFormTestEvent()
{
- nostr::Event event;
+ nostr::data::Event event;
event.pubkey = "13tn5ccv2guflxgffq4aj0hw5x39pz70zcdrfd6vym887gry38zys28dask";
event.kind = 30023;
event.tags =
@@ -123,7 +140,7 @@ public:
return event;
}
- static const string getTestEventMessage(shared_ptr<nostr::Event> event, string subscriptionId)
+ static const string getTestEventMessage(shared_ptr<nostr::data::Event> event, string subscriptionId)
{
auto signer = make_unique<FakeSigner>();
signer->sign(event);
@@ -136,9 +153,9 @@ public:
return jarr.dump();
}
- static const nostr::Filters getKind0And1TestFilters()
+ static const nostr::data::Filters getKind0And1TestFilters()
{
- nostr::Filters filters;
+ nostr::data::Filters filters;
filters.authors = {
"13tn5ccv2guflxgffq4aj0hw5x39pz70zcdrfd6vym887gry38zys28dask",
"1l9d9jh67rkwayalrxcy686aujyz5pper5kzjv8jvg8pu9v9ns4ls0xvq42",
@@ -150,9 +167,9 @@ public:
return filters;
}
- static const nostr::Filters getKind30023TestFilters()
+ static const nostr::data::Filters getKind30023TestFilters()
{
- nostr::Filters filters;
+ nostr::data::Filters filters;
filters.authors = {
"13tn5ccv2guflxgffq4aj0hw5x39pz70zcdrfd6vym887gry38zys28dask",
"1l9d9jh67rkwayalrxcy686aujyz5pper5kzjv8jvg8pu9v9ns4ls0xvq42",
@@ -461,7 +478,7 @@ TEST_F(NostrServiceTest, PublishEvent_CorrectlyIndicates_AllSuccesses)
.WillRepeatedly(Invoke([](string message, string uri, function<void(const string&)> messageHandler)
{
json messageArr = json::parse(message);
- auto event = nostr::Event::fromString(messageArr[1]);
+ auto event = nostr::data::Event::fromString(messageArr[1]);
json jarr = json::array({ "OK", event.id, true, "Event accepted" });
messageHandler(jarr.dump());
@@ -469,7 +486,7 @@ TEST_F(NostrServiceTest, PublishEvent_CorrectlyIndicates_AllSuccesses)
return make_tuple(uri, true);
}));
- auto testEvent = make_shared<nostr::Event>(getTextNoteTestEvent());
+ auto testEvent = make_shared<nostr::data::Event>(getTextNoteTestEvent());
auto [successes, failures] = nostrService->publishEvent(testEvent);
ASSERT_EQ(successes.size(), defaultTestRelays.size());
@@ -515,7 +532,7 @@ TEST_F(NostrServiceTest, PublishEvent_CorrectlyIndicates_AllFailures)
return make_tuple(uri, false);
}));
- auto testEvent = make_shared<nostr::Event>(getTextNoteTestEvent());
+ auto testEvent = make_shared<nostr::data::Event>(getTextNoteTestEvent());
auto [successes, failures] = nostrService->publishEvent(testEvent);
ASSERT_EQ(successes.size(), 0);
@@ -566,7 +583,7 @@ TEST_F(NostrServiceTest, PublishEvent_CorrectlyIndicates_MixedSuccessesAndFailur
.WillRepeatedly(Invoke([](string message, string uri, function<void(const string&)> messageHandler)
{
json messageArr = json::parse(message);
- auto event = nostr::Event::fromString(messageArr[1]);
+ auto event = nostr::data::Event::fromString(messageArr[1]);
json jarr = json::array({ "OK", event.id, true, "Event accepted" });
messageHandler(jarr.dump());
@@ -574,7 +591,7 @@ TEST_F(NostrServiceTest, PublishEvent_CorrectlyIndicates_MixedSuccessesAndFailur
return make_tuple(uri, true);
}));
- auto testEvent = make_shared<nostr::Event>(getTextNoteTestEvent());
+ auto testEvent = make_shared<nostr::data::Event>(getTextNoteTestEvent());
auto [successes, failures] = nostrService->publishEvent(testEvent);
ASSERT_EQ(successes.size(), 1);
@@ -616,7 +633,7 @@ TEST_F(NostrServiceTest, PublishEvent_CorrectlyIndicates_RejectedEvent)
.WillRepeatedly(Invoke([](string message, string uri, function<void(const string&)> messageHandler)
{
json messageArr = json::parse(message);
- auto event = nostr::Event::fromString(messageArr[1]);
+ auto event = nostr::data::Event::fromString(messageArr[1]);
json jarr = json::array({ "OK", event.id, false, "Event rejected" });
messageHandler(jarr.dump());
@@ -624,7 +641,7 @@ TEST_F(NostrServiceTest, PublishEvent_CorrectlyIndicates_RejectedEvent)
return make_tuple(uri, true);
}));
- auto testEvent = make_shared<nostr::Event>(getTextNoteTestEvent());
+ auto testEvent = make_shared<nostr::data::Event>(getTextNoteTestEvent());
auto [successes, failures] = nostrService->publishEvent(testEvent);
ASSERT_EQ(failures.size(), defaultTestRelays.size());
@@ -667,7 +684,7 @@ TEST_F(NostrServiceTest, PublishEvent_CorrectlyIndicates_EventRejectedBySomeRela
.WillRepeatedly(Invoke([](string message, string uri, function<void(const string&)> messageHandler)
{
json messageArr = json::parse(message);
- auto event = nostr::Event::fromString(messageArr[1]);
+ auto event = nostr::data::Event::fromString(messageArr[1]);
json jarr = json::array({ "OK", event.id, true, "Event accepted" });
messageHandler(jarr.dump());
@@ -679,7 +696,7 @@ TEST_F(NostrServiceTest, PublishEvent_CorrectlyIndicates_EventRejectedBySomeRela
.WillRepeatedly(Invoke([](string message, string uri, function<void(const string&)> messageHandler)
{
json messageArr = json::parse(message);
- auto event = nostr::Event::fromString(messageArr[1]);
+ auto event = nostr::data::Event::fromString(messageArr[1]);
json jarr = json::array({ "OK", event.id, false, "Event rejected" });
messageHandler(jarr.dump());
@@ -687,7 +704,7 @@ TEST_F(NostrServiceTest, PublishEvent_CorrectlyIndicates_EventRejectedBySomeRela
return make_tuple(uri, true);
}));
- auto testEvent = make_shared<nostr::Event>(getTextNoteTestEvent());
+ auto testEvent = make_shared<nostr::data::Event>(getTextNoteTestEvent());
auto [successes, failures] = nostrService->publishEvent(testEvent);
ASSERT_EQ(successes.size(), 1);
@@ -725,16 +742,16 @@ TEST_F(NostrServiceTest, QueryRelays_ReturnsEvents_UpToEOSE)
nostrService->openRelayConnections();
auto testEvents = getMultipleTextNoteTestEvents();
- vector<shared_ptr<nostr::Event>> signedTestEvents;
- for (nostr::Event testEvent : testEvents)
+ vector<shared_ptr<nostr::data::Event>> signedTestEvents;
+ for (nostr::data::Event testEvent : testEvents)
{
- auto signedEvent = make_shared<nostr::Event>(testEvent);
+ auto signedEvent = make_shared<nostr::data::Event>(testEvent);
signer->sign(signedEvent);
auto serializedEvent = signedEvent->serialize();
- auto deserializedEvent = nostr::Event::fromString(serializedEvent);
+ auto deserializedEvent = nostr::data::Event::fromString(serializedEvent);
- signedEvent = make_shared<nostr::Event>(deserializedEvent);
+ signedEvent = make_shared<nostr::data::Event>(deserializedEvent);
signedTestEvents.push_back(signedEvent);
}
@@ -751,7 +768,7 @@ TEST_F(NostrServiceTest, QueryRelays_ReturnsEvents_UpToEOSE)
for (auto event : testEvents)
{
- auto sendableEvent = make_shared<nostr::Event>(event);
+ auto sendableEvent = make_shared<nostr::data::Event>(event);
signer->sign(sendableEvent);
json jarr = json::array({ "EVENT", subscriptionId, sendableEvent->serialize() });
messageHandler(jarr.dump());
@@ -770,7 +787,7 @@ TEST_F(NostrServiceTest, QueryRelays_ReturnsEvents_UpToEOSE)
return make_tuple(uri, true);
}));
- auto filters = make_shared<nostr::Filters>(getKind0And1TestFilters());
+ auto filters = make_shared<nostr::data::Filters>(getKind0And1TestFilters());
auto results = nostrService->queryRelays(filters);
// TODO: Check results size when the queryRelays method deduplicates results before returning.
@@ -783,7 +800,7 @@ TEST_F(NostrServiceTest, QueryRelays_ReturnsEvents_UpToEOSE)
find_if(
signedTestEvents.begin(),
signedTestEvents.end(),
- [&resultEvent](shared_ptr<nostr::Event> testEvent)
+ [&resultEvent](shared_ptr<nostr::data::Event> testEvent)
{
return *testEvent == *resultEvent;
}),
@@ -822,16 +839,16 @@ TEST_F(NostrServiceTest, QueryRelays_CallsHandler_WithReturnedEvents)
nostrService->openRelayConnections();
auto testEvents = getMultipleTextNoteTestEvents();
- vector<shared_ptr<nostr::Event>> signedTestEvents;
- for (nostr::Event testEvent : testEvents)
+ vector<shared_ptr<nostr::data::Event>> signedTestEvents;
+ for (nostr::data::Event testEvent : testEvents)
{
- auto signedEvent = make_shared<nostr::Event>(testEvent);
+ auto signedEvent = make_shared<nostr::data::Event>(testEvent);
signer->sign(signedEvent);
auto serializedEvent = signedEvent->serialize();
- auto deserializedEvent = nostr::Event::fromString(serializedEvent);
+ auto deserializedEvent = nostr::data::Event::fromString(serializedEvent);
- signedEvent = make_shared<nostr::Event>(deserializedEvent);
+ signedEvent = make_shared<nostr::data::Event>(deserializedEvent);
signedTestEvents.push_back(signedEvent);
}
@@ -847,7 +864,7 @@ TEST_F(NostrServiceTest, QueryRelays_CallsHandler_WithReturnedEvents)
for (auto event : testEvents)
{
- auto sendableEvent = make_shared<nostr::Event>(event);
+ auto sendableEvent = make_shared<nostr::data::Event>(event);
signer->sign(sendableEvent);
json jarr = json::array({ "EVENT", subscriptionId, sendableEvent->serialize() });
messageHandler(jarr.dump());
@@ -859,21 +876,21 @@ TEST_F(NostrServiceTest, QueryRelays_CallsHandler_WithReturnedEvents)
return make_tuple(uri, true);
}));
- auto filters = make_shared<nostr::Filters>(getKind0And1TestFilters());
+ auto filters = make_shared<nostr::data::Filters>(getKind0And1TestFilters());
promise<void> eoseReceivedPromise;
auto eoseReceivedFuture = eoseReceivedPromise.get_future();
int eoseCount = 0;
string generatedSubscriptionId = nostrService->queryRelays(
filters,
- [&generatedSubscriptionId, &signedTestEvents](const string& subscriptionId, shared_ptr<nostr::Event> event)
+ [&generatedSubscriptionId, &signedTestEvents](const string& subscriptionId, shared_ptr<nostr::data::Event> event)
{
ASSERT_STREQ(subscriptionId.c_str(), generatedSubscriptionId.c_str());
ASSERT_NE(
find_if(
signedTestEvents.begin(),
signedTestEvents.end(),
- [&event](shared_ptr<nostr::Event> testEvent)
+ [&event](shared_ptr<nostr::data::Event> testEvent)
{
return *testEvent == *event;
}),
@@ -944,16 +961,16 @@ TEST_F(NostrServiceTest, Service_MaintainsMultipleSubscriptions_ThenClosesAll)
// Mock relay responses.
auto testEvents = getMultipleTextNoteTestEvents();
- vector<shared_ptr<nostr::Event>> signedTestEvents;
- for (nostr::Event testEvent : testEvents)
+ vector<shared_ptr<nostr::data::Event>> signedTestEvents;
+ for (nostr::data::Event testEvent : testEvents)
{
- auto signedEvent = make_shared<nostr::Event>(testEvent);
+ auto signedEvent = make_shared<nostr::data::Event>(testEvent);
signer->sign(signedEvent);
auto serializedEvent = signedEvent->serialize();
- auto deserializedEvent = nostr::Event::fromString(serializedEvent);
+ auto deserializedEvent = nostr::data::Event::fromString(serializedEvent);
- signedEvent = make_shared<nostr::Event>(deserializedEvent);
+ signedEvent = make_shared<nostr::data::Event>(deserializedEvent);
signedTestEvents.push_back(signedEvent);
}
@@ -970,7 +987,7 @@ TEST_F(NostrServiceTest, Service_MaintainsMultipleSubscriptions_ThenClosesAll)
for (auto event : testEvents)
{
- auto sendableEvent = make_shared<nostr::Event>(event);
+ auto sendableEvent = make_shared<nostr::data::Event>(event);
signer->sign(sendableEvent);
json jarr = json::array({ "EVENT", subscriptionIds.at(0), sendableEvent->serialize() });
messageHandler(jarr.dump());
@@ -991,7 +1008,7 @@ TEST_F(NostrServiceTest, Service_MaintainsMultipleSubscriptions_ThenClosesAll)
for (auto event : testEvents)
{
- auto sendableEvent = make_shared<nostr::Event>(event);
+ auto sendableEvent = make_shared<nostr::data::Event>(event);
signer->sign(sendableEvent);
json jarr = json::array({ "EVENT", subscriptionIds.at(1), sendableEvent->serialize() });
messageHandler(jarr.dump());
@@ -1004,8 +1021,8 @@ TEST_F(NostrServiceTest, Service_MaintainsMultipleSubscriptions_ThenClosesAll)
}));
// Send queries.
- auto shortFormFilters = make_shared<nostr::Filters>(getKind0And1TestFilters());
- auto longFormFilters = make_shared<nostr::Filters>(getKind30023TestFilters());
+ auto shortFormFilters = make_shared<nostr::data::Filters>(getKind0And1TestFilters());
+ auto longFormFilters = make_shared<nostr::data::Filters>(getKind30023TestFilters());
promise<void> shortFormPromise;
promise<void> longFormPromise;
auto shortFormFuture = shortFormPromise.get_future();
@@ -1013,14 +1030,14 @@ TEST_F(NostrServiceTest, Service_MaintainsMultipleSubscriptions_ThenClosesAll)
string shortFormSubscriptionId = nostrService->queryRelays(
shortFormFilters,
- [&shortFormSubscriptionId, &signedTestEvents](const string& subscriptionId, shared_ptr<nostr::Event> event)
+ [&shortFormSubscriptionId, &signedTestEvents](const string& subscriptionId, shared_ptr<nostr::data::Event> event)
{
ASSERT_STREQ(subscriptionId.c_str(), shortFormSubscriptionId.c_str());
ASSERT_NE(
find_if(
signedTestEvents.begin(),
signedTestEvents.end(),
- [&event](shared_ptr<nostr::Event> testEvent)
+ [&event](shared_ptr<nostr::data::Event> testEvent)
{
return *testEvent == *event;
}),
@@ -1035,14 +1052,14 @@ TEST_F(NostrServiceTest, Service_MaintainsMultipleSubscriptions_ThenClosesAll)
[](const string&, const string&) {});
string longFormSubscriptionId = nostrService->queryRelays(
shortFormFilters,
- [&longFormSubscriptionId, &signedTestEvents](const string& subscriptionId, shared_ptr<nostr::Event> event)
+ [&longFormSubscriptionId, &signedTestEvents](const string& subscriptionId, shared_ptr<nostr::data::Event> event)
{
ASSERT_STREQ(subscriptionId.c_str(), longFormSubscriptionId.c_str());
ASSERT_NE(
find_if(
signedTestEvents.begin(),
signedTestEvents.end(),
- [&event](shared_ptr<nostr::Event> testEvent)
+ [&event](shared_ptr<nostr::data::Event> testEvent)
{
return *testEvent == *event;
}),